1098ca2bdSWarner Losh /*- 29bac70b8SBill Paul * Copyright (c) 2003 39bac70b8SBill Paul * Bill Paul <wpaul@windriver.com>. All rights reserved. 49bac70b8SBill Paul * 59bac70b8SBill Paul * Redistribution and use in source and binary forms, with or without 69bac70b8SBill Paul * modification, are permitted provided that the following conditions 79bac70b8SBill Paul * are met: 89bac70b8SBill Paul * 1. Redistributions of source code must retain the above copyright 99bac70b8SBill Paul * notice, this list of conditions and the following disclaimer. 109bac70b8SBill Paul * 2. Redistributions in binary form must reproduce the above copyright 119bac70b8SBill Paul * notice, this list of conditions and the following disclaimer in the 129bac70b8SBill Paul * documentation and/or other materials provided with the distribution. 139bac70b8SBill Paul * 3. All advertising materials mentioning features or use of this software 149bac70b8SBill Paul * must display the following acknowledgement: 159bac70b8SBill Paul * This product includes software developed by Bill Paul. 169bac70b8SBill Paul * 4. Neither the name of the author nor the names of any co-contributors 179bac70b8SBill Paul * may be used to endorse or promote products derived from this software 189bac70b8SBill Paul * without specific prior written permission. 199bac70b8SBill Paul * 209bac70b8SBill Paul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 219bac70b8SBill Paul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 229bac70b8SBill Paul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 239bac70b8SBill Paul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 249bac70b8SBill Paul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 259bac70b8SBill Paul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 269bac70b8SBill Paul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 279bac70b8SBill Paul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 289bac70b8SBill Paul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 299bac70b8SBill Paul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 309bac70b8SBill Paul * THE POSSIBILITY OF SUCH DAMAGE. 319bac70b8SBill Paul */ 329bac70b8SBill Paul 3350aa1061SMarius Strobl #include <sys/cdefs.h> 3450aa1061SMarius Strobl __FBSDID("$FreeBSD$"); 3550aa1061SMarius Strobl 369bac70b8SBill Paul /* 37c06cddfeSPyun YongHyeon * Driver for the RealTek 8169S/8110S/8211B/8211C internal 10/100/1000 PHY. 389bac70b8SBill Paul */ 399bac70b8SBill Paul 409bac70b8SBill Paul #include <sys/param.h> 419bac70b8SBill Paul #include <sys/systm.h> 429bac70b8SBill Paul #include <sys/kernel.h> 4341ee9f1cSPoul-Henning Kamp #include <sys/module.h> 449bac70b8SBill Paul #include <sys/socket.h> 4562d76917SMarcel Moolenaar #include <sys/taskqueue.h> 469bac70b8SBill Paul #include <sys/bus.h> 479bac70b8SBill Paul 489bac70b8SBill Paul #include <net/if.h> 4961a3ac6eSGleb Smirnoff #include <net/if_var.h> 509bac70b8SBill Paul #include <net/if_arp.h> 519bac70b8SBill Paul #include <net/if_media.h> 529bac70b8SBill Paul 539bac70b8SBill Paul #include <dev/mii/mii.h> 549bac70b8SBill Paul #include <dev/mii/miivar.h> 559bac70b8SBill Paul #include "miidevs.h" 569bac70b8SBill Paul 579bac70b8SBill Paul #include <dev/mii/rgephyreg.h> 589bac70b8SBill Paul 599bac70b8SBill Paul #include "miibus_if.h" 609bac70b8SBill Paul 619bac70b8SBill Paul #include <machine/bus.h> 62b2d3d26fSGleb Smirnoff #include <dev/rl/if_rlreg.h> 639bac70b8SBill Paul 649bac70b8SBill Paul static int rgephy_probe(device_t); 659bac70b8SBill Paul static int rgephy_attach(device_t); 669bac70b8SBill Paul 679bac70b8SBill Paul static device_method_t rgephy_methods[] = { 689bac70b8SBill Paul /* device interface */ 699bac70b8SBill Paul DEVMETHOD(device_probe, rgephy_probe), 709bac70b8SBill Paul DEVMETHOD(device_attach, rgephy_attach), 719bac70b8SBill Paul DEVMETHOD(device_detach, mii_phy_detach), 729bac70b8SBill Paul DEVMETHOD(device_shutdown, bus_generic_shutdown), 73604f5f1fSMarius Strobl DEVMETHOD_END 749bac70b8SBill Paul }; 759bac70b8SBill Paul 769bac70b8SBill Paul static devclass_t rgephy_devclass; 779bac70b8SBill Paul 789bac70b8SBill Paul static driver_t rgephy_driver = { 799bac70b8SBill Paul "rgephy", 809bac70b8SBill Paul rgephy_methods, 813fcb7a53SMarius Strobl sizeof(struct mii_softc) 829bac70b8SBill Paul }; 839bac70b8SBill Paul 849bac70b8SBill Paul DRIVER_MODULE(rgephy, miibus, rgephy_driver, rgephy_devclass, 0, 0); 859bac70b8SBill Paul 869bac70b8SBill Paul static int rgephy_service(struct mii_softc *, struct mii_data *, int); 879bac70b8SBill Paul static void rgephy_status(struct mii_softc *); 88991ab941SMarius Strobl static int rgephy_mii_phy_auto(struct mii_softc *, int); 899bac70b8SBill Paul static void rgephy_reset(struct mii_softc *); 9072200a8aSPyun YongHyeon static int rgephy_linkup(struct mii_softc *); 919bac70b8SBill Paul static void rgephy_loop(struct mii_softc *); 929bac70b8SBill Paul static void rgephy_load_dspcode(struct mii_softc *); 939bac70b8SBill Paul 94a35b9333SMarius Strobl static const struct mii_phydesc rgephys[] = { 953fcb7a53SMarius Strobl MII_PHY_DESC(REALTEK, RTL8169S), 96cd10b400SPyun YongHyeon MII_PHY_DESC(REALTEK, RTL8251), 97a35b9333SMarius Strobl MII_PHY_END 98a35b9333SMarius Strobl }; 99a35b9333SMarius Strobl 1003fcb7a53SMarius Strobl static const struct mii_phy_funcs rgephy_funcs = { 1013fcb7a53SMarius Strobl rgephy_service, 1023fcb7a53SMarius Strobl rgephy_status, 1033fcb7a53SMarius Strobl rgephy_reset 1043fcb7a53SMarius Strobl }; 1053fcb7a53SMarius Strobl 1069bac70b8SBill Paul static int 1077d830ac9SWarner Losh rgephy_probe(device_t dev) 1089bac70b8SBill Paul { 1099bac70b8SBill Paul 110a35b9333SMarius Strobl return (mii_phy_dev_probe(dev, rgephys, BUS_PROBE_DEFAULT)); 1119bac70b8SBill Paul } 1129bac70b8SBill Paul 1139bac70b8SBill Paul static int 1147d830ac9SWarner Losh rgephy_attach(device_t dev) 1159bac70b8SBill Paul { 1169bac70b8SBill Paul struct mii_softc *sc; 117fed3ed71SPyun YongHyeon u_int flags; 1189bac70b8SBill Paul 1193fcb7a53SMarius Strobl sc = device_get_softc(dev); 120fed3ed71SPyun YongHyeon flags = 0; 1217e310d2dSGleb Smirnoff if (mii_dev_mac_match(dev, "re")) 122fed3ed71SPyun YongHyeon flags |= MIIF_PHYPRIV0; 123fed3ed71SPyun YongHyeon mii_phy_dev_attach(dev, flags, &rgephy_funcs, 0); 1249bac70b8SBill Paul 125991ab941SMarius Strobl /* RTL8169S do not report auto-sense; add manually. */ 126991ab941SMarius Strobl sc->mii_capabilities = (PHY_READ(sc, MII_BMSR) | BMSR_ANEG) & 1273fcb7a53SMarius Strobl sc->mii_capmask; 128b91805b4SMarius Strobl if (sc->mii_capabilities & BMSR_EXTSTAT) 129b91805b4SMarius Strobl sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 1309bac70b8SBill Paul device_printf(dev, " "); 131ba76315fSMarius Strobl mii_phy_add_media(sc); 1329bac70b8SBill Paul printf("\n"); 13339bb2e52SMarius Strobl /* 13439bb2e52SMarius Strobl * Allow IFM_FLAG0 to be set indicating that auto-negotiation with 13539bb2e52SMarius Strobl * manual configuration, which is used to work around issues with 13639bb2e52SMarius Strobl * certain setups by default, should not be triggered as it may in 13739bb2e52SMarius Strobl * turn cause harm in some edge cases. 13839bb2e52SMarius Strobl */ 1393fcb7a53SMarius Strobl sc->mii_pdata->mii_media.ifm_mask |= IFM_FLAG0; 1409bac70b8SBill Paul 1413fcb7a53SMarius Strobl PHY_RESET(sc); 1423fcb7a53SMarius Strobl 1439bac70b8SBill Paul MIIBUS_MEDIAINIT(sc->mii_dev); 1449bac70b8SBill Paul return (0); 1459bac70b8SBill Paul } 1469bac70b8SBill Paul 1479bac70b8SBill Paul static int 1487d830ac9SWarner Losh rgephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 1499bac70b8SBill Paul { 1509bac70b8SBill Paul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 15172200a8aSPyun YongHyeon int speed, gig, anar; 1529bac70b8SBill Paul 1539bac70b8SBill Paul switch (cmd) { 1549bac70b8SBill Paul case MII_POLLSTAT: 1559bac70b8SBill Paul break; 1569bac70b8SBill Paul 1579bac70b8SBill Paul case MII_MEDIACHG: 1583fcb7a53SMarius Strobl PHY_RESET(sc); /* XXX hardware bug work-around */ 1599bac70b8SBill Paul 1604eb561d2SPyun YongHyeon anar = PHY_READ(sc, RGEPHY_MII_ANAR); 161991ab941SMarius Strobl anar &= ~(RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP | 162991ab941SMarius Strobl RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX | 1634eb561d2SPyun YongHyeon RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10); 1644eb561d2SPyun YongHyeon 1659bac70b8SBill Paul switch (IFM_SUBTYPE(ife->ifm_media)) { 1669bac70b8SBill Paul case IFM_AUTO: 1679bac70b8SBill Paul #ifdef foo 1689bac70b8SBill Paul /* 1699bac70b8SBill Paul * If we're already in auto mode, just return. 1709bac70b8SBill Paul */ 1719bac70b8SBill Paul if (PHY_READ(sc, RGEPHY_MII_BMCR) & RGEPHY_BMCR_AUTOEN) 1729bac70b8SBill Paul return (0); 1739bac70b8SBill Paul #endif 174991ab941SMarius Strobl (void)rgephy_mii_phy_auto(sc, ife->ifm_media); 1759bac70b8SBill Paul break; 1769bac70b8SBill Paul case IFM_1000_T: 1779bac70b8SBill Paul speed = RGEPHY_S1000; 1789bac70b8SBill Paul goto setit; 1799bac70b8SBill Paul case IFM_100_TX: 1809bac70b8SBill Paul speed = RGEPHY_S100; 1814eb561d2SPyun YongHyeon anar |= RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX; 1829bac70b8SBill Paul goto setit; 1839bac70b8SBill Paul case IFM_10_T: 1849bac70b8SBill Paul speed = RGEPHY_S10; 1854eb561d2SPyun YongHyeon anar |= RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10; 1869bac70b8SBill Paul setit: 18739bb2e52SMarius Strobl if ((ife->ifm_media & IFM_FLOW) != 0 && 18839bb2e52SMarius Strobl (mii->mii_media.ifm_media & IFM_FLAG0) != 0) 18939bb2e52SMarius Strobl return (EINVAL); 19039bb2e52SMarius Strobl 19139bb2e52SMarius Strobl if ((ife->ifm_media & IFM_FDX) != 0) { 1929bac70b8SBill Paul speed |= RGEPHY_BMCR_FDX; 1939bac70b8SBill Paul gig = RGEPHY_1000CTL_AFD; 1944eb561d2SPyun YongHyeon anar &= ~(RGEPHY_ANAR_TX | RGEPHY_ANAR_10); 19539bb2e52SMarius Strobl if ((ife->ifm_media & IFM_FLOW) != 0 || 19639bb2e52SMarius Strobl (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 19739bb2e52SMarius Strobl anar |= 19839bb2e52SMarius Strobl RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP; 1999bac70b8SBill Paul } else { 2009bac70b8SBill Paul gig = RGEPHY_1000CTL_AHD; 2014eb561d2SPyun YongHyeon anar &= 2024eb561d2SPyun YongHyeon ~(RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_10_FD); 2039bac70b8SBill Paul } 20439bb2e52SMarius Strobl if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 205991ab941SMarius Strobl gig |= RGEPHY_1000CTL_MSE; 206991ab941SMarius Strobl if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 207991ab941SMarius Strobl gig |= RGEPHY_1000CTL_MSC; 20839bb2e52SMarius Strobl } else { 20939bb2e52SMarius Strobl gig = 0; 21039bb2e52SMarius Strobl anar &= ~RGEPHY_ANAR_ASP; 21139bb2e52SMarius Strobl } 21239bb2e52SMarius Strobl if ((mii->mii_media.ifm_media & IFM_FLAG0) == 0) 21339bb2e52SMarius Strobl speed |= 21439bb2e52SMarius Strobl RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG; 21539bb2e52SMarius Strobl rgephy_loop(sc); 216991ab941SMarius Strobl PHY_WRITE(sc, RGEPHY_MII_1000CTL, gig); 217991ab941SMarius Strobl PHY_WRITE(sc, RGEPHY_MII_ANAR, anar); 21839bb2e52SMarius Strobl PHY_WRITE(sc, RGEPHY_MII_BMCR, speed); 2199bac70b8SBill Paul break; 2209bac70b8SBill Paul case IFM_NONE: 2219bac70b8SBill Paul PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN); 2229bac70b8SBill Paul break; 2239bac70b8SBill Paul default: 2249bac70b8SBill Paul return (EINVAL); 2259bac70b8SBill Paul } 2269bac70b8SBill Paul break; 2279bac70b8SBill Paul 2289bac70b8SBill Paul case MII_TICK: 2299bac70b8SBill Paul /* 2309bac70b8SBill Paul * Only used for autonegotiation. 2319bac70b8SBill Paul */ 23237fd5f0fSPyun YongHyeon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 23337fd5f0fSPyun YongHyeon sc->mii_ticks = 0; 2349bac70b8SBill Paul break; 23537fd5f0fSPyun YongHyeon } 2369bac70b8SBill Paul 2379bac70b8SBill Paul /* 2389bac70b8SBill Paul * Check to see if we have link. If we do, we don't 23939bb2e52SMarius Strobl * need to restart the autonegotiation process. 2409bac70b8SBill Paul */ 24172200a8aSPyun YongHyeon if (rgephy_linkup(sc) != 0) { 242648bfbe6SPyun YongHyeon sc->mii_ticks = 0; 243648bfbe6SPyun YongHyeon break; 244648bfbe6SPyun YongHyeon } 24537fd5f0fSPyun YongHyeon 24637fd5f0fSPyun YongHyeon /* Announce link loss right after it happens. */ 24737fd5f0fSPyun YongHyeon if (sc->mii_ticks++ == 0) 2489bac70b8SBill Paul break; 2499bac70b8SBill Paul 25037fd5f0fSPyun YongHyeon /* Only retry autonegotiation every mii_anegticks seconds. */ 25137fd5f0fSPyun YongHyeon if (sc->mii_ticks <= sc->mii_anegticks) 25237fd5f0fSPyun YongHyeon return (0); 2539bac70b8SBill Paul 2549bac70b8SBill Paul sc->mii_ticks = 0; 255991ab941SMarius Strobl rgephy_mii_phy_auto(sc, ife->ifm_media); 25637fd5f0fSPyun YongHyeon break; 2579bac70b8SBill Paul } 2589bac70b8SBill Paul 2599bac70b8SBill Paul /* Update the media status. */ 2603fcb7a53SMarius Strobl PHY_STATUS(sc); 2619bac70b8SBill Paul 2629bac70b8SBill Paul /* 2639bac70b8SBill Paul * Callback if something changed. Note that we need to poke 264d5a50459SBill Paul * the DSP on the RealTek PHYs if the media changes. 2659bac70b8SBill Paul * 2669bac70b8SBill Paul */ 2679bac70b8SBill Paul if (sc->mii_media_active != mii->mii_media_active || 2689bac70b8SBill Paul sc->mii_media_status != mii->mii_media_status || 2699bac70b8SBill Paul cmd == MII_MEDIACHG) { 2709bac70b8SBill Paul rgephy_load_dspcode(sc); 2719bac70b8SBill Paul } 2729a54cbb9SAndre Oppermann mii_phy_update(sc, cmd); 2739bac70b8SBill Paul return (0); 2749bac70b8SBill Paul } 2759bac70b8SBill Paul 27672200a8aSPyun YongHyeon static int 27772200a8aSPyun YongHyeon rgephy_linkup(struct mii_softc *sc) 27872200a8aSPyun YongHyeon { 27972200a8aSPyun YongHyeon int linkup; 28072200a8aSPyun YongHyeon uint16_t reg; 28172200a8aSPyun YongHyeon 28272200a8aSPyun YongHyeon linkup = 0; 28372200a8aSPyun YongHyeon if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && 28472200a8aSPyun YongHyeon sc->mii_mpd_rev >= RGEPHY_8211B) { 28572200a8aSPyun YongHyeon if (sc->mii_mpd_rev == RGEPHY_8211F) { 28672200a8aSPyun YongHyeon reg = PHY_READ(sc, RGEPHY_F_MII_SSR); 28772200a8aSPyun YongHyeon if (reg & RGEPHY_F_SSR_LINK) 28872200a8aSPyun YongHyeon linkup++; 28972200a8aSPyun YongHyeon } else { 29072200a8aSPyun YongHyeon reg = PHY_READ(sc, RGEPHY_MII_SSR); 29172200a8aSPyun YongHyeon if (reg & RGEPHY_SSR_LINK) 29272200a8aSPyun YongHyeon linkup++; 29372200a8aSPyun YongHyeon } 29472200a8aSPyun YongHyeon } else { 29572200a8aSPyun YongHyeon reg = PHY_READ(sc, RL_GMEDIASTAT); 29672200a8aSPyun YongHyeon if (reg & RL_GMEDIASTAT_LINK) 29772200a8aSPyun YongHyeon linkup++; 29872200a8aSPyun YongHyeon } 29972200a8aSPyun YongHyeon 30072200a8aSPyun YongHyeon return (linkup); 30172200a8aSPyun YongHyeon } 30272200a8aSPyun YongHyeon 3039bac70b8SBill Paul static void 3047d830ac9SWarner Losh rgephy_status(struct mii_softc *sc) 3059bac70b8SBill Paul { 3069bac70b8SBill Paul struct mii_data *mii = sc->mii_pdata; 307d5a50459SBill Paul int bmsr, bmcr; 308648bfbe6SPyun YongHyeon uint16_t ssr; 3099bac70b8SBill Paul 3109bac70b8SBill Paul mii->mii_media_status = IFM_AVALID; 3119bac70b8SBill Paul mii->mii_media_active = IFM_ETHER; 3129bac70b8SBill Paul 31372200a8aSPyun YongHyeon if (rgephy_linkup(sc) != 0) 314648bfbe6SPyun YongHyeon mii->mii_media_status |= IFM_ACTIVE; 315648bfbe6SPyun YongHyeon 3169bac70b8SBill Paul bmsr = PHY_READ(sc, RGEPHY_MII_BMSR); 3179bac70b8SBill Paul bmcr = PHY_READ(sc, RGEPHY_MII_BMCR); 318b455d946SPyun YongHyeon if (bmcr & RGEPHY_BMCR_ISO) { 319b455d946SPyun YongHyeon mii->mii_media_active |= IFM_NONE; 320b455d946SPyun YongHyeon mii->mii_media_status = 0; 321b455d946SPyun YongHyeon return; 322b455d946SPyun YongHyeon } 3239bac70b8SBill Paul 3249bac70b8SBill Paul if (bmcr & RGEPHY_BMCR_LOOP) 3259bac70b8SBill Paul mii->mii_media_active |= IFM_LOOP; 3269bac70b8SBill Paul 3279bac70b8SBill Paul if (bmcr & RGEPHY_BMCR_AUTOEN) { 3289bac70b8SBill Paul if ((bmsr & RGEPHY_BMSR_ACOMP) == 0) { 3299bac70b8SBill Paul /* Erg, still trying, I guess... */ 3309bac70b8SBill Paul mii->mii_media_active |= IFM_NONE; 3319bac70b8SBill Paul return; 3329bac70b8SBill Paul } 3339bac70b8SBill Paul } 3349bac70b8SBill Paul 33572200a8aSPyun YongHyeon if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && 33672200a8aSPyun YongHyeon sc->mii_mpd_rev >= RGEPHY_8211B) { 33772200a8aSPyun YongHyeon if (sc->mii_mpd_rev == RGEPHY_8211F) { 33872200a8aSPyun YongHyeon ssr = PHY_READ(sc, RGEPHY_F_MII_SSR); 33972200a8aSPyun YongHyeon switch (ssr & RGEPHY_F_SSR_SPD_MASK) { 34072200a8aSPyun YongHyeon case RGEPHY_F_SSR_S1000: 34172200a8aSPyun YongHyeon mii->mii_media_active |= IFM_1000_T; 34272200a8aSPyun YongHyeon break; 34372200a8aSPyun YongHyeon case RGEPHY_F_SSR_S100: 34472200a8aSPyun YongHyeon mii->mii_media_active |= IFM_100_TX; 34572200a8aSPyun YongHyeon break; 34672200a8aSPyun YongHyeon case RGEPHY_F_SSR_S10: 34772200a8aSPyun YongHyeon mii->mii_media_active |= IFM_10_T; 34872200a8aSPyun YongHyeon break; 34972200a8aSPyun YongHyeon default: 35072200a8aSPyun YongHyeon mii->mii_media_active |= IFM_NONE; 35172200a8aSPyun YongHyeon break; 35272200a8aSPyun YongHyeon } 35372200a8aSPyun YongHyeon if (ssr & RGEPHY_F_SSR_FDX) 35472200a8aSPyun YongHyeon mii->mii_media_active |= IFM_FDX; 35572200a8aSPyun YongHyeon else 35672200a8aSPyun YongHyeon mii->mii_media_active |= IFM_HDX; 35772200a8aSPyun YongHyeon 35872200a8aSPyun YongHyeon } else { 359648bfbe6SPyun YongHyeon ssr = PHY_READ(sc, RGEPHY_MII_SSR); 360648bfbe6SPyun YongHyeon switch (ssr & RGEPHY_SSR_SPD_MASK) { 361648bfbe6SPyun YongHyeon case RGEPHY_SSR_S1000: 362648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_1000_T; 363648bfbe6SPyun YongHyeon break; 364648bfbe6SPyun YongHyeon case RGEPHY_SSR_S100: 365648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_100_TX; 366648bfbe6SPyun YongHyeon break; 367648bfbe6SPyun YongHyeon case RGEPHY_SSR_S10: 368648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_10_T; 369648bfbe6SPyun YongHyeon break; 370648bfbe6SPyun YongHyeon default: 371648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_NONE; 372648bfbe6SPyun YongHyeon break; 373648bfbe6SPyun YongHyeon } 374648bfbe6SPyun YongHyeon if (ssr & RGEPHY_SSR_FDX) 375648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_FDX; 376648bfbe6SPyun YongHyeon else 377648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_HDX; 37872200a8aSPyun YongHyeon } 379648bfbe6SPyun YongHyeon } else { 3809bac70b8SBill Paul bmsr = PHY_READ(sc, RL_GMEDIASTAT); 3819bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_1000MBPS) 3829bac70b8SBill Paul mii->mii_media_active |= IFM_1000_T; 383ed510fb0SBill Paul else if (bmsr & RL_GMEDIASTAT_100MBPS) 384ed510fb0SBill Paul mii->mii_media_active |= IFM_100_TX; 385ed510fb0SBill Paul else if (bmsr & RL_GMEDIASTAT_10MBPS) 386ed510fb0SBill Paul mii->mii_media_active |= IFM_10_T; 387ed510fb0SBill Paul else 388ed510fb0SBill Paul mii->mii_media_active |= IFM_NONE; 3899bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_FDX) 3909bac70b8SBill Paul mii->mii_media_active |= IFM_FDX; 391648bfbe6SPyun YongHyeon else 392648bfbe6SPyun YongHyeon mii->mii_media_active |= IFM_HDX; 393648bfbe6SPyun YongHyeon } 394991ab941SMarius Strobl 395991ab941SMarius Strobl if ((mii->mii_media_active & IFM_FDX) != 0) 396991ab941SMarius Strobl mii->mii_media_active |= mii_phy_flowstatus(sc); 397991ab941SMarius Strobl 398991ab941SMarius Strobl if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && 399991ab941SMarius Strobl (PHY_READ(sc, RGEPHY_MII_1000STS) & RGEPHY_1000STS_MSR) != 0) 400991ab941SMarius Strobl mii->mii_media_active |= IFM_ETH_MASTER; 4019bac70b8SBill Paul } 4029bac70b8SBill Paul 4039bac70b8SBill Paul static int 404991ab941SMarius Strobl rgephy_mii_phy_auto(struct mii_softc *sc, int media) 4059bac70b8SBill Paul { 406991ab941SMarius Strobl int anar; 407028ccec4SMarius Strobl 408991ab941SMarius Strobl rgephy_loop(sc); 4093fcb7a53SMarius Strobl PHY_RESET(sc); 4109bac70b8SBill Paul 411991ab941SMarius Strobl anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 412991ab941SMarius Strobl if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 413991ab941SMarius Strobl anar |= RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP; 414991ab941SMarius Strobl PHY_WRITE(sc, RGEPHY_MII_ANAR, anar); 4159bac70b8SBill Paul DELAY(1000); 416991ab941SMarius Strobl PHY_WRITE(sc, RGEPHY_MII_1000CTL, 417ed510fb0SBill Paul RGEPHY_1000CTL_AHD | RGEPHY_1000CTL_AFD); 4189bac70b8SBill Paul DELAY(1000); 419991ab941SMarius Strobl PHY_WRITE(sc, RGEPHY_MII_BMCR, 4209bac70b8SBill Paul RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG); 4219bac70b8SBill Paul DELAY(100); 4229bac70b8SBill Paul 4239bac70b8SBill Paul return (EJUSTRETURN); 4249bac70b8SBill Paul } 4259bac70b8SBill Paul 4269bac70b8SBill Paul static void 4279bac70b8SBill Paul rgephy_loop(struct mii_softc *sc) 4289bac70b8SBill Paul { 4299bac70b8SBill Paul int i; 4309bac70b8SBill Paul 431cd10b400SPyun YongHyeon if (sc->mii_mpd_model != MII_MODEL_REALTEK_RTL8251 && 43272200a8aSPyun YongHyeon sc->mii_mpd_rev < RGEPHY_8211B) { 4339bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_BMCR, RGEPHY_BMCR_PDOWN); 4349bac70b8SBill Paul DELAY(1000); 435648bfbe6SPyun YongHyeon } 4369bac70b8SBill Paul 4379bac70b8SBill Paul for (i = 0; i < 15000; i++) { 438028ccec4SMarius Strobl if (!(PHY_READ(sc, RGEPHY_MII_BMSR) & RGEPHY_BMSR_LINK)) { 4399bac70b8SBill Paul #if 0 4409bac70b8SBill Paul device_printf(sc->mii_dev, "looped %d\n", i); 4419bac70b8SBill Paul #endif 4429bac70b8SBill Paul break; 4439bac70b8SBill Paul } 4449bac70b8SBill Paul DELAY(10); 4459bac70b8SBill Paul } 4469bac70b8SBill Paul } 4479bac70b8SBill Paul 4489bac70b8SBill Paul #define PHY_SETBIT(x, y, z) \ 4499bac70b8SBill Paul PHY_WRITE(x, y, (PHY_READ(x, y) | (z))) 4509bac70b8SBill Paul #define PHY_CLRBIT(x, y, z) \ 4519bac70b8SBill Paul PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z))) 4529bac70b8SBill Paul 453d5a50459SBill Paul /* 454d5a50459SBill Paul * Initialize RealTek PHY per the datasheet. The DSP in the PHYs of 455d5a50459SBill Paul * existing revisions of the 8169S/8110S chips need to be tuned in 456ed510fb0SBill Paul * order to reliably negotiate a 1000Mbps link. This is only needed 457ed510fb0SBill Paul * for rev 0 and rev 1 of the PHY. Later versions work without 458ed510fb0SBill Paul * any fixups. 459d5a50459SBill Paul */ 4609bac70b8SBill Paul static void 4619bac70b8SBill Paul rgephy_load_dspcode(struct mii_softc *sc) 4629bac70b8SBill Paul { 4639bac70b8SBill Paul int val; 464ed510fb0SBill Paul 465cd10b400SPyun YongHyeon if (sc->mii_mpd_model == MII_MODEL_REALTEK_RTL8251 || 46672200a8aSPyun YongHyeon sc->mii_mpd_rev >= RGEPHY_8211B) 467ed510fb0SBill Paul return; 4689bac70b8SBill Paul 4699bac70b8SBill Paul PHY_WRITE(sc, 31, 0x0001); 4709bac70b8SBill Paul PHY_WRITE(sc, 21, 0x1000); 4719bac70b8SBill Paul PHY_WRITE(sc, 24, 0x65C7); 4729bac70b8SBill Paul PHY_CLRBIT(sc, 4, 0x0800); 4739bac70b8SBill Paul val = PHY_READ(sc, 4) & 0xFFF; 4749bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4759bac70b8SBill Paul PHY_WRITE(sc, 3, 0x00A1); 4769bac70b8SBill Paul PHY_WRITE(sc, 2, 0x0008); 4779bac70b8SBill Paul PHY_WRITE(sc, 1, 0x1020); 4789bac70b8SBill Paul PHY_WRITE(sc, 0, 0x1000); 4799bac70b8SBill Paul PHY_SETBIT(sc, 4, 0x0800); 4809bac70b8SBill Paul PHY_CLRBIT(sc, 4, 0x0800); 4819bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0x7000; 4829bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4839bac70b8SBill Paul PHY_WRITE(sc, 3, 0xFF41); 4849bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDE60); 4859bac70b8SBill Paul PHY_WRITE(sc, 1, 0x0140); 4869bac70b8SBill Paul PHY_WRITE(sc, 0, 0x0077); 4879bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0xA000; 4889bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4899bac70b8SBill Paul PHY_WRITE(sc, 3, 0xDF01); 4909bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDF20); 4919bac70b8SBill Paul PHY_WRITE(sc, 1, 0xFF95); 4929bac70b8SBill Paul PHY_WRITE(sc, 0, 0xFA00); 4939bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0xB000; 4949bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4959bac70b8SBill Paul PHY_WRITE(sc, 3, 0xFF41); 4969bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDE20); 4979bac70b8SBill Paul PHY_WRITE(sc, 1, 0x0140); 4989bac70b8SBill Paul PHY_WRITE(sc, 0, 0x00BB); 4999bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0xF000; 5009bac70b8SBill Paul PHY_WRITE(sc, 4, val); 5019bac70b8SBill Paul PHY_WRITE(sc, 3, 0xDF01); 5029bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDF20); 5039bac70b8SBill Paul PHY_WRITE(sc, 1, 0xFF95); 5049bac70b8SBill Paul PHY_WRITE(sc, 0, 0xBF00); 5059bac70b8SBill Paul PHY_SETBIT(sc, 4, 0x0800); 5069bac70b8SBill Paul PHY_CLRBIT(sc, 4, 0x0800); 5079bac70b8SBill Paul PHY_WRITE(sc, 31, 0x0000); 5089bac70b8SBill Paul 5099bac70b8SBill Paul DELAY(40); 5109bac70b8SBill Paul } 5119bac70b8SBill Paul 5129bac70b8SBill Paul static void 5139bac70b8SBill Paul rgephy_reset(struct mii_softc *sc) 5149bac70b8SBill Paul { 515cf402cc9SPyun YongHyeon uint16_t pcr, ssr; 516c06cddfeSPyun YongHyeon 51772200a8aSPyun YongHyeon switch (sc->mii_mpd_rev) { 51872200a8aSPyun YongHyeon case RGEPHY_8211F: 51972200a8aSPyun YongHyeon pcr = PHY_READ(sc, RGEPHY_F_MII_PCR1); 52072200a8aSPyun YongHyeon if ((pcr & RGEPHY_F_PCR1_MDI_MM) != 0) { 52172200a8aSPyun YongHyeon pcr &= ~RGEPHY_F_PCR1_MDI_MM; 52272200a8aSPyun YongHyeon PHY_WRITE(sc, RGEPHY_F_MII_PCR1, pcr); 52372200a8aSPyun YongHyeon } 52472200a8aSPyun YongHyeon break; 52572200a8aSPyun YongHyeon case RGEPHY_8211C: 52672200a8aSPyun YongHyeon if ((sc->mii_flags & MIIF_PHYPRIV0) == 0) { 527c06cddfeSPyun YongHyeon /* RTL8211C(L) */ 528c06cddfeSPyun YongHyeon ssr = PHY_READ(sc, RGEPHY_MII_SSR); 529c06cddfeSPyun YongHyeon if ((ssr & RGEPHY_SSR_ALDPS) != 0) { 530c06cddfeSPyun YongHyeon ssr &= ~RGEPHY_SSR_ALDPS; 531c06cddfeSPyun YongHyeon PHY_WRITE(sc, RGEPHY_MII_SSR, ssr); 532c06cddfeSPyun YongHyeon } 533c06cddfeSPyun YongHyeon } 534*ce18f7cdSPyun YongHyeon /* FALLTHROUGH */ 53572200a8aSPyun YongHyeon default: 53672200a8aSPyun YongHyeon if (sc->mii_mpd_rev >= RGEPHY_8211B) { 537cf402cc9SPyun YongHyeon pcr = PHY_READ(sc, RGEPHY_MII_PCR); 538cf402cc9SPyun YongHyeon if ((pcr & RGEPHY_PCR_MDIX_AUTO) == 0) { 539cf402cc9SPyun YongHyeon pcr &= ~RGEPHY_PCR_MDI_MASK; 540cf402cc9SPyun YongHyeon pcr |= RGEPHY_PCR_MDIX_AUTO; 541cf402cc9SPyun YongHyeon PHY_WRITE(sc, RGEPHY_MII_PCR, pcr); 542cf402cc9SPyun YongHyeon } 543cf402cc9SPyun YongHyeon } 54472200a8aSPyun YongHyeon break; 54572200a8aSPyun YongHyeon } 546cf402cc9SPyun YongHyeon 5479bac70b8SBill Paul mii_phy_reset(sc); 5489bac70b8SBill Paul DELAY(1000); 5499bac70b8SBill Paul rgephy_load_dspcode(sc); 5509bac70b8SBill Paul } 551