diff options
| author | jdc <jdc@NetBSD.org> | 2012-07-02 11:23:40 +0000 |
|---|---|---|
| committer | jdc <jdc@NetBSD.org> | 2012-07-02 11:23:40 +0000 |
| commit | a329900d4be67ddcb27475fcc01cdbb85f36f8a2 (patch) | |
| tree | f86cc5aba268a6cf61fd4f4038ee8b6d22206e8d /sys | |
| parent | 9d202de3a5496c9578e8a69524ed5b25135cd8b7 (diff) | |
Apply lockup fixes from Havard Eidnes/OpenBSD in PR port-sparc64/46260:
- add an additional watchdog for RX overflow
- re-initialise the chip on device timeout
Also alter the interrupt blanking rate to 8 packets, as per OpenSolaris.
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/dev/ic/gem.c | 84 | ||||
| -rw-r--r-- | sys/dev/ic/gemreg.h | 4 | ||||
| -rw-r--r-- | sys/dev/ic/gemvar.h | 7 |
3 files changed, 87 insertions, 8 deletions
diff --git a/sys/dev/ic/gem.c b/sys/dev/ic/gem.c index 928cabb2398..3f99bab2c90 100644 --- a/sys/dev/ic/gem.c +++ b/sys/dev/ic/gem.c @@ -1,4 +1,4 @@ -/* $NetBSD: gem.c,v 1.98 2012/02/02 19:43:03 tls Exp $ */ +/* $NetBSD: gem.c,v 1.99 2012/07/02 11:23:40 jdc Exp $ */ /* * @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.98 2012/02/02 19:43:03 tls Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.99 2012/07/02 11:23:40 jdc Exp $"); #include "opt_inet.h" @@ -89,6 +89,7 @@ static void gem_stop(struct ifnet *, int); int gem_ioctl(struct ifnet *, u_long, void *); void gem_tick(void *); void gem_watchdog(struct ifnet *); +void gem_rx_watchdog(void *); void gem_pcs_start(struct gem_softc *sc); void gem_pcs_stop(struct gem_softc *sc, int); int gem_init(struct ifnet *); @@ -177,6 +178,7 @@ gem_detach(struct gem_softc *sc, int flags) ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); callout_destroy(&sc->sc_tick_ch); + callout_destroy(&sc->sc_rx_watchdog); /*FALLTHROUGH*/ case GEM_ATT_MII: @@ -613,6 +615,8 @@ gem_attach(struct gem_softc *sc, const uint8_t *enaddr) #endif callout_init(&sc->sc_tick_ch, 0); + callout_init(&sc->sc_rx_watchdog, 0); + callout_setfunc(&sc->sc_rx_watchdog, gem_rx_watchdog, sc); sc->sc_att_stage = GEM_ATT_FINISHED; @@ -764,6 +768,8 @@ gem_reset_rx(struct gem_softc *sc) /* Wait till it finishes */ if (!gem_bitwait(sc, h, GEM_RX_CONFIG, 1, 0)) aprint_error_dev(sc->sc_dev, "cannot disable read dma\n"); + /* Wait 5ms extra. */ + delay(5000); /* Finally, reset the ERX */ bus_space_write_4(t, h2, GEM_RESET, GEM_RESET_RX); @@ -848,7 +854,7 @@ gem_rx_common(struct gem_softc *sc) (3 * sc->sc_rxfifosize / 256) | ((sc->sc_rxfifosize / 256) << 12)); bus_space_write_4(t, h, GEM_RX_BLANKING, - (6 << GEM_RX_BLANKING_TIME_SHIFT) | 6); + (6 << GEM_RX_BLANKING_TIME_SHIFT) | 8); } /* @@ -1824,6 +1830,8 @@ gem_rint(struct gem_softc *sc) if (gem_add_rxbuf(sc, i) != 0) { GEM_COUNTER_INCR(sc, sc_ev_rxnobuf); ifp->if_ierrors++; + aprint_error_dev(sc->sc_dev, + "receive error: RX no buffer space\n"); GEM_INIT_RXDESC(sc, i); bus_dmamap_sync(sc->sc_dmatag, rxs->rxs_dmamap, 0, rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); @@ -2204,12 +2212,20 @@ gem_intr(void *v) /* * At least with GEM_SUN_GEM and some GEM_SUN_ERI * revisions GEM_MAC_RX_OVERFLOW happen often due to a - * silicon bug so handle them silently. Moreover, it's - * likely that the receiver has hung so we reset it. + * silicon bug so handle them silently. 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. */ if (rxstat & GEM_MAC_RX_OVERFLOW) { ifp->if_ierrors++; - gem_reset_rxdma(sc); + aprint_error_dev(sc->sc_dev, + "receive error: RX overflow sc->rxptr %d, complete %d\n", sc->sc_rxptr, bus_space_read_4(t, h, GEM_RX_COMPLETION)); + sc->sc_rx_fifo_wr_ptr = + bus_space_read_4(t, h, GEM_RX_FIFO_WR_PTR); + sc->sc_rx_fifo_rd_ptr = + bus_space_read_4(t, h, GEM_RX_FIFO_RD_PTR); + callout_schedule(&sc->sc_rx_watchdog, 400); } else if (rxstat & ~(GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT)) printf("%s: MAC rx fault, status 0x%02x\n", device_xname(sc->sc_dev), rxstat); @@ -2236,6 +2252,61 @@ gem_intr(void *v) return (r); } +void +gem_rx_watchdog(void *arg) +{ + struct gem_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ethercom.ec_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) { + aprint_error_dev(sc->sc_dev, "receiver not running\n"); + 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. + */ + aprint_error_dev(sc->sc_dev, + "receiver stuck in overflow, resetting\n"); + gem_init(ifp); + } else { + if ((state & GEM_MAC_STATE_OVERFLOW) != GEM_MAC_STATE_OVERFLOW) { + aprint_error_dev(sc->sc_dev, + "rx_watchdog: not in overflow state: 0x%x\n", + state); + } + if (rx_fifo_wr_ptr != rx_fifo_rd_ptr) { + aprint_error_dev(sc->sc_dev, + "rx_watchdog: wr & rd ptr different\n"); + } + if (sc->sc_rx_fifo_wr_ptr != rx_fifo_wr_ptr) { + aprint_error_dev(sc->sc_dev, + "rx_watchdog: wr pointer != saved\n"); + } + if (sc->sc_rx_fifo_rd_ptr != rx_fifo_rd_ptr) { + aprint_error_dev(sc->sc_dev, + "rx_watchdog: rd pointer != saved\n"); + } + aprint_error_dev(sc->sc_dev, "resetting anyway\n"); + gem_init(ifp); + } +} void gem_watchdog(struct ifnet *ifp) @@ -2252,6 +2323,7 @@ gem_watchdog(struct ifnet *ifp) ++ifp->if_oerrors; /* Try to get more packets going. */ + gem_init(ifp); gem_start(ifp); } diff --git a/sys/dev/ic/gemreg.h b/sys/dev/ic/gemreg.h index 7deb72c80ef..67876cedf5e 100644 --- a/sys/dev/ic/gemreg.h +++ b/sys/dev/ic/gemreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: gemreg.h,v 1.14 2008/09/15 19:43:24 jdc Exp $ */ +/* $NetBSD: gemreg.h,v 1.15 2012/07/02 11:23:40 jdc Exp $ */ /* * @@ -516,6 +516,8 @@ #define GEM_MAC_CC_PASS_PAUSE 0x00000004 /* pass pause up */ #define GEM_MAC_CC_BITS "\177\020b\0TXPAUSE\0b\1RXPAUSE\0b\2NOPAUSE\0\0" +/* GEM_MAC_MAC_STATE register bits */ +#define GEM_MAC_STATE_OVERFLOW 0x03800000 /* * Bits in GEM_MAC_SLOT_TIME register diff --git a/sys/dev/ic/gemvar.h b/sys/dev/ic/gemvar.h index d3c8d0b69ae..59e6cb57b71 100644 --- a/sys/dev/ic/gemvar.h +++ b/sys/dev/ic/gemvar.h @@ -1,4 +1,4 @@ -/* $NetBSD: gemvar.h,v 1.23 2012/02/02 19:43:03 tls Exp $ */ +/* $NetBSD: gemvar.h,v 1.24 2012/07/02 11:23:40 jdc Exp $ */ /* * @@ -130,6 +130,7 @@ struct gem_softc { struct ethercom sc_ethercom; /* ethernet common data */ struct mii_data sc_mii; /* MII media control */ struct callout sc_tick_ch; /* tick callout */ + struct callout sc_rx_watchdog; /* RX watchdog callout */ /* The following bus handles are to be provided by the bus front-end */ bus_space_tag_t sc_bustag; /* bus tag */ @@ -223,6 +224,10 @@ struct gem_softc { struct evcnt sc_ev_rxhist[9]; #endif + /* For use by the RX watchdog */ + u_int32_t sc_rx_fifo_wr_ptr; + u_int32_t sc_rx_fifo_rd_ptr; + enum gem_attach_stage sc_att_stage; }; |
