Skip to content

Commit

Permalink
Merge branch 'Ethernet-Cable-test-support'
Browse files Browse the repository at this point in the history
Andrew Lunn says:

====================
Ethernet Cable test support

any copper Ethernet PHY have support for performing diagnostics of
the cable. Are the cable shorted, broken, not plugged into anything at
the other end? And they can report roughly how far along the cable any
fault is.

Add infrastructure in ethtool and phylib support for triggering a
cable test and reporting the results. The Marvell 1G PHY driver is
then extended to make use of this infrastructure.

For testing, a modified ethtool(1) can be found here:
https://github.com/lunn/ethtool.git feature/cable-test-v4. This also
contains extra code for TDR dump, which will be added to the kernel in
a later patch series.

Thanks to Chris Healy for extensive testing.

v2:
See individual patches but:

Remove _REPLY messages
Change length into a u32
Grammar fixes
Rename functions for consistency
Extack for cable test already running
Remove ethnl_cable_test_act_ops
Add status attributes
Rename pairs from numbers to letters

v3:

See individual patches but:
Remove ETHTOOL_MSG_CABLE_TEST_ACT_REPLY from documentation
Remove unused cable_test_get_policy
Fixed example in document
Add ETHTOOL_A_CABLE_NEST_* enum
Add ETHTOOL_MSG_CABLE_TEST_NTF to documentation
Poison phydev->skb
Return -EMSGSIZE when ethnl_bcastmsg_put() fails
Return valid error code when nla_nest_start() fails
Use u8 for results
Actually put u32 length into message
s/mavell/marvell/g
Remove include of <uapi/linux/ethtool_netlink.h>
EMSGSIZE when ethnl_bcastmsg_put() fails
Print an error message on failure, since this is a void function.

v4:
See individual patches but:
Remove unwanted blank line
ENOTSUPP->EOPNOTSUPP
Move EINVAL->EMSGSIZE fix to correct patch
====================

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed May 10, 2020
2 parents b9f9642 + 9896a45 commit bed37f0
Show file tree
Hide file tree
Showing 11 changed files with 722 additions and 4 deletions.
57 changes: 56 additions & 1 deletion Documentation/networking/ethtool-netlink.rst
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ Userspace to kernel:
``ETHTOOL_MSG_EEE_GET`` get EEE settings
``ETHTOOL_MSG_EEE_SET`` set EEE settings
``ETHTOOL_MSG_TSINFO_GET`` get timestamping info
``ETHTOOL_MSG_CABLE_TEST_ACT`` action start cable test
===================================== ================================

Kernel to userspace:
Expand Down Expand Up @@ -235,6 +236,7 @@ Kernel to userspace:
``ETHTOOL_MSG_EEE_GET_REPLY`` EEE settings
``ETHTOOL_MSG_EEE_NTF`` EEE settings
``ETHTOOL_MSG_TSINFO_GET_REPLY`` timestamping info
``ETHTOOL_MSG_CABLE_TEST_NTF`` Cable test results
===================================== =================================

``GET`` requests are sent by userspace applications to retrieve device
Expand Down Expand Up @@ -958,13 +960,65 @@ Kernel response contents:
is no special value for this case). The bitset attributes are omitted if they
would be empty (no bit set).

CABLE_TEST
==========

Start a cable test.

Request contents:

==================================== ====== ==========================
``ETHTOOL_A_CABLE_TEST_HEADER`` nested request header
==================================== ====== ==========================

Notification contents:

An Ethernet cable typically contains 1, 2 or 4 pairs. The length of
the pair can only be measured when there is a fault in the pair and
hence a reflection. Information about the fault may not be available,
depending on the specific hardware. Hence the contents of the notify
message are mostly optional. The attributes can be repeated an
arbitrary number of times, in an arbitrary order, for an arbitrary
number of pairs.

The example shows the notification sent when the test is completed for
a T2 cable, i.e. two pairs. One pair is OK and hence has no length
information. The second pair has a fault and does have length
information.

+---------------------------------------------+--------+---------------------+
| ``ETHTOOL_A_CABLE_TEST_HEADER`` | nested | reply header |
+---------------------------------------------+--------+---------------------+
| ``ETHTOOL_A_CABLE_TEST_STATUS`` | u8 | completed |
+---------------------------------------------+--------+---------------------+
| ``ETHTOOL_A_CABLE_TEST_NTF_NEST`` | nested | all the results |
+-+-------------------------------------------+--------+---------------------+
| | ``ETHTOOL_A_CABLE_NEST_RESULT`` | nested | cable test result |
+-+-+-----------------------------------------+--------+---------------------+
| | | ``ETHTOOL_A_CABLE_RESULTS_PAIR`` | u8 | pair number |
+-+-+-----------------------------------------+--------+---------------------+
| | | ``ETHTOOL_A_CABLE_RESULTS_CODE`` | u8 | result code |
+-+-+-----------------------------------------+--------+---------------------+
| | ``ETHTOOL_A_CABLE_NEST_RESULT`` | nested | cable test results |
+-+-+-----------------------------------------+--------+---------------------+
| | | ``ETHTOOL_A_CABLE_RESULTS_PAIR`` | u8 | pair number |
+-+-+-----------------------------------------+--------+---------------------+
| | | ``ETHTOOL_A_CABLE_RESULTS_CODE`` | u8 | result code |
+-+-+-----------------------------------------+--------+---------------------+
| | ``ETHTOOL_A_CABLE_NEST_FAULT_LENGTH`` | nested | cable length |
+-+-+-----------------------------------------+--------+---------------------+
| | | ``ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR`` | u8 | pair number |
+-+-+-----------------------------------------+--------+---------------------+
| | | ``ETHTOOL_A_CABLE_FAULT_LENGTH_CM`` | u32 | length in cm |
+-+-+-----------------------------------------+--------+---------------------+

Request translation
===================

The following table maps ioctl commands to netlink commands providing their
functionality. Entries with "n/a" in right column are commands which do not
have their netlink replacement yet.
have their netlink replacement yet. Entries which "n/a" in the left column
are netlink only.

=================================== =====================================
ioctl command netlink command
Expand Down Expand Up @@ -1053,4 +1107,5 @@ have their netlink replacement yet.
``ETHTOOL_PHY_STUNABLE`` n/a
``ETHTOOL_GFECPARAM`` n/a
``ETHTOOL_SFECPARAM`` n/a
n/a ''ETHTOOL_MSG_CABLE_TEST_ACT''
=================================== =====================================
201 changes: 201 additions & 0 deletions drivers/net/phy/marvell.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/phy.h>
#include <linux/marvell_phy.h>
#include <linux/bitfield.h>
Expand All @@ -42,6 +43,7 @@
#define MII_MARVELL_MSCR_PAGE 0x02
#define MII_MARVELL_LED_PAGE 0x03
#define MII_MARVELL_MISC_TEST_PAGE 0x06
#define MII_MARVELL_VCT7_PAGE 0x07
#define MII_MARVELL_WOL_PAGE 0x11

#define MII_M1011_IEVENT 0x13
Expand Down Expand Up @@ -162,6 +164,36 @@
#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */
#define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */

#define MII_VCT7_PAIR_0_DISTANCE 0x10
#define MII_VCT7_PAIR_1_DISTANCE 0x11
#define MII_VCT7_PAIR_2_DISTANCE 0x12
#define MII_VCT7_PAIR_3_DISTANCE 0x13

#define MII_VCT7_RESULTS 0x14
#define MII_VCT7_RESULTS_PAIR3_MASK 0xf000
#define MII_VCT7_RESULTS_PAIR2_MASK 0x0f00
#define MII_VCT7_RESULTS_PAIR1_MASK 0x00f0
#define MII_VCT7_RESULTS_PAIR0_MASK 0x000f
#define MII_VCT7_RESULTS_PAIR3_SHIFT 12
#define MII_VCT7_RESULTS_PAIR2_SHIFT 8
#define MII_VCT7_RESULTS_PAIR1_SHIFT 4
#define MII_VCT7_RESULTS_PAIR0_SHIFT 0
#define MII_VCT7_RESULTS_INVALID 0
#define MII_VCT7_RESULTS_OK 1
#define MII_VCT7_RESULTS_OPEN 2
#define MII_VCT7_RESULTS_SAME_SHORT 3
#define MII_VCT7_RESULTS_CROSS_SHORT 4
#define MII_VCT7_RESULTS_BUSY 9

#define MII_VCT7_CTRL 0x15
#define MII_VCT7_CTRL_RUN_NOW BIT(15)
#define MII_VCT7_CTRL_RUN_ANEG BIT(14)
#define MII_VCT7_CTRL_DISABLE_CROSS BIT(13)
#define MII_VCT7_CTRL_RUN_AFTER_BREAK_LINK BIT(12)
#define MII_VCT7_CTRL_IN_PROGRESS BIT(11)
#define MII_VCT7_CTRL_METERS BIT(10)
#define MII_VCT7_CTRL_CENTIMETERS 0

#define LPA_PAUSE_FIBER 0x180
#define LPA_PAUSE_ASYM_FIBER 0x100

Expand Down Expand Up @@ -1658,6 +1690,163 @@ static void marvell_get_stats(struct phy_device *phydev,
data[i] = marvell_get_stat(phydev, i);
}

static int marvell_vct7_cable_test_start(struct phy_device *phydev)
{
int bmcr, bmsr, ret;

/* If auto-negotiation is enabled, but not complete, the cable
* test never completes. So disable auto-neg.
*/
bmcr = phy_read(phydev, MII_BMCR);
if (bmcr < 0)
return bmcr;

bmsr = phy_read(phydev, MII_BMSR);

if (bmsr < 0)
return bmsr;

if (bmcr & BMCR_ANENABLE) {
ret = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
if (ret < 0)
return ret;
ret = genphy_soft_reset(phydev);
if (ret < 0)
return ret;
}

/* If the link is up, allow it some time to go down */
if (bmsr & BMSR_LSTATUS)
msleep(1500);

return phy_write_paged(phydev, MII_MARVELL_VCT7_PAGE,
MII_VCT7_CTRL,
MII_VCT7_CTRL_RUN_NOW |
MII_VCT7_CTRL_CENTIMETERS);
}

static int marvell_vct7_distance_to_length(int distance, bool meter)
{
if (meter)
distance *= 100;

return distance;
}

static bool marvell_vct7_distance_valid(int result)
{
switch (result) {
case MII_VCT7_RESULTS_OPEN:
case MII_VCT7_RESULTS_SAME_SHORT:
case MII_VCT7_RESULTS_CROSS_SHORT:
return true;
}
return false;
}

static int marvell_vct7_report_length(struct phy_device *phydev,
int pair, bool meter)
{
int length;
int ret;

ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
MII_VCT7_PAIR_0_DISTANCE + pair);
if (ret < 0)
return ret;

length = marvell_vct7_distance_to_length(ret, meter);

ethnl_cable_test_fault_length(phydev, pair, length);

return 0;
}

static int marvell_vct7_cable_test_report_trans(int result)
{
switch (result) {
case MII_VCT7_RESULTS_OK:
return ETHTOOL_A_CABLE_RESULT_CODE_OK;
case MII_VCT7_RESULTS_OPEN:
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
case MII_VCT7_RESULTS_SAME_SHORT:
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
case MII_VCT7_RESULTS_CROSS_SHORT:
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
default:
return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
}
}

static int marvell_vct7_cable_test_report(struct phy_device *phydev)
{
int pair0, pair1, pair2, pair3;
bool meter;
int ret;

ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
MII_VCT7_RESULTS);
if (ret < 0)
return ret;

pair3 = (ret & MII_VCT7_RESULTS_PAIR3_MASK) >>
MII_VCT7_RESULTS_PAIR3_SHIFT;
pair2 = (ret & MII_VCT7_RESULTS_PAIR2_MASK) >>
MII_VCT7_RESULTS_PAIR2_SHIFT;
pair1 = (ret & MII_VCT7_RESULTS_PAIR1_MASK) >>
MII_VCT7_RESULTS_PAIR1_SHIFT;
pair0 = (ret & MII_VCT7_RESULTS_PAIR0_MASK) >>
MII_VCT7_RESULTS_PAIR0_SHIFT;

ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
marvell_vct7_cable_test_report_trans(pair0));
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
marvell_vct7_cable_test_report_trans(pair1));
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
marvell_vct7_cable_test_report_trans(pair2));
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
marvell_vct7_cable_test_report_trans(pair3));

ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE, MII_VCT7_CTRL);
if (ret < 0)
return ret;

meter = ret & MII_VCT7_CTRL_METERS;

if (marvell_vct7_distance_valid(pair0))
marvell_vct7_report_length(phydev, 0, meter);
if (marvell_vct7_distance_valid(pair1))
marvell_vct7_report_length(phydev, 1, meter);
if (marvell_vct7_distance_valid(pair2))
marvell_vct7_report_length(phydev, 2, meter);
if (marvell_vct7_distance_valid(pair3))
marvell_vct7_report_length(phydev, 3, meter);

return 0;
}

static int marvell_vct7_cable_test_get_status(struct phy_device *phydev,
bool *finished)
{
int ret;

*finished = false;

ret = phy_read_paged(phydev, MII_MARVELL_VCT7_PAGE,
MII_VCT7_CTRL);

if (ret < 0)
return ret;

if (!(ret & MII_VCT7_CTRL_IN_PROGRESS)) {
*finished = true;

return marvell_vct7_cable_test_report(phydev);
}

return 0;
}

#ifdef CONFIG_HWMON
static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
{
Expand Down Expand Up @@ -2353,6 +2542,7 @@ static struct phy_driver marvell_drivers[] = {
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1510",
.features = PHY_GBIT_FIBRE_FEATURES,
.flags = PHY_POLL_CABLE_TEST,
.probe = &m88e1510_probe,
.config_init = &m88e1510_config_init,
.config_aneg = &m88e1510_config_aneg,
Expand All @@ -2372,12 +2562,15 @@ static struct phy_driver marvell_drivers[] = {
.set_loopback = genphy_loopback,
.get_tunable = m88e1011_get_tunable,
.set_tunable = m88e1011_set_tunable,
.cable_test_start = marvell_vct7_cable_test_start,
.cable_test_get_status = marvell_vct7_cable_test_get_status,
},
{
.phy_id = MARVELL_PHY_ID_88E1540,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1540",
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
.probe = m88e1510_probe,
.config_init = &marvell_config_init,
.config_aneg = &m88e1510_config_aneg,
Expand All @@ -2394,13 +2587,16 @@ static struct phy_driver marvell_drivers[] = {
.get_stats = marvell_get_stats,
.get_tunable = m88e1540_get_tunable,
.set_tunable = m88e1540_set_tunable,
.cable_test_start = marvell_vct7_cable_test_start,
.cable_test_get_status = marvell_vct7_cable_test_get_status,
},
{
.phy_id = MARVELL_PHY_ID_88E1545,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1545",
.probe = m88e1510_probe,
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
.config_init = &marvell_config_init,
.config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status,
Expand All @@ -2416,6 +2612,8 @@ static struct phy_driver marvell_drivers[] = {
.get_stats = marvell_get_stats,
.get_tunable = m88e1540_get_tunable,
.set_tunable = m88e1540_set_tunable,
.cable_test_start = marvell_vct7_cable_test_start,
.cable_test_get_status = marvell_vct7_cable_test_get_status,
},
{
.phy_id = MARVELL_PHY_ID_88E3016,
Expand All @@ -2442,6 +2640,7 @@ static struct phy_driver marvell_drivers[] = {
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E6390",
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
.probe = m88e6390_probe,
.config_init = &marvell_config_init,
.config_aneg = &m88e6390_config_aneg,
Expand All @@ -2458,6 +2657,8 @@ static struct phy_driver marvell_drivers[] = {
.get_stats = marvell_get_stats,
.get_tunable = m88e1540_get_tunable,
.set_tunable = m88e1540_set_tunable,
.cable_test_start = marvell_vct7_cable_test_start,
.cable_test_get_status = marvell_vct7_cable_test_get_status,
},
};

Expand Down
Loading

0 comments on commit bed37f0

Please sign in to comment.