Skip to content

Commit 0cda1ac

Browse files
oleremkuba-moo
authored andcommitted
net: phy: dp83td510: add cable testing support
This patch implements the TDR test procedure as described in "Application Note DP83TD510E Cable Diagnostics Toolkit revC", section 3.2. The procedure was tested with "draka 08 signalkabel 2x0.8mm". The reported cable length was 5 meters more for each 20 meters of actual cable length. For instance, a 20-meter cable showed as 25 meters, and a 40-meter cable showed as 50 meters. Since other parts of the diagnostics provided by this PHY (e.g., Active Link Cable Diagnostics) require accurate cable characterization to provide proper results, this tuning can be implemented in a separate patch/interface. Signed-off-by: Oleksij Rempel <[email protected]> changes v2: - add comments - change post silence time to 1000ms Reviewed-by: Andrew Lunn <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent e7cdef6 commit 0cda1ac

File tree

1 file changed

+264
-0
lines changed

1 file changed

+264
-0
lines changed

drivers/net/phy/dp83td510.c

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
#include <linux/bitfield.h>
7+
#include <linux/ethtool_netlink.h>
78
#include <linux/kernel.h>
89
#include <linux/module.h>
910
#include <linux/phy.h>
@@ -29,6 +30,10 @@
2930
#define DP83TD510E_INT1_LINK BIT(13)
3031
#define DP83TD510E_INT1_LINK_EN BIT(5)
3132

33+
#define DP83TD510E_CTRL 0x1f
34+
#define DP83TD510E_CTRL_HW_RESET BIT(15)
35+
#define DP83TD510E_CTRL_SW_RESET BIT(14)
36+
3237
#define DP83TD510E_AN_STAT_1 0x60c
3338
#define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15)
3439

@@ -53,6 +58,117 @@ static const u16 dp83td510_mse_sqi_map[] = {
5358
0x0000 /* 24dB =< SNR */
5459
};
5560

61+
/* Time Domain Reflectometry (TDR) Functionality of DP83TD510 PHY
62+
*
63+
* I assume that this PHY is using a variation of Spread Spectrum Time Domain
64+
* Reflectometry (SSTDR) rather than the commonly used TDR found in many PHYs.
65+
* Here are the following observations which likely confirm this:
66+
* - The DP83TD510 PHY transmits a modulated signal of configurable length
67+
* (default 16000 µs) instead of a single pulse pattern, which is typical
68+
* for traditional TDR.
69+
* - The pulse observed on the wire, triggered by the HW RESET register, is not
70+
* part of the cable testing process.
71+
*
72+
* I assume that SSTDR seems to be a logical choice for the 10BaseT1L
73+
* environment due to improved noise resistance, making it suitable for
74+
* environments with significant electrical noise, such as long 10BaseT1L cable
75+
* runs.
76+
*
77+
* Configuration Variables:
78+
* The SSTDR variation used in this PHY involves more configuration variables
79+
* that can dramatically affect the functionality and precision of cable
80+
* testing. Since most of these configuration options are either not well
81+
* documented or documented with minimal details, the following sections
82+
* describe my understanding and observations of these variables and their
83+
* impact on TDR functionality.
84+
*
85+
* Timeline:
86+
* ,<--cfg_pre_silence_time
87+
* | ,<-SSTDR Modulated Transmission
88+
* | | ,<--cfg_post_silence_time
89+
* | | | ,<--Force Link Mode
90+
* |<--'-->|<-------'------->|<--'-->|<--------'------->|
91+
*
92+
* - cfg_pre_silence_time: Optional silence time before TDR transmission starts.
93+
* - SSTDR Modulated Transmission: Transmission duration configured by
94+
* cfg_tdr_tx_duration and amplitude configured by cfg_tdr_tx_type.
95+
* - cfg_post_silence_time: Silence time after TDR transmission.
96+
* - Force Link Mode: If nothing is configured after cfg_post_silence_time,
97+
* the PHY continues in force link mode without autonegotiation.
98+
*/
99+
100+
#define DP83TD510E_TDR_CFG 0x1e
101+
#define DP83TD510E_TDR_START BIT(15)
102+
#define DP83TD510E_TDR_DONE BIT(1)
103+
#define DP83TD510E_TDR_FAIL BIT(0)
104+
105+
#define DP83TD510E_TDR_CFG1 0x300
106+
/* cfg_tdr_tx_type: Transmit voltage level for TDR.
107+
* 0 = 1V, 1 = 2.4V
108+
* Note: Using different voltage levels may not work
109+
* in all configuration variations. For example, setting
110+
* 2.4V may give different cable length measurements.
111+
* Other settings may be needed to make it work properly.
112+
*/
113+
#define DP83TD510E_TDR_TX_TYPE BIT(12)
114+
#define DP83TD510E_TDR_TX_TYPE_1V 0
115+
#define DP83TD510E_TDR_TX_TYPE_2_4V 1
116+
/* cfg_post_silence_time: Time after the TDR sequence. Since we force master mode
117+
* for the TDR will proceed with forced link state after this time. For Linux
118+
* it is better to set max value to avoid false link state detection.
119+
*/
120+
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME GENMASK(3, 2)
121+
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_0MS 0
122+
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_10MS 1
123+
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_100MS 2
124+
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_1000MS 3
125+
/* cfg_pre_silence_time: Time before the TDR sequence. It should be enough to
126+
* settle down all pulses and reflections. Since for 10BASE-T1L we have
127+
* maximum 2000m cable length, we can set it to 1ms.
128+
*/
129+
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME GENMASK(1, 0)
130+
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_0MS 0
131+
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_10MS 1
132+
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_100MS 2
133+
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_1000MS 3
134+
135+
#define DP83TD510E_TDR_CFG2 0x301
136+
#define DP83TD510E_TDR_END_TAP_INDEX_1 GENMASK(14, 8)
137+
#define DP83TD510E_TDR_END_TAP_INDEX_1_DEF 36
138+
#define DP83TD510E_TDR_START_TAP_INDEX_1 GENMASK(6, 0)
139+
#define DP83TD510E_TDR_START_TAP_INDEX_1_DEF 4
140+
141+
#define DP83TD510E_TDR_CFG3 0x302
142+
/* cfg_tdr_tx_duration: Duration of the TDR transmission in microseconds.
143+
* This value sets the duration of the modulated signal used for TDR
144+
* measurements.
145+
* - Default: 16000 µs
146+
* - Observation: A minimum duration of 6000 µs is recommended to ensure
147+
* accurate detection of cable faults. Durations shorter than 6000 µs may
148+
* result in incomplete data, especially for shorter cables (e.g., 20 meters),
149+
* leading to false "OK" results. Longer durations (e.g., 6000 µs or more)
150+
* provide better accuracy, particularly for detecting open circuits.
151+
*/
152+
#define DP83TD510E_TDR_TX_DURATION_US GENMASK(15, 0)
153+
#define DP83TD510E_TDR_TX_DURATION_US_DEF 16000
154+
155+
#define DP83TD510E_TDR_FAULT_CFG1 0x303
156+
#define DP83TD510E_TDR_FLT_LOC_OFFSET_1 GENMASK(14, 8)
157+
#define DP83TD510E_TDR_FLT_LOC_OFFSET_1_DEF 4
158+
#define DP83TD510E_TDR_FLT_INIT_1 GENMASK(7, 0)
159+
#define DP83TD510E_TDR_FLT_INIT_1_DEF 62
160+
161+
#define DP83TD510E_TDR_FAULT_STAT 0x30c
162+
#define DP83TD510E_TDR_PEAK_DETECT BIT(11)
163+
#define DP83TD510E_TDR_PEAK_SIGN BIT(10)
164+
#define DP83TD510E_TDR_PEAK_LOCATION GENMASK(9, 0)
165+
166+
/* Not documented registers and values but recommended according to
167+
* "DP83TD510E Cable Diagnostics Toolkit revC"
168+
*/
169+
#define DP83TD510E_UNKN_030E 0x30e
170+
#define DP83TD510E_030E_VAL 0x2520
171+
56172
static int dp83td510_config_intr(struct phy_device *phydev)
57173
{
58174
int ret;
@@ -198,6 +314,151 @@ static int dp83td510_get_sqi_max(struct phy_device *phydev)
198314
return DP83TD510_SQI_MAX;
199315
}
200316

317+
/**
318+
* dp83td510_cable_test_start - Start the cable test for the DP83TD510 PHY.
319+
* @phydev: Pointer to the phy_device structure.
320+
*
321+
* This sequence is implemented according to the "Application Note DP83TD510E
322+
* Cable Diagnostics Toolkit revC".
323+
*
324+
* Returns: 0 on success, a negative error code on failure.
325+
*/
326+
static int dp83td510_cable_test_start(struct phy_device *phydev)
327+
{
328+
int ret;
329+
330+
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_CTRL,
331+
DP83TD510E_CTRL_HW_RESET);
332+
if (ret)
333+
return ret;
334+
335+
ret = genphy_c45_an_disable_aneg(phydev);
336+
if (ret)
337+
return ret;
338+
339+
/* Force master mode */
340+
ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL,
341+
MDIO_PMA_PMD_BT1_CTRL_CFG_MST);
342+
if (ret)
343+
return ret;
344+
345+
/* There is no official recommendation for this register, but it is
346+
* better to use 1V for TDR since other values seems to be optimized
347+
* for this amplitude. Except of amplitude, it is better to configure
348+
* pre TDR silence time to 10ms to avoid false reflections (value 0
349+
* seems to be too short, otherwise we need to implement own silence
350+
* time). Also, post TDR silence time should be set to 1000ms to avoid
351+
* false link state detection, it fits to the polling time of the
352+
* PHY framework. The idea is to wait until
353+
* dp83td510_cable_test_get_status() will be called and reconfigure
354+
* the PHY to the default state within the post silence time window.
355+
*/
356+
ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG1,
357+
DP83TD510E_TDR_TX_TYPE |
358+
DP83TD510E_TDR_CFG1_POST_SILENCE_TIME |
359+
DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME,
360+
DP83TD510E_TDR_TX_TYPE_1V |
361+
DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_10MS |
362+
DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_1000MS);
363+
if (ret)
364+
return ret;
365+
366+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG2,
367+
FIELD_PREP(DP83TD510E_TDR_END_TAP_INDEX_1,
368+
DP83TD510E_TDR_END_TAP_INDEX_1_DEF) |
369+
FIELD_PREP(DP83TD510E_TDR_START_TAP_INDEX_1,
370+
DP83TD510E_TDR_START_TAP_INDEX_1_DEF));
371+
if (ret)
372+
return ret;
373+
374+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_FAULT_CFG1,
375+
FIELD_PREP(DP83TD510E_TDR_FLT_LOC_OFFSET_1,
376+
DP83TD510E_TDR_FLT_LOC_OFFSET_1_DEF) |
377+
FIELD_PREP(DP83TD510E_TDR_FLT_INIT_1,
378+
DP83TD510E_TDR_FLT_INIT_1_DEF));
379+
if (ret)
380+
return ret;
381+
382+
/* Undocumented register, from the "Application Note DP83TD510E Cable
383+
* Diagnostics Toolkit revC".
384+
*/
385+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_UNKN_030E,
386+
DP83TD510E_030E_VAL);
387+
if (ret)
388+
return ret;
389+
390+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG3,
391+
DP83TD510E_TDR_TX_DURATION_US_DEF);
392+
if (ret)
393+
return ret;
394+
395+
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_CTRL,
396+
DP83TD510E_CTRL_SW_RESET);
397+
if (ret)
398+
return ret;
399+
400+
return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG,
401+
DP83TD510E_TDR_START);
402+
}
403+
404+
/**
405+
* dp83td510_cable_test_get_status - Get the status of the cable test for the
406+
* DP83TD510 PHY.
407+
* @phydev: Pointer to the phy_device structure.
408+
* @finished: Pointer to a boolean that indicates whether the test is finished.
409+
*
410+
* The function sets the @finished flag to true if the test is complete.
411+
*
412+
* Returns: 0 on success or a negative error code on failure.
413+
*/
414+
static int dp83td510_cable_test_get_status(struct phy_device *phydev,
415+
bool *finished)
416+
{
417+
int ret, stat;
418+
419+
*finished = false;
420+
421+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG);
422+
if (ret < 0)
423+
return ret;
424+
425+
if (!(ret & DP83TD510E_TDR_DONE))
426+
return 0;
427+
428+
if (!(ret & DP83TD510E_TDR_FAIL)) {
429+
int location;
430+
431+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
432+
DP83TD510E_TDR_FAULT_STAT);
433+
if (ret < 0)
434+
return ret;
435+
436+
if (ret & DP83TD510E_TDR_PEAK_DETECT) {
437+
if (ret & DP83TD510E_TDR_PEAK_SIGN)
438+
stat = ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
439+
else
440+
stat = ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
441+
442+
location = FIELD_GET(DP83TD510E_TDR_PEAK_LOCATION,
443+
ret) * 100;
444+
ethnl_cable_test_fault_length(phydev,
445+
ETHTOOL_A_CABLE_PAIR_A,
446+
location);
447+
} else {
448+
stat = ETHTOOL_A_CABLE_RESULT_CODE_OK;
449+
}
450+
} else {
451+
/* Most probably we have active link partner */
452+
stat = ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
453+
}
454+
455+
*finished = true;
456+
457+
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, stat);
458+
459+
return phy_init_hw(phydev);
460+
}
461+
201462
static int dp83td510_get_features(struct phy_device *phydev)
202463
{
203464
/* This PHY can't respond on MDIO bus if no RMII clock is enabled.
@@ -221,13 +482,16 @@ static struct phy_driver dp83td510_driver[] = {
221482
PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
222483
.name = "TI DP83TD510E",
223484

485+
.flags = PHY_POLL_CABLE_TEST,
224486
.config_aneg = dp83td510_config_aneg,
225487
.read_status = dp83td510_read_status,
226488
.get_features = dp83td510_get_features,
227489
.config_intr = dp83td510_config_intr,
228490
.handle_interrupt = dp83td510_handle_interrupt,
229491
.get_sqi = dp83td510_get_sqi,
230492
.get_sqi_max = dp83td510_get_sqi_max,
493+
.cable_test_start = dp83td510_cable_test_start,
494+
.cable_test_get_status = dp83td510_cable_test_get_status,
231495

232496
.suspend = genphy_suspend,
233497
.resume = genphy_resume,

0 commit comments

Comments
 (0)