1202 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PHY drivers for the sungem ethernet driver.4 *5 * This file could be shared with other drivers.6 *7 * (c) 2002-2007, Benjamin Herrenscmidt (benh@kernel.crashing.org)8 *9 * TODO:10 * - Add support for PHYs that provide an IRQ line11 * - Eventually moved the entire polling state machine in12 * there (out of the eth driver), so that it can easily be13 * skipped on PHYs that implement it in hardware.14 * - On LXT971 & BCM5201, Apple uses some chip specific regs15 * to read the link status. Figure out why and if it makes16 * sense to do the same (magic aneg ?)17 * - Apple has some additional power management code for some18 * Broadcom PHYs that they "hide" from the OpenSource version19 * of darwin, still need to reverse engineer that20 */21 22 23#include <linux/module.h>24 25#include <linux/kernel.h>26#include <linux/types.h>27#include <linux/netdevice.h>28#include <linux/etherdevice.h>29#include <linux/mii.h>30#include <linux/ethtool.h>31#include <linux/delay.h>32#include <linux/of.h>33#include <linux/sungem_phy.h>34 35/* Link modes of the BCM5400 PHY */36static const int phy_BCM5400_link_table[8][3] = {37 { 0, 0, 0 }, /* No link */38 { 0, 0, 0 }, /* 10BT Half Duplex */39 { 1, 0, 0 }, /* 10BT Full Duplex */40 { 0, 1, 0 }, /* 100BT Half Duplex */41 { 0, 1, 0 }, /* 100BT Half Duplex */42 { 1, 1, 0 }, /* 100BT Full Duplex*/43 { 1, 0, 1 }, /* 1000BT */44 { 1, 0, 1 }, /* 1000BT */45};46 47static inline int __sungem_phy_read(struct mii_phy* phy, int id, int reg)48{49 return phy->mdio_read(phy->dev, id, reg);50}51 52static inline void __sungem_phy_write(struct mii_phy* phy, int id, int reg, int val)53{54 phy->mdio_write(phy->dev, id, reg, val);55}56 57static inline int sungem_phy_read(struct mii_phy* phy, int reg)58{59 return phy->mdio_read(phy->dev, phy->mii_id, reg);60}61 62static inline void sungem_phy_write(struct mii_phy* phy, int reg, int val)63{64 phy->mdio_write(phy->dev, phy->mii_id, reg, val);65}66 67static int reset_one_mii_phy(struct mii_phy* phy, int phy_id)68{69 u16 val;70 int limit = 10000;71 72 val = __sungem_phy_read(phy, phy_id, MII_BMCR);73 val &= ~(BMCR_ISOLATE | BMCR_PDOWN);74 val |= BMCR_RESET;75 __sungem_phy_write(phy, phy_id, MII_BMCR, val);76 77 udelay(100);78 79 while (--limit) {80 val = __sungem_phy_read(phy, phy_id, MII_BMCR);81 if ((val & BMCR_RESET) == 0)82 break;83 udelay(10);84 }85 if ((val & BMCR_ISOLATE) && limit > 0)86 __sungem_phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);87 88 return limit <= 0;89}90 91static int bcm5201_init(struct mii_phy* phy)92{93 u16 data;94 95 data = sungem_phy_read(phy, MII_BCM5201_MULTIPHY);96 data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;97 sungem_phy_write(phy, MII_BCM5201_MULTIPHY, data);98 99 sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);100 101 return 0;102}103 104static int bcm5201_suspend(struct mii_phy* phy)105{106 sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);107 sungem_phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);108 109 return 0;110}111 112static int bcm5221_init(struct mii_phy* phy)113{114 u16 data;115 116 data = sungem_phy_read(phy, MII_BCM5221_TEST);117 sungem_phy_write(phy, MII_BCM5221_TEST,118 data | MII_BCM5221_TEST_ENABLE_SHADOWS);119 120 data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);121 sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,122 data | MII_BCM5221_SHDOW_AUX_STAT2_APD);123 124 data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);125 sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,126 data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);127 128 data = sungem_phy_read(phy, MII_BCM5221_TEST);129 sungem_phy_write(phy, MII_BCM5221_TEST,130 data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);131 132 return 0;133}134 135static int bcm5221_suspend(struct mii_phy* phy)136{137 u16 data;138 139 data = sungem_phy_read(phy, MII_BCM5221_TEST);140 sungem_phy_write(phy, MII_BCM5221_TEST,141 data | MII_BCM5221_TEST_ENABLE_SHADOWS);142 143 data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);144 sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,145 data | MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE);146 147 return 0;148}149 150static int bcm5241_init(struct mii_phy* phy)151{152 u16 data;153 154 data = sungem_phy_read(phy, MII_BCM5221_TEST);155 sungem_phy_write(phy, MII_BCM5221_TEST,156 data | MII_BCM5221_TEST_ENABLE_SHADOWS);157 158 data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);159 sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,160 data | MII_BCM5221_SHDOW_AUX_STAT2_APD);161 162 data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);163 sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,164 data & ~MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);165 166 data = sungem_phy_read(phy, MII_BCM5221_TEST);167 sungem_phy_write(phy, MII_BCM5221_TEST,168 data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);169 170 return 0;171}172 173static int bcm5241_suspend(struct mii_phy* phy)174{175 u16 data;176 177 data = sungem_phy_read(phy, MII_BCM5221_TEST);178 sungem_phy_write(phy, MII_BCM5221_TEST,179 data | MII_BCM5221_TEST_ENABLE_SHADOWS);180 181 data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);182 sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,183 data | MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);184 185 return 0;186}187 188static int bcm5400_init(struct mii_phy* phy)189{190 u16 data;191 192 /* Configure for gigabit full duplex */193 data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);194 data |= MII_BCM5400_AUXCONTROL_PWR10BASET;195 sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);196 197 data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);198 data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;199 sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);200 201 udelay(100);202 203 /* Reset and configure cascaded 10/100 PHY */204 (void)reset_one_mii_phy(phy, 0x1f);205 206 data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);207 data |= MII_BCM5201_MULTIPHY_SERIALMODE;208 __sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);209 210 data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);211 data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;212 sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);213 214 return 0;215}216 217static int bcm5400_suspend(struct mii_phy* phy)218{219#if 0 /* Commented out in Darwin... someone has those dawn docs ? */220 sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);221#endif222 return 0;223}224 225static int bcm5401_init(struct mii_phy* phy)226{227 u16 data;228 int rev;229 230 rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;231 if (rev == 0 || rev == 3) {232 /* Some revisions of 5401 appear to need this233 * initialisation sequence to disable, according234 * to OF, "tap power management"235 *236 * WARNING ! OF and Darwin don't agree on the237 * register addresses. OF seem to interpret the238 * register numbers below as decimal239 *240 * Note: This should (and does) match tg3_init_5401phy_dsp241 * in the tg3.c driver. -DaveM242 */243 sungem_phy_write(phy, 0x18, 0x0c20);244 sungem_phy_write(phy, 0x17, 0x0012);245 sungem_phy_write(phy, 0x15, 0x1804);246 sungem_phy_write(phy, 0x17, 0x0013);247 sungem_phy_write(phy, 0x15, 0x1204);248 sungem_phy_write(phy, 0x17, 0x8006);249 sungem_phy_write(phy, 0x15, 0x0132);250 sungem_phy_write(phy, 0x17, 0x8006);251 sungem_phy_write(phy, 0x15, 0x0232);252 sungem_phy_write(phy, 0x17, 0x201f);253 sungem_phy_write(phy, 0x15, 0x0a20);254 }255 256 /* Configure for gigabit full duplex */257 data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);258 data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;259 sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);260 261 udelay(10);262 263 /* Reset and configure cascaded 10/100 PHY */264 (void)reset_one_mii_phy(phy, 0x1f);265 266 data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);267 data |= MII_BCM5201_MULTIPHY_SERIALMODE;268 __sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);269 270 return 0;271}272 273static int bcm5401_suspend(struct mii_phy* phy)274{275#if 0 /* Commented out in Darwin... someone has those dawn docs ? */276 sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);277#endif278 return 0;279}280 281static int bcm5411_init(struct mii_phy* phy)282{283 u16 data;284 285 /* Here's some more Apple black magic to setup286 * some voltage stuffs.287 */288 sungem_phy_write(phy, 0x1c, 0x8c23);289 sungem_phy_write(phy, 0x1c, 0x8ca3);290 sungem_phy_write(phy, 0x1c, 0x8c23);291 292 /* Here, Apple seems to want to reset it, do293 * it as well294 */295 sungem_phy_write(phy, MII_BMCR, BMCR_RESET);296 sungem_phy_write(phy, MII_BMCR, 0x1340);297 298 data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);299 data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;300 sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);301 302 udelay(10);303 304 /* Reset and configure cascaded 10/100 PHY */305 (void)reset_one_mii_phy(phy, 0x1f);306 307 return 0;308}309 310static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)311{312 u16 ctl, adv;313 314 phy->autoneg = 1;315 phy->speed = SPEED_10;316 phy->duplex = DUPLEX_HALF;317 phy->pause = 0;318 phy->advertising = advertise;319 320 /* Setup standard advertise */321 adv = sungem_phy_read(phy, MII_ADVERTISE);322 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);323 if (advertise & ADVERTISED_10baseT_Half)324 adv |= ADVERTISE_10HALF;325 if (advertise & ADVERTISED_10baseT_Full)326 adv |= ADVERTISE_10FULL;327 if (advertise & ADVERTISED_100baseT_Half)328 adv |= ADVERTISE_100HALF;329 if (advertise & ADVERTISED_100baseT_Full)330 adv |= ADVERTISE_100FULL;331 sungem_phy_write(phy, MII_ADVERTISE, adv);332 333 /* Start/Restart aneg */334 ctl = sungem_phy_read(phy, MII_BMCR);335 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);336 sungem_phy_write(phy, MII_BMCR, ctl);337 338 return 0;339}340 341static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)342{343 u16 ctl;344 345 phy->autoneg = 0;346 phy->speed = speed;347 phy->duplex = fd;348 phy->pause = 0;349 350 ctl = sungem_phy_read(phy, MII_BMCR);351 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);352 353 /* First reset the PHY */354 sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);355 356 /* Select speed & duplex */357 switch(speed) {358 case SPEED_10:359 break;360 case SPEED_100:361 ctl |= BMCR_SPEED100;362 break;363 case SPEED_1000:364 default:365 return -EINVAL;366 }367 if (fd == DUPLEX_FULL)368 ctl |= BMCR_FULLDPLX;369 sungem_phy_write(phy, MII_BMCR, ctl);370 371 return 0;372}373 374static int genmii_poll_link(struct mii_phy *phy)375{376 u16 status;377 378 (void)sungem_phy_read(phy, MII_BMSR);379 status = sungem_phy_read(phy, MII_BMSR);380 if ((status & BMSR_LSTATUS) == 0)381 return 0;382 if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))383 return 0;384 return 1;385}386 387static int genmii_read_link(struct mii_phy *phy)388{389 u16 lpa;390 391 if (phy->autoneg) {392 lpa = sungem_phy_read(phy, MII_LPA);393 394 if (lpa & (LPA_10FULL | LPA_100FULL))395 phy->duplex = DUPLEX_FULL;396 else397 phy->duplex = DUPLEX_HALF;398 if (lpa & (LPA_100FULL | LPA_100HALF))399 phy->speed = SPEED_100;400 else401 phy->speed = SPEED_10;402 phy->pause = 0;403 }404 /* On non-aneg, we assume what we put in BMCR is the speed,405 * though magic-aneg shouldn't prevent this case from occurring406 */407 408 return 0;409}410 411static int generic_suspend(struct mii_phy* phy)412{413 sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);414 415 return 0;416}417 418static int bcm5421_init(struct mii_phy* phy)419{420 u16 data;421 unsigned int id;422 423 id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));424 425 /* Revision 0 of 5421 needs some fixups */426 if (id == 0x002060e0) {427 /* This is borrowed from MacOS428 */429 sungem_phy_write(phy, 0x18, 0x1007);430 data = sungem_phy_read(phy, 0x18);431 sungem_phy_write(phy, 0x18, data | 0x0400);432 sungem_phy_write(phy, 0x18, 0x0007);433 data = sungem_phy_read(phy, 0x18);434 sungem_phy_write(phy, 0x18, data | 0x0800);435 sungem_phy_write(phy, 0x17, 0x000a);436 data = sungem_phy_read(phy, 0x15);437 sungem_phy_write(phy, 0x15, data | 0x0200);438 }439 440 /* Pick up some init code from OF for K2 version */441 if ((id & 0xfffffff0) == 0x002062e0) {442 sungem_phy_write(phy, 4, 0x01e1);443 sungem_phy_write(phy, 9, 0x0300);444 }445 446 /* Check if we can enable automatic low power */447#ifdef CONFIG_PPC_PMAC448 if (phy->platform_data) {449 struct device_node *np = of_get_parent(phy->platform_data);450 int can_low_power = 1;451 if (np == NULL || of_get_property(np, "no-autolowpower", NULL))452 can_low_power = 0;453 of_node_put(np);454 if (can_low_power) {455 /* Enable automatic low-power */456 sungem_phy_write(phy, 0x1c, 0x9002);457 sungem_phy_write(phy, 0x1c, 0xa821);458 sungem_phy_write(phy, 0x1c, 0x941d);459 }460 }461#endif /* CONFIG_PPC_PMAC */462 463 return 0;464}465 466static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)467{468 u16 ctl, adv;469 470 phy->autoneg = 1;471 phy->speed = SPEED_10;472 phy->duplex = DUPLEX_HALF;473 phy->pause = 0;474 phy->advertising = advertise;475 476 /* Setup standard advertise */477 adv = sungem_phy_read(phy, MII_ADVERTISE);478 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);479 if (advertise & ADVERTISED_10baseT_Half)480 adv |= ADVERTISE_10HALF;481 if (advertise & ADVERTISED_10baseT_Full)482 adv |= ADVERTISE_10FULL;483 if (advertise & ADVERTISED_100baseT_Half)484 adv |= ADVERTISE_100HALF;485 if (advertise & ADVERTISED_100baseT_Full)486 adv |= ADVERTISE_100FULL;487 if (advertise & ADVERTISED_Pause)488 adv |= ADVERTISE_PAUSE_CAP;489 if (advertise & ADVERTISED_Asym_Pause)490 adv |= ADVERTISE_PAUSE_ASYM;491 sungem_phy_write(phy, MII_ADVERTISE, adv);492 493 /* Setup 1000BT advertise */494 adv = sungem_phy_read(phy, MII_1000BASETCONTROL);495 adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);496 if (advertise & SUPPORTED_1000baseT_Half)497 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;498 if (advertise & SUPPORTED_1000baseT_Full)499 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;500 sungem_phy_write(phy, MII_1000BASETCONTROL, adv);501 502 /* Start/Restart aneg */503 ctl = sungem_phy_read(phy, MII_BMCR);504 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);505 sungem_phy_write(phy, MII_BMCR, ctl);506 507 return 0;508}509 510static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd)511{512 u16 ctl;513 514 phy->autoneg = 0;515 phy->speed = speed;516 phy->duplex = fd;517 phy->pause = 0;518 519 ctl = sungem_phy_read(phy, MII_BMCR);520 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);521 522 /* First reset the PHY */523 sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);524 525 /* Select speed & duplex */526 switch(speed) {527 case SPEED_10:528 break;529 case SPEED_100:530 ctl |= BMCR_SPEED100;531 break;532 case SPEED_1000:533 ctl |= BMCR_SPD2;534 }535 if (fd == DUPLEX_FULL)536 ctl |= BMCR_FULLDPLX;537 538 // XXX Should we set the sungem to GII now on 1000BT ?539 540 sungem_phy_write(phy, MII_BMCR, ctl);541 542 return 0;543}544 545static int bcm54xx_read_link(struct mii_phy *phy)546{547 int link_mode;548 u16 val;549 550 if (phy->autoneg) {551 val = sungem_phy_read(phy, MII_BCM5400_AUXSTATUS);552 link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>553 MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);554 phy->duplex = phy_BCM5400_link_table[link_mode][0] ?555 DUPLEX_FULL : DUPLEX_HALF;556 phy->speed = phy_BCM5400_link_table[link_mode][2] ?557 SPEED_1000 :558 (phy_BCM5400_link_table[link_mode][1] ?559 SPEED_100 : SPEED_10);560 val = sungem_phy_read(phy, MII_LPA);561 phy->pause = (phy->duplex == DUPLEX_FULL) &&562 ((val & LPA_PAUSE) != 0);563 }564 /* On non-aneg, we assume what we put in BMCR is the speed,565 * though magic-aneg shouldn't prevent this case from occurring566 */567 568 return 0;569}570 571static int marvell88e1111_init(struct mii_phy* phy)572{573 u16 rev;574 575 /* magic init sequence for rev 0 */576 rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;577 if (rev == 0) {578 sungem_phy_write(phy, 0x1d, 0x000a);579 sungem_phy_write(phy, 0x1e, 0x0821);580 581 sungem_phy_write(phy, 0x1d, 0x0006);582 sungem_phy_write(phy, 0x1e, 0x8600);583 584 sungem_phy_write(phy, 0x1d, 0x000b);585 sungem_phy_write(phy, 0x1e, 0x0100);586 587 sungem_phy_write(phy, 0x1d, 0x0004);588 sungem_phy_write(phy, 0x1e, 0x4850);589 }590 return 0;591}592 593#define BCM5421_MODE_MASK (1 << 5)594 595static int bcm5421_poll_link(struct mii_phy* phy)596{597 u32 phy_reg;598 int mode;599 600 /* find out in what mode we are */601 sungem_phy_write(phy, MII_NCONFIG, 0x1000);602 phy_reg = sungem_phy_read(phy, MII_NCONFIG);603 604 mode = (phy_reg & BCM5421_MODE_MASK) >> 5;605 606 if ( mode == BCM54XX_COPPER)607 return genmii_poll_link(phy);608 609 /* try to find out whether we have a link */610 sungem_phy_write(phy, MII_NCONFIG, 0x2000);611 phy_reg = sungem_phy_read(phy, MII_NCONFIG);612 613 if (phy_reg & 0x0020)614 return 0;615 else616 return 1;617}618 619static int bcm5421_read_link(struct mii_phy* phy)620{621 u32 phy_reg;622 int mode;623 624 /* find out in what mode we are */625 sungem_phy_write(phy, MII_NCONFIG, 0x1000);626 phy_reg = sungem_phy_read(phy, MII_NCONFIG);627 628 mode = (phy_reg & BCM5421_MODE_MASK ) >> 5;629 630 if ( mode == BCM54XX_COPPER)631 return bcm54xx_read_link(phy);632 633 phy->speed = SPEED_1000;634 635 /* find out whether we are running half- or full duplex */636 sungem_phy_write(phy, MII_NCONFIG, 0x2000);637 phy_reg = sungem_phy_read(phy, MII_NCONFIG);638 639 if ( (phy_reg & 0x0080) >> 7)640 phy->duplex |= DUPLEX_HALF;641 else642 phy->duplex |= DUPLEX_FULL;643 644 return 0;645}646 647static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg)648{649 /* enable fiber mode */650 sungem_phy_write(phy, MII_NCONFIG, 0x9020);651 /* LEDs active in both modes, autosense prio = fiber */652 sungem_phy_write(phy, MII_NCONFIG, 0x945f);653 654 if (!autoneg) {655 /* switch off fibre autoneg */656 sungem_phy_write(phy, MII_NCONFIG, 0xfc01);657 sungem_phy_write(phy, 0x0b, 0x0004);658 }659 660 phy->autoneg = autoneg;661 662 return 0;663}664 665#define BCM5461_FIBER_LINK (1 << 2)666#define BCM5461_MODE_MASK (3 << 1)667 668static int bcm5461_poll_link(struct mii_phy* phy)669{670 u32 phy_reg;671 int mode;672 673 /* find out in what mode we are */674 sungem_phy_write(phy, MII_NCONFIG, 0x7c00);675 phy_reg = sungem_phy_read(phy, MII_NCONFIG);676 677 mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;678 679 if ( mode == BCM54XX_COPPER)680 return genmii_poll_link(phy);681 682 /* find out whether we have a link */683 sungem_phy_write(phy, MII_NCONFIG, 0x7000);684 phy_reg = sungem_phy_read(phy, MII_NCONFIG);685 686 if (phy_reg & BCM5461_FIBER_LINK)687 return 1;688 else689 return 0;690}691 692#define BCM5461_FIBER_DUPLEX (1 << 3)693 694static int bcm5461_read_link(struct mii_phy* phy)695{696 u32 phy_reg;697 int mode;698 699 /* find out in what mode we are */700 sungem_phy_write(phy, MII_NCONFIG, 0x7c00);701 phy_reg = sungem_phy_read(phy, MII_NCONFIG);702 703 mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;704 705 if ( mode == BCM54XX_COPPER) {706 return bcm54xx_read_link(phy);707 }708 709 phy->speed = SPEED_1000;710 711 /* find out whether we are running half- or full duplex */712 sungem_phy_write(phy, MII_NCONFIG, 0x7000);713 phy_reg = sungem_phy_read(phy, MII_NCONFIG);714 715 if (phy_reg & BCM5461_FIBER_DUPLEX)716 phy->duplex |= DUPLEX_FULL;717 else718 phy->duplex |= DUPLEX_HALF;719 720 return 0;721}722 723static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg)724{725 /* select fiber mode, enable 1000 base-X registers */726 sungem_phy_write(phy, MII_NCONFIG, 0xfc0b);727 728 if (autoneg) {729 /* enable fiber with no autonegotiation */730 sungem_phy_write(phy, MII_ADVERTISE, 0x01e0);731 sungem_phy_write(phy, MII_BMCR, 0x1140);732 } else {733 /* enable fiber with autonegotiation */734 sungem_phy_write(phy, MII_BMCR, 0x0140);735 }736 737 phy->autoneg = autoneg;738 739 return 0;740}741 742static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)743{744 u16 ctl, adv;745 746 phy->autoneg = 1;747 phy->speed = SPEED_10;748 phy->duplex = DUPLEX_HALF;749 phy->pause = 0;750 phy->advertising = advertise;751 752 /* Setup standard advertise */753 adv = sungem_phy_read(phy, MII_ADVERTISE);754 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);755 if (advertise & ADVERTISED_10baseT_Half)756 adv |= ADVERTISE_10HALF;757 if (advertise & ADVERTISED_10baseT_Full)758 adv |= ADVERTISE_10FULL;759 if (advertise & ADVERTISED_100baseT_Half)760 adv |= ADVERTISE_100HALF;761 if (advertise & ADVERTISED_100baseT_Full)762 adv |= ADVERTISE_100FULL;763 if (advertise & ADVERTISED_Pause)764 adv |= ADVERTISE_PAUSE_CAP;765 if (advertise & ADVERTISED_Asym_Pause)766 adv |= ADVERTISE_PAUSE_ASYM;767 sungem_phy_write(phy, MII_ADVERTISE, adv);768 769 /* Setup 1000BT advertise & enable crossover detect770 * XXX How do we advertise 1000BT ? Darwin source is771 * confusing here, they read from specific control and772 * write to control... Someone has specs for those773 * beasts ?774 */775 adv = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);776 adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX;777 adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |778 MII_1000BASETCONTROL_HALFDUPLEXCAP);779 if (advertise & SUPPORTED_1000baseT_Half)780 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;781 if (advertise & SUPPORTED_1000baseT_Full)782 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;783 sungem_phy_write(phy, MII_1000BASETCONTROL, adv);784 785 /* Start/Restart aneg */786 ctl = sungem_phy_read(phy, MII_BMCR);787 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);788 sungem_phy_write(phy, MII_BMCR, ctl);789 790 return 0;791}792 793static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd)794{795 u16 ctl, ctl2;796 797 phy->autoneg = 0;798 phy->speed = speed;799 phy->duplex = fd;800 phy->pause = 0;801 802 ctl = sungem_phy_read(phy, MII_BMCR);803 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);804 ctl |= BMCR_RESET;805 806 /* Select speed & duplex */807 switch(speed) {808 case SPEED_10:809 break;810 case SPEED_100:811 ctl |= BMCR_SPEED100;812 break;813 /* I'm not sure about the one below, again, Darwin source is814 * quite confusing and I lack chip specs815 */816 case SPEED_1000:817 ctl |= BMCR_SPD2;818 }819 if (fd == DUPLEX_FULL)820 ctl |= BMCR_FULLDPLX;821 822 /* Disable crossover. Again, the way Apple does it is strange,823 * though I don't assume they are wrong ;)824 */825 ctl2 = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);826 ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX |827 MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX |828 MII_1000BASETCONTROL_FULLDUPLEXCAP |829 MII_1000BASETCONTROL_HALFDUPLEXCAP);830 if (speed == SPEED_1000)831 ctl2 |= (fd == DUPLEX_FULL) ?832 MII_1000BASETCONTROL_FULLDUPLEXCAP :833 MII_1000BASETCONTROL_HALFDUPLEXCAP;834 sungem_phy_write(phy, MII_1000BASETCONTROL, ctl2);835 836 // XXX Should we set the sungem to GII now on 1000BT ?837 838 sungem_phy_write(phy, MII_BMCR, ctl);839 840 return 0;841}842 843static int marvell_read_link(struct mii_phy *phy)844{845 u16 status, pmask;846 847 if (phy->autoneg) {848 status = sungem_phy_read(phy, MII_M1011_PHY_SPEC_STATUS);849 if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)850 return -EAGAIN;851 if (status & MII_M1011_PHY_SPEC_STATUS_1000)852 phy->speed = SPEED_1000;853 else if (status & MII_M1011_PHY_SPEC_STATUS_100)854 phy->speed = SPEED_100;855 else856 phy->speed = SPEED_10;857 if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)858 phy->duplex = DUPLEX_FULL;859 else860 phy->duplex = DUPLEX_HALF;861 pmask = MII_M1011_PHY_SPEC_STATUS_TX_PAUSE |862 MII_M1011_PHY_SPEC_STATUS_RX_PAUSE;863 phy->pause = (status & pmask) == pmask;864 }865 /* On non-aneg, we assume what we put in BMCR is the speed,866 * though magic-aneg shouldn't prevent this case from occurring867 */868 869 return 0;870}871 872#define MII_BASIC_FEATURES \873 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \874 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \875 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | \876 SUPPORTED_Pause)877 878/* On gigabit capable PHYs, we advertise Pause support but not asym pause879 * support for now as I'm not sure it's supported and Darwin doesn't do880 * it neither. --BenH.881 */882#define MII_GBIT_FEATURES \883 (MII_BASIC_FEATURES | \884 SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)885 886/* Broadcom BCM 5201 */887static const struct mii_phy_ops bcm5201_phy_ops = {888 .init = bcm5201_init,889 .suspend = bcm5201_suspend,890 .setup_aneg = genmii_setup_aneg,891 .setup_forced = genmii_setup_forced,892 .poll_link = genmii_poll_link,893 .read_link = genmii_read_link,894};895 896static const struct mii_phy_def bcm5201_phy_def = {897 .phy_id = 0x00406210,898 .phy_id_mask = 0xfffffff0,899 .name = "BCM5201",900 .features = MII_BASIC_FEATURES,901 .magic_aneg = 1,902 .ops = &bcm5201_phy_ops903};904 905/* Broadcom BCM 5221 */906static const struct mii_phy_ops bcm5221_phy_ops = {907 .suspend = bcm5221_suspend,908 .init = bcm5221_init,909 .setup_aneg = genmii_setup_aneg,910 .setup_forced = genmii_setup_forced,911 .poll_link = genmii_poll_link,912 .read_link = genmii_read_link,913};914 915static const struct mii_phy_def bcm5221_phy_def = {916 .phy_id = 0x004061e0,917 .phy_id_mask = 0xfffffff0,918 .name = "BCM5221",919 .features = MII_BASIC_FEATURES,920 .magic_aneg = 1,921 .ops = &bcm5221_phy_ops922};923 924/* Broadcom BCM 5241 */925static const struct mii_phy_ops bcm5241_phy_ops = {926 .suspend = bcm5241_suspend,927 .init = bcm5241_init,928 .setup_aneg = genmii_setup_aneg,929 .setup_forced = genmii_setup_forced,930 .poll_link = genmii_poll_link,931 .read_link = genmii_read_link,932};933 934static const struct mii_phy_def bcm5241_phy_def = {935 .phy_id = 0x0143bc30,936 .phy_id_mask = 0xfffffff0,937 .name = "BCM5241",938 .features = MII_BASIC_FEATURES,939 .magic_aneg = 1,940 .ops = &bcm5241_phy_ops941};942 943/* Broadcom BCM 5400 */944static const struct mii_phy_ops bcm5400_phy_ops = {945 .init = bcm5400_init,946 .suspend = bcm5400_suspend,947 .setup_aneg = bcm54xx_setup_aneg,948 .setup_forced = bcm54xx_setup_forced,949 .poll_link = genmii_poll_link,950 .read_link = bcm54xx_read_link,951};952 953static const struct mii_phy_def bcm5400_phy_def = {954 .phy_id = 0x00206040,955 .phy_id_mask = 0xfffffff0,956 .name = "BCM5400",957 .features = MII_GBIT_FEATURES,958 .magic_aneg = 1,959 .ops = &bcm5400_phy_ops960};961 962/* Broadcom BCM 5401 */963static const struct mii_phy_ops bcm5401_phy_ops = {964 .init = bcm5401_init,965 .suspend = bcm5401_suspend,966 .setup_aneg = bcm54xx_setup_aneg,967 .setup_forced = bcm54xx_setup_forced,968 .poll_link = genmii_poll_link,969 .read_link = bcm54xx_read_link,970};971 972static const struct mii_phy_def bcm5401_phy_def = {973 .phy_id = 0x00206050,974 .phy_id_mask = 0xfffffff0,975 .name = "BCM5401",976 .features = MII_GBIT_FEATURES,977 .magic_aneg = 1,978 .ops = &bcm5401_phy_ops979};980 981/* Broadcom BCM 5411 */982static const struct mii_phy_ops bcm5411_phy_ops = {983 .init = bcm5411_init,984 .suspend = generic_suspend,985 .setup_aneg = bcm54xx_setup_aneg,986 .setup_forced = bcm54xx_setup_forced,987 .poll_link = genmii_poll_link,988 .read_link = bcm54xx_read_link,989};990 991static const struct mii_phy_def bcm5411_phy_def = {992 .phy_id = 0x00206070,993 .phy_id_mask = 0xfffffff0,994 .name = "BCM5411",995 .features = MII_GBIT_FEATURES,996 .magic_aneg = 1,997 .ops = &bcm5411_phy_ops998};999 1000/* Broadcom BCM 5421 */1001static const struct mii_phy_ops bcm5421_phy_ops = {1002 .init = bcm5421_init,1003 .suspend = generic_suspend,1004 .setup_aneg = bcm54xx_setup_aneg,1005 .setup_forced = bcm54xx_setup_forced,1006 .poll_link = bcm5421_poll_link,1007 .read_link = bcm5421_read_link,1008 .enable_fiber = bcm5421_enable_fiber,1009};1010 1011static const struct mii_phy_def bcm5421_phy_def = {1012 .phy_id = 0x002060e0,1013 .phy_id_mask = 0xfffffff0,1014 .name = "BCM5421",1015 .features = MII_GBIT_FEATURES,1016 .magic_aneg = 1,1017 .ops = &bcm5421_phy_ops1018};1019 1020/* Broadcom BCM 5421 built-in K2 */1021static const struct mii_phy_ops bcm5421k2_phy_ops = {1022 .init = bcm5421_init,1023 .suspend = generic_suspend,1024 .setup_aneg = bcm54xx_setup_aneg,1025 .setup_forced = bcm54xx_setup_forced,1026 .poll_link = genmii_poll_link,1027 .read_link = bcm54xx_read_link,1028};1029 1030static const struct mii_phy_def bcm5421k2_phy_def = {1031 .phy_id = 0x002062e0,1032 .phy_id_mask = 0xfffffff0,1033 .name = "BCM5421-K2",1034 .features = MII_GBIT_FEATURES,1035 .magic_aneg = 1,1036 .ops = &bcm5421k2_phy_ops1037};1038 1039static const struct mii_phy_ops bcm5461_phy_ops = {1040 .init = bcm5421_init,1041 .suspend = generic_suspend,1042 .setup_aneg = bcm54xx_setup_aneg,1043 .setup_forced = bcm54xx_setup_forced,1044 .poll_link = bcm5461_poll_link,1045 .read_link = bcm5461_read_link,1046 .enable_fiber = bcm5461_enable_fiber,1047};1048 1049static const struct mii_phy_def bcm5461_phy_def = {1050 .phy_id = 0x002060c0,1051 .phy_id_mask = 0xfffffff0,1052 .name = "BCM5461",1053 .features = MII_GBIT_FEATURES,1054 .magic_aneg = 1,1055 .ops = &bcm5461_phy_ops1056};1057 1058/* Broadcom BCM 5462 built-in Vesta */1059static const struct mii_phy_ops bcm5462V_phy_ops = {1060 .init = bcm5421_init,1061 .suspend = generic_suspend,1062 .setup_aneg = bcm54xx_setup_aneg,1063 .setup_forced = bcm54xx_setup_forced,1064 .poll_link = genmii_poll_link,1065 .read_link = bcm54xx_read_link,1066};1067 1068static const struct mii_phy_def bcm5462V_phy_def = {1069 .phy_id = 0x002060d0,1070 .phy_id_mask = 0xfffffff0,1071 .name = "BCM5462-Vesta",1072 .features = MII_GBIT_FEATURES,1073 .magic_aneg = 1,1074 .ops = &bcm5462V_phy_ops1075};1076 1077/* Marvell 88E1101 amd 88E1111 */1078static const struct mii_phy_ops marvell88e1101_phy_ops = {1079 .suspend = generic_suspend,1080 .setup_aneg = marvell_setup_aneg,1081 .setup_forced = marvell_setup_forced,1082 .poll_link = genmii_poll_link,1083 .read_link = marvell_read_link1084};1085 1086static const struct mii_phy_ops marvell88e1111_phy_ops = {1087 .init = marvell88e1111_init,1088 .suspend = generic_suspend,1089 .setup_aneg = marvell_setup_aneg,1090 .setup_forced = marvell_setup_forced,1091 .poll_link = genmii_poll_link,1092 .read_link = marvell_read_link1093};1094 1095/* two revs in darwin for the 88e1101 ... I could use a datasheet1096 * to get the proper names...1097 */1098static const struct mii_phy_def marvell88e1101v1_phy_def = {1099 .phy_id = 0x01410c20,1100 .phy_id_mask = 0xfffffff0,1101 .name = "Marvell 88E1101v1",1102 .features = MII_GBIT_FEATURES,1103 .magic_aneg = 1,1104 .ops = &marvell88e1101_phy_ops1105};1106 1107static const struct mii_phy_def marvell88e1101v2_phy_def = {1108 .phy_id = 0x01410c60,1109 .phy_id_mask = 0xfffffff0,1110 .name = "Marvell 88E1101v2",1111 .features = MII_GBIT_FEATURES,1112 .magic_aneg = 1,1113 .ops = &marvell88e1101_phy_ops1114};1115 1116static const struct mii_phy_def marvell88e1111_phy_def = {1117 .phy_id = 0x01410cc0,1118 .phy_id_mask = 0xfffffff0,1119 .name = "Marvell 88E1111",1120 .features = MII_GBIT_FEATURES,1121 .magic_aneg = 1,1122 .ops = &marvell88e1111_phy_ops1123};1124 1125/* Generic implementation for most 10/100 PHYs */1126static const struct mii_phy_ops generic_phy_ops = {1127 .setup_aneg = genmii_setup_aneg,1128 .setup_forced = genmii_setup_forced,1129 .poll_link = genmii_poll_link,1130 .read_link = genmii_read_link1131};1132 1133static const struct mii_phy_def genmii_phy_def = {1134 .phy_id = 0x00000000,1135 .phy_id_mask = 0x00000000,1136 .name = "Generic MII",1137 .features = MII_BASIC_FEATURES,1138 .magic_aneg = 0,1139 .ops = &generic_phy_ops1140};1141 1142static const struct mii_phy_def *mii_phy_table[] = {1143 &bcm5201_phy_def,1144 &bcm5221_phy_def,1145 &bcm5241_phy_def,1146 &bcm5400_phy_def,1147 &bcm5401_phy_def,1148 &bcm5411_phy_def,1149 &bcm5421_phy_def,1150 &bcm5421k2_phy_def,1151 &bcm5461_phy_def,1152 &bcm5462V_phy_def,1153 &marvell88e1101v1_phy_def,1154 &marvell88e1101v2_phy_def,1155 &marvell88e1111_phy_def,1156 &genmii_phy_def,1157 NULL1158};1159 1160int sungem_phy_probe(struct mii_phy *phy, int mii_id)1161{1162 const struct mii_phy_def *def;1163 int rc;1164 u32 id;1165 int i;1166 1167 /* We do not reset the mii_phy structure as the driver1168 * may re-probe the PHY regulary1169 */1170 phy->mii_id = mii_id;1171 1172 /* Take PHY out of isloate mode and reset it. */1173 rc = reset_one_mii_phy(phy, mii_id);1174 if (rc)1175 goto fail;1176 1177 /* Read ID and find matching entry */1178 id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));1179 printk(KERN_DEBUG KBUILD_MODNAME ": " "PHY ID: %x, addr: %x\n",1180 id, mii_id);1181 for (i=0; (def = mii_phy_table[i]) != NULL; i++)1182 if ((id & def->phy_id_mask) == def->phy_id)1183 break;1184 /* Should never be NULL (we have a generic entry), but... */1185 if (def == NULL)1186 goto fail;1187 1188 phy->def = def;1189 1190 return 0;1191fail:1192 phy->speed = 0;1193 phy->duplex = 0;1194 phy->pause = 0;1195 phy->advertising = 0;1196 return -ENODEV;1197}1198 1199EXPORT_SYMBOL(sungem_phy_probe);1200MODULE_DESCRIPTION("PHY drivers for the sungem Ethernet MAC driver");1201MODULE_LICENSE("GPL");1202