138
138
#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */
139
139
#define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */
140
140
141
+ #define LPA_FIBER_1000HALF 0x40
142
+ #define LPA_FIBER_1000FULL 0x20
143
+
144
+ #define LPA_PAUSE_FIBER 0x180
145
+ #define LPA_PAUSE_ASYM_FIBER 0x100
146
+
147
+ #define ADVERTISE_FIBER_1000HALF 0x40
148
+ #define ADVERTISE_FIBER_1000FULL 0x20
149
+
150
+ #define ADVERTISE_PAUSE_FIBER 0x180
151
+ #define ADVERTISE_PAUSE_ASYM_FIBER 0x100
152
+
153
+ #define REGISTER_LINK_STATUS 0x400
154
+
141
155
MODULE_DESCRIPTION ("Marvell PHY driver" );
142
156
MODULE_AUTHOR ("Andy Fleming" );
143
157
MODULE_LICENSE ("GPL" );
@@ -890,26 +904,79 @@ static int m88e1145_config_init(struct phy_device *phydev)
890
904
return 0 ;
891
905
}
892
906
893
- /* marvell_read_status
907
+ /**
908
+ * fiber_lpa_to_ethtool_lpa_t
909
+ * @lpa: value of the MII_LPA register for fiber link
910
+ *
911
+ * A small helper function that translates MII_LPA
912
+ * bits to ethtool LP advertisement settings.
913
+ */
914
+ static u32 fiber_lpa_to_ethtool_lpa_t (u32 lpa )
915
+ {
916
+ u32 result = 0 ;
917
+
918
+ if (lpa & LPA_FIBER_1000HALF )
919
+ result |= ADVERTISED_1000baseT_Half ;
920
+ if (lpa & LPA_FIBER_1000FULL )
921
+ result |= ADVERTISED_1000baseT_Full ;
922
+
923
+ return result ;
924
+ }
925
+
926
+ /**
927
+ * marvell_update_link - update link status in real time in @phydev
928
+ * @phydev: target phy_device struct
929
+ *
930
+ * Description: Update the value in phydev->link to reflect the
931
+ * current link value.
932
+ */
933
+ static int marvell_update_link (struct phy_device * phydev , int fiber )
934
+ {
935
+ int status ;
936
+
937
+ /* Use the generic register for copper link, or specific
938
+ * register for fiber case */
939
+ if (fiber ) {
940
+ status = phy_read (phydev , MII_M1011_PHY_STATUS );
941
+ if (status < 0 )
942
+ return status ;
943
+
944
+ if ((status & REGISTER_LINK_STATUS ) == 0 )
945
+ phydev -> link = 0 ;
946
+ else
947
+ phydev -> link = 1 ;
948
+ } else {
949
+ return genphy_update_link (phydev );
950
+ }
951
+
952
+ return 0 ;
953
+ }
954
+
955
+ /* marvell_read_status_page
894
956
*
895
- * Generic status code does not detect Fiber correctly!
896
957
* Description:
897
958
* Check the link, then figure out the current state
898
959
* by comparing what we advertise with what the link partner
899
960
* advertises. Start by checking the gigabit possibilities,
900
961
* then move on to 10/100.
901
962
*/
902
- static int marvell_read_status (struct phy_device * phydev )
963
+ static int marvell_read_status_page (struct phy_device * phydev , int page )
903
964
{
904
965
int adv ;
905
966
int err ;
906
967
int lpa ;
907
968
int lpagb ;
908
969
int status = 0 ;
970
+ int fiber ;
909
971
910
- /* Update the link, but return if there
972
+ /* Detect and update the link, but return if there
911
973
* was an error */
912
- err = genphy_update_link (phydev );
974
+ if (page == MII_M1111_FIBER )
975
+ fiber = 1 ;
976
+ else
977
+ fiber = 0 ;
978
+
979
+ err = marvell_update_link (phydev , fiber );
913
980
if (err )
914
981
return err ;
915
982
@@ -930,9 +997,6 @@ static int marvell_read_status(struct phy_device *phydev)
930
997
if (adv < 0 )
931
998
return adv ;
932
999
933
- phydev -> lp_advertising = mii_stat1000_to_ethtool_lpa_t (lpagb ) |
934
- mii_lpa_to_ethtool_lpa_t (lpa );
935
-
936
1000
lpa &= adv ;
937
1001
938
1002
if (status & MII_M1011_PHY_STATUS_FULLDUPLEX )
@@ -957,9 +1021,30 @@ static int marvell_read_status(struct phy_device *phydev)
957
1021
break ;
958
1022
}
959
1023
960
- if (phydev -> duplex == DUPLEX_FULL ) {
961
- phydev -> pause = lpa & LPA_PAUSE_CAP ? 1 : 0 ;
962
- phydev -> asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0 ;
1024
+ if (!fiber ) {
1025
+ phydev -> lp_advertising = mii_stat1000_to_ethtool_lpa_t (lpagb ) |
1026
+ mii_lpa_to_ethtool_lpa_t (lpa );
1027
+
1028
+ if (phydev -> duplex == DUPLEX_FULL ) {
1029
+ phydev -> pause = lpa & LPA_PAUSE_CAP ? 1 : 0 ;
1030
+ phydev -> asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0 ;
1031
+ }
1032
+ } else {
1033
+ /* The fiber link is only 1000M capable */
1034
+ phydev -> lp_advertising = fiber_lpa_to_ethtool_lpa_t (lpa );
1035
+
1036
+ if (phydev -> duplex == DUPLEX_FULL ) {
1037
+ if (!(lpa & LPA_PAUSE_FIBER )) {
1038
+ phydev -> pause = 0 ;
1039
+ phydev -> asym_pause = 0 ;
1040
+ } else if ((lpa & LPA_PAUSE_ASYM_FIBER )) {
1041
+ phydev -> pause = 1 ;
1042
+ phydev -> asym_pause = 1 ;
1043
+ } else {
1044
+ phydev -> pause = 1 ;
1045
+ phydev -> asym_pause = 0 ;
1046
+ }
1047
+ }
963
1048
}
964
1049
} else {
965
1050
int bmcr = phy_read (phydev , MII_BMCR );
@@ -986,6 +1071,50 @@ static int marvell_read_status(struct phy_device *phydev)
986
1071
return 0 ;
987
1072
}
988
1073
1074
+ /* marvell_read_status
1075
+ *
1076
+ * Some Marvell's phys have two modes: fiber and copper.
1077
+ * Both need status checked.
1078
+ * Description:
1079
+ * First, check the fiber link and status.
1080
+ * If the fiber link is down, check the copper link and status which
1081
+ * will be the default value if both link are down.
1082
+ */
1083
+ static int marvell_read_status (struct phy_device * phydev )
1084
+ {
1085
+ int err ;
1086
+
1087
+ /* Check the fiber mode first */
1088
+ if (phydev -> supported & SUPPORTED_FIBRE ) {
1089
+ err = phy_write (phydev , MII_MARVELL_PHY_PAGE , MII_M1111_FIBER );
1090
+ if (err < 0 )
1091
+ goto error ;
1092
+
1093
+ err = marvell_read_status_page (phydev , MII_M1111_FIBER );
1094
+ if (err < 0 )
1095
+ goto error ;
1096
+
1097
+ /* If the fiber link is up, it is the selected and used link.
1098
+ * In this case, we need to stay in the fiber page.
1099
+ * Please to be careful about that, avoid to restore Copper page
1100
+ * in other functions which could break the behaviour
1101
+ * for some fiber phy like 88E1512.
1102
+ * */
1103
+ if (phydev -> link )
1104
+ return 0 ;
1105
+
1106
+ /* If fiber link is down, check and save copper mode state */
1107
+ err = phy_write (phydev , MII_MARVELL_PHY_PAGE , MII_M1111_COPPER );
1108
+ if (err < 0 )
1109
+ goto error ;
1110
+ }
1111
+
1112
+ return marvell_read_status_page (phydev , MII_M1111_COPPER );
1113
+
1114
+ error :
1115
+ phy_write (phydev , MII_MARVELL_PHY_PAGE , MII_M1111_COPPER );
1116
+ return err ;
1117
+ }
989
1118
static int marvell_aneg_done (struct phy_device * phydev )
990
1119
{
991
1120
int retval = phy_read (phydev , MII_M1011_PHY_STATUS );
@@ -1361,7 +1490,7 @@ static struct phy_driver marvell_drivers[] = {
1361
1490
.phy_id = MARVELL_PHY_ID_88E1510 ,
1362
1491
.phy_id_mask = MARVELL_PHY_ID_MASK ,
1363
1492
.name = "Marvell 88E1510" ,
1364
- .features = PHY_GBIT_FEATURES ,
1493
+ .features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE ,
1365
1494
.flags = PHY_HAS_INTERRUPT ,
1366
1495
.probe = marvell_probe ,
1367
1496
.config_init = & m88e1510_config_init ,
0 commit comments