Author: yongari
Date: Wed Oct  8 05:47:01 2014
New Revision: 272730
URL: https://svnweb.freebsd.org/changeset/base/272730

Log:
  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
  
  Tested by:    Many
  Relnotes:     yes
  MFC after:    2 weeks
  HW donated by:        Qualcomm Atheros Communications, Inc.

Modified:
  head/sys/dev/alc/if_alc.c
  head/sys/dev/alc/if_alcreg.h
  head/sys/dev/alc/if_alcvar.h

Modified: head/sys/dev/alc/if_alc.c
==============================================================================
--- head/sys/dev/alc/if_alc.c   Wed Oct  8 05:34:39 2014        (r272729)
+++ head/sys/dev/alc/if_alc.c   Wed Oct  8 05:47:01 2014        (r272730)
@@ -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_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 *);
@@ -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
        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 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)
 {
@@ -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) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to