aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sungem_phy.c
diff options
context:
space:
mode:
authorJens Osterkamp <jens@de.ibm.com>2007-02-20 17:30:50 -0500
committerJeff Garzik <jeff@garzik.org>2007-02-27 04:16:02 -0500
commiteb5b5b2ff96e8f3a42a46378968a166bb56bd900 (patch)
tree8293f3718c436078af6108e13ffc8d965c5a39ec /drivers/net/sungem_phy.c
parent6fedae1f6e66ab5f169bf58064e23e015fc1307d (diff)
sungem_phy: support bcm5461 phy, autoneg.
This version moves the medium variable to the card specific structure and changes the GMII_* to BCM54XX_* #defines. This patch adds improved version of enable_fiber for both the 5421 and the 5461 phy. It is now possible to specify with these wether you want autonegotiation or not. This is needed for bladecenter switches where some expect autonegotiation and some dont seem to like this at all. Depending on this flag it sets phy->autoneg accordingly for the fiber mode. More importantly it implements proper read_link and poll_link functions for both phys which can handle both copper and fiber mode by determining the medium first and then branching to the required functions. For fiber they all work fine, for copper they are not tested but return the result of the genmii_* function anyway which is supposed to work. The patch moves the genmii_* functions around to avoid foreward declarations. Signed-off-by: Jens Osterkamp <jens@de.ibm.com> Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com> Signed-off-by: Linas Vepstas <linas@austin.ibm.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/sungem_phy.c')
-rw-r--r--drivers/net/sungem_phy.c389
1 files changed, 254 insertions, 135 deletions
diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c
index 701ba4f3b69d..56a110ca5e6f 100644
--- a/drivers/net/sungem_phy.c
+++ b/drivers/net/sungem_phy.c
@@ -310,6 +310,107 @@ static int bcm5411_init(struct mii_phy* phy)
310 return 0; 310 return 0;
311} 311}
312 312
313static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
314{
315 u16 ctl, adv;
316
317 phy->autoneg = 1;
318 phy->speed = SPEED_10;
319 phy->duplex = DUPLEX_HALF;
320 phy->pause = 0;
321 phy->advertising = advertise;
322
323 /* Setup standard advertise */
324 adv = phy_read(phy, MII_ADVERTISE);
325 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
326 if (advertise & ADVERTISED_10baseT_Half)
327 adv |= ADVERTISE_10HALF;
328 if (advertise & ADVERTISED_10baseT_Full)
329 adv |= ADVERTISE_10FULL;
330 if (advertise & ADVERTISED_100baseT_Half)
331 adv |= ADVERTISE_100HALF;
332 if (advertise & ADVERTISED_100baseT_Full)
333 adv |= ADVERTISE_100FULL;
334 phy_write(phy, MII_ADVERTISE, adv);
335
336 /* Start/Restart aneg */
337 ctl = phy_read(phy, MII_BMCR);
338 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
339 phy_write(phy, MII_BMCR, ctl);
340
341 return 0;
342}
343
344static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
345{
346 u16 ctl;
347
348 phy->autoneg = 0;
349 phy->speed = speed;
350 phy->duplex = fd;
351 phy->pause = 0;
352
353 ctl = phy_read(phy, MII_BMCR);
354 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);
355
356 /* First reset the PHY */
357 phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
358
359 /* Select speed & duplex */
360 switch(speed) {
361 case SPEED_10:
362 break;
363 case SPEED_100:
364 ctl |= BMCR_SPEED100;
365 break;
366 case SPEED_1000:
367 default:
368 return -EINVAL;
369 }
370 if (fd == DUPLEX_FULL)
371 ctl |= BMCR_FULLDPLX;
372 phy_write(phy, MII_BMCR, ctl);
373
374 return 0;
375}
376
377static int genmii_poll_link(struct mii_phy *phy)
378{
379 u16 status;
380
381 (void)phy_read(phy, MII_BMSR);
382 status = phy_read(phy, MII_BMSR);
383 if ((status & BMSR_LSTATUS) == 0)
384 return 0;
385 if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))
386 return 0;
387 return 1;
388}
389
390static int genmii_read_link(struct mii_phy *phy)
391{
392 u16 lpa;
393
394 if (phy->autoneg) {
395 lpa = phy_read(phy, MII_LPA);
396
397 if (lpa & (LPA_10FULL | LPA_100FULL))
398 phy->duplex = DUPLEX_FULL;
399 else
400 phy->duplex = DUPLEX_HALF;
401 if (lpa & (LPA_100FULL | LPA_100HALF))
402 phy->speed = SPEED_100;
403 else
404 phy->speed = SPEED_10;
405 phy->pause = 0;
406 }
407 /* On non-aneg, we assume what we put in BMCR is the speed,
408 * though magic-aneg shouldn't prevent this case from occurring
409 */
410
411 return 0;
412}
413
313static int generic_suspend(struct mii_phy* phy) 414static int generic_suspend(struct mii_phy* phy)
314{ 415{
315 phy_write(phy, MII_BMCR, BMCR_PDOWN); 416 phy_write(phy, MII_BMCR, BMCR_PDOWN);
@@ -364,30 +465,6 @@ static int bcm5421_init(struct mii_phy* phy)
364 return 0; 465 return 0;
365} 466}
366 467
367static int bcm5421_enable_fiber(struct mii_phy* phy)
368{
369 /* enable fiber mode */
370 phy_write(phy, MII_NCONFIG, 0x9020);
371 /* LEDs active in both modes, autosense prio = fiber */
372 phy_write(phy, MII_NCONFIG, 0x945f);
373
374 /* switch off fibre autoneg */
375 phy_write(phy, MII_NCONFIG, 0xfc01);
376 phy_write(phy, 0x0b, 0x0004);
377
378 return 0;
379}
380
381static int bcm5461_enable_fiber(struct mii_phy* phy)
382{
383 phy_write(phy, MII_NCONFIG, 0xfc0c);
384 phy_write(phy, MII_BMCR, 0x4140);
385 phy_write(phy, MII_NCONFIG, 0xfc0b);
386 phy_write(phy, MII_BMCR, 0x0140);
387
388 return 0;
389}
390
391static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) 468static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)
392{ 469{
393 u16 ctl, adv; 470 u16 ctl, adv;
@@ -515,6 +592,155 @@ static int marvell88e1111_init(struct mii_phy* phy)
515 return 0; 592 return 0;
516} 593}
517 594
595#define BCM5421_MODE_MASK (1 << 5)
596
597static int bcm5421_poll_link(struct mii_phy* phy)
598{
599 u32 phy_reg;
600 int mode;
601
602 /* find out in what mode we are */
603 phy_write(phy, MII_NCONFIG, 0x1000);
604 phy_reg = phy_read(phy, MII_NCONFIG);
605
606 mode = (phy_reg & BCM5421_MODE_MASK) >> 5;
607
608 if ( mode == BCM54XX_COPPER)
609 return genmii_poll_link(phy);
610
611 /* try to find out wether we have a link */
612 phy_write(phy, MII_NCONFIG, 0x2000);
613 phy_reg = phy_read(phy, MII_NCONFIG);
614
615 if (phy_reg & 0x0020)
616 return 0;
617 else
618 return 1;
619}
620
621static int bcm5421_read_link(struct mii_phy* phy)
622{
623 u32 phy_reg;
624 int mode;
625
626 /* find out in what mode we are */
627 phy_write(phy, MII_NCONFIG, 0x1000);
628 phy_reg = phy_read(phy, MII_NCONFIG);
629
630 mode = (phy_reg & BCM5421_MODE_MASK ) >> 5;
631
632 if ( mode == BCM54XX_COPPER)
633 return bcm54xx_read_link(phy);
634
635 phy->speed = SPEED_1000;
636
637 /* find out wether we are running half- or full duplex */
638 phy_write(phy, MII_NCONFIG, 0x2000);
639 phy_reg = phy_read(phy, MII_NCONFIG);
640
641 if ( (phy_reg & 0x0080) >> 7)
642 phy->duplex |= DUPLEX_HALF;
643 else
644 phy->duplex |= DUPLEX_FULL;
645
646 return 0;
647}
648
649static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg)
650{
651 /* enable fiber mode */
652 phy_write(phy, MII_NCONFIG, 0x9020);
653 /* LEDs active in both modes, autosense prio = fiber */
654 phy_write(phy, MII_NCONFIG, 0x945f);
655
656 if (!autoneg) {
657 /* switch off fibre autoneg */
658 phy_write(phy, MII_NCONFIG, 0xfc01);
659 phy_write(phy, 0x0b, 0x0004);
660 }
661
662 phy->autoneg = autoneg;
663
664 return 0;
665}
666
667#define BCM5461_FIBER_LINK (1 << 2)
668#define BCM5461_MODE_MASK (3 << 1)
669
670static int bcm5461_poll_link(struct mii_phy* phy)
671{
672 u32 phy_reg;
673 int mode;
674
675 /* find out in what mode we are */
676 phy_write(phy, MII_NCONFIG, 0x7c00);
677 phy_reg = phy_read(phy, MII_NCONFIG);
678
679 mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;
680
681 if ( mode == BCM54XX_COPPER)
682 return genmii_poll_link(phy);
683
684 /* find out wether we have a link */
685 phy_write(phy, MII_NCONFIG, 0x7000);
686 phy_reg = phy_read(phy, MII_NCONFIG);
687
688 if (phy_reg & BCM5461_FIBER_LINK)
689 return 1;
690 else
691 return 0;
692}
693
694#define BCM5461_FIBER_DUPLEX (1 << 3)
695
696static int bcm5461_read_link(struct mii_phy* phy)
697{
698 u32 phy_reg;
699 int mode;
700
701 /* find out in what mode we are */
702 phy_write(phy, MII_NCONFIG, 0x7c00);
703 phy_reg = phy_read(phy, MII_NCONFIG);
704
705 mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;
706
707 if ( mode == BCM54XX_COPPER) {
708 return bcm54xx_read_link(phy);
709 }
710
711 phy->speed = SPEED_1000;
712
713 /* find out wether we are running half- or full duplex */
714 phy_write(phy, MII_NCONFIG, 0x7000);
715 phy_reg = phy_read(phy, MII_NCONFIG);
716
717 if (phy_reg & BCM5461_FIBER_DUPLEX)
718 phy->duplex |= DUPLEX_FULL;
719 else
720 phy->duplex |= DUPLEX_HALF;
721
722 return 0;
723}
724
725static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg)
726{
727 /* select fiber mode, enable 1000 base-X registers */
728 phy_write(phy, MII_NCONFIG, 0xfc0b);
729
730 if (autoneg) {
731 /* enable fiber with no autonegotiation */
732 phy_write(phy, MII_ADVERTISE, 0x01e0);
733 phy_write(phy, MII_BMCR, 0x1140);
734 } else {
735 /* enable fiber with autonegotiation */
736 phy_write(phy, MII_BMCR, 0x0140);
737 }
738
739 phy->autoneg = autoneg;
740
741 return 0;
742}
743
518static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) 744static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)
519{ 745{
520 u16 ctl, adv; 746 u16 ctl, adv;
@@ -645,113 +871,6 @@ static int marvell_read_link(struct mii_phy *phy)
645 return 0; 871 return 0;
646} 872}
647 873
648static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
649{
650 u16 ctl, adv;
651
652 phy->autoneg = 1;
653 phy->speed = SPEED_10;
654 phy->duplex = DUPLEX_HALF;
655 phy->pause = 0;
656 phy->advertising = advertise;
657
658 /* Setup standard advertise */
659 adv = phy_read(phy, MII_ADVERTISE);
660 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
661 if (advertise & ADVERTISED_10baseT_Half)
662 adv |= ADVERTISE_10HALF;
663 if (advertise & ADVERTISED_10baseT_Full)
664 adv |= ADVERTISE_10FULL;
665 if (advertise & ADVERTISED_100baseT_Half)
666 adv |= ADVERTISE_100HALF;
667 if (advertise & ADVERTISED_100baseT_Full)
668 adv |= ADVERTISE_100FULL;
669 if (advertise & ADVERTISED_Pause)
670 adv |= ADVERTISE_PAUSE_CAP;
671 if (advertise & ADVERTISED_Asym_Pause)
672 adv |= ADVERTISE_PAUSE_ASYM;
673 phy_write(phy, MII_ADVERTISE, adv);
674
675 /* Start/Restart aneg */
676 ctl = phy_read(phy, MII_BMCR);
677 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
678 phy_write(phy, MII_BMCR, ctl);
679
680 return 0;
681}
682
683static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
684{
685 u16 ctl;
686
687 phy->autoneg = 0;
688 phy->speed = speed;
689 phy->duplex = fd;
690 phy->pause = 0;
691
692 ctl = phy_read(phy, MII_BMCR);
693 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);
694
695 /* First reset the PHY */
696 phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
697
698 /* Select speed & duplex */
699 switch(speed) {
700 case SPEED_10:
701 break;
702 case SPEED_100:
703 ctl |= BMCR_SPEED100;
704 break;
705 case SPEED_1000:
706 default:
707 return -EINVAL;
708 }
709 if (fd == DUPLEX_FULL)
710 ctl |= BMCR_FULLDPLX;
711 phy_write(phy, MII_BMCR, ctl);
712
713 return 0;
714}
715
716static int genmii_poll_link(struct mii_phy *phy)
717{
718 u16 status;
719
720 (void)phy_read(phy, MII_BMSR);
721 status = phy_read(phy, MII_BMSR);
722 if ((status & BMSR_LSTATUS) == 0)
723 return 0;
724 if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))
725 return 0;
726 return 1;
727}
728
729static int genmii_read_link(struct mii_phy *phy)
730{
731 u16 lpa;
732
733 if (phy->autoneg) {
734 lpa = phy_read(phy, MII_LPA);
735
736 if (lpa & (LPA_10FULL | LPA_100FULL))
737 phy->duplex = DUPLEX_FULL;
738 else
739 phy->duplex = DUPLEX_HALF;
740 if (lpa & (LPA_100FULL | LPA_100HALF))
741 phy->speed = SPEED_100;
742 else
743 phy->speed = SPEED_10;
744 phy->pause = (phy->duplex == DUPLEX_FULL) &&
745 ((lpa & LPA_PAUSE) != 0);
746 }
747 /* On non-aneg, we assume what we put in BMCR is the speed,
748 * though magic-aneg shouldn't prevent this case from occurring
749 */
750
751 return 0;
752}
753
754
755#define MII_BASIC_FEATURES \ 874#define MII_BASIC_FEATURES \
756 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ 875 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
757 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ 876 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
@@ -885,8 +1004,8 @@ static struct mii_phy_ops bcm5421_phy_ops = {
885 .suspend = generic_suspend, 1004 .suspend = generic_suspend,
886 .setup_aneg = bcm54xx_setup_aneg, 1005 .setup_aneg = bcm54xx_setup_aneg,
887 .setup_forced = bcm54xx_setup_forced, 1006 .setup_forced = bcm54xx_setup_forced,
888 .poll_link = genmii_poll_link, 1007 .poll_link = bcm5421_poll_link,
889 .read_link = bcm54xx_read_link, 1008 .read_link = bcm5421_read_link,
890 .enable_fiber = bcm5421_enable_fiber, 1009 .enable_fiber = bcm5421_enable_fiber,
891}; 1010};
892 1011
@@ -923,8 +1042,8 @@ static struct mii_phy_ops bcm5461_phy_ops = {
923 .suspend = generic_suspend, 1042 .suspend = generic_suspend,
924 .setup_aneg = bcm54xx_setup_aneg, 1043 .setup_aneg = bcm54xx_setup_aneg,
925 .setup_forced = bcm54xx_setup_forced, 1044 .setup_forced = bcm54xx_setup_forced,
926 .poll_link = genmii_poll_link, 1045 .poll_link = bcm5461_poll_link,
927 .read_link = bcm54xx_read_link, 1046 .read_link = bcm5461_read_link,
928 .enable_fiber = bcm5461_enable_fiber, 1047 .enable_fiber = bcm5461_enable_fiber,
929}; 1048};
930 1049