1139749Simp/*- 259477Swpaul * Copyright (c) 2000 359477Swpaul * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 459477Swpaul * 559477Swpaul * Redistribution and use in source and binary forms, with or without 659477Swpaul * modification, are permitted provided that the following conditions 759477Swpaul * are met: 859477Swpaul * 1. Redistributions of source code must retain the above copyright 959477Swpaul * notice, this list of conditions and the following disclaimer. 1059477Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1159477Swpaul * notice, this list of conditions and the following disclaimer in the 1259477Swpaul * documentation and/or other materials provided with the distribution. 1359477Swpaul * 3. All advertising materials mentioning features or use of this software 1459477Swpaul * must display the following acknowledgement: 1559477Swpaul * This product includes software developed by Bill Paul. 1659477Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1759477Swpaul * may be used to endorse or promote products derived from this software 1859477Swpaul * without specific prior written permission. 1959477Swpaul * 2059477Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2159477Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259477Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359477Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2459477Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2559477Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2659477Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2759477Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2859477Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2959477Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3059477Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3159477Swpaul */ 3259477Swpaul 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/mii/brgphy.c 359843 2020-04-13 00:50:35Z jhibbits $"); 35119418Sobrien 3659477Swpaul/* 37166680Sjkim * Driver for the Broadcom BCM54xx/57xx 1000baseTX PHY. 3859477Swpaul */ 3959477Swpaul 4059477Swpaul#include <sys/param.h> 4159477Swpaul#include <sys/systm.h> 4259477Swpaul#include <sys/kernel.h> 43129876Sphk#include <sys/module.h> 4459477Swpaul#include <sys/socket.h> 4559477Swpaul#include <sys/bus.h> 46266974Smarcel#include <sys/taskqueue.h> 4759477Swpaul 4859477Swpaul#include <net/if.h> 49257184Sglebius#include <net/if_var.h> 50157642Sps#include <net/ethernet.h> 5159477Swpaul#include <net/if_media.h> 5259477Swpaul 5359477Swpaul#include <dev/mii/mii.h> 5459477Swpaul#include <dev/mii/miivar.h> 55109514Sobrien#include "miidevs.h" 5659477Swpaul 5759477Swpaul#include <dev/mii/brgphyreg.h> 58117659Swpaul#include <net/if_arp.h> 59117659Swpaul#include <machine/bus.h> 60117659Swpaul#include <dev/bge/if_bgereg.h> 61157642Sps#include <dev/bce/if_bcereg.h> 6259477Swpaul 63119285Simp#include <dev/pci/pcireg.h> 64119285Simp#include <dev/pci/pcivar.h> 65118814Swpaul 6659477Swpaul#include "miibus_if.h" 6759477Swpaul 68105135Salfredstatic int brgphy_probe(device_t); 69105135Salfredstatic int brgphy_attach(device_t); 7059477Swpaul 71166037Sjkimstruct brgphy_softc { 72166037Sjkim struct mii_softc mii_sc; 73170391Sdavidch int serdes_flags; /* Keeps track of the serdes type used */ 74204941Ssobomax#define BRGPHY_5706S 0x0001 75204941Ssobomax#define BRGPHY_5708S 0x0002 76204941Ssobomax#define BRGPHY_NOANWAIT 0x0004 77205299Sdavidch#define BRGPHY_5709S 0x0008 78179772Sdavidch int bce_phy_flags; /* PHY flags transferred from the MAC driver */ 79166037Sjkim}; 80166037Sjkim 8159477Swpaulstatic device_method_t brgphy_methods[] = { 8259477Swpaul /* device interface */ 8359477Swpaul DEVMETHOD(device_probe, brgphy_probe), 8459477Swpaul DEVMETHOD(device_attach, brgphy_attach), 8595722Sphk DEVMETHOD(device_detach, mii_phy_detach), 8659477Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 87227908Smarius DEVMETHOD_END 8859477Swpaul}; 8959477Swpaul 9059477Swpaulstatic devclass_t brgphy_devclass; 9159477Swpaul 9259477Swpaulstatic driver_t brgphy_driver = { 9359477Swpaul "brgphy", 9459477Swpaul brgphy_methods, 95166049Sjkim sizeof(struct brgphy_softc) 9659477Swpaul}; 9759477Swpaul 9859477SwpaulDRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0); 9959477Swpaul 10084145Sjlemonstatic int brgphy_service(struct mii_softc *, struct mii_data *, int); 101215297Smariusstatic void brgphy_setmedia(struct mii_softc *, int); 10284145Sjlemonstatic void brgphy_status(struct mii_softc *); 103215297Smariusstatic void brgphy_mii_phy_auto(struct mii_softc *, int); 104114590Spsstatic void brgphy_reset(struct mii_softc *); 105170391Sdavidchstatic void brgphy_enable_loopback(struct mii_softc *); 106114590Spsstatic void bcm5401_load_dspcode(struct mii_softc *); 107114590Spsstatic void bcm5411_load_dspcode(struct mii_softc *); 108204144Smariusstatic void bcm54k2_load_dspcode(struct mii_softc *); 109166676Sjkimstatic void brgphy_fixup_5704_a0_bug(struct mii_softc *); 110166031Sjkimstatic void brgphy_fixup_adc_bug(struct mii_softc *); 111166673Sjkimstatic void brgphy_fixup_adjust_trim(struct mii_softc *); 112166031Sjkimstatic void brgphy_fixup_ber_bug(struct mii_softc *); 113166677Sjkimstatic void brgphy_fixup_crc_bug(struct mii_softc *); 114166031Sjkimstatic void brgphy_fixup_jitter_bug(struct mii_softc *); 115166032Sjkimstatic void brgphy_ethernet_wirespeed(struct mii_softc *); 116166032Sjkimstatic void brgphy_jumbo_settings(struct mii_softc *, u_long); 11759477Swpaul 118160078Syongaristatic const struct mii_phydesc brgphys[] = { 119221407Smarius MII_PHY_DESC(BROADCOM, BCM5400), 120221407Smarius MII_PHY_DESC(BROADCOM, BCM5401), 121287470Ssbruno MII_PHY_DESC(BROADCOM, BCM5402), 122221407Smarius MII_PHY_DESC(BROADCOM, BCM5411), 123287470Ssbruno MII_PHY_DESC(BROADCOM, BCM5404), 124287470Ssbruno MII_PHY_DESC(BROADCOM, BCM5424), 125221407Smarius MII_PHY_DESC(BROADCOM, BCM54K2), 126221407Smarius MII_PHY_DESC(BROADCOM, BCM5701), 127221407Smarius MII_PHY_DESC(BROADCOM, BCM5703), 128221407Smarius MII_PHY_DESC(BROADCOM, BCM5704), 129221407Smarius MII_PHY_DESC(BROADCOM, BCM5705), 130221407Smarius MII_PHY_DESC(BROADCOM, BCM5706), 131221407Smarius MII_PHY_DESC(BROADCOM, BCM5714), 132221407Smarius MII_PHY_DESC(BROADCOM, BCM5421), 133221407Smarius MII_PHY_DESC(BROADCOM, BCM5750), 134221407Smarius MII_PHY_DESC(BROADCOM, BCM5752), 135221407Smarius MII_PHY_DESC(BROADCOM, BCM5780), 136221407Smarius MII_PHY_DESC(BROADCOM, BCM5708C), 137286041Ssbruno MII_PHY_DESC(BROADCOM, BCM5466), 138287470Ssbruno MII_PHY_DESC(BROADCOM2, BCM5478), 139287470Ssbruno MII_PHY_DESC(BROADCOM2, BCM5488), 140221407Smarius MII_PHY_DESC(BROADCOM2, BCM5482), 141221407Smarius MII_PHY_DESC(BROADCOM2, BCM5708S), 142221407Smarius MII_PHY_DESC(BROADCOM2, BCM5709C), 143221407Smarius MII_PHY_DESC(BROADCOM2, BCM5709S), 144221407Smarius MII_PHY_DESC(BROADCOM2, BCM5709CAX), 145221407Smarius MII_PHY_DESC(BROADCOM2, BCM5722), 146221407Smarius MII_PHY_DESC(BROADCOM2, BCM5755), 147221407Smarius MII_PHY_DESC(BROADCOM2, BCM5754), 148221407Smarius MII_PHY_DESC(BROADCOM2, BCM5761), 149221407Smarius MII_PHY_DESC(BROADCOM2, BCM5784), 150227913Smarius#ifdef notyet /* better handled by ukphy(4) until WARs are implemented */ 151227907Smarius MII_PHY_DESC(BROADCOM2, BCM5785), 152227913Smarius#endif 153359843Sjhibbits MII_PHY_DESC(BROADCOM3, BCM54618SE), 154221407Smarius MII_PHY_DESC(BROADCOM3, BCM5717C), 155221713Syongari MII_PHY_DESC(BROADCOM3, BCM5719C), 156226870Syongari MII_PHY_DESC(BROADCOM3, BCM5720C), 157221407Smarius MII_PHY_DESC(BROADCOM3, BCM57765), 158231913Smarius MII_PHY_DESC(BROADCOM3, BCM57780), 159253481Syongari MII_PHY_DESC(BROADCOM4, BCM5725C), 160221407Smarius MII_PHY_DESC(xxBROADCOM_ALT1, BCM5906), 161160078Syongari MII_PHY_END 162160078Syongari}; 163160078Syongari 164221407Smariusstatic const struct mii_phy_funcs brgphy_funcs = { 165221407Smarius brgphy_service, 166221407Smarius brgphy_status, 167221407Smarius brgphy_reset 168221407Smarius}; 169221407Smarius 170281877Syongaristatic const struct hs21_type { 171281877Syongari const uint32_t id; 172281877Syongari const char *prod; 173281877Syongari} hs21_type_lists[] = { 174281877Syongari { 0x57081021, "IBM eServer BladeCenter HS21" }, 175281877Syongari { 0x57081011, "IBM eServer BladeCenter HS21 -[8853PAU]-" }, 176281877Syongari}; 177170391Sdavidch 178204989Ssobomaxstatic int 179204989Ssobomaxdetect_hs21(struct bce_softc *bce_sc) 180204989Ssobomax{ 181204989Ssobomax char *sysenv; 182281877Syongari int found, i; 183204989Ssobomax 184215353Sjkim found = 0; 185281877Syongari sysenv = kern_getenv("smbios.system.product"); 186281877Syongari if (sysenv == NULL) 187281877Syongari return (found); 188281877Syongari for (i = 0; i < nitems(hs21_type_lists); i++) { 189281877Syongari if (bce_sc->bce_chipid == hs21_type_lists[i].id && 190281877Syongari strncmp(sysenv, hs21_type_lists[i].prod, 191281877Syongari strlen(hs21_type_lists[i].prod)) == 0) { 192281877Syongari found++; 193281877Syongari break; 194215353Sjkim } 195215353Sjkim } 196281877Syongari freeenv(sysenv); 197215353Sjkim return (found); 198204989Ssobomax} 199204989Ssobomax 200170391Sdavidch/* Search for our PHY in the list of known PHYs */ 201105135Salfredstatic int 202150763Simpbrgphy_probe(device_t dev) 20359477Swpaul{ 204215297Smarius 205170391Sdavidch return (mii_phy_dev_probe(dev, brgphys, BUS_PROBE_DEFAULT)); 20659477Swpaul} 20759477Swpaul 208170391Sdavidch/* Attach the PHY to the MII bus */ 209105135Salfredstatic int 210150763Simpbrgphy_attach(device_t dev) 21159477Swpaul{ 212166037Sjkim struct brgphy_softc *bsc; 213170391Sdavidch struct bge_softc *bge_sc = NULL; 214170391Sdavidch struct bce_softc *bce_sc = NULL; 21559477Swpaul struct mii_softc *sc; 21659477Swpaul 217166037Sjkim bsc = device_get_softc(dev); 218166037Sjkim sc = &bsc->mii_sc; 21959477Swpaul 220221407Smarius mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, 221221407Smarius &brgphy_funcs, 0); 222213364Smarius 223170391Sdavidch bsc->serdes_flags = 0; 224170391Sdavidch 225244482Syongari /* Find the MAC driver associated with this PHY. */ 226277093Sglebius if (mii_dev_mac_match(dev, "bge")) 227277093Sglebius bge_sc = mii_dev_mac_softc(dev); 228277093Sglebius else if (mii_dev_mac_match(dev, "bce")) 229277093Sglebius bce_sc = mii_dev_mac_softc(dev); 230244482Syongari 231170391Sdavidch /* Handle any special cases based on the PHY ID */ 232221407Smarius switch (sc->mii_mpd_oui) { 233181617Syongari case MII_OUI_BROADCOM: 234221407Smarius switch (sc->mii_mpd_model) { 235221407Smarius case MII_MODEL_BROADCOM_BCM5706: 236221407Smarius case MII_MODEL_BROADCOM_BCM5714: 237205299Sdavidch /* 238205299Sdavidch * The 5464 PHY used in the 5706 supports both copper 239205299Sdavidch * and fiber interfaces over GMII. Need to check the 240205299Sdavidch * shadow registers to see which mode is actually 241205299Sdavidch * in effect, and therefore whether we have 5706C or 242205299Sdavidch * 5706S. 243205299Sdavidch */ 244205299Sdavidch PHY_WRITE(sc, BRGPHY_MII_SHADOW_1C, 245205299Sdavidch BRGPHY_SHADOW_1C_MODE_CTRL); 246205299Sdavidch if (PHY_READ(sc, BRGPHY_MII_SHADOW_1C) & 247205299Sdavidch BRGPHY_SHADOW_1C_ENA_1000X) { 248205299Sdavidch bsc->serdes_flags |= BRGPHY_5706S; 249205299Sdavidch sc->mii_flags |= MIIF_HAVEFIBER; 250205299Sdavidch } 251205299Sdavidch break; 252231913Smarius } 253231913Smarius break; 254221407Smarius case MII_OUI_BROADCOM2: 255221407Smarius switch (sc->mii_mpd_model) { 256221407Smarius case MII_MODEL_BROADCOM2_BCM5708S: 257205299Sdavidch bsc->serdes_flags |= BRGPHY_5708S; 258205299Sdavidch sc->mii_flags |= MIIF_HAVEFIBER; 259205299Sdavidch break; 260221407Smarius case MII_MODEL_BROADCOM2_BCM5709S: 261244482Syongari /* 262244482Syongari * XXX 263244482Syongari * 5720S and 5709S shares the same PHY id. 264244482Syongari * Assume 5720S PHY if parent device is bge(4). 265244482Syongari */ 266244482Syongari if (bge_sc != NULL) 267244482Syongari bsc->serdes_flags |= BRGPHY_5708S; 268244482Syongari else 269244482Syongari bsc->serdes_flags |= BRGPHY_5709S; 270212307Syongari sc->mii_flags |= MIIF_HAVEFIBER; 271212307Syongari break; 272212307Syongari } 273212307Syongari break; 274170391Sdavidch } 275170391Sdavidch 276221407Smarius PHY_RESET(sc); 27759477Swpaul 278214012Smarius /* Read the PHY's capabilities. */ 279221407Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; 280214012Smarius if (sc->mii_capabilities & BMSR_EXTSTAT) 281214012Smarius sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 28259477Swpaul device_printf(dev, " "); 28359477Swpaul 284170391Sdavidch /* Add the supported media types */ 285170391Sdavidch if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 286215297Smarius mii_phy_add_media(sc); 287215297Smarius printf("\n"); 288170391Sdavidch } else { 289215297Smarius sc->mii_anegticks = MII_ANEGTICKS_GIGE; 290281816Sglebius ifmedia_add(&sc->mii_pdata->mii_media, 291281816Sglebius IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 292281816Sglebius 0, NULL); 293170391Sdavidch printf("1000baseSX-FDX, "); 294281816Sglebius /* 295281816Sglebius * 2.5G support is a software enabled feature 296281816Sglebius * on the 5708S and 5709S. 297281816Sglebius */ 298281816Sglebius if (bce_sc && (bce_sc->bce_phy_flags & 299281816Sglebius BCE_PHY_2_5G_CAPABLE_FLAG)) { 300281816Sglebius ifmedia_add(&sc->mii_pdata->mii_media, 301281816Sglebius IFM_MAKEWORD(IFM_ETHER, IFM_2500_SX, IFM_FDX, 302281816Sglebius sc->mii_inst), 0, NULL); 303170391Sdavidch printf("2500baseSX-FDX, "); 304204941Ssobomax } else if ((bsc->serdes_flags & BRGPHY_5708S) && bce_sc && 305204989Ssobomax (detect_hs21(bce_sc) != 0)) { 306204941Ssobomax /* 307204941Ssobomax * There appears to be certain silicon revision 308204989Ssobomax * in IBM HS21 blades that is having issues with 309204941Ssobomax * this driver wating for the auto-negotiation to 310204941Ssobomax * complete. This happens with a specific chip id 311204941Ssobomax * only and when the 1000baseSX-FDX is the only 312204941Ssobomax * mode. Workaround this issue since it's unlikely 313204941Ssobomax * to be ever addressed. 314204941Ssobomax */ 315204941Ssobomax printf("auto-neg workaround, "); 316204941Ssobomax bsc->serdes_flags |= BRGPHY_NOANWAIT; 317170391Sdavidch } 318281816Sglebius ifmedia_add(&sc->mii_pdata->mii_media, IFM_MAKEWORD(IFM_ETHER, 319281816Sglebius IFM_AUTO, 0, sc->mii_inst), 0, NULL); 320215297Smarius printf("auto\n"); 321170391Sdavidch } 322170391Sdavidch 32359477Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 324164830Smarius return (0); 32559477Swpaul} 32659477Swpaul 32784145Sjlemonstatic int 328150763Simpbrgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 32959477Swpaul{ 33059477Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 331170391Sdavidch int val; 33259477Swpaul 33359477Swpaul switch (cmd) { 33459477Swpaul case MII_POLLSTAT: 33559477Swpaul break; 33659477Swpaul case MII_MEDIACHG: 337170391Sdavidch /* Todo: Why is this here? Is it really needed? */ 338221407Smarius PHY_RESET(sc); /* XXX hardware bug work-around */ 33959477Swpaul 34059477Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 34159477Swpaul case IFM_AUTO: 342215297Smarius brgphy_mii_phy_auto(sc, ife->ifm_media); 34359477Swpaul break; 344170391Sdavidch case IFM_2500_SX: 345170391Sdavidch case IFM_1000_SX: 34695673Sphk case IFM_1000_T: 34783029Swpaul case IFM_100_TX: 34883029Swpaul case IFM_10_T: 349215297Smarius brgphy_setmedia(sc, ife->ifm_media); 35059477Swpaul break; 35159477Swpaul default: 352213364Smarius return (EINVAL); 35359477Swpaul } 35459477Swpaul break; 35559477Swpaul case MII_TICK: 356170391Sdavidch /* Bail if autoneg isn't in process. */ 357165343Soleg if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 358170391Sdavidch sc->mii_ticks = 0; 35984145Sjlemon break; 360165343Soleg } 36159477Swpaul 36259477Swpaul /* 36359477Swpaul * Check to see if we have link. If we do, we don't 364165343Soleg * need to restart the autonegotiation process. 36559477Swpaul */ 366215297Smarius val = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 367170391Sdavidch if (val & BMSR_LINK) { 368165343Soleg sc->mii_ticks = 0; /* Reset autoneg timer. */ 36959477Swpaul break; 370165343Soleg } 37159477Swpaul 372165343Soleg /* Announce link loss right after it happens. */ 373165343Soleg if (sc->mii_ticks++ == 0) 374128870Sandre break; 375164830Smarius 376165343Soleg /* Only retry autonegotiation every mii_anegticks seconds. */ 377165343Soleg if (sc->mii_ticks <= sc->mii_anegticks) 378181619Syongari break; 379165343Soleg 380170391Sdavidch 381170391Sdavidch /* Retry autonegotiation */ 38284145Sjlemon sc->mii_ticks = 0; 383215297Smarius brgphy_mii_phy_auto(sc, ife->ifm_media); 384153234Soleg break; 38559477Swpaul } 38659477Swpaul 38759477Swpaul /* Update the media status. */ 388221407Smarius PHY_STATUS(sc); 38959477Swpaul 390114628Sps /* 391114628Sps * Callback if something changed. Note that we need to poke 392114628Sps * the DSP on the Broadcom PHYs if the media changes. 393114628Sps */ 394164830Smarius if (sc->mii_media_active != mii->mii_media_active || 395114628Sps sc->mii_media_status != mii->mii_media_status || 396114628Sps cmd == MII_MEDIACHG) { 397221407Smarius switch (sc->mii_mpd_oui) { 398170391Sdavidch case MII_OUI_BROADCOM: 399221407Smarius switch (sc->mii_mpd_model) { 400221407Smarius case MII_MODEL_BROADCOM_BCM5400: 401166031Sjkim bcm5401_load_dspcode(sc); 402170391Sdavidch break; 403221407Smarius case MII_MODEL_BROADCOM_BCM5401: 404221407Smarius if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3) 405170391Sdavidch bcm5401_load_dspcode(sc); 406170391Sdavidch break; 407221407Smarius case MII_MODEL_BROADCOM_BCM5411: 408170391Sdavidch bcm5411_load_dspcode(sc); 409170391Sdavidch break; 410221407Smarius case MII_MODEL_BROADCOM_BCM54K2: 411204144Smarius bcm54k2_load_dspcode(sc); 412204144Smarius break; 413170391Sdavidch } 414166031Sjkim break; 415114628Sps } 416114628Sps } 417128870Sandre mii_phy_update(sc, cmd); 418213364Smarius return (0); 41959477Swpaul} 42059477Swpaul 421179772Sdavidch/****************************************************************************/ 422179772Sdavidch/* Sets the PHY link speed. */ 423179772Sdavidch/* */ 424179772Sdavidch/* Returns: */ 425179772Sdavidch/* None */ 426179772Sdavidch/****************************************************************************/ 42784145Sjlemonstatic void 428215297Smariusbrgphy_setmedia(struct mii_softc *sc, int media) 429165360Sjkim{ 430170391Sdavidch int bmcr = 0, gig; 431165360Sjkim 432165360Sjkim switch (IFM_SUBTYPE(media)) { 433170391Sdavidch case IFM_2500_SX: 434170391Sdavidch break; 435170391Sdavidch case IFM_1000_SX: 436165360Sjkim case IFM_1000_T: 437165360Sjkim bmcr = BRGPHY_S1000; 438165360Sjkim break; 439165360Sjkim case IFM_100_TX: 440165360Sjkim bmcr = BRGPHY_S100; 441165360Sjkim break; 442165360Sjkim case IFM_10_T: 443165360Sjkim default: 444165360Sjkim bmcr = BRGPHY_S10; 445165360Sjkim break; 446165360Sjkim } 447179772Sdavidch 448217413Smarius if ((media & IFM_FDX) != 0) { 449165360Sjkim bmcr |= BRGPHY_BMCR_FDX; 450165360Sjkim gig = BRGPHY_1000CTL_AFD; 451165360Sjkim } else { 452165360Sjkim gig = BRGPHY_1000CTL_AHD; 453165360Sjkim } 454165360Sjkim 455215297Smarius /* Force loopback to disconnect PHY from Ethernet medium. */ 456170391Sdavidch brgphy_enable_loopback(sc); 457179772Sdavidch 458165360Sjkim PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0); 459179772Sdavidch PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE); 460165360Sjkim 461215297Smarius if (IFM_SUBTYPE(media) != IFM_1000_T && 462215297Smarius IFM_SUBTYPE(media) != IFM_1000_SX) { 463215297Smarius PHY_WRITE(sc, BRGPHY_MII_BMCR, bmcr); 464215297Smarius return; 465215297Smarius } 466165360Sjkim 467215297Smarius if (IFM_SUBTYPE(media) == IFM_1000_T) { 468215297Smarius gig |= BRGPHY_1000CTL_MSE; 469215297Smarius if ((media & IFM_ETH_MASTER) != 0) 470215297Smarius gig |= BRGPHY_1000CTL_MSC; 471215297Smarius } 472165360Sjkim PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig); 473165360Sjkim PHY_WRITE(sc, BRGPHY_MII_BMCR, 474165360Sjkim bmcr | BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG); 475165360Sjkim} 476165360Sjkim 477179772Sdavidch/****************************************************************************/ 478179772Sdavidch/* Set the media status based on the PHY settings. */ 479179772Sdavidch/* */ 480179772Sdavidch/* Returns: */ 481179772Sdavidch/* None */ 482179772Sdavidch/****************************************************************************/ 483165360Sjkimstatic void 484150763Simpbrgphy_status(struct mii_softc *sc) 48559477Swpaul{ 486170391Sdavidch struct brgphy_softc *bsc = (struct brgphy_softc *)sc; 48759477Swpaul struct mii_data *mii = sc->mii_pdata; 488215297Smarius int aux, bmcr, bmsr, val, xstat; 489215297Smarius u_int flowstat; 49059477Swpaul 49159477Swpaul mii->mii_media_status = IFM_AVALID; 49259477Swpaul mii->mii_media_active = IFM_ETHER; 49359477Swpaul 494170391Sdavidch bmsr = PHY_READ(sc, BRGPHY_MII_BMSR) | PHY_READ(sc, BRGPHY_MII_BMSR); 495181618Syongari bmcr = PHY_READ(sc, BRGPHY_MII_BMCR); 496165360Sjkim 497170391Sdavidch if (bmcr & BRGPHY_BMCR_LOOP) { 49859477Swpaul mii->mii_media_active |= IFM_LOOP; 499170391Sdavidch } 50059477Swpaul 501167728Sjkim if ((bmcr & BRGPHY_BMCR_AUTOEN) && 502204941Ssobomax (bmsr & BRGPHY_BMSR_ACOMP) == 0 && 503204941Ssobomax (bsc->serdes_flags & BRGPHY_NOANWAIT) == 0) { 504167728Sjkim /* Erg, still trying, I guess... */ 505167728Sjkim mii->mii_media_active |= IFM_NONE; 506215297Smarius return; 507165360Sjkim } 50859477Swpaul 509170391Sdavidch if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 510215297Smarius /* 511215297Smarius * NB: reading the ANAR, ANLPAR or 1000STS after the AUXSTS 512215297Smarius * wedges at least the PHY of BCM5704 (but not others). 513215297Smarius */ 514215297Smarius flowstat = mii_phy_flowstatus(sc); 515215297Smarius xstat = PHY_READ(sc, BRGPHY_MII_1000STS); 516170391Sdavidch aux = PHY_READ(sc, BRGPHY_MII_AUXSTS); 517170391Sdavidch 518170391Sdavidch /* If copper link is up, get the negotiated speed/duplex. */ 519170391Sdavidch if (aux & BRGPHY_AUXSTS_LINK) { 520170391Sdavidch mii->mii_media_status |= IFM_ACTIVE; 521170391Sdavidch switch (aux & BRGPHY_AUXSTS_AN_RES) { 522170391Sdavidch case BRGPHY_RES_1000FD: 523170391Sdavidch mii->mii_media_active |= IFM_1000_T | IFM_FDX; break; 524170391Sdavidch case BRGPHY_RES_1000HD: 525170391Sdavidch mii->mii_media_active |= IFM_1000_T | IFM_HDX; break; 526170391Sdavidch case BRGPHY_RES_100FD: 527170391Sdavidch mii->mii_media_active |= IFM_100_TX | IFM_FDX; break; 528170391Sdavidch case BRGPHY_RES_100T4: 529170391Sdavidch mii->mii_media_active |= IFM_100_T4; break; 530170391Sdavidch case BRGPHY_RES_100HD: 531170391Sdavidch mii->mii_media_active |= IFM_100_TX | IFM_HDX; break; 532170391Sdavidch case BRGPHY_RES_10FD: 533170391Sdavidch mii->mii_media_active |= IFM_10_T | IFM_FDX; break; 534170391Sdavidch case BRGPHY_RES_10HD: 535170391Sdavidch mii->mii_media_active |= IFM_10_T | IFM_HDX; break; 536170391Sdavidch default: 537170391Sdavidch mii->mii_media_active |= IFM_NONE; break; 538170391Sdavidch } 539215297Smarius 540215297Smarius if ((mii->mii_media_active & IFM_FDX) != 0) 541215297Smarius mii->mii_media_active |= flowstat; 542215297Smarius 543215297Smarius if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T && 544215297Smarius (xstat & BRGPHY_1000STS_MSR) != 0) 545215297Smarius mii->mii_media_active |= IFM_ETH_MASTER; 54683029Swpaul } 547170391Sdavidch } else { 548215297Smarius /* Todo: Add support for flow control. */ 549170391Sdavidch /* If serdes link is up, get the negotiated speed/duplex. */ 550170391Sdavidch if (bmsr & BRGPHY_BMSR_LINK) { 551170391Sdavidch mii->mii_media_status |= IFM_ACTIVE; 552170391Sdavidch } 553170391Sdavidch 554170391Sdavidch /* Check the link speed/duplex based on the PHY type. */ 555170391Sdavidch if (bsc->serdes_flags & BRGPHY_5706S) { 556170391Sdavidch mii->mii_media_active |= IFM_1000_SX; 557170391Sdavidch 558170391Sdavidch /* If autoneg enabled, read negotiated duplex settings */ 559170391Sdavidch if (bmcr & BRGPHY_BMCR_AUTOEN) { 560170391Sdavidch val = PHY_READ(sc, BRGPHY_SERDES_ANAR) & PHY_READ(sc, BRGPHY_SERDES_ANLPAR); 561170391Sdavidch if (val & BRGPHY_SERDES_ANAR_FDX) 562170391Sdavidch mii->mii_media_active |= IFM_FDX; 563170391Sdavidch else 564170391Sdavidch mii->mii_media_active |= IFM_HDX; 565170391Sdavidch } 566170391Sdavidch } else if (bsc->serdes_flags & BRGPHY_5708S) { 567170391Sdavidch PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0); 568170391Sdavidch xstat = PHY_READ(sc, BRGPHY_5708S_PG0_1000X_STAT1); 569170391Sdavidch 570212307Syongari /* Check for MRBE auto-negotiated speed results. */ 571170391Sdavidch switch (xstat & BRGPHY_5708S_PG0_1000X_STAT1_SPEED_MASK) { 572181617Syongari case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_10: 573170391Sdavidch mii->mii_media_active |= IFM_10_FL; break; 574181617Syongari case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_100: 575170391Sdavidch mii->mii_media_active |= IFM_100_FX; break; 576181617Syongari case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_1G: 577170391Sdavidch mii->mii_media_active |= IFM_1000_SX; break; 578181617Syongari case BRGPHY_5708S_PG0_1000X_STAT1_SPEED_25G: 579170391Sdavidch mii->mii_media_active |= IFM_2500_SX; break; 580170391Sdavidch } 581170391Sdavidch 582212307Syongari /* Check for MRBE auto-negotiated duplex results. */ 583170391Sdavidch if (xstat & BRGPHY_5708S_PG0_1000X_STAT1_FDX) 584170391Sdavidch mii->mii_media_active |= IFM_FDX; 585170391Sdavidch else 586170391Sdavidch mii->mii_media_active |= IFM_HDX; 587212307Syongari } else if (bsc->serdes_flags & BRGPHY_5709S) { 588212307Syongari /* Select GP Status Block of the AN MMD, get autoneg results. */ 589212307Syongari PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_GP_STATUS); 590205299Sdavidch xstat = PHY_READ(sc, BRGPHY_GP_STATUS_TOP_ANEG_STATUS); 591205299Sdavidch 592212307Syongari /* Restore IEEE0 block (assumed in all brgphy(4) code). */ 593212307Syongari PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0); 594205299Sdavidch 595212307Syongari /* Check for MRBE auto-negotiated speed results. */ 596212307Syongari switch (xstat & BRGPHY_GP_STATUS_TOP_ANEG_SPEED_MASK) { 597212307Syongari case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_10: 598212307Syongari mii->mii_media_active |= IFM_10_FL; break; 599212307Syongari case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_100: 600212307Syongari mii->mii_media_active |= IFM_100_FX; break; 601212307Syongari case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_1G: 602212307Syongari mii->mii_media_active |= IFM_1000_SX; break; 603212307Syongari case BRGPHY_GP_STATUS_TOP_ANEG_SPEED_25G: 604212307Syongari mii->mii_media_active |= IFM_2500_SX; break; 605205299Sdavidch } 606205299Sdavidch 607212307Syongari /* Check for MRBE auto-negotiated duplex results. */ 608205299Sdavidch if (xstat & BRGPHY_GP_STATUS_TOP_ANEG_FDX) 609205299Sdavidch mii->mii_media_active |= IFM_FDX; 610205299Sdavidch else 611205299Sdavidch mii->mii_media_active |= IFM_HDX; 612212307Syongari } 613170391Sdavidch } 61459477Swpaul} 61559477Swpaul 616170391Sdavidchstatic void 617215297Smariusbrgphy_mii_phy_auto(struct mii_softc *sc, int media) 61859477Swpaul{ 619215297Smarius int anar, ktcr = 0; 62059477Swpaul 621221407Smarius PHY_RESET(sc); 622170391Sdavidch 623170391Sdavidch if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 624215297Smarius anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 625215297Smarius if ((media & IFM_FLOW) != 0 || 626215297Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 627215297Smarius anar |= BRGPHY_ANAR_PC | BRGPHY_ANAR_ASP; 628215297Smarius PHY_WRITE(sc, BRGPHY_MII_ANAR, anar); 629244481Syongari ktcr = BRGPHY_1000CTL_AFD | BRGPHY_1000CTL_AHD; 630244481Syongari if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5701) 631244481Syongari ktcr |= BRGPHY_1000CTL_MSE | BRGPHY_1000CTL_MSC; 632244481Syongari PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr); 633244481Syongari PHY_READ(sc, BRGPHY_MII_1000CTL); 634170391Sdavidch } else { 635215297Smarius anar = BRGPHY_SERDES_ANAR_FDX | BRGPHY_SERDES_ANAR_HDX; 636215297Smarius if ((media & IFM_FLOW) != 0 || 637215297Smarius (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 638215297Smarius anar |= BRGPHY_SERDES_ANAR_BOTH_PAUSE; 639215297Smarius PHY_WRITE(sc, BRGPHY_SERDES_ANAR, anar); 640170391Sdavidch } 641170391Sdavidch 642215297Smarius PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_AUTOEN | 643215297Smarius BRGPHY_BMCR_STARTNEG); 644165360Sjkim PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00); 64559477Swpaul} 646114590Sps 647170391Sdavidch/* Enable loopback to force the link down. */ 648114590Spsstatic void 649170391Sdavidchbrgphy_enable_loopback(struct mii_softc *sc) 650114590Sps{ 651114590Sps int i; 652114590Sps 653114590Sps PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP); 654114590Sps for (i = 0; i < 15000; i++) { 655170391Sdavidch if (!(PHY_READ(sc, BRGPHY_MII_BMSR) & BRGPHY_BMSR_LINK)) 656114590Sps break; 657114590Sps DELAY(10); 658114590Sps } 659114590Sps} 660114590Sps 661114590Sps/* Turn off tap power management on 5401. */ 662114590Spsstatic void 663114590Spsbcm5401_load_dspcode(struct mii_softc *sc) 664114590Sps{ 665114590Sps static const struct { 666114590Sps int reg; 667114590Sps uint16_t val; 668114590Sps } dspcode[] = { 669114590Sps { BRGPHY_MII_AUXCTL, 0x0c20 }, 670114590Sps { BRGPHY_MII_DSP_ADDR_REG, 0x0012 }, 671114590Sps { BRGPHY_MII_DSP_RW_PORT, 0x1804 }, 672114590Sps { BRGPHY_MII_DSP_ADDR_REG, 0x0013 }, 673114590Sps { BRGPHY_MII_DSP_RW_PORT, 0x1204 }, 674114590Sps { BRGPHY_MII_DSP_ADDR_REG, 0x8006 }, 675114590Sps { BRGPHY_MII_DSP_RW_PORT, 0x0132 }, 676114590Sps { BRGPHY_MII_DSP_ADDR_REG, 0x8006 }, 677114590Sps { BRGPHY_MII_DSP_RW_PORT, 0x0232 }, 678114590Sps { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, 679114590Sps { BRGPHY_MII_DSP_RW_PORT, 0x0a20 }, 680114590Sps { 0, 0 }, 681114590Sps }; 682114590Sps int i; 683114590Sps 684114590Sps for (i = 0; dspcode[i].reg != 0; i++) 685114590Sps PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 686114590Sps DELAY(40); 687114590Sps} 688114590Sps 689114590Spsstatic void 690114590Spsbcm5411_load_dspcode(struct mii_softc *sc) 691114590Sps{ 692114590Sps static const struct { 693114590Sps int reg; 694114590Sps uint16_t val; 695114590Sps } dspcode[] = { 696114590Sps { 0x1c, 0x8c23 }, 697114590Sps { 0x1c, 0x8ca3 }, 698114590Sps { 0x1c, 0x8c23 }, 699114590Sps { 0, 0 }, 700114590Sps }; 701114590Sps int i; 702114590Sps 703114590Sps for (i = 0; dspcode[i].reg != 0; i++) 704114590Sps PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 705114590Sps} 706114590Sps 707204144Smariusvoid 708204144Smariusbcm54k2_load_dspcode(struct mii_softc *sc) 709204144Smarius{ 710204144Smarius static const struct { 711204144Smarius int reg; 712204144Smarius uint16_t val; 713204144Smarius } dspcode[] = { 714204144Smarius { 4, 0x01e1 }, 715204144Smarius { 9, 0x0300 }, 716204144Smarius { 0, 0 }, 717204144Smarius }; 718204144Smarius int i; 719204144Smarius 720204144Smarius for (i = 0; dspcode[i].reg != 0; i++) 721204144Smarius PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 722204144Smarius 723204144Smarius} 724204144Smarius 725114590Spsstatic void 726166676Sjkimbrgphy_fixup_5704_a0_bug(struct mii_softc *sc) 727114590Sps{ 728114590Sps static const struct { 729114590Sps int reg; 730114590Sps uint16_t val; 731114590Sps } dspcode[] = { 732166676Sjkim { 0x1c, 0x8d68 }, 733166676Sjkim { 0x1c, 0x8d68 }, 734114590Sps { 0, 0 }, 735114590Sps }; 736114590Sps int i; 737114590Sps 738114590Sps for (i = 0; dspcode[i].reg != 0; i++) 739114590Sps PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 740114590Sps} 741114590Sps 742114590Spsstatic void 743166676Sjkimbrgphy_fixup_adc_bug(struct mii_softc *sc) 744114590Sps{ 745114590Sps static const struct { 746114590Sps int reg; 747166676Sjkim uint16_t val; 748114590Sps } dspcode[] = { 749166676Sjkim { BRGPHY_MII_AUXCTL, 0x0c00 }, 750166676Sjkim { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, 751166676Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x2aaa }, 752114590Sps { 0, 0 }, 753114590Sps }; 754114590Sps int i; 755114590Sps 756114590Sps for (i = 0; dspcode[i].reg != 0; i++) 757114590Sps PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 758114590Sps} 759114590Sps 760114590Spsstatic void 761166673Sjkimbrgphy_fixup_adjust_trim(struct mii_softc *sc) 762166673Sjkim{ 763166673Sjkim static const struct { 764166673Sjkim int reg; 765166673Sjkim uint16_t val; 766166673Sjkim } dspcode[] = { 767166673Sjkim { BRGPHY_MII_AUXCTL, 0x0c00 }, 768166673Sjkim { BRGPHY_MII_DSP_ADDR_REG, 0x000a }, 769166673Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x110b }, 770170391Sdavidch { BRGPHY_MII_TEST1, 0x0014 }, 771166673Sjkim { BRGPHY_MII_AUXCTL, 0x0400 }, 772166673Sjkim { 0, 0 }, 773166673Sjkim }; 774166673Sjkim int i; 775166673Sjkim 776166673Sjkim for (i = 0; dspcode[i].reg != 0; i++) 777166673Sjkim PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 778166673Sjkim} 779166673Sjkim 780166673Sjkimstatic void 781166031Sjkimbrgphy_fixup_ber_bug(struct mii_softc *sc) 782135772Sps{ 783135772Sps static const struct { 784135772Sps int reg; 785166676Sjkim uint16_t val; 786135772Sps } dspcode[] = { 787166665Sjkim { BRGPHY_MII_AUXCTL, 0x0c00 }, 788166665Sjkim { BRGPHY_MII_DSP_ADDR_REG, 0x000a }, 789166665Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x310b }, 790166665Sjkim { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, 791166665Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x9506 }, 792166665Sjkim { BRGPHY_MII_DSP_ADDR_REG, 0x401f }, 793166665Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x14e2 }, 794166665Sjkim { BRGPHY_MII_AUXCTL, 0x0400 }, 795135772Sps { 0, 0 }, 796135772Sps }; 797135772Sps int i; 798135772Sps 799135772Sps for (i = 0; dspcode[i].reg != 0; i++) 800135772Sps PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 801135772Sps} 802135772Sps 803135772Spsstatic void 804166677Sjkimbrgphy_fixup_crc_bug(struct mii_softc *sc) 805166677Sjkim{ 806166677Sjkim static const struct { 807166677Sjkim int reg; 808166677Sjkim uint16_t val; 809166677Sjkim } dspcode[] = { 810166716Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x0a75 }, 811166677Sjkim { 0x1c, 0x8c68 }, 812166677Sjkim { 0x1c, 0x8d68 }, 813166677Sjkim { 0x1c, 0x8c68 }, 814166677Sjkim { 0, 0 }, 815166677Sjkim }; 816166677Sjkim int i; 817166677Sjkim 818166677Sjkim for (i = 0; dspcode[i].reg != 0; i++) 819166677Sjkim PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 820166677Sjkim} 821166677Sjkim 822166677Sjkimstatic void 823166031Sjkimbrgphy_fixup_jitter_bug(struct mii_softc *sc) 824166031Sjkim{ 825166031Sjkim static const struct { 826166031Sjkim int reg; 827166031Sjkim uint16_t val; 828166031Sjkim } dspcode[] = { 829166031Sjkim { BRGPHY_MII_AUXCTL, 0x0c00 }, 830166031Sjkim { BRGPHY_MII_DSP_ADDR_REG, 0x000a }, 831166031Sjkim { BRGPHY_MII_DSP_RW_PORT, 0x010b }, 832166031Sjkim { BRGPHY_MII_AUXCTL, 0x0400 }, 833166031Sjkim { 0, 0 }, 834166031Sjkim }; 835166031Sjkim int i; 836166031Sjkim 837166031Sjkim for (i = 0; dspcode[i].reg != 0; i++) 838166031Sjkim PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 839166031Sjkim} 840166031Sjkim 841166031Sjkimstatic void 842179772Sdavidchbrgphy_fixup_disable_early_dac(struct mii_softc *sc) 843179772Sdavidch{ 844179772Sdavidch uint32_t val; 845179772Sdavidch 846179772Sdavidch PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x0f08); 847179772Sdavidch val = PHY_READ(sc, BRGPHY_MII_DSP_RW_PORT); 848179772Sdavidch val &= ~(1 << 8); 849179772Sdavidch PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, val); 850179772Sdavidch 851179772Sdavidch} 852179772Sdavidch 853179772Sdavidchstatic void 854166032Sjkimbrgphy_ethernet_wirespeed(struct mii_softc *sc) 855166032Sjkim{ 856166676Sjkim uint32_t val; 857166032Sjkim 858166032Sjkim /* Enable Ethernet@WireSpeed. */ 859166032Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007); 860166032Sjkim val = PHY_READ(sc, BRGPHY_MII_AUXCTL); 861166032Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4)); 862166032Sjkim} 863166032Sjkim 864166032Sjkimstatic void 865166032Sjkimbrgphy_jumbo_settings(struct mii_softc *sc, u_long mtu) 866166032Sjkim{ 867166676Sjkim uint32_t val; 868166032Sjkim 869166032Sjkim /* Set or clear jumbo frame settings in the PHY. */ 870166032Sjkim if (mtu > ETHER_MAX_LEN) { 871221407Smarius if (sc->mii_mpd_model == MII_MODEL_BROADCOM_BCM5401) { 872166680Sjkim /* BCM5401 PHY cannot read-modify-write. */ 873166666Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20); 874166680Sjkim } else { 875166666Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7); 876166666Sjkim val = PHY_READ(sc, BRGPHY_MII_AUXCTL); 877166666Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 878166666Sjkim val | BRGPHY_AUXCTL_LONG_PKT); 879166666Sjkim } 880166032Sjkim 881166032Sjkim val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL); 882166032Sjkim PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, 883166032Sjkim val | BRGPHY_PHY_EXTCTL_HIGH_LA); 884166032Sjkim } else { 885166032Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7); 886166032Sjkim val = PHY_READ(sc, BRGPHY_MII_AUXCTL); 887166032Sjkim PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 888166032Sjkim val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7)); 889166032Sjkim 890166032Sjkim val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL); 891181617Syongari PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, 892170391Sdavidch val & ~BRGPHY_PHY_EXTCTL_HIGH_LA); 893166032Sjkim } 894166032Sjkim} 895166032Sjkim 896166032Sjkimstatic void 897114590Spsbrgphy_reset(struct mii_softc *sc) 898114590Sps{ 899166037Sjkim struct bge_softc *bge_sc = NULL; 900166037Sjkim struct bce_softc *bce_sc = NULL; 901266974Smarcel if_t ifp; 902225014Smarius int i, val; 903114590Sps 904225014Smarius /* 905225014Smarius * Perform a reset. Note that at least some Broadcom PHYs default to 906225014Smarius * being powered down as well as isolated after a reset but don't work 907225014Smarius * if one or both of these bits are cleared. However, they just work 908225014Smarius * fine if both bits remain set, so we don't use mii_phy_reset() here. 909225014Smarius */ 910225014Smarius PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET); 911114590Sps 912225014Smarius /* Wait 100ms for it to complete. */ 913225014Smarius for (i = 0; i < 100; i++) { 914225014Smarius if ((PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_RESET) == 0) 915225014Smarius break; 916225014Smarius DELAY(1000); 917225014Smarius } 918225014Smarius 919179772Sdavidch /* Handle any PHY specific procedures following the reset. */ 920221407Smarius switch (sc->mii_mpd_oui) { 921170391Sdavidch case MII_OUI_BROADCOM: 922221407Smarius switch (sc->mii_mpd_model) { 923221407Smarius case MII_MODEL_BROADCOM_BCM5400: 924166031Sjkim bcm5401_load_dspcode(sc); 925170391Sdavidch break; 926221407Smarius case MII_MODEL_BROADCOM_BCM5401: 927221407Smarius if (sc->mii_mpd_rev == 1 || sc->mii_mpd_rev == 3) 928170391Sdavidch bcm5401_load_dspcode(sc); 929170391Sdavidch break; 930221407Smarius case MII_MODEL_BROADCOM_BCM5411: 931170391Sdavidch bcm5411_load_dspcode(sc); 932170391Sdavidch break; 933221407Smarius case MII_MODEL_BROADCOM_BCM54K2: 934204144Smarius bcm54k2_load_dspcode(sc); 935204144Smarius break; 936170391Sdavidch } 937166031Sjkim break; 938241437Syongari case MII_OUI_BROADCOM3: 939241437Syongari switch (sc->mii_mpd_model) { 940241437Syongari case MII_MODEL_BROADCOM3_BCM5717C: 941241437Syongari case MII_MODEL_BROADCOM3_BCM5719C: 942241437Syongari case MII_MODEL_BROADCOM3_BCM5720C: 943241437Syongari case MII_MODEL_BROADCOM3_BCM57765: 944241437Syongari return; 945241437Syongari } 946241437Syongari break; 947253481Syongari case MII_OUI_BROADCOM4: 948253481Syongari return; 949114590Sps } 950114590Sps 951117659Swpaul ifp = sc->mii_pdata->mii_ifp; 952117659Swpaul 953157642Sps /* Find the driver associated with this PHY. */ 954277093Sglebius if (mii_phy_mac_match(sc, "bge")) 955277093Sglebius bge_sc = mii_phy_mac_softc(sc); 956277093Sglebius else if (mii_phy_mac_match(sc, "bce")) 957277093Sglebius bce_sc = mii_phy_mac_softc(sc); 958117659Swpaul 959157642Sps if (bge_sc) { 960166031Sjkim /* Fix up various bugs */ 961213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_5704_A0_BUG) 962166676Sjkim brgphy_fixup_5704_a0_bug(sc); 963213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_ADC_BUG) 964166031Sjkim brgphy_fixup_adc_bug(sc); 965213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_ADJUST_TRIM) 966166673Sjkim brgphy_fixup_adjust_trim(sc); 967213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_BER_BUG) 968166031Sjkim brgphy_fixup_ber_bug(sc); 969213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_CRC_BUG) 970166677Sjkim brgphy_fixup_crc_bug(sc); 971213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_JITTER_BUG) 972166031Sjkim brgphy_fixup_jitter_bug(sc); 973157642Sps 974231913Smarius if (bge_sc->bge_flags & BGE_FLAG_JUMBO) 975266974Smarcel brgphy_jumbo_settings(sc, if_getmtu(ifp)); 976157642Sps 977221468Syongari if ((bge_sc->bge_phy_flags & BGE_PHY_NO_WIRESPEED) == 0) 978166032Sjkim brgphy_ethernet_wirespeed(sc); 979166032Sjkim 980166032Sjkim /* Enable Link LED on Dell boxes */ 981213464Syongari if (bge_sc->bge_phy_flags & BGE_PHY_NO_3LED) { 982164830Smarius PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, 983166032Sjkim PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL) & 984166032Sjkim ~BRGPHY_PHY_EXTCTL_3_LED); 985157642Sps } 986178667Sjhb 987178667Sjhb /* Adjust output voltage (From Linux driver) */ 988178667Sjhb if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906) 989178667Sjhb PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12); 990166032Sjkim } else if (bce_sc) { 991170391Sdavidch if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5708 && 992176850Sdavidch (bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) { 993170391Sdavidch 994170391Sdavidch /* Store autoneg capabilities/results in digital block (Page 0) */ 995170391Sdavidch PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG3_PG2); 996181618Syongari PHY_WRITE(sc, BRGPHY_5708S_PG2_DIGCTL_3_0, 997170391Sdavidch BRGPHY_5708S_PG2_DIGCTL_3_0_USE_IEEE); 998170391Sdavidch PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, BRGPHY_5708S_DIG_PG0); 999170391Sdavidch 1000170391Sdavidch /* Enable fiber mode and autodetection */ 1001181618Syongari PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL1, 1002181618Syongari PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL1) | 1003181618Syongari BRGPHY_5708S_PG0_1000X_CTL1_AUTODET_EN | 1004170391Sdavidch BRGPHY_5708S_PG0_1000X_CTL1_FIBER_MODE); 1005170391Sdavidch 1006170391Sdavidch /* Enable parallel detection */ 1007181618Syongari PHY_WRITE(sc, BRGPHY_5708S_PG0_1000X_CTL2, 1008181618Syongari PHY_READ(sc, BRGPHY_5708S_PG0_1000X_CTL2) | 1009170391Sdavidch BRGPHY_5708S_PG0_1000X_CTL2_PAR_DET_EN); 1010170391Sdavidch 1011170391Sdavidch /* Advertise 2.5G support through next page during autoneg */ 1012170391Sdavidch if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG) 1013181618Syongari PHY_WRITE(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1, 1014181618Syongari PHY_READ(sc, BRGPHY_5708S_ANEG_NXT_PG_XMIT1) | 1015170391Sdavidch BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G); 1016170391Sdavidch 1017170391Sdavidch /* Increase TX signal amplitude */ 1018170391Sdavidch if ((BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_A0) || 1019170391Sdavidch (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B0) || 1020170391Sdavidch (BCE_CHIP_ID(bce_sc) == BCE_CHIP_ID_5708_B1)) { 1021181618Syongari PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, 1022170391Sdavidch BRGPHY_5708S_TX_MISC_PG5); 1023181618Syongari PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL1, 1024170391Sdavidch PHY_READ(sc, BRGPHY_5708S_PG5_TXACTL1) & ~0x30); 1025181618Syongari PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, 1026170391Sdavidch BRGPHY_5708S_DIG_PG0); 1027170391Sdavidch } 1028170391Sdavidch 1029181618Syongari /* Backplanes use special driver/pre-driver/pre-emphasis values. */ 1030170391Sdavidch if ((bce_sc->bce_shared_hw_cfg & BCE_SHARED_HW_CFG_PHY_BACKPLANE) && 1031170391Sdavidch (bce_sc->bce_port_hw_cfg & BCE_PORT_HW_CFG_CFG_TXCTL3_MASK)) { 1032181618Syongari PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, 1033170391Sdavidch BRGPHY_5708S_TX_MISC_PG5); 1034181618Syongari PHY_WRITE(sc, BRGPHY_5708S_PG5_TXACTL3, 1035181618Syongari bce_sc->bce_port_hw_cfg & 1036170391Sdavidch BCE_PORT_HW_CFG_CFG_TXCTL3_MASK); 1037181618Syongari PHY_WRITE(sc, BRGPHY_5708S_BLOCK_ADDR, 1038170391Sdavidch BRGPHY_5708S_DIG_PG0); 1039170391Sdavidch } 1040205299Sdavidch } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709 && 1041205299Sdavidch (bce_sc->bce_phy_flags & BCE_PHY_SERDES_FLAG)) { 1042205299Sdavidch 1043212307Syongari /* Select the SerDes Digital block of the AN MMD. */ 1044212307Syongari PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_SERDES_DIG); 1045205299Sdavidch val = PHY_READ(sc, BRGPHY_SERDES_DIG_1000X_CTL1); 1046205299Sdavidch val &= ~BRGPHY_SD_DIG_1000X_CTL1_AUTODET; 1047205299Sdavidch val |= BRGPHY_SD_DIG_1000X_CTL1_FIBER; 1048205299Sdavidch PHY_WRITE(sc, BRGPHY_SERDES_DIG_1000X_CTL1, val); 1049205299Sdavidch 1050212307Syongari /* Select the Over 1G block of the AN MMD. */ 1051205299Sdavidch PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_OVER_1G); 1052205299Sdavidch 1053212307Syongari /* Enable autoneg "Next Page" to advertise 2.5G support. */ 1054212307Syongari val = PHY_READ(sc, BRGPHY_OVER_1G_UNFORMAT_PG1); 1055205299Sdavidch if (bce_sc->bce_phy_flags & BCE_PHY_2_5G_CAPABLE_FLAG) 1056205299Sdavidch val |= BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G; 1057205299Sdavidch else 1058205299Sdavidch val &= ~BRGPHY_5708S_ANEG_NXT_PG_XMIT1_25G; 1059205299Sdavidch PHY_WRITE(sc, BRGPHY_OVER_1G_UNFORMAT_PG1, val); 1060205299Sdavidch 1061212307Syongari /* Select the Multi-Rate Backplane Ethernet block of the AN MMD. */ 1062205299Sdavidch PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_MRBE); 1063205299Sdavidch 1064212307Syongari /* Enable MRBE speed autoneg. */ 1065212307Syongari val = PHY_READ(sc, BRGPHY_MRBE_MSG_PG5_NP); 1066205299Sdavidch val |= BRGPHY_MRBE_MSG_PG5_NP_MBRE | 1067205299Sdavidch BRGPHY_MRBE_MSG_PG5_NP_T2; 1068205299Sdavidch PHY_WRITE(sc, BRGPHY_MRBE_MSG_PG5_NP, val); 1069205299Sdavidch 1070212307Syongari /* Select the Clause 73 User B0 block of the AN MMD. */ 1071212307Syongari PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_CL73_USER_B0); 1072205299Sdavidch 1073212307Syongari /* Enable MRBE speed autoneg. */ 1074205299Sdavidch PHY_WRITE(sc, BRGPHY_CL73_USER_B0_MBRE_CTL1, 1075205299Sdavidch BRGPHY_CL73_USER_B0_MBRE_CTL1_NP_AFT_BP | 1076205299Sdavidch BRGPHY_CL73_USER_B0_MBRE_CTL1_STA_MGR | 1077205299Sdavidch BRGPHY_CL73_USER_B0_MBRE_CTL1_ANEG); 1078205299Sdavidch 1079212307Syongari /* Restore IEEE0 block (assumed in all brgphy(4) code). */ 1080212307Syongari PHY_WRITE(sc, BRGPHY_BLOCK_ADDR, BRGPHY_BLOCK_ADDR_COMBO_IEEE0); 1081205299Sdavidch } else if (BCE_CHIP_NUM(bce_sc) == BCE_CHIP_NUM_5709) { 1082179772Sdavidch if ((BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Ax) || 1083179772Sdavidch (BCE_CHIP_REV(bce_sc) == BCE_CHIP_REV_Bx)) 1084179772Sdavidch brgphy_fixup_disable_early_dac(sc); 1085212307Syongari 1086266974Smarcel brgphy_jumbo_settings(sc, if_getmtu(ifp)); 1087179772Sdavidch brgphy_ethernet_wirespeed(sc); 1088170391Sdavidch } else { 1089170391Sdavidch brgphy_fixup_ber_bug(sc); 1090266974Smarcel brgphy_jumbo_settings(sc, if_getmtu(ifp)); 1091170391Sdavidch brgphy_ethernet_wirespeed(sc); 1092170391Sdavidch } 1093119157Sambrisko } 1094114590Sps} 1095