diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-01-03 21:54:43 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-01-03 21:54:43 -0500 |
commit | 63ea998a26d4e03d390fbe31818cc552994d16fd (patch) | |
tree | ab12dd7eb3a0018ae5ab62a798b1d11b0b619e71 /drivers/net/sungem_phy.c | |
parent | 7f18ba6248625a587559ed1206d12ccaa3ad045e (diff) |
[SUNGEM]: PHY updates & pause fixes (#2)
This patch adds support for a few more PHYs used by Apple and fixes
advertising and detecting of Pause (we were missing setting the bit in
MII_ADVERTISE and weren't testing in LPA for all PHYs).
Note that I currently only advertise pause, not asymetric pause. I
don't know for sure the details there, I suppose I should read a bit
more 802.3 references, and I don't now what sungem is capable of, but
I noticed the PCS code (originated from you) does the same.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sungem_phy.c')
-rw-r--r-- | drivers/net/sungem_phy.c | 179 |
1 files changed, 155 insertions, 24 deletions
diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c index 49800b25907d..d21991ee88c4 100644 --- a/drivers/net/sungem_phy.c +++ b/drivers/net/sungem_phy.c | |||
@@ -3,10 +3,9 @@ | |||
3 | * | 3 | * |
4 | * This file could be shared with other drivers. | 4 | * This file could be shared with other drivers. |
5 | * | 5 | * |
6 | * (c) 2002, Benjamin Herrenscmidt (benh@kernel.crashing.org) | 6 | * (c) 2002-2007, Benjamin Herrenscmidt (benh@kernel.crashing.org) |
7 | * | 7 | * |
8 | * TODO: | 8 | * TODO: |
9 | * - Implement WOL | ||
10 | * - Add support for PHYs that provide an IRQ line | 9 | * - Add support for PHYs that provide an IRQ line |
11 | * - Eventually moved the entire polling state machine in | 10 | * - Eventually moved the entire polling state machine in |
12 | * there (out of the eth driver), so that it can easily be | 11 | * there (out of the eth driver), so that it can easily be |
@@ -152,6 +151,44 @@ static int bcm5221_suspend(struct mii_phy* phy) | |||
152 | return 0; | 151 | return 0; |
153 | } | 152 | } |
154 | 153 | ||
154 | static int bcm5241_init(struct mii_phy* phy) | ||
155 | { | ||
156 | u16 data; | ||
157 | |||
158 | data = phy_read(phy, MII_BCM5221_TEST); | ||
159 | phy_write(phy, MII_BCM5221_TEST, | ||
160 | data | MII_BCM5221_TEST_ENABLE_SHADOWS); | ||
161 | |||
162 | data = phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2); | ||
163 | phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2, | ||
164 | data | MII_BCM5221_SHDOW_AUX_STAT2_APD); | ||
165 | |||
166 | data = phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4); | ||
167 | phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4, | ||
168 | data & ~MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR); | ||
169 | |||
170 | data = phy_read(phy, MII_BCM5221_TEST); | ||
171 | phy_write(phy, MII_BCM5221_TEST, | ||
172 | data & ~MII_BCM5221_TEST_ENABLE_SHADOWS); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int bcm5241_suspend(struct mii_phy* phy) | ||
178 | { | ||
179 | u16 data; | ||
180 | |||
181 | data = phy_read(phy, MII_BCM5221_TEST); | ||
182 | phy_write(phy, MII_BCM5221_TEST, | ||
183 | data | MII_BCM5221_TEST_ENABLE_SHADOWS); | ||
184 | |||
185 | data = phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4); | ||
186 | phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4, | ||
187 | data | MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
155 | static int bcm5400_init(struct mii_phy* phy) | 192 | static int bcm5400_init(struct mii_phy* phy) |
156 | { | 193 | { |
157 | u16 data; | 194 | u16 data; |
@@ -373,6 +410,10 @@ static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) | |||
373 | adv |= ADVERTISE_100HALF; | 410 | adv |= ADVERTISE_100HALF; |
374 | if (advertise & ADVERTISED_100baseT_Full) | 411 | if (advertise & ADVERTISED_100baseT_Full) |
375 | adv |= ADVERTISE_100FULL; | 412 | adv |= ADVERTISE_100FULL; |
413 | if (advertise & ADVERTISED_Pause) | ||
414 | adv |= ADVERTISE_PAUSE_CAP; | ||
415 | if (advertise & ADVERTISED_Asym_Pause) | ||
416 | adv |= ADVERTISE_PAUSE_ASYM; | ||
376 | phy_write(phy, MII_ADVERTISE, adv); | 417 | phy_write(phy, MII_ADVERTISE, adv); |
377 | 418 | ||
378 | /* Setup 1000BT advertise */ | 419 | /* Setup 1000BT advertise */ |
@@ -436,12 +477,15 @@ static int bcm54xx_read_link(struct mii_phy *phy) | |||
436 | val = phy_read(phy, MII_BCM5400_AUXSTATUS); | 477 | val = phy_read(phy, MII_BCM5400_AUXSTATUS); |
437 | link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >> | 478 | link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >> |
438 | MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT); | 479 | MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT); |
439 | phy->duplex = phy_BCM5400_link_table[link_mode][0] ? DUPLEX_FULL : DUPLEX_HALF; | 480 | phy->duplex = phy_BCM5400_link_table[link_mode][0] ? |
481 | DUPLEX_FULL : DUPLEX_HALF; | ||
440 | phy->speed = phy_BCM5400_link_table[link_mode][2] ? | 482 | phy->speed = phy_BCM5400_link_table[link_mode][2] ? |
441 | SPEED_1000 : | 483 | SPEED_1000 : |
442 | (phy_BCM5400_link_table[link_mode][1] ? SPEED_100 : SPEED_10); | 484 | (phy_BCM5400_link_table[link_mode][1] ? |
485 | SPEED_100 : SPEED_10); | ||
443 | val = phy_read(phy, MII_LPA); | 486 | val = phy_read(phy, MII_LPA); |
444 | phy->pause = ((val & LPA_PAUSE) != 0); | 487 | phy->pause = (phy->duplex == DUPLEX_FULL) && |
488 | ((val & LPA_PAUSE) != 0); | ||
445 | } | 489 | } |
446 | /* On non-aneg, we assume what we put in BMCR is the speed, | 490 | /* On non-aneg, we assume what we put in BMCR is the speed, |
447 | * though magic-aneg shouldn't prevent this case from occurring | 491 | * though magic-aneg shouldn't prevent this case from occurring |
@@ -450,6 +494,28 @@ static int bcm54xx_read_link(struct mii_phy *phy) | |||
450 | return 0; | 494 | return 0; |
451 | } | 495 | } |
452 | 496 | ||
497 | static int marvell88e1111_init(struct mii_phy* phy) | ||
498 | { | ||
499 | u16 rev; | ||
500 | |||
501 | /* magic init sequence for rev 0 */ | ||
502 | rev = phy_read(phy, MII_PHYSID2) & 0x000f; | ||
503 | if (rev == 0) { | ||
504 | phy_write(phy, 0x1d, 0x000a); | ||
505 | phy_write(phy, 0x1e, 0x0821); | ||
506 | |||
507 | phy_write(phy, 0x1d, 0x0006); | ||
508 | phy_write(phy, 0x1e, 0x8600); | ||
509 | |||
510 | phy_write(phy, 0x1d, 0x000b); | ||
511 | phy_write(phy, 0x1e, 0x0100); | ||
512 | |||
513 | phy_write(phy, 0x1d, 0x0004); | ||
514 | phy_write(phy, 0x1e, 0x4850); | ||
515 | } | ||
516 | return 0; | ||
517 | } | ||
518 | |||
453 | static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) | 519 | static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) |
454 | { | 520 | { |
455 | u16 ctl, adv; | 521 | u16 ctl, adv; |
@@ -471,6 +537,10 @@ static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) | |||
471 | adv |= ADVERTISE_100HALF; | 537 | adv |= ADVERTISE_100HALF; |
472 | if (advertise & ADVERTISED_100baseT_Full) | 538 | if (advertise & ADVERTISED_100baseT_Full) |
473 | adv |= ADVERTISE_100FULL; | 539 | adv |= ADVERTISE_100FULL; |
540 | if (advertise & ADVERTISED_Pause) | ||
541 | adv |= ADVERTISE_PAUSE_CAP; | ||
542 | if (advertise & ADVERTISED_Asym_Pause) | ||
543 | adv |= ADVERTISE_PAUSE_ASYM; | ||
474 | phy_write(phy, MII_ADVERTISE, adv); | 544 | phy_write(phy, MII_ADVERTISE, adv); |
475 | 545 | ||
476 | /* Setup 1000BT advertise & enable crossover detect | 546 | /* Setup 1000BT advertise & enable crossover detect |
@@ -549,7 +619,7 @@ static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd) | |||
549 | 619 | ||
550 | static int marvell_read_link(struct mii_phy *phy) | 620 | static int marvell_read_link(struct mii_phy *phy) |
551 | { | 621 | { |
552 | u16 status; | 622 | u16 status, pmask; |
553 | 623 | ||
554 | if (phy->autoneg) { | 624 | if (phy->autoneg) { |
555 | status = phy_read(phy, MII_M1011_PHY_SPEC_STATUS); | 625 | status = phy_read(phy, MII_M1011_PHY_SPEC_STATUS); |
@@ -565,7 +635,9 @@ static int marvell_read_link(struct mii_phy *phy) | |||
565 | phy->duplex = DUPLEX_FULL; | 635 | phy->duplex = DUPLEX_FULL; |
566 | else | 636 | else |
567 | phy->duplex = DUPLEX_HALF; | 637 | phy->duplex = DUPLEX_HALF; |
568 | phy->pause = 0; /* XXX Check against spec ! */ | 638 | pmask = MII_M1011_PHY_SPEC_STATUS_TX_PAUSE | |
639 | MII_M1011_PHY_SPEC_STATUS_RX_PAUSE; | ||
640 | phy->pause = (status & pmask) == pmask; | ||
569 | } | 641 | } |
570 | /* On non-aneg, we assume what we put in BMCR is the speed, | 642 | /* On non-aneg, we assume what we put in BMCR is the speed, |
571 | * though magic-aneg shouldn't prevent this case from occurring | 643 | * though magic-aneg shouldn't prevent this case from occurring |
@@ -595,6 +667,10 @@ static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) | |||
595 | adv |= ADVERTISE_100HALF; | 667 | adv |= ADVERTISE_100HALF; |
596 | if (advertise & ADVERTISED_100baseT_Full) | 668 | if (advertise & ADVERTISED_100baseT_Full) |
597 | adv |= ADVERTISE_100FULL; | 669 | adv |= ADVERTISE_100FULL; |
670 | if (advertise & ADVERTISED_Pause) | ||
671 | adv |= ADVERTISE_PAUSE_CAP; | ||
672 | if (advertise & ADVERTISED_Asym_Pause) | ||
673 | adv |= ADVERTISE_PAUSE_ASYM; | ||
598 | phy_write(phy, MII_ADVERTISE, adv); | 674 | phy_write(phy, MII_ADVERTISE, adv); |
599 | 675 | ||
600 | /* Start/Restart aneg */ | 676 | /* Start/Restart aneg */ |
@@ -666,7 +742,8 @@ static int genmii_read_link(struct mii_phy *phy) | |||
666 | phy->speed = SPEED_100; | 742 | phy->speed = SPEED_100; |
667 | else | 743 | else |
668 | phy->speed = SPEED_10; | 744 | phy->speed = SPEED_10; |
669 | phy->pause = 0; | 745 | phy->pause = (phy->duplex == DUPLEX_FULL) && |
746 | ((lpa & LPA_PAUSE) != 0); | ||
670 | } | 747 | } |
671 | /* On non-aneg, we assume what we put in BMCR is the speed, | 748 | /* On non-aneg, we assume what we put in BMCR is the speed, |
672 | * though magic-aneg shouldn't prevent this case from occurring | 749 | * though magic-aneg shouldn't prevent this case from occurring |
@@ -676,11 +753,19 @@ static int genmii_read_link(struct mii_phy *phy) | |||
676 | } | 753 | } |
677 | 754 | ||
678 | 755 | ||
679 | #define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ | 756 | #define MII_BASIC_FEATURES \ |
680 | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ | 757 | (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ |
681 | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII) | 758 | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ |
682 | #define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \ | 759 | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | \ |
683 | SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full) | 760 | SUPPORTED_Pause) |
761 | |||
762 | /* On gigabit capable PHYs, we advertise Pause support but not asym pause | ||
763 | * support for now as I'm not sure it's supported and Darwin doesn't do | ||
764 | * it neither. --BenH. | ||
765 | */ | ||
766 | #define MII_GBIT_FEATURES \ | ||
767 | (MII_BASIC_FEATURES | \ | ||
768 | SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full) | ||
684 | 769 | ||
685 | /* Broadcom BCM 5201 */ | 770 | /* Broadcom BCM 5201 */ |
686 | static struct mii_phy_ops bcm5201_phy_ops = { | 771 | static struct mii_phy_ops bcm5201_phy_ops = { |
@@ -720,6 +805,24 @@ static struct mii_phy_def bcm5221_phy_def = { | |||
720 | .ops = &bcm5221_phy_ops | 805 | .ops = &bcm5221_phy_ops |
721 | }; | 806 | }; |
722 | 807 | ||
808 | /* Broadcom BCM 5241 */ | ||
809 | static struct mii_phy_ops bcm5241_phy_ops = { | ||
810 | .suspend = bcm5241_suspend, | ||
811 | .init = bcm5241_init, | ||
812 | .setup_aneg = genmii_setup_aneg, | ||
813 | .setup_forced = genmii_setup_forced, | ||
814 | .poll_link = genmii_poll_link, | ||
815 | .read_link = genmii_read_link, | ||
816 | }; | ||
817 | static struct mii_phy_def bcm5241_phy_def = { | ||
818 | .phy_id = 0x0143bc30, | ||
819 | .phy_id_mask = 0xfffffff0, | ||
820 | .name = "BCM5241", | ||
821 | .features = MII_BASIC_FEATURES, | ||
822 | .magic_aneg = 1, | ||
823 | .ops = &bcm5241_phy_ops | ||
824 | }; | ||
825 | |||
723 | /* Broadcom BCM 5400 */ | 826 | /* Broadcom BCM 5400 */ |
724 | static struct mii_phy_ops bcm5400_phy_ops = { | 827 | static struct mii_phy_ops bcm5400_phy_ops = { |
725 | .init = bcm5400_init, | 828 | .init = bcm5400_init, |
@@ -854,11 +957,8 @@ static struct mii_phy_def bcm5462V_phy_def = { | |||
854 | .ops = &bcm5462V_phy_ops | 957 | .ops = &bcm5462V_phy_ops |
855 | }; | 958 | }; |
856 | 959 | ||
857 | /* Marvell 88E1101 (Apple seem to deal with 2 different revs, | 960 | /* Marvell 88E1101 amd 88E1111 */ |
858 | * I masked out the 8 last bits to get both, but some specs | 961 | static struct mii_phy_ops marvell88e1101_phy_ops = { |
859 | * would be useful here) --BenH. | ||
860 | */ | ||
861 | static struct mii_phy_ops marvell_phy_ops = { | ||
862 | .suspend = generic_suspend, | 962 | .suspend = generic_suspend, |
863 | .setup_aneg = marvell_setup_aneg, | 963 | .setup_aneg = marvell_setup_aneg, |
864 | .setup_forced = marvell_setup_forced, | 964 | .setup_forced = marvell_setup_forced, |
@@ -866,13 +966,41 @@ static struct mii_phy_ops marvell_phy_ops = { | |||
866 | .read_link = marvell_read_link | 966 | .read_link = marvell_read_link |
867 | }; | 967 | }; |
868 | 968 | ||
869 | static struct mii_phy_def marvell_phy_def = { | 969 | static struct mii_phy_ops marvell88e1111_phy_ops = { |
870 | .phy_id = 0x01410c00, | 970 | .init = marvell88e1111_init, |
871 | .phy_id_mask = 0xffffff00, | 971 | .suspend = generic_suspend, |
872 | .name = "Marvell 88E1101", | 972 | .setup_aneg = marvell_setup_aneg, |
973 | .setup_forced = marvell_setup_forced, | ||
974 | .poll_link = genmii_poll_link, | ||
975 | .read_link = marvell_read_link | ||
976 | }; | ||
977 | |||
978 | /* two revs in darwin for the 88e1101 ... I could use a datasheet | ||
979 | * to get the proper names... | ||
980 | */ | ||
981 | static struct mii_phy_def marvell88e1101v1_phy_def = { | ||
982 | .phy_id = 0x01410c20, | ||
983 | .phy_id_mask = 0xfffffff0, | ||
984 | .name = "Marvell 88E1101v1", | ||
985 | .features = MII_GBIT_FEATURES, | ||
986 | .magic_aneg = 1, | ||
987 | .ops = &marvell88e1101_phy_ops | ||
988 | }; | ||
989 | static struct mii_phy_def marvell88e1101v2_phy_def = { | ||
990 | .phy_id = 0x01410c60, | ||
991 | .phy_id_mask = 0xfffffff0, | ||
992 | .name = "Marvell 88E1101v2", | ||
993 | .features = MII_GBIT_FEATURES, | ||
994 | .magic_aneg = 1, | ||
995 | .ops = &marvell88e1101_phy_ops | ||
996 | }; | ||
997 | static struct mii_phy_def marvell88e1111_phy_def = { | ||
998 | .phy_id = 0x01410cc0, | ||
999 | .phy_id_mask = 0xfffffff0, | ||
1000 | .name = "Marvell 88E1111", | ||
873 | .features = MII_GBIT_FEATURES, | 1001 | .features = MII_GBIT_FEATURES, |
874 | .magic_aneg = 1, | 1002 | .magic_aneg = 1, |
875 | .ops = &marvell_phy_ops | 1003 | .ops = &marvell88e1111_phy_ops |
876 | }; | 1004 | }; |
877 | 1005 | ||
878 | /* Generic implementation for most 10/100 PHYs */ | 1006 | /* Generic implementation for most 10/100 PHYs */ |
@@ -895,6 +1023,7 @@ static struct mii_phy_def genmii_phy_def = { | |||
895 | static struct mii_phy_def* mii_phy_table[] = { | 1023 | static struct mii_phy_def* mii_phy_table[] = { |
896 | &bcm5201_phy_def, | 1024 | &bcm5201_phy_def, |
897 | &bcm5221_phy_def, | 1025 | &bcm5221_phy_def, |
1026 | &bcm5241_phy_def, | ||
898 | &bcm5400_phy_def, | 1027 | &bcm5400_phy_def, |
899 | &bcm5401_phy_def, | 1028 | &bcm5401_phy_def, |
900 | &bcm5411_phy_def, | 1029 | &bcm5411_phy_def, |
@@ -902,7 +1031,9 @@ static struct mii_phy_def* mii_phy_table[] = { | |||
902 | &bcm5421k2_phy_def, | 1031 | &bcm5421k2_phy_def, |
903 | &bcm5461_phy_def, | 1032 | &bcm5461_phy_def, |
904 | &bcm5462V_phy_def, | 1033 | &bcm5462V_phy_def, |
905 | &marvell_phy_def, | 1034 | &marvell88e1101v1_phy_def, |
1035 | &marvell88e1101v2_phy_def, | ||
1036 | &marvell88e1111_phy_def, | ||
906 | &genmii_phy_def, | 1037 | &genmii_phy_def, |
907 | NULL | 1038 | NULL |
908 | }; | 1039 | }; |