1139749Simp/*- 2119976Swpaul * Copyright (c) 2003 3119976Swpaul * Bill Paul <wpaul@windriver.com>. All rights reserved. 4119976Swpaul * 5119976Swpaul * Redistribution and use in source and binary forms, with or without 6119976Swpaul * modification, are permitted provided that the following conditions 7119976Swpaul * are met: 8119976Swpaul * 1. Redistributions of source code must retain the above copyright 9119976Swpaul * notice, this list of conditions and the following disclaimer. 10119976Swpaul * 2. Redistributions in binary form must reproduce the above copyright 11119976Swpaul * notice, this list of conditions and the following disclaimer in the 12119976Swpaul * documentation and/or other materials provided with the distribution. 13119976Swpaul * 3. All advertising materials mentioning features or use of this software 14119976Swpaul * must display the following acknowledgement: 15119976Swpaul * This product includes software developed by Bill Paul. 16119976Swpaul * 4. Neither the name of the author nor the names of any co-contributors 17119976Swpaul * may be used to endorse or promote products derived from this software 18119976Swpaul * without specific prior written permission. 19119976Swpaul * 20119976Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21119976Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22119976Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23119976Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24119976Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25119976Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26119976Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27119976Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28119976Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29119976Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30119976Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 31119976Swpaul */ 32119976Swpaul 33129844Smarius#include <sys/cdefs.h> 34129844Smarius__FBSDID("$FreeBSD$"); 35129844Smarius 36119976Swpaul/* 37180178Syongari * Driver for the RealTek 8169S/8110S/8211B/8211C internal 10/100/1000 PHY. 38119976Swpaul */ 39119976Swpaul 40119976Swpaul#include <sys/param.h> 41119976Swpaul#include <sys/systm.h> 42119976Swpaul#include <sys/kernel.h> 43129876Sphk#include <sys/module.h> 44119976Swpaul#include <sys/socket.h> 45119976Swpaul#include <sys/bus.h> 46119976Swpaul 47119976Swpaul#include <net/if.h> 48119976Swpaul#include <net/if_arp.h> 49119976Swpaul#include <net/if_media.h> 50119976Swpaul 51119976Swpaul#include <dev/mii/mii.h> 52119976Swpaul#include <dev/mii/miivar.h> 53119976Swpaul#include "miidevs.h" 54119976Swpaul 55119976Swpaul#include <dev/mii/rgephyreg.h> 56119976Swpaul 57119976Swpaul#include "miibus_if.h" 58119976Swpaul 59119976Swpaul#include <machine/bus.h> 60119976Swpaul#include <pci/if_rlreg.h> 61119976Swpaul 62119976Swpaulstatic int rgephy_probe(device_t); 63119976Swpaulstatic int rgephy_attach(device_t); 64119976Swpaul 65119976Swpaulstatic device_method_t rgephy_methods[] = { 66119976Swpaul /* device interface */ 67119976Swpaul DEVMETHOD(device_probe, rgephy_probe), 68119976Swpaul DEVMETHOD(device_attach, rgephy_attach), 69119976Swpaul DEVMETHOD(device_detach, mii_phy_detach), 70119976Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 71227908Smarius DEVMETHOD_END 72119976Swpaul}; 73119976Swpaul 74119976Swpaulstatic devclass_t rgephy_devclass; 75119976Swpaul 76119976Swpaulstatic driver_t rgephy_driver = { 77119976Swpaul "rgephy", 78119976Swpaul rgephy_methods, 79221407Smarius sizeof(struct mii_softc) 80119976Swpaul}; 81119976Swpaul 82119976SwpaulDRIVER_MODULE(rgephy, miibus, rgephy_driver, rgephy_devclass, 0, 0); 83119976Swpaul 84119976Swpaulstatic int rgephy_service(struct mii_softc *, struct mii_data *, int); 85119976Swpaulstatic void rgephy_status(struct mii_softc *); 86215298Smariusstatic int rgephy_mii_phy_auto(struct mii_softc *, int); 87119976Swpaulstatic void rgephy_reset(struct mii_softc *); 88119976Swpaulstatic void rgephy_loop(struct mii_softc *); 89119976Swpaulstatic void rgephy_load_dspcode(struct mii_softc *); 90119976Swpaul 91164827Smariusstatic const struct mii_phydesc rgephys[] = { 92221407Smarius MII_PHY_DESC(REALTEK, RTL8169S), 93257612Syongari MII_PHY_DESC(REALTEK, RTL8251), 94164827Smarius MII_PHY_END 95164827Smarius}; 96164827Smarius 97221407Smariusstatic const struct mii_phy_funcs rgephy_funcs = { 98221407Smarius rgephy_service, 99221407Smarius rgephy_status, 100221407Smarius rgephy_reset 101221407Smarius}; 102221407Smarius 103119976Swpaulstatic int 104150763Simprgephy_probe(device_t dev) 105119976Swpaul{ 106119976Swpaul 107164827Smarius return (mii_phy_dev_probe(dev, rgephys, BUS_PROBE_DEFAULT)); 108119976Swpaul} 109119976Swpaul 110119976Swpaulstatic int 111150763Simprgephy_attach(device_t dev) 112119976Swpaul{ 113119976Swpaul struct mii_softc *sc; 114232246Syongari struct mii_attach_args *ma; 115232246Syongari u_int flags; 116119976Swpaul 117221407Smarius sc = device_get_softc(dev); 118232246Syongari ma = device_get_ivars(dev); 119232246Syongari flags = 0; 120232246Syongari if (strcmp(ma->mii_data->mii_ifp->if_dname, "re") == 0) 121232246Syongari flags |= MIIF_PHYPRIV0; 122232246Syongari mii_phy_dev_attach(dev, flags, &rgephy_funcs, 0); 123119976Swpaul 124215298Smarius /* RTL8169S do not report auto-sense; add manually. */ 125215298Smarius sc->mii_capabilities = (PHY_READ(sc, MII_BMSR) | BMSR_ANEG) & 126221407Smarius sc->mii_capmask; 127165313Smarius if (sc->mii_capabilities & BMSR_EXTSTAT) 128165313Smarius sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 129119976Swpaul device_printf(dev, " "); 130164831Smarius mii_phy_add_media(sc); 131119976Swpaul printf("\n"); 132217415Smarius /* 133217415Smarius * Allow IFM_FLAG0 to be set indicating that auto-negotiation with 134217415Smarius * manual configuration, which is used to work around issues with 135217415Smarius * certain setups by default, should not be triggered as it may in 136217415Smarius * turn cause harm in some edge cases. 137217415Smarius */ 138221407Smarius sc->mii_pdata->mii_media.ifm_mask |= IFM_FLAG0; 139119976Swpaul 140221407Smarius PHY_RESET(sc); 141221407Smarius 142119976Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 143164830Smarius return (0); 144119976Swpaul} 145119976Swpaul 146119976Swpaulstatic int 147150763Simprgephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 148119976Swpaul{ 149119976Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 150161237Syongari int reg, speed, gig, anar; 151119976Swpaul 152119976Swpaul switch (cmd) { 153119976Swpaul case MII_POLLSTAT: 154119976Swpaul break; 155119976Swpaul 156119976Swpaul case MII_MEDIACHG: 157119976Swpaul /* 158119976Swpaul * If the interface is not up, don't do anything. 159119976Swpaul */ 160119976Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 161119976Swpaul break; 162119976Swpaul 163221407Smarius PHY_RESET(sc); /* XXX hardware bug work-around */ 164119976Swpaul 165161237Syongari anar = PHY_READ(sc, RGEPHY_MII_ANAR); 166215298Smarius anar &= ~(RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP | 167215298Smarius RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX | 168161237Syongari RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10); 169161237Syongari 170119976Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 171119976Swpaul case IFM_AUTO: 172119976Swpaul#ifdef foo 173119976Swpaul /* 174119976Swpaul * If we're already in auto mode, just return. 175119976Swpaul */ 176119976Swpaul if (PHY_READ(sc, RGEPHY_MII_BMCR) & RGEPHY_BMCR_AUTOEN) 177119976Swpaul return (0); 178119976Swpaul#endif 179215298Smarius (void)rgephy_mii_phy_auto(sc, ife->ifm_media); 180119976Swpaul break; 181119976Swpaul case IFM_1000_T: 182119976Swpaul speed = RGEPHY_S1000; 183119976Swpaul goto setit; 184119976Swpaul case IFM_100_TX: 185119976Swpaul speed = RGEPHY_S100; 186161237Syongari anar |= RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX; 187119976Swpaul goto setit; 188119976Swpaul case IFM_10_T: 189119976Swpaul speed = RGEPHY_S10; 190161237Syongari anar |= RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10; 191119976Swpaulsetit: 192217415Smarius if ((ife->ifm_media & IFM_FLOW) != 0 && 193217415Smarius (mii->mii_media.ifm_media & IFM_FLAG0) != 0) 194217415Smarius return (EINVAL); 195217415Smarius 196217415Smarius if ((ife->ifm_media & IFM_FDX) != 0) { 197119976Swpaul speed |= RGEPHY_BMCR_FDX; 198119976Swpaul gig = RGEPHY_1000CTL_AFD; 199161237Syongari anar &= ~(RGEPHY_ANAR_TX | RGEPHY_ANAR_10); 200217415Smarius if ((ife->ifm_media & IFM_FLOW) != 0 || 201217415Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 202217415Smarius anar |= 203217415Smarius RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP; 204119976Swpaul } else { 205119976Swpaul gig = RGEPHY_1000CTL_AHD; 206161237Syongari anar &= 207161237Syongari ~(RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_10_FD); 208119976Swpaul } 209217415Smarius if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 210217415Smarius gig |= RGEPHY_1000CTL_MSE; 211217415Smarius if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 212217415Smarius gig |= RGEPHY_1000CTL_MSC; 213217415Smarius } else { 214217415Smarius gig = 0; 215217415Smarius anar &= ~RGEPHY_ANAR_ASP; 216161237Syongari } 217217415Smarius if ((mii->mii_media.ifm_media & IFM_FLAG0) == 0) 218217415Smarius speed |= 219217415Smarius RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG; 220217415Smarius rgephy_loop(sc); 221215298Smarius PHY_WRITE(sc, RGEPHY_MII_1000CTL, gig); 222215298Smarius PHY_WRITE(sc, RGEPHY_MII_ANAR, anar); 223217415Smarius PHY_WRITE(sc, RGEPHY_MII_BMCR, speed); 224119976Swpaul break; 225119976Swpaul case IFM_NONE: 226215298Smarius PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN); 227119976Swpaul break; 228119976Swpaul default: 229119976Swpaul return (EINVAL); 230119976Swpaul } 231119976Swpaul break; 232119976Swpaul 233119976Swpaul case MII_TICK: 234119976Swpaul /* 235119976Swpaul * Is the interface even up? 236119976Swpaul */ 237119976Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 238119976Swpaul return (0); 239119976Swpaul 240119976Swpaul /* 241119976Swpaul * Only used for autonegotiation. 242119976Swpaul */ 243173128Syongari if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 244173128Syongari sc->mii_ticks = 0; 245119976Swpaul break; 246173128Syongari } 247119976Swpaul 248119976Swpaul /* 249119976Swpaul * Check to see if we have link. If we do, we don't 250217415Smarius * need to restart the autonegotiation process. 251119976Swpaul */ 252232246Syongari if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && 253232246Syongari sc->mii_mpd_rev >= 2) { 254173129Syongari /* RTL8211B(L) */ 255173129Syongari reg = PHY_READ(sc, RGEPHY_MII_SSR); 256173129Syongari if (reg & RGEPHY_SSR_LINK) { 257173129Syongari sc->mii_ticks = 0; 258173129Syongari break; 259173129Syongari } 260173129Syongari } else { 261173129Syongari reg = PHY_READ(sc, RL_GMEDIASTAT); 262173129Syongari if (reg & RL_GMEDIASTAT_LINK) { 263173129Syongari sc->mii_ticks = 0; 264173129Syongari break; 265173129Syongari } 266173128Syongari } 267119976Swpaul 268173128Syongari /* Announce link loss right after it happens. */ 269173128Syongari if (sc->mii_ticks++ == 0) 270128870Sandre break; 271164830Smarius 272173128Syongari /* Only retry autonegotiation every mii_anegticks seconds. */ 273173128Syongari if (sc->mii_ticks <= sc->mii_anegticks) 274173128Syongari return (0); 275173128Syongari 276119976Swpaul sc->mii_ticks = 0; 277215298Smarius rgephy_mii_phy_auto(sc, ife->ifm_media); 278173128Syongari break; 279119976Swpaul } 280119976Swpaul 281119976Swpaul /* Update the media status. */ 282221407Smarius PHY_STATUS(sc); 283119976Swpaul 284119976Swpaul /* 285119976Swpaul * Callback if something changed. Note that we need to poke 286119983Swpaul * the DSP on the RealTek PHYs if the media changes. 287119976Swpaul * 288119976Swpaul */ 289164830Smarius if (sc->mii_media_active != mii->mii_media_active || 290119976Swpaul sc->mii_media_status != mii->mii_media_status || 291119976Swpaul cmd == MII_MEDIACHG) { 292119976Swpaul rgephy_load_dspcode(sc); 293119976Swpaul } 294128870Sandre mii_phy_update(sc, cmd); 295119976Swpaul return (0); 296119976Swpaul} 297119976Swpaul 298119976Swpaulstatic void 299150763Simprgephy_status(struct mii_softc *sc) 300119976Swpaul{ 301119976Swpaul struct mii_data *mii = sc->mii_pdata; 302119983Swpaul int bmsr, bmcr; 303173129Syongari uint16_t ssr; 304119976Swpaul 305119976Swpaul mii->mii_media_status = IFM_AVALID; 306119976Swpaul mii->mii_media_active = IFM_ETHER; 307119976Swpaul 308232246Syongari if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && sc->mii_mpd_rev >= 2) { 309173129Syongari ssr = PHY_READ(sc, RGEPHY_MII_SSR); 310173129Syongari if (ssr & RGEPHY_SSR_LINK) 311173129Syongari mii->mii_media_status |= IFM_ACTIVE; 312173129Syongari } else { 313173129Syongari bmsr = PHY_READ(sc, RL_GMEDIASTAT); 314173129Syongari if (bmsr & RL_GMEDIASTAT_LINK) 315173129Syongari mii->mii_media_status |= IFM_ACTIVE; 316173129Syongari } 317119976Swpaul 318119976Swpaul bmsr = PHY_READ(sc, RGEPHY_MII_BMSR); 319119976Swpaul 320119976Swpaul bmcr = PHY_READ(sc, RGEPHY_MII_BMCR); 321176809Syongari if (bmcr & RGEPHY_BMCR_ISO) { 322176809Syongari mii->mii_media_active |= IFM_NONE; 323176809Syongari mii->mii_media_status = 0; 324176809Syongari return; 325176809Syongari } 326119976Swpaul 327119976Swpaul if (bmcr & RGEPHY_BMCR_LOOP) 328119976Swpaul mii->mii_media_active |= IFM_LOOP; 329119976Swpaul 330119976Swpaul if (bmcr & RGEPHY_BMCR_AUTOEN) { 331119976Swpaul if ((bmsr & RGEPHY_BMSR_ACOMP) == 0) { 332119976Swpaul /* Erg, still trying, I guess... */ 333119976Swpaul mii->mii_media_active |= IFM_NONE; 334119976Swpaul return; 335119976Swpaul } 336119976Swpaul } 337119976Swpaul 338232246Syongari if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && sc->mii_mpd_rev >= 2) { 339173129Syongari ssr = PHY_READ(sc, RGEPHY_MII_SSR); 340173129Syongari switch (ssr & RGEPHY_SSR_SPD_MASK) { 341173129Syongari case RGEPHY_SSR_S1000: 342173129Syongari mii->mii_media_active |= IFM_1000_T; 343173129Syongari break; 344173129Syongari case RGEPHY_SSR_S100: 345173129Syongari mii->mii_media_active |= IFM_100_TX; 346173129Syongari break; 347173129Syongari case RGEPHY_SSR_S10: 348173129Syongari mii->mii_media_active |= IFM_10_T; 349173129Syongari break; 350173129Syongari default: 351173129Syongari mii->mii_media_active |= IFM_NONE; 352173129Syongari break; 353173129Syongari } 354173129Syongari if (ssr & RGEPHY_SSR_FDX) 355173129Syongari mii->mii_media_active |= IFM_FDX; 356173129Syongari else 357173129Syongari mii->mii_media_active |= IFM_HDX; 358173129Syongari } else { 359173129Syongari bmsr = PHY_READ(sc, RL_GMEDIASTAT); 360173129Syongari if (bmsr & RL_GMEDIASTAT_1000MBPS) 361173129Syongari mii->mii_media_active |= IFM_1000_T; 362173129Syongari else if (bmsr & RL_GMEDIASTAT_100MBPS) 363173129Syongari mii->mii_media_active |= IFM_100_TX; 364173129Syongari else if (bmsr & RL_GMEDIASTAT_10MBPS) 365173129Syongari mii->mii_media_active |= IFM_10_T; 366173129Syongari else 367173129Syongari mii->mii_media_active |= IFM_NONE; 368173129Syongari if (bmsr & RL_GMEDIASTAT_FDX) 369173129Syongari mii->mii_media_active |= IFM_FDX; 370173129Syongari else 371173129Syongari mii->mii_media_active |= IFM_HDX; 372173129Syongari } 373215298Smarius 374215298Smarius if ((mii->mii_media_active & IFM_FDX) != 0) 375215298Smarius mii->mii_media_active |= mii_phy_flowstatus(sc); 376215298Smarius 377215298Smarius if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && 378215298Smarius (PHY_READ(sc, RGEPHY_MII_1000STS) & RGEPHY_1000STS_MSR) != 0) 379215298Smarius mii->mii_media_active |= IFM_ETH_MASTER; 380119976Swpaul} 381119976Swpaul 382119976Swpaulstatic int 383215298Smariusrgephy_mii_phy_auto(struct mii_softc *sc, int media) 384119976Swpaul{ 385215298Smarius int anar; 386164830Smarius 387215298Smarius rgephy_loop(sc); 388221407Smarius PHY_RESET(sc); 389119976Swpaul 390215298Smarius anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 391215298Smarius if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 392215298Smarius anar |= RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP; 393215298Smarius PHY_WRITE(sc, RGEPHY_MII_ANAR, anar); 394119976Swpaul DELAY(1000); 395215298Smarius PHY_WRITE(sc, RGEPHY_MII_1000CTL, 396215298Smarius RGEPHY_1000CTL_AHD | RGEPHY_1000CTL_AFD); 397119976Swpaul DELAY(1000); 398215298Smarius PHY_WRITE(sc, RGEPHY_MII_BMCR, 399119976Swpaul RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG); 400119976Swpaul DELAY(100); 401119976Swpaul 402119976Swpaul return (EJUSTRETURN); 403119976Swpaul} 404119976Swpaul 405119976Swpaulstatic void 406119976Swpaulrgephy_loop(struct mii_softc *sc) 407119976Swpaul{ 408119976Swpaul int i; 409119976Swpaul 410257612Syongari if (sc->mii_mpd_model != MII_MODEL_REALTEK_RTL8251 && 411257612Syongari sc->mii_mpd_rev < 2) { 412173129Syongari PHY_WRITE(sc, RGEPHY_MII_BMCR, RGEPHY_BMCR_PDOWN); 413173129Syongari DELAY(1000); 414173129Syongari } 415119976Swpaul 416119976Swpaul for (i = 0; i < 15000; i++) { 417164830Smarius if (!(PHY_READ(sc, RGEPHY_MII_BMSR) & RGEPHY_BMSR_LINK)) { 418119976Swpaul#if 0 419119976Swpaul device_printf(sc->mii_dev, "looped %d\n", i); 420119976Swpaul#endif 421119976Swpaul break; 422119976Swpaul } 423119976Swpaul DELAY(10); 424119976Swpaul } 425119976Swpaul} 426119976Swpaul 427119976Swpaul#define PHY_SETBIT(x, y, z) \ 428119976Swpaul PHY_WRITE(x, y, (PHY_READ(x, y) | (z))) 429119976Swpaul#define PHY_CLRBIT(x, y, z) \ 430119976Swpaul PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z))) 431119976Swpaul 432119983Swpaul/* 433119983Swpaul * Initialize RealTek PHY per the datasheet. The DSP in the PHYs of 434119983Swpaul * existing revisions of the 8169S/8110S chips need to be tuned in 435159962Swpaul * order to reliably negotiate a 1000Mbps link. This is only needed 436159962Swpaul * for rev 0 and rev 1 of the PHY. Later versions work without 437159962Swpaul * any fixups. 438119983Swpaul */ 439119976Swpaulstatic void 440119976Swpaulrgephy_load_dspcode(struct mii_softc *sc) 441119976Swpaul{ 442119976Swpaul int val; 443119976Swpaul 444257612Syongari if (sc->mii_mpd_model == MII_MODEL_REALTEK_RTL8251 || 445257612Syongari sc->mii_mpd_rev >= 2) 446159962Swpaul return; 447159962Swpaul 448119976Swpaul PHY_WRITE(sc, 31, 0x0001); 449119976Swpaul PHY_WRITE(sc, 21, 0x1000); 450119976Swpaul PHY_WRITE(sc, 24, 0x65C7); 451119976Swpaul PHY_CLRBIT(sc, 4, 0x0800); 452119976Swpaul val = PHY_READ(sc, 4) & 0xFFF; 453119976Swpaul PHY_WRITE(sc, 4, val); 454119976Swpaul PHY_WRITE(sc, 3, 0x00A1); 455119976Swpaul PHY_WRITE(sc, 2, 0x0008); 456119976Swpaul PHY_WRITE(sc, 1, 0x1020); 457119976Swpaul PHY_WRITE(sc, 0, 0x1000); 458119976Swpaul PHY_SETBIT(sc, 4, 0x0800); 459119976Swpaul PHY_CLRBIT(sc, 4, 0x0800); 460119976Swpaul val = (PHY_READ(sc, 4) & 0xFFF) | 0x7000; 461119976Swpaul PHY_WRITE(sc, 4, val); 462119976Swpaul PHY_WRITE(sc, 3, 0xFF41); 463119976Swpaul PHY_WRITE(sc, 2, 0xDE60); 464119976Swpaul PHY_WRITE(sc, 1, 0x0140); 465119976Swpaul PHY_WRITE(sc, 0, 0x0077); 466119976Swpaul val = (PHY_READ(sc, 4) & 0xFFF) | 0xA000; 467119976Swpaul PHY_WRITE(sc, 4, val); 468119976Swpaul PHY_WRITE(sc, 3, 0xDF01); 469119976Swpaul PHY_WRITE(sc, 2, 0xDF20); 470119976Swpaul PHY_WRITE(sc, 1, 0xFF95); 471119976Swpaul PHY_WRITE(sc, 0, 0xFA00); 472119976Swpaul val = (PHY_READ(sc, 4) & 0xFFF) | 0xB000; 473119976Swpaul PHY_WRITE(sc, 4, val); 474119976Swpaul PHY_WRITE(sc, 3, 0xFF41); 475119976Swpaul PHY_WRITE(sc, 2, 0xDE20); 476119976Swpaul PHY_WRITE(sc, 1, 0x0140); 477119976Swpaul PHY_WRITE(sc, 0, 0x00BB); 478119976Swpaul val = (PHY_READ(sc, 4) & 0xFFF) | 0xF000; 479119976Swpaul PHY_WRITE(sc, 4, val); 480119976Swpaul PHY_WRITE(sc, 3, 0xDF01); 481119976Swpaul PHY_WRITE(sc, 2, 0xDF20); 482119976Swpaul PHY_WRITE(sc, 1, 0xFF95); 483119976Swpaul PHY_WRITE(sc, 0, 0xBF00); 484119976Swpaul PHY_SETBIT(sc, 4, 0x0800); 485119976Swpaul PHY_CLRBIT(sc, 4, 0x0800); 486119976Swpaul PHY_WRITE(sc, 31, 0x0000); 487164830Smarius 488119976Swpaul DELAY(40); 489119976Swpaul} 490119976Swpaul 491119976Swpaulstatic void 492119976Swpaulrgephy_reset(struct mii_softc *sc) 493119976Swpaul{ 494248542Syongari uint16_t pcr, ssr; 495164830Smarius 496232246Syongari if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && sc->mii_mpd_rev == 3) { 497180178Syongari /* RTL8211C(L) */ 498180178Syongari ssr = PHY_READ(sc, RGEPHY_MII_SSR); 499180178Syongari if ((ssr & RGEPHY_SSR_ALDPS) != 0) { 500180178Syongari ssr &= ~RGEPHY_SSR_ALDPS; 501180178Syongari PHY_WRITE(sc, RGEPHY_MII_SSR, ssr); 502180178Syongari } 503180178Syongari } 504180178Syongari 505248542Syongari if (sc->mii_mpd_rev >= 2) { 506248542Syongari pcr = PHY_READ(sc, RGEPHY_MII_PCR); 507248542Syongari if ((pcr & RGEPHY_PCR_MDIX_AUTO) == 0) { 508248542Syongari pcr &= ~RGEPHY_PCR_MDI_MASK; 509248542Syongari pcr |= RGEPHY_PCR_MDIX_AUTO; 510248542Syongari PHY_WRITE(sc, RGEPHY_MII_PCR, pcr); 511248542Syongari } 512248542Syongari } 513248542Syongari 514119976Swpaul mii_phy_reset(sc); 515119976Swpaul DELAY(1000); 516119976Swpaul rgephy_load_dspcode(sc); 517119976Swpaul} 518