summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2009-12-22 23:57:10 +0000
committeryongari <yongari@FreeBSD.org>2009-12-22 23:57:10 +0000
commit6b7d7d89355077dc34ae81b26db24ed112cb43bd (patch)
tree21d87d6402565b66b871cf19520d4fcde0559e0f
parentf1d3699b53f1dc16683d0655e1ab5dac8a4da96e (diff)
downloadFreeBSD-src-6b7d7d89355077dc34ae81b26db24ed112cb43bd.zip
FreeBSD-src-6b7d7d89355077dc34ae81b26db24ed112cb43bd.tar.gz
Reimplement Tx status error handler as recommended by datasheet.
If ste(4) encounter TX underrun or excessive collisions the TX MAC of controller is stalled so driver should wake it up again. TX underrun requires increasing TX threshold value to minimize further TX underruns. Previously ste(4) used to reset controller to recover from TX underrun, excessive collision and reclaiming error. However datasheet says only TX underrun requires resetting entire controller. So implement ste_restart_tx() that restarts TX MAC and do not perform full reset except TX underrun case. Now ste(4) uses CSR_READ_2 instead of CSR_READ_1 to read STE_TX_STATUS register. This way ste(4) will also read frame id value and we can write the same value back to STE_TX_FRAMEID register instead of overwriting it to 0. The datasheet was wrong in write back of STE_TX_STATUS so add some comments why we do so. Also always invoke ste_txeoc() after ste_txeof() in ste_poll as without reading TX status register can stall TX MAC.
-rw-r--r--sys/dev/ste/if_ste.c92
-rw-r--r--sys/dev/ste/if_stereg.h5
2 files changed, 76 insertions, 21 deletions
diff --git a/sys/dev/ste/if_ste.c b/sys/dev/ste/if_ste.c
index e8dfc83..f25c9cf 100644
--- a/sys/dev/ste/if_ste.c
+++ b/sys/dev/ste/if_ste.c
@@ -78,6 +78,9 @@ MODULE_DEPEND(ste, pci, 1, 1, 1);
MODULE_DEPEND(ste, ether, 1, 1, 1);
MODULE_DEPEND(ste, miibus, 1, 1, 1);
+/* Define to show Tx error status. */
+#define STE_SHOW_TXERRORS
+
/*
* Various supported device vendors/types and their names.
*/
@@ -118,6 +121,7 @@ static int ste_miibus_writereg(device_t, int, int, int);
static int ste_newbuf(struct ste_softc *, struct ste_chain_onefrag *);
static int ste_read_eeprom(struct ste_softc *, caddr_t, int, int, int);
static void ste_reset(struct ste_softc *);
+static void ste_restart_tx(struct ste_softc *);
static int ste_rxeof(struct ste_softc *, int);
static void ste_setmulti(struct ste_softc *);
static void ste_start(struct ifnet *);
@@ -627,6 +631,7 @@ ste_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
rx_npkts = ste_rxeof(sc, count);
ste_txeof(sc);
+ ste_txeoc(sc);
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
ste_start_locked(ifp);
@@ -635,9 +640,6 @@ ste_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
status = CSR_READ_2(sc, STE_ISR_ACK);
- if (status & STE_ISR_TX_DONE)
- ste_txeoc(sc);
-
if (status & STE_ISR_STATS_OFLOW)
ste_stats_update(sc);
@@ -788,35 +790,63 @@ ste_rxeof(struct ste_softc *sc, int count)
static void
ste_txeoc(struct ste_softc *sc)
{
+ uint16_t txstat;
struct ifnet *ifp;
- uint8_t txstat;
+
+ STE_LOCK_ASSERT(sc);
ifp = sc->ste_ifp;
- while ((txstat = CSR_READ_1(sc, STE_TX_STATUS)) &
- STE_TXSTATUS_TXDONE) {
- if (txstat & STE_TXSTATUS_UNDERRUN ||
- txstat & STE_TXSTATUS_EXCESSCOLLS ||
- txstat & STE_TXSTATUS_RECLAIMERR) {
+ /*
+ * STE_TX_STATUS register implements a queue of up to 31
+ * transmit status byte. Writing an arbitrary value to the
+ * register will advance the queue to the next transmit
+ * status byte. This means if driver does not read
+ * STE_TX_STATUS register after completing sending more
+ * than 31 frames the controller would be stalled so driver
+ * should re-wake the Tx MAC. This is the most severe
+ * limitation of ST201 based controller.
+ */
+ for (;;) {
+ txstat = CSR_READ_2(sc, STE_TX_STATUS);
+ if ((txstat & STE_TXSTATUS_TXDONE) == 0)
+ break;
+ if ((txstat & (STE_TXSTATUS_UNDERRUN |
+ STE_TXSTATUS_EXCESSCOLLS | STE_TXSTATUS_RECLAIMERR |
+ STE_TXSTATUS_STATSOFLOW)) != 0) {
ifp->if_oerrors++;
- device_printf(sc->ste_dev,
- "transmission error: %x\n", txstat);
-
- ste_init_locked(sc);
-
- if (txstat & STE_TXSTATUS_UNDERRUN &&
+#ifdef STE_SHOW_TXERRORS
+ device_printf(sc->ste_dev, "TX error : 0x%b\n",
+ txstat & 0xFF, STE_ERR_BITS);
+#endif
+ if ((txstat & STE_TXSTATUS_UNDERRUN) != 0 &&
sc->ste_tx_thresh < STE_PACKET_SIZE) {
sc->ste_tx_thresh += STE_MIN_FRAMELEN;
+ if (sc->ste_tx_thresh > STE_PACKET_SIZE)
+ sc->ste_tx_thresh = STE_PACKET_SIZE;
device_printf(sc->ste_dev,
- "tx underrun, increasing tx"
+ "TX underrun, increasing TX"
" start threshold to %d bytes\n",
sc->ste_tx_thresh);
+ /* Make sure to disable active DMA cycles. */
+ STE_SETBIT4(sc, STE_DMACTL,
+ STE_DMACTL_TXDMA_STALL);
+ ste_wait(sc);
+ ste_init_locked(sc);
+ break;
}
- CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh);
- CSR_WRITE_2(sc, STE_TX_RECLAIM_THRESH,
- (STE_PACKET_SIZE >> 4));
+ /* Restart Tx. */
+ ste_restart_tx(sc);
}
- ste_init_locked(sc);
+ /*
+ * Advance to next status and ACK TxComplete
+ * interrupt. ST201 data sheet was wrong here, to
+ * get next Tx status, we have to write both
+ * STE_TX_STATUS and STE_TX_FRAMEID register.
+ * Otherwise controller returns the same status
+ * as well as not acknowledge Tx completion
+ * interrupt.
+ */
CSR_WRITE_2(sc, STE_TX_STATUS, txstat);
}
}
@@ -1713,6 +1743,26 @@ ste_reset(struct ste_softc *sc)
device_printf(sc->ste_dev, "global reset never completed\n");
}
+static void
+ste_restart_tx(struct ste_softc *sc)
+{
+ uint16_t mac;
+ int i;
+
+ for (i = 0; i < STE_TIMEOUT; i++) {
+ mac = CSR_READ_2(sc, STE_MACCTL1);
+ mac |= STE_MACCTL1_TX_ENABLE;
+ CSR_WRITE_2(sc, STE_MACCTL1, mac);
+ mac = CSR_READ_2(sc, STE_MACCTL1);
+ if ((mac & STE_MACCTL1_TX_ENABLED) != 0)
+ break;
+ DELAY(10);
+ }
+
+ if (i == STE_TIMEOUT)
+ device_printf(sc->ste_dev, "starting Tx failed");
+}
+
static int
ste_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
{
@@ -1952,8 +2002,8 @@ ste_watchdog(struct ste_softc *sc)
ifp->if_oerrors++;
if_printf(ifp, "watchdog timeout\n");
- ste_txeoc(sc);
ste_txeof(sc);
+ ste_txeoc(sc);
ste_rxeof(sc, -1);
ste_init_locked(sc);
diff --git a/sys/dev/ste/if_stereg.h b/sys/dev/ste/if_stereg.h
index c0adcad..4ec1920 100644
--- a/sys/dev/ste/if_stereg.h
+++ b/sys/dev/ste/if_stereg.h
@@ -253,6 +253,11 @@
#define STE_TXSTATUS_TXINTR_REQ 0x40
#define STE_TXSTATUS_TXDONE 0x80
+#define STE_ERR_BITS "\20" \
+ "\2RECLAIM\3STSOFLOW" \
+ "\4EXCESSCOLLS\5UNDERRUN" \
+ "\6INTREQ\7DONE"
+
#define STE_ISRACK_INTLATCH 0x0001
#define STE_ISRACK_HOSTERR 0x0002
#define STE_ISRACK_TX_DONE 0x0004
OpenPOWER on IntegriCloud