diff options
-rw-r--r-- | drivers/net/sungem_phy.c | 389 | ||||
-rw-r--r-- | drivers/net/sungem_phy.h | 10 |
2 files changed, 263 insertions, 136 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 | ||
313 | static 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 | |||
344 | static 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 | |||
377 | static 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 | |||
390 | static 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 | |||
313 | static int generic_suspend(struct mii_phy* phy) | 414 | static 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 | ||
367 | static 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 | |||
381 | static 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 | |||
391 | static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) | 468 | static 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 | |||
597 | static 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 | |||
621 | static 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 | |||
649 | static 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 | |||
670 | static 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 | |||
696 | static 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 | |||
725 | static 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 | |||
518 | static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) | 744 | static 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 | ||
648 | static 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 | |||
683 | static 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 | |||
716 | static 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 | |||
729 | static 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 | ||
diff --git a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h index 1d70ba6f9f10..af02f9479cbb 100644 --- a/drivers/net/sungem_phy.h +++ b/drivers/net/sungem_phy.h | |||
@@ -12,7 +12,7 @@ struct mii_phy_ops | |||
12 | int (*setup_forced)(struct mii_phy *phy, int speed, int fd); | 12 | int (*setup_forced)(struct mii_phy *phy, int speed, int fd); |
13 | int (*poll_link)(struct mii_phy *phy); | 13 | int (*poll_link)(struct mii_phy *phy); |
14 | int (*read_link)(struct mii_phy *phy); | 14 | int (*read_link)(struct mii_phy *phy); |
15 | int (*enable_fiber)(struct mii_phy *phy); | 15 | int (*enable_fiber)(struct mii_phy *phy, int autoneg); |
16 | }; | 16 | }; |
17 | 17 | ||
18 | /* Structure used to statically define an mii/gii based PHY */ | 18 | /* Structure used to statically define an mii/gii based PHY */ |
@@ -26,6 +26,14 @@ struct mii_phy_def | |||
26 | const struct mii_phy_ops* ops; | 26 | const struct mii_phy_ops* ops; |
27 | }; | 27 | }; |
28 | 28 | ||
29 | enum { | ||
30 | BCM54XX_COPPER, | ||
31 | BCM54XX_FIBER, | ||
32 | BCM54XX_GBIC, | ||
33 | BCM54XX_SGMII, | ||
34 | BCM54XX_UNKNOWN, | ||
35 | }; | ||
36 | |||
29 | /* An instance of a PHY, partially borrowed from mii_if_info */ | 37 | /* An instance of a PHY, partially borrowed from mii_if_info */ |
30 | struct mii_phy | 38 | struct mii_phy |
31 | { | 39 | { |