1 From 61958b33ef0bab1c1874c933cd3910f495526782 Mon Sep 17 00:00:00 2001
2 From: Issam Hamdi <ih@simonwunderlich.de>
3 Date: Fri, 24 Oct 2025 11:49:00 +0200
4 Subject: [PATCH] net: phy: realtek: Add RTL8224 cable testing support
6 The RTL8224 can detect open pairs and short types (in same pair or some
7 other pair). The distance to this problem can be estimated. This is done
8 for each of the 4 pairs separately.
10 It is not meant to be run while there is an active link partner because
11 this interferes with the active test pulses.
13 Output with open 50 m cable:
15 Pair A code Open Circuit, source: TDR
16 Pair A, fault length: 51.79m, source: TDR
17 Pair B code Open Circuit, source: TDR
18 Pair B, fault length: 51.28m, source: TDR
19 Pair C code Open Circuit, source: TDR
20 Pair C, fault length: 50.46m, source: TDR
21 Pair D code Open Circuit, source: TDR
22 Pair D, fault length: 51.12m, source: TDR
26 Pair A code OK, source: TDR
27 Pair B code OK, source: TDR
28 Pair C code OK, source: TDR
29 Pair D code OK, source: TDR
31 Shorted cable (both short types are at roughly the same distance)
33 Pair A code Short to another pair, source: TDR
34 Pair A, fault length: 2.35m, source: TDR
35 Pair B code Short to another pair, source: TDR
36 Pair B, fault length: 2.15m, source: TDR
37 Pair C code OK, source: TDR
38 Pair D code Short within Pair, source: TDR
39 Pair D, fault length: 1.94m, source: TDR
41 Signed-off-by: Issam Hamdi <ih@simonwunderlich.de>
42 Co-developed-by: Sven Eckelmann <se@simonwunderlich.de>
43 Signed-off-by: Sven Eckelmann <se@simonwunderlich.de>
44 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
45 Link: https://patch.msgid.link/20251024-rtl8224-cable-test-v1-1-e3cda89ac98f@simonwunderlich.de
46 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
48 drivers/net/phy/realtek/realtek_main.c | 187 +++++++++++++++++++++++++
49 1 file changed, 187 insertions(+)
51 --- a/drivers/net/phy/realtek/realtek_main.c
52 +++ b/drivers/net/phy/realtek/realtek_main.c
54 * Copyright (c) 2004 Freescale Semiconductor, Inc.
56 #include <linux/bitops.h>
57 +#include <linux/ethtool_netlink.h>
59 #include <linux/phy.h>
60 #include <linux/netdevice.h>
63 #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
65 +#define RTL8224_MII_RTCT 0x11
66 +#define RTL8224_MII_RTCT_ENABLE BIT(0)
67 +#define RTL8224_MII_RTCT_PAIR_A BIT(4)
68 +#define RTL8224_MII_RTCT_PAIR_B BIT(5)
69 +#define RTL8224_MII_RTCT_PAIR_C BIT(6)
70 +#define RTL8224_MII_RTCT_PAIR_D BIT(7)
71 +#define RTL8224_MII_RTCT_DONE BIT(15)
73 +#define RTL8224_MII_SRAM_ADDR 0x1b
74 +#define RTL8224_MII_SRAM_DATA 0x1c
76 +#define RTL8224_SRAM_RTCT_FAULT(pair) (0x8026 + (pair) * 4)
77 +#define RTL8224_SRAM_RTCT_FAULT_BUSY BIT(0)
78 +#define RTL8224_SRAM_RTCT_FAULT_OPEN BIT(3)
79 +#define RTL8224_SRAM_RTCT_FAULT_SAME_SHORT BIT(4)
80 +#define RTL8224_SRAM_RTCT_FAULT_OK BIT(5)
81 +#define RTL8224_SRAM_RTCT_FAULT_DONE BIT(6)
82 +#define RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT BIT(7)
84 +#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4)
86 #define RTL8366RB_POWER_SAVE 0x15
87 #define RTL8366RB_POWER_SAVE_ON BIT(12)
89 @@ -1317,6 +1339,168 @@ static int rtl822xb_c45_read_status(stru
93 +static int rtl8224_cable_test_start(struct phy_device *phydev)
98 + /* disable auto-negotiation and force 1000/Full */
99 + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
100 + RTL822X_VND2_C22_REG(MII_BMCR),
101 + BMCR_ANENABLE | BMCR_SPEED100 | BMCR_SPEED10,
102 + BMCR_SPEED1000 | BMCR_FULLDPLX);
108 + /* trigger cable test */
109 + val = RTL8224_MII_RTCT_ENABLE;
110 + val |= RTL8224_MII_RTCT_PAIR_A;
111 + val |= RTL8224_MII_RTCT_PAIR_B;
112 + val |= RTL8224_MII_RTCT_PAIR_C;
113 + val |= RTL8224_MII_RTCT_PAIR_D;
115 + return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
116 + RTL822X_VND2_C22_REG(RTL8224_MII_RTCT),
117 + RTL8224_MII_RTCT_DONE, val);
120 +static int rtl8224_sram_read(struct phy_device *phydev, u32 reg)
124 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
125 + RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_ADDR),
130 + return phy_read_mmd(phydev, MDIO_MMD_VEND2,
131 + RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_DATA));
134 +static int rtl8224_pair_len_get(struct phy_device *phydev, u32 pair)
141 + reg_len = RTL8224_SRAM_RTCT_LEN(pair);
143 + ret = rtl8224_sram_read(phydev, reg_len);
147 + cable_len = ret & 0xff00;
149 + ret = rtl8224_sram_read(phydev, reg_len + 1);
153 + cable_len |= (ret & 0xff00) >> 8;
156 + cable_len = max(cable_len, 0);
158 + cm = cable_len * 100 / 78;
163 +static int rtl8224_cable_test_result_trans(u32 result)
165 + if (!(result & RTL8224_SRAM_RTCT_FAULT_DONE))
168 + if (result & RTL8224_SRAM_RTCT_FAULT_OK)
169 + return ETHTOOL_A_CABLE_RESULT_CODE_OK;
171 + if (result & RTL8224_SRAM_RTCT_FAULT_OPEN)
172 + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
174 + if (result & RTL8224_SRAM_RTCT_FAULT_SAME_SHORT)
175 + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
177 + if (result & RTL8224_SRAM_RTCT_FAULT_BUSY)
178 + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
180 + if (result & RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT)
181 + return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
183 + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
186 +static int rtl8224_cable_test_report_pair(struct phy_device *phydev, unsigned int pair)
191 + ret = rtl8224_sram_read(phydev, RTL8224_SRAM_RTCT_FAULT(pair));
195 + fault_rslt = rtl8224_cable_test_result_trans(ret);
196 + if (fault_rslt < 0)
199 + ret = ethnl_cable_test_result(phydev, pair, fault_rslt);
203 + switch (fault_rslt) {
204 + case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
205 + case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
206 + case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
207 + ret = rtl8224_pair_len_get(phydev, pair);
211 + return ethnl_cable_test_fault_length(phydev, pair, ret);
217 +static int rtl8224_cable_test_report(struct phy_device *phydev, bool *finished)
222 + for (pair = ETHTOOL_A_CABLE_PAIR_A; pair <= ETHTOOL_A_CABLE_PAIR_D; pair++) {
223 + ret = rtl8224_cable_test_report_pair(phydev, pair);
224 + if (ret == -EBUSY) {
236 +static int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished)
242 + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
243 + RTL822X_VND2_C22_REG(RTL8224_MII_RTCT));
247 + if (!(ret & RTL8224_MII_RTCT_DONE))
252 + return rtl8224_cable_test_report(phydev, finished);
255 static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
258 @@ -1794,11 +1978,14 @@ static struct phy_driver realtek_drvs[]
260 PHY_ID_MATCH_EXACT(0x001ccad0),
261 .name = "RTL8224 2.5Gbps PHY",
262 + .flags = PHY_POLL_CABLE_TEST,
263 .get_features = rtl822x_c45_get_features,
264 .config_aneg = rtl822x_c45_config_aneg,
265 .read_status = rtl822x_c45_read_status,
266 .suspend = genphy_c45_pma_suspend,
267 .resume = rtlgen_c45_resume,
268 + .cable_test_start = rtl8224_cable_test_start,
269 + .cable_test_get_status = rtl8224_cable_test_get_status,
271 PHY_ID_MATCH_EXACT(0x001cc961),
272 .name = "RTL8366RB Gigabit Ethernet",