Skip to content

Commit 986a7fa

Browse files
oleremkuba-moo
authored andcommitted
phy: dp83td510: Utilize ALCD for cable length measurement when link is active
In industrial environments where 10BaseT1L PHYs are replacing existing field bus systems like CAN, it's often essential to retain the existing cable infrastructure. After installation, collecting metrics such as cable length is crucial for assessing the quality of the infrastructure. Traditionally, TDR (Time Domain Reflectometry) is used for this purpose. However, TDR requires interrupting the link, and if the link partner remains active, the TDR measurement will fail. Unlike multi-pair systems, where TDR can be attempted during the MDI-X switching window, 10BaseT1L systems face greater challenges. The TDR sequence on 10BaseT1L is longer and coincides with uninterrupted autonegotiation pulses, making TDR impossible when the link partner is active. The DP83TD510 PHY provides an alternative through ALCD (Active Link Cable Diagnostics), which allows for cable length measurement without disrupting an active link. Since a live link indicates no short or open cable states, ALCD can be used effectively to gather cable length information. Enhance the dp83td510 driver by: - Leveraging ALCD to measure cable length when the link is active. - Bypassing TDR when a link is detected, as ALCD provides the required information without disruption. Signed-off-by: Oleksij Rempel <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 4715d87 commit 986a7fa

File tree

1 file changed

+113
-6
lines changed

1 file changed

+113
-6
lines changed

drivers/net/phy/dp83td510.c

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
172180
static 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
*/
326334
static 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+
462554
static 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+
480586
static 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

Comments
 (0)