ffdb02c12df8f83142f259ee2f6ba3a9593fda09
[openwrt/staging/xback.git] /
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
5
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.
9
10 It is not meant to be run while there is an active link partner because
11 this interferes with the active test pulses.
12
13 Output with open 50 m cable:
14
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
23
24 Terminated cable:
25
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
30
31 Shorted cable (both short types are at roughly the same distance)
32
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
40
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>
47 ---
48 drivers/net/phy/realtek/realtek_main.c | 187 +++++++++++++++++++++++++
49 1 file changed, 187 insertions(+)
50
51 --- a/drivers/net/phy/realtek/realtek_main.c
52 +++ b/drivers/net/phy/realtek/realtek_main.c
53 @@ -8,6 +8,7 @@
54 * Copyright (c) 2004 Freescale Semiconductor, Inc.
55 */
56 #include <linux/bitops.h>
57 +#include <linux/ethtool_netlink.h>
58 #include <linux/of.h>
59 #include <linux/phy.h>
60 #include <linux/netdevice.h>
61 @@ -129,6 +130,27 @@
62 */
63 #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
64
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)
72 +
73 +#define RTL8224_MII_SRAM_ADDR 0x1b
74 +#define RTL8224_MII_SRAM_DATA 0x1c
75 +
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)
83 +
84 +#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4)
85 +
86 #define RTL8366RB_POWER_SAVE 0x15
87 #define RTL8366RB_POWER_SAVE_ON BIT(12)
88
89 @@ -1317,6 +1339,168 @@ static int rtl822xb_c45_read_status(stru
90 return 0;
91 }
92
93 +static int rtl8224_cable_test_start(struct phy_device *phydev)
94 +{
95 + u32 val;
96 + int ret;
97 +
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);
103 + if (ret)
104 + return ret;
105 +
106 + mdelay(500);
107 +
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;
114 +
115 + return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
116 + RTL822X_VND2_C22_REG(RTL8224_MII_RTCT),
117 + RTL8224_MII_RTCT_DONE, val);
118 +}
119 +
120 +static int rtl8224_sram_read(struct phy_device *phydev, u32 reg)
121 +{
122 + int ret;
123 +
124 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
125 + RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_ADDR),
126 + reg);
127 + if (ret)
128 + return ret;
129 +
130 + return phy_read_mmd(phydev, MDIO_MMD_VEND2,
131 + RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_DATA));
132 +}
133 +
134 +static int rtl8224_pair_len_get(struct phy_device *phydev, u32 pair)
135 +{
136 + int cable_len;
137 + u32 reg_len;
138 + int ret;
139 + u32 cm;
140 +
141 + reg_len = RTL8224_SRAM_RTCT_LEN(pair);
142 +
143 + ret = rtl8224_sram_read(phydev, reg_len);
144 + if (ret < 0)
145 + return ret;
146 +
147 + cable_len = ret & 0xff00;
148 +
149 + ret = rtl8224_sram_read(phydev, reg_len + 1);
150 + if (ret < 0)
151 + return ret;
152 +
153 + cable_len |= (ret & 0xff00) >> 8;
154 +
155 + cable_len -= 620;
156 + cable_len = max(cable_len, 0);
157 +
158 + cm = cable_len * 100 / 78;
159 +
160 + return cm;
161 +}
162 +
163 +static int rtl8224_cable_test_result_trans(u32 result)
164 +{
165 + if (!(result & RTL8224_SRAM_RTCT_FAULT_DONE))
166 + return -EBUSY;
167 +
168 + if (result & RTL8224_SRAM_RTCT_FAULT_OK)
169 + return ETHTOOL_A_CABLE_RESULT_CODE_OK;
170 +
171 + if (result & RTL8224_SRAM_RTCT_FAULT_OPEN)
172 + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
173 +
174 + if (result & RTL8224_SRAM_RTCT_FAULT_SAME_SHORT)
175 + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
176 +
177 + if (result & RTL8224_SRAM_RTCT_FAULT_BUSY)
178 + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
179 +
180 + if (result & RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT)
181 + return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
182 +
183 + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
184 +}
185 +
186 +static int rtl8224_cable_test_report_pair(struct phy_device *phydev, unsigned int pair)
187 +{
188 + int fault_rslt;
189 + int ret;
190 +
191 + ret = rtl8224_sram_read(phydev, RTL8224_SRAM_RTCT_FAULT(pair));
192 + if (ret < 0)
193 + return ret;
194 +
195 + fault_rslt = rtl8224_cable_test_result_trans(ret);
196 + if (fault_rslt < 0)
197 + return 0;
198 +
199 + ret = ethnl_cable_test_result(phydev, pair, fault_rslt);
200 + if (ret < 0)
201 + return ret;
202 +
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);
208 + if (ret < 0)
209 + return ret;
210 +
211 + return ethnl_cable_test_fault_length(phydev, pair, ret);
212 + default:
213 + return 0;
214 + }
215 +}
216 +
217 +static int rtl8224_cable_test_report(struct phy_device *phydev, bool *finished)
218 +{
219 + unsigned int pair;
220 + int ret;
221 +
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) {
225 + *finished = false;
226 + return 0;
227 + }
228 +
229 + if (ret < 0)
230 + return ret;
231 + }
232 +
233 + return 0;
234 +}
235 +
236 +static int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished)
237 +{
238 + int ret;
239 +
240 + *finished = false;
241 +
242 + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
243 + RTL822X_VND2_C22_REG(RTL8224_MII_RTCT));
244 + if (ret < 0)
245 + return ret;
246 +
247 + if (!(ret & RTL8224_MII_RTCT_DONE))
248 + return 0;
249 +
250 + *finished = true;
251 +
252 + return rtl8224_cable_test_report(phydev, finished);
253 +}
254 +
255 static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
256 {
257 int val;
258 @@ -1794,11 +1978,14 @@ static struct phy_driver realtek_drvs[]
259 }, {
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,
270 }, {
271 PHY_ID_MATCH_EXACT(0x001cc961),
272 .name = "RTL8366RB Gigabit Ethernet",