diff options
-rw-r--r-- | sys/dev/mii/eephy.c | 278 |
1 files changed, 81 insertions, 197 deletions
diff --git a/sys/dev/mii/eephy.c b/sys/dev/mii/eephy.c index b86e708c73a..0c83254ff3b 100644 --- a/sys/dev/mii/eephy.c +++ b/sys/dev/mii/eephy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: eephy.c,v 1.37 2006/12/29 11:03:46 kettenis Exp $ */ +/* $OpenBSD: eephy.c,v 1.38 2006/12/30 20:37:46 kettenis Exp $ */ /* * Principal Author: Parag Patel * Copyright (c) 2001 @@ -115,8 +115,6 @@ static const struct mii_phydesc eephys[] = { NULL }, }; -static int fast_ether; - int eephymatch(struct device *parent, void *match, void *aux) { @@ -135,6 +133,7 @@ eephyattach(struct device *parent, struct device *self, void *aux) struct mii_attach_args *ma = aux; struct mii_data *mii = ma->mii_data; const struct mii_phydesc *mpd; + int reg; mpd = mii_phy_match(ma, eephys); printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); @@ -145,7 +144,6 @@ eephyattach(struct device *parent, struct device *self, void *aux) sc->mii_model = MII_MODEL(ma->mii_id2); sc->mii_pdata = mii; sc->mii_flags = ma->mii_flags; - sc->mii_anegticks = MII_ANEGTICKS_GIGE; /* * XXX really should be passed by the network controller @@ -153,99 +151,87 @@ eephyattach(struct device *parent, struct device *self, void *aux) */ sc->mii_flags |= MIIF_DOPAUSE; - if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_MARVELL && - MII_MODEL(ma->mii_id2) == MII_MODEL_MARVELL_E1011 && - (PHY_READ(sc, E1000_ESSR) & E1000_ESSR_FIBER_LINK)) - sc->mii_flags |= MIIF_HAVEFIBER; - - fast_ether = 0; - if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_MARVELL && - MII_MODEL(ma->mii_id2) == MII_MODEL_MARVELL_E3082) { - /* 88E3082 10/100 Fast Ethernet PHY. */ - sc->mii_anegticks = MII_ANEGTICKS; - fast_ether = 1; + /* XXX No loopback support yet, although the hardware can do it. */ + sc->mii_flags |= MIIF_NOLOOP; + + sc->mii_capabilities = PHY_READ(sc, E1000_SR) & ma->mii_capmask; + if (sc->mii_capabilities & BMSR_EXTSTAT) + sc->mii_extcapabilities = PHY_READ(sc, E1000_ESR); + + mii_phy_add_media(sc); + + /* + * Initialize PHY Specific Control Register. + */ + + reg = PHY_READ(sc, E1000_SCR); + + /* Assert CRS on transmit. */ + reg |= E1000_SCR_ASSERT_CRS_ON_TX; + + /* Enable auto crossover. */ + switch (sc->mii_model) { + case MII_MODEL_MARVELL_E3082: + /* Bits are in a different position. */ + reg |= (E1000_SCR_AUTO_X_MODE >> 1); + break; + default: + /* Automatic crossover causes problems for 1000baseX. */ + if (sc->mii_flags & MIIF_IS_1000X) + reg &= ~E1000_SCR_AUTO_X_MODE; + else + reg |= E1000_SCR_AUTO_X_MODE; } - PHY_RESET(sc); - -#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), - E1000_CR_ISOLATE); - - if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { - if (fast_ether == 0) { - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, - sc->mii_inst), E1000_CR_SPEED_1000 | - E1000_CR_FULL_DUPLEX); - /* - * 1000BT-simplex not supported; driver must ignore this entry, - * but it must be present in order to manually set full-duplex. - */ - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, - sc->mii_inst), E1000_CR_SPEED_1000); - } - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), - E1000_CR_SPEED_100 | E1000_CR_FULL_DUPLEX); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), - E1000_CR_SPEED_100); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), - E1000_CR_SPEED_10 | E1000_CR_FULL_DUPLEX); - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), - E1000_CR_SPEED_10); - } else { - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,sc->mii_inst), - E1000_CR_SPEED_1000 | E1000_CR_FULL_DUPLEX); + /* Disable energy detect; only available on some models. */ + switch(sc->mii_model) { + case MII_MODEL_MARVELL_E1011: + case MII_MODEL_MARVELL_E1111: + case MII_MODEL_MARVELL_E1112: + /* Disable energy detect. */ + reg &= ~E1000_SCR_EN_DETECT_MASK; + break; } - ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); + PHY_WRITE(sc, E1000_SCR, reg); -#undef ADD + /* 25 MHz TX_CLK should always work. */ + reg = PHY_READ(sc, E1000_ESCR); + reg |= E1000_ESCR_TX_CLK_25; + PHY_WRITE(sc, E1000_ESCR, reg); + + /* + * Do a software reset for these settings to take effect. + * Disable autonegotiation, such that all capabilities get + * advertised when it is switched back on. + */ + reg = PHY_READ(sc, E1000_CR); + reg &= ~E1000_CR_AUTO_NEG_ENABLE; + PHY_WRITE(sc, E1000_CR, reg | E1000_CR_RESET); } void eephy_reset(struct mii_softc *sc) { - u_int32_t reg; - int i; - - /* initialize custom E1000 registers to magic values */ - reg = PHY_READ(sc, E1000_SCR); - reg &= ~E1000_SCR_AUTO_X_MODE; - PHY_WRITE(sc, E1000_SCR, reg); + int reg, i; - /* normal PHY reset */ - /*mii_phy_reset(sc);*/ reg = PHY_READ(sc, E1000_CR); reg |= E1000_CR_RESET; PHY_WRITE(sc, E1000_CR, reg); - + for (i = 0; i < 500; i++) { DELAY(1); reg = PHY_READ(sc, E1000_CR); if (!(reg & E1000_CR_RESET)) break; } - - /* set more custom E1000 registers to magic values */ - reg = PHY_READ(sc, E1000_SCR); - reg |= E1000_SCR_ASSERT_CRS_ON_TX; - PHY_WRITE(sc, E1000_SCR, reg); - - reg = PHY_READ(sc, E1000_ESCR); - reg |= E1000_ESCR_TX_CLK_25; - PHY_WRITE(sc, E1000_ESCR, reg); - - /* even more magic to reset DSP? */ - PHY_WRITE(sc, 29, 0x1d); - PHY_WRITE(sc, 30, 0xc1); - PHY_WRITE(sc, 30, 0x00); } int eephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; - int reg; + int bmcr; if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) return (ENXIO); @@ -265,8 +251,8 @@ eephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) * isolate ourselves. */ if (IFM_INST(ife->ifm_media) != sc->mii_inst) { - reg = PHY_READ(sc, E1000_CR); - PHY_WRITE(sc, E1000_CR, reg | E1000_CR_ISOLATE); + bmcr = PHY_READ(sc, E1000_CR); + PHY_WRITE(sc, E1000_CR, bmcr | E1000_CR_ISOLATE); return (0); } @@ -276,70 +262,16 @@ eephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) if ((mii->mii_ifp->if_flags & IFF_UP) == 0) break; - switch (IFM_SUBTYPE(ife->ifm_media)) { - case IFM_AUTO: - PHY_RESET(sc); - (void) eephy_mii_phy_auto(sc); - break; - - case IFM_1000_SX: - if (fast_ether == 1) - return (EINVAL); - - PHY_RESET(sc); - - PHY_WRITE(sc, E1000_CR, - E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_1000); - PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD); - break; - - case IFM_1000_T: - if (fast_ether == 1) - return (EINVAL); - - PHY_RESET(sc); - - /* TODO - any other way to force 1000BT? */ - (void) eephy_mii_phy_auto(sc); - break; - - case IFM_100_TX: - PHY_RESET(sc); - - if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { - PHY_WRITE(sc, E1000_CR, - E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_100); - PHY_WRITE(sc, E1000_AR, E1000_AR_100TX_FD); - } else { - PHY_WRITE(sc, E1000_CR, E1000_CR_SPEED_100); - PHY_WRITE(sc, E1000_AR, E1000_AR_100TX); - } - break; - - case IFM_10_T: - PHY_RESET(sc); - - if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { - PHY_WRITE(sc, E1000_CR, - E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_10); - PHY_WRITE(sc, E1000_AR, E1000_AR_10T_FD); - } else { - PHY_WRITE(sc, E1000_CR, E1000_CR_SPEED_10); - PHY_WRITE(sc, E1000_AR, E1000_AR_10T); - } - - break; - - case IFM_NONE: - reg = PHY_READ(sc, E1000_CR); - PHY_WRITE(sc, E1000_CR, - reg | E1000_CR_ISOLATE | E1000_CR_POWER_DOWN); - break; + mii_phy_setmedia(sc); - default: - return (EINVAL); + /* + * If autonegitation is not enabled, we need a + * software reset for the settings to take effect. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { + bmcr = PHY_READ(sc, E1000_CR); + PHY_WRITE(sc, E1000_CR, bmcr | E1000_CR_RESET); } - break; case MII_TICK: @@ -349,46 +281,20 @@ eephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) if (IFM_INST(ife->ifm_media) != sc->mii_inst) return (0); - /* - * Is the interface even up? - */ - if ((mii->mii_ifp->if_flags & IFF_UP) == 0) - return (0); - - /* - * Only used for autonegotiation. - */ - if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) - break; - - /* - * Check to see if we have link. If we do, we don't - * need to restart the autonegotiation process. Read - * the BMSR twice in case it's latched. - */ - reg = PHY_READ(sc, E1000_SR) | PHY_READ(sc, E1000_SR); - if (reg & E1000_SR_LINK_STATUS) - break; - - /* - * Only retry autonegotiation every mii_anegticks seconds. - */ - if (++sc->mii_ticks <= sc->mii_anegticks) - break; - - sc->mii_ticks = 0; - PHY_RESET(sc); - - eephy_mii_phy_auto(sc); + if (mii_phy_tick(sc) == EJUSTRETURN) + return (0); break; + + case MII_DOWN: + mii_phy_down(sc); + return (0); } /* Update the media status. */ - eephy_status(sc); + mii_phy_status(sc); /* Callback if something changed. */ mii_phy_update(sc, cmd); - return (0); } @@ -396,38 +302,36 @@ void eephy_status(struct mii_softc *sc) { struct mii_data *mii = sc->mii_pdata; - int bmsr, bmcr, gsr, ssr; + int bmcr, gsr, ssr; mii->mii_media_status = IFM_AVALID; mii->mii_media_active = IFM_ETHER; - bmsr = PHY_READ(sc, E1000_SR) | PHY_READ(sc, E1000_SR); bmcr = PHY_READ(sc, E1000_CR); ssr = PHY_READ(sc, E1000_SSR); - if (bmsr & E1000_SR_LINK_STATUS) + if (ssr & E1000_SSR_LINK) mii->mii_media_status |= IFM_ACTIVE; if (bmcr & E1000_CR_LOOPBACK) mii->mii_media_active |= IFM_LOOP; - if ((!(bmsr & E1000_SR_AUTO_NEG_COMPLETE) || !(ssr & E1000_SSR_LINK) || - !(ssr & E1000_SSR_SPD_DPLX_RESOLVED))) { + if (!(ssr & E1000_SSR_SPD_DPLX_RESOLVED)) { /* Erg, still trying, I guess... */ mii->mii_media_active |= IFM_NONE; return; } - if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { + if (sc->mii_flags & MIIF_IS_1000X) { + if (ssr & E1000_SSR_1000MBS) + mii->mii_media_active |= IFM_1000_SX; + } else { if (ssr & E1000_SSR_1000MBS) mii->mii_media_active |= IFM_1000_T; else if (ssr & E1000_SSR_100MBS) mii->mii_media_active |= IFM_100_TX; else mii->mii_media_active |= IFM_10_T; - } else { - if (ssr & E1000_SSR_1000MBS) - mii->mii_media_active |= IFM_1000_SX; } if (ssr & E1000_SSR_DUPLEX) @@ -441,23 +345,3 @@ eephy_status(struct mii_softc *sc) mii->mii_media_active |= IFM_ETH_MASTER; } } - -int -eephy_mii_phy_auto(struct mii_softc *sc) -{ - if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { - PHY_WRITE(sc, E1000_AR, - E1000_AR_10T | E1000_AR_10T_FD | - E1000_AR_100TX | E1000_AR_100TX_FD | - E1000_AR_PAUSE | E1000_AR_ASM_DIR); - if (fast_ether == 0) - PHY_WRITE(sc, E1000_1GCR, E1000_1GCR_1000T_FD); - } else { - PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | - E1000_FA_SYM_PAUSE | E1000_FA_ASYM_PAUSE); - } - PHY_WRITE(sc, E1000_CR, - E1000_CR_AUTO_NEG_ENABLE | E1000_CR_RESTART_AUTO_NEG); - - return (EJUSTRETURN); -} |