Module Name: src Committed By: jdc Date: Mon Jul 2 11:23:41 UTC 2012
Modified Files: src/sys/dev/ic: gem.c gemreg.h gemvar.h Log Message: 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. To generate a diff of this commit: cvs rdiff -u -r1.98 -r1.99 src/sys/dev/ic/gem.c cvs rdiff -u -r1.14 -r1.15 src/sys/dev/ic/gemreg.h cvs rdiff -u -r1.23 -r1.24 src/sys/dev/ic/gemvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ic/gem.c diff -u src/sys/dev/ic/gem.c:1.98 src/sys/dev/ic/gem.c:1.99 --- src/sys/dev/ic/gem.c:1.98 Thu Feb 2 19:43:03 2012 +++ src/sys/dev/ic/gem.c Mon Jul 2 11:23:40 2012 @@ -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 fla 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 u #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); } Index: src/sys/dev/ic/gemreg.h diff -u src/sys/dev/ic/gemreg.h:1.14 src/sys/dev/ic/gemreg.h:1.15 --- src/sys/dev/ic/gemreg.h:1.14 Mon Sep 15 19:43:24 2008 +++ src/sys/dev/ic/gemreg.h Mon Jul 2 11:23:40 2012 @@ -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 Index: src/sys/dev/ic/gemvar.h diff -u src/sys/dev/ic/gemvar.h:1.23 src/sys/dev/ic/gemvar.h:1.24 --- src/sys/dev/ic/gemvar.h:1.23 Thu Feb 2 19:43:03 2012 +++ src/sys/dev/ic/gemvar.h Mon Jul 2 11:23:40 2012 @@ -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; };