Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: alc(4): add support for 816x devices (and request for review)



Hello Christos,

Christos Zoulas writes:
> Or we can just define IFM_UNKNOWN and use it.
Done. I have added it to if_alcreg.h. According to <net/if_media.h> that
the max subtype can be 31. I have chosen this value (that does not
conflict with the ones defined in <net/if_media.h>... If someday there
will be more subtype the "31" used for the local IFM_UNKNOWN may become
problematic! :)).

So IFM_UNKNOWN is used were appropiate as an argument for alc_aspm().

> Yes, look for PCI_SUBSYS_ID_REG in /usr/src/sys/dev/pci on how to
> get the subvendor/subdevice with pci_conf_read... Perhaps we should
> grow convenience functions for those too.
Thank you a lot for this information! I have added it too without the
subvendor part that does not seems useful, please correct me if I am
wrong (FreeBSD's VENDORID_ATHEROS or NetBSD's PCI_VENDOR_ATTANSIC is
ATM the only vendor for all the ethernet cards supported by alc(4)).

I will attach the new patches in this email. Apart the previous two
parts discussed in this email I have also introduced various cosmetic
fix in order to reduce the diff with the present if_alc.c (alc_attach()
variables definitions and initializations).

It would be nice if someone with an alc(4) ethernet card (especially a
813x) can test if I did not broke anything.

After I will receive an ok I will then open a PR attaching these patches
with also various man pages improvements (I will do my best in trying to
avoid wizd(8) invocation ;)).


Ciao,
L.
Index: pcidevs
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/pcidevs,v
retrieving revision 1.1211
diff -u -r1.1211 pcidevs
--- pcidevs	14 Jan 2015 15:26:08 -0000	1.1211
+++ pcidevs	15 Jan 2015 14:50:40 -0000
@@ -1182,6 +1182,7 @@
 product ATTANSIC ETHERNET_100	0x2048	L2 100 Mbit Ethernet Adapter
 product ATTANSIC AR8152_B	0x2060	AR8152 v1.1 Fast Ethernet Adapter
 product ATTANSIC AR8152_B2	0x2062	AR8152 v2.0 Fast Ethernet Adapter
+product ATTANSIC E2200		0xe091	E2200
 
 /* ATI products */
 /* See http://www.x.org/wiki/Radeon%20ASICs */
Index: if_alc.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_alc.c,v
retrieving revision 1.11
diff -u -r1.11 if_alc.c
--- if_alc.c	29 Mar 2014 19:28:24 -0000	1.11
+++ if_alc.c	15 Jan 2015 14:50:40 -0000
@@ -95,6 +95,16 @@
 		"Atheros AR8152 v1.1 PCIe Fast Ethernet" },
 	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8152_B2, 6 * 1024,
 		"Atheros AR8152 v2.0 PCIe Fast Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8161, 9 * 1024,
+		"Atheros AR8161 PCIe Gigabit Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8162, 9 * 1024,
+		"Atheros AR8162 PCIe Fast Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8171, 9 * 1024,
+		"Atheros AR8171 PCIe Gigabit Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8172, 9 * 1024,
+		"Atheros AR8172 PCIe Fast Ethernet" },
+	{ PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_E2200, 9 * 1024,
+		"Killer E2200 Gigabit Ethernet" },
 	{ 0, 0, 0, NULL },
 };
 
@@ -110,14 +120,20 @@
 static int	alc_mediachange(struct ifnet *);
 static void	alc_mediastatus(struct ifnet *, struct ifmediareq *);
 
-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 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_dsp_fixup(struct alc_softc *, int);
 static int	alc_encap(struct alc_softc *, struct mbuf **);
 static struct alc_ident *
 		alc_find_ident(struct pci_attach_args *);
 static void	alc_get_macaddr(struct alc_softc *);
+static void	alc_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_cmb(struct alc_softc *);
 static void	alc_init_rr_ring(struct alc_softc *);
 static int	alc_init_rx_ring(struct alc_softc *, bool);
@@ -125,12 +141,22 @@
 static void	alc_init_tx_ring(struct alc_softc *);
 static int	alc_intr(void *);
 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 void	alc_mii_writereg_813x(struct alc_softc *, int, int, int);
+static void	alc_mii_writereg_816x(struct alc_softc *, int, int, int);
 static int	alc_miibus_readreg(device_t, int, int);
 static void	alc_miibus_statchg(struct ifnet *);
 static void	alc_miibus_writereg(device_t, int, int, int);
+static uint32_t	alc_miidbg_readreg(struct alc_softc *, int);
+static void	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_newbuf(struct alc_softc *, struct alc_rxdesc *, bool);
 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 void	alc_reset(struct alc_softc *);
 static void	alc_rxeof(struct alc_softc *, struct rx_rdesc *);
 static int	alc_rxintr(struct alc_softc *);
@@ -160,12 +186,34 @@
 alc_miibus_readreg(device_t dev, int phy, int reg)
 {
 	struct alc_softc *sc = device_private(dev);
+	int v;
+
+	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;
 
 	if (phy != sc->alc_phyaddr)
 		return (0);
 
+	/*
+	 * For AR8132 fast ethernet controller, do not report 1000baseT
+	 * capability to mii(4). Even though AR8132 uses the same
+	 * model/revision number of F1 gigabit PHY, the PHY has no
+	 * ability to establish 1000baseT link.
+	 */
+	if ((sc->alc_flags & ALC_FLAG_FASTETHER) != 0 &&
+	    reg == MII_EXTSR)
+		return 0;
+
 	CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_READ |
 	    MDIO_SUP_PREAMBLE | MDIO_CLK_25_4 | MDIO_REG_ADDR(reg));
 	for (i = ALC_PHY_TIMEOUT; i > 0; i--) {
@@ -184,16 +232,56 @@
 	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 (phy != sc->alc_phyaddr)
+		return (0);
+
+	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) {
+		printf("%s: phy read timeout: phy %d, reg %d\n",
+		    device_xname(sc->sc_dev), phy, reg);
+		return (0);
+	}
+
+	return ((v & MDIO_DATA_MASK) >> MDIO_DATA_SHIFT);
+}
+
 static void
 alc_miibus_writereg(device_t dev, int phy, int reg, int val)
 {
 	struct alc_softc *sc = device_private(dev);
+
+	if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0)
+		alc_mii_writereg_816x(sc, phy, reg, val);
+	else
+		alc_mii_writereg_813x(sc, phy, reg, val);
+
+	return;
+}
+
+static void
+alc_mii_writereg_813x(struct alc_softc *sc, int phy, int reg, int val)
+{
 	uint32_t v;
 	int i;
 
-	if (phy != sc->alc_phyaddr)
-		return;
-
 	CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_WRITE |
 	    (val & MDIO_DATA_MASK) << MDIO_DATA_SHIFT |
 	    MDIO_SUP_PREAMBLE | MDIO_CLK_25_4 | MDIO_REG_ADDR(reg));
@@ -207,6 +295,35 @@
 	if (i == 0)
 		printf("%s: phy write timeout: phy %d, reg %d\n",
 		    device_xname(sc->sc_dev), phy, reg);
+
+	return;
+}
+
+static void
+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)
+		printf("%s: phy write timeout: phy %d, reg %d\n",
+		    device_xname(sc->sc_dev), phy, reg);
+
+	return;
 }
 
 static void
@@ -235,7 +352,6 @@
 			break;
 		}
 	}
-	alc_stop_queue(sc);
 	/* Stop Rx/Tx MACs. */
 	alc_stop_mac(sc);
 
@@ -247,11 +363,166 @@
 		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->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR,
+	    reg);
+	return (alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+	    ALC_MII_DBG_DATA));
 }
 
 static void
+alc_miidbg_writereg(struct alc_softc *sc, int reg, int val)
+{
+
+	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR,
+	    reg);
+	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA, val);
+
+	return;
+}
+
+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) {
+		printf("%s: phy ext read timeout: %d\n",
+		    device_xname(sc->sc_dev), 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) {
+		printf("%s: phy ext write timeout: reg %d\n",
+		    device_xname(sc->sc_dev), reg);
+		return (0);
+	}
+
+	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);
+		}
+ 	}
+}
+ 
+static void
 alc_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
 {
 	struct alc_softc *sc = ifp->if_softc;
@@ -307,7 +578,17 @@
 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;
 
@@ -402,6 +683,74 @@
 		}
 	}
 
+	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 (alcdebug)
+			printf("%s: reloading station address via TWSI timed out!\n",
+			    device_xname(sc->sc_dev));
+	}
+
+	/* 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 (alcdebug)
+				printf("%s: reloading EEPROM/FLASH timed out!\n",
+			  	  device_xname(sc->sc_dev));
+		}
+	}
+
+	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;
@@ -417,29 +766,39 @@
 {
 	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. */
-	CSR_WRITE_2(sc, ALC_GPHY_CFG,
-	    GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE | GPHY_CFG_SEL_ANA_RESET);
+	CSR_WRITE_2(sc, ALC_GPHY_CFG, GPHY_CFG_SEL_ANA_RESET);
 	CSR_READ_2(sc, ALC_GPHY_CFG);
 	DELAY(10 * 1000);
 
-	CSR_WRITE_2(sc, ALC_GPHY_CFG,
-	    GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE |
+	CSR_WRITE_2(sc, ALC_GPHY_CFG, GPHY_CFG_EXT_RESET |
 	    GPHY_CFG_SEL_ANA_RESET);
 	CSR_READ_2(sc, ALC_GPHY_CFG);
 	DELAY(10 * 1000);
@@ -524,14 +883,121 @@
 	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
 	    ALC_MII_DBG_DATA, data);
 	DELAY(1000);
+
+	/* Disable hibernation. */
+	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR,
+	    0x0029);
+	data = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+	    ALC_MII_DBG_DATA);
+	data &= ~0x8000;
+	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA,
+	    data);
+
+	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR,
+	    0x000B);
+	data = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+	    ALC_MII_DBG_DATA);
+	data &= ~0x8000;
+	alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA,
+	    data);
+}
+
+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 PCI_PRODUCT_ATTANSIC_AR8161:
+	case PCI_PRODUCT_ATTANSIC_E2200:
+	case PCI_PRODUCT_ATTANSIC_AR8162:
+	case PCI_PRODUCT_ATTANSIC_AR8171:
+	case PCI_PRODUCT_ATTANSIC_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 PCI_PRODUCT_ATTANSIC_AR8151:
 	case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+	case PCI_PRODUCT_ATTANSIC_AR8152_B:
+	case PCI_PRODUCT_ATTANSIC_AR8152_B2:
 		/*
 		 * GPHY power down caused more problems on AR8151 v2.0.
 		 * When driver is reloaded after GPHY power down,
@@ -548,8 +1014,7 @@
 		break;
 	default:
 		/* Force PHY down. */
-		CSR_WRITE_2(sc, ALC_GPHY_CFG,
-		    GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE |
+		CSR_WRITE_2(sc, ALC_GPHY_CFG, GPHY_CFG_EXT_RESET |
 		    GPHY_CFG_SEL_ANA_RESET | GPHY_CFG_PHY_IDDQ |
 		    GPHY_CFG_PWDOWN_HW);
 		DELAY(1000);
@@ -558,7 +1023,17 @@
 }
 
 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;
@@ -643,6 +1118,41 @@
 }
 
 static void
+alc_aspm_816x(struct alc_softc *sc, int init)
+{
+	uint32_t pmcfg;
+
+	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->sc_ec.ec_if.if_flags & IFF_RUNNING) != 0)
+			pmcfg |= PM_CFG_ASPM_L1_ENB | PM_CFG_MAC_ASPM_CHK;
+	}
+	CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg);
+}
+
+static void
 alc_attach(device_t parent, device_t self, void *aux)
 {
 
@@ -731,56 +1241,86 @@
 			    device_xname(sc->sc_dev),
 			    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);
-		CSR_WRITE_4(sc, ALC_LTSSM_ID_CFG,
-		    CSR_READ_4(sc, ALC_LTSSM_ID_CFG) & ~LTSSM_ID_WRO_ENB);
-		CSR_WRITE_4(sc, ALC_PCIE_PHYMISC,
-		    CSR_READ_4(sc, ALC_PCIE_PHYMISC) |
-		    PCIE_PHYMISC_FORCE_RCV_DET);
-		if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B &&
-		    sc->alc_rev == ATHEROS_AR8152_B_V10) {
-			val = CSR_READ_4(sc, ALC_PCIE_PHYMISC2);
-			val &= ~(PCIE_PHYMISC2_SERDES_CDR_MASK |
-			    PCIE_PHYMISC2_SERDES_TH_MASK);
-			val |= 3 << PCIE_PHYMISC2_SERDES_CDR_SHIFT;
-			val |= 3 << PCIE_PHYMISC2_SERDES_TH_SHIFT;
-			CSR_WRITE_4(sc, ALC_PCIE_PHYMISC2, val);
-		}
-		/* Disable ASPM L0S and L1. */
-		cap = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
-		    base + PCIE_LCAP) >> 16;
-		if ((cap & 0x00000c00) != 0) {
-			ctl = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
-			    base + PCIE_LCSR) >> 16;
-			if ((ctl & 0x08) != 0)
-				sc->alc_rcb = DMA_CFG_RCB_128;
-			if (alcdebug)
-				printf("%s: RCB %u bytes\n",
-				    device_xname(sc->sc_dev),
-				    sc->alc_rcb == DMA_CFG_RCB_64 ? 64 : 128);
-			state = ctl & 0x03;
-			if (state & 0x01)
-				sc->alc_flags |= ALC_FLAG_L0S;
-			if (state & 0x02)
-				sc->alc_flags |= ALC_FLAG_L1S;
-			if (alcdebug)
-				printf("%s: ASPM %s %s\n",
-				    device_xname(sc->sc_dev),
-				    aspm_state[state],
-				    state == 0 ? "disabled" : "enabled");
-			alc_disable_l0s_l1(sc);
+
+		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 == PCI_PRODUCT_ATTANSIC_AR8152_B &&
+			    sc->alc_rev == ATHEROS_AR8152_B_V10) {
+ 				val = CSR_READ_4(sc, ALC_PCIE_PHYMISC2);
+ 				val &= ~(PCIE_PHYMISC2_SERDES_CDR_MASK |
+ 				    PCIE_PHYMISC2_SERDES_TH_MASK);
+				val |= 3 << PCIE_PHYMISC2_SERDES_CDR_SHIFT;
+				val |= 3 << PCIE_PHYMISC2_SERDES_TH_SHIFT;
+				CSR_WRITE_4(sc, ALC_PCIE_PHYMISC2, val);
+			}
+			/* Disable ASPM L0S and L1. */
+			cap = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
+			    base + PCIE_LCAP) >> 16;
+			if ((cap & 0x00000c00) != 0) {
+				ctl = pci_conf_read(sc->sc_pct, sc->sc_pcitag,
+				    base + PCIE_LCSR) >> 16;
+				if ((ctl & 0x08) != 0)
+					sc->alc_rcb = DMA_CFG_RCB_128;
+				if (alcdebug)
+					printf("%s: RCB %u bytes\n",
+					    device_xname(sc->sc_dev),
+					    sc->alc_rcb == DMA_CFG_RCB_64 ? 64 : 128);
+				state = ctl & 0x03;
+				if (state & 0x01)
+					sc->alc_flags |= ALC_FLAG_L0S;
+				if (state & 0x02)
+					sc->alc_flags |= ALC_FLAG_L1S;
+				if (alcdebug)
+					printf("%s: ASPM %s %s\n",
+					    device_xname(sc->sc_dev),
+					    aspm_state[state],
+					    state == 0 ? "disabled" : "enabled");
+				alc_disable_l0s_l1(sc);
+			} else {
+				aprint_debug_dev(sc->sc_dev, "no ASPM support\n");
+			}
 		} else {
-			aprint_debug_dev(sc->sc_dev, "no ASPM support\n");
+			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);
 
 	/* Reset the ethernet controller. */
+	alc_stop_mac(sc);
 	alc_reset(sc);
 
 	/*
@@ -791,6 +1331,19 @@
 	 * shows the same PHY model/revision number of AR8131.
 	 */
 	switch (sc->alc_ident->deviceid) {
+	case PCI_PRODUCT_ATTANSIC_AR8161:
+		if (pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_SUBSYS_ID_REG)
+		   == 0x0091 && sc->alc_rev == 0)
+			sc->alc_flags |= ALC_FLAG_LINK_WAR;
+		/* FALLTHROUGH */
+	case PCI_PRODUCT_ATTANSIC_E2200:
+	case PCI_PRODUCT_ATTANSIC_AR8171:
+		sc->alc_flags |= ALC_FLAG_AR816X_FAMILY;
+		break;
+	case PCI_PRODUCT_ATTANSIC_AR8162:
+	case PCI_PRODUCT_ATTANSIC_AR8172:
+		sc->alc_flags |= ALC_FLAG_FASTETHER | ALC_FLAG_AR816X_FAMILY;
+		break;
 	case PCI_PRODUCT_ATTANSIC_AR8152_B:
 	case PCI_PRODUCT_ATTANSIC_AR8152_B2:
 		sc->alc_flags |= ALC_FLAG_APS;
@@ -805,7 +1358,7 @@
 	default:
 		break;
 	}
-	sc->alc_flags |= ALC_FLAG_JUMBO | ALC_FLAG_ASPM_MON;
+	sc->alc_flags |= ALC_FLAG_JUMBO;
 
 	/*
 	 * It seems that AR813x/AR815x has silicon bug for SMB. In
@@ -869,6 +1422,23 @@
 	sc->sc_ec.ec_capabilities |= ETHERCAP_VLAN_HWTAGGING;
 #endif
 
+	/*
+	 * XXX
+	 * It seems enabling Tx checksum offloading makes more trouble.
+	 * Sometimes the controller does not receive any frames when
+	 * Tx checksum offloading is enabled. I'm not sure whether this
+	 * is a bug in Tx checksum offloading logic or I got broken
+	 * 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.
+	 */
+	if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) == 0) {
+		ifp->if_capenable &= ~IFCAP_CSUM_IPv4_Tx;
+		ifp->if_capabilities &= ~ALC_CSUM_FEATURES;
+	}
+
 	/* Set up MII bus. */
 	sc->sc_miibus.mii_ifp = ifp;
 	sc->sc_miibus.mii_readreg = alc_miibus_readreg;
@@ -2014,14 +2584,78 @@
 }
 
 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)
+			printf("%s: MAC reset timeout!\n", device_xname(sc->sc_dev));
+	}
 	for (i = ALC_RESET_TIMEOUT; i > 0; i--) {
 		DELAY(10);
 		if ((CSR_READ_4(sc, ALC_MASTER_CFG) & MASTER_RESET) == 0)
@@ -2031,14 +2665,46 @@
 		printf("%s: master reset timeout!\n", device_xname(sc->sc_dev));
 
 	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)
-		printf("%s: reset timeout(0x%08x)!\n", device_xname(sc->sc_dev),
-		    reg);
+		printf("%s: reset timeout(0x%08x)!\n",
+		    device_xname(sc->sc_dev), 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 == PCI_PRODUCT_ATTANSIC_AR8152_B ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2)
+		CSR_WRITE_4(sc, ALC_SERDES_LOCK,
+		    CSR_READ_4(sc, ALC_SERDES_LOCK) | SERDES_MAC_CLK_SLOWDOWN |
+		    SERDES_PHY_CLK_SLOWDOWN);
 }
 
 static int
@@ -2080,7 +2746,17 @@
 	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. */
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
@@ -2106,10 +2782,12 @@
 	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);
@@ -2130,10 +2808,12 @@
 	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);
@@ -2162,16 +2842,20 @@
 	sc->alc_int_rx_mod = ALC_IM_RX_TIMER_DEFAULT;
 	sc->alc_int_tx_mod = ALC_IM_TX_TIMER_DEFAULT;
 	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);
 	/*
@@ -2180,11 +2864,17 @@
 	 */
 	CSR_WRITE_4(sc, ALC_INTR_RETRIG_TIMER, ALC_USECS(0));
 	/* Configure CMB. */
-	CSR_WRITE_4(sc, ALC_CMB_TD_THRESH, 4);
-	if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0)
-		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
@@ -2211,33 +2901,42 @@
 	 */
 	CSR_WRITE_4(sc, ALC_FRAME_SIZE, sc->alc_ident->max_framelen);
 
-	/* Disable header split(?) */
-	CSR_WRITE_4(sc, ALC_HDS_CFG, 0);
+	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));
+	}
 
-	/* 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;
@@ -2246,21 +2945,50 @@
 		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 == PCI_PRODUCT_ATTANSIC_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 == PCI_PRODUCT_ATTANSIC_AR8131 ||
 	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8132) {
 		reg = CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN);
 		rxf_hi = (reg * 8) / 10;
@@ -2272,22 +3000,23 @@
 		     RX_FIFO_PAUSE_THRESH_HI_MASK));
 	}
 
-	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B ||
-	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2)
-		CSR_WRITE_4(sc, ALC_SERDES_LOCK,
-		    CSR_READ_4(sc, ALC_SERDES_LOCK) | SERDES_MAC_CLK_SLOWDOWN |
-		    SERDES_PHY_CLK_SLOWDOWN);
-
-	/* Disable RSS until I understand L1C/L2C's RSS logic. */
-	CSR_WRITE_4(sc, ALC_RSS_IDT_TABLE0, 0);
-	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)
-		reg |= RXQ_CFG_ASPM_THROUGHPUT_LIMIT_1M;
+	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 != PCI_PRODUCT_ATTANSIC_AR8151_V2)
+ 		reg |= RXQ_CFG_ASPM_THROUGHPUT_LIMIT_1M;
 	CSR_WRITE_4(sc, ALC_RXQ_CFG, reg);
 
 	/* Configure DMA parameters. */
@@ -2307,6 +3036,19 @@
 	    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);
 
 	/*
@@ -2325,7 +3067,8 @@
 	reg = MAC_CFG_TX_CRC_ENB | MAC_CFG_TX_AUTO_PAD | MAC_CFG_FULL_DUPLEX |
 	    ((MAC_CFG_PREAMBLE_DEFAULT << MAC_CFG_PREAMBLE_SHIFT) &
 	    MAC_CFG_PREAMBLE_MASK);
-	if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151 ||
+	if ((sc->alc_flags & ALC_FLAG_AR816X_FAMILY) != 0 ||
+	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151 ||
 	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
 	    sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2)
 		reg |= MAC_CFG_HASH_ALG_CRC32 | MAC_CFG_SPEED_MODE_SW;
@@ -2383,7 +3126,6 @@
 	/* 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);
@@ -2398,6 +3140,9 @@
 	/* 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);
@@ -2429,6 +3174,7 @@
 	uint32_t reg;
 	int i;
 
+	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) {
@@ -2437,7 +3183,7 @@
 	}
 	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);
 	}
@@ -2460,8 +3206,11 @@
 
 	/* 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);
@@ -2477,16 +3226,24 @@
 
 	/* 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);
 	if ((reg & TXQ_CFG_ENB) != 0) {
 		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)
Index: if_alcreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_alcreg.h,v
retrieving revision 1.2
diff -u -r1.2 if_alcreg.h
--- if_alcreg.h	23 Feb 2011 02:25:04 -0000	1.2
+++ if_alcreg.h	15 Jan 2015 14:50:41 -0000
@@ -36,6 +36,17 @@
 #define ATHEROS_AR8152_B_V10		0xC0
 #define ATHEROS_AR8152_B_V11		0xC1
 
+/*
+ * Atheros AR816x/AR817x revisions
+ */
+#define	AR816X_REV_A0			0
+#define	AR816X_REV_A1			1
+#define	AR816X_REV_B0			2
+#define	AR816X_REV_C0			3
+
+#define	AR816X_REV_SHIFT		3
+#define	AR816X_REV(x)			((x) >> AR816X_REV_SHIFT)
+
 /* 0x0000 - 0x02FF : PCIe configuration space */
 
 #define	ALC_PEX_UNC_ERR_SEV		0x10C
@@ -51,11 +62,34 @@
 #define	PEX_UNC_ERR_SEV_ECRC		0x00080000
 #define	PEX_UNC_ERR_SEV_UR		0x00100000
 
+#define	ALC_EEPROM_LD			0x204	/* AR816x */
+#define	EEPROM_LD_START			0x00000001
+#define	EEPROM_LD_IDLE			0x00000010
+#define	EEPROM_LD_DONE			0x00000000
+#define	EEPROM_LD_PROGRESS		0x00000020
+#define	EEPROM_LD_EXIST			0x00000100
+#define	EEPROM_LD_EEPROM_EXIST		0x00000200
+#define	EEPROM_LD_FLASH_EXIST		0x00000400
+#define	EEPROM_LD_FLASH_END_ADDR_MASK	0x03FF0000
+#define	EEPROM_LD_FLASH_END_ADDR_SHIFT	16
+
 #define	ALC_TWSI_CFG			0x218
 #define	TWSI_CFG_SW_LD_START		0x00000800
 #define	TWSI_CFG_HW_LD_START		0x00001000
 #define	TWSI_CFG_LD_EXIST		0x00400000
 
+#define	ALC_SLD				0x218	/* AR816x */
+#define	SLD_START			0x00000800
+#define	SLD_PROGRESS			0x00001000
+#define	SLD_IDLE			0x00002000
+#define	SLD_SLVADDR_MASK		0x007F0000
+#define	SLD_EXIST			0x00800000
+#define	SLD_FREQ_MASK			0x03000000
+#define	SLD_FREQ_100K			0x00000000
+#define	SLD_FREQ_200K			0x01000000
+#define	SLD_FREQ_300K			0x02000000
+#define	SLD_FREQ_400K			0x03000000
+
 #define	ALC_PCIE_PHYMISC		0x1000
 #define	PCIE_PHYMISC_FORCE_RCV_DET	0x00000004
 
@@ -68,6 +102,9 @@
 #define	ALC_TWSI_DEBUG			0x1108
 #define	TWSI_DEBUG_DEV_EXIST		0x20000000
 
+#define	ALC_PDLL_TRNS1			0x1104
+#define	PDLL_TRNS1_D3PLLOFF_ENB		0x00000800
+
 #define	ALC_EEPROM_CFG			0x12C0
 #define	EEPROM_CFG_DATA_HI_MASK		0x0000FFFF
 #define	EEPROM_CFG_ADDR_MASK		0x03FF0000
@@ -91,11 +128,14 @@
 #define	PM_CFG_SERDES_PD_EX_L1		0x00000040
 #define	PM_CFG_SERDES_BUDS_RX_L1_ENB	0x00000080
 #define	PM_CFG_L0S_ENTRY_TIMER_MASK	0x00000F00
+#define	PM_CFG_RX_L1_AFTER_L0S		0x00000800
 #define	PM_CFG_ASPM_L0S_ENB		0x00001000
 #define	PM_CFG_CLK_SWH_L1		0x00002000
 #define	PM_CFG_CLK_PWM_VER1_1		0x00004000
 #define	PM_CFG_PCIE_RECV		0x00008000
 #define	PM_CFG_L1_ENTRY_TIMER_MASK	0x000F0000
+#define	PM_CFG_L1_ENTRY_TIMER_816X_MASK	0x00070000
+#define	PM_CFG_TX_L1_AFTER_L0S		0x00080000
 #define	PM_CFG_PM_REQ_TIMER_MASK	0x00F00000
 #define	PM_CFG_LCKDET_TIMER_MASK	0x0F000000
 #define	PM_CFG_EN_BUFS_RX_L0S		0x10000000
@@ -109,8 +149,10 @@
 
 #define PM_CFG_L0S_ENTRY_TIMER_DEFAULT	6
 #define	PM_CFG_L1_ENTRY_TIMER_DEFAULT	1
+#define	PM_CFG_L1_ENTRY_TIMER_816X_DEFAULT	4
 #define	PM_CFG_LCKDET_TIMER_DEFAULT	12
 #define	PM_CFG_PM_REQ_TIMER_DEFAULT	12
+#define	PM_CFG_PM_REQ_TIMER_816X_DEFAULT	15
 
 #define	ALC_LTSSM_ID_CFG		0x12FC
 #define	LTSSM_ID_WRO_ENB		0x00001000
@@ -119,6 +161,7 @@
 #define	MASTER_RESET			0x00000001
 #define	MASTER_TEST_MODE_MASK		0x0000000C
 #define	MASTER_BERT_START		0x00000010
+#define	MASTER_WAKEN_25M		0x00000020
 #define	MASTER_OOB_DIS_OFF		0x00000040
 #define	MASTER_SA_TIMER_ENB		0x00000080
 #define	MASTER_MTIMER_ENB		0x00000100
@@ -159,7 +202,7 @@
  */
 #define	ALC_IM_TX_TIMER_DEFAULT		50000	/* 50ms */
 
-#define	ALC_GPHY_CFG			0x140C	/* 16bits */
+#define	ALC_GPHY_CFG			0x140C	/* 16 bits, 32 bits on AR816x */
 #define	GPHY_CFG_EXT_RESET		0x0001
 #define	GPHY_CFG_RTL_MODE		0x0002
 #define	GPHY_CFG_LED_MODE		0x0004
@@ -176,6 +219,7 @@
 #define	GPHY_CFG_PHY_PLL_ON		0x2000
 #define	GPHY_CFG_PWDOWN_HW		0x4000
 #define	GPHY_CFG_PHY_PLL_BYPASS		0x8000
+#define	GPHY_CFG_100AB_ENB		0x00020000
 
 #define	ALC_IDLE_STATUS			0x1410
 #define	IDLE_STATUS_RXMAC		0x00000001
@@ -200,9 +244,10 @@
 #define	MDIO_CLK_25_10			0x04000000
 #define	MDIO_CLK_25_14			0x05000000
 #define	MDIO_CLK_25_20			0x06000000
-#define	MDIO_CLK_25_28			0x07000000
+#define	MDIO_CLK_25_128			0x07000000
 #define	MDIO_OP_BUSY			0x08000000
 #define	MDIO_AP_ENB			0x10000000
+#define	MDIO_MODE_EXT			0x40000000
 #define	MDIO_DATA_SHIFT			0
 #define	MDIO_REG_ADDR_SHIFT		16
 
@@ -236,6 +281,23 @@
 #define	SERDES_MAC_CLK_SLOWDOWN		0x00020000
 #define	SERDES_PHY_CLK_SLOWDOWN		0x00040000
 
+#define	ALC_LPI_CTL			0x1440
+#define	LPI_CTL_ENB			0x00000001
+
+#define	ALC_EXT_MDIO			0x1448
+#define	EXT_MDIO_REG_MASK		0x0000FFFF
+#define	EXT_MDIO_DEVADDR_MASK		0x001F0000
+#define	EXT_MDIO_REG_SHIFT		0
+#define	EXT_MDIO_DEVADDR_SHIFT		16
+
+#define	EXT_MDIO_REG(x)		\
+	(((x) << EXT_MDIO_REG_SHIFT) & EXT_MDIO_REG_MASK)
+#define	EXT_MDIO_DEVADDR(x)	\
+	(((x) << EXT_MDIO_DEVADDR_SHIFT) & EXT_MDIO_DEVADDR_MASK)
+
+#define	ALC_IDLE_DECISN_TIMER		0x1474
+#define	IDLE_DECISN_TIMER_DEFAULT_1MS	0x400
+
 #define	ALC_MAC_CFG			0x1480
 #define	MAC_CFG_TX_ENB			0x00000001
 #define	MAC_CFG_RX_ENB			0x00000002
@@ -266,6 +328,7 @@
 #define	MAC_CFG_SINGLE_PAUSE_ENB	0x10000000
 #define	MAC_CFG_HASH_ALG_CRC32		0x20000000
 #define	MAC_CFG_SPEED_MODE_SW		0x40000000
+#define	MAC_CFG_FAST_PAUSE		0x80000000
 #define	MAC_CFG_PREAMBLE_SHIFT		10
 #define	MAC_CFG_PREAMBLE_DEFAULT	7
 
@@ -366,8 +429,12 @@
 
 #define	ALC_RSS_IDT_TABLE0		0x14E0
 
+#define	ALC_TD_PRI2_HEAD_ADDR_LO	0x14E0	/* AR816x */
+
 #define	ALC_RSS_IDT_TABLE1		0x14E4
 
+#define	ALC_TD_PRI3_HEAD_ADDR_LO	0x14E4	/* AR816x */
+
 #define	ALC_RSS_IDT_TABLE2		0x14E8
 
 #define	ALC_RSS_IDT_TABLE3		0x14EC
@@ -410,7 +477,9 @@
 #define	ALC_SRAM_RX_FIFO_ADDR		0x1520
 
 #define	ALC_SRAM_RX_FIFO_LEN		0x1524
-
+#define	SRAM_RX_FIFO_LEN_MASK		0x00000FFF
+#define	SRAM_RX_FIFO_LEN_SHIFT		0
+ 
 #define	ALC_SRAM_TX_FIFO_ADDR		0x1528
 
 #define	ALC_SRAM_TX_FIFO_LEN		0x152C
@@ -466,8 +535,12 @@
 
 #define	ALC_TDH_HEAD_ADDR_LO		0x157C
 
+#define	ALC_TD_PRI1_HEAD_ADDR_LO	0x157C	/* AR816x */
+
 #define	ALC_TDL_HEAD_ADDR_LO		0x1580
 
+#define	ALC_TD_PRI0_HEAD_ADDR_LO	0x1580	/* AR816x */
+
 #define	ALC_TD_RING_CNT			0x1584
 #define	TD_RING_CNT_MASK		0x0000FFFF
 #define	TD_RING_CNT_SHIFT		0
@@ -487,6 +560,7 @@
 
 #define	ALC_TSO_OFFLOAD_THRESH		0x1594	/* 8 bytes unit */
 #define	TSO_OFFLOAD_THRESH_MASK		0x000007FF
+#define	TSO_OFFLOAD_ERRLGPKT_DROP_ENB	0x00000800
 #define	TSO_OFFLOAD_THRESH_SHIFT	0
 #define	TSO_OFFLOAD_THRESH_UNIT		8
 #define	TSO_OFFLOAD_THRESH_UNIT_SHIFT	3
@@ -534,6 +608,17 @@
 	(RXQ_CFG_QUEUE0_ENB | RXQ_CFG_QUEUE1_ENB |	\
 	 RXQ_CFG_QUEUE2_ENB | RXQ_CFG_QUEUE3_ENB)
 
+/* AR816x specific bits */
+#define	RXQ_CFG_816X_RSS_HASH_IPV4	0x00000004
+#define	RXQ_CFG_816X_RSS_HASH_IPV4_TCP	0x00000008
+#define	RXQ_CFG_816X_RSS_HASH_IPV6	0x00000010
+#define	RXQ_CFG_816X_RSS_HASH_IPV6_TCP	0x00000020
+#define	RXQ_CFG_816X_RSS_HASH_MASK	0x0000003C
+#define	RXQ_CFG_816X_IPV6_PARSE_ENB	0x00000080
+#define	RXQ_CFG_816X_IDT_TBL_SIZE_MASK	0x0001FF00
+#define	RXQ_CFG_816X_IDT_TBL_SIZE_SHIFT	8
+#define	RXQ_CFG_816X_IDT_TBL_SIZE_DEFAULT	0x100
+
 #define	ALC_RX_RD_FREE_THRESH		0x15A4	/* 8 bytes unit. */
 #define	RX_RD_FREE_THRESH_HI_MASK	0x0000003F
 #define	RX_RD_FREE_THRESH_LO_MASK	0x00000FC0
@@ -547,7 +632,13 @@
 #define	RX_FIFO_PAUSE_THRESH_HI_MASK	0x0FFF0000
 #define	RX_FIFO_PAUSE_THRESH_LO_SHIFT	0
 #define	RX_FIFO_PAUSE_THRESH_HI_SHIFT	16
-
+/*
+ * Size = tx-packet(1522) + IPG(12) + SOF(8) + 64(Pause) + IPG(12) + SOF(8) +
+ *	  rx-packet(1522) + delay-of-link(64)
+ *	= 3212.
+ */
+#define	RX_FIFO_PAUSE_816X_RSVD		3212
+ 
 #define	ALC_RD_DMA_CFG			0x15AC
 #define	RD_DMA_CFG_THRESH_MASK		0x00000FFF	/* 8 bytes unit */
 #define	RD_DMA_CFG_TIMER_MASK		0xFFFF0000
@@ -570,6 +661,7 @@
 #define	DMA_CFG_OUT_ORDER		0x00000004
 #define	DMA_CFG_RCB_64			0x00000000
 #define	DMA_CFG_RCB_128			0x00000008
+#define	DMA_CFG_PEND_AUTO_RST		0x00000008
 #define	DMA_CFG_RD_BURST_128		0x00000000
 #define	DMA_CFG_RD_BURST_256		0x00000010
 #define	DMA_CFG_RD_BURST_512		0x00000020
@@ -589,6 +681,14 @@
 #define	DMA_CFG_SMB_ENB			0x00200000
 #define	DMA_CFG_CMB_NOW			0x00400000
 #define	DMA_CFG_SMB_DIS			0x01000000
+#define	DMA_CFG_RD_CHNL_SEL_MASK	0x0C000000
+#define	DMA_CFG_RD_CHNL_SEL_1		0x00000000
+#define	DMA_CFG_RD_CHNL_SEL_2		0x04000000
+#define	DMA_CFG_RD_CHNL_SEL_3		0x08000000
+#define	DMA_CFG_RD_CHNL_SEL_4		0x0C000000
+#define	DMA_CFG_WSRAM_RDCTL		0x10000000
+#define	DMA_CFG_RD_PEND_CLR		0x20000000
+#define	DMA_CFG_WR_PEND_CLR		0x40000000
 #define	DMA_CFG_SMB_NOW			0x80000000
 #define	DMA_CFG_RD_BURST_MASK		0x07
 #define	DMA_CFG_RD_BURST_SHIFT		4
@@ -611,6 +711,12 @@
 #define	CMB_TX_TIMER_MASK		0x0000FFFF
 #define	CMB_TX_TIMER_SHIFT		0
 
+#define	ALC_MSI_MAP_TBL1		0x15D0
+
+#define	ALC_MSI_ID_MAP			0x15D4
+
+#define	ALC_MSI_MAP_TBL2		0x15D8
+
 #define	ALC_MBOX_RD0_PROD_IDX		0x15E0
 
 #define	ALC_MBOX_RD1_PROD_IDX		0x15E4
@@ -628,12 +734,20 @@
 #define	MBOX_TD_PROD_HI_IDX_SHIFT	0
 #define	MBOX_TD_PROD_LO_IDX_SHIFT	16
 
+#define	ALC_MBOX_TD_PRI1_PROD_IDX	0x15F0	/* 16 bits AR816x */
+
+#define	ALC_MBOX_TD_PRI0_PROD_IDX	0x15F2	/* 16 bits AR816x */
+
 #define	ALC_MBOX_TD_CONS_IDX		0x15F4
 #define	MBOX_TD_CONS_HI_IDX_MASK	0x0000FFFF
 #define	MBOX_TD_CONS_LO_IDX_MASK	0xFFFF0000
 #define	MBOX_TD_CONS_HI_IDX_SHIFT	0
 #define	MBOX_TD_CONS_LO_IDX_SHIFT	16
 
+#define	ALC_MBOX_TD_PRI1_CONS_IDX	0x15F4	/* 16 bits AR816x */
+
+#define	ALC_MBOX_TD_PRI0_CONS_IDX	0x15F6	/* 16 bits AR816x */
+
 #define	ALC_MBOX_RD01_CONS_IDX		0x15F8
 #define	MBOX_RD0_CONS_IDX_MASK		0x0000FFFF
 #define	MBOX_RD1_CONS_IDX_MASK		0xFFFF0000
@@ -662,7 +776,7 @@
 #define	INTR_GPHY			0x00001000
 #define	INTR_GPHY_LOW_PW		0x00002000
 #define	INTR_TXQ_TO_RST			0x00004000
-#define	INTR_TX_PKT			0x00008000
+#define	INTR_TX_PKT0			0x00008000
 #define	INTR_RX_PKT0			0x00010000
 #define	INTR_RX_PKT1			0x00020000
 #define	INTR_RX_PKT2			0x00040000
@@ -676,6 +790,15 @@
 #define	INTR_PHY_LINK_DOWN		0x04000000
 #define	INTR_DIS_INT			0x80000000
 
+/* INTR status for AR816x/AR817x  4 TX queues, 8 RX queues */
+#define	INTR_TX_PKT1			0x00000020
+#define	INTR_TX_PKT2			0x00000040
+#define	INTR_TX_PKT3			0x00000080
+#define	INTR_RX_PKT4			0x08000000
+#define	INTR_RX_PKT5			0x10000000
+#define	INTR_RX_PKT6			0x20000000
+#define	INTR_RX_PKT7			0x40000000
+
 /* Interrupt Mask Register */
 #define	ALC_INTR_MASK			0x1604
 
@@ -687,6 +810,7 @@
 	(INTR_RD0_UNDERRUN | INTR_RD1_UNDERRUN |	\
 	INTR_RD2_UNDERRUN | INTR_RD3_UNDERRUN)
 #else
+#define	INTR_TX_PKT			INTR_TX_PKT0
 #define	INTR_RX_PKT			INTR_RX_PKT0
 #define	INTR_RD_UNDERRUN		INTR_RD0_UNDERRUN
 #endif
@@ -708,11 +832,54 @@
 #define	HDS_CFG_BACKFILLSIZE_SHIFT	8
 #define	HDS_CFG_MAX_HDRSIZE_SHIFT	20
 
+#define	ALC_MBOX_TD_PRI3_PROD_IDX	0x1618	/* 16 bits AR816x */
+
+#define	ALC_MBOX_TD_PRI2_PROD_IDX	0x161A	/* 16 bits AR816x */
+
+#define	ALC_MBOX_TD_PRI3_CONS_IDX	0x161C	/* 16 bits AR816x */
+
+#define	ALC_MBOX_TD_PRI2_CONS_IDX	0x161E	/* 16 bits AR816x */
+
 /* AR813x/AR815x registers for MAC statistics */
 #define	ALC_RX_MIB_BASE			0x1700
 
 #define	ALC_TX_MIB_BASE			0x1760
 
+#define	ALC_DRV				0x1804	/* AR816x */
+#define	DRV_ASPM_SPD10LMT_1M		0x00000000
+#define	DRV_ASPM_SPD10LMT_10M		0x00000001
+#define	DRV_ASPM_SPD10LMT_100M		0x00000002
+#define	DRV_ASPM_SPD10LMT_NO		0x00000003
+#define	DRV_ASPM_SPD10LMT_MASK		0x00000003
+#define	DRV_ASPM_SPD100LMT_1M		0x00000000
+#define	DRV_ASPM_SPD100LMT_10M		0x00000004
+#define	DRV_ASPM_SPD100LMT_100M		0x00000008
+#define	DRV_ASPM_SPD100LMT_NO		0x0000000C
+#define	DRV_ASPM_SPD100LMT_MASK		0x0000000C
+#define	DRV_ASPM_SPD1000LMT_100M	0x00000000
+#define	DRV_ASPM_SPD1000LMT_NO		0x00000010
+#define	DRV_ASPM_SPD1000LMT_1M		0x00000020
+#define	DRV_ASPM_SPD1000LMT_10M		0x00000030
+#define	DRV_ASPM_SPD1000LMT_MASK	0x00000000
+#define	DRV_WOLCAP_BIOS_EN		0x00000100
+#define	DRV_WOLMAGIC_EN			0x00000200
+#define	DRV_WOLLINKUP_EN		0x00000400
+#define	DRV_WOLPATTERN_EN		0x00000800
+#define	DRV_AZ_EN			0x00001000
+#define	DRV_WOLS5_BIOS_EN		0x00010000
+#define	DRV_WOLS5_EN			0x00020000
+#define	DRV_DISABLE			0x00040000
+#define	DRV_PHY_MASK			0x1FE00000
+#define	DRV_PHY_EEE			0x00200000
+#define	DRV_PHY_APAUSE			0x00400000
+#define	DRV_PHY_PAUSE			0x00800000
+#define	DRV_PHY_DUPLEX			0x01000000
+#define	DRV_PHY_10			0x02000000
+#define	DRV_PHY_100			0x04000000
+#define	DRV_PHY_1000			0x08000000
+#define	DRV_PHY_AUTO			0x10000000
+#define	DRV_PHY_SHIFT			21
+
 #define	ALC_CLK_GATING_CFG		0x1814
 #define	CLK_GATING_DMAW_ENB		0x0001
 #define	CLK_GATING_DMAR_ENB		0x0002
@@ -725,6 +892,52 @@
 
 #define	ALC_DEBUG_DATA1			0x1904
 
+#define	ALC_MSI_RETRANS_TIMER		0x1920
+#define	MSI_RETRANS_TIMER_MASK		0x0000FFFF
+#define	MSI_RETRANS_MASK_SEL_STD	0x00000000
+#define	MSI_RETRANS_MASK_SEL_LINE	0x00010000
+#define	MSI_RETRANS_TIMER_SHIFT		0
+
+#define	ALC_WRR				0x1938
+#define	WRR_PRI0_MASK			0x0000001F
+#define	WRR_PRI1_MASK			0x00001F00
+#define	WRR_PRI2_MASK			0x001F0000
+#define	WRR_PRI3_MASK			0x1F000000
+#define	WRR_PRI_RESTRICT_MASK		0x60000000
+#define	WRR_PRI_RESTRICT_ALL		0x00000000
+#define	WRR_PRI_RESTRICT_HI		0x20000000
+#define	WRR_PRI_RESTRICT_HI2		0x40000000
+#define	WRR_PRI_RESTRICT_NONE		0x60000000
+#define	WRR_PRI0_SHIFT			0
+#define	WRR_PRI1_SHIFT			8
+#define	WRR_PRI2_SHIFT			16
+#define	WRR_PRI3_SHIFT			24
+#define	WRR_PRI_DEFAULT			4
+#define	WRR_PRI_RESTRICT_SHIFT		29
+
+#define	ALC_HQTD_CFG			0x193C
+#define	HQTD_CFG_Q1_BURST_MASK		0x0000000F
+#define	HQTD_CFG_Q2_BURST_MASK		0x000000F0
+#define	HQTD_CFG_Q3_BURST_MASK		0x00000F00
+#define	HQTD_CFG_BURST_ENB		0x80000000
+#define	HQTD_CFG_Q1_BURST_SHIFT		0
+#define	HQTD_CFG_Q2_BURST_SHIFT		4
+#define	HQTD_CFG_Q3_BURST_SHIFT		8
+
+#define	ALC_MISC			0x19C0
+#define	MISC_INTNLOSC_OPEN		0x00000008
+#define	MISC_ISO_ENB			0x00001000
+#define	MISC_PSW_OCP_MASK		0x00E00000
+#define	MISC_PSW_OCP_SHIFT		21
+#define	MISC_PSW_OCP_DEFAULT		7
+
+#define	ALC_MISC2			0x19C8
+#define	MISC2_CALB_START		0x00000001
+
+#define	ALC_MISC3			0x19CC
+#define	MISC3_25M_NOTO_INTNL		0x00000001
+#define	MISC3_25M_BY_SW			0x00000002
+
 #define	ALC_MII_DBG_ADDR		0x1D
 #define	ALC_MII_DBG_DATA		0x1E
 
@@ -744,6 +957,9 @@
 #define	ANA_SEL_CLK125M_DSP		0x8000
 #define	ANA_MANUL_SWICH_ON_SHIFT	1
 
+#define	MII_DBG_ANACTL			0x00
+#define	DBG_ANACTL_DEFAULT		0x02EF
+
 #define	MII_ANA_CFG4			0x04
 #define	ANA_IECHO_ADJ_MASK		0x0F
 #define	ANA_IECHO_ADJ_3_MASK		0x000F
@@ -755,6 +971,9 @@
 #define	ANA_IECHO_ADJ_1_SHIFT		8
 #define	ANA_IECHO_ADJ_0_SHIFT		12
 
+#define	MII_DBG_SYSMODCTL		0x04
+#define	DBG_SYSMODCTL_DEFAULT		0xBB8B
+
 #define	MII_ANA_CFG5			0x05
 #define	ANA_SERDES_CDR_BW_MASK		0x0003
 #define	ANA_MS_PAD_DBG			0x0004
@@ -771,9 +990,17 @@
 #define	ANA_SERDES_CDR_BW_SHIFT		0
 #define	ANA_SERDES_TH_LOS_SHIFT		4
 
+#define	MII_DBG_SRDSYSMOD		0x05
+#define	DBG_SRDSYSMOD_DEFAULT		0x2C46
+
 #define	MII_ANA_CFG11			0x0B
 #define	ANA_PS_HIB_EN			0x8000
 
+#define	MII_DBG_HIBNEG			0x0B
+#define	DBG_HIBNEG_HIB_PULSE		0x1000
+#define	DBG_HIBNEG_PSHIB_EN		0x8000
+#define	DBG_HIBNEG_DEFAULT		0xBC40
+
 #define	MII_ANA_CFG18			0x12
 #define	ANA_TEST_MODE_10BT_01MASK	0x0003
 #define	ANA_LOOP_SEL_10BT		0x0004
@@ -788,9 +1015,36 @@
 #define	ANA_TRIGGER_SEL_TIMER_SHIFT	12
 #define	ANA_INTERVAL_SEL_TIMER_SHIFT	14
 
+#define	MII_DBG_TST10BTCFG		0x12
+#define	DBG_TST10BTCFG_DEFAULT		0x4C04
+
+#define	MII_DBG_AZ_ANADECT		0x15
+#define	DBG_AZ_ANADECT_DEFAULT		0x3220
+#define	DBG_AZ_ANADECT_LONG		0x3210
+
+#define	MII_DBG_MSE16DB			0x18
+#define	DBG_MSE16DB_UP			0x05EA
+#define	DBG_MSE16DB_DOWN		0x02EA
+
+#define	MII_DBG_MSE20DB			0x1C
+#define	DBG_MSE20DB_TH_MASK		0x01FC
+#define	DBG_MSE20DB_TH_DEFAULT		0x2E
+#define	DBG_MSE20DB_TH_HI		0x54
+#define	DBG_MSE20DB_TH_SHIFT		2
+
+#define	MII_DBG_AGC			0x23
+#define	DBG_AGC_2_VGA_MASK		0x3F00
+#define	DBG_AGC_2_VGA_SHIFT		8
+#define	DBG_AGC_LONG1G_LIMT		40
+#define	DBG_AGC_LONG100M_LIMT		44
+
 #define	MII_ANA_CFG41			0x29
 #define	ANA_TOP_PS_EN			0x8000
 
+#define	MII_DBG_LEGCYPS			0x29
+#define	DBG_LEGCYPS_ENB			0x8000
+#define	DBG_LEGCYPS_DEFAULT		0x129D
+
 #define	MII_ANA_CFG54			0x36
 #define	ANA_LONG_CABLE_TH_100_MASK	0x003F
 #define	ANA_DESERVED			0x0040
@@ -801,6 +1055,51 @@
 #define	ANA_LONG_CABLE_TH_100_SHIFT	0
 #define	ANA_SHORT_CABLE_TH_100_SHIFT	8
 
+#define	MII_DBG_TST100BTCFG		0x36
+#define	DBG_TST100BTCFG_DEFAULT		0xE12C
+
+#define	MII_DBG_GREENCFG		0x3B
+#define	DBG_GREENCFG_DEFAULT		0x7078
+
+#define	MII_DBG_GREENCFG2		0x3D
+#define	DBG_GREENCFG2_GATE_DFSE_EN	0x0080
+#define	DBG_GREENCFG2_BP_GREEN		0x8000
+
+/* Device addr 3 */
+#define	MII_EXT_PCS			3
+
+#define	MII_EXT_CLDCTL3			0x8003
+#define	EXT_CLDCTL3_BP_CABLE1TH_DET_GT	0x8000
+
+#define	MII_EXT_CLDCTL5			0x8005
+#define	EXT_CLDCTL5_BP_VD_HLFBIAS	0x4000
+
+#define	MII_EXT_CLDCTL6			0x8006
+#define	EXT_CLDCTL6_CAB_LEN_MASK	0x00FF
+#define	EXT_CLDCTL6_CAB_LEN_SHIFT	0
+#define	EXT_CLDCTL6_CAB_LEN_SHORT1G	116
+#define	EXT_CLDCTL6_CAB_LEN_SHORT100M	152
+
+#define	MII_EXT_VDRVBIAS		0x8062
+#define	EXT_VDRVBIAS_DEFAULT		3
+
+/* Device addr 7 */
+#define	MII_EXT_ANEG			7
+
+#define	MII_EXT_ANEG_LOCAL_EEEADV	0x3C
+#define	ANEG_LOCA_EEEADV_100BT		0x0002
+#define	ANEG_LOCA_EEEADV_1000BT		0x0004
+
+#define	MII_EXT_ANEG_AFE		0x801A
+#define	ANEG_AFEE_10BT_100M_TH		0x0040
+
+#define	MII_EXT_ANEG_S3DIG10		0x8023
+#define	ANEG_S3DIG10_SL			0x0001
+#define	ANEG_S3DIG10_DEFAULT		0
+
+#define	MII_EXT_ANEG_NLP78		0x8027
+#define	ANEG_NLP78_120M_DEFAULT		0x8A05
+
 /* Statistics counters collected by the MAC. */
 struct smb {
 	/* Rx stats. */
@@ -991,6 +1290,10 @@
 /* Water mark to kick reclaiming Tx buffers. */
 #define	ALC_TX_DESC_HIWAT	((ALC_TX_RING_CNT * 6) / 10)
 
+/*
+ * AR816x controllers support up to 16 messages but this driver
+ * uses single message.
+ */
 #define	ALC_MSI_MESSAGES	1
 #define	ALC_MSIX_MESSAGES	1
 
@@ -1166,12 +1469,13 @@
 #define	ALC_FLAG_MSIX		0x0008
 #define	ALC_FLAG_FASTETHER	0x0020
 #define	ALC_FLAG_JUMBO		0x0040
-#define	ALC_FLAG_ASPM_MON	0x0080
 #define	ALC_FLAG_CMB_BUG	0x0100
 #define	ALC_FLAG_SMB_BUG	0x0200
 #define	ALC_FLAG_L0S		0x0400
 #define	ALC_FLAG_L1S		0x0800
 #define	ALC_FLAG_APS		0x1000
+#define	ALC_FLAG_AR816X_FAMILY	0x2000
+#define	ALC_FLAG_LINK_WAR	0x4000
 #define	ALC_FLAG_LINK		0x8000
 
 	callout_t		sc_tick_ch;
@@ -1208,4 +1512,7 @@
 #define	ALC_TIMEOUT		1000
 #define	ALC_PHY_TIMEOUT		1000
 
+/* For compatibility with FreeBSD */
+#define IFM_UNKNOWN		31
+
 #endif	/* _IF_ALCREG_H */



Home | Main Index | Thread Index | Old Index