mirror of https://gitee.com/openkylin/linux.git
net: phy: marvell: Add cable test support
The Marvell PHYs have a couple of different register sets for performing cable tests. Page 7 provides the simplest to use. v3: s/mavell/marvell/g Remove include of <uapi/linux/ethtool_netlink.h> Signed-off-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
1e2dc14509
commit
fc879f723c
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -2394,6 +2587,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_88E1545,
|
||||
|
@ -2401,6 +2596,7 @@ static struct phy_driver marvell_drivers[] = {
|
|||
.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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue