Skip to content

Commit 6cfb3bc

Browse files
Charles-Antoine Couretdavem330
authored andcommitted
Marvell phy: check link status in case of fiber link.
For concerned phy, the fiber link is checked before the copper link. According to datasheet, the link which is up is enabled. If both links are down, copper link would be used. To detect fiber link status, we used the real time status because of troubles with the copper method. Tested with Marvell 88E1512. Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: Charles-Antoine Couret <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent bcf77d5 commit 6cfb3bc

File tree

1 file changed

+141
-12
lines changed

1 file changed

+141
-12
lines changed

drivers/net/phy/marvell.c

Lines changed: 141 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@
138138
#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */
139139
#define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */
140140

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+
141155
MODULE_DESCRIPTION("Marvell PHY driver");
142156
MODULE_AUTHOR("Andy Fleming");
143157
MODULE_LICENSE("GPL");
@@ -890,26 +904,79 @@ static int m88e1145_config_init(struct phy_device *phydev)
890904
return 0;
891905
}
892906

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
894956
*
895-
* Generic status code does not detect Fiber correctly!
896957
* Description:
897958
* Check the link, then figure out the current state
898959
* by comparing what we advertise with what the link partner
899960
* advertises. Start by checking the gigabit possibilities,
900961
* then move on to 10/100.
901962
*/
902-
static int marvell_read_status(struct phy_device *phydev)
963+
static int marvell_read_status_page(struct phy_device *phydev, int page)
903964
{
904965
int adv;
905966
int err;
906967
int lpa;
907968
int lpagb;
908969
int status = 0;
970+
int fiber;
909971

910-
/* Update the link, but return if there
972+
/* Detect and update the link, but return if there
911973
* 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);
913980
if (err)
914981
return err;
915982

@@ -930,9 +997,6 @@ static int marvell_read_status(struct phy_device *phydev)
930997
if (adv < 0)
931998
return adv;
932999

933-
phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) |
934-
mii_lpa_to_ethtool_lpa_t(lpa);
935-
9361000
lpa &= adv;
9371001

9381002
if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
@@ -957,9 +1021,30 @@ static int marvell_read_status(struct phy_device *phydev)
9571021
break;
9581022
}
9591023

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+
}
9631048
}
9641049
} else {
9651050
int bmcr = phy_read(phydev, MII_BMCR);
@@ -986,6 +1071,50 @@ static int marvell_read_status(struct phy_device *phydev)
9861071
return 0;
9871072
}
9881073

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+
}
9891118
static int marvell_aneg_done(struct phy_device *phydev)
9901119
{
9911120
int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
@@ -1361,7 +1490,7 @@ static struct phy_driver marvell_drivers[] = {
13611490
.phy_id = MARVELL_PHY_ID_88E1510,
13621491
.phy_id_mask = MARVELL_PHY_ID_MASK,
13631492
.name = "Marvell 88E1510",
1364-
.features = PHY_GBIT_FEATURES,
1493+
.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
13651494
.flags = PHY_HAS_INTERRUPT,
13661495
.probe = marvell_probe,
13671496
.config_init = &m88e1510_config_init,

0 commit comments

Comments
 (0)