Skip to content

Commit 58389c0

Browse files
Marek Vasutkuba-moo
authored andcommitted
net: phy: micrel: ksz9031/ksz9131: add cabletest support
Add cable test support for Micrel KSZ9x31 PHYs. Tested on i.MX8M Mini with KSZ9131RNX in 100/Full mode with pairs shuffled before magnetics: (note: Cable test started/completed messages are omitted) mx8mm-ksz9131-a-d-connected$ ethtool --cable-test eth0 Pair A code OK Pair B code Short within Pair Pair B, fault length: 0.80m Pair C code Short within Pair Pair C, fault length: 0.80m Pair D code OK mx8mm-ksz9131-a-b-connected$ ethtool --cable-test eth0 Pair A code OK Pair B code OK Pair C code Short within Pair Pair C, fault length: 0.00m Pair D code Short within Pair Pair D, fault length: 0.00m Tested on R8A77951 Salvator-XS with KSZ9031RNX and all four pairs connected: (note: Cable test started/completed messages are omitted) r8a7795-ksz9031-all-connected$ ethtool --cable-test eth0 Pair A code OK Pair B code OK Pair C code OK Pair D code OK The CTRL1000 CTL1000_ENABLE_MASTER and CTL1000_AS_MASTER bits are not restored by calling phy_init_hw(), they must be manually cached in ksz9x31_cable_test_start() and restored at the end of ksz9x31_cable_test_get_status(). Signed-off-by: Marek Vasut <marex@denx.de> Cc: Heiner Kallweit <hkallweit1@gmail.com> Cc: Oleksij Rempel <linux@rempel-privat.de> Cc: Paolo Abeni <pabeni@redhat.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Tested-by: Claudiu Beznea <claudiu.beznea@microchip.com> Link: https://lore.kernel.org/r/20220407105534.85833-1-marex@denx.de Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 85b15c2 commit 58389c0

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed

drivers/net/phy/micrel.c

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,27 @@
7070
#define KSZ8081_LMD_SHORT_INDICATOR BIT(12)
7171
#define KSZ8081_LMD_DELTA_TIME_MASK GENMASK(8, 0)
7272

73+
#define KSZ9x31_LMD 0x12
74+
#define KSZ9x31_LMD_VCT_EN BIT(15)
75+
#define KSZ9x31_LMD_VCT_DIS_TX BIT(14)
76+
#define KSZ9x31_LMD_VCT_PAIR(n) (((n) & 0x3) << 12)
77+
#define KSZ9x31_LMD_VCT_SEL_RESULT 0
78+
#define KSZ9x31_LMD_VCT_SEL_THRES_HI BIT(10)
79+
#define KSZ9x31_LMD_VCT_SEL_THRES_LO BIT(11)
80+
#define KSZ9x31_LMD_VCT_SEL_MASK GENMASK(11, 10)
81+
#define KSZ9x31_LMD_VCT_ST_NORMAL 0
82+
#define KSZ9x31_LMD_VCT_ST_OPEN 1
83+
#define KSZ9x31_LMD_VCT_ST_SHORT 2
84+
#define KSZ9x31_LMD_VCT_ST_FAIL 3
85+
#define KSZ9x31_LMD_VCT_ST_MASK GENMASK(9, 8)
86+
#define KSZ9x31_LMD_VCT_DATA_REFLECTED_INVALID BIT(7)
87+
#define KSZ9x31_LMD_VCT_DATA_SIG_WAIT_TOO_LONG BIT(6)
88+
#define KSZ9x31_LMD_VCT_DATA_MASK100 BIT(5)
89+
#define KSZ9x31_LMD_VCT_DATA_NLP_FLP BIT(4)
90+
#define KSZ9x31_LMD_VCT_DATA_LO_PULSE_MASK GENMASK(3, 2)
91+
#define KSZ9x31_LMD_VCT_DATA_HI_PULSE_MASK GENMASK(1, 0)
92+
#define KSZ9x31_LMD_VCT_DATA_MASK GENMASK(7, 0)
93+
7394
/* Lan8814 general Interrupt control/status reg in GPHY specific block. */
7495
#define LAN8814_INTC 0x18
7596
#define LAN8814_INTS 0x1B
@@ -280,6 +301,7 @@ struct kszphy_priv {
280301
struct kszphy_ptp_priv ptp_priv;
281302
const struct kszphy_type *type;
282303
int led_mode;
304+
u16 vct_ctrl1000;
283305
bool rmii_ref_clk_sel;
284306
bool rmii_ref_clk_sel_val;
285307
u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
@@ -1326,6 +1348,199 @@ static int ksz9031_read_status(struct phy_device *phydev)
13261348
return 0;
13271349
}
13281350

1351+
static int ksz9x31_cable_test_start(struct phy_device *phydev)
1352+
{
1353+
struct kszphy_priv *priv = phydev->priv;
1354+
int ret;
1355+
1356+
/* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
1357+
* Prior to running the cable diagnostics, Auto-negotiation should
1358+
* be disabled, full duplex set and the link speed set to 1000Mbps
1359+
* via the Basic Control Register.
1360+
*/
1361+
ret = phy_modify(phydev, MII_BMCR,
1362+
BMCR_SPEED1000 | BMCR_FULLDPLX |
1363+
BMCR_ANENABLE | BMCR_SPEED100,
1364+
BMCR_SPEED1000 | BMCR_FULLDPLX);
1365+
if (ret)
1366+
return ret;
1367+
1368+
/* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
1369+
* The Master-Slave configuration should be set to Slave by writing
1370+
* a value of 0x1000 to the Auto-Negotiation Master Slave Control
1371+
* Register.
1372+
*/
1373+
ret = phy_read(phydev, MII_CTRL1000);
1374+
if (ret < 0)
1375+
return ret;
1376+
1377+
/* Cache these bits, they need to be restored once LinkMD finishes. */
1378+
priv->vct_ctrl1000 = ret & (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
1379+
ret &= ~(CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
1380+
ret |= CTL1000_ENABLE_MASTER;
1381+
1382+
return phy_write(phydev, MII_CTRL1000, ret);
1383+
}
1384+
1385+
static int ksz9x31_cable_test_result_trans(u16 status)
1386+
{
1387+
switch (FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status)) {
1388+
case KSZ9x31_LMD_VCT_ST_NORMAL:
1389+
return ETHTOOL_A_CABLE_RESULT_CODE_OK;
1390+
case KSZ9x31_LMD_VCT_ST_OPEN:
1391+
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
1392+
case KSZ9x31_LMD_VCT_ST_SHORT:
1393+
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
1394+
case KSZ9x31_LMD_VCT_ST_FAIL:
1395+
fallthrough;
1396+
default:
1397+
return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
1398+
}
1399+
}
1400+
1401+
static bool ksz9x31_cable_test_failed(u16 status)
1402+
{
1403+
int stat = FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status);
1404+
1405+
return stat == KSZ9x31_LMD_VCT_ST_FAIL;
1406+
}
1407+
1408+
static bool ksz9x31_cable_test_fault_length_valid(u16 status)
1409+
{
1410+
switch (FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status)) {
1411+
case KSZ9x31_LMD_VCT_ST_OPEN:
1412+
fallthrough;
1413+
case KSZ9x31_LMD_VCT_ST_SHORT:
1414+
return true;
1415+
}
1416+
return false;
1417+
}
1418+
1419+
static int ksz9x31_cable_test_fault_length(struct phy_device *phydev, u16 stat)
1420+
{
1421+
int dt = FIELD_GET(KSZ9x31_LMD_VCT_DATA_MASK, stat);
1422+
1423+
/* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
1424+
*
1425+
* distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity
1426+
*/
1427+
if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_KSZ9131)
1428+
dt = clamp(dt - 22, 0, 255);
1429+
1430+
return (dt * 400) / 10;
1431+
}
1432+
1433+
static int ksz9x31_cable_test_wait_for_completion(struct phy_device *phydev)
1434+
{
1435+
int val, ret;
1436+
1437+
ret = phy_read_poll_timeout(phydev, KSZ9x31_LMD, val,
1438+
!(val & KSZ9x31_LMD_VCT_EN),
1439+
30000, 100000, true);
1440+
1441+
return ret < 0 ? ret : 0;
1442+
}
1443+
1444+
static int ksz9x31_cable_test_get_pair(int pair)
1445+
{
1446+
static const int ethtool_pair[] = {
1447+
ETHTOOL_A_CABLE_PAIR_A,
1448+
ETHTOOL_A_CABLE_PAIR_B,
1449+
ETHTOOL_A_CABLE_PAIR_C,
1450+
ETHTOOL_A_CABLE_PAIR_D,
1451+
};
1452+
1453+
return ethtool_pair[pair];
1454+
}
1455+
1456+
static int ksz9x31_cable_test_one_pair(struct phy_device *phydev, int pair)
1457+
{
1458+
int ret, val;
1459+
1460+
/* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
1461+
* To test each individual cable pair, set the cable pair in the Cable
1462+
* Diagnostics Test Pair (VCT_PAIR[1:0]) field of the LinkMD Cable
1463+
* Diagnostic Register, along with setting the Cable Diagnostics Test
1464+
* Enable (VCT_EN) bit. The Cable Diagnostics Test Enable (VCT_EN) bit
1465+
* will self clear when the test is concluded.
1466+
*/
1467+
ret = phy_write(phydev, KSZ9x31_LMD,
1468+
KSZ9x31_LMD_VCT_EN | KSZ9x31_LMD_VCT_PAIR(pair));
1469+
if (ret)
1470+
return ret;
1471+
1472+
ret = ksz9x31_cable_test_wait_for_completion(phydev);
1473+
if (ret)
1474+
return ret;
1475+
1476+
val = phy_read(phydev, KSZ9x31_LMD);
1477+
if (val < 0)
1478+
return val;
1479+
1480+
if (ksz9x31_cable_test_failed(val))
1481+
return -EAGAIN;
1482+
1483+
ret = ethnl_cable_test_result(phydev,
1484+
ksz9x31_cable_test_get_pair(pair),
1485+
ksz9x31_cable_test_result_trans(val));
1486+
if (ret)
1487+
return ret;
1488+
1489+
if (!ksz9x31_cable_test_fault_length_valid(val))
1490+
return 0;
1491+
1492+
return ethnl_cable_test_fault_length(phydev,
1493+
ksz9x31_cable_test_get_pair(pair),
1494+
ksz9x31_cable_test_fault_length(phydev, val));
1495+
}
1496+
1497+
static int ksz9x31_cable_test_get_status(struct phy_device *phydev,
1498+
bool *finished)
1499+
{
1500+
struct kszphy_priv *priv = phydev->priv;
1501+
unsigned long pair_mask = 0xf;
1502+
int retries = 20;
1503+
int pair, ret, rv;
1504+
1505+
*finished = false;
1506+
1507+
/* Try harder if link partner is active */
1508+
while (pair_mask && retries--) {
1509+
for_each_set_bit(pair, &pair_mask, 4) {
1510+
ret = ksz9x31_cable_test_one_pair(phydev, pair);
1511+
if (ret == -EAGAIN)
1512+
continue;
1513+
if (ret < 0)
1514+
return ret;
1515+
clear_bit(pair, &pair_mask);
1516+
}
1517+
/* If link partner is in autonegotiation mode it will send 2ms
1518+
* of FLPs with at least 6ms of silence.
1519+
* Add 2ms sleep to have better chances to hit this silence.
1520+
*/
1521+
if (pair_mask)
1522+
usleep_range(2000, 3000);
1523+
}
1524+
1525+
/* Report remaining unfinished pair result as unknown. */
1526+
for_each_set_bit(pair, &pair_mask, 4) {
1527+
ret = ethnl_cable_test_result(phydev,
1528+
ksz9x31_cable_test_get_pair(pair),
1529+
ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC);
1530+
}
1531+
1532+
*finished = true;
1533+
1534+
/* Restore cached bits from before LinkMD got started. */
1535+
rv = phy_modify(phydev, MII_CTRL1000,
1536+
CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER,
1537+
priv->vct_ctrl1000);
1538+
if (rv)
1539+
return rv;
1540+
1541+
return ret;
1542+
}
1543+
13291544
static int ksz8873mll_config_aneg(struct phy_device *phydev)
13301545
{
13311546
return 0;
@@ -2806,6 +3021,7 @@ static struct phy_driver ksphy_driver[] = {
28063021
.phy_id = PHY_ID_KSZ9031,
28073022
.phy_id_mask = MICREL_PHY_ID_MASK,
28083023
.name = "Micrel KSZ9031 Gigabit PHY",
3024+
.flags = PHY_POLL_CABLE_TEST,
28093025
.driver_data = &ksz9021_type,
28103026
.probe = kszphy_probe,
28113027
.get_features = ksz9031_get_features,
@@ -2819,6 +3035,8 @@ static struct phy_driver ksphy_driver[] = {
28193035
.get_stats = kszphy_get_stats,
28203036
.suspend = kszphy_suspend,
28213037
.resume = kszphy_resume,
3038+
.cable_test_start = ksz9x31_cable_test_start,
3039+
.cable_test_get_status = ksz9x31_cable_test_get_status,
28223040
}, {
28233041
.phy_id = PHY_ID_LAN8814,
28243042
.phy_id_mask = MICREL_PHY_ID_MASK,
@@ -2853,6 +3071,7 @@ static struct phy_driver ksphy_driver[] = {
28533071
.phy_id_mask = MICREL_PHY_ID_MASK,
28543072
.name = "Microchip KSZ9131 Gigabit PHY",
28553073
/* PHY_GBIT_FEATURES */
3074+
.flags = PHY_POLL_CABLE_TEST,
28563075
.driver_data = &ksz9021_type,
28573076
.probe = kszphy_probe,
28583077
.config_init = ksz9131_config_init,
@@ -2863,6 +3082,8 @@ static struct phy_driver ksphy_driver[] = {
28633082
.get_stats = kszphy_get_stats,
28643083
.suspend = kszphy_suspend,
28653084
.resume = kszphy_resume,
3085+
.cable_test_start = ksz9x31_cable_test_start,
3086+
.cable_test_get_status = ksz9x31_cable_test_get_status,
28663087
}, {
28673088
.phy_id = PHY_ID_KSZ8873MLL,
28683089
.phy_id_mask = MICREL_PHY_ID_MASK,

0 commit comments

Comments
 (0)