diff options
| author | Poul-Henning Kamp <phk@FreeBSD.org> | 2002-04-29 11:57:30 +0000 |
|---|---|---|
| committer | Poul-Henning Kamp <phk@FreeBSD.org> | 2002-04-29 11:57:30 +0000 |
| commit | 78c8c3db4bca4f44d8a16e63de177324d2063069 (patch) | |
| tree | ab00a27fad6535ee94d5573484470b55ed8fe2c5 /sys/dev/mii/mii_physubr.c | |
| parent | f7bf276ff5c4afb3521b2b88eb6fbf58c55d7b1b (diff) | |
Notes
Diffstat (limited to 'sys/dev/mii/mii_physubr.c')
| -rw-r--r-- | sys/dev/mii/mii_physubr.c | 244 |
1 files changed, 194 insertions, 50 deletions
diff --git a/sys/dev/mii/mii_physubr.c b/sys/dev/mii/mii_physubr.c index 2bc17b09cdb2..c2b7a11003d0 100644 --- a/sys/dev/mii/mii_physubr.c +++ b/sys/dev/mii/mii_physubr.c @@ -1,7 +1,7 @@ /* $NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $ */ /*- - * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -127,10 +127,9 @@ mii_phy_setmedia(struct mii_softc *sc) * Table index is stored in the media entry. */ -#ifdef DIAGNOSTIC - if (ife->ifm_data < 0 || ife->ifm_data >= MII_NMEDIA) - panic("mii_phy_setmedia"); -#endif + KASSERT(ife->ifm_data >=0 && ife->ifm_data < MII_NMEDIA, + ("invalid ife->ifm_data (0x%x) in mii_phy_setmedia", + ife->ifm_data)); anar = mii_media_table[ife->ifm_data].mm_anar; bmcr = mii_media_table[ife->ifm_data].mm_bmcr; @@ -157,29 +156,57 @@ mii_phy_setmedia(struct mii_softc *sc) } int -mii_phy_auto(mii, waitfor) - struct mii_softc *mii; - int waitfor; +mii_phy_auto(struct mii_softc *sc, int waitfor) { int bmsr, i; - if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { - PHY_WRITE(mii, MII_ANAR, - BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA); - PHY_WRITE(mii, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); + if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) { + /* + * Check for 1000BASE-X. Autonegotiation is a bit + * different on such devices. + */ + if (sc->mii_flags & MIIF_IS_1000X) { + uint16_t anar = 0; + + if (sc->mii_extcapabilities & EXTSR_1000XFDX) + anar |= ANAR_X_FD; + if (sc->mii_extcapabilities & EXTSR_1000XHDX) + anar |= ANAR_X_HD; + + if (sc->mii_flags & MIIF_DOPAUSE) { + /* XXX Asymmetric vs. symmetric? */ + anar |= ANLPAR_X_PAUSE_TOWARDS; + } + + PHY_WRITE(sc, MII_ANAR, anar); + } else { + uint16_t anar; + + anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | + ANAR_CSMA; + if (sc->mii_flags & MIIF_DOPAUSE) + anar |= ANAR_FC; + PHY_WRITE(sc, MII_ANAR, anar); + if (sc->mii_flags & MIIF_HAVE_GTCR) { + uint16_t gtcr = 0; + + if (sc->mii_extcapabilities & EXTSR_1000TFDX) + gtcr |= GTCR_ADV_1000TFDX; + if (sc->mii_extcapabilities & EXTSR_1000THDX) + gtcr |= GTCR_ADV_1000THDX; + + PHY_WRITE(sc, MII_100T2CR, gtcr); + } + } + PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); } if (waitfor) { /* Wait 500ms for it to complete. */ for (i = 0; i < 500; i++) { - if ((bmsr = PHY_READ(mii, MII_BMSR)) & BMSR_ACOMP) + if ((bmsr = PHY_READ(sc, MII_BMSR)) & BMSR_ACOMP) return (0); DELAY(1000); -#if 0 - if ((bmsr & BMSR_ACOMP) == 0) - printf("%s: autonegotiation failed to complete\n", - mii->mii_dev.dv_xname); -#endif } /* @@ -195,9 +222,9 @@ mii_phy_auto(mii, waitfor) * the tick handler driving autonegotiation. Don't want 500ms * delays all the time while the system is running! */ - if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { - mii->mii_flags |= MIIF_DOINGAUTO; - mii->mii_auto_ch = timeout(mii_phy_auto_timeout, mii, hz >> 1); + if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) { + sc->mii_flags |= MIIF_DOINGAUTO; + sc->mii_auto_ch = timeout(mii_phy_auto_timeout, sc, hz >> 1); } return (EJUSTRETURN); } @@ -213,37 +240,33 @@ mii_phy_auto_stop(sc) } void -mii_phy_auto_timeout(arg) - void *arg; +mii_phy_auto_timeout(void *arg) { - struct mii_softc *mii = arg; + struct mii_softc *sc = arg; int s, bmsr; - s = splnet(); - mii->mii_flags &= ~MIIF_DOINGAUTO; - bmsr = PHY_READ(mii, MII_BMSR); #if 0 - if ((bmsr & BMSR_ACOMP) == 0) - printf("%s: autonegotiation failed to complete\n", - sc->sc_dev.dv_xname); + if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) + return; #endif + s = splnet(); + sc->mii_flags &= ~MIIF_DOINGAUTO; + bmsr = PHY_READ(sc, MII_BMSR); + /* Update the media status. */ - (void) (*mii->mii_service)(mii, mii->mii_pdata, MII_POLLSTAT); + (void) (*sc->mii_service)(sc, sc->mii_pdata, MII_POLLSTAT); splx(s); } int -mii_phy_tick(sc) - struct mii_softc *sc; +mii_phy_tick(struct mii_softc *sc) { struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; struct ifnet *ifp = sc->mii_pdata->mii_ifp; int reg; - /* - * Is the interface even up? - */ + /* Just bail now if the interface is down. */ if ((ifp->if_flags & IFF_UP) == 0) return (EJUSTRETURN); @@ -256,18 +279,21 @@ mii_phy_tick(sc) if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) return (0); - /* - * check for link. - * Read the status register twice; BMSR_LINK is latch-low. - */ + /* Read the status register twice; BMSR_LINK is latch-low. */ reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); - if (reg & BMSR_LINK) + if (reg & BMSR_LINK) { + /* + * See above. + */ return (0); + } /* - * Only retry autonegotiation every 5 seconds. + * Only retry autonegotiation every N seconds. */ - if (++sc->mii_ticks != 5) + if (sc->mii_anegticks == 0) + sc->mii_anegticks = 5; + if (++sc->mii_ticks != sc->mii_anegticks) return (EJUSTRETURN); sc->mii_ticks = 0; @@ -283,27 +309,26 @@ mii_phy_tick(sc) } void -mii_phy_reset(mii) - struct mii_softc *mii; +mii_phy_reset(struct mii_softc *sc) { int reg, i; - if (mii->mii_flags & MIIF_NOISOLATE) + if (sc->mii_flags & MIIF_NOISOLATE) reg = BMCR_RESET; else reg = BMCR_RESET | BMCR_ISO; - PHY_WRITE(mii, MII_BMCR, reg); + PHY_WRITE(sc, MII_BMCR, reg); /* Wait 100ms for it to complete. */ for (i = 0; i < 100; i++) { - reg = PHY_READ(mii, MII_BMCR); + reg = PHY_READ(sc, MII_BMCR); if ((reg & BMCR_RESET) == 0) break; DELAY(1000); } - if (mii->mii_inst != 0 && ((mii->mii_flags & MIIF_NOISOLATE) == 0)) - PHY_WRITE(mii, MII_BMCR, reg | BMCR_ISO); + if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0)) + PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); } void @@ -436,3 +461,122 @@ mii_add_media(struct mii_softc *sc) #undef ADD #undef PRINT } + +/* + * Initialize generic PHY media based on BMSR, called when a PHY is + * attached. We expect to be set up to print a comma-separated list + * of media names. Does not print a newline. + */ +void +mii_phy_add_media(struct mii_softc *sc) +{ + struct mii_data *mii = sc->mii_pdata; + const char *sep = ""; + +#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) +#define PRINT(s) printf("%s%s", sep, s); sep = ", " + + if ((sc->mii_flags & MIIF_NOISOLATE) == 0) + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), + MII_MEDIA_NONE); + + /* + * There are different interpretations for the bits in + * HomePNA PHYs. And there is really only one media type + * that is supported. + */ + if (sc->mii_flags & MIIF_IS_HPNA) { + if (sc->mii_capabilities & BMSR_10THDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, + sc->mii_inst), + MII_MEDIA_10_T); + PRINT("HomePNA1"); + } + return; + } + + if (sc->mii_capabilities & BMSR_10THDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), + MII_MEDIA_10_T); + PRINT("10baseT"); + } + if (sc->mii_capabilities & BMSR_10TFDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), + MII_MEDIA_10_T_FDX); + PRINT("10baseT-FDX"); + } + if (sc->mii_capabilities & BMSR_100TXHDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), + MII_MEDIA_100_TX); + PRINT("100baseTX"); + } + if (sc->mii_capabilities & BMSR_100TXFDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), + MII_MEDIA_100_TX_FDX); + PRINT("100baseTX-FDX"); + } + if (sc->mii_capabilities & BMSR_100T4) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst), + MII_MEDIA_100_T4); + PRINT("100baseT4"); + } + + if (sc->mii_extcapabilities & EXTSR_MEDIAMASK) { + /* + * XXX Right now only handle 1000SX and 1000TX. Need + * XXX to handle 1000LX and 1000CX some how. + * + * Note since it can take 5 seconds to auto-negotiate + * a gigabit link, we make anegticks 10 seconds for + * all the gigabit media types. + */ + if (sc->mii_extcapabilities & EXTSR_1000XHDX) { + sc->mii_anegticks = 10; + sc->mii_flags |= MIIF_IS_1000X; + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, + sc->mii_inst), MII_MEDIA_1000_X); + PRINT("1000baseSX"); + } + if (sc->mii_extcapabilities & EXTSR_1000XFDX) { + sc->mii_anegticks = 10; + sc->mii_flags |= MIIF_IS_1000X; + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, + sc->mii_inst), MII_MEDIA_1000_X_FDX); + PRINT("1000baseSX-FDX"); + } + + /* + * 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. + * + * All 1000baseT PHYs have a 1000baseT control register. + */ + if (sc->mii_extcapabilities & EXTSR_1000THDX) { + sc->mii_anegticks = 10; + 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"); + } + if (sc->mii_extcapabilities & EXTSR_1000TFDX) { + sc->mii_anegticks = 10; + 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"); + } + } + + if (sc->mii_capabilities & BMSR_ANEG) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), + MII_NMEDIA); /* intentionally invalid index */ + PRINT("auto"); + } +#undef ADD +#undef PRINT +} + |
