Module Name:    src
Committed By:   jmcneill
Date:           Wed Feb 23 02:25:04 UTC 2011

Modified Files:
        src/sys/dev/pci: if_alc.c if_alcreg.h

Log Message:
catch up with FreeBSD driver, adds support for AR815x chips


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/pci/if_alc.c src/sys/dev/pci/if_alcreg.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_alc.c
diff -u src/sys/dev/pci/if_alc.c:1.1 src/sys/dev/pci/if_alc.c:1.2
--- src/sys/dev/pci/if_alc.c:1.1	Wed Feb 23 00:35:29 2011
+++ src/sys/dev/pci/if_alc.c	Wed Feb 23 02:25:04 2011
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
 
-/* Driver for Atheros AR8131/AR8132 PCIe Ethernet. */
+/* Driver for Atheros AR813x/AR815x PCIe Ethernet. */
 
 #ifdef _KERNEL_OPT
 #include "vlan.h"
@@ -79,6 +79,25 @@
 
 #include <dev/pci/if_alcreg.h>
 
+/*
+ * Devices supported by this driver.
+ */
+static struct alc_ident alc_ident_table[] = {
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8131, 9 * 1024,
+		"Atheros AR8131 PCIe Gigabit Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8132, 9 * 1024,
+		"Atheros AR8132 PCIe Fast Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8151, 6 * 1024,
+		"Atheros AR8151 v1.0 PCIe Gigabit Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8151_V2, 6 * 1024,
+		"Atheros AR8151 v2.0 PCIe Gigabit Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8152_B, 6 * 1024,
+		"Atheros AR8152 v1.1 PCIe Fast Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8152_B2, 6 * 1024,
+		"Atheros AR8152 v2.0 PCIe Fast Ethernet" },
+	{ 0, 0, 0, NULL },
+};
+
 static int	alc_match(device_t, cfdata_t, void *);
 static void	alc_attach(device_t, device_t, void *);
 static int	alc_detach(device_t, int);
@@ -90,11 +109,13 @@
 static int	alc_mediachange(struct ifnet *);
 static void	alc_mediastatus(struct ifnet *, struct ifmediareq *);
 
-static void	alc_aspm(struct alc_softc *);
+static void	alc_aspm(struct alc_softc *, int);
 static void	alc_disable_l0s_l1(struct alc_softc *);
 static int	alc_dma_alloc(struct alc_softc *);
 static void	alc_dma_free(struct alc_softc *);
 static int	alc_encap(struct alc_softc *, struct mbuf **);
+static struct alc_ident *
+		alc_find_ident(struct pci_attach_args *);
 static void	alc_get_macaddr(struct alc_softc *);
 static void	alc_init_cmb(struct alc_softc *);
 static void	alc_init_rr_ring(struct alc_softc *);
@@ -228,8 +249,8 @@
 		reg = CSR_READ_4(sc, ALC_MAC_CFG);
 		reg |= MAC_CFG_TX_ENB | MAC_CFG_RX_ENB;
 		CSR_WRITE_4(sc, ALC_MAC_CFG, reg);
+		alc_aspm(sc, IFM_SUBTYPE(mii->mii_media_active));
 	}
-	alc_aspm(sc);
 }
 
 static void
@@ -261,44 +282,81 @@
 	return (error);
 }
 
+static struct alc_ident *
+alc_find_ident(struct pci_attach_args *pa)
+{
+	struct alc_ident *ident;
+	uint16_t vendor, devid;
+
+	vendor = PCI_VENDOR(pa->pa_id);
+	devid = PCI_PRODUCT(pa->pa_id);
+	for (ident = alc_ident_table; ident->name != NULL; ident++) {
+		if (vendor == ident->vendorid && devid == ident->deviceid)
+			return (ident);
+	}
+
+	return (NULL);
+}
+
 static int
 alc_match(device_t dev, cfdata_t match, void *aux)
 {
 	struct pci_attach_args *pa = aux;
 
-	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_ATTANSIC)
-		return 0;
-
-	switch (PCI_PRODUCT(pa->pa_id)) {
-	case PCI_PRODUCT_ATTANSIC_AR8131:
-	case PCI_PRODUCT_ATTANSIC_AR8132:
-	case PCI_PRODUCT_ATTANSIC_AR8152_B2:
-		break;
-	default:
-		return 0;
-	}
-
-	return 1;
+	return alc_find_ident(pa) != NULL;
 }
 
 static void
 alc_get_macaddr(struct alc_softc *sc)
 {
 	uint32_t ea[2], opt;
-	int i;
+	uint16_t val;
+	int eeprom, i;
 
+	eeprom = 0;
 	opt = CSR_READ_4(sc, ALC_OPT_CFG);
-	if ((CSR_READ_4(sc, ALC_TWSI_DEBUG) & TWSI_DEBUG_DEV_EXIST) != 0) {
+	if ((CSR_READ_4(sc, ALC_MASTER_CFG) & MASTER_OTP_SEL) != 0 &&
+	    (CSR_READ_4(sc, ALC_TWSI_DEBUG) & TWSI_DEBUG_DEV_EXIST) != 0) {
 		/*
 		 * EEPROM found, let TWSI reload EEPROM configuration.
 		 * This will set ethernet address of controller.
 		 */
-		if ((opt & OPT_CFG_CLK_ENB) == 0) {
-			opt |= OPT_CFG_CLK_ENB;
-			CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
-			CSR_READ_4(sc, ALC_OPT_CFG);
-			DELAY(1000);
+		eeprom++;
+		switch (sc->alc_ident->deviceid) {
+		case PCI_PRODUCT_ATTANSIC_AR8131:
+		case PCI_PRODUCT_ATTANSIC_AR8132:
+			if ((opt & OPT_CFG_CLK_ENB) == 0) {
+				opt |= OPT_CFG_CLK_ENB;
+				CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
+				CSR_READ_4(sc, ALC_OPT_CFG);
+				DELAY(1000);
+			}
+			break;
+		case PCI_PRODUCT_ATTANSIC_AR8151:
+		case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+		case PCI_PRODUCT_ATTANSIC_AR8152_B:
+		case PCI_PRODUCT_ATTANSIC_AR8152_B2:
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_ADDR, 0x00);
+			val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA);
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA, val & 0xFF7F);
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_ADDR, 0x3B);
+			val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA);
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA, val | 0x0008);
+			DELAY(20);
+			break;
 		}
+
+		CSR_WRITE_4(sc, ALC_LTSSM_ID_CFG,
+		    CSR_READ_4(sc, ALC_LTSSM_ID_CFG) & ~LTSSM_ID_WRO_ENB);
+		CSR_WRITE_4(sc, ALC_WOL_CFG, 0);
+		CSR_READ_4(sc, ALC_WOL_CFG);
+
 		CSR_WRITE_4(sc, ALC_TWSI_CFG, CSR_READ_4(sc, ALC_TWSI_CFG) |
 		    TWSI_CFG_SW_LD_START);
 		for (i = 100; i > 0; i--) {
@@ -314,11 +372,36 @@
 		if (alcdebug)
 			printf("%s: EEPROM not found!\n", device_xname(sc->sc_dev));
 	}
-	if ((opt & OPT_CFG_CLK_ENB) != 0) {
-		opt &= ~OPT_CFG_CLK_ENB;
-		CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
-		CSR_READ_4(sc, ALC_OPT_CFG);
-		DELAY(1000);
+	if (eeprom != 0) {
+		switch (sc->alc_ident->deviceid) {
+		case PCI_PRODUCT_ATTANSIC_AR8131:
+		case PCI_PRODUCT_ATTANSIC_AR8132:
+			if ((opt & OPT_CFG_CLK_ENB) != 0) {
+				opt &= ~OPT_CFG_CLK_ENB;
+				CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
+				CSR_READ_4(sc, ALC_OPT_CFG);
+				DELAY(1000);
+			}
+			break;
+		case PCI_PRODUCT_ATTANSIC_AR8151:
+		case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+		case PCI_PRODUCT_ATTANSIC_AR8152_B:
+		case PCI_PRODUCT_ATTANSIC_AR8152_B2:
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_ADDR, 0x00);
+			val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA);
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA, val | 0x0080);
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_ADDR, 0x3B);
+			val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA);
+			alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+			    ALC_MII_DBG_DATA, val & 0xFFF7);
+			DELAY(20);
+			break;
+		}
 	}
 
 	ea[0] = CSR_READ_4(sc, ALC_PAR0);
@@ -363,6 +446,43 @@
 	CSR_READ_2(sc, ALC_GPHY_CFG);
 	DELAY(10 * 1000);
 
+	/* DSP fixup, Vendor magic. */
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B) {
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_ADDR, 0x000A);
+		data = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_DATA);
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_DATA, data & 0xDFFF);
+	}
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2) {
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_ADDR, 0x003B);
+		data = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_DATA);
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_DATA, data & 0xFFF7);
+		DELAY(20 * 1000);
+	}
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151) {
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_ADDR, 0x0029);
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_DATA, 0x929D);
+	}
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8131 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8132 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2) {
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_ADDR, 0x0029);
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    ALC_MII_DBG_DATA, 0xB6DD);
+	}
+
 	/* Load DSP codes, vendor magic. */
 	data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE |
 	    ((1 << ANA_INTERVAL_SEL_TIMER_SHIFT) & ANA_INTERVAL_SEL_TIMER_MASK);
@@ -411,35 +531,115 @@
 static void
 alc_phy_down(struct alc_softc *sc)
 {
-
-	/* Force PHY down. */
-	CSR_WRITE_2(sc, ALC_GPHY_CFG,
-	    GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE |
-	    GPHY_CFG_SEL_ANA_RESET | GPHY_CFG_PHY_IDDQ | GPHY_CFG_PWDOWN_HW);
-	DELAY(1000);
+	switch (sc->alc_ident->deviceid) {
+	case PCI_PRODUCT_ATTANSIC_AR8151:
+	case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+		/*
+		 * GPHY power down caused more problems on AR8151 v2.0.
+		 * When driver is reloaded after GPHY power down,
+		 * accesses to PHY/MAC registers hung the system. Only
+		 * cold boot recovered from it.  I'm not sure whether
+		 * AR8151 v1.0 also requires this one though.  I don't
+		 * have AR8151 v1.0 controller in hand.
+		 * The only option left is to isolate the PHY and
+		 * initiates power down the PHY which in turn saves
+		 * more power when driver is unloaded.
+		 */
+		alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+		    MII_BMCR, BMCR_ISO | BMCR_PDOWN);
+		break;
+	default:
+		/* Force PHY down. */
+		CSR_WRITE_2(sc, ALC_GPHY_CFG,
+		    GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE |
+		    GPHY_CFG_SEL_ANA_RESET | GPHY_CFG_PHY_IDDQ |
+		    GPHY_CFG_PWDOWN_HW);
+		DELAY(1000);
+		break;
+	}
 }
 
 static void
-alc_aspm(struct alc_softc *sc)
+alc_aspm(struct alc_softc *sc, int media)
 {
 	uint32_t pmcfg;
-
+	uint16_t linkcfg;
+ 
 	pmcfg = CSR_READ_4(sc, ALC_PM_CFG);
+	if ((sc->alc_flags & (ALC_FLAG_APS | ALC_FLAG_PCIE)) ==
+	    (ALC_FLAG_APS | ALC_FLAG_PCIE))
+		linkcfg = CSR_READ_2(sc, sc->alc_expcap +
+		    PCI_PCIE_LCSR);
+	else
+		linkcfg = 0;
 	pmcfg &= ~PM_CFG_SERDES_PD_EX_L1;
-	pmcfg |= PM_CFG_SERDES_BUDS_RX_L1_ENB;
-	pmcfg |= PM_CFG_SERDES_L1_ENB;
-	pmcfg &= ~PM_CFG_L1_ENTRY_TIMER_MASK;
+	pmcfg &= ~(PM_CFG_L1_ENTRY_TIMER_MASK | PM_CFG_LCKDET_TIMER_MASK);
 	pmcfg |= PM_CFG_MAC_ASPM_CHK;
+	pmcfg |= (PM_CFG_LCKDET_TIMER_DEFAULT << PM_CFG_LCKDET_TIMER_SHIFT);
+	pmcfg &= ~(PM_CFG_ASPM_L1_ENB | PM_CFG_ASPM_L0S_ENB);
+
+	if ((sc->alc_flags & ALC_FLAG_APS) != 0) {
+		/* Disable extended sync except AR8152 B v1.0 */
+		linkcfg &= ~0x80;
+		if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B &&
+		    sc->alc_rev == ATHEROS_AR8152_B_V10)
+			linkcfg |= 0x80;
+		CSR_WRITE_2(sc, sc->alc_expcap + PCI_PCIE_LCSR,
+		    linkcfg);
+		pmcfg &= ~(PM_CFG_EN_BUFS_RX_L0S | PM_CFG_SA_DLY_ENB |
+		    PM_CFG_HOTRST);
+		pmcfg |= (PM_CFG_L1_ENTRY_TIMER_DEFAULT <<
+		    PM_CFG_L1_ENTRY_TIMER_SHIFT);
+		pmcfg &= ~PM_CFG_PM_REQ_TIMER_MASK;
+		pmcfg |= (PM_CFG_PM_REQ_TIMER_DEFAULT <<
+		    PM_CFG_PM_REQ_TIMER_SHIFT);
+		pmcfg |= PM_CFG_SERDES_PD_EX_L1 | PM_CFG_PCIE_RECV;
+	}
+
 	if ((sc->alc_flags & ALC_FLAG_LINK) != 0) {
-		pmcfg |= PM_CFG_SERDES_PLL_L1_ENB;
-		pmcfg &= ~PM_CFG_CLK_SWH_L1;
-		pmcfg &= ~PM_CFG_ASPM_L1_ENB;
-		pmcfg &= ~PM_CFG_ASPM_L0S_ENB;
+		if ((sc->alc_flags & ALC_FLAG_L0S) != 0)
+			pmcfg |= PM_CFG_ASPM_L0S_ENB;
+		if ((sc->alc_flags & ALC_FLAG_L1S) != 0)
+			pmcfg |= PM_CFG_ASPM_L1_ENB;
+		if ((sc->alc_flags & ALC_FLAG_APS) != 0) {
+			if (sc->alc_ident->deviceid ==
+			    PCI_PRODUCT_ATTANSIC_AR8152_B)
+				pmcfg &= ~PM_CFG_ASPM_L0S_ENB;
+			pmcfg &= ~(PM_CFG_SERDES_L1_ENB |
+			    PM_CFG_SERDES_PLL_L1_ENB |
+			    PM_CFG_SERDES_BUDS_RX_L1_ENB);
+			pmcfg |= PM_CFG_CLK_SWH_L1;
+			if (media == IFM_100_TX || media == IFM_1000_T) {
+				pmcfg &= ~PM_CFG_L1_ENTRY_TIMER_MASK;
+				switch (sc->alc_ident->deviceid) {
+				case PCI_PRODUCT_ATTANSIC_AR8152_B:
+					pmcfg |= (7 <<
+					    PM_CFG_L1_ENTRY_TIMER_SHIFT);
+					break;
+				case PCI_PRODUCT_ATTANSIC_AR8152_B2:
+				case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+					pmcfg |= (4 <<
+					    PM_CFG_L1_ENTRY_TIMER_SHIFT);
+					break;
+				default:
+					pmcfg |= (15 <<
+					    PM_CFG_L1_ENTRY_TIMER_SHIFT);
+					break;
+				}
+			}
+		} else {
+			pmcfg |= PM_CFG_SERDES_L1_ENB |
+			    PM_CFG_SERDES_PLL_L1_ENB |
+			    PM_CFG_SERDES_BUDS_RX_L1_ENB;
+			pmcfg &= ~(PM_CFG_CLK_SWH_L1 |
+			    PM_CFG_ASPM_L1_ENB | PM_CFG_ASPM_L0S_ENB);
+		}
 	} else {
-		pmcfg &= ~PM_CFG_SERDES_PLL_L1_ENB;
+		pmcfg &= ~(PM_CFG_SERDES_BUDS_RX_L1_ENB | PM_CFG_SERDES_L1_ENB |
+		    PM_CFG_SERDES_PLL_L1_ENB);
 		pmcfg |= PM_CFG_CLK_SWH_L1;
-		pmcfg &= ~PM_CFG_ASPM_L1_ENB;
-		pmcfg &= ~PM_CFG_ASPM_L0S_ENB;
+		if ((sc->alc_flags & ALC_FLAG_L1S) != 0)
+			pmcfg |= PM_CFG_ASPM_L1_ENB;
 	}
 	CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg);
 }
@@ -455,13 +655,15 @@
 	const char *intrstr;
 	struct ifnet *ifp;
 	pcireg_t memtype;
-	const char *aspm_state[] = { "L0s/L1", "L0s", "L1", "L0s/l1" };
+	const char *aspm_state[] = { "L0s/L1", "L0s", "L1", "L0s/L1" };
 	uint16_t burst;
 	int base, mii_flags, state, error = 0;
 	uint32_t cap, ctl, val;
 
+	sc->alc_ident = alc_find_ident(pa);
+
 	aprint_naive("\n");
-	aprint_normal(": Attansic/Atheros L1C/L2C Ethernet\n");
+	aprint_normal(": %s\n", sc->alc_ident->name);
 
 	sc->sc_dev = self;
 	sc->sc_dmat = pa->pa_dmat;
@@ -517,6 +719,7 @@
 	if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
 	    &base, NULL)) {
 		sc->alc_flags |= ALC_FLAG_PCIE;
+		sc->alc_expcap = base;
 		burst = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
 		    base + PCI_PCIE_DCSR) >> 16;
 		sc->alc_dma_rd_burst = (burst & 0x7000) >> 12;
@@ -533,6 +736,20 @@
 		val = CSR_READ_4(sc, ALC_PEX_UNC_ERR_SEV);
 		val &= ~(PEX_UNC_ERR_SEV_DLP | PEX_UNC_ERR_SEV_FCP);
 		CSR_WRITE_4(sc, ALC_PEX_UNC_ERR_SEV, val);
+		CSR_WRITE_4(sc, ALC_LTSSM_ID_CFG,
+		    CSR_READ_4(sc, ALC_LTSSM_ID_CFG) & ~LTSSM_ID_WRO_ENB);
+		CSR_WRITE_4(sc, ALC_PCIE_PHYMISC,
+		    CSR_READ_4(sc, ALC_PCIE_PHYMISC) |
+		    PCIE_PHYMISC_FORCE_RCV_DET);
+		if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B &&
+		    sc->alc_rev == ATHEROS_AR8152_B_V10) {
+			val = CSR_READ_4(sc, ALC_PCIE_PHYMISC2);
+			val &= ~(PCIE_PHYMISC2_SERDES_CDR_MASK |
+			    PCIE_PHYMISC2_SERDES_TH_MASK);
+			val |= 3 << PCIE_PHYMISC2_SERDES_CDR_SHIFT;
+			val |= 3 << PCIE_PHYMISC2_SERDES_TH_SHIFT;
+			CSR_WRITE_4(sc, ALC_PCIE_PHYMISC2, val);
+		}
 		/* Disable ASPM L0S and L1. */
 		cap = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
 		    base + PCI_PCIE_LCAP) >> 16;
@@ -546,13 +763,18 @@
 				    device_xname(sc->sc_dev),
 				    sc->alc_rcb == DMA_CFG_RCB_64 ? 64 : 128);
 			state = ctl & 0x03;
+			if (state & 0x01)
+				sc->alc_flags |= ALC_FLAG_L0S;
+			if (state & 0x02)
+				sc->alc_flags |= ALC_FLAG_L1S;
 			if (alcdebug)
 				printf("%s: ASPM %s %s\n",
 				    device_xname(sc->sc_dev),
 				    aspm_state[state],
 				    state == 0 ? "disabled" : "enabled");
-			if (state != 0)
-				alc_disable_l0s_l1(sc);
+			alc_disable_l0s_l1(sc);
+		} else {
+			aprint_debug_dev(sc->sc_dev, "no ASPM support\n");
 		}
 	}
 
@@ -569,18 +791,25 @@
 	 * used in AR8132 can't establish gigabit link even if it
 	 * shows the same PHY model/revision number of AR8131.
 	 */
-	switch (PCI_PRODUCT(pa->pa_id)) {
-	case PCI_PRODUCT_ATTANSIC_AR8132:
+	switch (sc->alc_ident->deviceid) {
+	case PCI_PRODUCT_ATTANSIC_AR8152_B:
 	case PCI_PRODUCT_ATTANSIC_AR8152_B2:
-		sc->alc_flags |= ALC_FLAG_FASTETHER | ALC_FLAG_JUMBO;
+		sc->alc_flags |= ALC_FLAG_APS;
+		/* FALLTHROUGH */
+	case PCI_PRODUCT_ATTANSIC_AR8132:
+		sc->alc_flags |= ALC_FLAG_FASTETHER;
 		break;
+	case PCI_PRODUCT_ATTANSIC_AR8151:
+	case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+		sc->alc_flags |= ALC_FLAG_APS;
+		/* FALLTHROUGH */
 	default:
-		sc->alc_flags |= ALC_FLAG_JUMBO | ALC_FLAG_ASPM_MON;
 		break;
 	}
+	sc->alc_flags |= ALC_FLAG_JUMBO | ALC_FLAG_ASPM_MON;
 
 	/*
-	 * It seems that AR8131/AR8132 has silicon bug for SMB. In
+	 * It seems that AR813x/AR815x has silicon bug for SMB. In
 	 * addition, Atheros said that enabling SMB wouldn't improve
 	 * performance. However I think it's bad to access lots of
 	 * registers to extract MAC statistics.
@@ -1257,6 +1486,10 @@
 	reg = CSR_READ_4(sc, ALC_MAC_CFG);
 	reg &= ~(MAC_CFG_FULL_DUPLEX | MAC_CFG_TX_FC | MAC_CFG_RX_FC |
 	    MAC_CFG_SPEED_MASK);
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2)
+		reg |= MAC_CFG_HASH_ALG_CRC32 | MAC_CFG_SPEED_MODE_SW;
 	/* Reprogram MAC with resolved speed/duplex. */
 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
 	case IFM_10_T:
@@ -1680,8 +1913,8 @@
 		 *  errored frames.
 		 */
 		status |= RRD_TCP_UDPCSUM_NOK | RRD_IPCSUM_NOK;
-		if ((RRD_ERR_CRC | RRD_ERR_ALIGN | RRD_ERR_TRUNC |
-		    RRD_ERR_RUNT) != 0)
+		if ((status & (RRD_ERR_CRC | RRD_ERR_ALIGN |
+		    RRD_ERR_TRUNC | RRD_ERR_RUNT)) != 0)
 			return;
 	}
 
@@ -1789,7 +2022,9 @@
 	uint32_t reg;
 	int i;
 
-	CSR_WRITE_4(sc, ALC_MASTER_CFG, MASTER_RESET);
+	reg = CSR_READ_4(sc, ALC_MASTER_CFG) & 0xFFFF;
+	reg |= MASTER_OOB_DIS_OFF | MASTER_RESET;
+	CSR_WRITE_4(sc, ALC_MASTER_CFG, reg);
 	for (i = ALC_RESET_TIMEOUT; i > 0; i--) {
 		DELAY(10);
 		if ((CSR_READ_4(sc, ALC_MASTER_CFG) & MASTER_RESET) == 0)
@@ -1840,6 +2075,9 @@
 	alc_init_cmb(sc);
 	alc_init_smb(sc);
 
+	/* Enable all clocks. */
+	CSR_WRITE_4(sc, ALC_CLK_GATING_CFG, 0);
+
 	/* Reprogram the station address. */
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
 	CSR_WRITE_4(sc, ALC_PAR0,
@@ -1901,6 +2139,18 @@
 	CSR_WRITE_4(sc, ALC_SMB_BASE_ADDR_HI, ALC_ADDR_HI(paddr));
 	CSR_WRITE_4(sc, ALC_SMB_BASE_ADDR_LO, ALC_ADDR_LO(paddr));
 
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B) {
+		/* Reconfigure SRAM - Vendor magic. */
+		CSR_WRITE_4(sc, ALC_SRAM_RX_FIFO_LEN, 0x000002A0);
+		CSR_WRITE_4(sc, ALC_SRAM_TX_FIFO_LEN, 0x00000100);
+		CSR_WRITE_4(sc, ALC_SRAM_RX_FIFO_ADDR, 0x029F0000);
+		CSR_WRITE_4(sc, ALC_SRAM_RD0_ADDR, 0x02BF02A0);
+		CSR_WRITE_4(sc, ALC_SRAM_TX_FIFO_ADDR, 0x03BF02C0);
+		CSR_WRITE_4(sc, ALC_SRAM_TD_ADDR, 0x03DF03C0);
+		CSR_WRITE_4(sc, ALC_TXF_WATER_MARK, 0x00000000);
+		CSR_WRITE_4(sc, ALC_RD_DMA_CFG, 0x00000000);
+	}
+
 	/* Tell hardware that we're ready to load DMA blocks. */
 	CSR_WRITE_4(sc, ALC_DMA_BLOCK, DMA_BLOCK_LOAD);
 
@@ -1910,14 +2160,11 @@
 	reg = ALC_USECS(sc->alc_int_rx_mod) << IM_TIMER_RX_SHIFT;
 	reg |= ALC_USECS(sc->alc_int_tx_mod) << IM_TIMER_TX_SHIFT;
 	CSR_WRITE_4(sc, ALC_IM_TIMER, reg);
-	reg = CSR_READ_4(sc, ALC_MASTER_CFG);
-	reg &= ~(MASTER_CHIP_REV_MASK | MASTER_CHIP_ID_MASK);
 	/*
 	 * We don't want to automatic interrupt clear as task queue
 	 * for the interrupt should know interrupt status.
 	 */
-	reg &= ~MASTER_INTR_RD_CLR;
-	reg &= ~(MASTER_IM_RX_TIMER_ENB | MASTER_IM_TX_TIMER_ENB);
+	reg = MASTER_SA_TIMER_ENB;
 	if (ALC_USECS(sc->alc_int_rx_mod) != 0)
 		reg |= MASTER_IM_RX_TIMER_ENB;
 	if (ALC_USECS(sc->alc_int_tx_mod) != 0)
@@ -1958,7 +2205,7 @@
 	 * Be conservative in what you do, be liberal in what you
 	 * accept from others - RFC 793.
 	 */
-	CSR_WRITE_4(sc, ALC_FRAME_SIZE, ALC_JUMBO_FRAMELEN);
+	CSR_WRITE_4(sc, ALC_FRAME_SIZE, sc->alc_ident->max_framelen);
 
 	/* Disable header split(?) */
 	CSR_WRITE_4(sc, ALC_HDS_CFG, 0);
@@ -1985,11 +2232,14 @@
 	 * TSO/checksum offloading.
 	 */
 	CSR_WRITE_4(sc, ALC_TSO_OFFLOAD_THRESH,
-	    (ALC_JUMBO_FRAMELEN >> TSO_OFFLOAD_THRESH_UNIT_SHIFT) &
+	    (sc->alc_ident->max_framelen >> TSO_OFFLOAD_THRESH_UNIT_SHIFT) &
 	    TSO_OFFLOAD_THRESH_MASK);
 	/* Configure TxQ. */
 	reg = (alc_dma_burst[sc->alc_dma_rd_burst] <<
 	    TXQ_CFG_TX_FIFO_BURST_SHIFT) & TXQ_CFG_TX_FIFO_BURST_MASK;
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2)
+		reg >>= 1;
 	reg |= (TXQ_CFG_TD_BURST_DEFAULT << TXQ_CFG_TD_BURST_SHIFT) &
 	    TXQ_CFG_TD_BURST_MASK;
 	CSR_WRITE_4(sc, ALC_TXQ_CFG, reg | TXQ_CFG_ENHANCED_MODE);
@@ -2006,14 +2256,23 @@
 	 * XON  : 80% of Rx FIFO
 	 * XOFF : 30% of Rx FIFO
 	 */
-	reg = CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN);
-	rxf_hi = (reg * 8) / 10;
-	rxf_lo = (reg * 3)/ 10;
-	CSR_WRITE_4(sc, ALC_RX_FIFO_PAUSE_THRESH,
-	    ((rxf_lo << RX_FIFO_PAUSE_THRESH_LO_SHIFT) &
-	    RX_FIFO_PAUSE_THRESH_LO_MASK) |
-	    ((rxf_hi << RX_FIFO_PAUSE_THRESH_HI_SHIFT) &
-	     RX_FIFO_PAUSE_THRESH_HI_MASK));
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8131 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8132) {
+		reg = CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN);
+		rxf_hi = (reg * 8) / 10;
+		rxf_lo = (reg * 3) / 10;
+		CSR_WRITE_4(sc, ALC_RX_FIFO_PAUSE_THRESH,
+		    ((rxf_lo << RX_FIFO_PAUSE_THRESH_LO_SHIFT) &
+		     RX_FIFO_PAUSE_THRESH_LO_MASK) |
+		    ((rxf_hi << RX_FIFO_PAUSE_THRESH_HI_SHIFT) &
+		     RX_FIFO_PAUSE_THRESH_HI_MASK));
+	}
+
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2)
+		CSR_WRITE_4(sc, ALC_SERDES_LOCK,
+		    CSR_READ_4(sc, ALC_SERDES_LOCK) | SERDES_MAC_CLK_SLOWDOWN |
+		    SERDES_PHY_CLK_SLOWDOWN);
 
 	/* Disable RSS until I understand L1C/L2C's RSS logic. */
 	CSR_WRITE_4(sc, ALC_RSS_IDT_TABLE0, 0);
@@ -2024,15 +2283,9 @@
 	    RXQ_CFG_RD_BURST_MASK;
 	reg |= RXQ_CFG_RSS_MODE_DIS;
 	if ((sc->alc_flags & ALC_FLAG_ASPM_MON) != 0)
-		reg |= RXQ_CFG_ASPM_THROUGHPUT_LIMIT_100M;
+		reg |= RXQ_CFG_ASPM_THROUGHPUT_LIMIT_1M;
 	CSR_WRITE_4(sc, ALC_RXQ_CFG, reg);
 
-	/* Configure Rx DMAW request thresold. */
-	CSR_WRITE_4(sc, ALC_RD_DMA_CFG,
-	    ((RD_DMA_CFG_THRESH_DEFAULT << RD_DMA_CFG_THRESH_SHIFT) &
-	    RD_DMA_CFG_THRESH_MASK) |
-	    ((ALC_RD_DMA_CFG_USECS(0) << RD_DMA_CFG_TIMER_SHIFT) &
-	    RD_DMA_CFG_TIMER_MASK));
 	/* Configure DMA parameters. */
 	reg = DMA_CFG_OUT_ORDER | DMA_CFG_RD_REQ_PRI;
 	reg |= sc->alc_rcb;
@@ -2058,7 +2311,7 @@
 	 *  - Enable CRC generation.
 	 *  Actual reconfiguration of MAC for resolved speed/duplex
 	 *  is followed after detection of link establishment.
-	 *  AR8131/AR8132 always does checksum computation regardless
+	 *  AR813x/AR815x always does checksum computation regardless
 	 *  of MAC_CFG_RXCSUM_ENB bit. Also the controller is known to
 	 *  have bug in protocol field in Rx return structure so
 	 *  these controllers can't handle fragmented frames. Disable
@@ -2068,6 +2321,10 @@
 	reg = MAC_CFG_TX_CRC_ENB | MAC_CFG_TX_AUTO_PAD | MAC_CFG_FULL_DUPLEX |
 	    ((MAC_CFG_PREAMBLE_DEFAULT << MAC_CFG_PREAMBLE_SHIFT) &
 	    MAC_CFG_PREAMBLE_MASK);
+	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2)
+		reg |= MAC_CFG_HASH_ALG_CRC32 | MAC_CFG_SPEED_MODE_SW;
 	if ((sc->alc_flags & ALC_FLAG_FASTETHER) != 0)
 		reg |= MAC_CFG_SPEED_10_100;
 	else
@@ -2171,7 +2428,7 @@
 	/* Disable Rx/Tx MAC. */
 	reg = CSR_READ_4(sc, ALC_MAC_CFG);
 	if ((reg & (MAC_CFG_TX_ENB | MAC_CFG_RX_ENB)) != 0) {
-		reg &= ~MAC_CFG_TX_ENB | MAC_CFG_RX_ENB;
+		reg &= ~(MAC_CFG_TX_ENB | MAC_CFG_RX_ENB);
 		CSR_WRITE_4(sc, ALC_MAC_CFG, reg);
 	}
 	for (i = ALC_TIMEOUT; i > 0; i--) {
@@ -2222,7 +2479,7 @@
 	}
 	/* Disable TxQ. */
 	reg = CSR_READ_4(sc, ALC_TXQ_CFG);
-	if ((reg & TXQ_CFG_ENB) == 0) {
+	if ((reg & TXQ_CFG_ENB) != 0) {
 		reg &= ~TXQ_CFG_ENB;
 		CSR_WRITE_4(sc, ALC_TXQ_CFG, reg);
 	}
Index: src/sys/dev/pci/if_alcreg.h
diff -u src/sys/dev/pci/if_alcreg.h:1.1 src/sys/dev/pci/if_alcreg.h:1.2
--- src/sys/dev/pci/if_alcreg.h:1.1	Wed Feb 23 00:35:29 2011
+++ src/sys/dev/pci/if_alcreg.h	Wed Feb 23 02:25:04 2011
@@ -33,6 +33,9 @@
 
 #define ALC_PCIR_BAR			0x10
 
+#define ATHEROS_AR8152_B_V10		0xC0
+#define ATHEROS_AR8152_B_V11		0xC1
+
 /* 0x0000 - 0x02FF : PCIe configuration space */
 
 #define	ALC_PEX_UNC_ERR_SEV		0x10C
@@ -56,6 +59,12 @@
 #define	ALC_PCIE_PHYMISC		0x1000
 #define	PCIE_PHYMISC_FORCE_RCV_DET	0x00000004
 
+#define	ALC_PCIE_PHYMISC2		0x1004
+#define	PCIE_PHYMISC2_SERDES_CDR_MASK	0x00030000
+#define	PCIE_PHYMISC2_SERDES_TH_MASK	0x000C0000
+#define	PCIE_PHYMISC2_SERDES_CDR_SHIFT	16
+#define	PCIE_PHYMISC2_SERDES_TH_SHIFT	18
+
 #define	ALC_TWSI_DEBUG			0x1108
 #define	TWSI_DEBUG_DEV_EXIST		0x20000000
 
@@ -88,7 +97,9 @@
 #define	PM_CFG_PCIE_RECV		0x00008000
 #define	PM_CFG_L1_ENTRY_TIMER_MASK	0x000F0000
 #define	PM_CFG_PM_REQ_TIMER_MASK	0x00F00000
-#define	PM_CFG_LCKDET_TIMER_MASK	0x3F000000
+#define	PM_CFG_LCKDET_TIMER_MASK	0x0F000000
+#define	PM_CFG_EN_BUFS_RX_L0S		0x10000000
+#define	PM_CFG_SA_DLY_ENB		0x20000000
 #define	PM_CFG_MAC_ASPM_CHK		0x40000000
 #define	PM_CFG_HOTRST			0x80000000
 #define	PM_CFG_L0S_ENTRY_TIMER_SHIFT	8
@@ -96,10 +107,20 @@
 #define	PM_CFG_PM_REQ_TIMER_SHIFT	20
 #define	PM_CFG_LCKDET_TIMER_SHIFT	24
 
+#define PM_CFG_L0S_ENTRY_TIMER_DEFAULT	6
+#define	PM_CFG_L1_ENTRY_TIMER_DEFAULT	1
+#define	PM_CFG_LCKDET_TIMER_DEFAULT	12
+#define	PM_CFG_PM_REQ_TIMER_DEFAULT	12
+
+#define	ALC_LTSSM_ID_CFG		0x12FC
+#define	LTSSM_ID_WRO_ENB		0x00001000
+
 #define	ALC_MASTER_CFG			0x1400
 #define	MASTER_RESET			0x00000001
+#define	MASTER_TEST_MODE_MASK		0x0000000C
 #define	MASTER_BERT_START		0x00000010
-#define	MASTER_TEST_MODE_MASK		0x000000C0
+#define	MASTER_OOB_DIS_OFF		0x00000040
+#define	MASTER_SA_TIMER_ENB		0x00000080
 #define	MASTER_MTIMER_ENB		0x00000100
 #define	MASTER_MANUAL_INTR_ENB		0x00000200
 #define	MASTER_IM_TX_TIMER_ENB		0x00000400
@@ -114,7 +135,7 @@
 #define	MASTER_CHIP_REV_SHIFT		16
 #define	MASTER_CHIP_ID_SHIFT		24
 
-/* Number of ticks per usec for AR8131/AR8132. */
+/* Number of ticks per usec for AR813x/AR815x. */
 #define	ALC_TICK_USECS			2
 #define	ALC_USECS(x)			((x) / ALC_TICK_USECS)
 
@@ -212,6 +233,8 @@
 #define	ALC_SERDES_LOCK			0x1424
 #define	SERDES_LOCK_DET			0x00000001
 #define	SERDES_LOCK_DET_ENB		0x00000002
+#define	SERDES_MAC_CLK_SLOWDOWN		0x00020000
+#define	SERDES_PHY_CLK_SLOWDOWN		0x00040000
 
 #define	ALC_MAC_CFG			0x1480
 #define	MAC_CFG_TX_ENB			0x00000001
@@ -241,6 +264,8 @@
 #define	MAC_CFG_BCAST			0x04000000
 #define	MAC_CFG_DBG			0x08000000
 #define	MAC_CFG_SINGLE_PAUSE_ENB	0x10000000
+#define	MAC_CFG_HASH_ALG_CRC32		0x20000000
+#define	MAC_CFG_SPEED_MODE_SW		0x40000000
 #define	MAC_CFG_PREAMBLE_SHIFT		10
 #define	MAC_CFG_PREAMBLE_DEFAULT	7
 
@@ -683,11 +708,19 @@
 #define	HDS_CFG_BACKFILLSIZE_SHIFT	8
 #define	HDS_CFG_MAX_HDRSIZE_SHIFT	20
 
-/* AR8131/AR8132 registers for MAC statistics */
+/* AR813x/AR815x registers for MAC statistics */
 #define	ALC_RX_MIB_BASE			0x1700
 
 #define	ALC_TX_MIB_BASE			0x1760
 
+#define	ALC_CLK_GATING_CFG		0x1814
+#define	CLK_GATING_DMAW_ENB		0x0001
+#define	CLK_GATING_DMAR_ENB		0x0002
+#define	CLK_GATING_TXQ_ENB		0x0004
+#define	CLK_GATING_RXQ_ENB		0x0008
+#define	CLK_GATING_TXMAC_ENB		0x0010
+#define	CLK_GATING_RXMAC_ENB		0x0020
+
 #define	ALC_DEBUG_DATA0			0x1900
 
 #define	ALC_DEBUG_DATA1			0x1904
@@ -974,13 +1007,8 @@
 #define	ALC_PROC_MAX		(ALC_RX_RING_CNT - 1)
 #define	ALC_PROC_DEFAULT	(ALC_RX_RING_CNT / 4)
 
-#define	ALC_JUMBO_FRAMELEN	(9 * 1024)
-#define	ALC_JUMBO_MTU		\
-	(ALC_JUMBO_FRAMELEN - sizeof(struct ether_vlan_header) - ETHER_CRC_LEN)
-#define	ALC_MAX_FRAMELEN	(ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN)
-
 /*
- * The number of bits reserved for MSS in AR8121/AR8132 controllers
+ * The number of bits reserved for MSS in AR813x/AR815x controllers
  * are 13 bits. This limits the maximum interface MTU size in TSO
  * case(8191 + sizeof(struct ip) + sizeof(struct tcphdr)) as upper
  * stack should not generate TCP segments with MSS greater than the
@@ -1099,6 +1127,13 @@
 	uint64_t tx_mcast_bytes;
 };
 
+struct alc_ident {
+	uint16_t	vendorid;
+	uint16_t	deviceid;
+	uint32_t	max_framelen;
+	const char	*name;
+};
+
 /*
  * Software state per device.
  */
@@ -1114,9 +1149,10 @@
 	pcitag_t		sc_pcitag;
 
 	void			*sc_irq_handle;
-
+	struct alc_ident	*alc_ident;
 	struct mii_data		sc_miibus;
 	int			alc_rev;
+	int			alc_expcap;
 	int			alc_chip_rev;
 	int			alc_phyaddr;
 	uint8_t			alc_eaddr[ETHER_ADDR_LEN];
@@ -1133,7 +1169,9 @@
 #define	ALC_FLAG_ASPM_MON	0x0080
 #define	ALC_FLAG_CMB_BUG	0x0100
 #define	ALC_FLAG_SMB_BUG	0x0200
-#define	ALC_FLAG_DETACH		0x4000
+#define	ALC_FLAG_L0S		0x0400
+#define	ALC_FLAG_L1S		0x0800
+#define	ALC_FLAG_APS		0x1000
 #define	ALC_FLAG_LINK		0x8000
 
 	callout_t		sc_tick_ch;

Reply via email to