Module Name:    src
Committed By:   bouyer
Date:           Sat Sep 15 09:32:36 UTC 2012

Modified Files:
        src/sys/dev/ic [netbsd-5]: gem.c gemreg.h gemvar.h

Log Message:
Pull up following revision(s) (requested by jdc in ticket #1789):
        sys/dev/ic/gem.c: revision 1.99 via patch
        sys/dev/ic/gemvar.h: revision 1.24 via patch
        sys/dev/ic/gemreg.h: revision 1.15 via patch
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.78.4.2 -r1.78.4.3 src/sys/dev/ic/gem.c
cvs rdiff -u -r1.14 -r1.14.4.1 src/sys/dev/ic/gemreg.h
cvs rdiff -u -r1.18 -r1.18.20.1 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.78.4.2 src/sys/dev/ic/gem.c:1.78.4.3
--- src/sys/dev/ic/gem.c:1.78.4.2	Sat Jun 18 16:40:31 2011
+++ src/sys/dev/ic/gem.c	Sat Sep 15 09:32:36 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: gem.c,v 1.78.4.2 2011/06/18 16:40:31 bouyer Exp $ */
+/*	$NetBSD: gem.c,v 1.78.4.3 2012/09/15 09:32:36 bouyer Exp $ */
 
 /*
  *
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.78.4.2 2011/06/18 16:40:31 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.78.4.3 2012/09/15 09:32:36 bouyer Exp $");
 
 #include "opt_inet.h"
 #include "bpfilter.h"
@@ -93,6 +93,8 @@ 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_shutdown(void *);
 void		gem_pcs_start(struct gem_softc *sc);
 void		gem_pcs_stop(struct gem_softc *sc, int);
@@ -504,6 +506,8 @@ gem_attach(sc, 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);
 	return;
 
 	/*
@@ -691,6 +695,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);
@@ -775,7 +781,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);
 }
 
 /*
@@ -1762,6 +1768,8 @@ gem_rint(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);
@@ -2143,12 +2151,20 @@ gem_intr(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);
@@ -2177,6 +2193,61 @@ gem_intr(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(ifp)
@@ -2194,6 +2265,7 @@ gem_watchdog(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.14.4.1
--- src/sys/dev/ic/gemreg.h:1.14	Mon Sep 15 19:43:24 2008
+++ src/sys/dev/ic/gemreg.h	Sat Sep 15 09:32:36 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: gemreg.h,v 1.14 2008/09/15 19:43:24 jdc Exp $ */
+/*	$NetBSD: gemreg.h,v 1.14.4.1 2012/09/15 09:32:36 bouyer 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.18 src/sys/dev/ic/gemvar.h:1.18.20.1
--- src/sys/dev/ic/gemvar.h:1.18	Fri Feb  1 10:53:25 2008
+++ src/sys/dev/ic/gemvar.h	Sat Sep 15 09:32:36 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: gemvar.h,v 1.18 2008/02/01 10:53:25 jdc Exp $ */
+/*	$NetBSD: gemvar.h,v 1.18.20.1 2012/09/15 09:32:36 bouyer Exp $ */
 
 /*
  *
@@ -118,6 +118,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 */
@@ -214,6 +215,10 @@ struct gem_softc {
 	struct evcnt sc_ev_rxfull;
 	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;
 };
 
 #ifdef GEM_COUNTERS

Reply via email to