Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: micrel: ksz886x/ksz8081: add cabletest support

This patch support for cable test for the ksz886x switches and the
ksz8081 PHY.

The patch was tested on a KSZ8873RLL switch with following results:

- port 1:
- provides invalid values, thus return -ENOTSUPP
(Errata: DS80000830A: "LinkMD does not work on Port 1",
http://ww1.microchip.com/downloads/en/DeviceDoc/KSZ8873-Errata-DS80000830A.pdf)

- port 2:
- can detect distance
- can detect open on each wire of pair A (wire 1 and 2)
- can detect open only on one wire of pair B (only wire 3)
- can detect short between wires of a pair (wires 1 + 2 or 3 + 6)
- short between pairs is detected as open.
For example short between wires 2 + 3 is detected as open.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Oleksij Rempel and committed by
David S. Miller
49011e0c c916e8e1

+194
+13
drivers/net/dsa/microchip/ksz8795.c
··· 970 970 DSA_TAG_PROTO_KSZ9893 : DSA_TAG_PROTO_KSZ8795; 971 971 } 972 972 973 + static u32 ksz8_sw_get_phy_flags(struct dsa_switch *ds, int port) 974 + { 975 + /* Silicon Errata Sheet (DS80000830A): 976 + * Port 1 does not work with LinkMD Cable-Testing. 977 + * Port 1 does not respond to received PAUSE control frames. 978 + */ 979 + if (!port) 980 + return MICREL_KSZ8_P1_ERRATA; 981 + 982 + return 0; 983 + } 984 + 973 985 static void ksz8_get_strings(struct dsa_switch *ds, int port, 974 986 u32 stringset, uint8_t *buf) 975 987 { ··· 1515 1503 1516 1504 static const struct dsa_switch_ops ksz8_switch_ops = { 1517 1505 .get_tag_protocol = ksz8_get_tag_protocol, 1506 + .get_phy_flags = ksz8_sw_get_phy_flags, 1518 1507 .setup = ksz8_setup, 1519 1508 .phy_read = ksz_phy_read16, 1520 1509 .phy_write = ksz_phy_write16,
+180
drivers/net/phy/micrel.c
··· 20 20 */ 21 21 22 22 #include <linux/bitfield.h> 23 + #include <linux/ethtool_netlink.h> 23 24 #include <linux/kernel.h> 24 25 #include <linux/module.h> 25 26 #include <linux/phy.h> ··· 53 52 #define KSZPHY_INTCS_LINK_UP_STATUS BIT(0) 54 53 #define KSZPHY_INTCS_STATUS (KSZPHY_INTCS_LINK_DOWN_STATUS |\ 55 54 KSZPHY_INTCS_LINK_UP_STATUS) 55 + 56 + /* LinkMD Control/Status */ 57 + #define KSZ8081_LMD 0x1d 58 + #define KSZ8081_LMD_ENABLE_TEST BIT(15) 59 + #define KSZ8081_LMD_STAT_NORMAL 0 60 + #define KSZ8081_LMD_STAT_OPEN 1 61 + #define KSZ8081_LMD_STAT_SHORT 2 62 + #define KSZ8081_LMD_STAT_FAIL 3 63 + #define KSZ8081_LMD_STAT_MASK GENMASK(14, 13) 64 + /* Short cable (<10 meter) has been detected by LinkMD */ 65 + #define KSZ8081_LMD_SHORT_INDICATOR BIT(12) 66 + #define KSZ8081_LMD_DELTA_TIME_MASK GENMASK(8, 0) 56 67 57 68 /* PHY Control 1 */ 58 69 #define MII_KSZPHY_CTRL_1 0x1e ··· 1376 1363 return 0; 1377 1364 } 1378 1365 1366 + static int ksz886x_cable_test_start(struct phy_device *phydev) 1367 + { 1368 + if (phydev->dev_flags & MICREL_KSZ8_P1_ERRATA) 1369 + return -EOPNOTSUPP; 1370 + 1371 + /* If autoneg is enabled, we won't be able to test cross pair 1372 + * short. In this case, the PHY will "detect" a link and 1373 + * confuse the internal state machine - disable auto neg here. 1374 + * If autoneg is disabled, we should set the speed to 10mbit. 1375 + */ 1376 + return phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_SPEED100); 1377 + } 1378 + 1379 + static int ksz886x_cable_test_result_trans(u16 status) 1380 + { 1381 + switch (FIELD_GET(KSZ8081_LMD_STAT_MASK, status)) { 1382 + case KSZ8081_LMD_STAT_NORMAL: 1383 + return ETHTOOL_A_CABLE_RESULT_CODE_OK; 1384 + case KSZ8081_LMD_STAT_SHORT: 1385 + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 1386 + case KSZ8081_LMD_STAT_OPEN: 1387 + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 1388 + case KSZ8081_LMD_STAT_FAIL: 1389 + fallthrough; 1390 + default: 1391 + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 1392 + } 1393 + } 1394 + 1395 + static bool ksz886x_cable_test_failed(u16 status) 1396 + { 1397 + return FIELD_GET(KSZ8081_LMD_STAT_MASK, status) == 1398 + KSZ8081_LMD_STAT_FAIL; 1399 + } 1400 + 1401 + static bool ksz886x_cable_test_fault_length_valid(u16 status) 1402 + { 1403 + switch (FIELD_GET(KSZ8081_LMD_STAT_MASK, status)) { 1404 + case KSZ8081_LMD_STAT_OPEN: 1405 + fallthrough; 1406 + case KSZ8081_LMD_STAT_SHORT: 1407 + return true; 1408 + } 1409 + return false; 1410 + } 1411 + 1412 + static int ksz886x_cable_test_fault_length(u16 status) 1413 + { 1414 + int dt; 1415 + 1416 + /* According to the data sheet the distance to the fault is 1417 + * DELTA_TIME * 0.4 meters. 1418 + */ 1419 + dt = FIELD_GET(KSZ8081_LMD_DELTA_TIME_MASK, status); 1420 + 1421 + return (dt * 400) / 10; 1422 + } 1423 + 1424 + static int ksz886x_cable_test_wait_for_completion(struct phy_device *phydev) 1425 + { 1426 + int val, ret; 1427 + 1428 + ret = phy_read_poll_timeout(phydev, KSZ8081_LMD, val, 1429 + !(val & KSZ8081_LMD_ENABLE_TEST), 1430 + 30000, 100000, true); 1431 + 1432 + return ret < 0 ? ret : 0; 1433 + } 1434 + 1435 + static int ksz886x_cable_test_one_pair(struct phy_device *phydev, int pair) 1436 + { 1437 + static const int ethtool_pair[] = { 1438 + ETHTOOL_A_CABLE_PAIR_A, 1439 + ETHTOOL_A_CABLE_PAIR_B, 1440 + }; 1441 + int ret, val, mdix; 1442 + 1443 + /* There is no way to choice the pair, like we do one ksz9031. 1444 + * We can workaround this limitation by using the MDI-X functionality. 1445 + */ 1446 + if (pair == 0) 1447 + mdix = ETH_TP_MDI; 1448 + else 1449 + mdix = ETH_TP_MDI_X; 1450 + 1451 + switch (phydev->phy_id & MICREL_PHY_ID_MASK) { 1452 + case PHY_ID_KSZ8081: 1453 + ret = ksz8081_config_mdix(phydev, mdix); 1454 + break; 1455 + case PHY_ID_KSZ886X: 1456 + ret = ksz886x_config_mdix(phydev, mdix); 1457 + break; 1458 + default: 1459 + ret = -ENODEV; 1460 + } 1461 + 1462 + if (ret) 1463 + return ret; 1464 + 1465 + /* Now we are ready to fire. This command will send a 100ns pulse 1466 + * to the pair. 1467 + */ 1468 + ret = phy_write(phydev, KSZ8081_LMD, KSZ8081_LMD_ENABLE_TEST); 1469 + if (ret) 1470 + return ret; 1471 + 1472 + ret = ksz886x_cable_test_wait_for_completion(phydev); 1473 + if (ret) 1474 + return ret; 1475 + 1476 + val = phy_read(phydev, KSZ8081_LMD); 1477 + if (val < 0) 1478 + return val; 1479 + 1480 + if (ksz886x_cable_test_failed(val)) 1481 + return -EAGAIN; 1482 + 1483 + ret = ethnl_cable_test_result(phydev, ethtool_pair[pair], 1484 + ksz886x_cable_test_result_trans(val)); 1485 + if (ret) 1486 + return ret; 1487 + 1488 + if (!ksz886x_cable_test_fault_length_valid(val)) 1489 + return 0; 1490 + 1491 + return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], 1492 + ksz886x_cable_test_fault_length(val)); 1493 + } 1494 + 1495 + static int ksz886x_cable_test_get_status(struct phy_device *phydev, 1496 + bool *finished) 1497 + { 1498 + unsigned long pair_mask = 0x3; 1499 + int retries = 20; 1500 + int pair, ret; 1501 + 1502 + *finished = false; 1503 + 1504 + /* Try harder if link partner is active */ 1505 + while (pair_mask && retries--) { 1506 + for_each_set_bit(pair, &pair_mask, 4) { 1507 + ret = ksz886x_cable_test_one_pair(phydev, pair); 1508 + if (ret == -EAGAIN) 1509 + continue; 1510 + if (ret < 0) 1511 + return ret; 1512 + clear_bit(pair, &pair_mask); 1513 + } 1514 + /* If link partner is in autonegotiation mode it will send 2ms 1515 + * of FLPs with at least 6ms of silence. 1516 + * Add 2ms sleep to have better chances to hit this silence. 1517 + */ 1518 + if (pair_mask) 1519 + msleep(2); 1520 + } 1521 + 1522 + *finished = true; 1523 + 1524 + return ret; 1525 + } 1526 + 1379 1527 static struct phy_driver ksphy_driver[] = { 1380 1528 { 1381 1529 .phy_id = PHY_ID_KS8737, ··· 1643 1469 .phy_id = PHY_ID_KSZ8081, 1644 1470 .name = "Micrel KSZ8081 or KSZ8091", 1645 1471 .phy_id_mask = MICREL_PHY_ID_MASK, 1472 + .flags = PHY_POLL_CABLE_TEST, 1646 1473 /* PHY_BASIC_FEATURES */ 1647 1474 .driver_data = &ksz8081_type, 1648 1475 .probe = kszphy_probe, ··· 1658 1483 .get_stats = kszphy_get_stats, 1659 1484 .suspend = kszphy_suspend, 1660 1485 .resume = kszphy_resume, 1486 + .cable_test_start = ksz886x_cable_test_start, 1487 + .cable_test_get_status = ksz886x_cable_test_get_status, 1661 1488 }, { 1662 1489 .phy_id = PHY_ID_KSZ8061, 1663 1490 .name = "Micrel KSZ8061", ··· 1748 1571 .phy_id_mask = MICREL_PHY_ID_MASK, 1749 1572 .name = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch", 1750 1573 /* PHY_BASIC_FEATURES */ 1574 + .flags = PHY_POLL_CABLE_TEST, 1751 1575 .config_init = kszphy_config_init, 1752 1576 .config_aneg = ksz886x_config_aneg, 1753 1577 .read_status = ksz886x_read_status, 1754 1578 .suspend = genphy_suspend, 1755 1579 .resume = genphy_resume, 1580 + .cable_test_start = ksz886x_cable_test_start, 1581 + .cable_test_get_status = ksz886x_cable_test_get_status, 1756 1582 }, { 1757 1583 .name = "Micrel KSZ87XX Switch", 1758 1584 /* PHY_BASIC_FEATURES */
+1
include/linux/micrel_phy.h
··· 39 39 /* struct phy_device dev_flags definitions */ 40 40 #define MICREL_PHY_50MHZ_CLK 0x00000001 41 41 #define MICREL_PHY_FXEN 0x00000002 42 + #define MICREL_KSZ8_P1_ERRATA 0x00000003 42 43 43 44 #define MICREL_KSZ9021_EXTREG_CTRL 0xB 44 45 #define MICREL_KSZ9021_EXTREG_DATA_WRITE 0xC