diff options
author | ps <ps@FreeBSD.org> | 2003-05-03 19:06:50 +0000 |
---|---|---|
committer | ps <ps@FreeBSD.org> | 2003-05-03 19:06:50 +0000 |
commit | f6b6404d7f64bbdd5d2288c71cce10b084ac1090 (patch) | |
tree | 17617e195afebb5b466aea278f14db480648dab7 /sys/dev | |
parent | 19105c8312c841631a5eff3408f8a43f4958e9ba (diff) | |
download | FreeBSD-src-f6b6404d7f64bbdd5d2288c71cce10b084ac1090.zip FreeBSD-src-f6b6404d7f64bbdd5d2288c71cce10b084ac1090.tar.gz |
- Move bge_phy_hack into the phy code and implement the various DSP
patch workarounds for each phy revision.
Obtained from: NetBSD & Broadcom Linux driver
- Disable AUTOPOLL when accessing the PHY as it may cause PCI errors.
Obtained from: NetBSD
- Check the UPDATED bit in the status block so the driver knows
that the status block as indeed changed since the last access.
Broadcom documentation states drivers should unset the UPDATED/CHANGED
bits after reading them.
- When changing media types, first loop the phy then set the media.
Broadcom documentation and Linux drivers do this and I observed
much better handling of link after this change.
- Broadcom documentation states that for 1000BaseT operation,
autonegotiation must be enabled. Fix hard coding of media so that
the driver only advertises 1000BaseT as the supported media type
and enable autonegotition.
- Only set Master/Slave on the 5701.
Obtained from Broadcom Linux driver.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/bge/if_bge.c | 87 | ||||
-rw-r--r-- | sys/dev/mii/brgphy.c | 181 |
2 files changed, 198 insertions, 70 deletions
diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c index 3eb7b60..186726f 100644 --- a/sys/dev/bge/if_bge.c +++ b/sys/dev/bge/if_bge.c @@ -218,7 +218,6 @@ static int bge_miibus_writereg (device_t, int, int, int); static void bge_miibus_statchg (device_t); static void bge_reset (struct bge_softc *); -static void bge_phy_hack (struct bge_softc *); static device_method_t bge_methods[] = { /* Device interface */ @@ -477,7 +476,7 @@ bge_miibus_readreg(dev, phy, reg) { struct bge_softc *sc; struct ifnet *ifp; - u_int32_t val; + u_int32_t val, autopoll; int i; sc = device_get_softc(dev); @@ -491,6 +490,13 @@ bge_miibus_readreg(dev, phy, reg) return(0); } + /* Reading with autopolling on may trigger PCI errors */ + autopoll = CSR_READ_4(sc, BGE_MI_MODE); + if (autopoll & BGE_MIMODE_AUTOPOLL) { + BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); + DELAY(40); + } + CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_READ|BGE_MICOMM_BUSY| BGE_MIPHY(phy)|BGE_MIREG(reg)); @@ -502,11 +508,18 @@ bge_miibus_readreg(dev, phy, reg) if (i == BGE_TIMEOUT) { printf("bge%d: PHY read timed out\n", sc->bge_unit); - return(0); + val = 0; + goto done; } val = CSR_READ_4(sc, BGE_MI_COMM); +done: + if (autopoll & BGE_MIMODE_AUTOPOLL) { + BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); + DELAY(40); + } + if (val & BGE_MICOMM_READFAIL) return(0); @@ -519,10 +532,18 @@ bge_miibus_writereg(dev, phy, reg, val) int phy, reg, val; { struct bge_softc *sc; + u_int32_t autopoll; int i; sc = device_get_softc(dev); + /* Reading with autopolling on may trigger PCI errors */ + autopoll = CSR_READ_4(sc, BGE_MI_MODE); + if (autopoll & BGE_MIMODE_AUTOPOLL) { + BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); + DELAY(40); + } + CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE|BGE_MICOMM_BUSY| BGE_MIPHY(phy)|BGE_MIREG(reg)|val); @@ -531,6 +552,11 @@ bge_miibus_writereg(dev, phy, reg, val) break; } + if (autopoll & BGE_MIMODE_AUTOPOLL) { + BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); + DELAY(40); + } + if (i == BGE_TIMEOUT) { printf("bge%d: PHY read timed out\n", sc->bge_unit); return(0); @@ -562,8 +588,6 @@ bge_miibus_statchg(dev) BGE_SETBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX); } - bge_phy_hack(sc); - return; } @@ -2029,14 +2053,20 @@ bge_intr(xsc) BRGPHY_INTRS); } } else { - if (sc->bge_rdata->bge_status_block.bge_status & - BGE_STATFLAG_LINKSTATE_CHANGED) { + if ((sc->bge_rdata->bge_status_block.bge_status & + BGE_STATFLAG_UPDATED) && + (sc->bge_rdata->bge_status_block.bge_status & + BGE_STATFLAG_LINKSTATE_CHANGED)) { + sc->bge_rdata->bge_status_block.bge_status &= ~(BGE_STATFLAG_UPDATED|BGE_STATFLAG_LINKSTATE_CHANGED); sc->bge_link = 0; untimeout(bge_tick, sc, sc->bge_stat_ch); bge_tick(sc); /* Clear the interrupt */ CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED| BGE_MACSTAT_CFG_CHANGED); + + /* Force flush the status block cached by PCI bridge */ + CSR_READ_4(sc, BGE_MBX_IRQ0_LO); } } @@ -2295,48 +2325,6 @@ bge_start(ifp) return; } -/* - * If we have a BCM5400 or BCM5401 PHY, we need to properly - * program its internal DSP. Failing to do this can result in - * massive packet loss at 1Gb speeds. - */ -static void -bge_phy_hack(sc) - struct bge_softc *sc; -{ - struct bge_bcom_hack bhack[] = { - { BRGPHY_MII_AUXCTL, 0x4C20 }, - { 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 } }; - u_int16_t vid, did; - int i; - - vid = bge_miibus_readreg(sc->bge_dev, 1, MII_PHYIDR1); - did = bge_miibus_readreg(sc->bge_dev, 1, MII_PHYIDR2); - - if (MII_OUI(vid, did) == MII_OUI_xxBROADCOM && - (MII_MODEL(did) == MII_MODEL_xxBROADCOM_BCM5400 || - MII_MODEL(did) == MII_MODEL_xxBROADCOM_BCM5401)) { - i = 0; - while(bhack[i].reg) { - bge_miibus_writereg(sc->bge_dev, 1, bhack[i].reg, - bhack[i].val); - i++; - } - } - - return; -} - static void bge_init(xsc) void *xsc; @@ -2474,7 +2462,6 @@ bge_ifmedia_upd(ifp) miisc = LIST_NEXT(miisc, mii_list)) mii_phy_reset(miisc); } - bge_phy_hack(sc); mii_mediachg(mii); return(0); diff --git a/sys/dev/mii/brgphy.c b/sys/dev/mii/brgphy.c index 49c4601..7f42688 100644 --- a/sys/dev/mii/brgphy.c +++ b/sys/dev/mii/brgphy.c @@ -82,6 +82,12 @@ DRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0); static int brgphy_service(struct mii_softc *, struct mii_data *, int); static void brgphy_status(struct mii_softc *); static int brgphy_mii_phy_auto(struct mii_softc *); +static void brgphy_reset(struct mii_softc *); +static void brgphy_loop(struct mii_softc *); +static void bcm5401_load_dspcode(struct mii_softc *); +static void bcm5411_load_dspcode(struct mii_softc *); +static void bcm5703_load_dspcode(struct mii_softc *); +static int brgphy_mii_model; static int brgphy_probe(dev) @@ -163,7 +169,8 @@ brgphy_attach(dev) BMCR_LOOP|BMCR_S100); #endif - mii_phy_reset(sc); + brgphy_mii_model = MII_MODEL(ma->mii_id2); + brgphy_reset(sc); sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; @@ -193,7 +200,7 @@ brgphy_service(sc, mii, cmd) int cmd; { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; - int reg, speed; + int reg, speed, gig; switch (cmd) { case MII_POLLSTAT: @@ -221,11 +228,7 @@ brgphy_service(sc, mii, cmd) if ((mii->mii_ifp->if_flags & IFF_UP) == 0) break; - PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, - BRGPHY_PHY_EXTCTL_HIGH_LA|BRGPHY_PHY_EXTCTL_EN_LTR); - PHY_WRITE(sc, BRGPHY_MII_AUXCTL, - BRGPHY_AUXCTL_LONG_PKT|BRGPHY_AUXCTL_TX_TST); - PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00); + brgphy_reset(sc); /* XXX hardware bug work-around */ switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: @@ -247,15 +250,26 @@ brgphy_service(sc, mii, cmd) case IFM_10_T: speed = BRGPHY_S10; setit: + brgphy_loop(sc); if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { - PHY_WRITE(sc, BRGPHY_MII_BMCR, - BRGPHY_BMCR_FDX|speed); + speed |= BRGPHY_BMCR_FDX; + gig = BRGPHY_1000CTL_AFD; } else { - PHY_WRITE(sc, BRGPHY_MII_BMCR, speed); + gig = BRGPHY_1000CTL_AHD; } + + PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0); + PHY_WRITE(sc, BRGPHY_MII_BMCR, speed); PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE); - if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) + if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) + break; + + PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig); + PHY_WRITE(sc, BRGPHY_MII_BMCR, + speed|BRGPHY_BMCR_AUTOEN|BRGPHY_BMCR_STARTNEG); + + if (brgphy_mii_model != MII_MODEL_xxBROADCOM_BCM5701) break; /* @@ -268,10 +282,10 @@ setit: */ if ((mii->mii_ifp->if_flags & IFF_LINK0)) { PHY_WRITE(sc, BRGPHY_MII_1000CTL, - BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC); + gig|BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC); } else { PHY_WRITE(sc, BRGPHY_MII_1000CTL, - BRGPHY_1000CTL_MSE); + gig|BRGPHY_1000CTL_MSE); } break; #ifdef foo @@ -320,7 +334,6 @@ setit: return (0); sc->mii_ticks = 0; - mii_phy_reset(sc); brgphy_mii_phy_auto(sc); return (0); } @@ -402,12 +415,12 @@ brgphy_mii_phy_auto(mii) { int ktcr = 0; - mii_phy_reset(mii); - PHY_WRITE(mii, BRGPHY_MII_BMCR, 0); - DELAY(1000); - ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL); - PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr | - BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD); + brgphy_loop(mii); + brgphy_reset(mii); + ktcr = BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD; + if (brgphy_mii_model == MII_MODEL_xxBROADCOM_BCM5701) + ktcr |= BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC; + PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr); ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL); DELAY(1000); PHY_WRITE(mii, BRGPHY_MII_ANAR, @@ -418,3 +431,131 @@ brgphy_mii_phy_auto(mii) PHY_WRITE(mii, BRGPHY_MII_IMR, 0xFF00); return (EJUSTRETURN); } + +static void +brgphy_loop(struct mii_softc *sc) +{ + u_int32_t bmsr; + int i; + + PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP); + for (i = 0; i < 15000; i++) { + bmsr = PHY_READ(sc, BRGPHY_MII_BMSR); + if (!(bmsr & BRGPHY_BMSR_LINK)) { +#if 0 + device_printf(sc->mii_dev, "looped %d\n", i); +#endif + 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); +} + +static void +bcm5703_load_dspcode(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 +bcm5704_load_dspcode(struct mii_softc *sc) +{ + static const struct { + int reg; + u_int16_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_reset(struct mii_softc *sc) +{ + u_int32_t val; + + mii_phy_reset(sc); + + switch (brgphy_mii_model) { + case MII_MODEL_xxBROADCOM_BCM5401: + bcm5401_load_dspcode(sc); + break; + case MII_MODEL_xxBROADCOM_BCM5411: + bcm5411_load_dspcode(sc); + break; + case MII_MODEL_xxBROADCOM_BCM5703: + bcm5703_load_dspcode(sc); + break; + case MII_MODEL_xxBROADCOM_BCM5704: + bcm5704_load_dspcode(sc); + break; + } + + /* 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)); +} |