svn commit: r325978 - head/sys/arm/allwinner
Emmanuel Vadot
manu at FreeBSD.org
Sat Nov 18 20:42:49 UTC 2017
Author: manu
Date: Sat Nov 18 20:42:48 2017
New Revision: 325978
URL: https://svnweb.freebsd.org/changeset/base/325978
Log:
if_awg: mark the first tx descriptor as ready only after all the other tx descriptors are set up
In a multi segment frame, if the first tx descriptor is marked with TX_DESC_CTL
but not all tx descriptors for the other segments in the frame are set up,
the TX DMA may transmit an incomplete frame.
To prevent this, set TX_DESC_CTL for the first tx descriptor only when done
with all the other segments.
Also, don't bother cleaning transmitted tx descriptors since TX_DESC_CTL
is cleared for them by the hardware and they will be reprogrammed before
TX_DESC_CTL is reenabled for them.
Submitted by: Guy Yur <guyyur at gmail.com>
Differential Revision: https://reviews.freebsd.org/D13030
Modified:
head/sys/arm/allwinner/if_awg.c
Modified: head/sys/arm/allwinner/if_awg.c
==============================================================================
--- head/sys/arm/allwinner/if_awg.c Sat Nov 18 20:38:05 2017 (r325977)
+++ head/sys/arm/allwinner/if_awg.c Sat Nov 18 20:42:48 2017 (r325978)
@@ -387,35 +387,17 @@ awg_media_change(if_t ifp)
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;
- ++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)
{
bus_dma_segment_t segs[TX_MAX_SEGS];
- int error, nsegs, cur, i, flags;
+ int error, nsegs, cur, first, i;
u_int csum_flags;
+ uint32_t flags, status;
struct mbuf *m;
+ cur = first = index;
+
m = *mp;
error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag,
sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT);
@@ -438,6 +420,7 @@ awg_setup_txbuf(struct awg_softc *sc, int index, struc
BUS_DMASYNC_PREWRITE);
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;
@@ -446,8 +429,7 @@ awg_setup_txbuf(struct awg_softc *sc, int index, struc
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);
+ for (i = 0; i < nsegs; i++) {
sc->tx.segs++;
if (i == nsegs - 1) {
flags |= TX_LAST_DESC;
@@ -460,16 +442,51 @@ awg_setup_txbuf(struct awg_softc *sc, int index, struc
flags |= TX_INT_CTL;
}
}
- awg_setup_txdesc(sc, cur, flags, segs[i].ds_addr,
- segs[i].ds_len);
+
+ 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 set up.
+ */
+ status = TX_DESC_CTL;
+
+ ++sc->tx.queued;
cur = TX_NEXT(cur);
}
+ sc->tx.buf_map[first].mbuf = m;
+
+ /*
+ * The whole mbuf chain has been DMA mapped,
+ * fix the first descriptor.
+ */
+ sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL);
+
return (nsegs);
}
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
awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr)
{
uint32_t status, size;
@@ -891,7 +908,6 @@ awg_rxintr(struct awg_softc *sc)
static void
awg_txintr(struct awg_softc *sc)
{
- struct awg_bufmap *bmap;
struct emac_desc *desc;
uint32_t status;
if_t ifp;
@@ -908,23 +924,12 @@ awg_txintr(struct awg_softc *sc)
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;
- }
- awg_setup_txdesc(sc, i, 0, 0, 0);
+ awg_clean_txbuf(sc, i);
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);
}
static void
@@ -1519,7 +1524,7 @@ awg_setup_dma(device_t dev)
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);
@@ -1527,7 +1532,6 @@ awg_setup_dma(device_t dev)
device_printf(dev, "cannot create TX buffer map\n");
return (error);
}
- awg_setup_txdesc(sc, i, 0, 0, 0);
}
/* Setup RX ring */
More information about the svn-src-head
mailing list