Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/netbsd-2-0]: src/sys/dev/pci Pull up revision 1.78 (requested by thorpej...



details:   https://anonhg.NetBSD.org/src/rev/6043302b2376
branches:  netbsd-2-0
changeset: 564641:6043302b2376
user:      jdc <jdc%NetBSD.org@localhost>
date:      Fri Jan 07 11:44:22 2005 +0000

description:
Pull up revision 1.78 (requested by thorpej in ticket #897)

Work around another problem (that causes horrible performance) on the 82547:
- Change the Tx:Rx on-chip RAM allocation ratio based on MTU:
  => 82547: > 8192 18K:22K, else 10K:30K
  => others: > 8192 24K:40K, else 16K:48K
  Values derived from Linux driver.
- On the 82547, keep track of how much of the Tx FIFO has been used.
  When in half-duplex mode, don't let packets wrap around the FIFO ring
  buffer, because that causes the chip to croak.  Detect this, stall the
  Tx queue, use a timer to wait for the packets to drain from the Tx FIFO,
  reset the internal FIFO pointers, and restart the Tx queue.  Basic
  algorithm (and some magic numbers) derived from FreeBSD and Linux drivers.

diffstat:

 sys/dev/pci/if_wm.c |  156 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 154 insertions(+), 2 deletions(-)

diffs (247 lines):

diff -r 50cb3ff0a9b6 -r 6043302b2376 sys/dev/pci/if_wm.c
--- a/sys/dev/pci/if_wm.c       Fri Jan 07 11:43:29 2005 +0000
+++ b/sys/dev/pci/if_wm.c       Fri Jan 07 11:44:22 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_wm.c,v 1.68.2.3 2004/07/16 21:21:07 he Exp $        */
+/*     $NetBSD: if_wm.c,v 1.68.2.4 2005/01/07 11:44:22 jdc Exp $       */
 
 /*
  * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
@@ -47,7 +47,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.68.2.3 2004/07/16 21:21:07 he Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.68.2.4 2005/01/07 11:44:22 jdc Exp $");
 
 #include "bpfilter.h"
 #include "rnd.h"
@@ -263,6 +263,7 @@
        struct evcnt sc_ev_txctx_init;  /* Tx cksum context cache initialized */
        struct evcnt sc_ev_txctx_hit;   /* Tx cksum context cache hit */
        struct evcnt sc_ev_txctx_miss;  /* Tx cksum context cache miss */
+       struct evcnt sc_ev_txfifo_stall;/* Tx FIFO stalls (82547) */
 
        struct evcnt sc_ev_txseg[WM_NTXSEGS]; /* Tx packets w/ N segments */
        struct evcnt sc_ev_txdrop;      /* Tx packets dropped (too many segs) */
@@ -298,6 +299,13 @@
        uint32_t sc_icr;                /* prototype interrupt bits */
        uint32_t sc_tctl;               /* prototype TCTL register */
        uint32_t sc_rctl;               /* prototype RCTL register */
+       /* These 5 variables are used only on the 82547. */
+       int     sc_txfifo_size;         /* Tx FIFO size */
+       int     sc_txfifo_head;         /* current head of FIFO */
+       uint32_t sc_txfifo_addr;        /* internal address of start of FIFO */
+       int     sc_txfifo_stall;        /* Tx FIFO is stalled */
+       struct callout sc_txfifo_ch;    /* Tx FIFO stall work-around timer */
+
        uint32_t sc_txcw;               /* prototype TXCW register */
        uint32_t sc_tipg;               /* prototype TIPG register */
 
@@ -320,6 +328,7 @@
 
 #define        WM_RXCHAIN_LINK(sc, m)                                          \
 do {                                                                   \
+       uint32_t sc_pba;                /* prototype PBA register */
        *(sc)->sc_rxtailp = (sc)->sc_rxtail = (m);                      \
        (sc)->sc_rxtailp = &(m)->m_next;                                \
 } while (/*CONSTCOND*/0)
@@ -365,6 +374,8 @@
                                                                        \
        /* Now sync whatever is left. */                                \
        bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap,               \
+#define        CSR_WRITE_FLUSH(sc)                                             \
+       (void) CSR_READ((sc), WMREG_STATUS)
            WM_CDTXOFF(__x), sizeof(wiseman_txdesc_t) * __n, (ops));    \
 } while (/*CONSTCOND*/0)
 
@@ -488,6 +499,8 @@
 
        { PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82544EI_FIBER,
          "Intel i82544EI 1000BASE-X Ethernet",
+static void    wm_82547_txfifo_stall(void *);
+
          WM_T_82544,           WMP_F_1000X },
 
        { PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82544GC_COPPER,
@@ -843,6 +856,13 @@
                sc->sc_bus_speed = 66;
                aprint_verbose("%s: Communication Streaming Architecture\n",
                    sc->sc_dev.dv_xname);
+               if (sc->sc_type == WM_T_82547) {
+                       callout_init(&sc->sc_txfifo_ch);
+                       callout_setfunc(&sc->sc_txfifo_ch,
+                                       wm_82547_txfifo_stall, sc);
+                       aprint_verbose("%s: using 82547 Tx FIFO stall "
+                                      "work-around\n", sc->sc_dev.dv_xname);
+               }
        } else {
                reg = CSR_READ(sc, WMREG_STATUS);
                if (reg & STATUS_BUS64)
@@ -1200,6 +1220,8 @@
        evcnt_attach_dynamic(&sc->sc_ev_txdstall, EVCNT_TYPE_MISC,
            NULL, sc->sc_dev.dv_xname, "txdstall");
        evcnt_attach_dynamic(&sc->sc_ev_txforceintr, EVCNT_TYPE_MISC,
+       evcnt_attach_dynamic(&sc->sc_ev_txfifo_stall, EVCNT_TYPE_MISC,
+           NULL, sc->sc_dev.dv_xname, "txfifo_stall");
            NULL, sc->sc_dev.dv_xname, "txforceintr");
        evcnt_attach_dynamic(&sc->sc_ev_txdw, EVCNT_TYPE_INTR,
            NULL, sc->sc_dev.dv_xname, "txdw");
@@ -1443,6 +1465,97 @@
         * Remember the previous number of free descriptors.
         */
        ofree = sc->sc_txfree;
+ * wm_82547_txfifo_stall:
+ *
+ *     Callout used to wait for the 82547 Tx FIFO to drain,
+ *     reset the FIFO pointers, and restart packet transmission.
+ */
+static void
+wm_82547_txfifo_stall(void *arg)
+{
+       struct wm_softc *sc = arg;
+       int s;
+
+       s = splnet();
+
+       if (sc->sc_txfifo_stall) {
+               if (CSR_READ(sc, WMREG_TDT) == CSR_READ(sc, WMREG_TDH) &&
+                   CSR_READ(sc, WMREG_TDFT) == CSR_READ(sc, WMREG_TDFH) &&
+                   CSR_READ(sc, WMREG_TDFTS) == CSR_READ(sc, WMREG_TDFHS)) {
+                       /*
+                        * Packets have drained.  Stop transmitter, reset
+                        * FIFO pointers, restart transmitter, and kick
+                        * the packet queue.
+                        */
+                       uint32_t tctl = CSR_READ(sc, WMREG_TCTL);
+                       CSR_WRITE(sc, WMREG_TCTL, tctl & ~TCTL_EN);
+                       CSR_WRITE(sc, WMREG_TDFT, sc->sc_txfifo_addr);
+                       CSR_WRITE(sc, WMREG_TDFH, sc->sc_txfifo_addr);
+                       CSR_WRITE(sc, WMREG_TDFTS, sc->sc_txfifo_addr);
+                       CSR_WRITE(sc, WMREG_TDFHS, sc->sc_txfifo_addr);
+                       CSR_WRITE(sc, WMREG_TCTL, tctl);
+                       CSR_WRITE_FLUSH(sc);
+
+                       sc->sc_txfifo_head = 0;
+                       sc->sc_txfifo_stall = 0;
+                       wm_start(&sc->sc_ethercom.ec_if);
+               } else {
+                       /*
+                        * Still waiting for packets to drain; try again in
+                        * another tick.
+                        */
+                       callout_schedule(&sc->sc_txfifo_ch, 1);
+               }
+       }
+
+       splx(s);
+}
+
+/*
+ * wm_82547_txfifo_bugchk:
+ *
+ *     Check for bug condition in the 82547 Tx FIFO.  We need to
+ *     prevent enqueueing a packet that would wrap around the end
+ *     if the Tx FIFO ring buffer, otherwise the chip will croak.
+ *
+ *     We do this by checking the amount of space before the end
+ *     of the Tx FIFO buffer.  If the packet will not fit, we "stall"
+ *     the Tx FIFO, wait for all remaining packets to drain, reset
+ *     the internal FIFO pointers to the beginning, and restart
+ *     transmission on the interface.
+ */
+#define        WM_FIFO_HDR             0x10
+#define        WM_82547_PAD_LEN        0x3e0
+static int
+wm_82547_txfifo_bugchk(struct wm_softc *sc, struct mbuf *m0)
+{
+       int space = sc->sc_txfifo_size - sc->sc_txfifo_head;
+       int len = roundup(m0->m_pkthdr.len + WM_FIFO_HDR, WM_FIFO_HDR);
+
+       /* Just return if already stalled. */
+       if (sc->sc_txfifo_stall)
+               return (1);
+
+       if (sc->sc_mii.mii_media_active & IFM_FDX) {
+               /* Stall only occurs in half-duplex mode. */
+               goto send_packet;
+       }
+
+       if (len >= WM_82547_PAD_LEN + space) {
+               sc->sc_txfifo_stall = 1;
+               callout_schedule(&sc->sc_txfifo_ch, 1);
+               return (1);
+       }
+
+ send_packet:
+       sc->sc_txfifo_head += len;
+       if (sc->sc_txfifo_head >= sc->sc_txfifo_size)
+               sc->sc_txfifo_head -= sc->sc_txfifo_size;
+
+       return (0);
+}
+
+/*
 
        /*
         * Loop through the send queue, setting up transmit descriptors
@@ -1554,6 +1667,22 @@
                 * is used to set the checksum context).
                 */
                txs->txs_mbuf = m0;
+               /*
+                * Check for 82547 Tx FIFO bug.  We need to do this
+                * once we know we can transmit the packet, since we
+                * do some internal FIFO space accounting here.
+                */
+               if (sc->sc_type == WM_T_82547 &&
+                   wm_82547_txfifo_bugchk(sc, m0)) {
+                       DPRINTF(WM_DEBUG_TX,
+                           ("%s: TX: 82547 Tx FIFO bug detected\n",
+                           sc->sc_dev.dv_xname));
+                       ifp->if_flags |= IFF_OACTIVE;
+                       bus_dmamap_unload(sc->sc_dmat, dmamap);
+                       WM_EVCNT_INCR(&sc->sc_ev_txfifo_stall);
+                       break;
+               }
+
                txs->txs_firstdesc = sc->sc_txnext;
                txs->txs_ndesc = dmamap->dm_nsegs;
 
@@ -2252,6 +2381,25 @@
  *
  *     Initialize the interface.  Must be called at splnet().
  */
+       /*
+        * Allocate on-chip memory according to the MTU size.
+        * The Packet Buffer Allocation register must be written
+        * before the chip is reset.
+        */
+       if (sc->sc_type < WM_T_82547) {
+               sc->sc_pba = sc->sc_ethercom.ec_if.if_mtu > 8192 ?
+                   PBA_40K : PBA_48K;
+       } else {
+               sc->sc_pba = sc->sc_ethercom.ec_if.if_mtu > 8192 ?
+                   PBA_22K : PBA_30K;
+               sc->sc_txfifo_head = 0;
+               sc->sc_txfifo_addr = sc->sc_pba << PBA_ADDR_SHIFT;
+               sc->sc_txfifo_size =
+                   (PBA_40K - sc->sc_pba) << PBA_BYTE_SHIFT;
+               sc->sc_txfifo_stall = 0;
+       }
+       CSR_WRITE(sc, WMREG_PBA, sc->sc_pba);
+
 static int
 wm_init(struct ifnet *ifp)
 {
@@ -2593,6 +2741,10 @@
        uint32_t reg;
        int x;
 
+       /* Stop the 82547 Tx FIFO stall check timer. */
+       if (sc->sc_type == WM_T_82547)
+               callout_stop(&sc->sc_txfifo_ch);
+
        if (sc->sc_flags & WM_F_EEPROM_HANDSHAKE)  {
                reg = CSR_READ(sc, WMREG_EECD);
 



Home | Main Index | Thread Index | Old Index