Index: dev/bge/if_bge.c =================================================================== --- dev/bge/if_bge.c (revision 215249) +++ dev/bge/if_bge.c (working copy) @@ -914,11 +914,13 @@ bge_miibus_statchg(device_t dev) if (IFM_OPTIONS(mii->mii_media_active & IFM_FDX) != 0) { BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX); - if (IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG1) + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_TXPAUSE) != 0) BGE_SETBIT(sc, BGE_TX_MODE, BGE_TXMODE_FLOWCTL_ENABLE); else BGE_CLRBIT(sc, BGE_TX_MODE, BGE_TXMODE_FLOWCTL_ENABLE); - if (IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG0) + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_RXPAUSE) != 0) BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_FLOWCTL_ENABLE); else BGE_CLRBIT(sc, BGE_RX_MODE, BGE_RXMODE_FLOWCTL_ENABLE); @@ -3102,9 +3104,9 @@ bge_attach(device_t dev) again: bge_asf_driver_up(sc); - error = (mii_attach(dev, &sc->bge_miibus, ifp, + error = mii_attach(dev, &sc->bge_miibus, ifp, bge_ifmedia_upd, bge_ifmedia_sts, BMSR_DEFCAPMASK, - phy_addr, MII_OFFSET_ANY, 0)); + phy_addr, MII_OFFSET_ANY, MIIF_DOPAUSE); if (error != 0) { if (trys++ < 4) { device_printf(sc->bge_dev, "Try again\n"); Index: dev/bce/if_bce.c =================================================================== --- dev/bce/if_bce.c (revision 215249) +++ dev/bce/if_bce.c (working copy) @@ -1143,7 +1143,7 @@ bce_attach(device_t dev) /* MII child bus by attaching the PHY. */ rc = mii_attach(dev, &sc->bce_miibus, ifp, bce_ifmedia_upd, bce_ifmedia_sts, BMSR_DEFCAPMASK, sc->bce_phy_addr, - MII_OFFSET_ANY, 0); + MII_OFFSET_ANY, MIIF_DOPAUSE); if (rc != 0) { BCE_PRINTF("%s(%d): attaching PHYs failed\n", __FILE__, __LINE__); @@ -1769,8 +1769,7 @@ bce_miibus_statchg(device_t dev) REG_WR(sc, BCE_EMAC_MODE, val); - /* FLAG0 is set if RX is enabled and FLAG1 if TX is enabled */ - if (mii->mii_media_active & IFM_FLAG0) { + if ((mii->mii_media_active & IFM_ETH_RXPAUSE) != 0) { DBPRINT(sc, BCE_INFO_PHY, "%s(): Enabling RX flow control.\n", __FUNCTION__); BCE_SETBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN); @@ -1780,7 +1779,7 @@ bce_miibus_statchg(device_t dev) BCE_CLRBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN); } - if (mii->mii_media_active & IFM_FLAG1) { + if ((mii->mii_media_active & IFM_ETH_TXPAUSE) != 0) { DBPRINT(sc, BCE_INFO_PHY, "%s(): Enabling TX flow control.\n", __FUNCTION__); BCE_SETBIT(sc, BCE_EMAC_TX_MODE, BCE_EMAC_TX_MODE_FLOW_EN); Index: dev/msk/if_msk.c =================================================================== --- dev/msk/if_msk.c (revision 215249) +++ dev/msk/if_msk.c (working copy) @@ -529,11 +529,11 @@ msk_miibus_statchg(device_t dev) break; } - /* Disable Rx flow control. */ - if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG0) == 0) + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_RXPAUSE) == 0) gmac |= GM_GPCR_FC_RX_DIS; - /* Disable Tx flow control. */ - if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG1) == 0) + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_TXPAUSE) == 0) gmac |= GM_GPCR_FC_TX_DIS; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) gmac |= GM_GPCR_DUP_FULL; @@ -545,7 +545,8 @@ msk_miibus_statchg(device_t dev) GMAC_READ_2(sc, sc_if->msk_port, GM_GP_CTRL); gmac = GMC_PAUSE_OFF; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { - if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG0) != 0) + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_RXPAUSE) != 0) gmac = GMC_PAUSE_ON; } CSR_WRITE_4(sc, MR_ADDR(sc_if->msk_port, GMAC_CTRL), gmac); @@ -1886,6 +1887,7 @@ mskc_attach(device_t dev) } mmd->port = MSK_PORT_A; mmd->pmd = sc->msk_pmd; + mmd->mii_flags |= MIIF_DOPAUSE; if (sc->msk_pmd == 'L' || sc->msk_pmd == 'S') mmd->mii_flags |= MIIF_HAVEFIBER; if (sc->msk_pmd == 'P') Index: dev/nfe/if_nfe.c =================================================================== --- dev/nfe/if_nfe.c (revision 215249) +++ dev/nfe/if_nfe.c (working copy) @@ -605,7 +605,8 @@ nfe_attach(device_t dev) /* Do MII setup */ error = mii_attach(dev, &sc->nfe_miibus, ifp, nfe_ifmedia_upd, - nfe_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + nfe_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, + MIIF_DOPAUSE); if (error != 0) { device_printf(dev, "attaching PHYs failed\n"); goto fail; @@ -906,7 +907,8 @@ nfe_mac_config(struct nfe_softc *sc, struct mii_da if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { /* It seems all hardwares supports Rx pause frames. */ val = NFE_READ(sc, NFE_RXFILTER); - if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG0) != 0) + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_RXPAUSE) != 0) val |= NFE_PFF_RX_PAUSE; else val &= ~NFE_PFF_RX_PAUSE; @@ -914,7 +916,7 @@ nfe_mac_config(struct nfe_softc *sc, struct mii_da if ((sc->nfe_flags & NFE_TX_FLOW_CTRL) != 0) { val = NFE_READ(sc, NFE_MISC1); if ((IFM_OPTIONS(mii->mii_media_active) & - IFM_FLAG1) != 0) { + IFM_ETH_TXPAUSE) != 0) { NFE_WRITE(sc, NFE_TX_PAUSE_FRAME, NFE_TX_PAUSE_FRAME_ENABLE); val |= NFE_MISC1_TX_PAUSE; Index: dev/stge/if_stge.c =================================================================== --- dev/stge/if_stge.c (revision 215249) +++ dev/stge/if_stge.c (working copy) @@ -738,7 +738,7 @@ stge_attach(device_t dev) (PC_PhyDuplexPolarity | PC_PhyLnkPolarity); /* Set up MII bus. */ - flags = 0; + flags = MIIF_DOPAUSE; if (sc->sc_rev >= 0x40 && sc->sc_rev <= 0x4e) flags |= MIIF_MACPRIV0; error = mii_attach(sc->sc_dev, &sc->sc_miibus, ifp, stge_mediachange, @@ -1524,9 +1524,9 @@ stge_link_task(void *arg, int pending) sc->sc_MACCtrl = 0; if (((mii->mii_media_active & IFM_GMASK) & IFM_FDX) != 0) sc->sc_MACCtrl |= MC_DuplexSelect; - if (((mii->mii_media_active & IFM_GMASK) & IFM_FLAG0) != 0) + if (((mii->mii_media_active & IFM_GMASK) & IFM_ETH_RXPAUSE) != 0) sc->sc_MACCtrl |= MC_RxFlowControlEnable; - if (((mii->mii_media_active & IFM_GMASK) & IFM_FLAG1) != 0) + if (((mii->mii_media_active & IFM_GMASK) & IFM_ETH_TXPAUSE) != 0) sc->sc_MACCtrl |= MC_TxFlowControlEnable; /* * Update STGE_MACCtrl register depending on link status. Index: dev/mii/brgphy.c =================================================================== --- dev/mii/brgphy.c (revision 215249) +++ dev/mii/brgphy.c (working copy) @@ -99,9 +99,9 @@ static driver_t brgphy_driver = { DRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0); static int brgphy_service(struct mii_softc *, struct mii_data *, int); -static void brgphy_setmedia(struct mii_softc *, int, int); +static void brgphy_setmedia(struct mii_softc *, int); static void brgphy_status(struct mii_softc *); -static void brgphy_mii_phy_auto(struct mii_softc *); +static void brgphy_mii_phy_auto(struct mii_softc *, int); static void brgphy_reset(struct mii_softc *); static void brgphy_enable_loopback(struct mii_softc *); static void bcm5401_load_dspcode(struct mii_softc *); @@ -169,6 +169,7 @@ detect_hs21(struct bce_softc *bce_sc) static int brgphy_probe(device_t dev) { + return (mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT)); } @@ -183,7 +184,6 @@ brgphy_attach(device_t dev) struct mii_attach_args *ma; struct mii_data *mii; struct ifnet *ifp; - int fast_ether; bsc = device_get_softc(dev); sc = &bsc->mii_sc; @@ -203,8 +203,7 @@ brgphy_attach(device_t dev) * At least some variants wedge when isolating, at least some also * don't support loopback. */ - sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; - sc->mii_anegticks = MII_ANEGTICKS_GIGE; + sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP | MIIF_NOMANPAUSE; /* Initialize brgphy_softc structure */ bsc->mii_oui = MII_OUI(ma->mii_id1, ma->mii_id2); @@ -212,8 +211,6 @@ brgphy_attach(device_t dev) bsc->mii_rev = MII_REV(ma->mii_id2); bsc->serdes_flags = 0; - fast_ether = 0; - if (bootverbose) device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", bsc->mii_oui, bsc->mii_model, bsc->mii_rev); @@ -279,8 +276,7 @@ brgphy_attach(device_t dev) pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5901A2 || pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5906 || pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5906M)) { - fast_ether = 1; - sc->mii_anegticks = MII_ANEGTICKS; + ma->mii_capmask &= ~BMSR_EXTSTAT; } brgphy_reset(sc); @@ -295,27 +291,10 @@ brgphy_attach(device_t dev) /* Add the supported media types */ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), - BRGPHY_S10); - printf("10baseT, "); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), - BRGPHY_S10 | BRGPHY_BMCR_FDX); - printf("10baseT-FDX, "); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), - BRGPHY_S100); - printf("100baseTX, "); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), - BRGPHY_S100 | BRGPHY_BMCR_FDX); - printf("100baseTX-FDX, "); - if (fast_ether == 0) { - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst), - BRGPHY_S1000); - printf("1000baseT, "); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst), - BRGPHY_S1000 | BRGPHY_BMCR_FDX); - printf("1000baseT-FDX, "); - } + mii_phy_add_media(sc); + printf("\n"); } else { + sc->mii_anegticks = MII_ANEGTICKS_GIGE; ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), BRGPHY_S1000 | BRGPHY_BMCR_FDX); printf("1000baseSX-FDX, "); @@ -337,11 +316,10 @@ brgphy_attach(device_t dev) printf("auto-neg workaround, "); bsc->serdes_flags |= BRGPHY_NOANWAIT; } + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); + printf("auto\n"); } - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); - printf("auto\n"); - #undef ADD MIIBUS_MEDIAINIT(sc->mii_dev); return (0); @@ -367,15 +345,14 @@ brgphy_service(struct mii_softc *sc, struct mii_da switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: - brgphy_mii_phy_auto(sc); + brgphy_mii_phy_auto(sc, ife->ifm_media); break; case IFM_2500_SX: case IFM_1000_SX: case IFM_1000_T: case IFM_100_TX: case IFM_10_T: - brgphy_setmedia(sc, ife->ifm_media, - mii->mii_ifp->if_flags & IFF_LINK0); + brgphy_setmedia(sc, ife->ifm_media); break; default: return (EINVAL); @@ -397,7 +374,7 @@ brgphy_service(struct mii_softc *sc, struct mii_da * Check to see if we have link. If we do, we don't * need to restart the autonegotiation process. */ - val = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + val = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); if (val & BMSR_LINK) { sc->mii_ticks = 0; /* Reset autoneg timer. */ break; @@ -414,7 +391,7 @@ brgphy_service(struct mii_softc *sc, struct mii_da /* Retry autonegotiation */ sc->mii_ticks = 0; - brgphy_mii_phy_auto(sc); + brgphy_mii_phy_auto(sc, ife->ifm_media); break; } @@ -456,7 +433,6 @@ brgphy_service(struct mii_softc *sc, struct mii_da return (0); } - /****************************************************************************/ /* Sets the PHY link speed. */ /* */ @@ -464,12 +440,10 @@ brgphy_service(struct mii_softc *sc, struct mii_da /* None */ /****************************************************************************/ static void -brgphy_setmedia(struct mii_softc *sc, int media, int master) +brgphy_setmedia(struct mii_softc *sc, int media) { - struct brgphy_softc *bsc = (struct brgphy_softc *)sc; int bmcr = 0, gig; - /* Calculate the value for the BMCR register. */ switch (IFM_SUBTYPE(media)) { case IFM_2500_SX: break; @@ -486,7 +460,6 @@ static void break; } - /* Calculate duplex settings for 1000BasetT/1000BaseX. */ if ((media & IFM_GMASK) == IFM_FDX) { bmcr |= BRGPHY_BMCR_FDX; gig = BRGPHY_1000CTL_AFD; @@ -494,53 +467,30 @@ static void gig = BRGPHY_1000CTL_AHD; } - /* Force loopback to disconnect PHY for Ethernet medium. */ + /* Force loopback to disconnect PHY from Ethernet medium. */ brgphy_enable_loopback(sc); - /* Disable 1000BaseT advertisements. */ PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0); - /* Disable 10/100 advertisements. */ PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE); - /* Write forced link speed. */ - PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr); - /* If 10/100 only then configuration is complete. */ - if ((IFM_SUBTYPE(media) != IFM_1000_T) && (IFM_SUBTYPE(media) != IFM_1000_SX)) - goto brgphy_setmedia_exit; + if (IFM_SUBTYPE(media) != IFM_1000_T && + IFM_SUBTYPE(media) != IFM_1000_SX) { + PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr); + return; + } - /* Set duplex speed advertisement for 1000BaseT/1000BaseX. */ + if (IFM_SUBTYPE(media) == IFM_1000_T) { + gig |= BRGPHY_1000CTL_MSE; + if ((media & IFM_ETH_MASTER) != 0) + gig |= BRGPHY_1000CTL_MSC; + } PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig); - /* Restart auto-negotiation for 1000BaseT/1000BaseX. */ PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG); - - /* If not 5701 PHY then configuration is complete. */ - if (bsc->mii_model != MII_MODEL_xxBROADCOM_BCM5701) - goto brgphy_setmedia_exit; - - /* - * When setting the link manually, one side must be the master and - * the other the slave. However ifmedia doesn't give us a good way - * to specify this, so we fake it by using one of the LINK flags. - * If LINK0 is set, we program the PHY to be a master, otherwise - * it's a slave. - */ - if (master) { - PHY_WRITE(sc, BRGPHY_MII_1000CTL, - gig | BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC); - } else { - PHY_WRITE(sc, BRGPHY_MII_1000CTL, - gig | BRGPHY_1000CTL_MSE); - } - -brgphy_setmedia_exit: - return; } /****************************************************************************/ /* Set the media status based on the PHY settings. */ -/* IFM_FLAG0 = 0 (RX flow control disabled) | 1 (enabled) */ -/* IFM_FLAG1 = 0 (TX flow control disabled) | 1 (enabled) */ /* */ /* Returns: */ /* None */ @@ -550,34 +500,34 @@ brgphy_status(struct mii_softc *sc) { struct brgphy_softc *bsc = (struct brgphy_softc *)sc; struct mii_data *mii = sc->mii_pdata; - int aux, bmcr, bmsr, anar, anlpar, xstat, val; + int aux, bmcr, bmsr, val, xstat; + u_int flowstat; - mii->mii_media_status = IFM_AVALID; mii->mii_media_active = IFM_ETHER; bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR); bmcr = PHY_READ(sc, BRGPHY_MII_BMCR); - anar = PHY_READ(sc, BRGPHY_MII_ANAR); - anlpar = PHY_READ(sc, BRGPHY_MII_ANLPAR); - /* Loopback is enabled. */ if (bmcr & BRGPHY_BMCR_LOOP) { - mii->mii_media_active |= IFM_LOOP; } - /* Autoneg is still in progress. */ if ((bmcr & BRGPHY_BMCR_AUTOEN) && (bmsr & BRGPHY_BMSR_ACOMP) == 0 && (bsc->serdes_flags & BRGPHY_NOANWAIT) == 0) { /* Erg, still trying, I guess... */ mii->mii_media_active |= IFM_NONE; - goto brgphy_status_exit; + return; } - /* Autoneg is enabled and complete, link should be up. */ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { + /* + * NB: reading the ANAR, ANLPAR or 1000STS after the AUXSTS + * wedges at least the PHY of BCM5704 (but not others). + */ + flowstat = mii_phy_flowstatus(sc); + xstat = PHY_READ(sc, BRGPHY_MII_1000STS); aux = PHY_READ(sc, BRGPHY_MII_AUXSTS); /* If copper link is up, get the negotiated speed/duplex. */ @@ -601,8 +551,16 @@ brgphy_status(struct mii_softc *sc) default: mii->mii_media_active |= IFM_NONE; break; } + + if ((mii->mii_media_active & IFM_FDX) != 0) + mii->mii_media_active |= flowstat; + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T && + (xstat & BRGPHY_1000STS_MSR) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; } } else { + /* Todo: Add support for flow control. */ /* If serdes link is up, get the negotiated speed/duplex. */ if (bmsr & BRGPHY_BMSR_LINK) { mii->mii_media_status |= IFM_ACTIVE; @@ -620,7 +578,6 @@ brgphy_status(struct mii_softc *sc) else mii->mii_media_active |= IFM_HDX; } - } else if (bsc->serdes_flags & BRGPHY_5708S) { PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0); xstat = PHY_READ(sc, BRGPHY_5708S_PG0_1000X_STAT1); @@ -642,9 +599,7 @@ brgphy_status(struct mii_softc *sc) mii->mii_media_active |= IFM_FDX; else mii->mii_media_active |= IFM_HDX; - } else if (bsc->serdes_flags & BRGPHY_5709S) { - /* Select GP Status Block of the AN MMD, get autoneg results. */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_GP_STATUS); xstat = PHY_READ(sc, BRGPHY_GP_STATUS_TOP_ANEG_STATUS); @@ -670,65 +625,42 @@ brgphy_status(struct mii_softc *sc) else mii->mii_media_active |= IFM_HDX; } - } - - /* Todo: Change bge to use these settings. */ - - /* Fetch flow control settings from the copper PHY. */ - if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { - /* Set FLAG0 if RX is enabled and FLAG1 if TX is enabled */ - if ((anar & BRGPHY_ANAR_PC) && (anlpar & BRGPHY_ANLPAR_PC)) { - mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1; - } else if (!(anar & BRGPHY_ANAR_PC) && (anlpar & BRGPHY_ANAR_ASP) && - (anlpar & BRGPHY_ANLPAR_PC) && (anlpar & BRGPHY_ANLPAR_ASP)) { - mii->mii_media_active |= IFM_FLAG1; - } else if ((anar & BRGPHY_ANAR_PC) && (anar & BRGPHY_ANAR_ASP) && - !(anlpar & BRGPHY_ANLPAR_PC) && (anlpar & BRGPHY_ANLPAR_ASP)) { - mii->mii_media_active |= IFM_FLAG0; - } - } - - /* Todo: Add support for fiber settings too. */ - - -brgphy_status_exit: - return; } static void -brgphy_mii_phy_auto(struct mii_softc *sc) +brgphy_mii_phy_auto(struct mii_softc *sc, int media) { struct brgphy_softc *bsc = (struct brgphy_softc *)sc; - int ktcr = 0; + int anar, ktcr = 0; brgphy_reset(sc); - /* Enable flow control in the advertisement register. */ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { - /* Pause capability advertisement (pause capable & asymmetric) */ - PHY_WRITE(sc, BRGPHY_MII_ANAR, - BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA | - BRGPHY_ANAR_ASP | BRGPHY_ANAR_PC); + anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; + if ((media & IFM_FLOW) != 0 || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) + anar |= BRGPHY_ANAR_PC | BRGPHY_ANAR_ASP; + PHY_WRITE(sc, BRGPHY_MII_ANAR, anar); } else { - PHY_WRITE(sc, BRGPHY_SERDES_ANAR, BRGPHY_SERDES_ANAR_FDX | - BRGPHY_SERDES_ANAR_HDX | BRGPHY_SERDES_ANAR_BOTH_PAUSE); + anar = BRGPHY_SERDES_ANAR_FDX | BRGPHY_SERDES_ANAR_HDX; + if ((media & IFM_FLOW) != 0 || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) + anar |= BRGPHY_SERDES_ANAR_BOTH_PAUSE; + PHY_WRITE(sc, BRGPHY_SERDES_ANAR, anar); } - /* Enable speed in the 1000baseT control register */ ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD; if (bsc->mii_model == MII_MODEL_xxBROADCOM_BCM5701) ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC; PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr); ktcr = PHY_READ(sc, BRGPHY_MII_1000CTL); - /* Start autonegotiation */ - PHY_WRITE(sc, BRGPHY_MII_BMCR,BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG); + PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_AUTOEN | + BRGPHY_BMCR_STARTNEG); PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00); - } - /* Enable loopback to force the link down. */ static void brgphy_enable_loopback(struct mii_softc *sc) @@ -923,7 +855,6 @@ brgphy_fixup_jitter_bug(struct mii_softc *sc) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } - static void brgphy_fixup_disable_early_dac(struct mii_softc *sc) { @@ -936,7 +867,6 @@ brgphy_fixup_disable_early_dac(struct mii_softc *s } - static void brgphy_ethernet_wirespeed(struct mii_softc *sc) { @@ -948,7 +878,6 @@ brgphy_ethernet_wirespeed(struct mii_softc *sc) PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4)); } - static void brgphy_jumbo_settings(struct mii_softc *sc, u_long mtu) { @@ -989,7 +918,7 @@ brgphy_reset(struct mii_softc *sc) struct bge_softc *bge_sc = NULL; struct bce_softc *bce_sc = NULL; struct ifnet *ifp; - int val; + int val; /* Perform a standard PHY reset. */ mii_phy_reset(sc); @@ -1029,7 +958,6 @@ brgphy_reset(struct mii_softc *sc) bce_sc = ifp->if_softc; } - /* Handle any bge (NetXtreme/NetLink) workarounds. */ if (bge_sc) { /* Fix up various bugs */ if (bge_sc->bge_phy_flags & BGE_PHY_5704_A0_BUG) @@ -1060,10 +988,7 @@ brgphy_reset(struct mii_softc *sc) /* Adjust output voltage (From Linux driver) */ if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906) PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12); - - /* Handle any bce (NetXtreme II) workarounds. */ } else if (bce_sc) { - if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5708 && (bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) { @@ -1154,7 +1079,6 @@ brgphy_reset(struct mii_softc *sc) /* Restore IEEE0 block (assumed in all brgphy(4) code). */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0); - } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709) { if ((BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Ax) || (BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Bx)) @@ -1167,6 +1091,5 @@ brgphy_reset(struct mii_softc *sc) brgphy_jumbo_settings(sc, ifp->if_mtu); brgphy_ethernet_wirespeed(sc); } - } } Index: dev/mii/ciphy.c =================================================================== --- dev/mii/ciphy.c (revision 215249) +++ dev/mii/ciphy.c (working copy) @@ -165,7 +165,7 @@ ciphy_service(struct mii_softc *sc, struct mii_dat if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN) return (0); #endif - (void) mii_phy_auto(sc); + (void)mii_phy_auto(sc); break; case IFM_1000_T: speed = CIPHY_S1000; @@ -190,30 +190,16 @@ setit: if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) break; + gig |= CIPHY_1000CTL_MSE; + if ((ife->ifm_media & IFM_ETH_MASTER) != 0) + gig |= CIPHY_1000CTL_MSC; PHY_WRITE(sc, CIPHY_MII_1000CTL, gig); PHY_WRITE(sc, CIPHY_MII_BMCR, - speed|CIPHY_BMCR_AUTOEN|CIPHY_BMCR_STARTNEG); - - /* - * When setting the link manually, one side must - * be the master and the other the slave. However - * ifmedia doesn't give us a good way to specify - * this, so we fake it by using one of the LINK - * flags. If LINK0 is set, we program the PHY to - * be a master, otherwise it's a slave. - */ - if ((mii->mii_ifp->if_flags & IFF_LINK0)) { - PHY_WRITE(sc, CIPHY_MII_1000CTL, - gig|CIPHY_1000CTL_MSE|CIPHY_1000CTL_MSC); - } else { - PHY_WRITE(sc, CIPHY_MII_1000CTL, - gig|CIPHY_1000CTL_MSE); - } + speed | CIPHY_BMCR_AUTOEN | CIPHY_BMCR_STARTNEG); break; case IFM_NONE: - PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN); + PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN); break; - case IFM_100_T4: default: return (EINVAL); } @@ -319,6 +305,10 @@ ciphy_status(struct mii_softc *sc) mii->mii_media_active |= IFM_FDX; else mii->mii_media_active |= IFM_HDX; + + if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && + (PHY_READ(sc, CIPHY_MII_1000STS) & CIPHY_1000STS_MSR) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; } static void Index: dev/mii/e1000phy.c =================================================================== --- dev/mii/e1000phy.c (revision 215249) +++ dev/mii/e1000phy.c (working copy) @@ -91,7 +91,7 @@ DRIVER_MODULE(e1000phy, miibus, e1000phy_driver, e static int e1000phy_service(struct mii_softc *, struct mii_data *, int); static void e1000phy_status(struct mii_softc *); static void e1000phy_reset(struct mii_softc *); -static int e1000phy_mii_phy_auto(struct e1000phy_softc *); +static int e1000phy_mii_phy_auto(struct e1000phy_softc *, int); static const struct mii_phydesc e1000phys[] = { MII_PHY_DESC(MARVELL, E1000), @@ -146,6 +146,8 @@ e1000phy_attach(device_t dev) sc->mii_service = e1000phy_service; sc->mii_pdata = mii; + sc->mii_flags |= MIIF_NOMANPAUSE; + esc->mii_model = MII_MODEL(ma->mii_id2); ifp = sc->mii_pdata->mii_ifp; if (strcmp(ifp->if_dname, "msk") == 0 && @@ -323,7 +325,7 @@ e1000phy_service(struct mii_softc *sc, struct mii_ break; if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { - e1000phy_mii_phy_auto(esc); + e1000phy_mii_phy_auto(esc, ife->ifm_media); break; } @@ -366,27 +368,14 @@ e1000phy_service(struct mii_softc *sc, struct mii_ reg &= ~E1000_CR_AUTO_NEG_ENABLE; PHY_WRITE(sc, E1000_CR, reg | E1000_CR_RESET); - /* - * When setting the link manually, one side must - * be the master and the other the slave. However - * ifmedia doesn't give us a good way to specify - * this, so we fake it by using one of the LINK - * flags. If LINK0 is set, we program the PHY to - * be a master, otherwise it's a slave. - */ - if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T || - (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_SX)) { - if ((mii->mii_ifp->if_flags & IFF_LINK0)) - PHY_WRITE(sc, E1000_1GCR, gig | - E1000_1GCR_MS_ENABLE | E1000_1GCR_MS_VALUE); - else - PHY_WRITE(sc, E1000_1GCR, gig | - E1000_1GCR_MS_ENABLE); - } else { - if ((sc->mii_extcapabilities & - (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0) - PHY_WRITE(sc, E1000_1GCR, 0); - } + if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { + gig |= E1000_1GCR_MS_ENABLE; + if ((ife->ifm_media & IFM_ETH_MASTER) != 0) + gig |= E1000_1GCR_MS_VALUE; + PHY_WRITE(sc, E1000_1GCR, gig); + } else if ((sc->mii_extcapabilities & + (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0) + PHY_WRITE(sc, E1000_1GCR, 0); PHY_WRITE(sc, E1000_AR, E1000_AR_SELECTOR_FIELD); PHY_WRITE(sc, E1000_CR, speed | E1000_CR_RESET); done: @@ -424,7 +413,7 @@ done: sc->mii_ticks = 0; e1000phy_reset(sc); - e1000phy_mii_phy_auto(esc); + e1000phy_mii_phy_auto(esc, ife->ifm_media); break; } @@ -440,7 +429,7 @@ static void e1000phy_status(struct mii_softc *sc) { struct mii_data *mii = sc->mii_pdata; - int bmcr, bmsr, gsr, ssr, ar, lpar; + int bmcr, bmsr, ssr; mii->mii_media_status = IFM_AVALID; mii->mii_media_active = IFM_ETHER; @@ -485,38 +474,22 @@ e1000phy_status(struct mii_softc *sc) mii->mii_media_active |= IFM_1000_SX; } - if (ssr & E1000_SSR_DUPLEX) + if (ssr & E1000_SSR_DUPLEX) { mii->mii_media_active |= IFM_FDX; - else + if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) + mii->mii_media_active |= mii_phy_flowstatus(sc); + } else mii->mii_media_active |= IFM_HDX; - if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { - ar = PHY_READ(sc, E1000_AR); - lpar = PHY_READ(sc, E1000_LPAR); - /* FLAG0==rx-flow-control FLAG1==tx-flow-control */ - if ((ar & E1000_AR_PAUSE) && (lpar & E1000_LPAR_PAUSE)) { - mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1; - } else if (!(ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) && - (lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) { - mii->mii_media_active |= IFM_FLAG1; - } else if ((ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) && - !(lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) { - mii->mii_media_active |= IFM_FLAG0; - } + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { + if (((PHY_READ(sc, E1000_1GSR) | PHY_READ(sc, E1000_1GSR)) & + E1000_1GSR_MS_CONFIG_RES) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; } - - /* FLAG2 : local PHY resolved to MASTER */ - if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) || - (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX)) { - PHY_READ(sc, E1000_1GSR); - gsr = PHY_READ(sc, E1000_1GSR); - if ((gsr & E1000_1GSR_MS_CONFIG_RES) != 0) - mii->mii_media_active |= IFM_FLAG2; - } } static int -e1000phy_mii_phy_auto(struct e1000phy_softc *esc) +e1000phy_mii_phy_auto(struct e1000phy_softc *esc, int media) { struct mii_softc *sc; uint16_t reg; @@ -525,12 +498,13 @@ static int if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { reg = PHY_READ(sc, E1000_AR); reg |= E1000_AR_10T | E1000_AR_10T_FD | - E1000_AR_100TX | E1000_AR_100TX_FD | - E1000_AR_PAUSE | E1000_AR_ASM_DIR; + E1000_AR_100TX | E1000_AR_100TX_FD; + if ((media & IFM_FLOW) != 0 || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) + reg |= E1000_AR_PAUSE | E1000_AR_ASM_DIR; PHY_WRITE(sc, E1000_AR, reg | E1000_AR_SELECTOR_FIELD); } else - PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X | - E1000_FA_SYM_PAUSE | E1000_FA_ASYM_PAUSE); + PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X); if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0) PHY_WRITE(sc, E1000_1GCR, E1000_1GCR_1000T_FD | E1000_1GCR_1000T); Index: dev/mii/ip1000phy.c =================================================================== --- dev/mii/ip1000phy.c (revision 215249) +++ dev/mii/ip1000phy.c (working copy) @@ -84,7 +84,7 @@ DRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, static int ip1000phy_service(struct mii_softc *, struct mii_data *, int); static void ip1000phy_status(struct mii_softc *); static void ip1000phy_reset(struct mii_softc *); -static int ip1000phy_mii_phy_auto(struct mii_softc *); +static int ip1000phy_mii_phy_auto(struct mii_softc *, int); static const struct mii_phydesc ip1000phys[] = { MII_PHY_DESC(ICPLUS, IP1000A), @@ -120,7 +120,7 @@ ip1000phy_attach(device_t dev) sc->mii_service = ip1000phy_service; sc->mii_pdata = mii; - sc->mii_flags |= MIIF_NOISOLATE; + sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOMANPAUSE; isc->model = MII_MODEL(ma->mii_id2); isc->revision = MII_REV(ma->mii_id2); @@ -163,9 +163,8 @@ ip1000phy_service(struct mii_softc *sc, struct mii ip1000phy_reset(sc); switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: - (void)ip1000phy_mii_phy_auto(sc); + (void)ip1000phy_mii_phy_auto(sc, ife->ifm_media); goto done; - break; case IFM_1000_T: /* @@ -199,27 +198,11 @@ ip1000phy_service(struct mii_softc *sc, struct mii if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) break; + gig |= IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL; + if ((ife->ifm_media & IFM_ETH_MASTER) != 0) + gig |= IP1000PHY_1000CR_MMASTER; PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig); - PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed); - /* - * When setting the link manually, one side must - * be the master and the other the slave. However - * ifmedia doesn't give us a good way to specify - * this, so we fake it by using one of the LINK - * flags. If LINK0 is set, we program the PHY to - * be a master, otherwise it's a slave. - */ - if ((mii->mii_ifp->if_flags & IFF_LINK0)) - PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig | - IP1000PHY_1000CR_MASTER | - IP1000PHY_1000CR_MMASTER | - IP1000PHY_1000CR_MANUAL); - else - PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig | - IP1000PHY_1000CR_MASTER | - IP1000PHY_1000CR_MANUAL); - done: break; @@ -258,7 +241,7 @@ done: break; sc->mii_ticks = 0; - ip1000phy_mii_phy_auto(sc); + ip1000phy_mii_phy_auto(sc, ife->ifm_media); break; } @@ -276,7 +259,6 @@ ip1000phy_status(struct mii_softc *sc) struct ip1000phy_softc *isc; struct mii_data *mii = sc->mii_pdata; uint32_t bmsr, bmcr, stat; - uint32_t ar, lpar; isc = (struct ip1000phy_softc *)sc; @@ -345,36 +327,18 @@ ip1000phy_status(struct mii_softc *sc) mii->mii_media_active |= IFM_HDX; } - ar = PHY_READ(sc, IP1000PHY_MII_ANAR); - lpar = PHY_READ(sc, IP1000PHY_MII_ANLPAR); + if ((mii->mii_media_active & IFM_FDX) != 0) + mii->mii_media_active |= mii_phy_flowstatus(sc); - /* - * FLAG0 : Rx flow-control - * FLAG1 : Tx flow-control - */ - if ((ar & IP1000PHY_ANAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_PAUSE)) - mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1; - else if (!(ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) && - (lpar & IP1000PHY_ANLPAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_APAUSE)) - mii->mii_media_active |= IFM_FLAG1; - else if ((ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) && - !(lpar & IP1000PHY_ANLPAR_PAUSE) && - (lpar & IP1000PHY_ANLPAR_APAUSE)) { - mii->mii_media_active |= IFM_FLAG0; - } - - /* - * FLAG2 : local PHY resolved to MASTER - */ if ((mii->mii_media_active & IFM_1000_T) != 0) { stat = PHY_READ(sc, IP1000PHY_MII_1000SR); if ((stat & IP1000PHY_1000SR_MASTER) != 0) - mii->mii_media_active |= IFM_FLAG2; + mii->mii_media_active |= IFM_ETH_MASTER; } } static int -ip1000phy_mii_phy_auto(struct mii_softc *sc) +ip1000phy_mii_phy_auto(struct mii_softc *sc, int media) { struct ip1000phy_softc *isc; uint32_t reg; @@ -386,8 +350,9 @@ static int reg |= IP1000PHY_ANAR_NP; } reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX | - IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX | - IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE; + IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX; + if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) + reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE; PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA); reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX; Index: dev/mii/mii_physubr.c =================================================================== --- dev/mii/mii_physubr.c (revision 215249) +++ dev/mii/mii_physubr.c (working copy) @@ -106,8 +106,13 @@ mii_phy_setmedia(struct mii_softc *sc) int bmcr, anar, gtcr; if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { + /* + * Force renegotiation if MIIF_DOPAUSE or MIIF_FORCEANEG. + * The former is necessary as we might switch from flow- + * control advertisment being off to on or vice versa. + */ if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 || - (sc->mii_flags & MIIF_FORCEANEG) != 0) + (sc->mii_flags & (MIIF_DOPAUSE | MIIF_FORCEANEG)) != 0) (void)mii_phy_auto(sc); return; } @@ -124,14 +129,23 @@ mii_phy_setmedia(struct mii_softc *sc) bmcr = mii_media_table[ife->ifm_data].mm_bmcr; gtcr = mii_media_table[ife->ifm_data].mm_gtcr; - if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0) { - switch (IFM_SUBTYPE(ife->ifm_media)) { - case IFM_1000_T: - gtcr |= GTCR_MAN_MS | GTCR_ADV_MS; - break; + if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { + gtcr |= GTCR_MAN_MS; + if ((ife->ifm_media & IFM_ETH_MASTER) != 0) + gtcr |= GTCR_ADV_MS; + } - default: - printf("mii_phy_setmedia: MASTER on wrong media\n"); + if ((ife->ifm_media & IFM_GMASK) == (IFM_FDX | IFM_FLOW) || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) { + if ((sc->mii_flags & MIIF_IS_1000X) != 0) + anar |= ANAR_X_PAUSE_TOWARDS; + else { + anar |= ANAR_FC; + /* XXX Only 1000BASE-T has PAUSE_ASYM? */ + if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0 && + (sc->mii_extcapabilities & + (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) + anar |= ANAR_X_PAUSE_ASYM; } } @@ -147,6 +161,7 @@ mii_phy_setmedia(struct mii_softc *sc) int mii_phy_auto(struct mii_softc *sc) { + struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; int anar, gtcr; /* @@ -160,16 +175,23 @@ mii_phy_auto(struct mii_softc *sc) if ((sc->mii_extcapabilities & EXTSR_1000XHDX) != 0) anar |= ANAR_X_HD; - if ((sc->mii_flags & MIIF_DOPAUSE) != 0) { - /* XXX Asymmetric vs. symmetric? */ - anar |= ANLPAR_X_PAUSE_TOWARDS; - } + if ((ife->ifm_media & IFM_FLOW) != 0 || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) + anar |= ANAR_X_PAUSE_TOWARDS; PHY_WRITE(sc, MII_ANAR, anar); } else { anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; - if ((sc->mii_flags & MIIF_DOPAUSE) != 0) - anar |= ANAR_FC; + if ((ife->ifm_media & IFM_FLOW) != 0 || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) { + if ((sc->mii_capabilities & BMSR_100TXFDX) != 0) + anar |= ANAR_FC; + /* XXX Only 1000BASE-T has PAUSE_ASYM? */ + if (((sc->mii_flags & MIIF_HAVE_GTCR) != 0) && + (sc->mii_extcapabilities & + (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) + anar |= ANAR_X_PAUSE_ASYM; + } PHY_WRITE(sc, MII_ANAR, anar); if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { gtcr = 0; @@ -291,6 +313,7 @@ mii_phy_add_media(struct mii_softc *sc) { struct mii_data *mii = sc->mii_pdata; const char *sep = ""; + int fdx = 0; if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) { @@ -334,6 +357,14 @@ mii_phy_add_media(struct mii_softc *sc) ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), MII_MEDIA_10_T_FDX); PRINT("10baseT-FDX"); + if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && + (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, + IFM_FDX | IFM_FLOW, sc->mii_inst), + MII_MEDIA_10_T_FDX); + PRINT("10baseT-FDX-flow"); + } + fdx = 1; } if ((sc->mii_capabilities & BMSR_100TXHDX) != 0) { ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), @@ -344,6 +375,14 @@ mii_phy_add_media(struct mii_softc *sc) ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), MII_MEDIA_100_TX_FDX); PRINT("100baseTX-FDX"); + if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && + (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, + IFM_FDX | IFM_FLOW, sc->mii_inst), + MII_MEDIA_100_TX_FDX); + PRINT("100baseTX-FDX-flow"); + } + fdx = 1; } if ((sc->mii_capabilities & BMSR_100T4) != 0) { ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst), @@ -369,38 +408,67 @@ mii_phy_add_media(struct mii_softc *sc) ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), MII_MEDIA_1000_X_FDX); PRINT("1000baseSX-FDX"); + if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && + (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, + IFM_FDX | IFM_FLOW, sc->mii_inst), + MII_MEDIA_1000_X_FDX); + PRINT("1000baseSX-FDX-flow"); + } + fdx = 1; } /* * 1000baseT media needs to be able to manipulate - * master/slave mode. We set IFM_ETH_MASTER in - * the "don't care mask" and filter it out when - * the media is set. + * master/slave mode. * * All 1000baseT PHYs have a 1000baseT control register. */ if ((sc->mii_extcapabilities & EXTSR_1000THDX) != 0) { sc->mii_anegticks = MII_ANEGTICKS_GIGE; sc->mii_flags |= MIIF_HAVE_GTCR; - mii->mii_media.ifm_mask |= IFM_ETH_MASTER; ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst), MII_MEDIA_1000_T); PRINT("1000baseT"); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, + IFM_ETH_MASTER, sc->mii_inst), MII_MEDIA_1000_T); + PRINT("1000baseT-master"); } if ((sc->mii_extcapabilities & EXTSR_1000TFDX) != 0) { sc->mii_anegticks = MII_ANEGTICKS_GIGE; sc->mii_flags |= MIIF_HAVE_GTCR; - mii->mii_media.ifm_mask |= IFM_ETH_MASTER; ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst), MII_MEDIA_1000_T_FDX); PRINT("1000baseT-FDX"); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, + IFM_FDX | IFM_ETH_MASTER, sc->mii_inst), + MII_MEDIA_1000_T_FDX); + PRINT("1000baseT-FDX-master"); + if ((sc->mii_flags & MIIF_DOPAUSE) != 0 && + (sc->mii_flags & MIIF_NOMANPAUSE) == 0) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, + IFM_FDX | IFM_FLOW, sc->mii_inst), + MII_MEDIA_1000_T_FDX); + PRINT("1000baseT-FDX-flow"); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, + IFM_FDX | IFM_FLOW | IFM_ETH_MASTER, + sc->mii_inst), MII_MEDIA_1000_T_FDX); + PRINT("1000baseT-FDX-flow-master"); + } + fdx = 1; } } if ((sc->mii_capabilities & BMSR_ANEG) != 0) { + /* intentionally invalid index */ ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), - MII_NMEDIA); /* intentionally invalid index */ + MII_NMEDIA); PRINT("auto"); + if (fdx != 0 && (sc->mii_flags & MIIF_DOPAUSE) != 0) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FLOW, + sc->mii_inst), MII_NMEDIA); + PRINT("auto-flow"); + } } #undef ADD #undef PRINT @@ -424,7 +492,7 @@ mii_phy_match_gen(const struct mii_attach_args *ma { for (; mpd->mpd_name != NULL; - mpd = (const struct mii_phydesc *) ((const char *) mpd + len)) { + mpd = (const struct mii_phydesc *)((const char *)mpd + len)) { if (MII_OUI(ma->mii_id1, ma->mii_id2) == mpd->mpd_oui && MII_MODEL(ma->mii_id2) == mpd->mpd_model) return (mpd); @@ -450,3 +518,55 @@ mii_phy_dev_probe(device_t dev, const struct mii_p } return (ENXIO); } + +/* + * Return the flow control status flag from MII_ANAR & MII_ANLPAR. + */ +u_int +mii_phy_flowstatus(struct mii_softc *sc) +{ + int anar, anlpar; + + if ((sc->mii_flags & MIIF_DOPAUSE) == 0) + return (0); + + anar = PHY_READ(sc, MII_ANAR); + anlpar = PHY_READ(sc, MII_ANLPAR); + + /* + * Check for 1000BASE-X. Autonegotiation is a bit + * different on such devices. + */ + if ((sc->mii_flags & MIIF_IS_1000X) != 0) { + anar <<= 3; + anlpar <<= 3; + } + + if ((anar & ANAR_PAUSE_SYM) != 0 && (anlpar & ANLPAR_PAUSE_SYM) != 0) + return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); + + if ((anar & ANAR_PAUSE_SYM) == 0) { + if ((anar & ANAR_PAUSE_ASYM) != 0 && + (anlpar & ANLPAR_PAUSE_TOWARDS) != 0) + return (IFM_FLOW | IFM_ETH_TXPAUSE); + else + return (0); + } + + if ((anar & ANAR_PAUSE_ASYM) == 0) { + if ((anlpar & ANLPAR_PAUSE_SYM) != 0) + return (IFM_FLOW | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); + else + return (0); + } + + switch ((anlpar & ANLPAR_PAUSE_TOWARDS)) { + case ANLPAR_PAUSE_NONE: + return (0); + case ANLPAR_PAUSE_ASYM: + return (IFM_FLOW | IFM_ETH_RXPAUSE); + default: + return (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE); + } + /* NOTREACHED */ +} Index: dev/mii/mii.h =================================================================== --- dev/mii/mii.h (revision 215249) +++ dev/mii/mii.h (working copy) @@ -128,6 +128,10 @@ #define ANAR_10_FD 0x0040 /* local device supports 10bT FD */ #define ANAR_10 0x0020 /* local device supports 10bT */ #define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */ +#define ANAR_PAUSE_NONE (0 << 10) +#define ANAR_PAUSE_SYM (1 << 10) +#define ANAR_PAUSE_ASYM (2 << 10) +#define ANAR_PAUSE_TOWARDS (3 << 10) #define ANAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */ #define ANAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */ @@ -148,6 +152,11 @@ #define ANLPAR_10_FD 0x0040 /* link partner supports 10bT FD */ #define ANLPAR_10 0x0020 /* link partner supports 10bT */ #define ANLPAR_CSMA 0x0001 /* protocol selector CSMA/CD */ +#define ANLPAR_PAUSE_MASK (3 << 10) +#define ANLPAR_PAUSE_NONE (0 << 10) +#define ANLPAR_PAUSE_SYM (1 << 10) +#define ANLPAR_PAUSE_ASYM (2 << 10) +#define ANLPAR_PAUSE_TOWARDS (3 << 10) #define ANLPAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */ #define ANLPAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */ Index: dev/mii/miivar.h =================================================================== --- dev/mii/miivar.h (revision 215249) +++ dev/mii/miivar.h (working copy) @@ -125,6 +125,7 @@ typedef struct mii_softc mii_softc_t; #define MIIF_INITDONE 0x00000001 /* has been initialized (mii_data) */ #define MIIF_NOISOLATE 0x00000002 /* do not isolate the PHY */ #define MIIF_NOLOOP 0x00000004 /* no loopback capability */ +#define MIIF_DOINGAUTO 0x00000008 /* doing autonegotiation (mii_softc) */ #define MIIF_AUTOTSLEEP 0x00000010 /* use tsleep(), not callout() */ #define MIIF_HAVEFIBER 0x00000020 /* from parent: has fiber interface */ #define MIIF_HAVE_GTCR 0x00000040 /* has 100base-T2/1000base-T CR */ @@ -132,6 +133,8 @@ typedef struct mii_softc mii_softc_t; #define MIIF_DOPAUSE 0x00000100 /* advertise PAUSE capability */ #define MIIF_IS_HPNA 0x00000200 /* is a HomePNA device */ #define MIIF_FORCEANEG 0x00000400 /* force auto-negotiation */ +#define MIIF_NOMANPAUSE 0x00100000 /* no manual PAUSE selection */ +#define MIIF_FORCEPAUSE 0x00200000 /* force PAUSE advertisment */ #define MIIF_MACPRIV0 0x01000000 /* private to the MAC driver */ #define MIIF_MACPRIV1 0x02000000 /* private to the MAC driver */ #define MIIF_MACPRIV2 0x04000000 /* private to the MAC driver */ @@ -236,6 +239,7 @@ void mii_phy_add_media(struct mii_softc *); int mii_phy_auto(struct mii_softc *); int mii_phy_detach(device_t dev); void mii_phy_down(struct mii_softc *); +u_int mii_phy_flowstatus(struct mii_softc *); void mii_phy_reset(struct mii_softc *); void mii_phy_setmedia(struct mii_softc *sc); void mii_phy_update(struct mii_softc *, int); Index: dev/mii/smcphy.c =================================================================== --- dev/mii/smcphy.c (revision 215249) +++ dev/mii/smcphy.c (working copy) @@ -54,7 +54,7 @@ static int smcphy_attach(device_t); static int smcphy_service(struct mii_softc *, struct mii_data *, int); static int smcphy_reset(struct mii_softc *); -static void smcphy_auto(struct mii_softc *); +static void smcphy_auto(struct mii_softc *, int); static device_method_t smcphy_methods[] = { /* device interface */ @@ -148,7 +148,7 @@ smcphy_service(struct mii_softc *sc, struct mii_da switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: - smcphy_auto(sc); + smcphy_auto(sc, ife->ifm_media); break; default: @@ -187,7 +187,7 @@ smcphy_service(struct mii_softc *sc, struct mii_da if (smcphy_reset(sc) != 0) { device_printf(sc->mii_dev, "reset failed\n"); } - smcphy_auto(sc); + smcphy_auto(sc, ife->ifm_media); break; } @@ -223,13 +223,13 @@ smcphy_reset(struct mii_softc *sc) } static void -smcphy_auto(struct mii_softc *sc) +smcphy_auto(struct mii_softc *sc, int media) { uint16_t anar; anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; - if (sc->mii_flags & MIIF_DOPAUSE) + if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) anar |= ANAR_FC; PHY_WRITE(sc, MII_ANAR, anar); /* Apparently this helps. */ Index: dev/mii/ukphy_subr.c =================================================================== --- dev/mii/ukphy_subr.c (revision 215249) +++ dev/mii/ukphy_subr.c (working copy) @@ -117,6 +117,13 @@ ukphy_status(struct mii_softc *phy) mii->mii_media_active |= IFM_10_T|IFM_HDX; else mii->mii_media_active |= IFM_NONE; + + if ((mii->mii_media_active & IFM_1000_T) != 0 && + (gtsr & GTSR_MS_RES) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; + + if ((mii->mii_media_active & IFM_FDX) != 0) + mii->mii_media_active |= mii_phy_flowstatus(phy); } else mii->mii_media_active = ife->ifm_media; } Index: net/if_media.h =================================================================== --- net/if_media.h (revision 215249) +++ net/if_media.h (working copy) @@ -155,6 +155,8 @@ uint64_t ifmedia_baudrate(int); /* note 31 is the max! */ #define IFM_ETH_MASTER 0x00000100 /* master mode (1000baseT) */ +#define IFM_ETH_RXPAUSE 0x00000200 /* receive PAUSE frames */ +#define IFM_ETH_TXPAUSE 0x00000400 /* transmit PAUSE frames */ /* * Token ring @@ -262,6 +264,7 @@ uint64_t ifmedia_baudrate(int); */ #define IFM_FDX 0x00100000 /* Force full duplex */ #define IFM_HDX 0x00200000 /* Force half duplex */ +#define IFM_FLOW 0x00400000 /* enable hardware flow control */ #define IFM_FLAG0 0x01000000 /* Driver defined flag */ #define IFM_FLAG1 0x02000000 /* Driver defined flag */ #define IFM_FLAG2 0x04000000 /* Driver defined flag */ @@ -279,6 +282,9 @@ uint64_t ifmedia_baudrate(int); #define IFM_MSHIFT 16 /* Mode shift */ #define IFM_GMASK 0x0ff00000 /* Global options */ +/* Ethernet flow control mask */ +#define IFM_ETH_FMASK (IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE) + /* * Status bits */ @@ -388,6 +394,9 @@ struct ifmedia_description { } #define IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS { \ + { IFM_ETH_MASTER, "master" }, \ + { IFM_ETH_RXPAUSE, "rxpause" }, \ + { IFM_ETH_TXPAUSE, "txpause" }, \ { 0, NULL }, \ } @@ -583,6 +592,7 @@ struct ifmedia_description { #define IFM_SHARED_OPTION_DESCRIPTIONS { \ { IFM_FDX, "full-duplex" }, \ { IFM_HDX, "half-duplex" }, \ + { IFM_FLOW, "flowcontrol" }, \ { IFM_FLAG0, "flag0" }, \ { IFM_FLAG1, "flag1" }, \ { IFM_FLAG2, "flag2" }, \