Index: projects/ifnet/sys/dev/mii/brgphy.c =================================================================== --- projects/ifnet/sys/dev/mii/brgphy.c (revision 277120) +++ projects/ifnet/sys/dev/mii/brgphy.c (revision 277121) @@ -1,1074 +1,1074 @@ /*- * Copyright (c) 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Broadcom BCM54xx/57xx 1000baseTX PHY. */ #include #include #include +#include /* XXXGL: if_b[cg]ereg.h contamination */ +#include /* XXXGL: if_b[cg]ereg.h contamination */ #include #include #include #include #include -#include #include #include #include #include #include "miidevs.h" #include -#include #include #include #include #include #include #include "miibus_if.h" static int brgphy_probe(device_t); static int brgphy_attach(device_t); struct brgphy_softc { struct mii_softc mii_sc; int serdes_flags; /* Keeps track of the serdes type used */ #define BRGPHY_5706S 0x0001 #define BRGPHY_5708S 0x0002 #define BRGPHY_NOANWAIT 0x0004 #define BRGPHY_5709S 0x0008 int bce_phy_flags; /* PHY flags transferred from the MAC driver */ }; static device_method_t brgphy_methods[] = { /* device interface */ DEVMETHOD(device_probe, brgphy_probe), DEVMETHOD(device_attach, brgphy_attach), DEVMETHOD(device_detach, mii_phy_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static devclass_t brgphy_devclass; static driver_t brgphy_driver = { "brgphy", brgphy_methods, sizeof(struct brgphy_softc) }; 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); static void brgphy_status(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 *); static void bcm5411_load_dspcode(struct mii_softc *); static void bcm54k2_load_dspcode(struct mii_softc *); static void brgphy_fixup_5704_a0_bug(struct mii_softc *); static void brgphy_fixup_adc_bug(struct mii_softc *); static void brgphy_fixup_adjust_trim(struct mii_softc *); static void brgphy_fixup_ber_bug(struct mii_softc *); static void brgphy_fixup_crc_bug(struct mii_softc *); static void brgphy_fixup_jitter_bug(struct mii_softc *); static void brgphy_ethernet_wirespeed(struct mii_softc *); static void brgphy_jumbo_settings(struct mii_softc *, u_long); static const struct mii_phydesc brgphys[] = { MII_PHY_DESC(BROADCOM, BCM5400), MII_PHY_DESC(BROADCOM, BCM5401), MII_PHY_DESC(BROADCOM, BCM5411), MII_PHY_DESC(BROADCOM, BCM54K2), MII_PHY_DESC(BROADCOM, BCM5701), MII_PHY_DESC(BROADCOM, BCM5703), MII_PHY_DESC(BROADCOM, BCM5704), MII_PHY_DESC(BROADCOM, BCM5705), MII_PHY_DESC(BROADCOM, BCM5706), MII_PHY_DESC(BROADCOM, BCM5714), MII_PHY_DESC(BROADCOM, BCM5421), MII_PHY_DESC(BROADCOM, BCM5750), MII_PHY_DESC(BROADCOM, BCM5752), MII_PHY_DESC(BROADCOM, BCM5780), MII_PHY_DESC(BROADCOM, BCM5708C), MII_PHY_DESC(BROADCOM2, BCM5482), MII_PHY_DESC(BROADCOM2, BCM5708S), MII_PHY_DESC(BROADCOM2, BCM5709C), MII_PHY_DESC(BROADCOM2, BCM5709S), MII_PHY_DESC(BROADCOM2, BCM5709CAX), MII_PHY_DESC(BROADCOM2, BCM5722), MII_PHY_DESC(BROADCOM2, BCM5755), MII_PHY_DESC(BROADCOM2, BCM5754), MII_PHY_DESC(BROADCOM2, BCM5761), MII_PHY_DESC(BROADCOM2, BCM5784), #ifdef notyet /* better handled by ukphy(4) until WARs are implemented */ MII_PHY_DESC(BROADCOM2, BCM5785), #endif MII_PHY_DESC(BROADCOM3, BCM5717C), MII_PHY_DESC(BROADCOM3, BCM5719C), MII_PHY_DESC(BROADCOM3, BCM5720C), MII_PHY_DESC(BROADCOM3, BCM57765), MII_PHY_DESC(BROADCOM3, BCM57780), MII_PHY_DESC(BROADCOM4, BCM5725C), MII_PHY_DESC(xxBROADCOM_ALT1, BCM5906), MII_PHY_END }; static const struct mii_phy_funcs brgphy_funcs = { brgphy_service, brgphy_status, brgphy_reset }; #define HS21_PRODUCT_ID "IBM eServer BladeCenter HS21" #define HS21_BCM_CHIPID 0x57081021 static int detect_hs21(struct bce_softc *bce_sc) { char *sysenv; int found; found = 0; if (bce_sc->bce_chipid == HS21_BCM_CHIPID) { sysenv = kern_getenv("smbios.system.product"); if (sysenv != NULL) { if (strncmp(sysenv, HS21_PRODUCT_ID, strlen(HS21_PRODUCT_ID)) == 0) found = 1; freeenv(sysenv); } } return (found); } /* Search for our PHY in the list of known PHYs */ static int brgphy_probe(device_t dev) { return (mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT)); } /* Attach the PHY to the MII bus */ static int brgphy_attach(device_t dev) { struct brgphy_softc *bsc; struct bge_softc *bge_sc = NULL; struct bce_softc *bce_sc = NULL; struct mii_softc *sc; bsc = device_get_softc(dev); sc = &bsc->mii_sc; mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, &brgphy_funcs, 0); bsc->serdes_flags = 0; /* Find the MAC driver associated with this PHY. */ if (mii_dev_mac_match(dev, "bge")) bge_sc = mii_dev_mac_softc(dev); else if (mii_dev_mac_match(dev, "bce")) bce_sc = mii_dev_mac_softc(dev); /* Handle any special cases based on the PHY ID */ switch (sc->mii_mpd_oui) { case MII_OUI_BROADCOM: switch (sc->mii_mpd_model) { case MII_MODEL_BROADCOM_BCM5706: case MII_MODEL_BROADCOM_BCM5714: /* * The 5464 PHY used in the 5706 supports both copper * and fiber interfaces over GMII. Need to check the * shadow registers to see which mode is actually * in effect, and therefore whether we have 5706C or * 5706S. */ PHY_WRITE(sc, BRGPHY_MII_SHADOW_1C, BRGPHY_SHADOW_1C_MODE_CTRL); if (PHY_READ(sc, BRGPHY_MII_SHADOW_1C) & BRGPHY_SHADOW_1C_ENA_1000X) { bsc->serdes_flags |= BRGPHY_5706S; sc->mii_flags |= MIIF_HAVEFIBER; } break; } break; case MII_OUI_BROADCOM2: switch (sc->mii_mpd_model) { case MII_MODEL_BROADCOM2_BCM5708S: bsc->serdes_flags |= BRGPHY_5708S; sc->mii_flags |= MIIF_HAVEFIBER; break; case MII_MODEL_BROADCOM2_BCM5709S: /* * XXX * 5720S and 5709S shares the same PHY id. * Assume 5720S PHY if parent device is bge(4). */ if (bge_sc != NULL) bsc->serdes_flags |= BRGPHY_5708S; else bsc->serdes_flags |= BRGPHY_5709S; sc->mii_flags |= MIIF_HAVEFIBER; break; } break; } PHY_RESET(sc); /* Read the PHY's capabilities. */ sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; if (sc->mii_capabilities & BMSR_EXTSTAT) sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); device_printf(dev, " "); #define ADD(m, c) ifmedia_add(&sc->mii_pdata->mii_media, (m), (c), NULL) /* Add the supported media types */ if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 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, "); /* 2.5G support is a software enabled feature on the 5708S and 5709S. */ if (bce_sc && (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG)) { ADD(IFM_MAKEWORD(IFM_ETHER, IFM_2500_SX, IFM_FDX, sc->mii_inst), 0); printf("2500baseSX-FDX, "); } else if ((bsc->serdes_flags & BRGPHY_5708S) && bce_sc && (detect_hs21(bce_sc) != 0)) { /* * There appears to be certain silicon revision * in IBM HS21 blades that is having issues with * this driver wating for the auto-negotiation to * complete. This happens with a specific chip id * only and when the 1000baseSX-FDX is the only * mode. Workaround this issue since it's unlikely * to be ever addressed. */ printf("auto-neg workaround, "); bsc->serdes_flags |= BRGPHY_NOANWAIT; } ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); printf("auto\n"); } #undef ADD MIIBUS_MEDIAINIT(sc->mii_dev); return (0); } static int brgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; int val; switch (cmd) { case MII_POLLSTAT: break; case MII_MEDIACHG: /* Todo: Why is this here? Is it really needed? */ PHY_RESET(sc); /* XXX hardware bug work-around */ switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: 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); break; default: return (EINVAL); } break; case MII_TICK: /* Bail if autoneg isn't in process. */ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { sc->mii_ticks = 0; break; } /* * 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); if (val & BMSR_LINK) { sc->mii_ticks = 0; /* Reset autoneg timer. */ break; } /* Announce link loss right after it happens. */ if (sc->mii_ticks++ == 0) break; /* Only retry autonegotiation every mii_anegticks seconds. */ if (sc->mii_ticks <= sc->mii_anegticks) break; /* Retry autonegotiation */ sc->mii_ticks = 0; brgphy_mii_phy_auto(sc, ife->ifm_media); break; } /* Update the media status. */ PHY_STATUS(sc); /* * Callback if something changed. Note that we need to poke * the DSP on the Broadcom PHYs if the media changes. */ if (sc->mii_media_active != mii->mii_media_active || sc->mii_media_status != mii->mii_media_status || cmd == MII_MEDIACHG) { switch (sc->mii_mpd_oui) { case MII_OUI_BROADCOM: switch (sc->mii_mpd_model) { case MII_MODEL_BROADCOM_BCM5400: bcm5401_load_dspcode(sc); break; case MII_MODEL_BROADCOM_BCM5401: if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3) bcm5401_load_dspcode(sc); break; case MII_MODEL_BROADCOM_BCM5411: bcm5411_load_dspcode(sc); break; case MII_MODEL_BROADCOM_BCM54K2: bcm54k2_load_dspcode(sc); break; } break; } } mii_phy_update(sc, cmd); return (0); } /****************************************************************************/ /* Sets the PHY link speed. */ /* */ /* Returns: */ /* None */ /****************************************************************************/ static void brgphy_setmedia(struct mii_softc *sc, int media) { int bmcr = 0, gig; switch (IFM_SUBTYPE(media)) { case IFM_2500_SX: break; case IFM_1000_SX: case IFM_1000_T: bmcr = BRGPHY_S1000; break; case IFM_100_TX: bmcr = BRGPHY_S100; break; case IFM_10_T: default: bmcr = BRGPHY_S10; break; } if ((media & IFM_FDX) != 0) { bmcr |= BRGPHY_BMCR_FDX; gig = BRGPHY_1000CTL_AFD; } else { gig = BRGPHY_1000CTL_AHD; } /* Force loopback to disconnect PHY from Ethernet medium. */ brgphy_enable_loopback(sc); PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0); PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE); if (IFM_SUBTYPE(media) != IFM_1000_T && IFM_SUBTYPE(media) != IFM_1000_SX) { PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr); return; } 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); PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG); } /****************************************************************************/ /* Set the media status based on the PHY settings. */ /* */ /* Returns: */ /* None */ /****************************************************************************/ static void 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, 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); if (bmcr & BRGPHY_BMCR_LOOP) { mii->mii_media_active |= IFM_LOOP; } 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; return; } 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. */ if (aux & BRGPHY_AUXSTS_LINK) { mii->mii_media_status |= IFM_ACTIVE; switch (aux & BRGPHY_AUXSTS_AN_RES) { case BRGPHY_RES_1000FD: mii->mii_media_active |= IFM_1000_T | IFM_FDX; break; case BRGPHY_RES_1000HD: mii->mii_media_active |= IFM_1000_T | IFM_HDX; break; case BRGPHY_RES_100FD: mii->mii_media_active |= IFM_100_TX | IFM_FDX; break; case BRGPHY_RES_100T4: mii->mii_media_active |= IFM_100_T4; break; case BRGPHY_RES_100HD: mii->mii_media_active |= IFM_100_TX | IFM_HDX; break; case BRGPHY_RES_10FD: mii->mii_media_active |= IFM_10_T | IFM_FDX; break; case BRGPHY_RES_10HD: mii->mii_media_active |= IFM_10_T | IFM_HDX; break; 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; } /* Check the link speed/duplex based on the PHY type. */ if (bsc->serdes_flags & BRGPHY_5706S) { mii->mii_media_active |= IFM_1000_SX; /* If autoneg enabled, read negotiated duplex settings */ if (bmcr & BRGPHY_BMCR_AUTOEN) { val = PHY_READ(sc, BRGPHY_SERDES_ANAR) & PHY_READ(sc, BRGPHY_SERDES_ANLPAR); if (val & BRGPHY_SERDES_ANAR_FDX) mii->mii_media_active |= IFM_FDX; 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); /* Check for MRBE auto-negotiated speed results. */ switch (xstat & BRGPHY_5708S_PG0_1000X_STAT1_SPEED_MASK) { case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_10: mii->mii_media_active |= IFM_10_FL; break; case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_100: mii->mii_media_active |= IFM_100_FX; break; case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_1G: mii->mii_media_active |= IFM_1000_SX; break; case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_25G: mii->mii_media_active |= IFM_2500_SX; break; } /* Check for MRBE auto-negotiated duplex results. */ if (xstat & BRGPHY_5708S_PG0_1000X_STAT1_FDX) 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); /* Restore IEEE0 block (assumed in all brgphy(4) code). */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0); /* Check for MRBE auto-negotiated speed results. */ switch (xstat & BRGPHY_GP_STATUS_TOP_ANEG_SPEED_MASK) { case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_10: mii->mii_media_active |= IFM_10_FL; break; case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_100: mii->mii_media_active |= IFM_100_FX; break; case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_1G: mii->mii_media_active |= IFM_1000_SX; break; case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_25G: mii->mii_media_active |= IFM_2500_SX; break; } /* Check for MRBE auto-negotiated duplex results. */ if (xstat & BRGPHY_GP_STATUS_TOP_ANEG_FDX) mii->mii_media_active |= IFM_FDX; else mii->mii_media_active |= IFM_HDX; } } } static void brgphy_mii_phy_auto(struct mii_softc *sc, int media) { int anar, ktcr = 0; PHY_RESET(sc); if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 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); ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD; if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5701) ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC; PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr); PHY_READ(sc, BRGPHY_MII_1000CTL); } else { 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); } 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) { int i; PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP); for (i = 0; i < 15000; i++) { if (!(PHY_READ(sc, BRGPHY_MII_BMSR) & BRGPHY_BMSR_LINK)) break; DELAY(10); } } /* Turn off tap power management on 5401. */ static void bcm5401_load_dspcode(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { BRGPHY_MII_AUXCTL, 0x0c20 }, { BRGPHY_MII_DSP_ADDR_REG, 0x0012 }, { BRGPHY_MII_DSP_RW_PORT, 0x1804 }, { BRGPHY_MII_DSP_ADDR_REG, 0x0013 }, { BRGPHY_MII_DSP_RW_PORT, 0x1204 }, { BRGPHY_MII_DSP_ADDR_REG, 0x8006 }, { BRGPHY_MII_DSP_RW_PORT, 0x0132 }, { BRGPHY_MII_DSP_ADDR_REG, 0x8006 }, { BRGPHY_MII_DSP_RW_PORT, 0x0232 }, { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, { BRGPHY_MII_DSP_RW_PORT, 0x0a20 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); DELAY(40); } static void bcm5411_load_dspcode(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { 0x1c, 0x8c23 }, { 0x1c, 0x8ca3 }, { 0x1c, 0x8c23 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } void bcm54k2_load_dspcode(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { 4, 0x01e1 }, { 9, 0x0300 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_5704_a0_bug(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { 0x1c, 0x8d68 }, { 0x1c, 0x8d68 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_adc_bug(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { BRGPHY_MII_AUXCTL, 0x0c00 }, { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, { BRGPHY_MII_DSP_RW_PORT, 0x2aaa }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_adjust_trim(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { BRGPHY_MII_AUXCTL, 0x0c00 }, { BRGPHY_MII_DSP_ADDR_REG, 0x000a }, { BRGPHY_MII_DSP_RW_PORT, 0x110b }, { BRGPHY_MII_TEST1, 0x0014 }, { BRGPHY_MII_AUXCTL, 0x0400 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_ber_bug(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { BRGPHY_MII_AUXCTL, 0x0c00 }, { BRGPHY_MII_DSP_ADDR_REG, 0x000a }, { BRGPHY_MII_DSP_RW_PORT, 0x310b }, { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, { BRGPHY_MII_DSP_RW_PORT, 0x9506 }, { BRGPHY_MII_DSP_ADDR_REG, 0x401f }, { BRGPHY_MII_DSP_RW_PORT, 0x14e2 }, { BRGPHY_MII_AUXCTL, 0x0400 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_crc_bug(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { BRGPHY_MII_DSP_RW_PORT, 0x0a75 }, { 0x1c, 0x8c68 }, { 0x1c, 0x8d68 }, { 0x1c, 0x8c68 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_jitter_bug(struct mii_softc *sc) { static const struct { int reg; uint16_t val; } dspcode[] = { { BRGPHY_MII_AUXCTL, 0x0c00 }, { BRGPHY_MII_DSP_ADDR_REG, 0x000a }, { BRGPHY_MII_DSP_RW_PORT, 0x010b }, { BRGPHY_MII_AUXCTL, 0x0400 }, { 0, 0 }, }; int i; for (i = 0; dspcode[i].reg != 0; i++) PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); } static void brgphy_fixup_disable_early_dac(struct mii_softc *sc) { uint32_t val; PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x0f08); val = PHY_READ(sc, BRGPHY_MII_DSP_RW_PORT); val &= ~(1 << 8); PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, val); } static void brgphy_ethernet_wirespeed(struct mii_softc *sc) { uint32_t val; /* Enable Ethernet@WireSpeed. */ PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007); val = PHY_READ(sc, BRGPHY_MII_AUXCTL); PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4)); } static void brgphy_jumbo_settings(struct mii_softc *sc, u_long mtu) { uint32_t val; /* Set or clear jumbo frame settings in the PHY. */ if (mtu > ETHER_MAX_LEN) { if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5401) { /* BCM5401 PHY cannot read-modify-write. */ PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20); } else { PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7); val = PHY_READ(sc, BRGPHY_MII_AUXCTL); PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | BRGPHY_AUXCTL_LONG_PKT); } val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL); PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, val | BRGPHY_PHY_EXTCTL_HIGH_LA); } else { PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7); val = PHY_READ(sc, BRGPHY_MII_AUXCTL); PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7)); val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL); PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, val & ~BRGPHY_PHY_EXTCTL_HIGH_LA); } } static void brgphy_reset(struct mii_softc *sc) { struct bge_softc *bge_sc = NULL; struct bce_softc *bce_sc = NULL; - if_t ifp; + u_int mtu; int i, val; /* * Perform a reset. Note that at least some Broadcom PHYs default to * being powered down as well as isolated after a reset but don't work * if one or both of these bits are cleared. However, they just work * fine if both bits remain set, so we don't use mii_phy_reset() here. */ PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET); /* Wait 100ms for it to complete. */ for (i = 0; i < 100; i++) { if ((PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_RESET) == 0) break; DELAY(1000); } /* Handle any PHY specific procedures following the reset. */ switch (sc->mii_mpd_oui) { case MII_OUI_BROADCOM: switch (sc->mii_mpd_model) { case MII_MODEL_BROADCOM_BCM5400: bcm5401_load_dspcode(sc); break; case MII_MODEL_BROADCOM_BCM5401: if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3) bcm5401_load_dspcode(sc); break; case MII_MODEL_BROADCOM_BCM5411: bcm5411_load_dspcode(sc); break; case MII_MODEL_BROADCOM_BCM54K2: bcm54k2_load_dspcode(sc); break; } break; case MII_OUI_BROADCOM3: switch (sc->mii_mpd_model) { case MII_MODEL_BROADCOM3_BCM5717C: case MII_MODEL_BROADCOM3_BCM5719C: case MII_MODEL_BROADCOM3_BCM5720C: case MII_MODEL_BROADCOM3_BCM57765: return; } break; case MII_OUI_BROADCOM4: return; } - ifp = sc->mii_pdata->mii_ifp; + mtu = MIIBUS_READVAR(sc->mii_dev, IF_MTU); /* Find the driver associated with this PHY. */ if (mii_phy_mac_match(sc, "bge")) bge_sc = mii_phy_mac_softc(sc); else if (mii_phy_mac_match(sc, "bce")) bce_sc = mii_phy_mac_softc(sc); if (bge_sc) { /* Fix up various bugs */ if (bge_sc->bge_phy_flags & BGE_PHY_5704_A0_BUG) brgphy_fixup_5704_a0_bug(sc); if (bge_sc->bge_phy_flags & BGE_PHY_ADC_BUG) brgphy_fixup_adc_bug(sc); if (bge_sc->bge_phy_flags & BGE_PHY_ADJUST_TRIM) brgphy_fixup_adjust_trim(sc); if (bge_sc->bge_phy_flags & BGE_PHY_BER_BUG) brgphy_fixup_ber_bug(sc); if (bge_sc->bge_phy_flags & BGE_PHY_CRC_BUG) brgphy_fixup_crc_bug(sc); if (bge_sc->bge_phy_flags & BGE_PHY_JITTER_BUG) brgphy_fixup_jitter_bug(sc); if (bge_sc->bge_flags & BGE_FLAG_JUMBO) - brgphy_jumbo_settings(sc, if_getmtu(ifp)); + brgphy_jumbo_settings(sc, mtu); if ((bge_sc->bge_phy_flags & BGE_PHY_NO_WIRESPEED) == 0) brgphy_ethernet_wirespeed(sc); /* Enable Link LED on Dell boxes */ if (bge_sc->bge_phy_flags & BGE_PHY_NO_3LED) { PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL) & ~BRGPHY_PHY_EXTCTL_3_LED); } /* Adjust output voltage (From Linux driver) */ if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906) PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12); } else if (bce_sc) { if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5708 && (bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) { /* Store autoneg capabilities/results in digital block (Page 0) */ PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG3_PG2); PHY_WRITE(sc, BRGPHY_5708S_PG2_DIGCTL_3_0, BRGPHY_5708S_PG2_DIGCTL_3_0_USE_IEEE); PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0); /* Enable fiber mode and autodetection */ PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL1, PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL1) | BRGPHY_5708S_PG0_1000X_CTL1_AUTODET_EN | BRGPHY_5708S_PG0_1000X_CTL1_FIBER_MODE); /* Enable parallel detection */ PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL2, PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL2) | BRGPHY_5708S_PG0_1000X_CTL2_PAR_DET_EN); /* Advertise 2.5G support through next page during autoneg */ if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG) PHY_WRITE(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1, PHY_READ(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1) | BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G); /* Increase TX signal amplitude */ if ((BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_A0) || (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B0) || (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B1)) { PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_TX_MISC_PG5); PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL1, PHY_READ(sc, BRGPHY_5708S_PG5_TXACTL1) & ~0x30); PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0); } /* Backplanes use special driver/pre-driver/pre-emphasis values. */ if ((bce_sc->bce_shared_hw_cfg & BCE_SHARED_HW_CFG_PHY_BACKPLANE) && (bce_sc->bce_port_hw_cfg & BCE_PORT_HW_CFG_CFG_TXCTL3_MASK)) { PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_TX_MISC_PG5); PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL3, bce_sc->bce_port_hw_cfg & BCE_PORT_HW_CFG_CFG_TXCTL3_MASK); PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0); } } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709 && (bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) { /* Select the SerDes Digital block of the AN MMD. */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_SERDES_DIG); val = PHY_READ(sc, BRGPHY_SERDES_DIG_1000X_CTL1); val &= ~BRGPHY_SD_DIG_1000X_CTL1_AUTODET; val |= BRGPHY_SD_DIG_1000X_CTL1_FIBER; PHY_WRITE(sc, BRGPHY_SERDES_DIG_1000X_CTL1, val); /* Select the Over 1G block of the AN MMD. */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_OVER_1G); /* Enable autoneg "Next Page" to advertise 2.5G support. */ val = PHY_READ(sc, BRGPHY_OVER_1G_UNFORMAT_PG1); if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG) val |= BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G; else val &= ~BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G; PHY_WRITE(sc, BRGPHY_OVER_1G_UNFORMAT_PG1, val); /* Select the Multi-Rate Backplane Ethernet block of the AN MMD. */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_MRBE); /* Enable MRBE speed autoneg. */ val = PHY_READ(sc, BRGPHY_MRBE_MSG_PG5_NP); val |= BRGPHY_MRBE_MSG_PG5_NP_MBRE | BRGPHY_MRBE_MSG_PG5_NP_T2; PHY_WRITE(sc, BRGPHY_MRBE_MSG_PG5_NP, val); /* Select the Clause 73 User B0 block of the AN MMD. */ PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_CL73_USER_B0); /* Enable MRBE speed autoneg. */ PHY_WRITE(sc, BRGPHY_CL73_USER_B0_MBRE_CTL1, BRGPHY_CL73_USER_B0_MBRE_CTL1_NP_AFT_BP | BRGPHY_CL73_USER_B0_MBRE_CTL1_STA_MGR | BRGPHY_CL73_USER_B0_MBRE_CTL1_ANEG); /* 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)) brgphy_fixup_disable_early_dac(sc); - brgphy_jumbo_settings(sc, if_getmtu(ifp)); + brgphy_jumbo_settings(sc, mtu); brgphy_ethernet_wirespeed(sc); } else { brgphy_fixup_ber_bug(sc); - brgphy_jumbo_settings(sc, if_getmtu(ifp)); + brgphy_jumbo_settings(sc, mtu); brgphy_ethernet_wirespeed(sc); } } } Index: projects/ifnet/sys/dev/mii/mii.c =================================================================== --- projects/ifnet/sys/dev/mii/mii.c (revision 277120) +++ projects/ifnet/sys/dev/mii/mii.c (revision 277121) @@ -1,677 +1,656 @@ /* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * MII bus layer, glues MII-capable network interface drivers to sharable * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, * plus some NetBSD extensions. */ #include #include #include #include #include #include #include -#include #include #include #include MODULE_VERSION(miibus, 1); #include "miibus_if.h" static device_attach_t miibus_attach; static bus_child_location_str_t miibus_child_location_str; static bus_child_pnpinfo_str_t miibus_child_pnpinfo_str; static device_detach_t miibus_detach; static bus_hinted_child_t miibus_hinted_child; static bus_print_child_t miibus_print_child; static device_probe_t miibus_probe; static bus_read_ivar_t miibus_read_ivar; static miibus_readreg_t miibus_readreg; static miibus_statchg_t miibus_statchg; static miibus_writereg_t miibus_writereg; static miibus_linkchg_t miibus_linkchg; +static miibus_readvar_t miibus_readvar; static miibus_mediainit_t miibus_mediainit; static unsigned char mii_bitreverse(unsigned char x); static device_method_t miibus_methods[] = { /* device interface */ DEVMETHOD(device_probe, miibus_probe), DEVMETHOD(device_attach, miibus_attach), DEVMETHOD(device_detach, miibus_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, miibus_print_child), DEVMETHOD(bus_read_ivar, miibus_read_ivar), DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, miibus_child_location_str), DEVMETHOD(bus_hinted_child, miibus_hinted_child), /* MII interface */ DEVMETHOD(miibus_readreg, miibus_readreg), DEVMETHOD(miibus_writereg, miibus_writereg), DEVMETHOD(miibus_statchg, miibus_statchg), DEVMETHOD(miibus_linkchg, miibus_linkchg), + DEVMETHOD(miibus_readvar, miibus_readvar), DEVMETHOD(miibus_mediainit, miibus_mediainit), DEVMETHOD_END }; devclass_t miibus_devclass; driver_t miibus_driver = { "miibus", miibus_methods, sizeof(struct mii_data) }; struct miibus_ivars { - if_t ifp; ifm_change_cb_t ifmedia_upd; ifm_stat_cb_t ifmedia_sts; u_int mii_flags; u_int mii_offset; }; static int miibus_probe(device_t dev) { device_set_desc(dev, "MII bus"); return (BUS_PROBE_SPECIFIC); } static int miibus_attach(device_t dev) { struct miibus_ivars *ivars; struct mii_attach_args *ma; struct mii_data *mii; device_t *children; int i, nchildren; mii = device_get_softc(dev); if (device_get_children(dev, &children, &nchildren) == 0) { for (i = 0; i < nchildren; i++) { ma = device_get_ivars(children[i]); ma->mii_data = mii; } free(children, M_TEMP); } if (nchildren == 0) { device_printf(dev, "cannot get children\n"); return (ENXIO); } ivars = device_get_ivars(dev); ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd, ivars->ifmedia_sts); - mii->mii_ifp = ivars->ifp; - if_setcapabilitiesbit(mii->mii_ifp, IFCAP_LINKSTATE, 0); - if_setcapenablebit(mii->mii_ifp, IFCAP_LINKSTATE, 0); LIST_INIT(&mii->mii_phys); return (bus_generic_attach(dev)); } static int miibus_detach(device_t dev) { struct mii_data *mii; bus_generic_detach(dev); mii = device_get_softc(dev); ifmedia_removeall(&mii->mii_media); - mii->mii_ifp = NULL; return (0); } static int miibus_print_child(device_t dev, device_t child) { struct mii_attach_args *ma; int retval; ma = device_get_ivars(child); retval = bus_print_child_header(dev, child); retval += printf(" PHY %d", ma->mii_phyno); retval += bus_print_child_footer(dev, child); return (retval); } static int miibus_read_ivar(device_t dev, device_t child __unused, int which, uintptr_t *result) { struct miibus_ivars *ivars; /* * NB: this uses the instance variables of the miibus rather than * its PHY children. */ ivars = device_get_ivars(dev); switch (which) { case MIIBUS_IVAR_FLAGS: *result = ivars->mii_flags; break; default: return (ENOENT); } return (0); } static int miibus_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf, size_t buflen) { struct mii_attach_args *ma; ma = device_get_ivars(child); snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); return (0); } static int miibus_child_location_str(device_t dev __unused, device_t child, char *buf, size_t buflen) { struct mii_attach_args *ma; ma = device_get_ivars(child); snprintf(buf, buflen, "phyno=%d", ma->mii_phyno); return (0); } static void miibus_hinted_child(device_t dev, const char *name, int unit) { struct miibus_ivars *ivars; struct mii_attach_args *args, *ma; device_t *children, phy; int i, nchildren; u_int val; if (resource_int_value(name, unit, "phyno", &val) != 0) return; if (device_get_children(dev, &children, &nchildren) != 0) return; ma = NULL; for (i = 0; i < nchildren; i++) { args = device_get_ivars(children[i]); if (args->mii_phyno == val) { ma = args; break; } } free(children, M_TEMP); /* * Don't add a PHY that was automatically identified by having media * in its BMSR twice, only allow to alter its attach arguments. */ if (ma == NULL) { ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF, M_NOWAIT); if (ma == NULL) return; phy = device_add_child(dev, name, unit); if (phy == NULL) { free(ma, M_DEVBUF); return; } ivars = device_get_ivars(dev); ma->mii_phyno = val; ma->mii_offset = ivars->mii_offset++; ma->mii_id1 = 0; ma->mii_id2 = 0; ma->mii_capmask = BMSR_DEFCAPMASK; device_set_ivars(phy, ma); } if (resource_int_value(name, unit, "id1", &val) == 0) ma->mii_id1 = val; if (resource_int_value(name, unit, "id2", &val) == 0) ma->mii_id2 = val; if (resource_int_value(name, unit, "capmask", &val) == 0) ma->mii_capmask = val; } +/* + * The miibus just relays most devmethods to the parent. + */ static int miibus_readreg(device_t dev, int phy, int reg) { - device_t parent; - parent = device_get_parent(dev); - return (MIIBUS_READREG(parent, phy, reg)); + return (MIIBUS_READREG(device_get_parent(dev), phy, reg)); } static int miibus_writereg(device_t dev, int phy, int reg, int data) { - device_t parent; - parent = device_get_parent(dev); - return (MIIBUS_WRITEREG(parent, phy, reg, data)); + return (MIIBUS_WRITEREG(device_get_parent(dev), phy, reg, data)); } static void miibus_statchg(device_t dev) { - device_t parent; - struct mii_data *mii; - parent = device_get_parent(dev); - MIIBUS_STATCHG(parent); - - mii = device_get_softc(dev); - if_setbaudrate(mii->mii_ifp, ifmedia_baudrate(mii->mii_media_active)); + MIIBUS_STATCHG(device_get_parent(dev)); } static void miibus_linkchg(device_t dev) { - struct mii_data *mii; - device_t parent; - int link_state; - parent = device_get_parent(dev); - MIIBUS_LINKCHG(parent); + MIIBUS_LINKCHG(device_get_parent(dev)); +} - mii = device_get_softc(dev); +static uint64_t +miibus_readvar(device_t dev, int var) +{ - if (mii->mii_media_status & IFM_AVALID) { - if (mii->mii_media_status & IFM_ACTIVE) - link_state = LINK_STATE_UP; - else - link_state = LINK_STATE_DOWN; - } else - link_state = LINK_STATE_UNKNOWN; - if_link_state_change(mii->mii_ifp, link_state); + return (MIIBUS_READVAR(device_get_parent(dev), var)); } static void miibus_mediainit(device_t dev) { struct mii_data *mii; struct ifmedia_entry *m; int media = 0; /* Poke the parent in case it has any media of its own to add. */ MIIBUS_MEDIAINIT(device_get_parent(dev)); mii = device_get_softc(dev); LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { media = m->ifm_media; if (media == (IFM_ETHER | IFM_AUTO)) break; } ifmedia_set(&mii->mii_media, media); } /* * Helper function used by network interface drivers, attaches the miibus and * the PHYs to the network interface driver parent. */ int -mii_attach(device_t dev, device_t *miibus, if_t ifp, - ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, - int phyloc, int offloc, int flags) +mii_attach(device_t dev, device_t *miibus, ifm_change_cb_t ifmedia_upd, + ifm_stat_cb_t ifmedia_sts, int capmask, int phyloc, int offloc, int flags) { struct miibus_ivars *ivars; struct mii_attach_args *args, ma; device_t *children, phy; int bmsr, first, i, nchildren, phymax, phymin, rv; uint32_t phymask; if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { printf("%s: phyloc and offloc specified\n", __func__); return (EINVAL); } if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { printf("%s: ivalid offloc %d\n", __func__, offloc); return (EINVAL); } if (phyloc == MII_PHY_ANY) { phymin = 0; phymax = MII_NPHY - 1; } else { if (phyloc < 0 || phyloc >= MII_NPHY) { printf("%s: ivalid phyloc %d\n", __func__, phyloc); return (EINVAL); } phymin = phymax = phyloc; } first = 0; if (*miibus == NULL) { first = 1; ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); if (ivars == NULL) return (ENOMEM); - ivars->ifp = ifp; ivars->ifmedia_upd = ifmedia_upd; ivars->ifmedia_sts = ifmedia_sts; ivars->mii_flags = flags; *miibus = device_add_child(dev, "miibus", -1); if (*miibus == NULL) { rv = ENXIO; goto fail; } device_set_ivars(*miibus, ivars); } else { ivars = device_get_ivars(*miibus); - if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || + if (ivars->ifmedia_upd != ifmedia_upd || ivars->ifmedia_sts != ifmedia_sts || ivars->mii_flags != flags) { printf("%s: non-matching invariant\n", __func__); return (EINVAL); } /* * Assignment of the attach arguments mii_data for the first * pass is done in miibus_attach(), i.e. once the miibus softc * has been allocated. */ ma.mii_data = device_get_softc(*miibus); } ma.mii_capmask = capmask; if (resource_int_value(device_get_name(*miibus), device_get_unit(*miibus), "phymask", &phymask) != 0) phymask = 0xffffffff; if (device_get_children(*miibus, &children, &nchildren) != 0) { children = NULL; nchildren = 0; } ivars->mii_offset = 0; for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { /* * Make sure we haven't already configured a PHY at this * address. This allows mii_attach() to be called * multiple times. */ for (i = 0; i < nchildren; i++) { args = device_get_ivars(children[i]); if (args->mii_phyno == ma.mii_phyno) { /* * Yes, there is already something * configured at this address. */ goto skip; } } /* * Check to see if there is a PHY at this address. Note, * many braindead PHYs report 0/0 in their ID registers, * so we test for media in the BMSR. */ bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR); if (bmsr == 0 || bmsr == 0xffff || (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { /* Assume no PHY at this address. */ continue; } /* * There is a PHY at this address. If we were given an * `offset' locator, skip this PHY if it doesn't match. */ if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset) goto skip; /* * Skip this PHY if it's not included in the phymask hint. */ if ((phymask & (1 << ma.mii_phyno)) == 0) goto skip; /* * Extract the IDs. Braindead PHYs will be handled by * the `ukphy' driver, as we have no ID information to * match on. */ ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1); ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2); ma.mii_offset = ivars->mii_offset; args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, M_NOWAIT); if (args == NULL) goto skip; bcopy((char *)&ma, (char *)args, sizeof(ma)); phy = device_add_child(*miibus, NULL, -1); if (phy == NULL) { free(args, M_DEVBUF); goto skip; } device_set_ivars(phy, args); skip: ivars->mii_offset++; } free(children, M_TEMP); if (first != 0) { rv = device_set_driver(*miibus, &miibus_driver); if (rv != 0) goto fail; bus_enumerate_hinted_children(*miibus); rv = device_get_children(*miibus, &children, &nchildren); if (rv != 0) goto fail; free(children, M_TEMP); if (nchildren == 0) { rv = ENXIO; goto fail; } rv = bus_generic_attach(dev); if (rv != 0) goto fail; /* Attaching of the PHY drivers is done in miibus_attach(). */ return (0); } rv = bus_generic_attach(*miibus); if (rv != 0) goto fail; return (0); fail: if (*miibus != NULL) device_delete_child(dev, *miibus); free(ivars, M_DEVBUF); if (first != 0) *miibus = NULL; return (rv); } /* * Media changed; notify all PHYs. */ int mii_mediachg(struct mii_data *mii) { struct mii_softc *child; struct ifmedia_entry *ife = mii->mii_media.ifm_cur; int rv; mii->mii_media_status = 0; mii->mii_media_active = IFM_NONE; LIST_FOREACH(child, &mii->mii_phys, mii_list) { /* * If the media indicates a different PHY instance, * isolate this one. */ if (IFM_INST(ife->ifm_media) != child->mii_inst) { if ((child->mii_flags & MIIF_NOISOLATE) != 0) { device_printf(child->mii_dev, "%s: " "can't handle non-zero PHY instance %d\n", __func__, child->mii_inst); continue; } PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | BMCR_ISO); continue; } rv = PHY_SERVICE(child, mii, MII_MEDIACHG); if (rv) return (rv); } return (0); } /* * Call the PHY tick routines, used during autonegotiation. */ void mii_tick(struct mii_data *mii) { struct mii_softc *child; struct ifmedia_entry *ife = mii->mii_media.ifm_cur; LIST_FOREACH(child, &mii->mii_phys, mii_list) { /* * If this PHY instance isn't currently selected, just skip * it. */ if (IFM_INST(ife->ifm_media) != child->mii_inst) continue; (void)PHY_SERVICE(child, mii, MII_TICK); } } /* * Get media status from PHYs. */ void mii_pollstat(struct mii_data *mii) { struct mii_softc *child; struct ifmedia_entry *ife = mii->mii_media.ifm_cur; mii->mii_media_status = 0; mii->mii_media_active = IFM_NONE; LIST_FOREACH(child, &mii->mii_phys, mii_list) { /* * If we're not polling this PHY instance, just skip it. */ if (IFM_INST(ife->ifm_media) != child->mii_inst) continue; (void)PHY_SERVICE(child, mii, MII_POLLSTAT); } } /* * Inform the PHYs that the interface is down. */ void mii_down(struct mii_data *mii) { struct mii_softc *child; LIST_FOREACH(child, &mii->mii_phys, mii_list) mii_phy_down(child); } static unsigned char mii_bitreverse(unsigned char x) { static unsigned const char nibbletab[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); } u_int mii_oui(u_int id1, u_int id2) { u_int h; h = (id1 << 6) | (id2 >> 10); return ((mii_bitreverse(h >> 16) << 16) | (mii_bitreverse((h >> 8) & 0xff) << 8) | mii_bitreverse(h & 0xff)); } int mii_phy_mac_match(struct mii_softc *mii, const char *name) { return (strcmp(device_get_name(device_get_parent(mii->mii_dev)), name) == 0); } int mii_dev_mac_match(device_t parent, const char *name) { return (strcmp(device_get_name(device_get_parent( device_get_parent(parent))), name) == 0); } void * mii_phy_mac_softc(struct mii_softc *mii) { return (device_get_softc(device_get_parent(mii->mii_dev))); } void * mii_dev_mac_softc(device_t parent) { return (device_get_softc(device_get_parent(device_get_parent(parent)))); } Index: projects/ifnet/sys/dev/mii/miibus_if.m =================================================================== --- projects/ifnet/sys/dev/mii/miibus_if.m (revision 277120) +++ projects/ifnet/sys/dev/mii/miibus_if.m (revision 277121) @@ -1,45 +1,53 @@ # $FreeBSD$ #include INTERFACE miibus; # # Read register from device on MII bus # METHOD int readreg { device_t dev; int phy; int reg; }; # # Write register to device on MII bus # METHOD int writereg { device_t dev; int phy; int reg; int val; }; # # Notify bus about PHY status change. # METHOD void statchg { device_t dev; }; # # Notify bus about PHY link change. # METHOD void linkchg { device_t dev; }; # +# Read software configuration data from device on MII bus. +# +METHOD uint64_t readvar { + device_t dev; + int var; +}; + +# # Notify bus that media has been set. # METHOD void mediainit { device_t dev; }; Index: projects/ifnet/sys/dev/mii/miivar.h =================================================================== --- projects/ifnet/sys/dev/mii/miivar.h (revision 277120) +++ projects/ifnet/sys/dev/mii/miivar.h (revision 277121) @@ -1,274 +1,272 @@ /* $NetBSD: miivar.h,v 1.8 1999/04/23 04:24:32 thorpej Exp $ */ /*- * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_MII_MIIVAR_H_ #define _DEV_MII_MIIVAR_H_ #include -#include /* XXX driver API temporary */ /* * Media Independent Interface data structure defintions */ struct mii_softc; /* * A network interface driver has one of these structures in its softc. * It is the interface from the network interface driver to the MII * layer. */ struct mii_data { struct ifmedia mii_media; /* media information */ - if_t mii_ifp; /* pointer back to network interface */ /* * For network interfaces with multiple PHYs, a list of all * PHYs is required so they can all be notified when a media * request is made. */ LIST_HEAD(mii_listhead, mii_softc) mii_phys; u_int mii_instance; /* * PHY driver fills this in with active media status. */ u_int mii_media_status; u_int mii_media_active; }; typedef struct mii_data mii_data_t; /* * Functions provided by the PHY to perform various functions. */ struct mii_phy_funcs { int (*pf_service)(struct mii_softc *, struct mii_data *, int); void (*pf_status)(struct mii_softc *); void (*pf_reset)(struct mii_softc *); }; /* * Requests that can be made to the downcall. */ #define MII_TICK 1 /* once-per-second tick */ #define MII_MEDIACHG 2 /* user changed media; perform the switch */ #define MII_POLLSTAT 3 /* user requested media status; fill it in */ /* * Each PHY driver's softc has one of these as the first member. * XXX This would be better named "phy_softc", but this is the name * XXX BSDI used, and we would like to have the same interface. */ struct mii_softc { device_t mii_dev; /* generic device glue */ LIST_ENTRY(mii_softc) mii_list; /* entry on parent's PHY list */ uint32_t mii_mpd_oui; /* the PHY's OUI (MII_OUI())*/ uint32_t mii_mpd_model; /* the PHY's model (MII_MODEL())*/ uint32_t mii_mpd_rev; /* the PHY's revision (MII_REV())*/ u_int mii_capmask; /* capability mask for BMSR */ u_int mii_phy; /* our MII address */ u_int mii_offset; /* first PHY, second PHY, etc. */ u_int mii_inst; /* instance for ifmedia */ /* Our PHY functions. */ const struct mii_phy_funcs *mii_funcs; struct mii_data *mii_pdata; /* pointer to parent's mii_data */ u_int mii_flags; /* misc. flags; see below */ u_int mii_capabilities; /* capabilities from BMSR */ u_int mii_extcapabilities; /* extended capabilities */ u_int mii_ticks; /* MII_TICK counter */ u_int mii_anegticks; /* ticks before retrying aneg */ u_int mii_media_active; /* last active media */ u_int mii_media_status; /* last active status */ }; typedef struct mii_softc mii_softc_t; /* mii_flags */ #define MIIF_INITDONE 0x00000001 /* has been initialized (mii_data) */ #define MIIF_NOISOLATE 0x00000002 /* do not isolate the PHY */ #if 0 #define MIIF_NOLOOP 0x00000004 /* no loopback capability */ #endif #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 */ #define MIIF_IS_1000X 0x00000080 /* is a 1000BASE-X device */ #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 advertisement */ #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 */ #define MIIF_PHYPRIV0 0x10000000 /* private to the PHY driver */ #define MIIF_PHYPRIV1 0x20000000 /* private to the PHY driver */ #define MIIF_PHYPRIV2 0x40000000 /* private to the PHY driver */ /* Default mii_anegticks values */ #define MII_ANEGTICKS 5 #define MII_ANEGTICKS_GIGE 17 #define MIIF_INHERIT_MASK (MIIF_NOISOLATE|MIIF_NOLOOP|MIIF_AUTOTSLEEP) /* * Special `locators' passed to mii_attach(). If one of these is not * an `any' value, we look for *that* PHY and configure it. If both * are not `any', that is an error, and mii_attach() will fail. */ #define MII_OFFSET_ANY -1 #define MII_PHY_ANY -1 /* * Used to attach a PHY to a parent. */ struct mii_attach_args { struct mii_data *mii_data; /* pointer to parent data */ u_int mii_phyno; /* MII address */ u_int mii_offset; /* first PHY, second PHY, etc. */ uint32_t mii_id1; /* PHY ID register 1 */ uint32_t mii_id2; /* PHY ID register 2 */ u_int mii_capmask; /* capability mask for BMSR */ }; typedef struct mii_attach_args mii_attach_args_t; /* * Used to match a PHY. */ struct mii_phydesc { uint32_t mpd_oui; /* the PHY's OUI */ uint32_t mpd_model; /* the PHY's model */ const char *mpd_name; /* the PHY's name */ }; #define MII_PHY_DESC(a, b) { MII_OUI_ ## a, MII_MODEL_ ## a ## _ ## b, \ MII_STR_ ## a ## _ ## b } #define MII_PHY_END { 0, 0, NULL } /* * An array of these structures map MII media types to BMCR/ANAR settings. */ struct mii_media { u_int mm_bmcr; /* BMCR settings for this media */ u_int mm_anar; /* ANAR settings for this media */ u_int mm_gtcr; /* 100base-T2 or 1000base-T CR */ }; #define MII_MEDIA_NONE 0 #define MII_MEDIA_10_T 1 #define MII_MEDIA_10_T_FDX 2 #define MII_MEDIA_100_T4 3 #define MII_MEDIA_100_TX 4 #define MII_MEDIA_100_TX_FDX 5 #define MII_MEDIA_1000_X 6 #define MII_MEDIA_1000_X_FDX 7 #define MII_MEDIA_1000_T 8 #define MII_MEDIA_1000_T_FDX 9 #define MII_NMEDIA 10 #ifdef _KERNEL #define PHY_READ(p, r) \ MIIBUS_READREG((p)->mii_dev, (p)->mii_phy, (r)) #define PHY_WRITE(p, r, v) \ MIIBUS_WRITEREG((p)->mii_dev, (p)->mii_phy, (r), (v)) #define PHY_SERVICE(p, d, o) \ (*(p)->mii_funcs->pf_service)((p), (d), (o)) #define PHY_STATUS(p) \ (*(p)->mii_funcs->pf_status)(p) #define PHY_RESET(p) \ (*(p)->mii_funcs->pf_reset)(p) enum miibus_device_ivars { MIIBUS_IVAR_FLAGS }; /* * Simplified accessors for miibus */ #define MIIBUS_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(miibus, var, MIIBUS, ivar, type) MIIBUS_ACCESSOR(flags, FLAGS, u_int) extern devclass_t miibus_devclass; extern driver_t miibus_driver; -int mii_attach(device_t, device_t *, if_t, ifm_change_cb_t, - ifm_stat_cb_t, int, int, int, int); +int mii_attach(device_t, device_t *, ifm_change_cb_t, ifm_stat_cb_t, + int, int, int, int); void mii_down(struct mii_data *); int mii_mediachg(struct mii_data *); void mii_tick(struct mii_data *); void mii_pollstat(struct mii_data *); 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); int mii_phy_tick(struct mii_softc *); int mii_phy_mac_match(struct mii_softc *, const char *); int mii_dev_mac_match(device_t, const char *); void *mii_phy_mac_softc(struct mii_softc *); void *mii_dev_mac_softc(device_t); const struct mii_phydesc * mii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd); const struct mii_phydesc * mii_phy_match_gen(const struct mii_attach_args *ma, const struct mii_phydesc *mpd, size_t endlen); int mii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv); void mii_phy_dev_attach(device_t dev, u_int flags, const struct mii_phy_funcs *mpf, int add_media); void ukphy_status(struct mii_softc *); u_int mii_oui(u_int, u_int); #define MII_OUI(id1, id2) mii_oui(id1, id2) #define MII_MODEL(id2) (((id2) & IDR2_MODEL) >> 4) #define MII_REV(id2) ((id2) & IDR2_REV) #endif /* _KERNEL */ #endif /* _DEV_MII_MIIVAR_H_ */ Index: projects/ifnet/sys/dev/mii/truephy.c =================================================================== --- projects/ifnet/sys/dev/mii/truephy.c (revision 277120) +++ projects/ifnet/sys/dev/mii/truephy.c (revision 277121) @@ -1,318 +1,315 @@ /*- * Copyright (c) 2007 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sepherosa Ziehau * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $DragonFly: src/sys/dev/netif/mii_layer/truephy.c,v 1.3 2008/02/10 07:29:27 sephe Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include -#include #include -#include #include -#include #include #include #include "miidevs.h" #include #include "miibus_if.h" #define TRUEPHY_FRAMELEN(mtu) \ (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + (mtu) + ETHER_CRC_LEN) static int truephy_service(struct mii_softc *, struct mii_data *, int); static int truephy_attach(device_t); static int truephy_probe(device_t); static void truephy_reset(struct mii_softc *); static void truephy_status(struct mii_softc *); static device_method_t truephy_methods[] = { /* device interface */ DEVMETHOD(device_probe, truephy_probe), DEVMETHOD(device_attach, truephy_attach), DEVMETHOD(device_detach, mii_phy_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static const struct mii_phydesc truephys[] = { MII_PHY_DESC(AGERE, ET1011), MII_PHY_DESC(AGERE, ET1011C), MII_PHY_END }; static devclass_t truephy_devclass; static driver_t truephy_driver = { "truephy", truephy_methods, sizeof(struct mii_softc) }; DRIVER_MODULE(truephy, miibus, truephy_driver, truephy_devclass, 0, 0); static const struct mii_phy_funcs truephy_funcs = { truephy_service, truephy_status, truephy_reset }; static const struct truephy_dsp { uint16_t index; uint16_t data; } truephy_dspcode[] = { { 0x880b, 0x0926 }, /* AfeIfCreg4B1000Msbs */ { 0x880c, 0x0926 }, /* AfeIfCreg4B100Msbs */ { 0x880d, 0x0926 }, /* AfeIfCreg4B10Msbs */ { 0x880e, 0xb4d3 }, /* AfeIfCreg4B1000Lsbs */ { 0x880f, 0xb4d3 }, /* AfeIfCreg4B100Lsbs */ { 0x8810, 0xb4d3 }, /* AfeIfCreg4B10Lsbs */ { 0x8805, 0xb03e }, /* AfeIfCreg3B1000Msbs */ { 0x8806, 0xb03e }, /* AfeIfCreg3B100Msbs */ { 0x8807, 0xff00 }, /* AfeIfCreg3B10Msbs */ { 0x8808, 0xe090 }, /* AfeIfCreg3B1000Lsbs */ { 0x8809, 0xe110 }, /* AfeIfCreg3B100Lsbs */ { 0x880a, 0x0000 }, /* AfeIfCreg3B10Lsbs */ { 0x300d, 1 }, /* DisableNorm */ { 0x280c, 0x0180 }, /* LinkHoldEnd */ { 0x1c21, 0x0002 }, /* AlphaM */ { 0x3821, 6 }, /* FfeLkgTx0 */ { 0x381d, 1 }, /* FfeLkg1g4 */ { 0x381e, 1 }, /* FfeLkg1g5 */ { 0x381f, 1 }, /* FfeLkg1g6 */ { 0x3820, 1 }, /* FfeLkg1g7 */ { 0x8402, 0x01f0 }, /* Btinact */ { 0x800e, 20 }, /* LftrainTime */ { 0x800f, 24 }, /* DvguardTime */ { 0x8010, 46 } /* IdlguardTime */ }; static int truephy_probe(device_t dev) { return (mii_phy_dev_probe(dev, truephys, BUS_PROBE_DEFAULT)); } static int truephy_attach(device_t dev) { struct mii_softc *sc; sc = device_get_softc(dev); mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, &truephy_funcs, 0); PHY_RESET(sc); sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; if (sc->mii_capabilities & BMSR_EXTSTAT) { sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); /* No 1000baseT half-duplex support */ sc->mii_extcapabilities &= ~EXTSR_1000THDX; } device_printf(dev, " "); mii_phy_add_media(sc); printf("\n"); MIIBUS_MEDIAINIT(sc->mii_dev); return (0); } static int truephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; int bmcr; switch (cmd) { case MII_POLLSTAT: break; case MII_MEDIACHG: if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_AUTOEN; PHY_WRITE(sc, MII_BMCR, bmcr); PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_PDOWN); } mii_phy_setmedia(sc); if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_PDOWN; PHY_WRITE(sc, MII_BMCR, bmcr); if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG); } } break; case MII_TICK: if (mii_phy_tick(sc) == EJUSTRETURN) return (0); break; } /* Update the media status. */ PHY_STATUS(sc); /* Callback if something changed. */ mii_phy_update(sc, cmd); return (0); } static void truephy_reset(struct mii_softc *sc) { int i; if (sc->mii_mpd_model == MII_MODEL_AGERE_ET1011) { mii_phy_reset(sc); return; } for (i = 0; i < 2; ++i) { PHY_READ(sc, MII_PHYIDR1); PHY_READ(sc, MII_PHYIDR2); PHY_READ(sc, TRUEPHY_CTRL); PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_DIAG | TRUEPHY_CTRL_RSV1); PHY_WRITE(sc, TRUEPHY_INDEX, TRUEPHY_INDEX_MAGIC); PHY_READ(sc, TRUEPHY_DATA); PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_RSV1); } PHY_READ(sc, MII_BMCR); PHY_READ(sc, TRUEPHY_CTRL); PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_PDOWN | BMCR_S1000); PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_DIAG | TRUEPHY_CTRL_RSV1 | TRUEPHY_CTRL_RSV0); for (i = 0; i < nitems(truephy_dspcode); ++i) { const struct truephy_dsp *dsp = &truephy_dspcode[i]; PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); PHY_WRITE(sc, TRUEPHY_DATA, dsp->data); PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); PHY_READ(sc, TRUEPHY_DATA); } PHY_READ(sc, MII_BMCR); PHY_READ(sc, TRUEPHY_CTRL); PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_S1000); PHY_WRITE(sc, TRUEPHY_CTRL, TRUEPHY_CTRL_RSV1); mii_phy_reset(sc); - if (TRUEPHY_FRAMELEN((if_getmtu(sc->mii_pdata->mii_ifp)) > 2048)) { + if (TRUEPHY_FRAMELEN((MIIBUS_READVAR(sc->mii_dev, IF_MTU)) > 2048)) { int conf; conf = PHY_READ(sc, TRUEPHY_CONF); conf &= ~TRUEPHY_CONF_TXFIFO_MASK; conf |= TRUEPHY_CONF_TXFIFO_24; PHY_WRITE(sc, TRUEPHY_CONF, conf); } } static void truephy_status(struct mii_softc *sc) { struct mii_data *mii = sc->mii_pdata; int bmsr, bmcr, sr; mii->mii_media_status = IFM_AVALID; mii->mii_media_active = IFM_ETHER; sr = PHY_READ(sc, TRUEPHY_SR); bmcr = PHY_READ(sc, MII_BMCR); bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); if (bmsr & BMSR_LINK) mii->mii_media_status |= IFM_ACTIVE; if (bmcr & BMCR_AUTOEN) { if ((bmsr & BMSR_ACOMP) == 0) { mii->mii_media_active |= IFM_NONE; return; } } switch (sr & TRUEPHY_SR_SPD_MASK) { case TRUEPHY_SR_SPD_1000T: mii->mii_media_active |= IFM_1000_T; break; case TRUEPHY_SR_SPD_100TX: mii->mii_media_active |= IFM_100_TX; break; case TRUEPHY_SR_SPD_10T: mii->mii_media_active |= IFM_10_T; break; default: /* XXX will this ever happen? */ printf("invalid media SR %#x\n", sr); mii->mii_media_active |= IFM_NONE; return; } if (sr & TRUEPHY_SR_FDX) mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); else mii->mii_media_active |= IFM_HDX; }