diff options
author | melifaro <melifaro@FreeBSD.org> | 2014-10-09 15:35:28 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2014-10-09 15:35:28 +0000 |
commit | d23efba7dd470e247cbcacf9ec6cf642d02d5b8c (patch) | |
tree | 6d2822f6a5ff6c5c487b859a557d076b53b03c46 /sys/dev/alc/if_alc.c | |
parent | cab1d703b61e2216f130c6d1a23ca70b2b322386 (diff) | |
parent | c47600c4ec062b8225ac1d48197a2a1de778760d (diff) | |
download | FreeBSD-src-d23efba7dd470e247cbcacf9ec6cf642d02d5b8c.zip FreeBSD-src-d23efba7dd470e247cbcacf9ec6cf642d02d5b8c.tar.gz |
Sync to HEAD@r272825.
Diffstat (limited to 'sys/dev/alc/if_alc.c')
-rw-r--r-- | sys/dev/alc/if_alc.c | 1243 |
1 files changed, 1045 insertions, 198 deletions
diff --git a/sys/dev/alc/if_alc.c b/sys/dev/alc/if_alc.c index ecb3b47..6fed142 100644 --- a/sys/dev/alc/if_alc.c +++ b/sys/dev/alc/if_alc.c @@ -111,17 +111,31 @@ static struct alc_ident alc_ident_table[] = { "Atheros AR8152 v1.1 PCIe Fast Ethernet" }, { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8152_B2, 6 * 1024, "Atheros AR8152 v2.0 PCIe Fast Ethernet" }, + { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8161, 9 * 1024, + "Atheros AR8161 PCIe Gigabit Ethernet" }, + { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8162, 9 * 1024, + "Atheros AR8161 PCIe Fast Ethernet" }, + { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8171, 9 * 1024, + "Atheros AR8161 PCIe Gigabit Ethernet" }, + { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8172, 9 * 1024, + "Atheros AR8161 PCIe Fast Ethernet" }, + { VENDORID_ATHEROS, DEVICEID_ATHEROS_E2200, 9 * 1024, + "Killer E2200 Gigabit Ethernet" }, { 0, 0, 0, NULL} }; -static void alc_aspm(struct alc_softc *, int); +static void alc_aspm(struct alc_softc *, int, int); +static void alc_aspm_813x(struct alc_softc *, int); +static void alc_aspm_816x(struct alc_softc *, int); static int alc_attach(device_t); static int alc_check_boundary(struct alc_softc *); +static void alc_config_msi(struct alc_softc *); static int alc_detach(device_t); 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 void alc_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static void alc_dsp_fixup(struct alc_softc *, int); static int alc_encap(struct alc_softc *, struct mbuf **); static struct alc_ident * alc_find_ident(device_t); @@ -130,6 +144,9 @@ static struct mbuf * alc_fixup_rx(struct ifnet *, struct mbuf *); #endif static void alc_get_macaddr(struct alc_softc *); +static void alc_get_macaddr_813x(struct alc_softc *); +static void alc_get_macaddr_816x(struct alc_softc *); +static void alc_get_macaddr_par(struct alc_softc *); static void alc_init(void *); static void alc_init_cmb(struct alc_softc *); static void alc_init_locked(struct alc_softc *); @@ -141,14 +158,26 @@ static void alc_int_task(void *, int); static int alc_intr(void *); static int alc_ioctl(struct ifnet *, u_long, caddr_t); static void alc_mac_config(struct alc_softc *); +static uint32_t alc_mii_readreg_813x(struct alc_softc *, int, int); +static uint32_t alc_mii_readreg_816x(struct alc_softc *, int, int); +static uint32_t alc_mii_writereg_813x(struct alc_softc *, int, int, int); +static uint32_t alc_mii_writereg_816x(struct alc_softc *, int, int, int); static int alc_miibus_readreg(device_t, int, int); static void alc_miibus_statchg(device_t); static int alc_miibus_writereg(device_t, int, int, int); +static uint32_t alc_miidbg_readreg(struct alc_softc *, int); +static uint32_t alc_miidbg_writereg(struct alc_softc *, int, int); +static uint32_t alc_miiext_readreg(struct alc_softc *, int, int); +static uint32_t alc_miiext_writereg(struct alc_softc *, int, int, int); static int alc_mediachange(struct ifnet *); +static int alc_mediachange_locked(struct alc_softc *); static void alc_mediastatus(struct ifnet *, struct ifmediareq *); static int alc_newbuf(struct alc_softc *, struct alc_rxdesc *); +static void alc_osc_reset(struct alc_softc *); static void alc_phy_down(struct alc_softc *); static void alc_phy_reset(struct alc_softc *); +static void alc_phy_reset_813x(struct alc_softc *); +static void alc_phy_reset_816x(struct alc_softc *); static int alc_probe(device_t); static void alc_reset(struct alc_softc *); static int alc_resume(device_t); @@ -158,6 +187,8 @@ static void alc_rxfilter(struct alc_softc *); static void alc_rxvlan(struct alc_softc *); static void alc_setlinkspeed(struct alc_softc *); static void alc_setwol(struct alc_softc *); +static void alc_setwol_813x(struct alc_softc *); +static void alc_setwol_816x(struct alc_softc *); static int alc_shutdown(device_t); static void alc_start(struct ifnet *); static void alc_start_locked(struct ifnet *); @@ -230,10 +261,21 @@ static int alc_miibus_readreg(device_t dev, int phy, int reg) { struct alc_softc *sc; - uint32_t v; - int i; + int v; sc = device_get_softc(dev); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + v = alc_mii_readreg_816x(sc, phy, reg); + else + v = alc_mii_readreg_813x(sc, phy, reg); + return (v); +} + +static uint32_t +alc_mii_readreg_813x(struct alc_softc *sc, int phy, int reg) +{ + uint32_t v; + int i; /* * For AR8132 fast ethernet controller, do not report 1000baseT @@ -262,14 +304,52 @@ alc_miibus_readreg(device_t dev, int phy, int reg) return ((v & MDIO_DATA_MASK) >> MDIO_DATA_SHIFT); } +static uint32_t +alc_mii_readreg_816x(struct alc_softc *sc, int phy, int reg) +{ + uint32_t clk, v; + int i; + + if ((sc->alc_flags & ALC_FLAG_LINK) != 0) + clk = MDIO_CLK_25_128; + else + clk = MDIO_CLK_25_4; + CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_READ | + MDIO_SUP_PREAMBLE | clk | MDIO_REG_ADDR(reg)); + for (i = ALC_PHY_TIMEOUT; i > 0; i--) { + DELAY(5); + v = CSR_READ_4(sc, ALC_MDIO); + if ((v & MDIO_OP_BUSY) == 0) + break; + } + + if (i == 0) { + device_printf(sc->alc_dev, "phy read timeout : %d\n", reg); + return (0); + } + + return ((v & MDIO_DATA_MASK) >> MDIO_DATA_SHIFT); +} + static int alc_miibus_writereg(device_t dev, int phy, int reg, int val) { struct alc_softc *sc; - uint32_t v; - int i; + int v; sc = device_get_softc(dev); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + v = alc_mii_writereg_816x(sc, phy, reg, val); + else + v = alc_mii_writereg_813x(sc, phy, reg, val); + return (v); +} + +static uint32_t +alc_mii_writereg_813x(struct alc_softc *sc, int phy, int reg, int val) +{ + uint32_t v; + int i; CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_WRITE | (val & MDIO_DATA_MASK) << MDIO_DATA_SHIFT | @@ -287,6 +367,32 @@ alc_miibus_writereg(device_t dev, int phy, int reg, int val) return (0); } +static uint32_t +alc_mii_writereg_816x(struct alc_softc *sc, int phy, int reg, int val) +{ + uint32_t clk, v; + int i; + + if ((sc->alc_flags & ALC_FLAG_LINK) != 0) + clk = MDIO_CLK_25_128; + else + clk = MDIO_CLK_25_4; + CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_WRITE | + ((val & MDIO_DATA_MASK) << MDIO_DATA_SHIFT) | MDIO_REG_ADDR(reg) | + MDIO_SUP_PREAMBLE | clk); + for (i = ALC_PHY_TIMEOUT; i > 0; i--) { + DELAY(5); + v = CSR_READ_4(sc, ALC_MDIO); + if ((v & MDIO_OP_BUSY) == 0) + break; + } + + if (i == 0) + device_printf(sc->alc_dev, "phy write timeout : %d\n", reg); + + return (0); +} + static void alc_miibus_statchg(device_t dev) { @@ -319,7 +425,6 @@ alc_miibus_statchg(device_t dev) break; } } - alc_stop_queue(sc); /* Stop Rx/Tx MACs. */ alc_stop_mac(sc); @@ -331,7 +436,159 @@ alc_miibus_statchg(device_t dev) 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, 0, IFM_SUBTYPE(mii->mii_media_active)); + alc_dsp_fixup(sc, IFM_SUBTYPE(mii->mii_media_active)); +} + +static uint32_t +alc_miidbg_readreg(struct alc_softc *sc, int reg) +{ + + alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, + reg); + return (alc_miibus_readreg(sc->alc_dev, sc->alc_phyaddr, + ALC_MII_DBG_DATA)); +} + +static uint32_t +alc_miidbg_writereg(struct alc_softc *sc, int reg, int val) +{ + + alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, + reg); + return (alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, + ALC_MII_DBG_DATA, val)); +} + +static uint32_t +alc_miiext_readreg(struct alc_softc *sc, int devaddr, int reg) +{ + uint32_t clk, v; + int i; + + CSR_WRITE_4(sc, ALC_EXT_MDIO, EXT_MDIO_REG(reg) | + EXT_MDIO_DEVADDR(devaddr)); + if ((sc->alc_flags & ALC_FLAG_LINK) != 0) + clk = MDIO_CLK_25_128; + else + clk = MDIO_CLK_25_4; + CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_READ | + MDIO_SUP_PREAMBLE | clk | MDIO_MODE_EXT); + for (i = ALC_PHY_TIMEOUT; i > 0; i--) { + DELAY(5); + v = CSR_READ_4(sc, ALC_MDIO); + if ((v & MDIO_OP_BUSY) == 0) + break; + } + + if (i == 0) { + device_printf(sc->alc_dev, "phy ext read timeout : %d, %d\n", + devaddr, reg); + return (0); + } + + return ((v & MDIO_DATA_MASK) >> MDIO_DATA_SHIFT); +} + +static uint32_t +alc_miiext_writereg(struct alc_softc *sc, int devaddr, int reg, int val) +{ + uint32_t clk, v; + int i; + + CSR_WRITE_4(sc, ALC_EXT_MDIO, EXT_MDIO_REG(reg) | + EXT_MDIO_DEVADDR(devaddr)); + if ((sc->alc_flags & ALC_FLAG_LINK) != 0) + clk = MDIO_CLK_25_128; + else + clk = MDIO_CLK_25_4; + CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_WRITE | + ((val & MDIO_DATA_MASK) << MDIO_DATA_SHIFT) | + MDIO_SUP_PREAMBLE | clk | MDIO_MODE_EXT); + for (i = ALC_PHY_TIMEOUT; i > 0; i--) { + DELAY(5); + v = CSR_READ_4(sc, ALC_MDIO); + if ((v & MDIO_OP_BUSY) == 0) + break; + } + + if (i == 0) + device_printf(sc->alc_dev, "phy ext write timeout : %d, %d\n", + devaddr, reg); + + return (0); +} + +static void +alc_dsp_fixup(struct alc_softc *sc, int media) +{ + uint16_t agc, len, val; + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + return; + if (AR816X_REV(sc->alc_rev) >= AR816X_REV_C0) + return; + + /* + * Vendor PHY magic. + * 1000BT/AZ, wrong cable length + */ + if ((sc->alc_flags & ALC_FLAG_LINK) != 0) { + len = alc_miiext_readreg(sc, MII_EXT_PCS, MII_EXT_CLDCTL6); + len = (len >> EXT_CLDCTL6_CAB_LEN_SHIFT) & + EXT_CLDCTL6_CAB_LEN_MASK; + agc = alc_miidbg_readreg(sc, MII_DBG_AGC); + agc = (agc >> DBG_AGC_2_VGA_SHIFT) & DBG_AGC_2_VGA_MASK; + if ((media == IFM_1000_T && len > EXT_CLDCTL6_CAB_LEN_SHORT1G && + agc > DBG_AGC_LONG1G_LIMT) || + (media == IFM_100_TX && len > DBG_AGC_LONG100M_LIMT && + agc > DBG_AGC_LONG1G_LIMT)) { + alc_miidbg_writereg(sc, MII_DBG_AZ_ANADECT, + DBG_AZ_ANADECT_LONG); + val = alc_miiext_readreg(sc, MII_EXT_ANEG, + MII_EXT_ANEG_AFE); + val |= ANEG_AFEE_10BT_100M_TH; + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_AFE, + val); + } else { + alc_miidbg_writereg(sc, MII_DBG_AZ_ANADECT, + DBG_AZ_ANADECT_DEFAULT); + val = alc_miiext_readreg(sc, MII_EXT_ANEG, + MII_EXT_ANEG_AFE); + val &= ~ANEG_AFEE_10BT_100M_TH; + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_AFE, + val); + } + if ((sc->alc_flags & ALC_FLAG_LINK_WAR) != 0 && + AR816X_REV(sc->alc_rev) == AR816X_REV_B0) { + if (media == IFM_1000_T) { + /* + * Giga link threshold, raise the tolerance of + * noise 50%. + */ + val = alc_miidbg_readreg(sc, MII_DBG_MSE20DB); + val &= ~DBG_MSE20DB_TH_MASK; + val |= (DBG_MSE20DB_TH_HI << + DBG_MSE20DB_TH_SHIFT); + alc_miidbg_writereg(sc, MII_DBG_MSE20DB, val); + } else if (media == IFM_100_TX) + alc_miidbg_writereg(sc, MII_DBG_MSE16DB, + DBG_MSE16DB_UP); + } + } else { + val = alc_miiext_readreg(sc, MII_EXT_ANEG, MII_EXT_ANEG_AFE); + val &= ~ANEG_AFEE_10BT_100M_TH; + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_AFE, val); + if ((sc->alc_flags & ALC_FLAG_LINK_WAR) != 0 && + AR816X_REV(sc->alc_rev) == AR816X_REV_B0) { + alc_miidbg_writereg(sc, MII_DBG_MSE16DB, + DBG_MSE16DB_DOWN); + val = alc_miidbg_readreg(sc, MII_DBG_MSE20DB); + val &= ~DBG_MSE20DB_TH_MASK; + val |= (DBG_MSE20DB_TH_DEFAULT << DBG_MSE20DB_TH_SHIFT); + alc_miidbg_writereg(sc, MII_DBG_MSE20DB, val); + } } } @@ -359,17 +616,29 @@ static int alc_mediachange(struct ifnet *ifp) { struct alc_softc *sc; - struct mii_data *mii; - struct mii_softc *miisc; int error; sc = ifp->if_softc; ALC_LOCK(sc); + error = alc_mediachange_locked(sc); + ALC_UNLOCK(sc); + + return (error); +} + +static int +alc_mediachange_locked(struct alc_softc *sc) +{ + struct mii_data *mii; + struct mii_softc *miisc; + int error; + + ALC_LOCK_ASSERT(sc); + mii = device_get_softc(sc->alc_miibus); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); - ALC_UNLOCK(sc); return (error); } @@ -407,7 +676,17 @@ alc_probe(device_t dev) static void alc_get_macaddr(struct alc_softc *sc) { - uint32_t ea[2], opt; + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + alc_get_macaddr_816x(sc); + else + alc_get_macaddr_813x(sc); +} + +static void +alc_get_macaddr_813x(struct alc_softc *sc) +{ + uint32_t opt; uint16_t val; int eeprom, i; @@ -502,6 +781,73 @@ alc_get_macaddr(struct alc_softc *sc) } } + alc_get_macaddr_par(sc); +} + +static void +alc_get_macaddr_816x(struct alc_softc *sc) +{ + uint32_t reg; + int i, reloaded; + + reloaded = 0; + /* Try to reload station address via TWSI. */ + for (i = 100; i > 0; i--) { + reg = CSR_READ_4(sc, ALC_SLD); + if ((reg & (SLD_PROGRESS | SLD_START)) == 0) + break; + DELAY(1000); + } + if (i != 0) { + CSR_WRITE_4(sc, ALC_SLD, reg | SLD_START); + for (i = 100; i > 0; i--) { + DELAY(1000); + reg = CSR_READ_4(sc, ALC_SLD); + if ((reg & SLD_START) == 0) + break; + } + if (i != 0) + reloaded++; + else if (bootverbose) + device_printf(sc->alc_dev, + "reloading station address via TWSI timed out!\n"); + } + + /* Try to reload station address from EEPROM or FLASH. */ + if (reloaded == 0) { + reg = CSR_READ_4(sc, ALC_EEPROM_LD); + if ((reg & (EEPROM_LD_EEPROM_EXIST | + EEPROM_LD_FLASH_EXIST)) != 0) { + for (i = 100; i > 0; i--) { + reg = CSR_READ_4(sc, ALC_EEPROM_LD); + if ((reg & (EEPROM_LD_PROGRESS | + EEPROM_LD_START)) == 0) + break; + DELAY(1000); + } + if (i != 0) { + CSR_WRITE_4(sc, ALC_EEPROM_LD, reg | + EEPROM_LD_START); + for (i = 100; i > 0; i--) { + DELAY(1000); + reg = CSR_READ_4(sc, ALC_EEPROM_LD); + if ((reg & EEPROM_LD_START) == 0) + break; + } + } else if (bootverbose) + device_printf(sc->alc_dev, + "reloading EEPROM/FLASH timed out!\n"); + } + } + + alc_get_macaddr_par(sc); +} + +static void +alc_get_macaddr_par(struct alc_softc *sc) +{ + uint32_t ea[2]; + ea[0] = CSR_READ_4(sc, ALC_PAR0); ea[1] = CSR_READ_4(sc, ALC_PAR1); sc->alc_eaddr[0] = (ea[1] >> 8) & 0xFF; @@ -517,19 +863,31 @@ alc_disable_l0s_l1(struct alc_softc *sc) { uint32_t pmcfg; - /* Another magic from vendor. */ - pmcfg = CSR_READ_4(sc, ALC_PM_CFG); - pmcfg &= ~(PM_CFG_L1_ENTRY_TIMER_MASK | PM_CFG_CLK_SWH_L1 | - PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB | PM_CFG_MAC_ASPM_CHK | - PM_CFG_SERDES_PD_EX_L1); - pmcfg |= PM_CFG_SERDES_BUDS_RX_L1_ENB | PM_CFG_SERDES_PLL_L1_ENB | - PM_CFG_SERDES_L1_ENB; - CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + /* Another magic from vendor. */ + pmcfg = CSR_READ_4(sc, ALC_PM_CFG); + pmcfg &= ~(PM_CFG_L1_ENTRY_TIMER_MASK | PM_CFG_CLK_SWH_L1 | + PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB | + PM_CFG_MAC_ASPM_CHK | PM_CFG_SERDES_PD_EX_L1); + pmcfg |= PM_CFG_SERDES_BUDS_RX_L1_ENB | + PM_CFG_SERDES_PLL_L1_ENB | PM_CFG_SERDES_L1_ENB; + CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); + } } static void alc_phy_reset(struct alc_softc *sc) { + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + alc_phy_reset_816x(sc); + else + alc_phy_reset_813x(sc); +} + +static void +alc_phy_reset_813x(struct alc_softc *sc) +{ uint16_t data; /* Reset magic from Linux. */ @@ -642,12 +1000,101 @@ alc_phy_reset(struct alc_softc *sc) } static void +alc_phy_reset_816x(struct alc_softc *sc) +{ + uint32_t val; + + val = CSR_READ_4(sc, ALC_GPHY_CFG); + val &= ~(GPHY_CFG_EXT_RESET | GPHY_CFG_LED_MODE | + GPHY_CFG_GATE_25M_ENB | GPHY_CFG_PHY_IDDQ | GPHY_CFG_PHY_PLL_ON | + GPHY_CFG_PWDOWN_HW | GPHY_CFG_100AB_ENB); + val |= GPHY_CFG_SEL_ANA_RESET; +#ifdef notyet + val |= GPHY_CFG_HIB_PULSE | GPHY_CFG_HIB_EN | GPHY_CFG_SEL_ANA_RESET; +#else + /* Disable PHY hibernation. */ + val &= ~(GPHY_CFG_HIB_PULSE | GPHY_CFG_HIB_EN); +#endif + CSR_WRITE_4(sc, ALC_GPHY_CFG, val); + DELAY(10); + CSR_WRITE_4(sc, ALC_GPHY_CFG, val | GPHY_CFG_EXT_RESET); + DELAY(800); + + /* Vendor PHY magic. */ +#ifdef notyet + alc_miidbg_writereg(sc, MII_DBG_LEGCYPS, DBG_LEGCYPS_DEFAULT); + alc_miidbg_writereg(sc, MII_DBG_SYSMODCTL, DBG_SYSMODCTL_DEFAULT); + alc_miiext_writereg(sc, MII_EXT_PCS, MII_EXT_VDRVBIAS, + EXT_VDRVBIAS_DEFAULT); +#else + /* Disable PHY hibernation. */ + alc_miidbg_writereg(sc, MII_DBG_LEGCYPS, + DBG_LEGCYPS_DEFAULT & ~DBG_LEGCYPS_ENB); + alc_miidbg_writereg(sc, MII_DBG_HIBNEG, + DBG_HIBNEG_DEFAULT & ~(DBG_HIBNEG_PSHIB_EN | DBG_HIBNEG_HIB_PULSE)); + alc_miidbg_writereg(sc, MII_DBG_GREENCFG, DBG_GREENCFG_DEFAULT); +#endif + + /* XXX Disable EEE. */ + val = CSR_READ_4(sc, ALC_LPI_CTL); + val &= ~LPI_CTL_ENB; + CSR_WRITE_4(sc, ALC_LPI_CTL, val); + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_LOCAL_EEEADV, 0); + + /* PHY power saving. */ + alc_miidbg_writereg(sc, MII_DBG_TST10BTCFG, DBG_TST10BTCFG_DEFAULT); + alc_miidbg_writereg(sc, MII_DBG_SRDSYSMOD, DBG_SRDSYSMOD_DEFAULT); + alc_miidbg_writereg(sc, MII_DBG_TST100BTCFG, DBG_TST100BTCFG_DEFAULT); + alc_miidbg_writereg(sc, MII_DBG_ANACTL, DBG_ANACTL_DEFAULT); + val = alc_miidbg_readreg(sc, MII_DBG_GREENCFG2); + val &= ~DBG_GREENCFG2_GATE_DFSE_EN; + alc_miidbg_writereg(sc, MII_DBG_GREENCFG2, val); + + /* RTL8139C, 120m issue. */ + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_NLP78, + ANEG_NLP78_120M_DEFAULT); + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_S3DIG10, + ANEG_S3DIG10_DEFAULT); + + if ((sc->alc_flags & ALC_FLAG_LINK_WAR) != 0) { + /* Turn off half amplitude. */ + val = alc_miiext_readreg(sc, MII_EXT_PCS, MII_EXT_CLDCTL3); + val |= EXT_CLDCTL3_BP_CABLE1TH_DET_GT; + alc_miiext_writereg(sc, MII_EXT_PCS, MII_EXT_CLDCTL3, val); + /* Turn off Green feature. */ + val = alc_miidbg_readreg(sc, MII_DBG_GREENCFG2); + val |= DBG_GREENCFG2_BP_GREEN; + alc_miidbg_writereg(sc, MII_DBG_GREENCFG2, val); + /* Turn off half bias. */ + val = alc_miiext_readreg(sc, MII_EXT_PCS, MII_EXT_CLDCTL5); + val |= EXT_CLDCTL5_BP_VD_HLFBIAS; + alc_miiext_writereg(sc, MII_EXT_PCS, MII_EXT_CLDCTL5, val); + } +} + +static void alc_phy_down(struct alc_softc *sc) { + uint32_t gphy; switch (sc->alc_ident->deviceid) { + case DEVICEID_ATHEROS_AR8161: + case DEVICEID_ATHEROS_E2200: + case DEVICEID_ATHEROS_AR8162: + case DEVICEID_ATHEROS_AR8171: + case DEVICEID_ATHEROS_AR8172: + gphy = CSR_READ_4(sc, ALC_GPHY_CFG); + gphy &= ~(GPHY_CFG_EXT_RESET | GPHY_CFG_LED_MODE | + GPHY_CFG_100AB_ENB | GPHY_CFG_PHY_PLL_ON); + gphy |= GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE | + GPHY_CFG_SEL_ANA_RESET; + gphy |= GPHY_CFG_PHY_IDDQ | GPHY_CFG_PWDOWN_HW; + CSR_WRITE_4(sc, ALC_GPHY_CFG, gphy); + break; case DEVICEID_ATHEROS_AR8151: case DEVICEID_ATHEROS_AR8151_V2: + case DEVICEID_ATHEROS_AR8152_B: + case DEVICEID_ATHEROS_AR8152_B2: /* * GPHY power down caused more problems on AR8151 v2.0. * When driver is reloaded after GPHY power down, @@ -673,12 +1120,23 @@ alc_phy_down(struct alc_softc *sc) } static void -alc_aspm(struct alc_softc *sc, int media) +alc_aspm(struct alc_softc *sc, int init, int media) +{ + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + alc_aspm_816x(sc, init); + else + alc_aspm_813x(sc, media); +} + +static void +alc_aspm_813x(struct alc_softc *sc, int media) { uint32_t pmcfg; uint16_t linkcfg; - ALC_LOCK_ASSERT(sc); + if ((sc->alc_flags & ALC_FLAG_LINK) == 0) + return; pmcfg = CSR_READ_4(sc, ALC_PM_CFG); if ((sc->alc_flags & (ALC_FLAG_APS | ALC_FLAG_PCIE)) == @@ -759,71 +1217,61 @@ alc_aspm(struct alc_softc *sc, int media) CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); } -static int -alc_attach(device_t dev) +static void +alc_aspm_816x(struct alc_softc *sc, int init) { - struct alc_softc *sc; - struct ifnet *ifp; - char *aspm_state[] = { "L0s/L1", "L0s", "L1", "L0s/L1" }; - uint16_t burst; - int base, error, i, msic, msixc, state; - uint32_t cap, ctl, val; - - error = 0; - sc = device_get_softc(dev); - sc->alc_dev = dev; - - mtx_init(&sc->alc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF); - callout_init_mtx(&sc->alc_tick_ch, &sc->alc_mtx, 0); - TASK_INIT(&sc->alc_int_task, 0, alc_int_task, sc); - sc->alc_ident = alc_find_ident(dev); + uint32_t pmcfg; - /* Map the device. */ - pci_enable_busmaster(dev); - sc->alc_res_spec = alc_res_spec_mem; - sc->alc_irq_spec = alc_irq_spec_legacy; - error = bus_alloc_resources(dev, sc->alc_res_spec, sc->alc_res); - if (error != 0) { - device_printf(dev, "cannot allocate memory resources.\n"); - goto fail; + pmcfg = CSR_READ_4(sc, ALC_PM_CFG); + pmcfg &= ~PM_CFG_L1_ENTRY_TIMER_816X_MASK; + pmcfg |= PM_CFG_L1_ENTRY_TIMER_816X_DEFAULT; + pmcfg &= ~PM_CFG_PM_REQ_TIMER_MASK; + pmcfg |= PM_CFG_PM_REQ_TIMER_816X_DEFAULT; + pmcfg &= ~PM_CFG_LCKDET_TIMER_MASK; + pmcfg |= PM_CFG_LCKDET_TIMER_DEFAULT; + pmcfg |= PM_CFG_SERDES_PD_EX_L1 | PM_CFG_CLK_SWH_L1 | PM_CFG_PCIE_RECV; + pmcfg &= ~(PM_CFG_RX_L1_AFTER_L0S | PM_CFG_TX_L1_AFTER_L0S | + PM_CFG_ASPM_L1_ENB | PM_CFG_ASPM_L0S_ENB | + PM_CFG_SERDES_L1_ENB | PM_CFG_SERDES_PLL_L1_ENB | + PM_CFG_SERDES_BUDS_RX_L1_ENB | PM_CFG_SA_DLY_ENB | + PM_CFG_MAC_ASPM_CHK | PM_CFG_HOTRST); + if (AR816X_REV(sc->alc_rev) <= AR816X_REV_A1 && + (sc->alc_rev & 0x01) != 0) + pmcfg |= PM_CFG_SERDES_L1_ENB | PM_CFG_SERDES_PLL_L1_ENB; + if ((sc->alc_flags & ALC_FLAG_LINK) != 0) { + /* Link up, enable both L0s, L1s. */ + pmcfg |= PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB | + PM_CFG_MAC_ASPM_CHK; + } else { + if (init != 0) + pmcfg |= PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB | + PM_CFG_MAC_ASPM_CHK; + else if ((sc->alc_ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + pmcfg |= PM_CFG_ASPM_L1_ENB | PM_CFG_MAC_ASPM_CHK; } + CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); +} - /* Set PHY address. */ - sc->alc_phyaddr = ALC_PHY_ADDR; +static void +alc_init_pcie(struct alc_softc *sc) +{ + const char *aspm_state[] = { "L0s/L1", "L0s", "L1", "L0s/L1" }; + uint32_t cap, ctl, val; + int state; - /* Initialize DMA parameters. */ - sc->alc_dma_rd_burst = 0; - sc->alc_dma_wr_burst = 0; - sc->alc_rcb = DMA_CFG_RCB_64; - if (pci_find_cap(dev, PCIY_EXPRESS, &base) == 0) { - sc->alc_flags |= ALC_FLAG_PCIE; - sc->alc_expcap = base; - burst = CSR_READ_2(sc, base + PCIER_DEVICE_CTL); - sc->alc_dma_rd_burst = - (burst & PCIEM_CTL_MAX_READ_REQUEST) >> 12; - sc->alc_dma_wr_burst = (burst & PCIEM_CTL_MAX_PAYLOAD) >> 5; - if (bootverbose) { - device_printf(dev, "Read request size : %u bytes.\n", - alc_dma_burst[sc->alc_dma_rd_burst]); - device_printf(dev, "TLP payload size : %u bytes.\n", - alc_dma_burst[sc->alc_dma_wr_burst]); - } - if (alc_dma_burst[sc->alc_dma_rd_burst] > 1024) - sc->alc_dma_rd_burst = 3; - if (alc_dma_burst[sc->alc_dma_wr_burst] > 1024) - sc->alc_dma_wr_burst = 3; - /* Clear data link and flow-control protocol error. */ - 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); + /* Clear data link and flow-control protocol error. */ + 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); + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { 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 == DEVICEID_ATHEROS_AR8152_B && - pci_get_revid(dev) == ATHEROS_AR8152_B_V10) { + 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); @@ -832,13 +1280,13 @@ alc_attach(device_t dev) CSR_WRITE_4(sc, ALC_PCIE_PHYMISC2, val); } /* Disable ASPM L0S and L1. */ - cap = CSR_READ_2(sc, base + PCIER_LINK_CAP); + cap = CSR_READ_2(sc, sc->alc_expcap + PCIER_LINK_CAP); if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { - ctl = CSR_READ_2(sc, base + PCIER_LINK_CTL); + ctl = CSR_READ_2(sc, sc->alc_expcap + PCIER_LINK_CTL); if ((ctl & PCIEM_LINK_CTL_RCB) != 0) sc->alc_rcb = DMA_CFG_RCB_128; if (bootverbose) - device_printf(dev, "RCB %u bytes\n", + device_printf(sc->alc_dev, "RCB %u bytes\n", sc->alc_rcb == DMA_CFG_RCB_64 ? 64 : 128); state = ctl & PCIEM_LINK_CTL_ASPMC; if (state & PCIEM_LINK_CTL_ASPMC_L0S) @@ -855,13 +1303,91 @@ alc_attach(device_t dev) device_printf(sc->alc_dev, "no ASPM support\n"); } + } else { + val = CSR_READ_4(sc, ALC_PDLL_TRNS1); + val &= ~PDLL_TRNS1_D3PLLOFF_ENB; + CSR_WRITE_4(sc, ALC_PDLL_TRNS1, val); + val = CSR_READ_4(sc, ALC_MASTER_CFG); + if (AR816X_REV(sc->alc_rev) <= AR816X_REV_A1 && + (sc->alc_rev & 0x01) != 0) { + if ((val & MASTER_WAKEN_25M) == 0 || + (val & MASTER_CLK_SEL_DIS) == 0) { + val |= MASTER_WAKEN_25M | MASTER_CLK_SEL_DIS; + CSR_WRITE_4(sc, ALC_MASTER_CFG, val); + } + } else { + if ((val & MASTER_WAKEN_25M) == 0 || + (val & MASTER_CLK_SEL_DIS) != 0) { + val |= MASTER_WAKEN_25M; + val &= ~MASTER_CLK_SEL_DIS; + CSR_WRITE_4(sc, ALC_MASTER_CFG, val); + } + } } + alc_aspm(sc, 1, IFM_UNKNOWN); +} - /* Reset PHY. */ - alc_phy_reset(sc); +static void +alc_config_msi(struct alc_softc *sc) +{ + uint32_t ctl, mod; - /* Reset the ethernet controller. */ - alc_reset(sc); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + /* + * It seems interrupt moderation is controlled by + * ALC_MSI_RETRANS_TIMER register if MSI/MSIX is active. + * Driver uses RX interrupt moderation parameter to + * program ALC_MSI_RETRANS_TIMER register. + */ + ctl = CSR_READ_4(sc, ALC_MSI_RETRANS_TIMER); + ctl &= ~MSI_RETRANS_TIMER_MASK; + ctl &= ~MSI_RETRANS_MASK_SEL_LINE; + mod = ALC_USECS(sc->alc_int_rx_mod); + if (mod == 0) + mod = 1; + ctl |= mod; + if ((sc->alc_flags & ALC_FLAG_MSIX) != 0) + CSR_WRITE_4(sc, ALC_MSI_RETRANS_TIMER, ctl | + MSI_RETRANS_MASK_SEL_STD); + else if ((sc->alc_flags & ALC_FLAG_MSI) != 0) + CSR_WRITE_4(sc, ALC_MSI_RETRANS_TIMER, ctl | + MSI_RETRANS_MASK_SEL_LINE); + else + CSR_WRITE_4(sc, ALC_MSI_RETRANS_TIMER, 0); + } +} + +static int +alc_attach(device_t dev) +{ + struct alc_softc *sc; + struct ifnet *ifp; + int base, error, i, msic, msixc; + uint16_t burst; + + error = 0; + sc = device_get_softc(dev); + sc->alc_dev = dev; + sc->alc_rev = pci_get_revid(dev); + + mtx_init(&sc->alc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->alc_tick_ch, &sc->alc_mtx, 0); + TASK_INIT(&sc->alc_int_task, 0, alc_int_task, sc); + sc->alc_ident = alc_find_ident(dev); + + /* Map the device. */ + pci_enable_busmaster(dev); + sc->alc_res_spec = alc_res_spec_mem; + sc->alc_irq_spec = alc_irq_spec_legacy; + error = bus_alloc_resources(dev, sc->alc_res_spec, sc->alc_res); + if (error != 0) { + device_printf(dev, "cannot allocate memory resources.\n"); + goto fail; + } + + /* Set PHY address. */ + sc->alc_phyaddr = ALC_PHY_ADDR; /* * One odd thing is AR8132 uses the same PHY hardware(F1 @@ -871,6 +1397,19 @@ alc_attach(device_t dev) * shows the same PHY model/revision number of AR8131. */ switch (sc->alc_ident->deviceid) { + case DEVICEID_ATHEROS_AR8161: + if (pci_get_subvendor(dev) == VENDORID_ATHEROS && + pci_get_subdevice(dev) == 0x0091 && sc->alc_rev == 0) + sc->alc_flags |= ALC_FLAG_LINK_WAR; + /* FALLTHROUGH */ + case DEVICEID_ATHEROS_E2200: + case DEVICEID_ATHEROS_AR8171: + sc->alc_flags |= ALC_FLAG_AR816X_FAMILY; + break; + case DEVICEID_ATHEROS_AR8162: + case DEVICEID_ATHEROS_AR8172: + sc->alc_flags |= ALC_FLAG_FASTETHER | ALC_FLAG_AR816X_FAMILY; + break; case DEVICEID_ATHEROS_AR8152_B: case DEVICEID_ATHEROS_AR8152_B2: sc->alc_flags |= ALC_FLAG_APS; @@ -885,7 +1424,7 @@ alc_attach(device_t dev) default: break; } - sc->alc_flags |= ALC_FLAG_ASPM_MON | ALC_FLAG_JUMBO; + sc->alc_flags |= ALC_FLAG_JUMBO; /* * It seems that AR813x/AR815x has silicon bug for SMB. In @@ -898,7 +1437,6 @@ alc_attach(device_t dev) * Don't use Tx CMB. It is known to have silicon bug. */ sc->alc_flags |= ALC_FLAG_CMB_BUG; - sc->alc_rev = pci_get_revid(dev); sc->alc_chip_rev = CSR_READ_4(sc, ALC_MASTER_CFG) >> MASTER_CHIP_REV_SHIFT; if (bootverbose) { @@ -906,11 +1444,45 @@ alc_attach(device_t dev) sc->alc_rev); device_printf(dev, "Chip id/revision : 0x%04x\n", sc->alc_chip_rev); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + device_printf(dev, "AR816x revision : 0x%x\n", + AR816X_REV(sc->alc_rev)); } device_printf(dev, "%u Tx FIFO, %u Rx FIFO\n", CSR_READ_4(sc, ALC_SRAM_TX_FIFO_LEN) * 8, CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN) * 8); + /* Initialize DMA parameters. */ + sc->alc_dma_rd_burst = 0; + sc->alc_dma_wr_burst = 0; + sc->alc_rcb = DMA_CFG_RCB_64; + if (pci_find_cap(dev, PCIY_EXPRESS, &base) == 0) { + sc->alc_flags |= ALC_FLAG_PCIE; + sc->alc_expcap = base; + burst = CSR_READ_2(sc, base + PCIER_DEVICE_CTL); + sc->alc_dma_rd_burst = + (burst & PCIEM_CTL_MAX_READ_REQUEST) >> 12; + sc->alc_dma_wr_burst = (burst & PCIEM_CTL_MAX_PAYLOAD) >> 5; + if (bootverbose) { + device_printf(dev, "Read request size : %u bytes.\n", + alc_dma_burst[sc->alc_dma_rd_burst]); + device_printf(dev, "TLP payload size : %u bytes.\n", + alc_dma_burst[sc->alc_dma_wr_burst]); + } + if (alc_dma_burst[sc->alc_dma_rd_burst] > 1024) + sc->alc_dma_rd_burst = 3; + if (alc_dma_burst[sc->alc_dma_wr_burst] > 1024) + sc->alc_dma_wr_burst = 3; + alc_init_pcie(sc); + } + + /* Reset PHY. */ + alc_phy_reset(sc); + + /* Reset the ethernet controller. */ + alc_stop_mac(sc); + alc_reset(sc); + /* Allocate IRQ resources. */ msixc = pci_msix_count(dev); msic = pci_msi_count(dev); @@ -918,11 +1490,20 @@ alc_attach(device_t dev) device_printf(dev, "MSIX count : %d\n", msixc); device_printf(dev, "MSI count : %d\n", msic); } - /* Prefer MSIX over MSI. */ + if (msixc > 1) + msixc = 1; + if (msic > 1) + msic = 1; + /* + * Prefer MSIX over MSI. + * AR816x controller has a silicon bug that MSI interrupt + * does not assert if PCIM_CMD_INTxDIS bit of command + * register is set. pci(4) was taught to handle that case. + */ if (msix_disable == 0 || msi_disable == 0) { - if (msix_disable == 0 && msixc == ALC_MSIX_MESSAGES && + if (msix_disable == 0 && msixc > 0 && pci_alloc_msix(dev, &msixc) == 0) { - if (msic == ALC_MSIX_MESSAGES) { + if (msic == 1) { device_printf(dev, "Using %d MSIX message(s).\n", msixc); sc->alc_flags |= ALC_FLAG_MSIX; @@ -931,9 +1512,8 @@ alc_attach(device_t dev) pci_release_msi(dev); } if (msi_disable == 0 && (sc->alc_flags & ALC_FLAG_MSIX) == 0 && - msic == ALC_MSI_MESSAGES && - pci_alloc_msi(dev, &msic) == 0) { - if (msic == ALC_MSI_MESSAGES) { + msic > 0 && pci_alloc_msi(dev, &msic) == 0) { + if (msic == 1) { device_printf(dev, "Using %d MSI message(s).\n", msic); sc->alc_flags |= ALC_FLAG_MSI; @@ -1007,9 +1587,13 @@ alc_attach(device_t dev) * sample boards. To safety, don't enable Tx checksum offloading * by default but give chance to users to toggle it if they know * their controllers work without problems. + * Fortunately, Tx checksum offloading for AR816x family + * seems to work. */ - ifp->if_capenable &= ~IFCAP_TXCSUM; - ifp->if_hwassist &= ~ALC_CSUM_FEATURES; + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + ifp->if_capenable &= ~IFCAP_TXCSUM; + ifp->if_hwassist &= ~ALC_CSUM_FEATURES; + } /* Tell the upper layer(s) we support long frames. */ ifp->if_hdrlen = sizeof(struct ether_vlan_header); @@ -1026,6 +1610,7 @@ alc_attach(device_t dev) taskqueue_start_threads(&sc->alc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->alc_dev)); + alc_config_msi(sc); if ((sc->alc_flags & ALC_FLAG_MSIX) != 0) msic = ALC_MSIX_MESSAGES; else if ((sc->alc_flags & ALC_FLAG_MSI) != 0) @@ -1287,8 +1872,6 @@ alc_sysctl_node(struct alc_softc *sc) &stats->tx_late_colls, "Late collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "excess_colls", &stats->tx_excess_colls, "Excessive collisions"); - ALC_SYSCTL_STAT_ADD32(ctx, child, "abort", - &stats->tx_abort, "Aborted frames due to Excessive collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "underruns", &stats->tx_underrun, "FIFO underruns"); ALC_SYSCTL_STAT_ADD32(ctx, child, "desc_underruns", @@ -1600,7 +2183,7 @@ again: /* * Create Tx buffer parent tag. - * AR813x/AR815x allows 64bit DMA addressing of Tx/Rx buffers + * AR81[3567]x allows 64bit DMA addressing of Tx/Rx buffers * so it needs separate parent DMA tag as parent DMA address * space could be restricted to be within 32bit address space * by 4GB boundary crossing. @@ -1902,6 +2485,16 @@ alc_setlinkspeed(struct alc_softc *sc) static void alc_setwol(struct alc_softc *sc) { + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + alc_setwol_816x(sc); + else + alc_setwol_813x(sc); +} + +static void +alc_setwol_813x(struct alc_softc *sc) +{ struct ifnet *ifp; uint32_t reg, pmcs; uint16_t pmstat; @@ -1962,6 +2555,72 @@ alc_setwol(struct alc_softc *sc) sc->alc_pmcap + PCIR_POWER_STATUS, pmstat, 2); } +static void +alc_setwol_816x(struct alc_softc *sc) +{ + struct ifnet *ifp; + uint32_t gphy, mac, master, pmcs, reg; + uint16_t pmstat; + + ALC_LOCK_ASSERT(sc); + + ifp = sc->alc_ifp; + master = CSR_READ_4(sc, ALC_MASTER_CFG); + master &= ~MASTER_CLK_SEL_DIS; + gphy = CSR_READ_4(sc, ALC_GPHY_CFG); + gphy &= ~(GPHY_CFG_EXT_RESET | GPHY_CFG_LED_MODE | GPHY_CFG_100AB_ENB | + GPHY_CFG_PHY_PLL_ON); + gphy |= GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE | GPHY_CFG_SEL_ANA_RESET; + if ((sc->alc_flags & ALC_FLAG_PM) == 0) { + CSR_WRITE_4(sc, ALC_WOL_CFG, 0); + gphy |= GPHY_CFG_PHY_IDDQ | GPHY_CFG_PWDOWN_HW; + mac = CSR_READ_4(sc, ALC_MAC_CFG); + } else { + if ((ifp->if_capenable & IFCAP_WOL) != 0) { + gphy |= GPHY_CFG_EXT_RESET; + if ((sc->alc_flags & ALC_FLAG_FASTETHER) == 0) + alc_setlinkspeed(sc); + } + pmcs = 0; + if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) + pmcs |= WOL_CFG_MAGIC | WOL_CFG_MAGIC_ENB; + CSR_WRITE_4(sc, ALC_WOL_CFG, pmcs); + mac = CSR_READ_4(sc, ALC_MAC_CFG); + mac &= ~(MAC_CFG_DBG | MAC_CFG_PROMISC | MAC_CFG_ALLMULTI | + MAC_CFG_BCAST); + if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) + mac |= MAC_CFG_ALLMULTI | MAC_CFG_BCAST; + if ((ifp->if_capenable & IFCAP_WOL) != 0) + mac |= MAC_CFG_RX_ENB; + alc_miiext_writereg(sc, MII_EXT_ANEG, MII_EXT_ANEG_S3DIG10, + ANEG_S3DIG10_SL); + } + + /* Enable OSC. */ + reg = CSR_READ_4(sc, ALC_MISC); + reg &= ~MISC_INTNLOSC_OPEN; + CSR_WRITE_4(sc, ALC_MISC, reg); + reg |= MISC_INTNLOSC_OPEN; + CSR_WRITE_4(sc, ALC_MISC, reg); + CSR_WRITE_4(sc, ALC_MASTER_CFG, master); + CSR_WRITE_4(sc, ALC_MAC_CFG, mac); + CSR_WRITE_4(sc, ALC_GPHY_CFG, gphy); + reg = CSR_READ_4(sc, ALC_PDLL_TRNS1); + reg |= PDLL_TRNS1_D3PLLOFF_ENB; + CSR_WRITE_4(sc, ALC_PDLL_TRNS1, reg); + + if ((sc->alc_flags & ALC_FLAG_PM) != 0) { + /* Request PME. */ + pmstat = pci_read_config(sc->alc_dev, + sc->alc_pmcap + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + if ((ifp->if_capenable & IFCAP_WOL) != 0) + pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->alc_dev, + sc->alc_pmcap + PCIR_POWER_STATUS, pmstat, 2); + } +} + static int alc_suspend(device_t dev) { @@ -2032,7 +2691,7 @@ alc_encap(struct alc_softc *sc, struct mbuf **m_head) ip_off = poff = 0; if ((m->m_pkthdr.csum_flags & (ALC_CSUM_FEATURES | CSUM_TSO)) != 0) { /* - * AR813x/AR815x requires offset of TCP/UDP header in its + * AR81[3567]x requires offset of TCP/UDP header in its * Tx descriptor to perform Tx checksum offloading. TSO * also requires TCP header offset and modification of * IP/TCP header. This kind of operation takes many CPU @@ -2170,7 +2829,7 @@ alc_encap(struct alc_softc *sc, struct mbuf **m_head) cflags |= (poff << TD_TCPHDR_OFFSET_SHIFT) & TD_TCPHDR_OFFSET_MASK; /* - * AR813x/AR815x requires the first buffer should + * AR81[3567]x requires the first buffer should * only hold IP/TCP header data. Payload should * be handled in other descriptors. */ @@ -2301,10 +2960,14 @@ alc_start_locked(struct ifnet *ifp) bus_dmamap_sync(sc->alc_cdata.alc_tx_ring_tag, sc->alc_cdata.alc_tx_ring_map, BUS_DMASYNC_PREWRITE); /* Kick. Assume we're using normal Tx priority queue. */ - CSR_WRITE_4(sc, ALC_MBOX_TD_PROD_IDX, - (sc->alc_cdata.alc_tx_prod << - MBOX_TD_PROD_LO_IDX_SHIFT) & - MBOX_TD_PROD_LO_IDX_MASK); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + CSR_WRITE_2(sc, ALC_MBOX_TD_PRI0_PROD_IDX, + (uint16_t)sc->alc_cdata.alc_tx_prod); + else + CSR_WRITE_4(sc, ALC_MBOX_TD_PROD_IDX, + (sc->alc_cdata.alc_tx_prod << + MBOX_TD_PROD_LO_IDX_SHIFT) & + MBOX_TD_PROD_LO_IDX_MASK); /* Set a timeout in case the chip goes out to lunch. */ sc->alc_watchdog_timer = ALC_TX_TIMEOUT; } @@ -2358,7 +3021,7 @@ alc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) else if (ifp->if_mtu != ifr->ifr_mtu) { ALC_LOCK(sc); ifp->if_mtu = ifr->ifr_mtu; - /* AR813x/AR815x has 13 bits MSS field. */ + /* AR81[3567]x has 13 bits MSS field. */ if (ifp->if_mtu > ALC_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; @@ -2409,7 +3072,7 @@ alc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) (ifp->if_capabilities & IFCAP_TSO4) != 0) { ifp->if_capenable ^= IFCAP_TSO4; if ((ifp->if_capenable & IFCAP_TSO4) != 0) { - /* AR813x/AR815x has 13 bits MSS field. */ + /* AR81[3567]x has 13 bits MSS field. */ if (ifp->if_mtu > ALC_TSO_MTU) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; @@ -2461,7 +3124,8 @@ alc_mac_config(struct alc_softc *sc) 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 == DEVICEID_ATHEROS_AR8151 || + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0 || + sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8151 || sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8151_V2 || sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8152_B2) reg |= MAC_CFG_HASH_ALG_CRC32 | MAC_CFG_SPEED_MODE_SW; @@ -2599,7 +3263,6 @@ alc_stats_update(struct alc_softc *sc) stat->tx_multi_colls += smb->tx_multi_colls; stat->tx_late_colls += smb->tx_late_colls; stat->tx_excess_colls += smb->tx_excess_colls; - stat->tx_abort += smb->tx_abort; stat->tx_underrun += smb->tx_underrun; stat->tx_desc_underrun += smb->tx_desc_underrun; stat->tx_lenerrs += smb->tx_lenerrs; @@ -2612,17 +3275,10 @@ alc_stats_update(struct alc_softc *sc) if_inc_counter(ifp, IFCOUNTER_COLLISIONS, smb->tx_single_colls + smb->tx_multi_colls * 2 + smb->tx_late_colls + - smb->tx_abort * HDPX_CFG_RETRY_DEFAULT); + smb->tx_excess_colls * HDPX_CFG_RETRY_DEFAULT); - /* - * XXX - * tx_pkts_truncated counter looks suspicious. It constantly - * increments with no sign of Tx errors. This may indicate - * the counter name is not correct one so I've removed the - * counter in output errors. - */ - if_inc_counter(ifp, IFCOUNTER_OERRORS, - smb->tx_abort + smb->tx_late_colls + smb->tx_underrun); + if_inc_counter(ifp, IFCOUNTER_OERRORS, smb->tx_late_colls + + smb->tx_excess_colls + smb->tx_underrun + smb->tx_pkts_truncated); if_inc_counter(ifp, IFCOUNTER_IPACKETS, smb->rx_frames); @@ -2751,11 +3407,16 @@ alc_txeof(struct alc_softc *sc) bus_dmamap_sync(sc->alc_cdata.alc_cmb_tag, sc->alc_cdata.alc_cmb_map, BUS_DMASYNC_POSTREAD); prod = sc->alc_rdata.alc_cmb->cons; - } else - prod = CSR_READ_4(sc, ALC_MBOX_TD_CONS_IDX); - /* Assume we're using normal Tx priority queue. */ - prod = (prod & MBOX_TD_CONS_LO_IDX_MASK) >> - MBOX_TD_CONS_LO_IDX_SHIFT; + } else { + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + prod = CSR_READ_2(sc, ALC_MBOX_TD_PRI0_CONS_IDX); + else { + prod = CSR_READ_4(sc, ALC_MBOX_TD_CONS_IDX); + /* Assume we're using normal Tx priority queue. */ + prod = (prod & MBOX_TD_CONS_LO_IDX_MASK) >> + MBOX_TD_CONS_LO_IDX_SHIFT; + } + } cons = sc->alc_cdata.alc_tx_cons; /* * Go through our Tx list and free mbufs for those @@ -2891,8 +3552,12 @@ alc_rxintr(struct alc_softc *sc, int count) * it still seems that pre-fetching needs more * experimentation. */ - CSR_WRITE_4(sc, ALC_MBOX_RD0_PROD_IDX, - sc->alc_cdata.alc_rx_cons); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + CSR_WRITE_2(sc, ALC_MBOX_RD0_PROD_IDX, + (uint16_t)sc->alc_cdata.alc_rx_cons); + else + CSR_WRITE_4(sc, ALC_MBOX_RD0_PROD_IDX, + sc->alc_cdata.alc_rx_cons); } return (count > 0 ? 0 : EAGAIN); @@ -3084,14 +3749,78 @@ alc_tick(void *arg) } static void -alc_reset(struct alc_softc *sc) +alc_osc_reset(struct alc_softc *sc) { uint32_t reg; + + reg = CSR_READ_4(sc, ALC_MISC3); + reg &= ~MISC3_25M_BY_SW; + reg |= MISC3_25M_NOTO_INTNL; + CSR_WRITE_4(sc, ALC_MISC3, reg); + + reg = CSR_READ_4(sc, ALC_MISC); + if (AR816X_REV(sc->alc_rev) >= AR816X_REV_B0) { + /* + * Restore over-current protection default value. + * This value could be reset by MAC reset. + */ + reg &= ~MISC_PSW_OCP_MASK; + reg |= (MISC_PSW_OCP_DEFAULT << MISC_PSW_OCP_SHIFT); + reg &= ~MISC_INTNLOSC_OPEN; + CSR_WRITE_4(sc, ALC_MISC, reg); + CSR_WRITE_4(sc, ALC_MISC, reg | MISC_INTNLOSC_OPEN); + reg = CSR_READ_4(sc, ALC_MISC2); + reg &= ~MISC2_CALB_START; + CSR_WRITE_4(sc, ALC_MISC2, reg); + CSR_WRITE_4(sc, ALC_MISC2, reg | MISC2_CALB_START); + + } else { + reg &= ~MISC_INTNLOSC_OPEN; + /* Disable isolate for revision A devices. */ + if (AR816X_REV(sc->alc_rev) <= AR816X_REV_A1) + reg &= ~MISC_ISO_ENB; + CSR_WRITE_4(sc, ALC_MISC, reg | MISC_INTNLOSC_OPEN); + CSR_WRITE_4(sc, ALC_MISC, reg); + } + + DELAY(20); +} + +static void +alc_reset(struct alc_softc *sc) +{ + uint32_t pmcfg, reg; int i; - reg = CSR_READ_4(sc, ALC_MASTER_CFG) & 0xFFFF; + pmcfg = 0; + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + /* Reset workaround. */ + CSR_WRITE_4(sc, ALC_MBOX_RD0_PROD_IDX, 1); + if (AR816X_REV(sc->alc_rev) <= AR816X_REV_A1 && + (sc->alc_rev & 0x01) != 0) { + /* Disable L0s/L1s before reset. */ + pmcfg = CSR_READ_4(sc, ALC_PM_CFG); + if ((pmcfg & (PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB)) + != 0) { + pmcfg &= ~(PM_CFG_ASPM_L0S_ENB | + PM_CFG_ASPM_L1_ENB); + CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); + } + } + } + reg = CSR_READ_4(sc, ALC_MASTER_CFG); reg |= MASTER_OOB_DIS_OFF | MASTER_RESET; CSR_WRITE_4(sc, ALC_MASTER_CFG, reg); + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + for (i = ALC_RESET_TIMEOUT; i > 0; i--) { + DELAY(10); + if (CSR_READ_4(sc, ALC_MBOX_RD0_PROD_IDX) == 0) + break; + } + if (i == 0) + device_printf(sc->alc_dev, "MAC reset timeout!\n"); + } for (i = ALC_RESET_TIMEOUT; i > 0; i--) { DELAY(10); if ((CSR_READ_4(sc, ALC_MASTER_CFG) & MASTER_RESET) == 0) @@ -3101,13 +3830,45 @@ alc_reset(struct alc_softc *sc) device_printf(sc->alc_dev, "master reset timeout!\n"); for (i = ALC_RESET_TIMEOUT; i > 0; i--) { - if ((reg = CSR_READ_4(sc, ALC_IDLE_STATUS)) == 0) + reg = CSR_READ_4(sc, ALC_IDLE_STATUS); + if ((reg & (IDLE_STATUS_RXMAC | IDLE_STATUS_TXMAC | + IDLE_STATUS_RXQ | IDLE_STATUS_TXQ)) == 0) break; DELAY(10); } - if (i == 0) device_printf(sc->alc_dev, "reset timeout(0x%08x)!\n", reg); + + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + if (AR816X_REV(sc->alc_rev) <= AR816X_REV_A1 && + (sc->alc_rev & 0x01) != 0) { + reg = CSR_READ_4(sc, ALC_MASTER_CFG); + reg |= MASTER_CLK_SEL_DIS; + CSR_WRITE_4(sc, ALC_MASTER_CFG, reg); + /* Restore L0s/L1s config. */ + if ((pmcfg & (PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB)) + != 0) + CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); + } + + alc_osc_reset(sc); + reg = CSR_READ_4(sc, ALC_MISC3); + reg &= ~MISC3_25M_BY_SW; + reg |= MISC3_25M_NOTO_INTNL; + CSR_WRITE_4(sc, ALC_MISC3, reg); + reg = CSR_READ_4(sc, ALC_MISC); + reg &= ~MISC_INTNLOSC_OPEN; + if (AR816X_REV(sc->alc_rev) <= AR816X_REV_A1) + reg &= ~MISC_ISO_ENB; + CSR_WRITE_4(sc, ALC_MISC, reg); + DELAY(20); + } + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0 || + sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8152_B || + sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8151_V2) + CSR_WRITE_4(sc, ALC_SERDES_LOCK, + CSR_READ_4(sc, ALC_SERDES_LOCK) | SERDES_MAC_CLK_SLOWDOWN | + SERDES_PHY_CLK_SLOWDOWN); } static void @@ -3158,7 +3919,16 @@ alc_init_locked(struct alc_softc *sc) alc_init_smb(sc); /* Enable all clocks. */ - CSR_WRITE_4(sc, ALC_CLK_GATING_CFG, 0); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + CSR_WRITE_4(sc, ALC_CLK_GATING_CFG, CLK_GATING_DMAW_ENB | + CLK_GATING_DMAR_ENB | CLK_GATING_TXQ_ENB | + CLK_GATING_RXQ_ENB | CLK_GATING_TXMAC_ENB | + CLK_GATING_RXMAC_ENB); + if (AR816X_REV(sc->alc_rev) >= AR816X_REV_B0) + CSR_WRITE_4(sc, ALC_IDLE_DECISN_TIMER, + IDLE_DECISN_TIMER_DEFAULT_1MS); + } else + CSR_WRITE_4(sc, ALC_CLK_GATING_CFG, 0); /* Reprogram the station address. */ bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); @@ -3184,10 +3954,12 @@ alc_init_locked(struct alc_softc *sc) paddr = sc->alc_rdata.alc_rx_ring_paddr; CSR_WRITE_4(sc, ALC_RX_BASE_ADDR_HI, ALC_ADDR_HI(paddr)); CSR_WRITE_4(sc, ALC_RD0_HEAD_ADDR_LO, ALC_ADDR_LO(paddr)); - /* We use one Rx ring. */ - CSR_WRITE_4(sc, ALC_RD1_HEAD_ADDR_LO, 0); - CSR_WRITE_4(sc, ALC_RD2_HEAD_ADDR_LO, 0); - CSR_WRITE_4(sc, ALC_RD3_HEAD_ADDR_LO, 0); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + /* We use one Rx ring. */ + CSR_WRITE_4(sc, ALC_RD1_HEAD_ADDR_LO, 0); + CSR_WRITE_4(sc, ALC_RD2_HEAD_ADDR_LO, 0); + CSR_WRITE_4(sc, ALC_RD3_HEAD_ADDR_LO, 0); + } /* Set Rx descriptor counter. */ CSR_WRITE_4(sc, ALC_RD_RING_CNT, (ALC_RX_RING_CNT << RD_RING_CNT_SHIFT) & RD_RING_CNT_MASK); @@ -3212,10 +3984,12 @@ alc_init_locked(struct alc_softc *sc) paddr = sc->alc_rdata.alc_rr_ring_paddr; /* Set Rx return descriptor base addresses. */ CSR_WRITE_4(sc, ALC_RRD0_HEAD_ADDR_LO, ALC_ADDR_LO(paddr)); - /* We use one Rx return ring. */ - CSR_WRITE_4(sc, ALC_RRD1_HEAD_ADDR_LO, 0); - CSR_WRITE_4(sc, ALC_RRD2_HEAD_ADDR_LO, 0); - CSR_WRITE_4(sc, ALC_RRD3_HEAD_ADDR_LO, 0); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + /* We use one Rx return ring. */ + CSR_WRITE_4(sc, ALC_RRD1_HEAD_ADDR_LO, 0); + CSR_WRITE_4(sc, ALC_RRD2_HEAD_ADDR_LO, 0); + CSR_WRITE_4(sc, ALC_RRD3_HEAD_ADDR_LO, 0); + } /* Set Rx return descriptor counter. */ CSR_WRITE_4(sc, ALC_RRD_RING_CNT, (ALC_RR_RING_CNT << RRD_RING_CNT_SHIFT) & RRD_RING_CNT_MASK); @@ -3242,16 +4016,20 @@ alc_init_locked(struct alc_softc *sc) /* Configure interrupt moderation timer. */ reg = ALC_USECS(sc->alc_int_rx_mod) << IM_TIMER_RX_SHIFT; - reg |= ALC_USECS(sc->alc_int_tx_mod) << IM_TIMER_TX_SHIFT; + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) + reg |= ALC_USECS(sc->alc_int_tx_mod) << IM_TIMER_TX_SHIFT; CSR_WRITE_4(sc, ALC_IM_TIMER, reg); /* * We don't want to automatic interrupt clear as task queue * for the interrupt should know interrupt status. */ - reg = MASTER_SA_TIMER_ENB; + reg = CSR_READ_4(sc, ALC_MASTER_CFG); + 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) + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0 && + ALC_USECS(sc->alc_int_tx_mod) != 0) reg |= MASTER_IM_TX_TIMER_ENB; CSR_WRITE_4(sc, ALC_MASTER_CFG, reg); /* @@ -3260,11 +4038,17 @@ alc_init_locked(struct alc_softc *sc) */ CSR_WRITE_4(sc, ALC_INTR_RETRIG_TIMER, ALC_USECS(0)); /* Configure CMB. */ - if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0) { - CSR_WRITE_4(sc, ALC_CMB_TD_THRESH, 4); - CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, ALC_USECS(5000)); - } else - CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, ALC_USECS(0)); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + CSR_WRITE_4(sc, ALC_CMB_TD_THRESH, ALC_TX_RING_CNT / 3); + CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, + ALC_USECS(sc->alc_int_tx_mod)); + } else { + if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0) { + CSR_WRITE_4(sc, ALC_CMB_TD_THRESH, 4); + CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, ALC_USECS(5000)); + } else + CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, ALC_USECS(0)); + } /* * Hardware can be configured to issue SMB interrupt based * on programmed interval. Since there is a callout that is @@ -3291,33 +4075,42 @@ alc_init_locked(struct alc_softc *sc) */ CSR_WRITE_4(sc, ALC_FRAME_SIZE, sc->alc_ident->max_framelen); - /* Disable header split(?) */ - CSR_WRITE_4(sc, ALC_HDS_CFG, 0); - - /* Configure IPG/IFG parameters. */ - CSR_WRITE_4(sc, ALC_IPG_IFG_CFG, - ((IPG_IFG_IPGT_DEFAULT << IPG_IFG_IPGT_SHIFT) & IPG_IFG_IPGT_MASK) | - ((IPG_IFG_MIFG_DEFAULT << IPG_IFG_MIFG_SHIFT) & IPG_IFG_MIFG_MASK) | - ((IPG_IFG_IPG1_DEFAULT << IPG_IFG_IPG1_SHIFT) & IPG_IFG_IPG1_MASK) | - ((IPG_IFG_IPG2_DEFAULT << IPG_IFG_IPG2_SHIFT) & IPG_IFG_IPG2_MASK)); - /* Set parameters for half-duplex media. */ - CSR_WRITE_4(sc, ALC_HDPX_CFG, - ((HDPX_CFG_LCOL_DEFAULT << HDPX_CFG_LCOL_SHIFT) & - HDPX_CFG_LCOL_MASK) | - ((HDPX_CFG_RETRY_DEFAULT << HDPX_CFG_RETRY_SHIFT) & - HDPX_CFG_RETRY_MASK) | HDPX_CFG_EXC_DEF_EN | - ((HDPX_CFG_ABEBT_DEFAULT << HDPX_CFG_ABEBT_SHIFT) & - HDPX_CFG_ABEBT_MASK) | - ((HDPX_CFG_JAMIPG_DEFAULT << HDPX_CFG_JAMIPG_SHIFT) & - HDPX_CFG_JAMIPG_MASK)); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + /* Disable header split(?) */ + CSR_WRITE_4(sc, ALC_HDS_CFG, 0); + + /* Configure IPG/IFG parameters. */ + CSR_WRITE_4(sc, ALC_IPG_IFG_CFG, + ((IPG_IFG_IPGT_DEFAULT << IPG_IFG_IPGT_SHIFT) & + IPG_IFG_IPGT_MASK) | + ((IPG_IFG_MIFG_DEFAULT << IPG_IFG_MIFG_SHIFT) & + IPG_IFG_MIFG_MASK) | + ((IPG_IFG_IPG1_DEFAULT << IPG_IFG_IPG1_SHIFT) & + IPG_IFG_IPG1_MASK) | + ((IPG_IFG_IPG2_DEFAULT << IPG_IFG_IPG2_SHIFT) & + IPG_IFG_IPG2_MASK)); + /* Set parameters for half-duplex media. */ + CSR_WRITE_4(sc, ALC_HDPX_CFG, + ((HDPX_CFG_LCOL_DEFAULT << HDPX_CFG_LCOL_SHIFT) & + HDPX_CFG_LCOL_MASK) | + ((HDPX_CFG_RETRY_DEFAULT << HDPX_CFG_RETRY_SHIFT) & + HDPX_CFG_RETRY_MASK) | HDPX_CFG_EXC_DEF_EN | + ((HDPX_CFG_ABEBT_DEFAULT << HDPX_CFG_ABEBT_SHIFT) & + HDPX_CFG_ABEBT_MASK) | + ((HDPX_CFG_JAMIPG_DEFAULT << HDPX_CFG_JAMIPG_SHIFT) & + HDPX_CFG_JAMIPG_MASK)); + } + /* * Set TSO/checksum offload threshold. For frames that is * larger than this threshold, hardware wouldn't do * TSO/checksum offloading. */ - CSR_WRITE_4(sc, ALC_TSO_OFFLOAD_THRESH, - (sc->alc_ident->max_framelen >> TSO_OFFLOAD_THRESH_UNIT_SHIFT) & - TSO_OFFLOAD_THRESH_MASK); + reg = (sc->alc_ident->max_framelen >> TSO_OFFLOAD_THRESH_UNIT_SHIFT) & + TSO_OFFLOAD_THRESH_MASK; + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + reg |= TSO_OFFLOAD_ERRLGPKT_DROP_ENB; + CSR_WRITE_4(sc, ALC_TSO_OFFLOAD_THRESH, reg); /* Configure TxQ. */ reg = (alc_dma_burst[sc->alc_dma_rd_burst] << TXQ_CFG_TX_FIFO_BURST_SHIFT) & TXQ_CFG_TX_FIFO_BURST_MASK; @@ -3326,21 +4119,50 @@ alc_init_locked(struct alc_softc *sc) reg >>= 1; reg |= (TXQ_CFG_TD_BURST_DEFAULT << TXQ_CFG_TD_BURST_SHIFT) & TXQ_CFG_TD_BURST_MASK; + reg |= TXQ_CFG_IP_OPTION_ENB | TXQ_CFG_8023_ENB; CSR_WRITE_4(sc, ALC_TXQ_CFG, reg | TXQ_CFG_ENHANCED_MODE); - - /* Configure Rx free descriptor pre-fetching. */ - CSR_WRITE_4(sc, ALC_RX_RD_FREE_THRESH, - ((RX_RD_FREE_THRESH_HI_DEFAULT << RX_RD_FREE_THRESH_HI_SHIFT) & - RX_RD_FREE_THRESH_HI_MASK) | - ((RX_RD_FREE_THRESH_LO_DEFAULT << RX_RD_FREE_THRESH_LO_SHIFT) & - RX_RD_FREE_THRESH_LO_MASK)); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + reg = (TXQ_CFG_TD_BURST_DEFAULT << HQTD_CFG_Q1_BURST_SHIFT | + TXQ_CFG_TD_BURST_DEFAULT << HQTD_CFG_Q2_BURST_SHIFT | + TXQ_CFG_TD_BURST_DEFAULT << HQTD_CFG_Q3_BURST_SHIFT | + HQTD_CFG_BURST_ENB); + CSR_WRITE_4(sc, ALC_HQTD_CFG, reg); + reg = WRR_PRI_RESTRICT_NONE; + reg |= (WRR_PRI_DEFAULT << WRR_PRI0_SHIFT | + WRR_PRI_DEFAULT << WRR_PRI1_SHIFT | + WRR_PRI_DEFAULT << WRR_PRI2_SHIFT | + WRR_PRI_DEFAULT << WRR_PRI3_SHIFT); + CSR_WRITE_4(sc, ALC_WRR, reg); + } else { + /* Configure Rx free descriptor pre-fetching. */ + CSR_WRITE_4(sc, ALC_RX_RD_FREE_THRESH, + ((RX_RD_FREE_THRESH_HI_DEFAULT << + RX_RD_FREE_THRESH_HI_SHIFT) & RX_RD_FREE_THRESH_HI_MASK) | + ((RX_RD_FREE_THRESH_LO_DEFAULT << + RX_RD_FREE_THRESH_LO_SHIFT) & RX_RD_FREE_THRESH_LO_MASK)); + } /* * Configure flow control parameters. * XON : 80% of Rx FIFO * XOFF : 30% of Rx FIFO */ - if (sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8131 || + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + reg = CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN); + reg &= SRAM_RX_FIFO_LEN_MASK; + reg *= 8; + if (reg > 8 * 1024) + reg -= RX_FIFO_PAUSE_816X_RSVD; + else + reg -= RX_BUF_SIZE_MAX; + reg /= 8; + CSR_WRITE_4(sc, ALC_RX_FIFO_PAUSE_THRESH, + ((reg << RX_FIFO_PAUSE_THRESH_LO_SHIFT) & + RX_FIFO_PAUSE_THRESH_LO_MASK) | + (((RX_FIFO_PAUSE_816X_RSVD / 8) << + RX_FIFO_PAUSE_THRESH_HI_SHIFT) & + RX_FIFO_PAUSE_THRESH_HI_MASK)); + } else if (sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8131 || sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8132) { reg = CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN); rxf_hi = (reg * 8) / 10; @@ -3352,21 +4174,22 @@ alc_init_locked(struct alc_softc *sc) RX_FIFO_PAUSE_THRESH_HI_MASK)); } - if (sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8152_B || - sc->alc_ident->deviceid == DEVICEID_ATHEROS_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); - CSR_WRITE_4(sc, ALC_RSS_CPU, 0); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + /* Disable RSS until I understand L1C/L2C's RSS logic. */ + CSR_WRITE_4(sc, ALC_RSS_IDT_TABLE0, 0); + CSR_WRITE_4(sc, ALC_RSS_CPU, 0); + } /* Configure RxQ. */ reg = (RXQ_CFG_RD_BURST_DEFAULT << RXQ_CFG_RD_BURST_SHIFT) & RXQ_CFG_RD_BURST_MASK; reg |= RXQ_CFG_RSS_MODE_DIS; - if ((sc->alc_flags & ALC_FLAG_ASPM_MON) != 0) + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) + reg |= (RXQ_CFG_816X_IDT_TBL_SIZE_DEFAULT << + RXQ_CFG_816X_IDT_TBL_SIZE_SHIFT) & + RXQ_CFG_816X_IDT_TBL_SIZE_MASK; + if ((sc->alc_flags & ALC_FLAG_FASTETHER) == 0 && + sc->alc_ident->deviceid != DEVICEID_ATHEROS_AR8151_V2) reg |= RXQ_CFG_ASPM_THROUGHPUT_LIMIT_1M; CSR_WRITE_4(sc, ALC_RXQ_CFG, reg); @@ -3387,6 +4210,19 @@ alc_init_locked(struct alc_softc *sc) DMA_CFG_RD_DELAY_CNT_MASK; reg |= (DMA_CFG_WR_DELAY_CNT_DEFAULT << DMA_CFG_WR_DELAY_CNT_SHIFT) & DMA_CFG_WR_DELAY_CNT_MASK; + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0) { + switch (AR816X_REV(sc->alc_rev)) { + case AR816X_REV_A0: + case AR816X_REV_A1: + reg |= DMA_CFG_RD_CHNL_SEL_1; + break; + case AR816X_REV_B0: + /* FALLTHROUGH */ + default: + reg |= DMA_CFG_RD_CHNL_SEL_3; + break; + } + } CSR_WRITE_4(sc, ALC_DMA_CFG, reg); /* @@ -3405,7 +4241,8 @@ alc_init_locked(struct alc_softc *sc) 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 == DEVICEID_ATHEROS_AR8151 || + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0 || + sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8151 || sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8151_V2 || sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8152_B2) reg |= MAC_CFG_HASH_ALG_CRC32 | MAC_CFG_SPEED_MODE_SW; @@ -3424,14 +4261,14 @@ alc_init_locked(struct alc_softc *sc) CSR_WRITE_4(sc, ALC_INTR_STATUS, 0xFFFFFFFF); CSR_WRITE_4(sc, ALC_INTR_STATUS, 0); + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->alc_flags &= ~ALC_FLAG_LINK; /* Switch to the current media. */ - mii_mediachg(mii); + alc_mediachange_locked(sc); callout_reset(&sc->alc_tick_ch, hz, alc_tick, sc); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void @@ -3456,7 +4293,6 @@ alc_stop(struct alc_softc *sc) /* Disable interrupts. */ CSR_WRITE_4(sc, ALC_INTR_MASK, 0); CSR_WRITE_4(sc, ALC_INTR_STATUS, 0xFFFFFFFF); - alc_stop_queue(sc); /* Disable DMA. */ reg = CSR_READ_4(sc, ALC_DMA_CFG); reg &= ~(DMA_CFG_CMB_ENB | DMA_CFG_SMB_ENB); @@ -3467,7 +4303,8 @@ alc_stop(struct alc_softc *sc) alc_stop_mac(sc); /* Disable interrupts which might be touched in taskq handler. */ CSR_WRITE_4(sc, ALC_INTR_STATUS, 0xFFFFFFFF); - + /* Disable L0s/L1s */ + alc_aspm(sc, 0, IFM_UNKNOWN); /* Reclaim Rx buffers that have been processed. */ if (sc->alc_cdata.alc_rxhead != NULL) m_freem(sc->alc_cdata.alc_rxhead); @@ -3505,8 +4342,7 @@ alc_stop_mac(struct alc_softc *sc) uint32_t reg; int i; - ALC_LOCK_ASSERT(sc); - + alc_stop_queue(sc); /* Disable Rx/Tx MAC. */ reg = CSR_READ_4(sc, ALC_MAC_CFG); if ((reg & (MAC_CFG_TX_ENB | MAC_CFG_RX_ENB)) != 0) { @@ -3515,7 +4351,7 @@ alc_stop_mac(struct alc_softc *sc) } for (i = ALC_TIMEOUT; i > 0; i--) { reg = CSR_READ_4(sc, ALC_IDLE_STATUS); - if (reg == 0) + if ((reg & (IDLE_STATUS_RXMAC | IDLE_STATUS_TXMAC)) == 0) break; DELAY(10); } @@ -3540,8 +4376,11 @@ alc_start_queue(struct alc_softc *sc) /* Enable RxQ. */ cfg = CSR_READ_4(sc, ALC_RXQ_CFG); - cfg &= ~RXQ_CFG_ENB; - cfg |= qcfg[1]; + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + cfg &= ~RXQ_CFG_ENB; + cfg |= qcfg[1]; + } else + cfg |= RXQ_CFG_QUEUE0_ENB; CSR_WRITE_4(sc, ALC_RXQ_CFG, cfg); /* Enable TxQ. */ cfg = CSR_READ_4(sc, ALC_TXQ_CFG); @@ -3559,9 +4398,16 @@ alc_stop_queue(struct alc_softc *sc) /* Disable RxQ. */ reg = CSR_READ_4(sc, ALC_RXQ_CFG); - if ((reg & RXQ_CFG_ENB) != 0) { - reg &= ~RXQ_CFG_ENB; - CSR_WRITE_4(sc, ALC_RXQ_CFG, reg); + if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) { + if ((reg & RXQ_CFG_ENB) != 0) { + reg &= ~RXQ_CFG_ENB; + CSR_WRITE_4(sc, ALC_RXQ_CFG, reg); + } + } else { + if ((reg & RXQ_CFG_QUEUE0_ENB) != 0) { + reg &= ~RXQ_CFG_QUEUE0_ENB; + CSR_WRITE_4(sc, ALC_RXQ_CFG, reg); + } } /* Disable TxQ. */ reg = CSR_READ_4(sc, ALC_TXQ_CFG); @@ -3569,6 +4415,7 @@ alc_stop_queue(struct alc_softc *sc) reg &= ~TXQ_CFG_ENB; CSR_WRITE_4(sc, ALC_TXQ_CFG, reg); } + DELAY(40); for (i = ALC_TIMEOUT; i > 0; i--) { reg = CSR_READ_4(sc, ALC_IDLE_STATUS); if ((reg & (IDLE_STATUS_RXQ | IDLE_STATUS_TXQ)) == 0) |