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

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Pawel Dembicki and committed by
Jakub Kicinski
a9e4230d 9cc8a6e6

+208
+208
drivers/net/phy/marvell.c
··· 279 279 #define MII_VCT7_CTRL_METERS BIT(10) 280 280 #define MII_VCT7_CTRL_CENTIMETERS 0 281 281 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 + 282 299 #define LPA_PAUSE_FIBER 0x180 283 300 #define LPA_PAUSE_ASYM_FIBER 0x100 284 301 ··· 318 301 { "phy_receive_errors_fiber", 1, 21, 16}, 319 302 }; 320 303 304 + enum { 305 + M88E3082_VCT_OFF, 306 + M88E3082_VCT_PHASE1, 307 + M88E3082_VCT_PHASE2, 308 + }; 309 + 321 310 struct marvell_priv { 322 311 u64 stats[ARRAY_SIZE(marvell_hw_stats)]; 323 312 char *hwmon_name; ··· 333 310 u32 last; 334 311 u32 step; 335 312 s8 pair; 313 + u8 vct_phase; 336 314 }; 337 315 338 316 static int marvell_read_page(struct phy_device *phydev) ··· 2441 2417 return 0; 2442 2418 } 2443 2419 2420 + static int m88e3082_vct_cable_test_start(struct phy_device *phydev) 2421 + { 2422 + struct marvell_priv *priv = phydev->priv; 2423 + int ret; 2424 + 2425 + /* It needs some magic workarounds described in VCT manual for this PHY. 2426 + */ 2427 + ret = phy_write(phydev, 29, 0x0003); 2428 + if (ret < 0) 2429 + return ret; 2430 + 2431 + ret = phy_write(phydev, 30, 0x6440); 2432 + if (ret < 0) 2433 + return ret; 2434 + 2435 + if (priv->vct_phase == M88E3082_VCT_PHASE1) { 2436 + ret = phy_write(phydev, 29, 0x000a); 2437 + if (ret < 0) 2438 + return ret; 2439 + 2440 + ret = phy_write(phydev, 30, 0x0002); 2441 + if (ret < 0) 2442 + return ret; 2443 + } 2444 + 2445 + ret = phy_write(phydev, MII_BMCR, 2446 + BMCR_RESET | BMCR_SPEED100 | BMCR_FULLDPLX); 2447 + if (ret < 0) 2448 + return ret; 2449 + 2450 + ret = phy_write(phydev, MII_VCT_TXPINS, MII_VCT_TXPINS_ENVCT); 2451 + if (ret < 0) 2452 + return ret; 2453 + 2454 + ret = phy_write(phydev, 29, 0x0003); 2455 + if (ret < 0) 2456 + return ret; 2457 + 2458 + ret = phy_write(phydev, 30, 0x0); 2459 + if (ret < 0) 2460 + return ret; 2461 + 2462 + if (priv->vct_phase == M88E3082_VCT_OFF) { 2463 + priv->vct_phase = M88E3082_VCT_PHASE1; 2464 + priv->pair = 0; 2465 + 2466 + return 0; 2467 + } 2468 + 2469 + ret = phy_write(phydev, 29, 0x000a); 2470 + if (ret < 0) 2471 + return ret; 2472 + 2473 + ret = phy_write(phydev, 30, 0x0); 2474 + if (ret < 0) 2475 + return ret; 2476 + 2477 + priv->vct_phase = M88E3082_VCT_PHASE2; 2478 + 2479 + return 0; 2480 + } 2481 + 2482 + static int m88e3082_vct_cable_test_report_trans(int result, u8 distance) 2483 + { 2484 + switch (result) { 2485 + case MII_VCT_TXRXPINS_VCTTST_OK: 2486 + if (distance == MII_VCT_TXRXPINS_DISTRFLN_MAX) 2487 + return ETHTOOL_A_CABLE_RESULT_CODE_OK; 2488 + return ETHTOOL_A_CABLE_RESULT_CODE_IMPEDANCE_MISMATCH; 2489 + case MII_VCT_TXRXPINS_VCTTST_SHORT: 2490 + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 2491 + case MII_VCT_TXRXPINS_VCTTST_OPEN: 2492 + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 2493 + default: 2494 + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 2495 + } 2496 + } 2497 + 2498 + static u32 m88e3082_vct_distrfln_2_cm(u8 distrfln) 2499 + { 2500 + if (distrfln < 24) 2501 + return 0; 2502 + 2503 + /* Original function for meters: y = 0.7861x - 18.862 */ 2504 + return (7861 * distrfln - 188620) / 100; 2505 + } 2506 + 2507 + static int m88e3082_vct_cable_test_get_status(struct phy_device *phydev, 2508 + bool *finished) 2509 + { 2510 + u8 tx_vcttst_res, rx_vcttst_res, tx_distrfln, rx_distrfln; 2511 + struct marvell_priv *priv = phydev->priv; 2512 + int ret, tx_result, rx_result; 2513 + bool done_phase = true; 2514 + 2515 + *finished = false; 2516 + 2517 + ret = phy_read(phydev, MII_VCT_TXPINS); 2518 + if (ret < 0) 2519 + return ret; 2520 + else if (ret & MII_VCT_TXPINS_ENVCT) 2521 + return 0; 2522 + 2523 + tx_distrfln = ret & MII_VCT_TXRXPINS_DISTRFLN; 2524 + tx_vcttst_res = (ret & MII_VCT_TXRXPINS_VCTTST) >> 2525 + MII_VCT_TXRXPINS_VCTTST_SHIFT; 2526 + 2527 + ret = phy_read(phydev, MII_VCT_RXPINS); 2528 + if (ret < 0) 2529 + return ret; 2530 + 2531 + rx_distrfln = ret & MII_VCT_TXRXPINS_DISTRFLN; 2532 + rx_vcttst_res = (ret & MII_VCT_TXRXPINS_VCTTST) >> 2533 + MII_VCT_TXRXPINS_VCTTST_SHIFT; 2534 + 2535 + *finished = true; 2536 + 2537 + switch (priv->vct_phase) { 2538 + case M88E3082_VCT_PHASE1: 2539 + tx_result = m88e3082_vct_cable_test_report_trans(tx_vcttst_res, 2540 + tx_distrfln); 2541 + rx_result = m88e3082_vct_cable_test_report_trans(rx_vcttst_res, 2542 + rx_distrfln); 2543 + 2544 + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 2545 + tx_result); 2546 + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 2547 + rx_result); 2548 + 2549 + if (tx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN) { 2550 + done_phase = false; 2551 + priv->pair |= M88E3082_PAIR_A; 2552 + } else if (tx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) { 2553 + u8 pair = ETHTOOL_A_CABLE_PAIR_A; 2554 + u32 cm = m88e3082_vct_distrfln_2_cm(tx_distrfln); 2555 + 2556 + ethnl_cable_test_fault_length(phydev, pair, cm); 2557 + } 2558 + 2559 + if (rx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN) { 2560 + done_phase = false; 2561 + priv->pair |= M88E3082_PAIR_B; 2562 + } else if (rx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) { 2563 + u8 pair = ETHTOOL_A_CABLE_PAIR_B; 2564 + u32 cm = m88e3082_vct_distrfln_2_cm(rx_distrfln); 2565 + 2566 + ethnl_cable_test_fault_length(phydev, pair, cm); 2567 + } 2568 + 2569 + break; 2570 + case M88E3082_VCT_PHASE2: 2571 + if (priv->pair & M88E3082_PAIR_A && 2572 + tx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN && 2573 + tx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) { 2574 + u8 pair = ETHTOOL_A_CABLE_PAIR_A; 2575 + u32 cm = m88e3082_vct_distrfln_2_cm(tx_distrfln); 2576 + 2577 + ethnl_cable_test_fault_length(phydev, pair, cm); 2578 + } 2579 + if (priv->pair & M88E3082_PAIR_B && 2580 + rx_vcttst_res == MII_VCT_TXRXPINS_VCTTST_OPEN && 2581 + rx_distrfln < MII_VCT_TXRXPINS_DISTRFLN_MAX) { 2582 + u8 pair = ETHTOOL_A_CABLE_PAIR_B; 2583 + u32 cm = m88e3082_vct_distrfln_2_cm(rx_distrfln); 2584 + 2585 + ethnl_cable_test_fault_length(phydev, pair, cm); 2586 + } 2587 + 2588 + break; 2589 + default: 2590 + return -EINVAL; 2591 + } 2592 + 2593 + if (!done_phase) { 2594 + *finished = false; 2595 + return m88e3082_vct_cable_test_start(phydev); 2596 + } 2597 + if (*finished) 2598 + priv->vct_phase = M88E3082_VCT_OFF; 2599 + return 0; 2600 + } 2601 + 2444 2602 #ifdef CONFIG_HWMON 2445 2603 struct marvell_hwmon_ops { 2446 2604 int (*config)(struct phy_device *phydev); ··· 3506 3300 .read_status = marvell_read_status, 3507 3301 .resume = genphy_resume, 3508 3302 .suspend = genphy_suspend, 3303 + .cable_test_start = m88e3082_vct_cable_test_start, 3304 + .cable_test_get_status = m88e3082_vct_cable_test_get_status, 3509 3305 }, 3510 3306 { 3511 3307 .phy_id = MARVELL_PHY_ID_88E1112,