Skip to content

Commit a9e4230

Browse files
CHKDSK88kuba-moo
authored andcommitted
net: phy: marvell: implement cable-test for 88E308X/88E609X family
This commit implements VCT in 88E308X/88E609X Family. It require two workarounds with some magic configuration. Regular use require only one register configuration. But Open Circuit require second workaround. It cause implementation two phases for fault length measuring. Fast Ethernet PHY have implemented very simple version of VCT. It's complitley different than vct5 or vct7. Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Link: https://lore.kernel.org/r/20240402201123.2961909-3-paweldembicki@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 9cc8a6e commit a9e4230

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

drivers/net/phy/marvell.c

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,23 @@
279279
#define MII_VCT7_CTRL_METERS BIT(10)
280280
#define MII_VCT7_CTRL_CENTIMETERS 0
281281

282+
#define MII_VCT_TXPINS 0x1A
283+
#define MII_VCT_RXPINS 0x1B
284+
#define MII_VCT_TXPINS_ENVCT BIT(15)
285+
#define MII_VCT_TXRXPINS_VCTTST GENMASK(14, 13)
286+
#define MII_VCT_TXRXPINS_VCTTST_SHIFT 13
287+
#define MII_VCT_TXRXPINS_VCTTST_OK 0
288+
#define MII_VCT_TXRXPINS_VCTTST_SHORT 1
289+
#define MII_VCT_TXRXPINS_VCTTST_OPEN 2
290+
#define MII_VCT_TXRXPINS_VCTTST_FAIL 3
291+
#define MII_VCT_TXRXPINS_AMPRFLN GENMASK(12, 8)
292+
#define MII_VCT_TXRXPINS_AMPRFLN_SHIFT 8
293+
#define MII_VCT_TXRXPINS_DISTRFLN GENMASK(7, 0)
294+
#define MII_VCT_TXRXPINS_DISTRFLN_MAX 0xff
295+
296+
#define M88E3082_PAIR_A BIT(0)
297+
#define M88E3082_PAIR_B BIT(1)
298+
282299
#define LPA_PAUSE_FIBER 0x180
283300
#define LPA_PAUSE_ASYM_FIBER 0x100
284301

@@ -301,6 +318,12 @@ static struct marvell_hw_stat marvell_hw_stats[] = {
301318
{ "phy_receive_errors_fiber", 1, 21, 16},
302319
};
303320

321+
enum {
322+
M88E3082_VCT_OFF,
323+
M88E3082_VCT_PHASE1,
324+
M88E3082_VCT_PHASE2,
325+
};
326+
304327
struct marvell_priv {
305328
u64 stats[ARRAY_SIZE(marvell_hw_stats)];
306329
char *hwmon_name;
@@ -310,6 +333,7 @@ struct marvell_priv {
310333
u32 last;
311334
u32 step;
312335
s8 pair;
336+
u8 vct_phase;
313337
};
314338

315339
static int marvell_read_page(struct phy_device *phydev)
@@ -2417,6 +2441,188 @@ static int marvell_vct7_cable_test_get_status(struct phy_device *phydev,
24172441
return 0;
24182442
}
24192443

2444+
static int m88e3082_vct_cable_test_start(struct phy_device *phydev)
2445+
{
2446+
struct marvell_priv *priv = phydev->priv;
2447+
int ret;
2448+
2449+
/* It needs some magic workarounds described in VCT manual for this PHY.
2450+
*/
2451+
ret = phy_write(phydev, 29, 0x0003);
2452+
if (ret < 0)
2453+
return ret;
2454+
2455+
ret = phy_write(phydev, 30, 0x6440);
2456+
if (ret < 0)
2457+
return ret;
2458+
2459+
if (priv->vct_phase == M88E3082_VCT_PHASE1) {
2460+
ret = phy_write(phydev, 29, 0x000a);
2461+
if (ret < 0)
2462+
return ret;
2463+
2464+
ret = phy_write(phydev, 30, 0x0002);
2465+
if (ret < 0)
2466+
return ret;
2467+
}
2468+
2469+
ret = phy_write(phydev, MII_BMCR,
2470+
BMCR_RESET | BMCR_SPEED100 | BMCR_FULLDPLX);
2471+
if (ret < 0)
2472+
return ret;
2473+
2474+
ret = phy_write(phydev, MII_VCT_TXPINS, MII_VCT_TXPINS_ENVCT);
2475+
if (ret < 0)
2476+
return ret;
2477+
2478+
ret = phy_write(phydev, 29, 0x0003);
2479+
if (ret < 0)
2480+
return ret;
2481+
2482+
ret = phy_write(phydev, 30, 0x0);
2483+
if (ret < 0)
2484+
return ret;
2485+
2486+
if (priv->vct_phase == M88E3082_VCT_OFF) {
2487+
priv->vct_phase = M88E3082_VCT_PHASE1;
2488+
priv->pair = 0;
2489+
2490+
return 0;
2491+
}
2492+
2493+
ret = phy_write(phydev, 29, 0x000a);
2494+
if (ret < 0)
2495+
return ret;
2496+
2497+
ret = phy_write(phydev, 30, 0x0);
2498+
if (ret < 0)
2499+
return ret;
2500+
2501+
priv->vct_phase = M88E3082_VCT_PHASE2;
2502+
2503+
return 0;
2504+
}
2505+
2506+
static int m88e3082_vct_cable_test_report_trans(int result, u8 distance)
2507+
{
2508+
switch (result) {
2509+
case MII_VCT_TXRXPINS_VCTTST_OK:
2510+
if (distance == MII_VCT_TXRXPINS_DISTRFLN_MAX)
2511+
return ETHTOOL_A_CABLE_RESULT_CODE_OK;
2512+
return ETHTOOL_A_CABLE_RESULT_CODE_IMPEDANCE_MISMATCH;
2513+
case MII_VCT_TXRXPINS_VCTTST_SHORT:
2514+
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
2515+
case MII_VCT_TXRXPINS_VCTTST_OPEN:
2516+
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
2517+
default:
2518+
return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
2519+
}
2520+
}
2521+
2522+
static u32 m88e3082_vct_distrfln_2_cm(u8 distrfln)
2523+
{
2524+
if (distrfln < 24)
2525+
return 0;
2526+
2527+
/* Original function for meters: y = 0.7861x - 18.862 */
2528+
return (7861 * distrfln - 188620) / 100;
2529+
}
2530+
2531+
static int m88e3082_vct_cable_test_get_status(struct phy_device *phydev,
2532+
bool *finished)
2533+
{
2534+
u8 tx_vcttst_res, rx_vcttst_res, tx_distrfln, rx_distrfln;
2535+
struct marvell_priv *priv = phydev->priv;
2536+
int ret, tx_result, rx_result;
2537+
bool done_phase = true;
2538+
2539+
*finished = false;
2540+
2541+
ret = phy_read(phydev, MII_VCT_TXPINS);
2542+
if (ret < 0)
2543+
return ret;
2544+
else if (ret & MII_VCT_TXPINS_ENVCT)
2545+
return 0;
2546+
2547+
tx_distrfln = ret & MII_VCT_TXRXPINS_DISTRFLN;
2548+
tx_vcttst_res = (ret & MII_VCT_TXRXPINS_VCTTST) >>
2549+
MII_VCT_TXRXPINS_VCTTST_SHIFT;
2550+
2551+
ret = phy_read(phydev, MII_VCT_RXPINS);
2552+
if (ret < 0)
2553+
return ret;
2554+
2555+
rx_distrfln = ret & MII_VCT_TXRXPINS_DISTRFLN;
2556+
rx_vcttst_res = (ret & MII_VCT_TXRXPINS_VCTTST) >>
2557+
MII_VCT_TXRXPINS_VCTTST_SHIFT;
2558+
2559+
*finished = true;
2560+
2561+
switch (priv->vct_phase) {
2562+
case M88E3082_VCT_PHASE1:
2563+
tx_result = m88e3082_vct_cable_test_report_trans(tx_vcttst_res,
2564+
tx_distrfln);
2565+
rx_result = m88e3082_vct_cable_test_report_trans(rx_vcttst_res,
2566+
rx_distrfln);
2567+
2568+
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
2569+
tx_result);
2570+
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
2571+
rx_result);
2572+
2573+
if (tx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN) {
2574+
done_phase = false;
2575+
priv->pair |= M88E3082_PAIR_A;
2576+
} else if (tx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) {
2577+
u8 pair = ETHTOOL_A_CABLE_PAIR_A;
2578+
u32 cm = m88e3082_vct_distrfln_2_cm(tx_distrfln);
2579+
2580+
ethnl_cable_test_fault_length(phydev, pair, cm);
2581+
}
2582+
2583+
if (rx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN) {
2584+
done_phase = false;
2585+
priv->pair |= M88E3082_PAIR_B;
2586+
} else if (rx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) {
2587+
u8 pair = ETHTOOL_A_CABLE_PAIR_B;
2588+
u32 cm = m88e3082_vct_distrfln_2_cm(rx_distrfln);
2589+
2590+
ethnl_cable_test_fault_length(phydev, pair, cm);
2591+
}
2592+
2593+
break;
2594+
case M88E3082_VCT_PHASE2:
2595+
if (priv->pair & M88E3082_PAIR_A &&
2596+
tx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN &&
2597+
tx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) {
2598+
u8 pair = ETHTOOL_A_CABLE_PAIR_A;
2599+
u32 cm = m88e3082_vct_distrfln_2_cm(tx_distrfln);
2600+
2601+
ethnl_cable_test_fault_length(phydev, pair, cm);
2602+
}
2603+
if (priv->pair & M88E3082_PAIR_B &&
2604+
rx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN &&
2605+
rx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) {
2606+
u8 pair = ETHTOOL_A_CABLE_PAIR_B;
2607+
u32 cm = m88e3082_vct_distrfln_2_cm(rx_distrfln);
2608+
2609+
ethnl_cable_test_fault_length(phydev, pair, cm);
2610+
}
2611+
2612+
break;
2613+
default:
2614+
return -EINVAL;
2615+
}
2616+
2617+
if (!done_phase) {
2618+
*finished = false;
2619+
return m88e3082_vct_cable_test_start(phydev);
2620+
}
2621+
if (*finished)
2622+
priv->vct_phase = M88E3082_VCT_OFF;
2623+
return 0;
2624+
}
2625+
24202626
#ifdef CONFIG_HWMON
24212627
struct marvell_hwmon_ops {
24222628
int (*config)(struct phy_device *phydev);
@@ -3300,6 +3506,8 @@ static struct phy_driver marvell_drivers[] = {
33003506
.read_status = marvell_read_status,
33013507
.resume = genphy_resume,
33023508
.suspend = genphy_suspend,
3509+
.cable_test_start = m88e3082_vct_cable_test_start,
3510+
.cable_test_get_status = m88e3082_vct_cable_test_get_status,
33033511
},
33043512
{
33053513
.phy_id = MARVELL_PHY_ID_88E1112,

0 commit comments

Comments
 (0)