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; } /*