Commit 560d9a39 authored by Dimitri Fedrau's avatar Dimitri Fedrau Committed by Jakub Kicinski
Browse files

net: phy: marvell-88q2xxx: add cable test support



Add cable test support for Marvell 88Q222x devices. Reported distance
granularity is 1m.

1m cable, open:
  Cable test started for device eth0.
  Cable test completed for device eth0.
  Pair A code Open Circuit
  Pair A, fault length: 1.00m

1m cable, shorted:
  Cable test started for device eth0.
  Cable test completed for device eth0.
  Pair A code Short within Pair
  Pair A, fault length: 1.00m

6m cable, open:
  Cable test started for device eth0.
  Cable test completed for device eth0.
  Pair A code Open Circuit
  Pair A, fault length: 6.00m

6m cable, shorted:
  Cable test started for device eth0.
  Cable test completed for device eth0.
  Pair A code Short within Pair
  Pair A, fault length: 6.00m

Signed-off-by: default avatarDimitri Fedrau <dima.fedrau@gmail.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20240218075753.18067-10-dima.fedrau@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent a557a92e
Loading
Loading
Loading
Loading
+107 −0
Original line number Diff line number Diff line
@@ -72,6 +72,27 @@

#define MDIO_MMD_PCS_MV_RX_STAT			33328

#define MDIO_MMD_PCS_MV_TDR_RESET			65226
#define MDIO_MMD_PCS_MV_TDR_RESET_TDR_RST		0x1000

#define MDIO_MMD_PCS_MV_TDR_OFF_SHORT_CABLE		65241

#define MDIO_MMD_PCS_MV_TDR_OFF_LONG_CABLE		65242

#define MDIO_MMD_PCS_MV_TDR_STATUS			65245
#define MDIO_MMD_PCS_MV_TDR_STATUS_MASK			0x0003
#define MDIO_MMD_PCS_MV_TDR_STATUS_OFF			0x0001
#define MDIO_MMD_PCS_MV_TDR_STATUS_ON			0x0002
#define MDIO_MMD_PCS_MV_TDR_STATUS_DIST_MASK		0xff00
#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_MASK	0x00f0
#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_SHORT	0x0030
#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OPEN	0x00e0
#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OK		0x0070
#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_IN_PROGR	0x0080
#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_NOISE	0x0050

#define MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF			65246

struct mmd_val {
	int devad;
	u32 regnum;
@@ -715,6 +736,89 @@ static int mv88q222x_revb0_config_init(struct phy_device *phydev)
	return 0;
}

static int mv88q222x_cable_test_start(struct phy_device *phydev)
{
	int ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
			    MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF, 0x0058);
	if (ret < 0)
		return ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
			    MDIO_MMD_PCS_MV_TDR_OFF_LONG_CABLE, 0x00eb);
	if (ret < 0)
		return ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
			    MDIO_MMD_PCS_MV_TDR_OFF_SHORT_CABLE, 0x010e);
	if (ret < 0)
		return ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_RESET,
			    0x0d90);
	if (ret < 0)
		return ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_STATUS,
			    MDIO_MMD_PCS_MV_TDR_STATUS_ON);
	if (ret < 0)
		return ret;

	/* According to the Marvell API the test is finished within 500 ms */
	msleep(500);

	return 0;
}

static int mv88q222x_cable_test_get_status(struct phy_device *phydev,
					   bool *finished)
{
	int ret, status;
	u32 dist;

	status = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_STATUS);
	if (status < 0)
		return status;

	ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_RESET,
			    MDIO_MMD_PCS_MV_TDR_RESET_TDR_RST | 0xd90);
	if (ret < 0)
		return ret;

	/* Test could not be finished */
	if (FIELD_GET(MDIO_MMD_PCS_MV_TDR_STATUS_MASK, status) !=
	    MDIO_MMD_PCS_MV_TDR_STATUS_OFF)
		return -ETIMEDOUT;

	*finished = true;
	/* Fault length reported in meters, convert to centimeters */
	dist = FIELD_GET(MDIO_MMD_PCS_MV_TDR_STATUS_DIST_MASK, status) * 100;
	switch (status & MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_MASK) {
	case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OPEN:
		ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
					ETHTOOL_A_CABLE_RESULT_CODE_OPEN);
		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
					      dist);
		break;
	case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_SHORT:
		ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
					ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT);
		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
					      dist);
		break;
	case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OK:
		ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
					ETHTOOL_A_CABLE_RESULT_CODE_OK);
		break;
	default:
		ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
					ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC);
	}

	return 0;
}

static struct phy_driver mv88q2xxx_driver[] = {
	{
		.phy_id			= MARVELL_PHY_ID_88Q2110,
@@ -732,6 +836,7 @@ static struct phy_driver mv88q2xxx_driver[] = {
	{
		PHY_ID_MATCH_EXACT(PHY_ID_88Q2220_REVB0),
		.name			= "mv88q2220",
		.flags			= PHY_POLL_CABLE_TEST,
		.probe			= mv88q2xxx_probe,
		.get_features		= mv88q2xxx_get_features,
		.config_aneg		= mv88q222x_config_aneg,
@@ -742,6 +847,8 @@ static struct phy_driver mv88q2xxx_driver[] = {
		.config_intr		= mv88q2xxx_config_intr,
		.handle_interrupt	= mv88q2xxx_handle_interrupt,
		.set_loopback		= genphy_c45_loopback,
		.cable_test_start	= mv88q222x_cable_test_start,
		.cable_test_get_status	= mv88q222x_cable_test_get_status,
		.get_sqi		= mv88q2xxx_get_sqi,
		.get_sqi_max		= mv88q2xxx_get_sqi_max,
		.suspend		= mv88q2xxx_suspend,