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+
304327struct 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
315339static 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
24212627struct 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