@@ -58,6 +58,10 @@ static const u16 dp83td510_mse_sqi_map[] = {
5858 0x0000 /* 24dB =< SNR */
5959};
6060
61+ struct dp83td510_priv {
62+ bool alcd_test_active ;
63+ };
64+
6165/* Time Domain Reflectometry (TDR) Functionality of DP83TD510 PHY
6266 *
6367 * I assume that this PHY is using a variation of Spread Spectrum Time Domain
@@ -169,6 +173,10 @@ static const u16 dp83td510_mse_sqi_map[] = {
169173#define DP83TD510E_UNKN_030E 0x30e
170174#define DP83TD510E_030E_VAL 0x2520
171175
176+ #define DP83TD510E_ALCD_STAT 0xa9f
177+ #define DP83TD510E_ALCD_COMPLETE BIT(15)
178+ #define DP83TD510E_ALCD_CABLE_LENGTH GENMASK(10, 0)
179+
172180static int dp83td510_config_intr (struct phy_device * phydev )
173181{
174182 int ret ;
@@ -325,8 +333,23 @@ static int dp83td510_get_sqi_max(struct phy_device *phydev)
325333 */
326334static int dp83td510_cable_test_start (struct phy_device * phydev )
327335{
336+ struct dp83td510_priv * priv = phydev -> priv ;
328337 int ret ;
329338
339+ /* If link partner is active, we won't be able to use TDR, since
340+ * we can't force link partner to be silent. The autonegotiation
341+ * pulses will be too frequent and the TDR sequence will be
342+ * too long. So, TDR will always fail. Since the link is established
343+ * we already know that the cable is working, so we can get some
344+ * extra information line the cable length using ALCD.
345+ */
346+ if (phydev -> link ) {
347+ priv -> alcd_test_active = true;
348+ return 0 ;
349+ }
350+
351+ priv -> alcd_test_active = false;
352+
330353 ret = phy_set_bits_mmd (phydev , MDIO_MMD_VEND2 , DP83TD510E_CTRL ,
331354 DP83TD510E_CTRL_HW_RESET );
332355 if (ret )
@@ -402,22 +425,20 @@ static int dp83td510_cable_test_start(struct phy_device *phydev)
402425}
403426
404427/**
405- * dp83td510_cable_test_get_status - Get the status of the cable test for the
406- * DP83TD510 PHY.
428+ * dp83td510_cable_test_get_tdr_status - Get the status of the TDR test for the
429+ * DP83TD510 PHY.
407430 * @phydev: Pointer to the phy_device structure.
408431 * @finished: Pointer to a boolean that indicates whether the test is finished.
409432 *
410433 * The function sets the @finished flag to true if the test is complete.
411434 *
412435 * Returns: 0 on success or a negative error code on failure.
413436 */
414- static int dp83td510_cable_test_get_status (struct phy_device * phydev ,
415- bool * finished )
437+ static int dp83td510_cable_test_get_tdr_status (struct phy_device * phydev ,
438+ bool * finished )
416439{
417440 int ret , stat ;
418441
419- * finished = false;
420-
421442 ret = phy_read_mmd (phydev , MDIO_MMD_VEND2 , DP83TD510E_TDR_CFG );
422443 if (ret < 0 )
423444 return ret ;
@@ -459,6 +480,77 @@ static int dp83td510_cable_test_get_status(struct phy_device *phydev,
459480 return phy_init_hw (phydev );
460481}
461482
483+ /**
484+ * dp83td510_cable_test_get_alcd_status - Get the status of the ALCD test for the
485+ * DP83TD510 PHY.
486+ * @phydev: Pointer to the phy_device structure.
487+ * @finished: Pointer to a boolean that indicates whether the test is finished.
488+ *
489+ * The function sets the @finished flag to true if the test is complete.
490+ * The function reads the cable length and reports it to the user.
491+ *
492+ * Returns: 0 on success or a negative error code on failure.
493+ */
494+ static int dp83td510_cable_test_get_alcd_status (struct phy_device * phydev ,
495+ bool * finished )
496+ {
497+ unsigned int location ;
498+ int ret , phy_sts ;
499+
500+ phy_sts = phy_read (phydev , DP83TD510E_PHY_STS );
501+
502+ if (!(phy_sts & DP83TD510E_LINK_STATUS )) {
503+ /* If the link is down, we can't do any thing usable now */
504+ ethnl_cable_test_result_with_src (phydev , ETHTOOL_A_CABLE_PAIR_A ,
505+ ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC ,
506+ ETHTOOL_A_CABLE_INF_SRC_ALCD );
507+ * finished = true;
508+ return 0 ;
509+ }
510+
511+ ret = phy_read_mmd (phydev , MDIO_MMD_VEND2 , DP83TD510E_ALCD_STAT );
512+ if (ret < 0 )
513+ return ret ;
514+
515+ if (!(ret & DP83TD510E_ALCD_COMPLETE ))
516+ return 0 ;
517+
518+ location = FIELD_GET (DP83TD510E_ALCD_CABLE_LENGTH , ret ) * 100 ;
519+
520+ ethnl_cable_test_fault_length_with_src (phydev , ETHTOOL_A_CABLE_PAIR_A ,
521+ location ,
522+ ETHTOOL_A_CABLE_INF_SRC_ALCD );
523+
524+ ethnl_cable_test_result_with_src (phydev , ETHTOOL_A_CABLE_PAIR_A ,
525+ ETHTOOL_A_CABLE_RESULT_CODE_OK ,
526+ ETHTOOL_A_CABLE_INF_SRC_ALCD );
527+ * finished = true;
528+
529+ return 0 ;
530+ }
531+
532+ /**
533+ * dp83td510_cable_test_get_status - Get the status of the cable test for the
534+ * DP83TD510 PHY.
535+ * @phydev: Pointer to the phy_device structure.
536+ * @finished: Pointer to a boolean that indicates whether the test is finished.
537+ *
538+ * The function sets the @finished flag to true if the test is complete.
539+ *
540+ * Returns: 0 on success or a negative error code on failure.
541+ */
542+ static int dp83td510_cable_test_get_status (struct phy_device * phydev ,
543+ bool * finished )
544+ {
545+ struct dp83td510_priv * priv = phydev -> priv ;
546+ * finished = false;
547+
548+ if (priv -> alcd_test_active )
549+ return dp83td510_cable_test_get_alcd_status (phydev , finished );
550+
551+ return dp83td510_cable_test_get_tdr_status (phydev , finished );
552+ }
553+
462554static int dp83td510_get_features (struct phy_device * phydev )
463555{
464556 /* This PHY can't respond on MDIO bus if no RMII clock is enabled.
@@ -477,12 +569,27 @@ static int dp83td510_get_features(struct phy_device *phydev)
477569 return 0 ;
478570}
479571
572+ static int dp83td510_probe (struct phy_device * phydev )
573+ {
574+ struct device * dev = & phydev -> mdio .dev ;
575+ struct dp83td510_priv * priv ;
576+
577+ priv = devm_kzalloc (dev , sizeof (* priv ), GFP_KERNEL );
578+ if (!priv )
579+ return - ENOMEM ;
580+
581+ phydev -> priv = priv ;
582+
583+ return 0 ;
584+ }
585+
480586static struct phy_driver dp83td510_driver [] = {
481587{
482588 PHY_ID_MATCH_MODEL (DP83TD510E_PHY_ID ),
483589 .name = "TI DP83TD510E" ,
484590
485591 .flags = PHY_POLL_CABLE_TEST ,
592+ .probe = dp83td510_probe ,
486593 .config_aneg = dp83td510_config_aneg ,
487594 .read_status = dp83td510_read_status ,
488595 .get_features = dp83td510_get_features ,
0 commit comments