Module Name:    src
Committed By:   msaitoh
Date:           Mon Jan  7 01:43:22 UTC 2019

Modified Files:
        src/sys/dev/mii: inbmphyreg.h
        src/sys/dev/pci: if_wm.c

Log Message:
 Add EEE(802.3az) support for I350, I210, I211, PCH2 and newer.

 Not yet for I354(C2000). It'll be supported after implementing MI MII clause
45 register read/write API.


To generate a diff of this commit:
cvs rdiff -u -r1.15 -r1.16 src/sys/dev/mii/inbmphyreg.h
cvs rdiff -u -r1.613 -r1.614 src/sys/dev/pci/if_wm.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/mii/inbmphyreg.h
diff -u src/sys/dev/mii/inbmphyreg.h:1.15 src/sys/dev/mii/inbmphyreg.h:1.16
--- src/sys/dev/mii/inbmphyreg.h:1.15	Thu Dec 20 09:32:12 2018
+++ src/sys/dev/mii/inbmphyreg.h	Mon Jan  7 01:43:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: inbmphyreg.h,v 1.15 2018/12/20 09:32:12 msaitoh Exp $	*/
+/*	$NetBSD: inbmphyreg.h,v 1.16 2019/01/07 01:43:22 msaitoh Exp $	*/
 /*******************************************************************************
 Copyright (c) 2001-2015, Intel Corporation 
 All rights reserved.
@@ -78,6 +78,19 @@ POSSIBILITY OF SUCH DAMAGE.
 #define BME1000_PSCR_DOWNSHIFT_COUNTER_MASK     0x7000
 #define BME1000_PSCR_DOWNSHIFT_COUNTER_SHIFT    12
 
+/* Extended Management Interface (EMI) Registers */
+#define I82579_EMI_ADDR	0x10
+#define I82579_EMI_DATA	0x11
+#define I82579_EEE_ADVERTISEMENT 0x040e  /* IEEE MMD Register 7.60 */
+#define I82579_EEE_LP_ABILITY	0x040f   /* IEEE MMD Register 7.61 */
+#define I82579_EEE_PCS_STATUS	0x182e
+#define I82579_LPI_PLL_SHUT	0x4412
+#define I82579_LPI_PLL_SHUT_100		__BIT(2) /* 100M LPI PLL Shut Enable */
+#define I217_EEE_PCS_STATUS	0x9401   /* IEEE MMD Register 3.1 */
+#define I217_EEE_CAPABILITY	0x8000   /* IEEE MMD Register 3.20 */
+#define I217_EEE_ADVERTISEMENT	0x8001   /* IEEE MMD Register 7.60 */
+#define I217_EEE_LP_ABILITY	0x8002   /* IEEE MMD Register 7.61 */
+
 /* BM PHY Copper Specific Status */
 #define BM_CS_STATUS		BME1000_REG(0, 17)
 #define BM_CS_STATUS_LINK_UP	0x0400

Index: src/sys/dev/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.613 src/sys/dev/pci/if_wm.c:1.614
--- src/sys/dev/pci/if_wm.c:1.613	Thu Jan  3 08:46:03 2019
+++ src/sys/dev/pci/if_wm.c	Mon Jan  7 01:43:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.613 2019/01/03 08:46:03 msaitoh Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.614 2019/01/07 01:43:22 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -83,7 +83,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.613 2019/01/03 08:46:03 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.614 2019/01/07 01:43:22 msaitoh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -129,6 +129,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.
 #include <machine/endian.h>
 
 #include <dev/mii/mii.h>
+#include <dev/mii/mdio.h>
 #include <dev/mii/miivar.h>
 #include <dev/mii/miidevs.h>
 #include <dev/mii/mii_bitbang.h>
@@ -514,7 +515,9 @@ struct wm_softc {
 	int sc_funcid;			/* unit number of the chip (0 to 3) */
 	int sc_flags;			/* flags; see below */
 	int sc_if_flags;		/* last if_flags */
+	int sc_ec_capenable;		/* last ec_capenable */
 	int sc_flowflags;		/* 802.3x flow control flags */
+	uint16_t eee_lp_ability;	/* EEE link partner's ability */
 	int sc_align_tweak;
 
 	void *sc_ihs[WM_MAX_NINTR];	/*
@@ -852,10 +855,16 @@ static int	wm_kmrn_readreg(struct wm_sof
 static int	wm_kmrn_readreg_locked(struct wm_softc *, int, uint16_t *);
 static int	wm_kmrn_writereg(struct wm_softc *, int, uint16_t);
 static int	wm_kmrn_writereg_locked(struct wm_softc *, int, uint16_t);
+/* EMI register related */
+static int	wm_access_emi_reg_locked(device_t, int, uint16_t *, bool);
+static int	wm_read_emi_reg_locked(device_t, int, uint16_t *);
+static int	wm_write_emi_reg_locked(device_t, int, uint16_t);
 /* SGMII */
 static bool	wm_sgmii_uses_mdio(struct wm_softc *);
 static int	wm_sgmii_readreg(device_t, int, int);
+static int	wm_sgmii_readreg_locked(device_t, int, int, uint16_t *);
 static void	wm_sgmii_writereg(device_t, int, int, int);
+static int	wm_sgmii_writereg_locked(device_t, int, int, uint16_t);
 /* TBI related */
 static bool	wm_tbi_havesignal(struct wm_softc *, uint32_t);
 static void	wm_tbi_mediainit(struct wm_softc *);
@@ -967,7 +976,9 @@ static void	wm_disable_aspm(struct wm_so
 /* LPLU (Low Power Link Up) */
 static void	wm_lplu_d0_disable(struct wm_softc *);
 /* EEE */
-static void	wm_set_eee_i350(struct wm_softc *);
+static int	wm_set_eee_i350(struct wm_softc *);
+static int	wm_set_eee_pchlan(struct wm_softc *);
+static int	wm_set_eee(struct wm_softc *);
 
 /*
  * Workarounds (mainly PHY related).
@@ -2758,12 +2769,22 @@ alloc_retry:
 			sc->sc_mediatype = WM_MEDIATYPE_COPPER;
 		}
 	}
-	snprintb(buf, sizeof(buf), WM_FLAGS, sc->sc_flags);
-	aprint_verbose_dev(sc->sc_dev, "%s\n", buf);
+
+	if (sc->sc_type >= WM_T_PCH2)
+		sc->sc_flags |= WM_F_EEE;
+	else if ((sc->sc_type >= WM_T_I350) && (sc->sc_type <= WM_T_I211)
+	    && (sc->sc_mediatype == WM_MEDIATYPE_COPPER)) {
+		/* XXX: Need special handling for I354. (not yet) */
+		if (sc->sc_type != WM_T_I354)
+			sc->sc_flags |= WM_F_EEE;
+	}
 
 	/* Set device properties (macflags) */
 	prop_dictionary_set_uint32(dict, "macflags", sc->sc_flags);
 
+	snprintb(buf, sizeof(buf), WM_FLAGS, sc->sc_flags);
+	aprint_verbose_dev(sc->sc_dev, "%s\n", buf);
+
 	/* Initialize the media structures accordingly. */
 	if (sc->sc_mediatype == WM_MEDIATYPE_COPPER)
 		wm_gmii_mediainit(sc, wmp->wmp_product);
@@ -2854,6 +2875,9 @@ alloc_retry:
 		sc->sc_ethercom.ec_capabilities |=
 		    ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING;
 
+	if ((sc->sc_flags & WM_F_EEE) != 0)
+		sc->sc_ethercom.ec_capabilities |= ETHERCAP_EEE;
+
 	/*
 	 * We can perform TCPv4 and UDPv4 checkums in-bound.  Only
 	 * on i82543 and later.
@@ -3256,6 +3280,8 @@ wm_ifflags_cb(struct ethercom *ec)
 {
 	struct ifnet *ifp = &ec->ec_if;
 	struct wm_softc *sc = ifp->if_softc;
+	int iffchange, ecchange;
+	bool needreset = false;
 	int rc = 0;
 
 	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
@@ -3263,20 +3289,38 @@ wm_ifflags_cb(struct ethercom *ec)
 
 	WM_CORE_LOCK(sc);
 
-	int change = ifp->if_flags ^ sc->sc_if_flags;
+	/*
+	 * Check for if_flags.
+	 * Main usage is to prevent linkdown when opening bpf.
+	 */
+	iffchange = ifp->if_flags ^ sc->sc_if_flags;
 	sc->sc_if_flags = ifp->if_flags;
-
-	if ((change & ~(IFF_CANTCHANGE | IFF_DEBUG)) != 0) {
-		rc = ENETRESET;
-		goto out;
+	if ((iffchange & ~(IFF_CANTCHANGE | IFF_DEBUG)) != 0) {
+		needreset = true;
+		goto ec;
 	}
 
-	if ((change & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
+	/* iff related updates */
+	if ((iffchange & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
 		wm_set_filter(sc);
 
 	wm_set_vlan(sc);
 
+ec:
+	/* Check for ec_capenable. */
+	ecchange = ec->ec_capenable ^ sc->sc_ec_capenable;
+	sc->sc_ec_capenable = ec->ec_capenable;
+	if ((ecchange & ~ETHERCAP_EEE) != 0) {
+		needreset = true;
+		goto out;
+	}
+
+	/* ec related updates */
+	wm_set_eee(sc);
+	
 out:
+	if (needreset)
+		rc = ENETRESET;
 	WM_CORE_UNLOCK(sc);
 
 	return rc;
@@ -4992,13 +5036,7 @@ wm_reset(struct wm_softc *sc)
 	/* reload sc_ctrl */
 	sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL);
 
-	if (sc->sc_type == WM_T_I354) {
-#if 0
-		/* I354 uses an external PHY */
-		wm_set_eee_i354(sc);
-#endif
-	} else if ((sc->sc_type >= WM_T_I350) && (sc->sc_type <= WM_T_I211))
-		wm_set_eee_i350(sc);
+	wm_set_eee(sc);
 
 	/*
 	 * For PCH, this write will make sure that any noise will be detected
@@ -5624,6 +5662,7 @@ static int
 wm_init_locked(struct ifnet *ifp)
 {
 	struct wm_softc *sc = ifp->if_softc;
+	struct ethercom *ec = &sc->sc_ethercom;
 	int i, j, trynum, error = 0;
 	uint32_t reg;
 
@@ -6098,7 +6137,7 @@ wm_init_locked(struct ifnet *ifp)
 	    || (sc->sc_type == WM_T_I210))
 		sc->sc_rctl |= RCTL_SECRC;
 
-	if (((sc->sc_ethercom.ec_capabilities & ETHERCAP_JUMBO_MTU) != 0)
+	if (((ec->ec_capabilities & ETHERCAP_JUMBO_MTU) != 0)
 	    && (ifp->if_mtu > ETHERMTU)) {
 		sc->sc_rctl |= RCTL_LPE;
 		if ((sc->sc_flags & WM_F_NEWQUEUE) != 0)
@@ -6181,7 +6220,9 @@ wm_init_locked(struct ifnet *ifp)
 	ifp->if_flags &= ~IFF_OACTIVE;
 
  out:
+	/* Save last flags for the callback */
 	sc->sc_if_flags = ifp->if_flags;
+	sc->sc_ec_capenable = ec->ec_capenable;
 	if (error)
 		log(LOG_ERR, "%s: interface not running\n",
 		    device_xname(sc->sc_dev));
@@ -9002,6 +9043,9 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 			    ((sc->sc_mii.mii_media_status & IFM_ACTIVE) != 0));
 		}
 
+		/* Clear link partner's EEE ability */
+		sc->eee_lp_ability = 0;
+
 		/* FEXTNVM6 K1-off workaround */
 		if (sc->sc_type == WM_T_PCH_SPT) {
 			reg = CSR_READ(sc, WMREG_FEXTNVM6);
@@ -9027,6 +9071,11 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 		default:
 			break;
 		}
+
+		/* Enable/Disable EEE after link up */
+		if (sc->sc_phytype > WMPHY_82579)
+			wm_set_eee_pchlan(sc);
+
 	} else if (icr & ICR_RXSEQ) {
 		DPRINTF(WM_DEBUG_LINK, ("%s: LINK Receive sequence error\n",
 			device_xname(sc->sc_dev)));
@@ -9932,6 +9981,9 @@ wm_gmii_setup_phytype(struct wm_softc *s
 	if (new_readreg == wm_gmii_hv_readreg) {
 		sc->phy.readreg_locked = wm_gmii_hv_readreg_locked;
 		sc->phy.writereg_locked = wm_gmii_hv_writereg_locked;
+	} else if (new_readreg == wm_sgmii_readreg) {
+		sc->phy.readreg_locked = wm_sgmii_readreg_locked;
+		sc->phy.writereg_locked = wm_sgmii_writereg_locked;
 	} else if (new_readreg == wm_gmii_i82544_readreg) {
 		sc->phy.readreg_locked = wm_gmii_i82544_readreg_locked;
 		sc->phy.writereg_locked = wm_gmii_i82544_writereg_locked;
@@ -11286,6 +11338,41 @@ wm_kmrn_writereg_locked(struct wm_softc 
 	return 0;
 }
 
+/*
+ * EMI register related (82579, WMPHY_I217(PCH2 and newer))
+ * This access method is different from IEEE MMD.
+ */
+static int
+wm_access_emi_reg_locked(device_t dev, int reg, uint16_t *val, bool rd)
+{
+	struct wm_softc *sc = device_private(dev);
+	int rv;
+
+	rv = sc->phy.writereg_locked(dev, 2, I82579_EMI_ADDR, reg);
+	if (rv != 0)
+		return rv;
+
+	if (rd)
+		rv = sc->phy.readreg_locked(dev, 2, I82579_EMI_DATA, val);
+	else
+		rv = sc->phy.writereg_locked(dev, 2, I82579_EMI_DATA, *val);
+	return rv;
+}
+
+static int
+wm_read_emi_reg_locked(device_t dev, int reg, uint16_t *val)
+{
+
+	return wm_access_emi_reg_locked(dev, reg, val, true);
+}
+
+static int
+wm_write_emi_reg_locked(device_t dev, int reg, uint16_t val)
+{
+
+	return wm_access_emi_reg_locked(dev, reg, &val, false);
+}
+
 /* SGMII related */
 
 /*
@@ -11332,14 +11419,26 @@ static int
 wm_sgmii_readreg(device_t dev, int phy, int reg)
 {
 	struct wm_softc *sc = device_private(dev);
-	uint32_t i2ccmd;
-	int i, rv;
+	uint16_t val;
 
 	if (sc->phy.acquire(sc)) {
 		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
+	wm_sgmii_readreg_locked(dev, phy, reg, &val);
+
+	sc->phy.release(sc);
+	return val;
+}
+
+static int
+wm_sgmii_readreg_locked(device_t dev, int phy, int reg, uint16_t *val)
+{
+	struct wm_softc *sc = device_private(dev);
+	uint32_t i2ccmd;
+	int i, rv;
+
 	i2ccmd = (reg << I2CCMD_REG_ADDR_SHIFT)
 	    | (phy << I2CCMD_PHY_ADDR_SHIFT) | I2CCMD_OPCODE_READ;
 	CSR_WRITE(sc, WMREG_I2CCMD, i2ccmd);
@@ -11351,14 +11450,17 @@ wm_sgmii_readreg(device_t dev, int phy, 
 		if (i2ccmd & I2CCMD_READY)
 			break;
 	}
-	if ((i2ccmd & I2CCMD_READY) == 0)
+	if ((i2ccmd & I2CCMD_READY) == 0) {
 		device_printf(dev, "I2CCMD Read did not complete\n");
-	if ((i2ccmd & I2CCMD_ERROR) != 0)
+		rv = ETIMEDOUT;
+	}
+	if ((i2ccmd & I2CCMD_ERROR) != 0) {
 		device_printf(dev, "I2CCMD Error bit set\n");
+		rv = EIO;
+	}
 
-	rv = ((i2ccmd >> 8) & 0x00ff) | ((i2ccmd << 8) & 0xff00);
+	*val = (uint16_t)((i2ccmd >> 8) & 0x00ff) | ((i2ccmd << 8) & 0xff00);
 
-	sc->phy.release(sc);
 	return rv;
 }
 
@@ -11373,14 +11475,26 @@ static void
 wm_sgmii_writereg(device_t dev, int phy, int reg, int val)
 {
 	struct wm_softc *sc = device_private(dev);
-	uint32_t i2ccmd;
-	int i;
-	int swapdata;
 
 	if (sc->phy.acquire(sc) != 0) {
 		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
+
+	wm_sgmii_writereg_locked(dev, phy, reg, val);
+
+	sc->phy.release(sc);
+}
+
+static int
+wm_sgmii_writereg_locked(device_t dev, int phy, int reg, uint16_t val)
+{
+	struct wm_softc *sc = device_private(dev);
+	uint32_t i2ccmd;
+	uint16_t swapdata;
+	int rv = 0;
+	int i;
+
 	/* Swap the data bytes for the I2C interface */
 	swapdata = ((val >> 8) & 0x00FF) | ((val << 8) & 0xFF00);
 	i2ccmd = (reg << I2CCMD_REG_ADDR_SHIFT)
@@ -11394,12 +11508,16 @@ wm_sgmii_writereg(device_t dev, int phy,
 		if (i2ccmd & I2CCMD_READY)
 			break;
 	}
-	if ((i2ccmd & I2CCMD_READY) == 0)
+	if ((i2ccmd & I2CCMD_READY) == 0) {
 		device_printf(dev, "I2CCMD Write did not complete\n");
-	if ((i2ccmd & I2CCMD_ERROR) != 0)
+		rv = ETIMEDOUT;
+	}
+	if ((i2ccmd & I2CCMD_ERROR) != 0) {
 		device_printf(dev, "I2CCMD Error bit set\n");
+		rv = EIO;
+	}
 
-	sc->phy.release(sc);
+	return rv;
 }
 
 /* TBI related */
@@ -14850,29 +14968,139 @@ wm_lplu_d0_disable(struct wm_softc *sc)
 
 /* EEE */
 
-static void
+static int
 wm_set_eee_i350(struct wm_softc *sc)
 {
+	struct ethercom *ec = &sc->sc_ethercom;
 	uint32_t ipcnfg, eeer;
+	uint32_t ipcnfg_mask
+	    = IPCNFG_EEE_1G_AN | IPCNFG_EEE_100M_AN | IPCNFG_10BASE_TE;
+	uint32_t eeer_mask = EEER_TX_LPI_EN | EEER_RX_LPI_EN | EEER_LPI_FC;
 
 	ipcnfg = CSR_READ(sc, WMREG_IPCNFG);
 	eeer = CSR_READ(sc, WMREG_EEER);
 
-	if ((sc->sc_flags & WM_F_EEE) != 0) {
-		ipcnfg |= (IPCNFG_EEE_1G_AN | IPCNFG_EEE_100M_AN);
-		eeer |= (EEER_TX_LPI_EN | EEER_RX_LPI_EN
-		    | EEER_LPI_FC);
-	} else {
-		ipcnfg &= ~(IPCNFG_EEE_1G_AN | IPCNFG_EEE_100M_AN);
-		ipcnfg &= ~IPCNFG_10BASE_TE;
-		eeer &= ~(EEER_TX_LPI_EN | EEER_RX_LPI_EN
-		    | EEER_LPI_FC);
+	/* enable or disable per user setting */
+	if ((ec->ec_capenable & ETHERCAP_EEE) != 0) {
+		ipcnfg |= ipcnfg_mask;
+		eeer |= eeer_mask;
+	} else {
+		ipcnfg &= ~ipcnfg_mask;
+		eeer &= ~eeer_mask;
 	}
 
 	CSR_WRITE(sc, WMREG_IPCNFG, ipcnfg);
 	CSR_WRITE(sc, WMREG_EEER, eeer);
 	CSR_READ(sc, WMREG_IPCNFG); /* XXX flush? */
 	CSR_READ(sc, WMREG_EEER); /* XXX flush? */
+
+	return 0;
+}
+
+static int
+wm_set_eee_pchlan(struct wm_softc *sc)
+{
+	device_t dev = sc->sc_dev;
+	struct ethercom *ec = &sc->sc_ethercom;
+	uint16_t lpa, pcs_status, adv_addr, adv, lpi_ctrl, data;
+	int rv = 0;
+
+	switch (sc->sc_phytype) {
+	case WMPHY_82579:
+		lpa = I82579_EEE_LP_ABILITY;
+		pcs_status = I82579_EEE_PCS_STATUS;
+		adv_addr = I82579_EEE_ADVERTISEMENT;
+		break;
+	case WMPHY_I217:
+		lpa = I217_EEE_LP_ABILITY;
+		pcs_status = I217_EEE_PCS_STATUS;
+		adv_addr = I217_EEE_ADVERTISEMENT;
+		break;
+	default:
+		return 0;
+	}
+
+	if (sc->phy.acquire(sc)) {
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
+		return 0;
+	}
+
+	rv = sc->phy.readreg_locked(dev, 1, I82579_LPI_CTRL, &lpi_ctrl);
+	if (rv != 0)
+		goto release;
+
+	/* Clear bits that enable EEE in various speeds */
+	lpi_ctrl &= ~I82579_LPI_CTRL_ENABLE;
+
+	if ((ec->ec_capenable & ETHERCAP_EEE) != 0) {
+		/* Save off link partner's EEE ability */
+		rv = wm_read_emi_reg_locked(dev, lpa, &sc->eee_lp_ability);
+		if (rv != 0)
+			goto release;
+
+		/* Read EEE advertisement */
+		if ((rv = wm_read_emi_reg_locked(dev, adv_addr, &adv)) != 0)
+			goto release;
+
+		/*
+		 * Enable EEE only for speeds in which the link partner is
+		 * EEE capable and for which we advertise EEE.
+		 */
+		if (adv & sc->eee_lp_ability & AN_EEEADVERT_1000_T)
+			lpi_ctrl |= I82579_LPI_CTRL_EN_1000;
+		if (adv & sc->eee_lp_ability & AN_EEEADVERT_100_TX) {
+			sc->phy.readreg_locked(dev, 2, MII_ANLPAR, &data);
+			if ((data & ANLPAR_TX_FD) != 0)
+				lpi_ctrl |= I82579_LPI_CTRL_EN_100;
+			else {
+				/*
+				 * EEE is not supported in 100Half, so ignore
+				 * partner's EEE in 100 ability if full-duplex
+				 * is not advertised.
+				 */
+				sc->eee_lp_ability
+				    &= ~AN_EEEADVERT_100_TX;
+			}
+		}
+	}
+
+	if (sc->sc_phytype == WMPHY_82579) {
+		rv = wm_read_emi_reg_locked(dev, I82579_LPI_PLL_SHUT, &data);
+		if (rv != 0)
+			goto release;
+
+		data &= ~I82579_LPI_PLL_SHUT_100;
+		rv = wm_write_emi_reg_locked(dev, I82579_LPI_PLL_SHUT, data);
+	}
+
+	/* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */
+	if ((rv = wm_read_emi_reg_locked(dev, pcs_status, &data)) != 0)
+		goto release;
+
+	rv = wm_write_emi_reg_locked(dev, I82579_LPI_CTRL, lpi_ctrl);
+release:
+	sc->phy.release(sc);
+
+	return rv;
+}
+
+static int
+wm_set_eee(struct wm_softc *sc)
+{
+	struct ethercom *ec = &sc->sc_ethercom;
+
+	if ((ec->ec_capabilities & ETHERCAP_EEE) == 0)
+		return 0;
+
+	if (sc->sc_type == WM_T_I354) {
+		/* I354 uses an external PHY */
+		return 0; /* not yet */
+	} else if ((sc->sc_type >= WM_T_I350) && (sc->sc_type <= WM_T_I211))
+		return wm_set_eee_i350(sc);
+	else if (sc->sc_type >= WM_T_PCH2)
+		return wm_set_eee_pchlan(sc);
+
+	return 0;
 }
 
 /*

Reply via email to