Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <o.rempel@pengutronix.de>
changes v2:
- add comments
- change post silence time to 1000ms
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20240712152848.2479912-1-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Oleksij Rempel and committed by
Jakub Kicinski
0cda1acf e7cdef62

+264
+264
drivers/net/phy/dp83td510.c
··· 4 4 */ 5 5 6 6 #include <linux/bitfield.h> 7 + #include <linux/ethtool_netlink.h> 7 8 #include <linux/kernel.h> 8 9 #include <linux/module.h> 9 10 #include <linux/phy.h> ··· 30 29 #define DP83TD510E_INT1_LINK BIT(13) 31 30 #define DP83TD510E_INT1_LINK_EN BIT(5) 32 31 32 + #define DP83TD510E_CTRL 0x1f 33 + #define DP83TD510E_CTRL_HW_RESET BIT(15) 34 + #define DP83TD510E_CTRL_SW_RESET BIT(14) 35 + 33 36 #define DP83TD510E_AN_STAT_1 0x60c 34 37 #define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15) 35 38 ··· 57 52 0x015b, /* 23dB =< SNR < 24dB */ 58 53 0x0000 /* 24dB =< SNR */ 59 54 }; 55 + 56 + /* Time Domain Reflectometry (TDR) Functionality of DP83TD510 PHY 57 + * 58 + * I assume that this PHY is using a variation of Spread Spectrum Time Domain 59 + * Reflectometry (SSTDR) rather than the commonly used TDR found in many PHYs. 60 + * Here are the following observations which likely confirm this: 61 + * - The DP83TD510 PHY transmits a modulated signal of configurable length 62 + * (default 16000 µs) instead of a single pulse pattern, which is typical 63 + * for traditional TDR. 64 + * - The pulse observed on the wire, triggered by the HW RESET register, is not 65 + * part of the cable testing process. 66 + * 67 + * I assume that SSTDR seems to be a logical choice for the 10BaseT1L 68 + * environment due to improved noise resistance, making it suitable for 69 + * environments with significant electrical noise, such as long 10BaseT1L cable 70 + * runs. 71 + * 72 + * Configuration Variables: 73 + * The SSTDR variation used in this PHY involves more configuration variables 74 + * that can dramatically affect the functionality and precision of cable 75 + * testing. Since most of these configuration options are either not well 76 + * documented or documented with minimal details, the following sections 77 + * describe my understanding and observations of these variables and their 78 + * impact on TDR functionality. 79 + * 80 + * Timeline: 81 + * ,<--cfg_pre_silence_time 82 + * | ,<-SSTDR Modulated Transmission 83 + * | | ,<--cfg_post_silence_time 84 + * | | | ,<--Force Link Mode 85 + * |<--'-->|<-------'------->|<--'-->|<--------'------->| 86 + * 87 + * - cfg_pre_silence_time: Optional silence time before TDR transmission starts. 88 + * - SSTDR Modulated Transmission: Transmission duration configured by 89 + * cfg_tdr_tx_duration and amplitude configured by cfg_tdr_tx_type. 90 + * - cfg_post_silence_time: Silence time after TDR transmission. 91 + * - Force Link Mode: If nothing is configured after cfg_post_silence_time, 92 + * the PHY continues in force link mode without autonegotiation. 93 + */ 94 + 95 + #define DP83TD510E_TDR_CFG 0x1e 96 + #define DP83TD510E_TDR_START BIT(15) 97 + #define DP83TD510E_TDR_DONE BIT(1) 98 + #define DP83TD510E_TDR_FAIL BIT(0) 99 + 100 + #define DP83TD510E_TDR_CFG1 0x300 101 + /* cfg_tdr_tx_type: Transmit voltage level for TDR. 102 + * 0 = 1V, 1 = 2.4V 103 + * Note: Using different voltage levels may not work 104 + * in all configuration variations. For example, setting 105 + * 2.4V may give different cable length measurements. 106 + * Other settings may be needed to make it work properly. 107 + */ 108 + #define DP83TD510E_TDR_TX_TYPE BIT(12) 109 + #define DP83TD510E_TDR_TX_TYPE_1V 0 110 + #define DP83TD510E_TDR_TX_TYPE_2_4V 1 111 + /* cfg_post_silence_time: Time after the TDR sequence. Since we force master mode 112 + * for the TDR will proceed with forced link state after this time. For Linux 113 + * it is better to set max value to avoid false link state detection. 114 + */ 115 + #define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME GENMASK(3, 2) 116 + #define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_0MS 0 117 + #define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_10MS 1 118 + #define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_100MS 2 119 + #define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_1000MS 3 120 + /* cfg_pre_silence_time: Time before the TDR sequence. It should be enough to 121 + * settle down all pulses and reflections. Since for 10BASE-T1L we have 122 + * maximum 2000m cable length, we can set it to 1ms. 123 + */ 124 + #define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME GENMASK(1, 0) 125 + #define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_0MS 0 126 + #define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_10MS 1 127 + #define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_100MS 2 128 + #define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_1000MS 3 129 + 130 + #define DP83TD510E_TDR_CFG2 0x301 131 + #define DP83TD510E_TDR_END_TAP_INDEX_1 GENMASK(14, 8) 132 + #define DP83TD510E_TDR_END_TAP_INDEX_1_DEF 36 133 + #define DP83TD510E_TDR_START_TAP_INDEX_1 GENMASK(6, 0) 134 + #define DP83TD510E_TDR_START_TAP_INDEX_1_DEF 4 135 + 136 + #define DP83TD510E_TDR_CFG3 0x302 137 + /* cfg_tdr_tx_duration: Duration of the TDR transmission in microseconds. 138 + * This value sets the duration of the modulated signal used for TDR 139 + * measurements. 140 + * - Default: 16000 µs 141 + * - Observation: A minimum duration of 6000 µs is recommended to ensure 142 + * accurate detection of cable faults. Durations shorter than 6000 µs may 143 + * result in incomplete data, especially for shorter cables (e.g., 20 meters), 144 + * leading to false "OK" results. Longer durations (e.g., 6000 µs or more) 145 + * provide better accuracy, particularly for detecting open circuits. 146 + */ 147 + #define DP83TD510E_TDR_TX_DURATION_US GENMASK(15, 0) 148 + #define DP83TD510E_TDR_TX_DURATION_US_DEF 16000 149 + 150 + #define DP83TD510E_TDR_FAULT_CFG1 0x303 151 + #define DP83TD510E_TDR_FLT_LOC_OFFSET_1 GENMASK(14, 8) 152 + #define DP83TD510E_TDR_FLT_LOC_OFFSET_1_DEF 4 153 + #define DP83TD510E_TDR_FLT_INIT_1 GENMASK(7, 0) 154 + #define DP83TD510E_TDR_FLT_INIT_1_DEF 62 155 + 156 + #define DP83TD510E_TDR_FAULT_STAT 0x30c 157 + #define DP83TD510E_TDR_PEAK_DETECT BIT(11) 158 + #define DP83TD510E_TDR_PEAK_SIGN BIT(10) 159 + #define DP83TD510E_TDR_PEAK_LOCATION GENMASK(9, 0) 160 + 161 + /* Not documented registers and values but recommended according to 162 + * "DP83TD510E Cable Diagnostics Toolkit revC" 163 + */ 164 + #define DP83TD510E_UNKN_030E 0x30e 165 + #define DP83TD510E_030E_VAL 0x2520 60 166 61 167 static int dp83td510_config_intr(struct phy_device *phydev) 62 168 { ··· 314 198 return DP83TD510_SQI_MAX; 315 199 } 316 200 201 + /** 202 + * dp83td510_cable_test_start - Start the cable test for the DP83TD510 PHY. 203 + * @phydev: Pointer to the phy_device structure. 204 + * 205 + * This sequence is implemented according to the "Application Note DP83TD510E 206 + * Cable Diagnostics Toolkit revC". 207 + * 208 + * Returns: 0 on success, a negative error code on failure. 209 + */ 210 + static int dp83td510_cable_test_start(struct phy_device *phydev) 211 + { 212 + int ret; 213 + 214 + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_CTRL, 215 + DP83TD510E_CTRL_HW_RESET); 216 + if (ret) 217 + return ret; 218 + 219 + ret = genphy_c45_an_disable_aneg(phydev); 220 + if (ret) 221 + return ret; 222 + 223 + /* Force master mode */ 224 + ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL, 225 + MDIO_PMA_PMD_BT1_CTRL_CFG_MST); 226 + if (ret) 227 + return ret; 228 + 229 + /* There is no official recommendation for this register, but it is 230 + * better to use 1V for TDR since other values seems to be optimized 231 + * for this amplitude. Except of amplitude, it is better to configure 232 + * pre TDR silence time to 10ms to avoid false reflections (value 0 233 + * seems to be too short, otherwise we need to implement own silence 234 + * time). Also, post TDR silence time should be set to 1000ms to avoid 235 + * false link state detection, it fits to the polling time of the 236 + * PHY framework. The idea is to wait until 237 + * dp83td510_cable_test_get_status() will be called and reconfigure 238 + * the PHY to the default state within the post silence time window. 239 + */ 240 + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG1, 241 + DP83TD510E_TDR_TX_TYPE | 242 + DP83TD510E_TDR_CFG1_POST_SILENCE_TIME | 243 + DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME, 244 + DP83TD510E_TDR_TX_TYPE_1V | 245 + DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_10MS | 246 + DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_1000MS); 247 + if (ret) 248 + return ret; 249 + 250 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG2, 251 + FIELD_PREP(DP83TD510E_TDR_END_TAP_INDEX_1, 252 + DP83TD510E_TDR_END_TAP_INDEX_1_DEF) | 253 + FIELD_PREP(DP83TD510E_TDR_START_TAP_INDEX_1, 254 + DP83TD510E_TDR_START_TAP_INDEX_1_DEF)); 255 + if (ret) 256 + return ret; 257 + 258 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_FAULT_CFG1, 259 + FIELD_PREP(DP83TD510E_TDR_FLT_LOC_OFFSET_1, 260 + DP83TD510E_TDR_FLT_LOC_OFFSET_1_DEF) | 261 + FIELD_PREP(DP83TD510E_TDR_FLT_INIT_1, 262 + DP83TD510E_TDR_FLT_INIT_1_DEF)); 263 + if (ret) 264 + return ret; 265 + 266 + /* Undocumented register, from the "Application Note DP83TD510E Cable 267 + * Diagnostics Toolkit revC". 268 + */ 269 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_UNKN_030E, 270 + DP83TD510E_030E_VAL); 271 + if (ret) 272 + return ret; 273 + 274 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG3, 275 + DP83TD510E_TDR_TX_DURATION_US_DEF); 276 + if (ret) 277 + return ret; 278 + 279 + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_CTRL, 280 + DP83TD510E_CTRL_SW_RESET); 281 + if (ret) 282 + return ret; 283 + 284 + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG, 285 + DP83TD510E_TDR_START); 286 + } 287 + 288 + /** 289 + * dp83td510_cable_test_get_status - Get the status of the cable test for the 290 + * DP83TD510 PHY. 291 + * @phydev: Pointer to the phy_device structure. 292 + * @finished: Pointer to a boolean that indicates whether the test is finished. 293 + * 294 + * The function sets the @finished flag to true if the test is complete. 295 + * 296 + * Returns: 0 on success or a negative error code on failure. 297 + */ 298 + static int dp83td510_cable_test_get_status(struct phy_device *phydev, 299 + bool *finished) 300 + { 301 + int ret, stat; 302 + 303 + *finished = false; 304 + 305 + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG); 306 + if (ret < 0) 307 + return ret; 308 + 309 + if (!(ret & DP83TD510E_TDR_DONE)) 310 + return 0; 311 + 312 + if (!(ret & DP83TD510E_TDR_FAIL)) { 313 + int location; 314 + 315 + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 316 + DP83TD510E_TDR_FAULT_STAT); 317 + if (ret < 0) 318 + return ret; 319 + 320 + if (ret & DP83TD510E_TDR_PEAK_DETECT) { 321 + if (ret & DP83TD510E_TDR_PEAK_SIGN) 322 + stat = ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 323 + else 324 + stat = ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 325 + 326 + location = FIELD_GET(DP83TD510E_TDR_PEAK_LOCATION, 327 + ret) * 100; 328 + ethnl_cable_test_fault_length(phydev, 329 + ETHTOOL_A_CABLE_PAIR_A, 330 + location); 331 + } else { 332 + stat = ETHTOOL_A_CABLE_RESULT_CODE_OK; 333 + } 334 + } else { 335 + /* Most probably we have active link partner */ 336 + stat = ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 337 + } 338 + 339 + *finished = true; 340 + 341 + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, stat); 342 + 343 + return phy_init_hw(phydev); 344 + } 345 + 317 346 static int dp83td510_get_features(struct phy_device *phydev) 318 347 { 319 348 /* This PHY can't respond on MDIO bus if no RMII clock is enabled. ··· 482 221 PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID), 483 222 .name = "TI DP83TD510E", 484 223 224 + .flags = PHY_POLL_CABLE_TEST, 485 225 .config_aneg = dp83td510_config_aneg, 486 226 .read_status = dp83td510_read_status, 487 227 .get_features = dp83td510_get_features, ··· 490 228 .handle_interrupt = dp83td510_handle_interrupt, 491 229 .get_sqi = dp83td510_get_sqi, 492 230 .get_sqi_max = dp83td510_get_sqi_max, 231 + .cable_test_start = dp83td510_cable_test_start, 232 + .cable_test_get_status = dp83td510_cable_test_get_status, 493 233 494 234 .suspend = genphy_suspend, 495 235 .resume = genphy_resume,