Subject: kern/35797: Fixes for if_wm.c which fix media link issues with fiber-based cards
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Brian Buhrow <buhrow@lothlorien.nfbcal.org>
List: netbsd-bugs
Date: 02/23/2007 01:25:00
>Number:         35797
>Category:       kern
>Synopsis:       Fiber based cards don't allow you to specify media manually, nor does autonegotiation work reliably
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Feb 23 01:25:00 +0000 2007
>Originator:     Brian Buhrow
>Release:        NetBSD 3.0_STABLE
>Organization:
	
>Environment:
	
	
System: NetBSD lothlorien.nfbcal.org 3.0_STABLE NetBSD 3.0_STABLE (NFBNETBSD) #0: Tue Jan 31 14:45:08 PST 2006 buhrow@lothlorien.nfbcal.org:/usr/src/sys/arch/i386/compile/NFBNETBSD i386
Architecture: i386
Machine: i386
>Description:
	

	Fiber based intel cards sometimes work with autonegotiation enabled,
but if you want to nail up a link at a fixed speed and duplex, all bets are
off. For example,
ifconfig wm0 media 1000baseSX mediaopt full-duplex up
with the stock driver yields no link.
The problem is that the driver doesn't know how to properly detect when the
optics are seeing light from the link partner on most versions of the chip.
Also, the driver doesn't really disable autonegotiation, which can confuse
the card as well.
>How-To-Repeat:
	
>Fix:
	
	Here is a patch which fixes both problems:

1.  Allows you to configure media manually, and disable autonegotiation if
you wish.

2.  Properly reset the card so that it will do autonegotiation if you have
it enabled.

	I've been running with these patches in production for a couple of
months, and they work quite well.
Also, although the diff is against the 3.x sources, this portion of the
driver hasn't changed much since then, so it should apply fairly cleanly to
-current and 4.x as well.

Please let me know if  you want me to write a patch against the 4.x and
-current versions.
-thanks
-Brian


Index: if_wm.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_wm.c,v
retrieving revision 1.100.2.5
diff -u -r1.100.2.5 if_wm.c
--- if_wm.c	7 Jul 2006 06:24:40 -0000	1.100.2.5
+++ if_wm.c	23 Feb 2007 00:45:43 -0000
@@ -1725,7 +1725,7 @@
 			 * layer that there are no more slots left.
 			 */
 			DPRINTF(WM_DEBUG_TX,
-			    ("%s: TX: need %d (%) descriptors, have %d\n",
+			    ("%s: TX: need %d (%d) descriptors, have %d\n",
 			    sc->sc_dev.dv_xname, dmamap->dm_nsegs, segs_needed,
 			    sc->sc_txfree - 1));
 			ifp->if_flags |= IFF_OACTIVE;
@@ -1830,11 +1830,11 @@
 				sc->sc_txdescs[nexttx].wtx_fields.wtxu_vlan = 0;
 				lasttx = nexttx;
 
-				DPRINTF(WM_DEBUG_TX,
+				/*DPRINTF(WM_DEBUG_TX,
 				    ("%s: TX: desc %d: low 0x%08x, "
-				     "len 0x%04x\n",
+				     "len 0x%x\n",
 				    sc->sc_dev.dv_xname, nexttx,
-				    curaddr & 0xffffffffU, curlen, curlen));
+				    curaddr & 0xffffffffU, curlen));*/
 			}
 		}
 
@@ -2744,6 +2744,7 @@
 	    TCTL_COLD(TX_COLLISION_DISTANCE_FDX);
 	CSR_WRITE(sc, WMREG_TCTL, sc->sc_tctl);
 
+
 	/* Set the media. */
 	(void) (*sc->sc_mii.mii_media.ifm_change)(ifp);
 
@@ -3385,11 +3386,23 @@
 	int i;
 
 	sc->sc_txcw = ife->ifm_data;
+	DPRINTF(WM_DEBUG_LINK,("%s: sc_txcw = 0x%x on entry\n",
+	sc->sc_dev.dv_xname,sc->sc_txcw));
 	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_ANE;
+	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { 
+		sc->sc_txcw |= TXCW_ANE; 
+	} else {
+		/*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;
+		CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
+		delay(1000);
+	}
 
+	DPRINTF(WM_DEBUG_LINK,("%s: sc_txcw = 0x%x after autoneg check\n",
+	sc->sc_dev.dv_xname,sc->sc_txcw));
 	CSR_WRITE(sc, WMREG_TXCW, sc->sc_txcw);
 	delay(10000);
 
@@ -3397,15 +3410,39 @@
 
 	sc->sc_tbi_anstate = 0;
 
-	if ((CSR_READ(sc, WMREG_CTRL) & CTRL_SWDPIN(1)) == 0) {
+	i = CSR_READ(sc, WMREG_CTRL) & CTRL_SWDPIN(1);
+	DPRINTF(WM_DEBUG_LINK,("%s: i = 0x%x\n",
+	sc->sc_dev.dv_xname,i));
+
+	/* 
+	 *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)) {
 		/* Have signal; wait for the link to come up. */
+
+		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
+			/*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);
+		}
+
 		for (i = 0; i < 50; i++) {
 			delay(10000);
 			if (CSR_READ(sc, WMREG_STATUS) & STATUS_LU)
 				break;
 		}
+		DPRINTF(WM_DEBUG_LINK,("%s: i = %d after waiting for link\n",
+		sc->sc_dev.dv_xname,i));
 
 		status = CSR_READ(sc, WMREG_STATUS);
+		DPRINTF(WM_DEBUG_LINK,("%s: status after final read = 0x%x, STATUS_LU = 0x%x\n",
+		sc->sc_dev.dv_xname,status, STATUS_LU));
 		if (status & STATUS_LU) {
 			/* Link is up. */
 			DPRINTF(WM_DEBUG_LINK,

>Unformatted: