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

Reply via email to