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;
 };
 

Reply via email to