Index: sys/arm/allwinner/if_awg.c =================================================================== --- sys/arm/allwinner/if_awg.c +++ sys/arm/allwinner/if_awg.c @@ -104,6 +104,8 @@ #define TX_INTERVAL_DEFAULT 64 #define RX_BATCH_DEFAULT 64 +#define TX_INTERVAL_NEXT(n) (((n) + 1) & (awg_tx_interval - 1)) + /* syscon EMAC clock register */ #define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */ #define EMAC_CLK_EPHY_ADDR_SHIFT 20 @@ -169,6 +171,7 @@ bus_dma_tag_t buf_tag; struct awg_bufmap buf_map[TX_DESC_COUNT]; u_int cur, next, queued; + u_int pkts; }; struct awg_rxring { @@ -212,6 +215,8 @@ { -1, 0 } }; +static void awg_txeof(struct awg_softc *sc); + static int awg_miibus_readreg(device_t dev, int phy, int reg) { @@ -385,55 +390,52 @@ return (error); } -static void -awg_setup_txdesc(struct awg_softc *sc, int index, int flags, bus_addr_t paddr, - u_int len) -{ - uint32_t status, size; - - if (paddr == 0 || len == 0) { - status = 0; - size = 0; - --sc->tx.queued; - } else { - status = TX_DESC_CTL; - size = flags | len; - if ((index & (awg_tx_interval - 1)) == 0) - size |= TX_INT_CTL; - ++sc->tx.queued; - } - - sc->tx.desc_ring[index].addr = htole32((uint32_t)paddr); - sc->tx.desc_ring[index].size = htole32(size); - sc->tx.desc_ring[index].status = htole32(status); -} - static int -awg_setup_txbuf(struct awg_softc *sc, int index, struct mbuf **mp) +awg_encap(struct awg_softc *sc, struct mbuf **mp) { + bus_dmamap_t map; bus_dma_segment_t segs[TX_MAX_SEGS]; - int error, nsegs, cur, i, flags; + int error, i, nsegs, cur, first, last; + uint32_t flags, status; u_int csum_flags; struct mbuf *m; + cur = first = sc->tx.cur; + map = sc->tx.buf_map[first].map; + m = *mp; - error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, - sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, segs, + &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS); - if (m == NULL) - return (0); + if (m == NULL) { + m_freem(*mp); + *mp = NULL; + return (ENOMEM); + } *mp = m; - error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, - sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, + segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(*mp); + *mp = NULL; + return (ENOMEM); + } + } else if (error != 0) + return (error); + if (nsegs == 0) { + m_freem(*mp); + *mp = NULL; + return (EIO); } - if (error != 0) - return (0); - bus_dmamap_sync(sc->tx.buf_tag, sc->tx.buf_map[index].map, - BUS_DMASYNC_PREWRITE); + if (sc->tx.queued + nsegs > TX_DESC_COUNT) { + bus_dmamap_unload(sc->tx.buf_tag, map); + return (ENOBUFS); + } flags = TX_FIR_DESC; + status = 0; if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) csum_flags = TX_CHECKSUM_CTL_FULL; @@ -442,17 +444,67 @@ flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT); } - for (cur = index, i = 0; i < nsegs; i++) { - sc->tx.buf_map[cur].mbuf = (i == 0 ? m : NULL); - if (i == nsegs - 1) + bus_dmamap_sync(sc->tx.buf_tag, map, BUS_DMASYNC_PREWRITE); + + for (i = 0; i < nsegs; i++) { + if (i == nsegs - 1) { flags |= TX_LAST_DESC; - awg_setup_txdesc(sc, cur, flags, segs[i].ds_addr, - segs[i].ds_len); + /* + * Request Tx completion interrupt for + * every awg_tx_interval frames. + */ + sc->tx.pkts = TX_INTERVAL_NEXT(sc->tx.pkts); + if (sc->tx.pkts == 0) + flags |= TX_INT_CTL; + } + + sc->tx.desc_ring[cur].addr = htole32((uint32_t)segs[i].ds_addr); + sc->tx.desc_ring[cur].size = htole32(flags | segs[i].ds_len); + sc->tx.desc_ring[cur].status = htole32(status); + flags &= ~TX_FIR_DESC; + + /* + * Setting of the valid bit in the first descriptor is + * deferred until the whole chain is fully setup. + */ + status = TX_DESC_CTL; + + ++sc->tx.queued; cur = TX_NEXT(cur); } - return (nsegs); + /* + * the whole mbuf chain has been DMA mapped, fix first descriptor. + */ + sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL); + + sc->tx.cur = cur; + + /* store mapping and mbuf in the last segment */ + last = TX_SKIP(cur, TX_DESC_COUNT - 1); + sc->tx.buf_map[first].map = sc->tx.buf_map[last].map; + sc->tx.buf_map[last].map = map; + sc->tx.buf_map[last].mbuf = m; + + return (0); +} + +static void +awg_clean_txbuf(struct awg_softc *sc, int index) +{ + struct awg_bufmap *bmap; + + --sc->tx.queued; + + bmap = &sc->tx.buf_map[index]; + if (bmap->mbuf != NULL) { + bus_dmamap_sync(sc->tx.buf_tag, bmap->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tx.buf_tag, bmap->map); + m_freem(bmap->mbuf); + bmap->mbuf = NULL; + } } static void @@ -510,7 +562,7 @@ struct mbuf *m; uint32_t val; if_t ifp; - int cnt, nsegs; + int cnt, err; AWG_ASSERT_LOCKED(sc); @@ -524,22 +576,19 @@ return; for (cnt = 0; ; cnt++) { - if (sc->tx.queued >= TX_DESC_COUNT - TX_MAX_SEGS) { - if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); - break; - } - m = if_dequeue(ifp); if (m == NULL) break; - nsegs = awg_setup_txbuf(sc, sc->tx.cur, &m); - if (nsegs == 0) { - if_sendq_prepend(ifp, m); + err = awg_encap(sc, &m); + if (err != 0) { + if (err == ENOBUFS) + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + if (m != NULL) + if_sendq_prepend(ifp, m); break; } if_bpfmtap(ifp, m); - sc->tx.cur = TX_SKIP(sc->tx.cur, nsegs); } if (cnt != 0) { @@ -875,13 +924,12 @@ } static void -awg_txintr(struct awg_softc *sc) +awg_txeof(struct awg_softc *sc) { - struct awg_bufmap *bmap; struct emac_desc *desc; - uint32_t status; + uint32_t status, size; if_t ifp; - int i; + int i, prog; AWG_ASSERT_LOCKED(sc); @@ -889,28 +937,29 @@ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); ifp = sc->ifp; + + prog = 0; for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { desc = &sc->tx.desc_ring[i]; status = le32toh(desc->status); if ((status & TX_DESC_CTL) != 0) break; - bmap = &sc->tx.buf_map[i]; - if (bmap->mbuf != NULL) { - bus_dmamap_sync(sc->tx.buf_tag, bmap->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->tx.buf_tag, bmap->map); - m_freem(bmap->mbuf); - bmap->mbuf = NULL; + + prog++; + awg_clean_txbuf(sc, i); + size = le32toh(desc->size); + if (size & TX_LAST_DESC) { + if ((status & (TX_HEADER_ERR | TX_PAYLOAD_ERR)) != 0) + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + else + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } - awg_setup_txdesc(sc, i, 0, 0, 0); - if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } - sc->tx.next = i; - - bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, - BUS_DMASYNC_PREWRITE); + if (prog > 0) { + sc->tx.next = i; + if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); + } } static void @@ -928,8 +977,10 @@ if (val & RX_INT) awg_rxintr(sc); - if (val & (TX_INT|TX_BUF_UA_INT)) { - awg_txintr(sc); + if (val & TX_INT) + awg_txeof(sc); + + if (val & (TX_INT | TX_BUF_UA_INT)) { if (!if_sendq_empty(sc->ifp)) awg_start_locked(sc); } @@ -956,7 +1007,7 @@ } rx_npkts = awg_rxintr(sc); - awg_txintr(sc); + awg_txeof(sc); if (!if_sendq_empty(ifp)) awg_start_locked(sc); @@ -1505,7 +1556,7 @@ return (error); } - sc->tx.queued = TX_DESC_COUNT; + sc->tx.queued = 0; for (i = 0; i < TX_DESC_COUNT; i++) { error = bus_dmamap_create(sc->tx.buf_tag, 0, &sc->tx.buf_map[i].map); @@ -1513,7 +1564,6 @@ device_printf(dev, "cannot create TX buffer map\n"); return (error); } - awg_setup_txdesc(sc, i, 0, 0, 0); } /* Setup RX ring */