diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-03-22 21:46:32 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-03-22 21:46:32 +0000 |
commit | e99416eaab79d47df5693337d004ed01348b6214 (patch) | |
tree | 91e39567bd8bd50a57b799609640f69e44496cda /sys/dev/ic | |
parent | 0ec6f45eff20dea0077e4a95d84045dda0dd3399 (diff) |
Add workaround for RX MAC/FIFO hangs on ERI. Tested by matthieu@ and naddy@.
ok dlg@
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/gem.c | 52 | ||||
-rw-r--r-- | sys/dev/ic/gemreg.h | 5 | ||||
-rw-r--r-- | sys/dev/ic/gemvar.h | 6 |
3 files changed, 58 insertions, 5 deletions
diff --git a/sys/dev/ic/gem.c b/sys/dev/ic/gem.c index a96e5161237..6f6b13b35b2 100644 --- a/sys/dev/ic/gem.c +++ b/sys/dev/ic/gem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gem.c,v 1.88 2009/03/20 23:29:47 kettenis Exp $ */ +/* $OpenBSD: gem.c,v 1.89 2009/03/22 21:46:31 kettenis Exp $ */ /* $NetBSD: gem.c,v 1.1 2001/09/16 00:11:43 eeh Exp $ */ /* @@ -97,6 +97,7 @@ int gem_reset_rx(struct gem_softc *); int gem_reset_tx(struct gem_softc *); int gem_disable_rx(struct gem_softc *); int gem_disable_tx(struct gem_softc *); +void gem_rx_watchdog(void *); void gem_rxdrain(struct gem_softc *); void gem_fill_rx_ring(struct gem_softc *); int gem_add_rxbuf(struct gem_softc *, int idx); @@ -351,6 +352,7 @@ gem_config(struct gem_softc *sc) panic("gem_config: can't establish shutdownhook"); timeout_set(&sc->sc_tick_ch, gem_tick, sc); + timeout_set(&sc->sc_rx_watchdog, gem_rx_watchdog, sc); return; /* @@ -1047,7 +1049,6 @@ gem_add_rxbuf(struct gem_softc *sc, int idx) return (0); } - int gem_eint(struct gem_softc *sc, u_int status) { @@ -1122,8 +1123,23 @@ gem_intr(void *v) printf("%s: MAC rx fault, status %x\n", sc->sc_dev.dv_xname, rxstat); #endif - if (rxstat & GEM_MAC_RX_OVERFLOW) + if (rxstat & GEM_MAC_RX_OVERFLOW) { ifp->if_ierrors++; + + /* + * Apparently a silicon bug causes ERI to hang + * from time to time. So if we detect an RX + * FIFO overflow, we fire off a timer, and + * check whether we're still making progress + * by looking at the RX FIFO write and read + * pointers. + */ + sc->sc_rx_fifo_wr_ptr = + bus_space_read_4(t, seb, GEM_RX_FIFO_WR_PTR); + sc->sc_rx_fifo_rd_ptr = + bus_space_read_4(t, seb, GEM_RX_FIFO_RD_PTR); + timeout_add_msec(&sc->sc_rx_watchdog, 400); + } #ifdef GEM_DEBUG else if (rxstat & ~(GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT)) printf("%s: MAC rx fault, status %x\n", @@ -1133,6 +1149,36 @@ gem_intr(void *v) return (r); } +void +gem_rx_watchdog(void *arg) +{ + struct gem_softc *sc = arg; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + bus_space_tag_t t = sc->sc_bustag; + bus_space_handle_t h = sc->sc_h1; + u_int32_t rx_fifo_wr_ptr; + u_int32_t rx_fifo_rd_ptr; + u_int32_t state; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + rx_fifo_wr_ptr = bus_space_read_4(t, h, GEM_RX_FIFO_WR_PTR); + rx_fifo_rd_ptr = bus_space_read_4(t, h, GEM_RX_FIFO_RD_PTR); + state = bus_space_read_4(t, h, GEM_MAC_MAC_STATE); + if ((state & GEM_MAC_STATE_OVERFLOW) == GEM_MAC_STATE_OVERFLOW && + ((rx_fifo_wr_ptr == rx_fifo_rd_ptr) || + ((sc->sc_rx_fifo_wr_ptr == rx_fifo_wr_ptr) && + (sc->sc_rx_fifo_rd_ptr == rx_fifo_rd_ptr)))) { + /* + * The RX state machine is still in overflow state and + * the RX FIFO write and read pointers seem to be + * stuck. Whack the chip over the head to get things + * going again. + */ + gem_init(ifp); + } +} void gem_watchdog(struct ifnet *ifp) diff --git a/sys/dev/ic/gemreg.h b/sys/dev/ic/gemreg.h index 488a0fd3bce..b837265904a 100644 --- a/sys/dev/ic/gemreg.h +++ b/sys/dev/ic/gemreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: gemreg.h,v 1.15 2008/11/07 17:44:14 brad Exp $ */ +/* $OpenBSD: gemreg.h,v 1.16 2009/03/22 21:46:31 kettenis Exp $ */ /* $NetBSD: gemreg.h,v 1.1 2001/09/16 00:11:43 eeh Exp $ */ /* @@ -375,6 +375,9 @@ #define GEM_MAC_CC_RX_PAUSE 0x00000002 /* receive pause enabled */ #define GEM_MAC_CC_PASS_PAUSE 0x00000004 /* pass pause up */ +/* GEM_MAC_MAC_STATE register bits */ +#define GEM_MAC_STATE_OVERFLOW 0x03800000 + /* GEM MIF registers */ /* Bit bang registers use low bit only */ #define GEM_MIF_BB_CLOCK 0x6200 /* bit bang clock */ diff --git a/sys/dev/ic/gemvar.h b/sys/dev/ic/gemvar.h index 1abe2ce3cfa..0acaae0e678 100644 --- a/sys/dev/ic/gemvar.h +++ b/sys/dev/ic/gemvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: gemvar.h,v 1.20 2008/12/14 21:31:50 kettenis Exp $ */ +/* $OpenBSD: gemvar.h,v 1.21 2009/03/22 21:46:31 kettenis Exp $ */ /* $NetBSD: gemvar.h,v 1.1 2001/09/16 00:11:43 eeh Exp $ */ /* @@ -194,6 +194,10 @@ struct gem_softc { int sc_rxfifosize; + u_int32_t sc_rx_fifo_wr_ptr; + u_int32_t sc_rx_fifo_rd_ptr; + struct timeout sc_rx_watchdog; + /* ========== */ int sc_inited; int sc_debug; |