Skip to content

Commit f85cd6a

Browse files
committed
Merge branch 'add-alcd-support-to-cable-testing-interface'
Oleksij Rempel says: ==================== Add ALCD Support to Cable Testing Interface This patch series introduces support for Active Link Cable Diagnostics (ALCD) in the ethtool cable testing interface and the DP83TD510 PHY driver. Why ALCD? On a 10BaseT1L interface, TDR (Time Domain Reflectometry) is not possible if the link partner is active - TDR will fail in these cases because it requires interrupting the link. Since the link is active, we already know the cable is functioning, so instead of using TDR, we can use ALCD. ALCD lets us measure cable length without disrupting the active link, which is crucial in environments where network uptime is important. It provides a way to gather diagnostic data without the need for downtime. What's in this series: - Extended the ethtool cable testing interface to specify the source of diagnostic results (TDR or ALCD). - Updated the DP83TD510 PHY driver to use ALCD when the link is active, ensuring we can still get cable length info without dropping the connection. ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents b494b16 + 986a7fa commit f85cd6a

File tree

6 files changed

+173
-16
lines changed

6 files changed

+173
-16
lines changed

Documentation/netlink/specs/ethtool.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,9 @@ attribute-sets:
667667
-
668668
name: code
669669
type: u8
670+
-
671+
name: src
672+
type: u32
670673
-
671674
name: cable-fault-length
672675
attributes:
@@ -676,6 +679,9 @@ attribute-sets:
676679
-
677680
name: cm
678681
type: u32
682+
-
683+
name: src
684+
type: u32
679685
-
680686
name: cable-nest
681687
attributes:

Documentation/networking/ethtool-netlink.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,12 +1314,17 @@ information.
13141314
+-+-+-----------------------------------------+--------+---------------------+
13151315
| | | ``ETHTOOL_A_CABLE_RESULTS_CODE`` | u8 | result code |
13161316
+-+-+-----------------------------------------+--------+---------------------+
1317+
| | | ``ETHTOOL_A_CABLE_RESULT_SRC`` | u32 | information source |
1318+
+-+-+-----------------------------------------+--------+---------------------+
13171319
| | ``ETHTOOL_A_CABLE_NEST_FAULT_LENGTH`` | nested | cable length |
13181320
+-+-+-----------------------------------------+--------+---------------------+
13191321
| | | ``ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR`` | u8 | pair number |
13201322
+-+-+-----------------------------------------+--------+---------------------+
13211323
| | | ``ETHTOOL_A_CABLE_FAULT_LENGTH_CM`` | u32 | length in cm |
13221324
+-+-+-----------------------------------------+--------+---------------------+
1325+
| | | ``ETHTOOL_A_CABLE_FAULT_LENGTH_SRC`` | u32 | information source |
1326+
+-+-+-----------------------------------------+--------+---------------------+
1327+
13231328

13241329
CABLE_TEST TDR
13251330
==============

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,

include/linux/ethtool_netlink.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ struct phy_device;
2323
int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd);
2424
void ethnl_cable_test_free(struct phy_device *phydev);
2525
void ethnl_cable_test_finished(struct phy_device *phydev);
26-
int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result);
27-
int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm);
26+
int ethnl_cable_test_result_with_src(struct phy_device *phydev, u8 pair,
27+
u8 result, u32 src);
28+
int ethnl_cable_test_fault_length_with_src(struct phy_device *phydev, u8 pair,
29+
u32 cm, u32 src);
2830
int ethnl_cable_test_amplitude(struct phy_device *phydev, u8 pair, s16 mV);
2931
int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV);
3032
int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last,
@@ -54,14 +56,14 @@ static inline void ethnl_cable_test_free(struct phy_device *phydev)
5456
static inline void ethnl_cable_test_finished(struct phy_device *phydev)
5557
{
5658
}
57-
static inline int ethnl_cable_test_result(struct phy_device *phydev, u8 pair,
58-
u8 result)
59+
static inline int ethnl_cable_test_result_with_src(struct phy_device *phydev,
60+
u8 pair, u8 result, u32 src)
5961
{
6062
return -EOPNOTSUPP;
6163
}
6264

63-
static inline int ethnl_cable_test_fault_length(struct phy_device *phydev,
64-
u8 pair, u32 cm)
65+
static inline int ethnl_cable_test_fault_length_with_src(struct phy_device *phydev,
66+
u8 pair, u32 cm, u32 src)
6567
{
6668
return -EOPNOTSUPP;
6769
}
@@ -119,4 +121,19 @@ static inline bool ethtool_dev_mm_supported(struct net_device *dev)
119121
}
120122

121123
#endif /* IS_ENABLED(CONFIG_ETHTOOL_NETLINK) */
124+
125+
static inline int ethnl_cable_test_result(struct phy_device *phydev, u8 pair,
126+
u8 result)
127+
{
128+
return ethnl_cable_test_result_with_src(phydev, pair, result,
129+
ETHTOOL_A_CABLE_INF_SRC_TDR);
130+
}
131+
132+
static inline int ethnl_cable_test_fault_length(struct phy_device *phydev,
133+
u8 pair, u32 cm)
134+
{
135+
return ethnl_cable_test_fault_length_with_src(phydev, pair, cm,
136+
ETHTOOL_A_CABLE_INF_SRC_TDR);
137+
}
138+
122139
#endif /* _LINUX_ETHTOOL_NETLINK_H_ */

include/uapi/linux/ethtool_netlink.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,10 +573,20 @@ enum {
573573
ETHTOOL_A_CABLE_PAIR_D,
574574
};
575575

576+
/* Information source for specific results. */
577+
enum {
578+
ETHTOOL_A_CABLE_INF_SRC_UNSPEC,
579+
/* Results provided by the Time Domain Reflectometry (TDR) */
580+
ETHTOOL_A_CABLE_INF_SRC_TDR,
581+
/* Results provided by the Active Link Cable Diagnostic (ALCD) */
582+
ETHTOOL_A_CABLE_INF_SRC_ALCD,
583+
};
584+
576585
enum {
577586
ETHTOOL_A_CABLE_RESULT_UNSPEC,
578587
ETHTOOL_A_CABLE_RESULT_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */
579588
ETHTOOL_A_CABLE_RESULT_CODE, /* u8 ETHTOOL_A_CABLE_RESULT_CODE_ */
589+
ETHTOOL_A_CABLE_RESULT_SRC, /* u32 ETHTOOL_A_CABLE_INF_SRC_ */
580590

581591
__ETHTOOL_A_CABLE_RESULT_CNT,
582592
ETHTOOL_A_CABLE_RESULT_MAX = (__ETHTOOL_A_CABLE_RESULT_CNT - 1)
@@ -586,6 +596,7 @@ enum {
586596
ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC,
587597
ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */
588598
ETHTOOL_A_CABLE_FAULT_LENGTH_CM, /* u32 */
599+
ETHTOOL_A_CABLE_FAULT_LENGTH_SRC, /* u32 ETHTOOL_A_CABLE_INF_SRC_ */
589600

590601
__ETHTOOL_A_CABLE_FAULT_LENGTH_CNT,
591602
ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = (__ETHTOOL_A_CABLE_FAULT_LENGTH_CNT - 1)

net/ethtool/cabletest.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ void ethnl_cable_test_finished(struct phy_device *phydev)
164164
}
165165
EXPORT_SYMBOL_GPL(ethnl_cable_test_finished);
166166

167-
int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
167+
int ethnl_cable_test_result_with_src(struct phy_device *phydev, u8 pair,
168+
u8 result, u32 src)
168169
{
169170
struct nlattr *nest;
170171
int ret = -EMSGSIZE;
@@ -177,6 +178,10 @@ int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
177178
goto err;
178179
if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result))
179180
goto err;
181+
if (src != ETHTOOL_A_CABLE_INF_SRC_UNSPEC) {
182+
if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_RESULT_SRC, src))
183+
goto err;
184+
}
180185

181186
nla_nest_end(phydev->skb, nest);
182187
return 0;
@@ -185,9 +190,10 @@ int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
185190
nla_nest_cancel(phydev->skb, nest);
186191
return ret;
187192
}
188-
EXPORT_SYMBOL_GPL(ethnl_cable_test_result);
193+
EXPORT_SYMBOL_GPL(ethnl_cable_test_result_with_src);
189194

190-
int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
195+
int ethnl_cable_test_fault_length_with_src(struct phy_device *phydev, u8 pair,
196+
u32 cm, u32 src)
191197
{
192198
struct nlattr *nest;
193199
int ret = -EMSGSIZE;
@@ -201,6 +207,11 @@ int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
201207
goto err;
202208
if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm))
203209
goto err;
210+
if (src != ETHTOOL_A_CABLE_INF_SRC_UNSPEC) {
211+
if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_SRC,
212+
src))
213+
goto err;
214+
}
204215

205216
nla_nest_end(phydev->skb, nest);
206217
return 0;
@@ -209,7 +220,7 @@ int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
209220
nla_nest_cancel(phydev->skb, nest);
210221
return ret;
211222
}
212-
EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
223+
EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length_with_src);
213224

214225
static const struct nla_policy cable_test_tdr_act_cfg_policy[] = {
215226
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 },

0 commit comments

Comments
 (0)