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);