Module Name:    src
Committed By:   msaitoh
Date:           Tue Apr  7 18:23:37 UTC 2009

Modified Files:
        src/sys/dev/pci: if_wm.c if_wmreg.h

Log Message:
Fix about TBI mode. This fix doesn't influence MII mode.
 - Fix SWDPIN(1)'s polarity on some chips.
 - Fix flow control stuff (includes PR#32009).
 - Stop RXCFG storm. It ocours easily.
 - And more fix about autonego.

Tested on PRO/1000T, PRO/1000XF and PRO/1000 MF.


To generate a diff of this commit:
cvs rdiff -u -r1.172 -r1.173 src/sys/dev/pci/if_wm.c
cvs rdiff -u -r1.26 -r1.27 src/sys/dev/pci/if_wmreg.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/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.172 src/sys/dev/pci/if_wm.c:1.173
--- src/sys/dev/pci/if_wm.c:1.172	Tue Mar 31 06:58:55 2009
+++ src/sys/dev/pci/if_wm.c	Tue Apr  7 18:23:37 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.172 2009/03/31 06:58:55 darran Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.173 2009/04/07 18:23:37 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.172 2009/03/31 06:58:55 darran Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.173 2009/04/07 18:23:37 msaitoh Exp $");
 
 #include "bpfilter.h"
 #include "rnd.h"
@@ -257,6 +257,8 @@
 	WM_T_ICH10,			/* ICH10 LAN */
 } wm_chip_type;
 
+#define WM_LINKUP_TIMEOUT	50
+
 /*
  * Software state per device.
  */
@@ -377,7 +379,10 @@
 	uint32_t sc_pba;		/* prototype PBA register */
 
 	int sc_tbi_linkup;		/* TBI link status */
-	int sc_tbi_anstate;		/* autonegotiation state */
+	int sc_tbi_anegticks;		/* autonegotiation ticks */
+	int sc_tbi_ticks;		/* tbi ticks */
+	int sc_tbi_nrxcfg;		/* count of ICR_RXCFG */
+	int sc_tbi_lastnrxcfg;		/* count of ICR_RXCFG (on last tick) */
 
 	int sc_mchash_type;		/* multicast filter offset */
 
@@ -594,6 +599,7 @@
 #endif
 static int	wm_check_mng_mode_generic(struct wm_softc *);
 static void	wm_get_hw_control(struct wm_softc *);
+static int	wm_check_for_link(struct wm_softc *);
 
 CFATTACH_DECL_NEW(wm, sizeof(struct wm_softc),
     wm_match, wm_attach, NULL, NULL);
@@ -2780,6 +2786,8 @@
 {
 	uint32_t status;
 
+	DPRINTF(WM_DEBUG_LINK, ("%s: %s:\n", device_xname(sc->sc_dev),
+		__func__));
 	/*
 	 * If we get a link status interrupt on a 1000BASE-T
 	 * device, just fall into the normal MII tick path.
@@ -2836,22 +2844,18 @@
 		return;
 	}
 
-	/*
-	 * If we are now receiving /C/, check for link again in
-	 * a couple of link clock ticks.
-	 */
-	if (icr & ICR_RXCFG) {
-		DPRINTF(WM_DEBUG_LINK, ("%s: LINK: receiving /C/\n",
-		    device_xname(sc->sc_dev)));
-		sc->sc_tbi_anstate = 2;
-	}
-
+	status = CSR_READ(sc, WMREG_STATUS);
 	if (icr & ICR_LSC) {
-		status = CSR_READ(sc, WMREG_STATUS);
 		if (status & STATUS_LU) {
 			DPRINTF(WM_DEBUG_LINK, ("%s: LINK: LSC -> up %s\n",
 			    device_xname(sc->sc_dev),
 			    (status & STATUS_FD) ? "FDX" : "HDX"));
+			/*
+			 * NOTE: CTRL will update TFCE and RFCE automatically,
+			 * so we should update sc->sc_ctrl
+			 */
+			
+			sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL);
 			sc->sc_tctl &= ~TCTL_COLD(0x3ff);
 			sc->sc_fcrtl &= ~FCRTL_XONE;
 			if (status & STATUS_FD)
@@ -2860,7 +2864,7 @@
 			else
 				sc->sc_tctl |=
 				    TCTL_COLD(TX_COLLISION_DISTANCE_HDX);
-			if (CSR_READ(sc, WMREG_CTRL) & CTRL_TFCE)
+			if (sc->sc_ctrl & CTRL_TFCE)
 				sc->sc_fcrtl |= FCRTL_XONE;
 			CSR_WRITE(sc, WMREG_TCTL, sc->sc_tctl);
 			CSR_WRITE(sc, (sc->sc_type < WM_T_82543) ?
@@ -2872,8 +2876,12 @@
 			    device_xname(sc->sc_dev)));
 			sc->sc_tbi_linkup = 0;
 		}
-		sc->sc_tbi_anstate = 2;
 		wm_tbi_set_linkled(sc);
+	} else if (icr & ICR_RXCFG) {
+		DPRINTF(WM_DEBUG_LINK, ("%s: LINK: receiving /C/\n",
+		    device_xname(sc->sc_dev)));
+		sc->sc_tbi_nrxcfg++;
+		wm_check_for_link(sc);
 	} else if (icr & ICR_RXSEQ) {
 		DPRINTF(WM_DEBUG_LINK,
 		    ("%s: LINK: Receive sequence error\n",
@@ -3305,6 +3313,9 @@
 		reg |= RXCSUM_IPV6OFL | RXCSUM_TUOFL;
 	CSR_WRITE(sc, WMREG_RXCSUM, reg);
 
+	/* Reset TBI's RXCFG count */
+	sc->sc_tbi_nrxcfg = sc->sc_tbi_lastnrxcfg = 0;
+
 	/*
 	 * Set up the interrupt registers.
 	 */
@@ -3461,6 +3472,11 @@
 	if (sc->sc_flags & WM_F_HAS_MII) {
 		/* Down the MII. */
 		mii_down(&sc->sc_mii);
+	} else {
+#if 0
+		/* Should we clear PHY's status properly? */
+		wm_reset(sc);
+#endif
 	}
 
 	/* Stop the transmit and receive processes. */
@@ -4095,6 +4111,7 @@
 static void
 wm_tbi_mediainit(struct wm_softc *sc)
 {
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
 	const char *sep = "";
 
 	if (sc->sc_type < WM_T_82543)
@@ -4102,6 +4119,12 @@
 	else
 		sc->sc_tipg = TIPG_LG_DFLT;
 
+	sc->sc_tbi_anegticks = 5;
+
+	/* Initialize our media structures */
+	sc->sc_mii.mii_ifp = ifp;
+
+	sc->sc_ethercom.ec_mii = &sc->sc_mii;
 	ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, wm_tbi_mediachange,
 	    wm_tbi_mediastatus);
 
@@ -4143,12 +4166,13 @@
 wm_tbi_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
 {
 	struct wm_softc *sc = ifp->if_softc;
-	uint32_t ctrl;
+	uint32_t ctrl, status;
 
 	ifmr->ifm_status = IFM_AVALID;
 	ifmr->ifm_active = IFM_ETHER;
 
-	if (sc->sc_tbi_linkup == 0) {
+	status = CSR_READ(sc, WMREG_STATUS);
+	if ((status & STATUS_LU) == 0) {
 		ifmr->ifm_active |= IFM_NONE;
 		return;
 	}
@@ -4177,18 +4201,20 @@
 	uint32_t status;
 	int i;
 
-	sc->sc_txcw = ife->ifm_data;
-	DPRINTF(WM_DEBUG_LINK,("%s: sc_txcw = 0x%x on entry\n",
-		    device_xname(sc->sc_dev),sc->sc_txcw));
+	sc->sc_txcw = 0;
 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
 	    (sc->sc_mii.mii_media.ifm_media & IFM_FLOW) != 0)
-		sc->sc_txcw |= ANAR_X_PAUSE_SYM | ANAR_X_PAUSE_ASYM;
+		sc->sc_txcw |= TXCW_SYM_PAUSE | TXCW_ASYM_PAUSE;
 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { 
-		sc->sc_txcw |= TXCW_ANE; 
+		sc->sc_txcw |= TXCW_ANE;
 	} else {
-		/*If autonegotiation is turned off, force link up and turn on full duplex*/
+		/*
+		 * If autonegotiation is turned off, force link up and turn on
+		 * full duplex
+		 */
 		sc->sc_txcw &= ~TXCW_ANE;
 		sc->sc_ctrl |= CTRL_SLU | CTRL_FD;
+		sc->sc_ctrl &= ~(CTRL_TFCE | CTRL_RFCE);
 		CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
 		delay(1000);
 	}
@@ -4198,10 +4224,6 @@
 	CSR_WRITE(sc, WMREG_TXCW, sc->sc_txcw);
 	delay(10000);
 
-	/* NOTE: CTRL will update TFCE and RFCE automatically. */
-
-	sc->sc_tbi_anstate = 0;
-
 	i = CSR_READ(sc, WMREG_CTRL) & CTRL_SWDPIN(1);
 	DPRINTF(WM_DEBUG_LINK,("%s: i = 0x%x\n", device_xname(sc->sc_dev),i));
 
@@ -4209,7 +4231,7 @@
 	 * On 82544 chips and later, the CTRL_SWDPIN(1) bit will be set if the
 	 * optics detect a signal, 0 if they don't.
 	 */
-	if (((i != 0) && (sc->sc_type >= WM_T_82544)) || (i == 0)) {
+	if (((i != 0) && (sc->sc_type > WM_T_82544)) || (i == 0)) {
 		/* Have signal; wait for the link to come up. */
 
 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
@@ -4224,7 +4246,7 @@
 			delay(1000);
 		}
 
-		for (i = 0; i < 50; i++) {
+		for (i = 0; i < WM_LINKUP_TIMEOUT; i++) {
 			delay(10000);
 			if (CSR_READ(sc, WMREG_STATUS) & STATUS_LU)
 				break;
@@ -4243,6 +4265,12 @@
 			    ("%s: LINK: set media -> link up %s\n",
 			    device_xname(sc->sc_dev),
 			    (status & STATUS_FD) ? "FDX" : "HDX"));
+
+			/*
+			 * NOTE: CTRL will update TFCE and RFCE automatically,
+			 * so we should update sc->sc_ctrl
+			 */
+			sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL);
 			sc->sc_tctl &= ~TCTL_COLD(0x3ff);
 			sc->sc_fcrtl &= ~FCRTL_XONE;
 			if (status & STATUS_FD)
@@ -4259,6 +4287,8 @@
 				      sc->sc_fcrtl);
 			sc->sc_tbi_linkup = 1;
 		} else {
+			if (i == WM_LINKUP_TIMEOUT)
+				wm_check_for_link(sc);
 			/* Link is down. */
 			DPRINTF(WM_DEBUG_LINK,
 			    ("%s: LINK: set media -> link down\n",
@@ -4290,6 +4320,9 @@
 	else
 		sc->sc_ctrl &= ~CTRL_SWDPIN(0);
 
+	/* 82540 or newer devices are active low */
+	sc->sc_ctrl ^= (sc->sc_type >= WM_T_82540) ? CTRL_SWDPIN(0) : 0;
+
 	CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
 }
 
@@ -4301,49 +4334,58 @@
 static void
 wm_tbi_check_link(struct wm_softc *sc)
 {
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	struct ifmedia_entry *ife = sc->sc_mii.mii_media.ifm_cur;
 	uint32_t rxcw, ctrl, status;
 
-	if (sc->sc_tbi_anstate == 0)
-		return;
-	else if (sc->sc_tbi_anstate > 1) {
-		DPRINTF(WM_DEBUG_LINK,
-		    ("%s: LINK: anstate %d\n", device_xname(sc->sc_dev),
-		    sc->sc_tbi_anstate));
-		sc->sc_tbi_anstate--;
-		return;
-	}
-
-	sc->sc_tbi_anstate = 0;
+	status = CSR_READ(sc, WMREG_STATUS);
 
 	rxcw = CSR_READ(sc, WMREG_RXCW);
 	ctrl = CSR_READ(sc, WMREG_CTRL);
-	status = CSR_READ(sc, WMREG_STATUS);
 
+	/* set link status */
 	if ((status & STATUS_LU) == 0) {
 		DPRINTF(WM_DEBUG_LINK,
 		    ("%s: LINK: checklink -> down\n", device_xname(sc->sc_dev)));
 		sc->sc_tbi_linkup = 0;
-	} else {
+	} else if (sc->sc_tbi_linkup == 0) {
 		DPRINTF(WM_DEBUG_LINK,
 		    ("%s: LINK: checklink -> up %s\n", device_xname(sc->sc_dev),
 		    (status & STATUS_FD) ? "FDX" : "HDX"));
-		sc->sc_tctl &= ~TCTL_COLD(0x3ff);
-		sc->sc_fcrtl &= ~FCRTL_XONE;
-		if (status & STATUS_FD)
-			sc->sc_tctl |=
-			    TCTL_COLD(TX_COLLISION_DISTANCE_FDX);
-		else
-			sc->sc_tctl |=
-			    TCTL_COLD(TX_COLLISION_DISTANCE_HDX);
-		if (ctrl & CTRL_TFCE)
-			sc->sc_fcrtl |= FCRTL_XONE;
-		CSR_WRITE(sc, WMREG_TCTL, sc->sc_tctl);
-		CSR_WRITE(sc, (sc->sc_type < WM_T_82543) ?
-			      WMREG_OLD_FCRTL : WMREG_FCRTL,
-			      sc->sc_fcrtl);
 		sc->sc_tbi_linkup = 1;
 	}
 
+	if ((sc->sc_ethercom.ec_if.if_flags & IFF_UP)
+	    && ((status & STATUS_LU) == 0)) {
+		sc->sc_tbi_linkup = 0;
+		if (sc->sc_tbi_nrxcfg - sc->sc_tbi_lastnrxcfg > 100) {
+			/* RXCFG storm! */
+			DPRINTF(WM_DEBUG_LINK, ("RXCFG storm! (%d)\n",
+				sc->sc_tbi_nrxcfg - sc->sc_tbi_lastnrxcfg));
+			wm_init(ifp);
+			wm_start(ifp);
+		} else if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
+			/* If the timer expired, retry autonegotiation */
+			if (++sc->sc_tbi_ticks >= sc->sc_tbi_anegticks) {
+				DPRINTF(WM_DEBUG_LINK, ("EXPIRE\n"));
+				sc->sc_tbi_ticks = 0;
+				/*
+				 * Reset the link, and let autonegotiation do
+				 * its thing
+				 */
+				sc->sc_ctrl |= CTRL_LRST;
+				CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
+				delay(1000);
+				sc->sc_ctrl &= ~CTRL_LRST;
+				CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
+				delay(1000);
+				CSR_WRITE(sc, WMREG_TXCW,
+				    sc->sc_txcw & ~TXCW_ANE);
+				CSR_WRITE(sc, WMREG_TXCW, sc->sc_txcw);
+			}
+		}
+	}
+
 	wm_tbi_set_linkled(sc);
 }
 
@@ -5511,3 +5553,74 @@
 		break;
 	}
 }
+
+/* XXX Currently TBI only */
+static int
+wm_check_for_link(struct wm_softc *sc)
+{
+	struct ifmedia_entry *ife = sc->sc_mii.mii_media.ifm_cur;
+	uint32_t rxcw;
+	uint32_t ctrl;
+	uint32_t status;
+	uint32_t sig;
+
+	rxcw = CSR_READ(sc, WMREG_RXCW);
+	ctrl = CSR_READ(sc, WMREG_CTRL);
+	status = CSR_READ(sc, WMREG_STATUS);
+
+	sig = (sc->sc_type > WM_T_82544) ? CTRL_SWDPIN(1) : 0;
+
+	DPRINTF(WM_DEBUG_LINK, ("%s: %s: sig = %d, status_lu = %d, rxcw_c = %d\n",
+		device_xname(sc->sc_dev), __func__,
+		((ctrl & CTRL_SWDPIN(1)) == sig),
+		((status & STATUS_LU) != 0),
+		((rxcw & RXCW_C) != 0)
+		    ));
+
+	/*
+	 * SWDPIN   LU RXCW
+	 *      0    0    0
+	 *      0    0    1	(should not happen)
+	 *      0    1    0	(should not happen)
+	 *      0    1    1	(should not happen)
+	 *      1    0    0	Disable autonego and force linkup
+	 *      1    0    1	got /C/ but not linkup yet
+	 *      1    1    0	(linkup)
+	 *      1    1    1	If IFM_AUTO, back to autonego
+	 *
+	 */
+	if (((ctrl & CTRL_SWDPIN(1)) == sig)
+	    && ((status & STATUS_LU) == 0)
+	    && ((rxcw & RXCW_C) == 0)) {
+		DPRINTF(WM_DEBUG_LINK, ("%s: force linkup and fullduplex\n",
+			__func__));
+		sc->sc_tbi_linkup = 0;
+		/* Disable auto-negotiation in the TXCW register */
+		CSR_WRITE(sc, WMREG_TXCW, (sc->sc_txcw & ~TXCW_ANE));
+
+		/*
+		 * Force link-up and also force full-duplex.
+		 *
+		 * NOTE: CTRL was updated TFCE and RFCE automatically,
+		 * so we should update sc->sc_ctrl
+		 */
+		sc->sc_ctrl = ctrl | CTRL_SLU | CTRL_FD;
+		CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
+	} else if(((status & STATUS_LU) != 0)
+	    && ((rxcw & RXCW_C) != 0)
+	    && (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO)) {
+		sc->sc_tbi_linkup = 1;
+		DPRINTF(WM_DEBUG_LINK, ("%s: go back to autonego\n",
+			__func__));
+		CSR_WRITE(sc, WMREG_TXCW, sc->sc_txcw);
+		CSR_WRITE(sc, WMREG_CTRL, (ctrl & ~CTRL_SLU));
+	} else if (((ctrl & CTRL_SWDPIN(1)) == sig)
+	    && ((rxcw & RXCW_C) != 0)) {
+		DPRINTF(WM_DEBUG_LINK, ("/C/"));
+	} else {
+		DPRINTF(WM_DEBUG_LINK, ("%s: %x,%x,%x\n", __func__, rxcw, ctrl,
+			status));
+	}
+
+	return 0;
+}

Index: src/sys/dev/pci/if_wmreg.h
diff -u src/sys/dev/pci/if_wmreg.h:1.26 src/sys/dev/pci/if_wmreg.h:1.27
--- src/sys/dev/pci/if_wmreg.h:1.26	Fri Mar 20 07:29:15 2009
+++ src/sys/dev/pci/if_wmreg.h	Tue Apr  7 18:23:37 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wmreg.h,v 1.26 2009/03/20 07:29:15 msaitoh Exp $	*/
+/*	$NetBSD: if_wmreg.h,v 1.27 2009/04/07 18:23:37 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 2001 Wasabi Systems, Inc.
@@ -482,6 +482,8 @@
 
 #define	WMREG_TXCW	0x0178	/* Transmit Configuration Word (TBI mode) */
 	/* See MII ANAR_X bits. */
+#define	TXCW_SYM_PAUSE	(1U << 7)	/* sym pause request */
+#define	TXCW_ASYM_PAUSE	(1U << 8)	/* asym pause request */
 #define	TXCW_TxConfig	(1U << 30)	/* Tx Config */
 #define	TXCW_ANE	(1U << 31)	/* Autonegotiate */
 

Reply via email to