aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/mii/mii_physubr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mii/mii_physubr.c')
-rw-r--r--sys/dev/mii/mii_physubr.c244
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
+}
+