Subject: kern/9675: tlp0: sorry, unable to handle your board
To: None <gnats-bugs@gnats.netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: netbsd-bugs
Date: 03/26/2000 08:21:42
>Number:         9675
>Category:       kern
>Synopsis:       tlp0: sorry, unable to handle your board
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Mar 26 08:15:00 2000
>Last-Modified:
>Originator:     
>Organization:

LIP6

>Release:        -current as of yesterday
>Environment:
	
System: NetBSD armandeche 1.4W NetBSD 1.4W (ARMANDECHE) #2: Sun Mar 26 17:39:32 MEST 2000 bouyer@armandeche:/home/cvs.netbsd.org/src/sys/arch/i386/compile/ARMANDECHE i386


>Description:
	I have a 21140A-based SMC board which used to work fine with the
	TLP driver:
	tlp0 at pci0 dev 9 function 0: DECchip 21140A Ethernet, pass 2.0
	tlp0: interrupting at irq 5
	tlp0: Ethernet address 00:00:c0:f1:ee:e9
	nsphy0 at tlp0 phy 3: DP83840 10/100 media interface, rev. 0
	nsphy0: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto
	tlp0: supplying EUI64: 00:00:c0:ff:fe:f1:ee:e9

	 I haven't upgraded this box for a while (previous kernel
	was from early march) but I had to compile a new kernel from fresh
	sources. With the new kernel I get:
	tlp0 at pci0 dev 9 function 0: DECchip 21140A Ethernet, pass 2.0
	tlp0: sorry, unable to handle your board
	and of course the board is unusable

>How-To-Repeat:
	boot -current in a box with a 21140A-based SMC board; I think other
	boards based on the 21140A may have the same problem.

>Fix:
	I narrowed the problem down to tlp_isv_srom_enaddr() failing.
	dev/ic/tulip.c 1.46->1.47
	dev/pci/if_tlp_pci.c 1.32->1.33
	sys/dev/ic/tulipvar.h 1.28->1.29
	(single change, commit message was:
Determine the size of the SROM by probing the chip using the standard Microwire
protocol.  Eliminates the need for srom_quirks and some other trash.)
	and my board is working again.
	I provide the diff as a workaroud for peoples who have the same
	problem, but obviously this is not the rigth solution :)
	
Index: ic/tulip.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/tulip.c,v
retrieving revision 1.56
diff -u -r1.56 tulip.c
--- tulip.c	2000/03/23 07:01:33	1.56
+++ tulip.c	2000/03/26 16:02:57
@@ -1,4 +1,4 @@
-/*	$NetBSD: tulip.c,v 1.56 2000/03/23 07:01:33 thorpej Exp $	*/
+/*	$NetBSD: tulip.c,v 1.46 2000/03/06 21:02:01 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
@@ -153,7 +153,6 @@
 int	tlp_add_rxbuf __P((struct tulip_softc *, int));
 void	tlp_idle __P((struct tulip_softc *, u_int32_t));
 void	tlp_srom_idle __P((struct tulip_softc *));
-int	tlp_srom_size __P((struct tulip_softc *));
 
 int	tlp_enable __P((struct tulip_softc *));
 void	tlp_disable __P((struct tulip_softc *));
@@ -630,9 +629,6 @@
 	shutdownhook_disestablish(sc->sc_sdhook);
 	powerhook_disestablish(sc->sc_powerhook);
 
-	if (sc->sc_srom)
-		free(sc->sc_srom, M_DEVBUF);
-
 	return (0);
 }
 
@@ -2011,7 +2007,7 @@
 #define	SROM_EMIT(sc, x)						\
 do {									\
 	TULIP_WRITE((sc), CSR_MIIROM, (x));				\
-	delay(2);							\
+	delay(1);							\
 } while (0)
 
 /*
@@ -2037,8 +2033,8 @@
 
 	SROM_EMIT(sc, miirom|MIIROM_SROMSK);
 
-	/* Strobe the clock 32 times. */
-	for (i = 0; i < 32; i++) {
+	/* Strobe the clock 25 times. */
+	for (i = 0; i < 25; i++) {
 		SROM_EMIT(sc, miirom);
 		SROM_EMIT(sc, miirom|MIIROM_SROMSK);
 	}
@@ -2052,90 +2048,22 @@
 }
 
 /*
- * tlp_srom_size:
- *
- *	Determine the number of address bits in the SROM.
- */
-int
-tlp_srom_size(sc)
-	struct tulip_softc *sc;
-{
-	u_int32_t miirom;
-	int x;
-
-	/* Select the SROM. */
-	miirom = MIIROM_SR;
-	SROM_EMIT(sc, miirom);
-
-	miirom |= MIIROM_RD;
-	SROM_EMIT(sc, miirom);
-
-	/* Send CHIP SELECT for one clock tick. */
-	miirom |= MIIROM_SROMCS;
-	SROM_EMIT(sc, miirom);
-
-	/* Shift in the READ opcode. */
-	for (x = 3; x > 0; x--) {
-		if (TULIP_SROM_OPC_READ & (1 << (x - 1)))
-			miirom |= MIIROM_SROMDI;
-		else
-			miirom &= ~MIIROM_SROMDI;
-		SROM_EMIT(sc, miirom);
-		SROM_EMIT(sc, miirom|MIIROM_SROMSK);
-		SROM_EMIT(sc, miirom);
-	}
-
-	/* Shift in address and look for dummy 0 bit. */
-	for (x = 1; x <= 12; x++) {
-		miirom &= ~MIIROM_SROMDI;
-		SROM_EMIT(sc, miirom);
-		SROM_EMIT(sc, miirom|MIIROM_SROMSK);
-		if (!TULIP_ISSET(sc, CSR_MIIROM, MIIROM_SROMDO))
-			break;
-		SROM_EMIT(sc, miirom);
-	}
-
-	/* Clear CHIP SELECT. */
-	miirom &= ~MIIROM_SROMCS;
-	SROM_EMIT(sc, miirom);
-
-	/* Deselect the SROM. */
-	SROM_EMIT(sc, 0);
-
-	if (x > 12) {
-		printf("%s: failed to find SROM size\n", sc->sc_dev.dv_xname);
-		return (0);
-	} else {
-#ifdef TLP_DEBUG
-		printf("%s: SROM size is 2^%d*16 bits (%d bytes)\n",
-			sc->sc_dev.dv_xname, x, (1 << (x + 4)) >> 3);
-#endif
-		return (x);
-	}
-}
-
-/*
  * tlp_read_srom:
  *
  *	Read the Tulip SROM.
  */
-int
-tlp_read_srom(sc)
+void
+tlp_read_srom(sc, word, wordcnt, data)
 	struct tulip_softc *sc;
+	int word, wordcnt;
+	u_int8_t *data;
 {
-	int size;
 	u_int32_t miirom;
 	u_int16_t datain;
 	int i, x;
 
 	tlp_srom_idle(sc);
 
-	sc->sc_srom_addrbits = tlp_srom_size(sc);
-	if (sc->sc_srom_addrbits == 0)
-		return (0);
-	size = TULIP_ROM_SIZE(sc->sc_srom_addrbits);
-	sc->sc_srom = malloc(size, M_DEVBUF, M_NOWAIT);
-
 	/* Select the SROM. */
 	miirom = MIIROM_SR;
 	SROM_EMIT(sc, miirom);
@@ -2143,7 +2071,7 @@
 	miirom |= MIIROM_RD;
 	SROM_EMIT(sc, miirom);
 
-	for (i = 0; i < size; i += 2) {
+	for (i = 0; i < wordcnt; i++) {
 		/* Send CHIP SELECT for one clock tick. */
 		miirom |= MIIROM_SROMCS;
 		SROM_EMIT(sc, miirom);
@@ -2161,7 +2089,7 @@
 
 		/* Shift in address. */
 		for (x = sc->sc_srom_addrbits; x > 0; x--) {
-			if (i & (1 << x))
+			if ((word + i) & (1 << (x - 1)))
 				miirom |= MIIROM_SROMDI;
 			else
 				miirom &= ~MIIROM_SROMDI;
@@ -2179,8 +2107,8 @@
 				datain |= (1 << (x - 1));
 			SROM_EMIT(sc, miirom);
 		}
-		sc->sc_srom[i] = datain & 0xff;
-		sc->sc_srom[i + 1] = datain >> 8;
+		data[2 * i] = datain & 0xff;
+		data[(2 * i) + 1] = datain >> 8;
 
 		/* Clear CHIP SELECT. */
 		miirom &= ~MIIROM_SROMCS;
@@ -2192,18 +2120,6 @@
 
 	/* ...and idle it. */
 	tlp_srom_idle(sc);
-
-#if 0
-	printf("SROM CONTENTS:");
-	for (i = 0; i < size; i++) {
-		if ((i % 8) == 0)
-			printf("\n\t");
-		printf("0x%02x ", sc->sc_srom[i]);
-	}
-	printf("\n");
-#endif
-
-	return (1);
 }
 
 #undef SROM_EMIT
Index: ic/tulipvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/tulipvar.h,v
retrieving revision 1.32
diff -u -r1.32 tulipvar.h
--- tulipvar.h	2000/03/23 07:01:33	1.32
+++ tulipvar.h	2000/03/26 16:02:57
@@ -1,4 +1,4 @@
-/*	$NetBSD: tulipvar.h,v 1.32 2000/03/23 07:01:33 thorpej Exp $	*/
+/*	$NetBSD: tulipvar.h,v 1.28 2000/02/01 22:54:48 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
@@ -278,7 +278,7 @@
 	/*
 	 * Contents of the SROM.
 	 */
-	u_int8_t *sc_srom;
+	u_int8_t sc_srom[TULIP_MAX_ROM_SIZE];
 	int sc_srom_addrbits;
 
 	/*
@@ -515,7 +515,7 @@
 int	tlp_activate __P((struct device *, enum devact));
 int	tlp_detach __P((struct tulip_softc *));
 int	tlp_intr __P((void *));
-int	tlp_read_srom __P((struct tulip_softc *));
+void	tlp_read_srom __P((struct tulip_softc *, int, int, u_int8_t *));
 int	tlp_srom_crcok __P((const u_int8_t *));
 int	tlp_isv_srom __P((const u_int8_t *));
 int	tlp_isv_srom_enaddr __P((struct tulip_softc *, u_int8_t *));
Index: pci/if_tlp_pci.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/pci/if_tlp_pci.c,v
retrieving revision 1.35
diff -u -r1.35 if_tlp_pci.c
--- if_tlp_pci.c	2000/03/23 22:23:03	1.35
+++ if_tlp_pci.c	2000/03/26 16:02:57
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_tlp_pci.c,v 1.35 2000/03/23 22:23:03 mycroft Exp $	*/
+/*	$NetBSD: if_tlp_pci.c,v 1.32 2000/01/26 16:51:11 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
@@ -245,6 +245,26 @@
 	{ NULL,				{ 0, 0, 0 } }
 };
 
+/*
+ * Even more disgusting... some 21143 implementations (namely Cobalt's)
+ * which should have a 8-address-bit SROM actually only have a
+ * 6-address-bit SROM (even though it's rev 4.1!).  Broken!  This
+ * quirk detects that.
+ */
+#define	TPSQ_NOMATCH			0
+#define	TPSQ_CONTINUE			1
+#define	TPSQ_READ_AGAIN_AND_CONTINUE	2
+
+typedef	int (*tlp_pci_srom_quirk_t) __P((struct tulip_pci_softc *));
+
+int	tlp_pci_cobalt_21143_srom_quirks __P((struct tulip_pci_softc *));
+int	tlp_pci_21143_srom_quirks __P((struct tulip_pci_softc *));
+
+tlp_pci_srom_quirk_t tlp_pci_21143_srom_quirks_list[] = {
+	tlp_pci_cobalt_21143_srom_quirks,
+	tlp_pci_21143_srom_quirks,		/* MUST BE AT THE END */
+};
+
 int	tlp_pci_shared_intr __P((void *));
 
 const struct tulip_pci_product *tlp_pci_lookup
@@ -380,6 +400,13 @@
 	sc->sc_flags |= TULIPF_ENABLED;
 
 	/*
+	 * Some chips have a 128 byte SROM (6 address bits), and some
+	 * have a 512 byte SROM (8 address bits).  Default to 6; we'll
+	 * adjust below.
+	 */
+	sc->sc_srom_addrbits = 6;
+
+	/*
 	 * Get revision info, and set some chip-specific variables.
 	 */
 	sc->sc_rev = PCI_REVISION(pa->pa_class);
@@ -542,12 +569,12 @@
 	/*
 	 * Read the contents of the Ethernet Address ROM/SROM.
 	 */
+ read_srom_again:
+	memset(sc->sc_srom, 0, sizeof(sc->sc_srom));
 	switch (sc->sc_chip) {
 	case TULIP_CHIP_21040:
-		sc->sc_srom_addrbits = 6;
-		sc->sc_srom = malloc(TULIP_ROM_SIZE(6), M_DEVBUF, M_NOWAIT);
 		TULIP_WRITE(sc, CSR_MIIROM, MIIROM_SROMCS);
-		for (i = 0; i < TULIP_ROM_SIZE(6); i++) {
+		for (i = 0; i < TULIP_ROM_SIZE(sc->sc_srom_addrbits); i++) {
 			for (j = 0; j < 10000; j++) {
 				val = TULIP_READ(sc, CSR_MIIROM);
 				if ((val & MIIROM_DN) == 0)
@@ -560,17 +587,16 @@
 	case TULIP_CHIP_82C168:
 	case TULIP_CHIP_82C169:
 	    {
-		sc->sc_srom_addrbits = 2;
-		sc->sc_srom = malloc(TULIP_ROM_SIZE(2), M_DEVBUF, M_NOWAIT);
+		u_int16_t *rombuf = (u_int16_t *)sc->sc_srom;
 
 		/*
 		 * The Lite-On PNIC stores the Ethernet address in
 		 * the first 3 words of the EEPROM.  EEPROM access
 		 * is not like the other Tulip chips.
 		 */
-		for (i = 0; i < 6; i += 2) {
+		for (i = 0; i < 3; i++) {
 			TULIP_WRITE(sc, CSR_PNIC_SROMCTL,
-			    PNIC_SROMCTL_READ | (i >> 1));
+			    PNIC_SROMCTL_READ | i);
 			for (j = 0; j < 500; j++) {
 				delay(2);
 				val = TULIP_READ(sc, CSR_MIIROM);
@@ -582,17 +608,23 @@
 				    sc->sc_dev.dv_xname);
 				return;
 			}
-			val &= PNIC_MIIROM_DATA;
-			sc->sc_srom[i] = val >> 8;
-			sc->sc_srom[i + 1] = val & 0xff;
+			rombuf[i] = bswap16(val & PNIC_MIIROM_DATA);
 		}
 		break;
 	    }
 
 	default:
-		if (tlp_read_srom(sc) == 0)
-			goto cant_cope;
-		break;
+		tlp_read_srom(sc, 0, TULIP_ROM_SIZE(sc->sc_srom_addrbits) >> 1,
+		    sc->sc_srom);
+#if 0
+		printf("SROM CONTENTS:");
+		for (i = 0; i < TULIP_ROM_SIZE(sc->sc_srom_addrbits); i++) {
+			if ((i % 8) == 0)
+				printf("\n\t");
+			printf("0x%02x ", sc->sc_srom[i]);
+		}
+		printf("\n");
+#endif
 	}
 
 	/*
@@ -612,8 +644,11 @@
 		/*
 		 * Parse the Ethernet Address ROM.
 		 */
-		if (tlp_parse_old_srom(sc, enaddr) == 0)
-			goto cant_cope;
+		if (tlp_parse_old_srom(sc, enaddr) == 0) {
+			printf("%s: unable to decode Ethernet Address ROM\n",
+			    sc->sc_dev.dv_xname);
+			return;
+		}
 
 		/*
 		 * If we have a slaved ROM, adjust the Ethernet address.
@@ -648,8 +683,11 @@
 			 * Not an ISV SROM; try the old DEC Ethernet Address
 			 * ROM format.
 			 */
-			if (tlp_parse_old_srom(sc, enaddr) == 0)
-				goto cant_cope;
+			if (tlp_parse_old_srom(sc, enaddr) == 0) {
+				printf("%s: unable to decode Ethernet "
+				    "Address ROM\n", sc->sc_dev.dv_xname);
+				return;
+			}
 		}
 
 		/*
@@ -672,8 +710,10 @@
 			 * Not an ISV SROM; try the old DEC Ethernet Address
 			 * ROM format.
 			 */
-			if (tlp_parse_old_srom(sc, enaddr) == 0)
+			if (tlp_parse_old_srom(sc, enaddr) == 0) {
+				printf("Bad eth addr\n");
 				goto cant_cope;
+			}
 		} else {
 			/*
 			 * We start out with the 2114x ISV media switch.
@@ -699,12 +739,37 @@
 	case TULIP_CHIP_21143:
 		/* Check for new format SROM. */
 		if (tlp_isv_srom_enaddr(sc, enaddr) == 0) {
+			if (sc->sc_chip == TULIP_CHIP_21143) {
+				tlp_pci_srom_quirk_t q;
+
+				/*
+				 * Check for SROM quirkiness.
+				 */
+				for (i = 0; sc->sc_srom_addrbits != 8; i++) {
+					q = tlp_pci_21143_srom_quirks_list[i];
+					switch ((*q)(psc)) {
+					case TPSQ_NOMATCH:
+						continue;
+
+					case TPSQ_CONTINUE:
+						break;
+
+					case TPSQ_READ_AGAIN_AND_CONTINUE:
+						goto read_srom_again;
+					}
+					break;  /* for TPSQ_CONTINUE */
+				}
+			}
+
 			/*
 			 * Not an ISV SROM; try the old DEC Ethernet Address
 			 * ROM format.
 			 */
-			if (tlp_parse_old_srom(sc, enaddr) == 0)
+			 if (tlp_parse_old_srom(sc, enaddr) == 0) {
+				printf("%s: unable to decode Ethernet "
+				    "Address ROM\n", sc->sc_dev.dv_xname);
 				goto cant_cope;
+			}
 		} else {
 			/*
 			 * We start out with the 2114x ISV media switch.
@@ -1092,4 +1157,43 @@
 	 * Cobalt Networks interfaces are just MII-on-SIO.
 	 */
 	sc->sc_mediasw = &tlp_sio_mii_mediasw;
+}
+
+int
+tlp_pci_cobalt_21143_srom_quirks(psc)
+	struct tulip_pci_softc *psc;
+{
+	struct tulip_softc *sc = &psc->sc_tulip;
+
+	/*
+	 * Check for broken Cobalt interface; pass 4.1 Tulip with
+	 * only 6-bit SROM and Ethernet address in first 6 bytes.
+	 */
+	if (sc->sc_srom[0] == 0x00 &&
+	    sc->sc_srom[1] == 0x10 &&
+	    sc->sc_srom[2] == 0xe0)
+		return (TPSQ_CONTINUE);
+
+	return (TPSQ_NOMATCH);
+}
+
+int
+tlp_pci_21143_srom_quirks(psc)
+	struct tulip_pci_softc *psc;
+{
+	struct tulip_softc *sc = &psc->sc_tulip;
+
+	/*
+	 * Pass 4.1 21143s are supposed to have an 8-address-bit SROM.
+	 * We need to read them again.
+	 */
+	if (sc->sc_rev >= 0x41) {
+		sc->sc_srom_addrbits = 8;
+		return (TPSQ_READ_AGAIN_AND_CONTINUE);
+	}
+
+	/*
+	 * ...otherwise, what we read is just fine.
+	 */
+	return (TPSQ_CONTINUE);
 }
>Audit-Trail:
>Unformatted: