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 /* 379bac70b8SBill Paul * Driver for the RealTek 8169S/8110S 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> 459bac70b8SBill Paul #include <sys/bus.h> 469bac70b8SBill Paul 479bac70b8SBill Paul #include <machine/clock.h> 489bac70b8SBill Paul 499bac70b8SBill Paul #include <net/if.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> 629bac70b8SBill Paul #include <pci/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), 739bac70b8SBill Paul { 0, 0 } 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, 819bac70b8SBill Paul 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 *); 889bac70b8SBill Paul static int rgephy_mii_phy_auto(struct mii_softc *); 899bac70b8SBill Paul static void rgephy_reset(struct mii_softc *); 909bac70b8SBill Paul static void rgephy_loop(struct mii_softc *); 919bac70b8SBill Paul static void rgephy_load_dspcode(struct mii_softc *); 929bac70b8SBill Paul static int rgephy_mii_model; 939bac70b8SBill Paul 949bac70b8SBill Paul static int 959bac70b8SBill Paul rgephy_probe(dev) 969bac70b8SBill Paul device_t dev; 979bac70b8SBill Paul { 989bac70b8SBill Paul struct mii_attach_args *ma; 999bac70b8SBill Paul 1009bac70b8SBill Paul ma = device_get_ivars(dev); 1019bac70b8SBill Paul 1029bac70b8SBill Paul if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxREALTEK && 1039bac70b8SBill Paul MII_MODEL(ma->mii_id2) == MII_MODEL_xxREALTEK_RTL8169S) { 1049bac70b8SBill Paul device_set_desc(dev, MII_STR_xxREALTEK_RTL8169S); 1059bac70b8SBill Paul return(0); 1069bac70b8SBill Paul } 1079bac70b8SBill Paul 1089bac70b8SBill Paul return(ENXIO); 1099bac70b8SBill Paul } 1109bac70b8SBill Paul 1119bac70b8SBill Paul static int 1129bac70b8SBill Paul rgephy_attach(dev) 1139bac70b8SBill Paul device_t dev; 1149bac70b8SBill Paul { 1159bac70b8SBill Paul struct mii_softc *sc; 1169bac70b8SBill Paul struct mii_attach_args *ma; 1179bac70b8SBill Paul struct mii_data *mii; 1189bac70b8SBill Paul const char *sep = ""; 1199bac70b8SBill Paul 1209bac70b8SBill Paul sc = device_get_softc(dev); 1219bac70b8SBill Paul ma = device_get_ivars(dev); 1229bac70b8SBill Paul sc->mii_dev = device_get_parent(dev); 1239bac70b8SBill Paul mii = device_get_softc(sc->mii_dev); 1249bac70b8SBill Paul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 1259bac70b8SBill Paul 1269bac70b8SBill Paul sc->mii_inst = mii->mii_instance; 1279bac70b8SBill Paul sc->mii_phy = ma->mii_phyno; 1289bac70b8SBill Paul sc->mii_service = rgephy_service; 1299bac70b8SBill Paul sc->mii_pdata = mii; 1309bac70b8SBill Paul 1319bac70b8SBill Paul sc->mii_flags |= MIIF_NOISOLATE; 1329bac70b8SBill Paul mii->mii_instance++; 1339bac70b8SBill Paul 1349bac70b8SBill Paul #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 1359bac70b8SBill Paul #define PRINT(s) printf("%s%s", sep, s); sep = ", " 1369bac70b8SBill Paul 1379bac70b8SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 1389bac70b8SBill Paul BMCR_ISO); 1399bac70b8SBill Paul #if 0 1409bac70b8SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 1419bac70b8SBill Paul BMCR_LOOP|BMCR_S100); 1429bac70b8SBill Paul #endif 1439bac70b8SBill Paul 1449bac70b8SBill Paul rgephy_mii_model = MII_MODEL(ma->mii_id2); 1459bac70b8SBill Paul rgephy_reset(sc); 1469bac70b8SBill Paul 1479bac70b8SBill Paul sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 1489bac70b8SBill Paul sc->mii_capabilities &= ~BMSR_ANEG; 1499bac70b8SBill Paul 1509bac70b8SBill Paul device_printf(dev, " "); 1519bac70b8SBill Paul mii_add_media(sc); 1529bac70b8SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst), 1539bac70b8SBill Paul RGEPHY_BMCR_FDX); 1549bac70b8SBill Paul PRINT(", 1000baseTX"); 1559bac70b8SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst), 0); 1569bac70b8SBill Paul PRINT("1000baseTX-FDX"); 1579bac70b8SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); 1589bac70b8SBill Paul PRINT("auto"); 1599bac70b8SBill Paul 1609bac70b8SBill Paul printf("\n"); 1619bac70b8SBill Paul #undef ADD 1629bac70b8SBill Paul #undef PRINT 1639bac70b8SBill Paul 1649bac70b8SBill Paul MIIBUS_MEDIAINIT(sc->mii_dev); 1659bac70b8SBill Paul return(0); 1669bac70b8SBill Paul } 1679bac70b8SBill Paul 1689bac70b8SBill Paul static int 1699bac70b8SBill Paul rgephy_service(sc, mii, cmd) 1709bac70b8SBill Paul struct mii_softc *sc; 1719bac70b8SBill Paul struct mii_data *mii; 1729bac70b8SBill Paul int cmd; 1739bac70b8SBill Paul { 1749bac70b8SBill Paul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 1759bac70b8SBill Paul int reg, speed, gig; 1769bac70b8SBill Paul 1779bac70b8SBill Paul switch (cmd) { 1789bac70b8SBill Paul case MII_POLLSTAT: 1799bac70b8SBill Paul /* 1809bac70b8SBill Paul * If we're not polling our PHY instance, just return. 1819bac70b8SBill Paul */ 1829bac70b8SBill Paul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 1839bac70b8SBill Paul return (0); 1849bac70b8SBill Paul break; 1859bac70b8SBill Paul 1869bac70b8SBill Paul case MII_MEDIACHG: 1879bac70b8SBill Paul /* 1889bac70b8SBill Paul * If the media indicates a different PHY instance, 1899bac70b8SBill Paul * isolate ourselves. 1909bac70b8SBill Paul */ 1919bac70b8SBill Paul if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 1929bac70b8SBill Paul reg = PHY_READ(sc, MII_BMCR); 1939bac70b8SBill Paul PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 1949bac70b8SBill Paul return (0); 1959bac70b8SBill Paul } 1969bac70b8SBill Paul 1979bac70b8SBill Paul /* 1989bac70b8SBill Paul * If the interface is not up, don't do anything. 1999bac70b8SBill Paul */ 2009bac70b8SBill Paul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 2019bac70b8SBill Paul break; 2029bac70b8SBill Paul 2039bac70b8SBill Paul rgephy_reset(sc); /* XXX hardware bug work-around */ 2049bac70b8SBill Paul 2059bac70b8SBill Paul switch (IFM_SUBTYPE(ife->ifm_media)) { 2069bac70b8SBill Paul case IFM_AUTO: 2079bac70b8SBill Paul #ifdef foo 2089bac70b8SBill Paul /* 2099bac70b8SBill Paul * If we're already in auto mode, just return. 2109bac70b8SBill Paul */ 2119bac70b8SBill Paul if (PHY_READ(sc, RGEPHY_MII_BMCR) & RGEPHY_BMCR_AUTOEN) 2129bac70b8SBill Paul return (0); 2139bac70b8SBill Paul #endif 2149bac70b8SBill Paul (void) rgephy_mii_phy_auto(sc); 2159bac70b8SBill Paul break; 2169bac70b8SBill Paul case IFM_1000_T: 2179bac70b8SBill Paul speed = RGEPHY_S1000; 2189bac70b8SBill Paul goto setit; 2199bac70b8SBill Paul case IFM_100_TX: 2209bac70b8SBill Paul speed = RGEPHY_S100; 2219bac70b8SBill Paul goto setit; 2229bac70b8SBill Paul case IFM_10_T: 2239bac70b8SBill Paul speed = RGEPHY_S10; 2249bac70b8SBill Paul setit: 2259bac70b8SBill Paul rgephy_loop(sc); 2269bac70b8SBill Paul if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { 2279bac70b8SBill Paul speed |= RGEPHY_BMCR_FDX; 2289bac70b8SBill Paul gig = RGEPHY_1000CTL_AFD; 2299bac70b8SBill Paul } else { 2309bac70b8SBill Paul gig = RGEPHY_1000CTL_AHD; 2319bac70b8SBill Paul } 2329bac70b8SBill Paul 2339bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_1000CTL, 0); 2349bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_BMCR, speed); 2359bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_ANAR, RGEPHY_SEL_TYPE); 2369bac70b8SBill Paul 2379bac70b8SBill Paul if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) 2389bac70b8SBill Paul break; 2399bac70b8SBill Paul 2409bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_1000CTL, gig); 2419bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_BMCR, 2429bac70b8SBill Paul speed|RGEPHY_BMCR_AUTOEN|RGEPHY_BMCR_STARTNEG); 2439bac70b8SBill Paul 2449bac70b8SBill Paul /* 2459bac70b8SBill Paul * When settning the link manually, one side must 2469bac70b8SBill Paul * be the master and the other the slave. However 2479bac70b8SBill Paul * ifmedia doesn't give us a good way to specify 2489bac70b8SBill Paul * this, so we fake it by using one of the LINK 2499bac70b8SBill Paul * flags. If LINK0 is set, we program the PHY to 2509bac70b8SBill Paul * be a master, otherwise it's a slave. 2519bac70b8SBill Paul */ 2529bac70b8SBill Paul if ((mii->mii_ifp->if_flags & IFF_LINK0)) { 2539bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_1000CTL, 2549bac70b8SBill Paul gig|RGEPHY_1000CTL_MSE|RGEPHY_1000CTL_MSC); 2559bac70b8SBill Paul } else { 2569bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_1000CTL, 2579bac70b8SBill Paul gig|RGEPHY_1000CTL_MSE); 2589bac70b8SBill Paul } 2599bac70b8SBill Paul break; 2609bac70b8SBill Paul #ifdef foo 2619bac70b8SBill Paul case IFM_NONE: 2629bac70b8SBill Paul PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN); 2639bac70b8SBill Paul break; 2649bac70b8SBill Paul #endif 2659bac70b8SBill Paul case IFM_100_T4: 2669bac70b8SBill Paul default: 2679bac70b8SBill Paul return (EINVAL); 2689bac70b8SBill Paul } 2699bac70b8SBill Paul break; 2709bac70b8SBill Paul 2719bac70b8SBill Paul case MII_TICK: 2729bac70b8SBill Paul /* 2739bac70b8SBill Paul * If we're not currently selected, just return. 2749bac70b8SBill Paul */ 2759bac70b8SBill Paul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 2769bac70b8SBill Paul return (0); 2779bac70b8SBill Paul 2789bac70b8SBill Paul /* 2799bac70b8SBill Paul * Is the interface even up? 2809bac70b8SBill Paul */ 2819bac70b8SBill Paul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 2829bac70b8SBill Paul return (0); 2839bac70b8SBill Paul 2849bac70b8SBill Paul /* 2859bac70b8SBill Paul * Only used for autonegotiation. 2869bac70b8SBill Paul */ 2879bac70b8SBill Paul if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 2889bac70b8SBill Paul break; 2899bac70b8SBill Paul 2909bac70b8SBill Paul /* 2919bac70b8SBill Paul * Check to see if we have link. If we do, we don't 2929bac70b8SBill Paul * need to restart the autonegotiation process. Read 2939bac70b8SBill Paul * the BMSR twice in case it's latched. 2949bac70b8SBill Paul */ 2959bac70b8SBill Paul reg = PHY_READ(sc, RL_GMEDIASTAT); 2969bac70b8SBill Paul if (reg & RL_GMEDIASTAT_LINK) 2979bac70b8SBill Paul break; 2989bac70b8SBill Paul 2999bac70b8SBill Paul /* 3009bac70b8SBill Paul * Only retry autonegotiation every 5 seconds. 3019bac70b8SBill Paul */ 3029a54cbb9SAndre Oppermann if (++sc->mii_ticks <= 5/*10*/) 3039a54cbb9SAndre Oppermann break; 3049bac70b8SBill Paul 3059bac70b8SBill Paul sc->mii_ticks = 0; 3069bac70b8SBill Paul rgephy_mii_phy_auto(sc); 3079bac70b8SBill Paul return (0); 3089bac70b8SBill Paul } 3099bac70b8SBill Paul 3109bac70b8SBill Paul /* Update the media status. */ 3119bac70b8SBill Paul rgephy_status(sc); 3129bac70b8SBill Paul 3139bac70b8SBill Paul /* 3149bac70b8SBill Paul * Callback if something changed. Note that we need to poke 315d5a50459SBill Paul * the DSP on the RealTek PHYs if the media changes. 3169bac70b8SBill Paul * 3179bac70b8SBill Paul */ 3189bac70b8SBill Paul if (sc->mii_media_active != mii->mii_media_active || 3199bac70b8SBill Paul sc->mii_media_status != mii->mii_media_status || 3209bac70b8SBill Paul cmd == MII_MEDIACHG) { 3219bac70b8SBill Paul rgephy_load_dspcode(sc); 3229bac70b8SBill Paul } 3239a54cbb9SAndre Oppermann mii_phy_update(sc, cmd); 3249bac70b8SBill Paul return (0); 3259bac70b8SBill Paul } 3269bac70b8SBill Paul 3279bac70b8SBill Paul static void 3289bac70b8SBill Paul rgephy_status(sc) 3299bac70b8SBill Paul struct mii_softc *sc; 3309bac70b8SBill Paul { 3319bac70b8SBill Paul struct mii_data *mii = sc->mii_pdata; 332d5a50459SBill Paul int bmsr, bmcr; 3339bac70b8SBill Paul 3349bac70b8SBill Paul mii->mii_media_status = IFM_AVALID; 3359bac70b8SBill Paul mii->mii_media_active = IFM_ETHER; 3369bac70b8SBill Paul 3379bac70b8SBill Paul bmsr = PHY_READ(sc, RL_GMEDIASTAT); 3389bac70b8SBill Paul 3399bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_LINK) 3409bac70b8SBill Paul mii->mii_media_status |= IFM_ACTIVE; 3419bac70b8SBill Paul bmsr = PHY_READ(sc, RGEPHY_MII_BMSR); 3429bac70b8SBill Paul 3439bac70b8SBill Paul bmcr = PHY_READ(sc, RGEPHY_MII_BMCR); 3449bac70b8SBill Paul 3459bac70b8SBill Paul if (bmcr & RGEPHY_BMCR_LOOP) 3469bac70b8SBill Paul mii->mii_media_active |= IFM_LOOP; 3479bac70b8SBill Paul 3489bac70b8SBill Paul if (bmcr & RGEPHY_BMCR_AUTOEN) { 3499bac70b8SBill Paul if ((bmsr & RGEPHY_BMSR_ACOMP) == 0) { 3509bac70b8SBill Paul /* Erg, still trying, I guess... */ 3519bac70b8SBill Paul mii->mii_media_active |= IFM_NONE; 3529bac70b8SBill Paul return; 3539bac70b8SBill Paul } 3549bac70b8SBill Paul } 3559bac70b8SBill Paul 3569bac70b8SBill Paul bmsr = PHY_READ(sc, RL_GMEDIASTAT); 3579bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_10MBPS) 3589bac70b8SBill Paul mii->mii_media_active |= IFM_10_T; 3599bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_100MBPS) 3609bac70b8SBill Paul mii->mii_media_active |= IFM_100_TX; 3619bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_1000MBPS) 3629bac70b8SBill Paul mii->mii_media_active |= IFM_1000_T; 3639bac70b8SBill Paul if (bmsr & RL_GMEDIASTAT_FDX) 3649bac70b8SBill Paul mii->mii_media_active |= IFM_FDX; 3659bac70b8SBill Paul 3669bac70b8SBill Paul return; 3679bac70b8SBill Paul } 3689bac70b8SBill Paul 3699bac70b8SBill Paul 3709bac70b8SBill Paul static int 3719bac70b8SBill Paul rgephy_mii_phy_auto(mii) 3729bac70b8SBill Paul struct mii_softc *mii; 3739bac70b8SBill Paul { 3749bac70b8SBill Paul rgephy_loop(mii); 3759bac70b8SBill Paul rgephy_reset(mii); 3769bac70b8SBill Paul 3779bac70b8SBill Paul PHY_WRITE(mii, RGEPHY_MII_ANAR, 3789bac70b8SBill Paul BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA); 3799bac70b8SBill Paul DELAY(1000); 3809bac70b8SBill Paul PHY_WRITE(mii, RGEPHY_MII_1000CTL, RGEPHY_1000CTL_AFD); 3819bac70b8SBill Paul DELAY(1000); 3829bac70b8SBill Paul PHY_WRITE(mii, RGEPHY_MII_BMCR, 3839bac70b8SBill Paul RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG); 3849bac70b8SBill Paul DELAY(100); 3859bac70b8SBill Paul 3869bac70b8SBill Paul return (EJUSTRETURN); 3879bac70b8SBill Paul } 3889bac70b8SBill Paul 3899bac70b8SBill Paul static void 3909bac70b8SBill Paul rgephy_loop(struct mii_softc *sc) 3919bac70b8SBill Paul { 3929bac70b8SBill Paul u_int32_t bmsr; 3939bac70b8SBill Paul int i; 3949bac70b8SBill Paul 3959bac70b8SBill Paul PHY_WRITE(sc, RGEPHY_MII_BMCR, RGEPHY_BMCR_PDOWN); 3969bac70b8SBill Paul DELAY(1000); 3979bac70b8SBill Paul 3989bac70b8SBill Paul for (i = 0; i < 15000; i++) { 3999bac70b8SBill Paul bmsr = PHY_READ(sc, RGEPHY_MII_BMSR); 4009bac70b8SBill Paul if (!(bmsr & RGEPHY_BMSR_LINK)) { 4019bac70b8SBill Paul #if 0 4029bac70b8SBill Paul device_printf(sc->mii_dev, "looped %d\n", i); 4039bac70b8SBill Paul #endif 4049bac70b8SBill Paul break; 4059bac70b8SBill Paul } 4069bac70b8SBill Paul DELAY(10); 4079bac70b8SBill Paul } 4089bac70b8SBill Paul } 4099bac70b8SBill Paul 4109bac70b8SBill Paul #define PHY_SETBIT(x, y, z) \ 4119bac70b8SBill Paul PHY_WRITE(x, y, (PHY_READ(x, y) | (z))) 4129bac70b8SBill Paul #define PHY_CLRBIT(x, y, z) \ 4139bac70b8SBill Paul PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z))) 4149bac70b8SBill Paul 415d5a50459SBill Paul /* 416d5a50459SBill Paul * Initialize RealTek PHY per the datasheet. The DSP in the PHYs of 417d5a50459SBill Paul * existing revisions of the 8169S/8110S chips need to be tuned in 418d5a50459SBill Paul * order to reliably negotiate a 1000Mbps link. Later revs of the 419d5a50459SBill Paul * chips may not require this software tuning. 420d5a50459SBill Paul */ 4219bac70b8SBill Paul static void 4229bac70b8SBill Paul rgephy_load_dspcode(struct mii_softc *sc) 4239bac70b8SBill Paul { 4249bac70b8SBill Paul int val; 4259bac70b8SBill Paul 4269bac70b8SBill Paul PHY_WRITE(sc, 31, 0x0001); 4279bac70b8SBill Paul PHY_WRITE(sc, 21, 0x1000); 4289bac70b8SBill Paul PHY_WRITE(sc, 24, 0x65C7); 4299bac70b8SBill Paul PHY_CLRBIT(sc, 4, 0x0800); 4309bac70b8SBill Paul val = PHY_READ(sc, 4) & 0xFFF; 4319bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4329bac70b8SBill Paul PHY_WRITE(sc, 3, 0x00A1); 4339bac70b8SBill Paul PHY_WRITE(sc, 2, 0x0008); 4349bac70b8SBill Paul PHY_WRITE(sc, 1, 0x1020); 4359bac70b8SBill Paul PHY_WRITE(sc, 0, 0x1000); 4369bac70b8SBill Paul PHY_SETBIT(sc, 4, 0x0800); 4379bac70b8SBill Paul PHY_CLRBIT(sc, 4, 0x0800); 4389bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0x7000; 4399bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4409bac70b8SBill Paul PHY_WRITE(sc, 3, 0xFF41); 4419bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDE60); 4429bac70b8SBill Paul PHY_WRITE(sc, 1, 0x0140); 4439bac70b8SBill Paul PHY_WRITE(sc, 0, 0x0077); 4449bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0xA000; 4459bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4469bac70b8SBill Paul PHY_WRITE(sc, 3, 0xDF01); 4479bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDF20); 4489bac70b8SBill Paul PHY_WRITE(sc, 1, 0xFF95); 4499bac70b8SBill Paul PHY_WRITE(sc, 0, 0xFA00); 4509bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0xB000; 4519bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4529bac70b8SBill Paul PHY_WRITE(sc, 3, 0xFF41); 4539bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDE20); 4549bac70b8SBill Paul PHY_WRITE(sc, 1, 0x0140); 4559bac70b8SBill Paul PHY_WRITE(sc, 0, 0x00BB); 4569bac70b8SBill Paul val = (PHY_READ(sc, 4) & 0xFFF) | 0xF000; 4579bac70b8SBill Paul PHY_WRITE(sc, 4, val); 4589bac70b8SBill Paul PHY_WRITE(sc, 3, 0xDF01); 4599bac70b8SBill Paul PHY_WRITE(sc, 2, 0xDF20); 4609bac70b8SBill Paul PHY_WRITE(sc, 1, 0xFF95); 4619bac70b8SBill Paul PHY_WRITE(sc, 0, 0xBF00); 4629bac70b8SBill Paul PHY_SETBIT(sc, 4, 0x0800); 4639bac70b8SBill Paul PHY_CLRBIT(sc, 4, 0x0800); 4649bac70b8SBill Paul PHY_WRITE(sc, 31, 0x0000); 4659bac70b8SBill Paul 4669bac70b8SBill Paul DELAY(40); 4679bac70b8SBill Paul } 4689bac70b8SBill Paul 4699bac70b8SBill Paul static void 4709bac70b8SBill Paul rgephy_reset(struct mii_softc *sc) 4719bac70b8SBill Paul { 4729bac70b8SBill Paul mii_phy_reset(sc); 4739bac70b8SBill Paul DELAY(1000); 4749bac70b8SBill Paul rgephy_load_dspcode(sc); 4759bac70b8SBill Paul 4769bac70b8SBill Paul return; 4779bac70b8SBill Paul } 478