svn commit: r273367 - stable/9/sys/dev/alc

Pyun YongHyeon yongari at FreeBSD.org
Tue Oct 21 04:50:08 UTC 2014


Author: yongari
Date: Tue Oct 21 04:50:07 2014
New Revision: 273367
URL: https://svnweb.freebsd.org/changeset/base/273367

Log:
  MFC r272730,273018:
    Add support for QAC AR816x/AR817x Gigabit/Fast Ethernet controllers.
    These controllers seem to have the same feature of AR813x/AR815x and
    improved RSS support(4 TX queues and 8 RX queues).  alc(4) supports
    all hardware features except RSS.  I didn't implement RX checksum
    offloading for AR816x/AR817x just because I couldn't get
    confirmation from the Vendor whether AR816x/AR817x corrected its
    predecessor's RX checksum offloading bug on fragmented packets.
    This change adds supports for the following controllers.
     o AR8161 PCIe Gigabit Ethernet controller
     o AR8162 PCIe Fast Ethernet controller
     o AR8171 PCIe Gigabit Ethernet controller
     o AR8172 PCIe Fast Ethernet controller
     o Killer E2200 Gigabit Ethernet controller

Modified:
  stable/9/sys/dev/alc/if_alc.c
  stable/9/sys/dev/alc/if_alcreg.h
  stable/9/sys/dev/alc/if_alcvar.h
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)

Modified: stable/9/sys/dev/alc/if_alc.c
==============================================================================
--- stable/9/sys/dev/alc/if_alc.c	Tue Oct 21 04:48:49 2014	(r273366)
+++ stable/9/sys/dev/alc/if_alc.c	Tue Oct 21 04:50:07 2014	(r273367)
@@ -110,17 +110,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);
@@ -129,6 +143,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 *);
@@ -140,14 +157,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);
@@ -157,6 +186,8 @@ static void	alc_rxfilter(struct alc_soft
 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 *);
@@ -229,10 +260,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
@@ -261,14 +303,52 @@ alc_miibus_readreg(device_t dev, int phy
 	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 |
@@ -286,6 +366,32 @@ alc_miibus_writereg(device_t dev, int ph
 	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)
 {
@@ -318,7 +424,6 @@ alc_miibus_statchg(device_t dev)
 			break;
 		}
 	}
-	alc_stop_queue(sc);
 	/* Stop Rx/Tx MACs. */
 	alc_stop_mac(sc);
 
@@ -330,7 +435,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);
+		}
 	}
 }
 
@@ -358,17 +615,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);
 }
@@ -406,7 +675,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;
 
@@ -501,6 +780,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;
@@ -516,19 +862,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. */
@@ -641,12 +999,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,
@@ -672,12 +1119,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)) ==
@@ -758,71 +1216,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);
@@ -831,13 +1279,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 & 0x08) != 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 & 0x03;
 			if (state & 0x01)
@@ -854,13 +1302,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
@@ -870,6 +1396,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;
@@ -884,7 +1423,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
@@ -897,7 +1436,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) {
@@ -905,11 +1443,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);
@@ -917,11 +1489,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;
@@ -930,9 +1511,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) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-stable-9 mailing list