Module Name:    src
Committed By:   snj
Date:           Sun Nov  8 21:58:03 UTC 2009

Modified Files:
        src/sys/dev/pci [netbsd-5]: if_age.c

Log Message:
Pull up following revision(s) (requested by cegger in ticket #1124):
        sys/dev/pci/if_age.c: revision 1.30
- Don't access VPD even if hardware advertised the capability.
  It seems that some revisions of the controllers hang while accessing
  the VPD. Because VPD access routine is now unused, nuke it.
- Let TWSI reload EEPROM if VPD capability is detected. Reloading
  the EEPROM will also set the Ethernet address, so age(4) now reads
  AGE_PAR0 and AGE_PAR1 register to get the Ethernet address. This removes
  removes a lot of hacks and enhance readability a lot.
- Double PHY reset timeout as it takes more time to take the PHY out of
  power-saving state.
- Explicitly check power-saving state by checking undocumented PHY
  registers. If link is not up, poke undocumented registers to take
  PHY out of power-saving state. This is the same thing done by the
  Linux driver.
- Don't rely on auto-clearing feature of master reset bit, just wait
  1ms and check idle status of MAC.
>From FreeBSD via OpenBSD.


To generate a diff of this commit:
cvs rdiff -u -r1.28.2.3 -r1.28.2.4 src/sys/dev/pci/if_age.c

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_age.c
diff -u src/sys/dev/pci/if_age.c:1.28.2.3 src/sys/dev/pci/if_age.c:1.28.2.4
--- src/sys/dev/pci/if_age.c:1.28.2.3	Sun Nov  8 21:55:46 2009
+++ src/sys/dev/pci/if_age.c	Sun Nov  8 21:58:02 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_age.c,v 1.28.2.3 2009/11/08 21:55:46 snj Exp $ */
+/*	$NetBSD: if_age.c,v 1.28.2.4 2009/11/08 21:58:02 snj Exp $ */
 /*	$OpenBSD: if_age.c,v 1.1 2009/01/16 05:00:34 kevlo Exp $	*/
 
 /*-
@@ -31,7 +31,7 @@
 /* Driver for Attansic Technology Corp. L1 Gigabit Ethernet. */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_age.c,v 1.28.2.3 2009/11/08 21:55:46 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_age.c,v 1.28.2.4 2009/11/08 21:58:02 snj Exp $");
 
 #include "bpfilter.h"
 #include "vlan.h"
@@ -97,7 +97,6 @@
 static int	age_mediachange(struct ifnet *);
 
 static int	age_intr(void *);
-static int	age_read_vpd_word(struct age_softc *, uint32_t, uint32_t, uint32_t *);
 static int	age_dma_alloc(struct age_softc *);
 static void	age_dma_free(struct age_softc *);
 static void	age_get_macaddr(struct age_softc *, uint8_t[]);
@@ -558,37 +557,11 @@
 	return 1;
 }
 
-static int
-age_read_vpd_word(struct age_softc *sc, uint32_t vpdc, uint32_t offset,
-    uint32_t *word)
-{
-	int i;
-	pcireg_t rv;
-
-	pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_VPD_ADDRESS(vpdc),
-	    offset << PCI_VPD_ADDRESS_SHIFT);
-	for (i = AGE_TIMEOUT; i > 0; i--) {
-		DELAY(10);
-		rv = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
-		    PCI_VPD_ADDRESS(vpdc));
-		if ((rv & PCI_VPD_OPFLAG) == PCI_VPD_OPFLAG)
-			break;
-	}
-	if (i == 0) {
-		printf("%s: VPD read timeout!\n", device_xname(sc->sc_dev));
-		*word = 0;
-		return ETIMEDOUT;
-	}
-
-	*word = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_VPD_DATAREG(vpdc));
-	return 0;
-}
-
 static void
 age_get_macaddr(struct age_softc *sc, uint8_t eaddr[])
 {
-	uint32_t ea[2], off, reg, word;
-	int vpd_error, match, vpdc;
+	uint32_t ea[2], reg;
+	int i, vpdc;
 
 	reg = CSR_READ_4(sc, AGE_SPI_CTRL);
 	if ((reg & SPI_VPD_ENB) != 0) {
@@ -597,67 +570,22 @@
 		CSR_WRITE_4(sc, AGE_SPI_CTRL, reg);
 	}
 
-	vpd_error = 0;
-	ea[0] = ea[1] = 0;
-	if ((vpd_error = pci_get_capability(sc->sc_pct, sc->sc_pcitag,
-	    PCI_CAP_VPD, &vpdc, NULL))) {
+	if (pci_get_capability(sc->sc_pct, sc->sc_pcitag,
+	    PCI_CAP_VPD, &vpdc, NULL)) {
 		/*
-		 * PCI VPD capability exists, but it seems that it's
-		 * not in the standard form as stated in PCI VPD
-		 * specification such that driver could not use
-		 * pci_get_vpd_readonly(9) with keyword 'NA'.
-		 * Search VPD data starting at address 0x0100. The data
-		 * should be used as initializers to set AGE_PAR0,
-		 * AGE_PAR1 register including other PCI configuration
-		 * registers.
+		 * PCI VPD capability found, let TWSI reload EEPROM.
+		 * This will set Ethernet address of controller.
 		 */
-		word = 0;
-		match = 0;
-		reg = 0;
-		for (off = AGE_VPD_REG_CONF_START; off < AGE_VPD_REG_CONF_END;
-		    off += sizeof(uint32_t)) {
-			vpd_error = age_read_vpd_word(sc, vpdc, off, &word);
-			if (vpd_error != 0)
-				break;
-			if (match != 0) {
-				switch (reg) {
-				case AGE_PAR0:
-					ea[0] = word;
-					break;
-				case AGE_PAR1:
-					ea[1] = word;
-					break;
-				default:
-					break;
-				}
-				match = 0;
-			} else if ((word & 0xFF) == AGE_VPD_REG_CONF_SIG) {
-				match = 1;
-				reg = word >> 16;
-			} else
+		CSR_WRITE_4(sc, AGE_TWSI_CTRL, CSR_READ_4(sc, AGE_TWSI_CTRL) |
+		    TWSI_CTRL_SW_LD_START);
+		for (i = 100; i > 0; i++) {
+			DELAY(1000);
+			reg = CSR_READ_4(sc, AGE_TWSI_CTRL);
+			if ((reg & TWSI_CTRL_SW_LD_START) == 0)
 				break;
 		}
-		if (off >= AGE_VPD_REG_CONF_END)
-			vpd_error = ENOENT;
-		if (vpd_error == 0) {
-			/*
-			 * Don't blindly trust ethernet address obtained
-			 * from VPD. Check whether ethernet address is
-			 * valid one. Otherwise fall-back to reading
-			 * PAR register.
-			 */
-			ea[1] &= 0xFFFF;
-			if ((ea[0] == 0 && ea[1] == 0) ||
-			    (ea[0] == 0xFFFFFFFF && ea[1] == 0xFFFF)) {
-				if (agedebug)
-					printf("%s: invalid ethernet address "
-				    	    "returned from VPD.\n", 
-				    	    device_xname(sc->sc_dev));
-				vpd_error = EINVAL;
-			}
-		}
-		if (vpd_error != 0 && (agedebug))
-			printf("%s: VPD access failure!\n", 
+		if (i == 0)
+			printf("%s: reloading EEPROM timeout!\n", 
 			    device_xname(sc->sc_dev));	
 	} else {
 		if (agedebug)
@@ -665,26 +593,9 @@
 			    device_xname(sc->sc_dev));
 	}
 
-	/*
-	 * It seems that L1 also provides a way to extract ethernet
-	 * address via SPI flash interface. Because SPI flash memory
-	 * device of different vendors vary in their instruction
-	 * codes for read ID instruction, it's very hard to get
-	 * instructions codes without detailed information for the
-	 * flash memory device used on ethernet controller. To simplify
-	 * code, just read AGE_PAR0/AGE_PAR1 register to get ethernet
-	 * address which is supposed to be set by hardware during
-	 * power on reset.
-	 */
-	if (vpd_error != 0) {
-		/*
-		 * VPD is mapped to SPI flash memory or BIOS set it.
-		 */
-		ea[0] = CSR_READ_4(sc, AGE_PAR0);
-		ea[1] = CSR_READ_4(sc, AGE_PAR1);
-	}
+	ea[0] = CSR_READ_4(sc, AGE_PAR0);
+	ea[1] = CSR_READ_4(sc, AGE_PAR1);
 
-	ea[1] &= 0xFFFF;
 	eaddr[0] = (ea[1] >> 8) & 0xFF;
 	eaddr[1] = (ea[1] >> 0) & 0xFF;
 	eaddr[2] = (ea[0] >> 24) & 0xFF;
@@ -696,11 +607,79 @@
 static void
 age_phy_reset(struct age_softc *sc)
 {
+	uint16_t reg, pn;
+	int i, linkup;
+
 	/* Reset PHY. */
 	CSR_WRITE_4(sc, AGE_GPHY_CTRL, GPHY_CTRL_RST);
-	DELAY(1000);
+	DELAY(2000);
 	CSR_WRITE_4(sc, AGE_GPHY_CTRL, GPHY_CTRL_CLR);
-	DELAY(1000);
+	DELAY(2000);
+
+#define ATPHY_DBG_ADDR		0x1D
+#define ATPHY_DBG_DATA		0x1E
+#define ATPHY_CDTC		0x16
+#define PHY_CDTC_ENB		0x0001
+#define PHY_CDTC_POFF		8
+#define ATPHY_CDTS		0x1C
+#define PHY_CDTS_STAT_OK	0x0000
+#define PHY_CDTS_STAT_SHORT	0x0100
+#define PHY_CDTS_STAT_OPEN	0x0200
+#define PHY_CDTS_STAT_INVAL	0x0300
+#define PHY_CDTS_STAT_MASK	0x0300
+
+	/* Check power saving mode. Magic from Linux. */
+	age_miibus_writereg(sc->sc_dev, sc->age_phyaddr, MII_BMCR, BMCR_RESET);
+	for (linkup = 0, pn = 0; pn < 4; pn++) {
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr, ATPHY_CDTC,
+		    (pn << PHY_CDTC_POFF) | PHY_CDTC_ENB);
+		for (i = 200; i > 0; i--) {
+			DELAY(1000);
+			reg = age_miibus_readreg(sc->sc_dev, sc->age_phyaddr,
+			    ATPHY_CDTC);
+			if ((reg & PHY_CDTC_ENB) == 0)
+				break;
+		}
+		DELAY(1000);
+		reg = age_miibus_readreg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_CDTS);
+		if ((reg & PHY_CDTS_STAT_MASK) != PHY_CDTS_STAT_OPEN) {
+			linkup++;
+			break;
+		}
+	}
+	age_miibus_writereg(sc->sc_dev, sc->age_phyaddr, MII_BMCR,
+	    BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
+	if (linkup == 0) {
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_ADDR, 0);
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_DATA, 0x124E);
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_ADDR, 1);
+		reg = age_miibus_readreg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_DATA);
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_DATA, reg | 0x03);
+		/* XXX */
+		DELAY(1500 * 1000);
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_ADDR, 0);
+		age_miibus_writereg(sc->sc_dev, sc->age_phyaddr,
+		    ATPHY_DBG_DATA, 0x024E);
+	}
+
+#undef ATPHY_DBG_ADDR
+#undef ATPHY_DBG_DATA
+#undef ATPHY_CDTC
+#undef PHY_CDTC_ENB
+#undef PHY_CDTC_POFF
+#undef ATPHY_CDTS
+#undef PHY_CDTS_STAT_OK
+#undef PHY_CDTS_STAT_SHORT
+#undef PHY_CDTS_STAT_OPEN
+#undef PHY_CDTS_STAT_INVAL
+#undef PHY_CDTS_STAT_MASK
 }
 
 static int
@@ -1616,14 +1595,8 @@
 	int i;
 
 	CSR_WRITE_4(sc, AGE_MASTER_CFG, MASTER_RESET);
-	for (i = AGE_RESET_TIMEOUT; i > 0; i--) {
-		DELAY(1);
-		if ((CSR_READ_4(sc, AGE_MASTER_CFG) & MASTER_RESET) == 0)
-			break;
-	}
-	if (i == 0)
-		printf("%s: master reset timeout!\n", device_xname(sc->sc_dev));
-
+	CSR_READ_4(sc, AGE_MASTER_CFG);
+	DELAY(1000);
 	for (i = AGE_RESET_TIMEOUT; i > 0; i--) {
 		if ((reg = CSR_READ_4(sc, AGE_IDLE_STATUS)) == 0)
 			break;

Reply via email to