Hardware has two transmit FIFOs, and the idea is to alternate between the two. The code that decides which FIFO to use is busted. It keeps a count of the number of FIFOs in use. That means that if the count is one, we don't actually know which if the FIFOs is currently in use. If we pick the wrong FIFO for the next packet, the interface hangs and we get a watchdog timeout.
Diff below fixes this by using a bitmask to keep track of the FIFOs that are in use. ok? Index: arch/armv7/sunxi/sxie.c =================================================================== RCS file: /cvs/src/sys/arch/armv7/sunxi/sxie.c,v retrieving revision 1.21 diff -u -p -r1.21 sxie.c --- arch/armv7/sunxi/sxie.c 22 Aug 2016 19:38:42 -0000 1.21 +++ arch/armv7/sunxi/sxie.c 11 Sep 2016 11:18:24 -0000 @@ -101,6 +101,9 @@ #define SXIE_INTR_DISABLE 0x0000 #define SXIE_INTR_CLEAR 0x0000 +#define SXIE_TX_FIFO0 0x0001 +#define SXIE_TX_FIFO1 0x0002 + #define SXIE_RX_ENABLE 0x0004 #define SXIE_TX_ENABLE 0x0003 #define SXIE_RXTX_ENABLE 0x0007 @@ -436,16 +439,13 @@ sxie_intr(void *arg) sxie_recv(sc); } - pending &= 3; - - if (pending) { + if (pending & (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) { ifq_clr_oactive(&ifp->if_snd); - sc->txf_inuse--; - ifp->if_opackets++; - if (pending == 3) { /* 2 packets got sent */ - sc->txf_inuse--; + if (pending & SXIE_TX_FIFO0) + ifp->if_opackets++; + if (pending & SXIE_TX_FIFO1) ifp->if_opackets++; - } + sc->txf_inuse &= ~pending; if (sc->txf_inuse == 0) ifp->if_timer = 0; else @@ -473,7 +473,7 @@ sxie_start(struct ifnet *ifp) uint32_t fifo; uint32_t txbuf[SXIE_MAX_PKT_SIZE / sizeof(uint32_t)]; /* XXX !!! */ - if (sc->txf_inuse > 1) + if (sc->txf_inuse == (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) ifq_set_oactive(&ifp->if_snd); if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) @@ -494,7 +494,7 @@ trynext: return; } - if (sc->txf_inuse > 1) { + if (sc->txf_inuse == (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) { ifq_deq_rollback(&ifp->if_snd, m); printf("sxie_start: tx fifos in use.\n"); ifq_set_oactive(&ifp->if_snd); @@ -502,10 +502,14 @@ trynext: } /* select fifo */ - fifo = sc->txf_inuse; + if (sc->txf_inuse & SXIE_TX_FIFO0) { + sc->txf_inuse |= SXIE_TX_FIFO1; + fifo = 1; + } else { + sc->txf_inuse |= SXIE_TX_FIFO0; + fifo = 0; + } SXIWRITE4(sc, SXIE_TXINS, fifo); - - sc->txf_inuse++; /* set packet length */ SXIWRITE4(sc, SXIE_TXPKTLEN0 + (fifo * 4), m->m_pkthdr.len);