Module Name: src Committed By: msaitoh Date: Tue Jun 2 14:19:26 UTC 2015
Modified Files: src/sys/dev/pci: if_wm.c if_wmreg.h if_wmvar.h Log Message: Fix a lot of bugs to 82575 and newer's SERDES based systems work. - Add SERDES specific functions. - Fix IO pin configuration. - Reset autonego timer when link becomes up. TODO: - Fix a bug that SFP ROM can't read. - Perhaps some work is required for 8257[12] serdes systems. - Remove duplicated code in TBI's link related functions. To generate a diff of this commit: cvs rdiff -u -r1.324 -r1.325 src/sys/dev/pci/if_wm.c cvs rdiff -u -r1.72 -r1.73 src/sys/dev/pci/if_wmreg.h cvs rdiff -u -r1.24 -r1.25 src/sys/dev/pci/if_wmvar.h 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/pci/if_wm.c diff -u src/sys/dev/pci/if_wm.c:1.324 src/sys/dev/pci/if_wm.c:1.325 --- src/sys/dev/pci/if_wm.c:1.324 Tue Jun 2 13:26:36 2015 +++ src/sys/dev/pci/if_wm.c Tue Jun 2 14:19:26 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wm.c,v 1.324 2015/06/02 13:26:36 msaitoh Exp $ */ +/* $NetBSD: if_wm.c,v 1.325 2015/06/02 14:19:26 msaitoh Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -81,7 +81,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.324 2015/06/02 13:26:36 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.325 2015/06/02 14:19:26 msaitoh Exp $"); #ifdef _KERNEL_OPT #include "opt_net_mpsafe.h" @@ -392,8 +392,8 @@ struct wm_softc { uint32_t sc_pba; /* prototype PBA register */ int sc_tbi_linkup; /* TBI link status */ - int sc_tbi_anegticks; /* autonegotiation ticks */ - int sc_tbi_ticks; /* tbi ticks */ + int sc_tbi_serdes_anegticks; /* autonegotiation ticks */ + int sc_tbi_serdes_ticks; /* tbi ticks */ int sc_mchash_type; /* multicast filter offset */ @@ -595,6 +595,7 @@ static void wm_txintr(struct wm_softc *) static void wm_rxintr(struct wm_softc *); static void wm_linkintr_gmii(struct wm_softc *, uint32_t); static void wm_linkintr_tbi(struct wm_softc *, uint32_t); +static void wm_linkintr_serdes(struct wm_softc *, uint32_t); static void wm_linkintr(struct wm_softc *, uint32_t); static int wm_intr(void *); @@ -602,6 +603,8 @@ static int wm_intr(void *); * Media related. * GMII, SGMII, TBI, SERDES and SFP. */ +/* Common */ +static void wm_tbi_serdes_set_linkled(struct wm_softc *); /* GMII related */ static void wm_gmii_reset(struct wm_softc *); static int wm_get_phy_id_82575(struct wm_softc *); @@ -631,12 +634,16 @@ static bool wm_sgmii_uses_mdio(struct wm static int wm_sgmii_readreg(device_t, int, int); static void wm_sgmii_writereg(device_t, int, int, int); /* TBI related */ -static int wm_check_for_link(struct wm_softc *); static void wm_tbi_mediainit(struct wm_softc *); static int wm_tbi_mediachange(struct ifnet *); static void wm_tbi_mediastatus(struct ifnet *, struct ifmediareq *); -static void wm_tbi_set_linkled(struct wm_softc *); -static void wm_tbi_check_link(struct wm_softc *); +static int wm_check_for_link(struct wm_softc *); +static void wm_tbi_tick(struct wm_softc *); +/* SERDES related */ +static void wm_serdes_power_up_link_82575(struct wm_softc *); +static int wm_serdes_mediachange(struct ifnet *); +static void wm_serdes_mediastatus(struct ifnet *, struct ifmediareq *); +static void wm_serdes_tick(struct wm_softc *); /* SFP related */ static int wm_sfp_read_data_byte(struct wm_softc *, uint16_t, uint8_t *); static uint32_t wm_sfp_get_media_type(struct wm_softc *); @@ -728,6 +735,7 @@ static void wm_k1_gig_workaround_hv(stru static void wm_set_mdio_slow_mode_hv(struct wm_softc *); static void wm_configure_k1_ich8lan(struct wm_softc *, int); static void wm_reset_init_script_82575(struct wm_softc *); +static void wm_reset_mdicnfg_82580(struct wm_softc *); CFATTACH_DECL3_NEW(wm, sizeof(struct wm_softc), wm_match, wm_attach, wm_detach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN); @@ -1366,7 +1374,7 @@ wm_attach(device_t parent, device_t self prop_data_t ea; prop_number_t pn; uint8_t enaddr[ETHER_ADDR_LEN]; - uint16_t cfg1, cfg2, swdpin, io3; + uint16_t cfg1, cfg2, swdpin, nvmword; pcireg_t preg, memtype; uint16_t eeprom_data, apme_mask; bool force_clear_smbi; @@ -2046,6 +2054,14 @@ wm_attach(device_t parent, device_t self printf("WOL\n"); #endif + if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)) { + /* Check NVM for autonegotiation */ + if (wm_nvm_read(sc, NVM_OFF_COMPAT, 1, &nvmword) == 0) { + if ((nvmword & NVM_COMPAT_SERDES_FORCE_MODE) != 0) + sc->sc_flags |= WM_F_PCS_DIS_AUTONEGO; + } + } + /* * XXX need special handling for some multiple port cards * to disable a paticular port. @@ -2067,17 +2083,37 @@ wm_attach(device_t parent, device_t self if (cfg1 & NVM_CFG1_ILOS) sc->sc_ctrl |= CTRL_ILOS; - if (sc->sc_type >= WM_T_82544) { - sc->sc_ctrl |= - ((swdpin >> NVM_SWDPIN_SWDPIO_SHIFT) & 0xf) << - CTRL_SWDPIO_SHIFT; - sc->sc_ctrl |= - ((swdpin >> NVM_SWDPIN_SWDPIN_SHIFT) & 0xf) << - CTRL_SWDPINS_SHIFT; - } else { - sc->sc_ctrl |= - ((cfg1 >> NVM_CFG1_SWDPIO_SHIFT) & 0xf) << - CTRL_SWDPIO_SHIFT; + + /* + * XXX + * This code isn't correct because pin 2 and 3 are located + * in different position on newer chips. Check all datasheet. + * + * Until resolve this problem, check if a chip < 82580 + */ + if (sc->sc_type <= WM_T_82580) { + if (sc->sc_type >= WM_T_82544) { + sc->sc_ctrl |= + ((swdpin >> NVM_SWDPIN_SWDPIO_SHIFT) & 0xf) << + CTRL_SWDPIO_SHIFT; + sc->sc_ctrl |= + ((swdpin >> NVM_SWDPIN_SWDPIN_SHIFT) & 0xf) << + CTRL_SWDPINS_SHIFT; + } else { + sc->sc_ctrl |= + ((cfg1 >> NVM_CFG1_SWDPIO_SHIFT) & 0xf) << + CTRL_SWDPIO_SHIFT; + } + } + + /* XXX For other than 82580? */ + if (sc->sc_type == WM_T_82580) { + wm_nvm_read(sc, NVM_OFF_CFG3_PORTA, 1, &nvmword); + printf("CFG3 = %08x\n", (uint32_t)nvmword); + if (nvmword & __BIT(13)) { + printf("SET ILOS\n"); + sc->sc_ctrl |= CTRL_ILOS; + } } #if 0 @@ -2255,8 +2291,8 @@ wm_attach(device_t parent, device_t self switch (sc->sc_type) { case WM_T_82573: /* XXX limited to 9234 if ASPM is disabled */ - wm_nvm_read(sc, NVM_OFF_INIT_3GIO_3, 1, &io3); - if ((io3 & NVM_3GIO_3_ASPM_MASK) != 0) + wm_nvm_read(sc, NVM_OFF_INIT_3GIO_3, 1, &nvmword); + if ((nvmword & NVM_3GIO_3_ASPM_MASK) != 0) sc->sc_ethercom.ec_capabilities |= ETHERCAP_JUMBO_MTU; break; case WM_T_82571: @@ -2655,8 +2691,11 @@ wm_tick(void *arg) if (sc->sc_flags & WM_F_HAS_MII) mii_tick(&sc->sc_mii); + else if ((sc->sc_type >= WM_T_82575) + && (sc->sc_mediatype == WM_MEDIATYPE_SERDES)) + wm_serdes_tick(sc); else - wm_tbi_check_link(sc); + wm_tbi_tick(sc); out: WM_TX_UNLOCK(sc); @@ -3772,9 +3811,7 @@ wm_reset(struct wm_softc *sc) switch (sc->sc_type) { case WM_T_82575: case WM_T_82576: -#if 0 /* XXX */ case WM_T_82580: -#endif case WM_T_I350: case WM_T_I354: case WM_T_ICH8: @@ -3782,11 +3819,7 @@ wm_reset(struct wm_softc *sc) if ((CSR_READ(sc, WMREG_EECD) & EECD_EE_PRES) == 0) { /* Not found */ sc->sc_flags |= WM_F_EEPROM_INVALID; - if ((sc->sc_type == WM_T_82575) - || (sc->sc_type == WM_T_82576) - || (sc->sc_type == WM_T_82580) - || (sc->sc_type == WM_T_I350) - || (sc->sc_type == WM_T_I354)) + if (sc->sc_type == WM_T_82575) wm_reset_init_script_82575(sc); } break; @@ -3824,7 +3857,7 @@ wm_reset(struct wm_softc *sc) if ((sc->sc_flags & WM_F_NEWQUEUE) != 0) CSR_WRITE(sc, WMREG_WUC, 0); - /* XXX need special handling for 82580 */ + wm_reset_mdicnfg_82580(sc); } /* @@ -6015,7 +6048,8 @@ wm_linkintr_tbi(struct wm_softc *sc, uin device_xname(sc->sc_dev))); sc->sc_tbi_linkup = 0; } - wm_tbi_set_linkled(sc); + /* Update LED */ + wm_tbi_serdes_set_linkled(sc); } else if (icr & ICR_RXSEQ) { DPRINTF(WM_DEBUG_LINK, ("%s: LINK: Receive sequence error\n", @@ -6024,6 +6058,76 @@ wm_linkintr_tbi(struct wm_softc *sc, uin } /* + * wm_linkintr_serdes: + * + * Helper; handle link interrupts for TBI mode. + */ +static void +wm_linkintr_serdes(struct wm_softc *sc, uint32_t icr) +{ + struct mii_data *mii = &sc->sc_mii; + struct ifmedia_entry *ife = sc->sc_mii.mii_media.ifm_cur; + uint32_t pcs_adv, pcs_lpab, reg; + + DPRINTF(WM_DEBUG_LINK, ("%s: %s:\n", device_xname(sc->sc_dev), + __func__)); + + if (icr & ICR_LSC) { + /* Check PCS */ + reg = CSR_READ(sc, WMREG_PCS_LSTS); + if ((reg & PCS_LSTS_LINKOK) != 0) { + mii->mii_media_status |= IFM_ACTIVE; + sc->sc_tbi_linkup = 1; + } else { + mii->mii_media_status |= IFM_NONE; + sc->sc_tbi_linkup = 0; + wm_tbi_serdes_set_linkled(sc); + return; + } + mii->mii_media_active |= IFM_1000_SX; + if ((reg & PCS_LSTS_FDX) != 0) + mii->mii_media_active |= IFM_FDX; + else + mii->mii_media_active |= IFM_HDX; + if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { + /* Check flow */ + reg = CSR_READ(sc, WMREG_PCS_LSTS); + if ((reg & PCS_LSTS_AN_COMP) == 0) { + DPRINTF(WM_DEBUG_LINK, + ("XXX LINKOK but not ACOMP\n")); + return; + } + pcs_adv = CSR_READ(sc, WMREG_PCS_ANADV); + pcs_lpab = CSR_READ(sc, WMREG_PCS_LPAB); + DPRINTF(WM_DEBUG_LINK, + ("XXX AN result %08x, %08x\n", pcs_adv, pcs_lpab)); + if ((pcs_adv & TXCW_SYM_PAUSE) + && (pcs_lpab & TXCW_SYM_PAUSE)) { + mii->mii_media_active |= IFM_FLOW + | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE; + } else if (((pcs_adv & TXCW_SYM_PAUSE) == 0) + && (pcs_adv & TXCW_ASYM_PAUSE) + && (pcs_lpab & TXCW_SYM_PAUSE) + && (pcs_lpab & TXCW_ASYM_PAUSE)) + mii->mii_media_active |= IFM_FLOW + | IFM_ETH_TXPAUSE; + else if ((pcs_adv & TXCW_SYM_PAUSE) + && (pcs_adv & TXCW_ASYM_PAUSE) + && ((pcs_lpab & TXCW_SYM_PAUSE) == 0) + && (pcs_lpab & TXCW_ASYM_PAUSE)) + mii->mii_media_active |= IFM_FLOW + | IFM_ETH_RXPAUSE; + } + /* Update LED */ + wm_tbi_serdes_set_linkled(sc); + } else { + DPRINTF(WM_DEBUG_LINK, + ("%s: LINK: Receive sequence error\n", + device_xname(sc->sc_dev))); + } +} + +/* * wm_linkintr: * * Helper; handle link interrupts. @@ -6034,6 +6138,9 @@ wm_linkintr(struct wm_softc *sc, uint32_ if (sc->sc_flags & WM_F_HAS_MII) wm_linkintr_gmii(sc, icr); + else if ((sc->sc_mediatype == WM_MEDIATYPE_SERDES) + && (sc->sc_type >= WM_T_82575)) + wm_linkintr_serdes(sc, icr); else wm_linkintr_tbi(sc, icr); } @@ -6118,6 +6225,28 @@ wm_intr(void *arg) * GMII, SGMII, TBI (and SERDES) */ +/* Common */ + +/* + * wm_tbi_serdes_set_linkled: + * + * Update the link LED on TBI and SERDES devices. + */ +static void +wm_tbi_serdes_set_linkled(struct wm_softc *sc) +{ + + if (sc->sc_tbi_linkup) + sc->sc_ctrl |= CTRL_SWDPIN(0); + else + sc->sc_ctrl &= ~CTRL_SWDPIN(0); + + /* 82540 or newer devices are active low */ + sc->sc_ctrl ^= (sc->sc_type >= WM_T_82540) ? CTRL_SWDPIN(0) : 0; + + CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); +} + /* GMII related */ /* @@ -7498,14 +7627,19 @@ wm_tbi_mediainit(struct wm_softc *sc) else sc->sc_tipg = TIPG_LG_DFLT; - sc->sc_tbi_anegticks = 5; + sc->sc_tbi_serdes_anegticks = 5; /* Initialize our media structures */ sc->sc_mii.mii_ifp = ifp; - sc->sc_ethercom.ec_mii = &sc->sc_mii; - ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, wm_tbi_mediachange, - wm_tbi_mediastatus); + + if ((sc->sc_type >= WM_T_82575) + && (sc->sc_mediatype == WM_MEDIATYPE_SERDES)) + ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, + wm_serdes_mediachange, wm_serdes_mediastatus); + else + ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, + wm_tbi_mediachange, wm_tbi_mediastatus); /* * SWD Pins: @@ -7514,7 +7648,11 @@ wm_tbi_mediainit(struct wm_softc *sc) * 1 = Loss Of Signal (input) */ sc->sc_ctrl |= CTRL_SWDPIO(0); - sc->sc_ctrl &= ~CTRL_SWDPIO(1); + + /* XXX Perhaps this is only for TBI */ + if (sc->sc_mediatype != WM_MEDIATYPE_SERDES) + sc->sc_ctrl &= ~CTRL_SWDPIO(1); + if (sc->sc_mediatype == WM_MEDIATYPE_SERDES) sc->sc_ctrl &= ~CTRL_LRST; @@ -7558,15 +7696,16 @@ wm_tbi_mediachange(struct ifnet *ifp) uint32_t status; int i; - if (sc->sc_mediatype == WM_MEDIATYPE_SERDES) - return 0; + if (sc->sc_mediatype == WM_MEDIATYPE_SERDES) { + /* XXX need some work for >= 82571 and < 82575 */ + if (sc->sc_type < WM_T_82575) + return 0; + } if ((sc->sc_type == WM_T_82571) || (sc->sc_type == WM_T_82572) || (sc->sc_type >= WM_T_82575)) CSR_WRITE(sc, WMREG_SCTL, SCTL_DISABLE_SERDES_LOOPBACK); - /* XXX power_up_serdes_link_82575() */ - sc->sc_ctrl &= ~CTRL_LRST; sc->sc_txcw = TXCW_ANE; if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) @@ -7650,7 +7789,7 @@ wm_tbi_mediachange(struct ifnet *ifp) sc->sc_tbi_linkup = 0; } - wm_tbi_set_linkled(sc); + wm_tbi_serdes_set_linkled(sc); return 0; } @@ -7692,7 +7831,7 @@ wm_tbi_mediastatus(struct ifnet *ifp, st ifmr->ifm_active |= IFM_FLOW | IFM_ETH_TXPAUSE; } -/* XXX Currently TBI only */ +/* XXX TBI only */ static int wm_check_for_link(struct wm_softc *sc) { @@ -7703,8 +7842,11 @@ wm_check_for_link(struct wm_softc *sc) uint32_t sig; if (sc->sc_mediatype == WM_MEDIATYPE_SERDES) { - sc->sc_tbi_linkup = 1; - return 0; + /* XXX need some work for >= 82571 */ + if (sc->sc_type >= WM_T_82571) { + sc->sc_tbi_linkup = 1; + return 0; + } } rxcw = CSR_READ(sc, WMREG_RXCW); @@ -7769,43 +7911,20 @@ wm_check_for_link(struct wm_softc *sc) } /* - * wm_tbi_set_linkled: + * wm_tbi_tick: * - * Update the link LED on 1000BASE-X devices. + * Check the link on TBI devices. + * This function acts as mii_tick(). */ static void -wm_tbi_set_linkled(struct wm_softc *sc) +wm_tbi_tick(struct wm_softc *sc) { - - if (sc->sc_tbi_linkup) - sc->sc_ctrl |= CTRL_SWDPIN(0); - else - sc->sc_ctrl &= ~CTRL_SWDPIN(0); - - /* 82540 or newer devices are active low */ - sc->sc_ctrl ^= (sc->sc_type >= WM_T_82540) ? CTRL_SWDPIN(0) : 0; - - CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); -} - -/* - * wm_tbi_check_link: - * - * Check the link on 1000BASE-X devices. - */ -static void -wm_tbi_check_link(struct wm_softc *sc) -{ - struct ifmedia_entry *ife = sc->sc_mii.mii_media.ifm_cur; + struct mii_data *mii = &sc->sc_mii; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; uint32_t status; KASSERT(WM_TX_LOCKED(sc)); - if (sc->sc_mediatype == WM_MEDIATYPE_SERDES) { - sc->sc_tbi_linkup = 1; - return; - } - status = CSR_READ(sc, WMREG_STATUS); /* XXX is this needed? */ @@ -7824,36 +7943,225 @@ wm_tbi_check_link(struct wm_softc *sc) device_xname(sc->sc_dev), (status & STATUS_FD) ? "FDX" : "HDX")); sc->sc_tbi_linkup = 1; + sc->sc_tbi_serdes_ticks = 0; } - if ((sc->sc_ethercom.ec_if.if_flags & IFF_UP) - && ((status & STATUS_LU) == 0)) { + if ((sc->sc_ethercom.ec_if.if_flags & IFF_UP) == 0) + goto setled; + + if ((status & STATUS_LU) == 0) { sc->sc_tbi_linkup = 0; - if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { - /* If the timer expired, retry autonegotiation */ - if (++sc->sc_tbi_ticks >= sc->sc_tbi_anegticks) { - DPRINTF(WM_DEBUG_LINK, ("EXPIRE\n")); - sc->sc_tbi_ticks = 0; - /* - * Reset the link, and let autonegotiation do - * its thing - */ - sc->sc_ctrl |= CTRL_LRST; - CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); - CSR_WRITE_FLUSH(sc); - delay(1000); - sc->sc_ctrl &= ~CTRL_LRST; - CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); - CSR_WRITE_FLUSH(sc); - delay(1000); - CSR_WRITE(sc, WMREG_TXCW, - sc->sc_txcw & ~TXCW_ANE); - CSR_WRITE(sc, WMREG_TXCW, sc->sc_txcw); - } + /* If the timer expired, retry autonegotiation */ + if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) + && (++sc->sc_tbi_serdes_ticks + >= sc->sc_tbi_serdes_anegticks)) { + DPRINTF(WM_DEBUG_LINK, ("EXPIRE\n")); + sc->sc_tbi_serdes_ticks = 0; + /* + * Reset the link, and let autonegotiation do + * its thing + */ + sc->sc_ctrl |= CTRL_LRST; + CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); + CSR_WRITE_FLUSH(sc); + delay(1000); + sc->sc_ctrl &= ~CTRL_LRST; + CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); + CSR_WRITE_FLUSH(sc); + delay(1000); + CSR_WRITE(sc, WMREG_TXCW, + sc->sc_txcw & ~TXCW_ANE); + CSR_WRITE(sc, WMREG_TXCW, sc->sc_txcw); } } - wm_tbi_set_linkled(sc); +setled: + wm_tbi_serdes_set_linkled(sc); +} + +/* SERDES related */ +static void +wm_serdes_power_up_link_82575(struct wm_softc *sc) +{ + uint32_t reg; + + if ((sc->sc_mediatype != WM_MEDIATYPE_SERDES) + && ((sc->sc_flags & WM_F_SGMII) == 0)) + return; + + reg = CSR_READ(sc, WMREG_PCS_CFG); + reg |= PCS_CFG_PCS_EN; + CSR_WRITE(sc, WMREG_PCS_CFG, reg); + + reg = CSR_READ(sc, WMREG_CTRL_EXT); + reg &= ~CTRL_EXT_SWDPIN(3); + CSR_WRITE(sc, WMREG_CTRL_EXT, reg); + CSR_WRITE_FLUSH(sc); +} + +static int +wm_serdes_mediachange(struct ifnet *ifp) +{ + struct wm_softc *sc = ifp->if_softc; + bool pcs_autoneg = true; /* XXX */ + uint32_t ctrl_ext, pcs_lctl, reg; + + /* XXX Currently, this function is not called on 8257[12] */ + if ((sc->sc_type == WM_T_82571) || (sc->sc_type == WM_T_82572) + || (sc->sc_type >= WM_T_82575)) + CSR_WRITE(sc, WMREG_SCTL, SCTL_DISABLE_SERDES_LOOPBACK); + + wm_serdes_power_up_link_82575(sc); + + sc->sc_ctrl |= CTRL_SLU; + + if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)) + sc->sc_ctrl |= CTRL_SWDPIN(0) | CTRL_SWDPIN(1); + + ctrl_ext = CSR_READ(sc, WMREG_CTRL_EXT); + pcs_lctl = CSR_READ(sc, WMREG_PCS_LCTL); + switch (ctrl_ext & CTRL_EXT_LINK_MODE_MASK) { + case CTRL_EXT_LINK_MODE_SGMII: + pcs_autoneg = true; + pcs_lctl &= ~PCS_LCTL_AN_TIMEOUT; + break; + case CTRL_EXT_LINK_MODE_1000KX: + pcs_autoneg = false; + /* FALLTHROUGH */ + default: + if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)){ + if ((sc->sc_flags & WM_F_PCS_DIS_AUTONEGO) != 0) + pcs_autoneg = false; + } + sc->sc_ctrl |= CTRL_SPEED_1000 | CTRL_FRCSPD | CTRL_FD + | CTRL_FRCFDX; + pcs_lctl |= PCS_LCTL_FSV_1000 | PCS_LCTL_FDV_FULL; + } + CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); + + if (pcs_autoneg) { + pcs_lctl |= PCS_LCTL_AN_ENABLE | PCS_LCTL_AN_RESTART; + pcs_lctl &= ~PCS_LCTL_FORCE_FC; + + reg = CSR_READ(sc, WMREG_PCS_ANADV); + reg &= ~(TXCW_ASYM_PAUSE | TXCW_SYM_PAUSE); + reg |= TXCW_ASYM_PAUSE | TXCW_SYM_PAUSE; + CSR_WRITE(sc, WMREG_PCS_ANADV, reg); + } else + pcs_lctl |= PCS_LCTL_FSD | PCS_LCTL_FORCE_FC; + + CSR_WRITE(sc, WMREG_PCS_LCTL, pcs_lctl); + + + return 0; +} + +static void +wm_serdes_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct wm_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_mii; + struct ifmedia_entry *ife = sc->sc_mii.mii_media.ifm_cur; + uint32_t pcs_adv, pcs_lpab, reg; + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + /* Check PCS */ + reg = CSR_READ(sc, WMREG_PCS_LSTS); + if ((reg & PCS_LSTS_LINKOK) == 0) { + ifmr->ifm_active |= IFM_NONE; + sc->sc_tbi_linkup = 0; + goto setled; + } + + sc->sc_tbi_linkup = 1; + ifmr->ifm_status |= IFM_ACTIVE; + ifmr->ifm_active |= IFM_1000_SX; /* XXX */ + if ((reg & PCS_LSTS_FDX) != 0) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + mii->mii_media_active &= ~IFM_ETH_FMASK; + if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { + /* Check flow */ + reg = CSR_READ(sc, WMREG_PCS_LSTS); + if ((reg & PCS_LSTS_AN_COMP) == 0) { + printf("XXX LINKOK but not ACOMP\n"); + goto setled; + } + pcs_adv = CSR_READ(sc, WMREG_PCS_ANADV); + pcs_lpab = CSR_READ(sc, WMREG_PCS_LPAB); + printf("XXX AN result(2) %08x, %08x\n", pcs_adv, pcs_lpab); + if ((pcs_adv & TXCW_SYM_PAUSE) + && (pcs_lpab & TXCW_SYM_PAUSE)) { + mii->mii_media_active |= IFM_FLOW + | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE; + } else if (((pcs_adv & TXCW_SYM_PAUSE) == 0) + && (pcs_adv & TXCW_ASYM_PAUSE) + && (pcs_lpab & TXCW_SYM_PAUSE) + && (pcs_lpab & TXCW_ASYM_PAUSE)) { + mii->mii_media_active |= IFM_FLOW + | IFM_ETH_TXPAUSE; + } else if ((pcs_adv & TXCW_SYM_PAUSE) + && (pcs_adv & TXCW_ASYM_PAUSE) + && ((pcs_lpab & TXCW_SYM_PAUSE) == 0) + && (pcs_lpab & TXCW_ASYM_PAUSE)) { + mii->mii_media_active |= IFM_FLOW + | IFM_ETH_RXPAUSE; + } else { + } + } + ifmr->ifm_active = (ifmr->ifm_active & ~IFM_ETH_FMASK) + | (mii->mii_media_active & IFM_ETH_FMASK); +setled: + wm_tbi_serdes_set_linkled(sc); +} + +/* + * wm_serdes_tick: + * + * Check the link on serdes devices. + */ +static void +wm_serdes_tick(struct wm_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct mii_data *mii = &sc->sc_mii; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + uint32_t reg; + + KASSERT(WM_TX_LOCKED(sc)); + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + /* Check PCS */ + reg = CSR_READ(sc, WMREG_PCS_LSTS); + if ((reg & PCS_LSTS_LINKOK) != 0) { + mii->mii_media_status |= IFM_ACTIVE; + sc->sc_tbi_linkup = 1; + sc->sc_tbi_serdes_ticks = 0; + mii->mii_media_active |= IFM_1000_SX; /* XXX */ + if ((reg & PCS_LSTS_FDX) != 0) + mii->mii_media_active |= IFM_FDX; + else + mii->mii_media_active |= IFM_HDX; + } else { + mii->mii_media_status |= IFM_NONE; + sc->sc_tbi_linkup = 0; + /* If the timer expired, retry autonegotiation */ + if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) + && (++sc->sc_tbi_serdes_ticks + >= sc->sc_tbi_serdes_anegticks)) { + DPRINTF(WM_DEBUG_LINK, ("EXPIRE\n")); + sc->sc_tbi_serdes_ticks = 0; + /* XXX */ + wm_serdes_mediachange(ifp); + } + } + + wm_tbi_serdes_set_linkled(sc); } /* SFP related */ @@ -9814,3 +10122,29 @@ wm_reset_init_script_82575(struct wm_sof wm_82575_write_8bit_ctlr_reg(sc, WMREG_SCCTL, 0x14, 0x00); wm_82575_write_8bit_ctlr_reg(sc, WMREG_SCCTL, 0x10, 0x00); } + +static void +wm_reset_mdicnfg_82580(struct wm_softc *sc) +{ + uint32_t reg; + uint16_t nvmword; + int rv; + + if ((sc->sc_flags & WM_F_SGMII) == 0) + return; + + rv = wm_nvm_read(sc, NVM_OFF_LAN_FUNC_82580(sc->sc_funcid) + + NVM_OFF_CFG3_PORTA, 1, &nvmword); + if (rv != 0) { + aprint_error_dev(sc->sc_dev, "%s: failed to read NVM\n", + __func__); + return; + } + + reg = CSR_READ(sc, WMREG_MDICNFG); + if (nvmword & NVM_CFG3_PORTA_EXT_MDIO) + reg |= MDICNFG_DEST; + if (nvmword & NVM_CFG3_PORTA_COM_MDIO) + reg |= MDICNFG_COM_MDIO; + CSR_WRITE(sc, WMREG_MDICNFG, reg); +} Index: src/sys/dev/pci/if_wmreg.h diff -u src/sys/dev/pci/if_wmreg.h:1.72 src/sys/dev/pci/if_wmreg.h:1.73 --- src/sys/dev/pci/if_wmreg.h:1.72 Fri May 22 03:15:43 2015 +++ src/sys/dev/pci/if_wmreg.h Tue Jun 2 14:19:26 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wmreg.h,v 1.72 2015/05/22 03:15:43 msaitoh Exp $ */ +/* $NetBSD: if_wmreg.h,v 1.73 2015/06/02 14:19:26 msaitoh Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. @@ -798,6 +798,28 @@ struct livengood_tcpip_ctxdesc { #define WMREG_TLPIC 0x4148 /* EEE Tx LPI Count */ #define WMREG_RLPIC 0x414c /* EEE Rx LPI Count */ +#define WMREG_PCS_CFG 0x4200 /* PCS Configuration */ +#define PCS_CFG_PCS_EN __BIT(3) + +#define WMREG_PCS_LCTL 0x4208 /* PCS Link Control */ +#define PCS_LCTL_FSV_1000 __BIT(2) /* AN Timeout Enable */ +#define PCS_LCTL_FDV_FULL __BIT(3) /* AN Timeout Enable */ +#define PCS_LCTL_FSD __BIT(4) /* AN Timeout Enable */ +#define PCS_LCTL_FORCE_FC __BIT(7) /* AN Timeout Enable */ +#define PCS_LCTL_AN_ENABLE __BIT(16) /* AN Timeout Enable */ +#define PCS_LCTL_AN_RESTART __BIT(17) /* AN Timeout Enable */ +#define PCS_LCTL_AN_TIMEOUT __BIT(18) /* AN Timeout Enable */ + +#define WMREG_PCS_LSTS 0x420c /* PCS Link Status */ +#define PCS_LSTS_LINKOK __BIT(0) +#define PCS_LSTS_SPEED_100 __BIT(1) +#define PCS_LSTS_SPEED_1000 __BIT(2) +#define PCS_LSTS_FDX __BIT(3) +#define PCS_LSTS_AN_COMP __BIT(16) + +#define WMREG_PCS_ANADV 0x4218 /* AN Advertsement */ +#define WMREG_PCS_LPAB 0x421c /* Link Partnet Ability */ + #define WMREG_RXCSUM 0x5000 /* Receive Checksum register */ #define RXCSUM_PCSS 0x000000ff /* Packet Checksum Start */ #define RXCSUM_IPOFL (1U << 8) /* IP checksum offload */ @@ -981,6 +1003,8 @@ struct livengood_tcpip_ctxdesc { #define NVM_CFG2_MNGM_NCSI 1 #define NVM_CFG2_MNGM_PT 2 +#define NVM_COMPAT_SERDES_FORCE_MODE __BIT(14) /* Don't use autonego */ + #define NVM_FUTURE_INIT_WORD1_VALID_CHECKSUM 0x0040 #define NVM_K1_CONFIG_ENABLE 0x01 @@ -992,6 +1016,8 @@ struct livengood_tcpip_ctxdesc { #define NVM_3GIO_3_ASPM_MASK (0x3 << 2) /* Active State PM Support */ #define NVM_CFG3_APME (1U << 10) +#define NVM_CFG3_PORTA_EXT_MDIO (1U << 2) /* External MDIO Interface */ +#define NVM_CFG3_PORTA_COM_MDIO (1U << 3) /* MDIO Interface is shared */ #define NVM_OFF_MACADDR_82571(x) (3 * (x)) Index: src/sys/dev/pci/if_wmvar.h diff -u src/sys/dev/pci/if_wmvar.h:1.24 src/sys/dev/pci/if_wmvar.h:1.25 --- src/sys/dev/pci/if_wmvar.h:1.24 Sat May 16 22:41:59 2015 +++ src/sys/dev/pci/if_wmvar.h Tue Jun 2 14:19:26 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wmvar.h,v 1.24 2015/05/16 22:41:59 msaitoh Exp $ */ +/* $NetBSD: if_wmvar.h,v 1.25 2015/06/02 14:19:26 msaitoh Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -97,6 +97,7 @@ #define WM_F_EEE 0x00400000 /* Energy Efficiency Ethernet */ #define WM_F_ATTACHED 0x00800000 /* attach() finished successfully */ #define WM_F_EEPROM_INVM 0x01000000 /* NVM is iNVM */ +#define WM_F_PCS_DIS_AUTONEGO 0x02000000 /* PCS Disable Autonego */ /*