generic: copy backport, hack, pending patch and config from 6.1 to 6.6
[openwrt/openwrt.git] / target / linux / generic / backport-6.6 / 712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch
1 From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
2 From: Christian Marangi <ansuelsmth@gmail.com>
3 Date: Thu, 25 Jan 2024 21:37:01 +0100
4 Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x
5
6 Add LED support for QCA8081 PHY.
7
8 Documentation for this LEDs PHY is very scarce even with NDA access
9 to Documentation for OEMs. Only the blink pattern are documented and are
10 very confusing most of the time. No documentation is present about
11 forcing the LED on/off or to always blink.
12
13 Those settings were reversed by poking the regs and trying to find the
14 correct bits to trigger these modes. Some bits mode are not clear and
15 maybe the documentation option are not 100% correct. For the sake of LED
16 support the reversed option are enough to add support for current LED
17 APIs.
18
19 Supported HW control modes are:
20 - tx
21 - rx
22 - link_10
23 - link_100
24 - link_1000
25 - link_2500
26 - half_duplex
27 - full_duplex
28
29 Also add support for LED polarity set to set LED polarity to active
30 high or low. QSDK sets this value to high by default but PHY reset value
31 doesn't have this enabled by default.
32
33 QSDK also sets 2 additional bits but their usage is not clear, info about
34 this is added in the header. It was verified that for correct function
35 of the LED if active high is needed, only BIT 6 is needed.
36
37 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
38 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
39 Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com
40 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
41 ---
42 drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++
43 1 file changed, 327 insertions(+)
44
45 --- a/drivers/net/phy/at803x.c
46 +++ b/drivers/net/phy/at803x.c
47 @@ -301,6 +301,87 @@
48 /* Added for reference of existence but should be handled by wait_for_completion already */
49 #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
50
51 +#define QCA808X_MMD7_LED_GLOBAL 0x8073
52 +#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
53 +#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
54 +/* Values are the same for both BLINK_1 and BLINK_2 */
55 +#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
56 +#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
57 +#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
58 +#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
59 +#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
60 +#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
61 +#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
62 +#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
63 +#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
64 +#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
65 +#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
66 +#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
67 +#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
68 +#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
69 +#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
70 +#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
71 +#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
72 +#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
73 +
74 +#define QCA808X_MMD7_LED2_CTRL 0x8074
75 +#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
76 +#define QCA808X_MMD7_LED1_CTRL 0x8076
77 +#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
78 +#define QCA808X_MMD7_LED0_CTRL 0x8078
79 +#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
80 +
81 +/* LED hw control pattern is the same for every LED */
82 +#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
83 +#define QCA808X_LED_SPEED2500_ON BIT(15)
84 +#define QCA808X_LED_SPEED2500_BLINK BIT(14)
85 +/* Follow blink trigger even if duplex or speed condition doesn't match */
86 +#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
87 +#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
88 +#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
89 +#define QCA808X_LED_TX_BLINK BIT(10)
90 +#define QCA808X_LED_RX_BLINK BIT(9)
91 +#define QCA808X_LED_TX_ON_10MS BIT(8)
92 +#define QCA808X_LED_RX_ON_10MS BIT(7)
93 +#define QCA808X_LED_SPEED1000_ON BIT(6)
94 +#define QCA808X_LED_SPEED100_ON BIT(5)
95 +#define QCA808X_LED_SPEED10_ON BIT(4)
96 +#define QCA808X_LED_COLLISION_BLINK BIT(3)
97 +#define QCA808X_LED_SPEED1000_BLINK BIT(2)
98 +#define QCA808X_LED_SPEED100_BLINK BIT(1)
99 +#define QCA808X_LED_SPEED10_BLINK BIT(0)
100 +
101 +#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
102 +#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
103 +
104 +/* LED force ctrl is the same for every LED
105 + * No documentation exist for this, not even internal one
106 + * with NDA as QCOM gives only info about configuring
107 + * hw control pattern rules and doesn't indicate any way
108 + * to force the LED to specific mode.
109 + * These define comes from reverse and testing and maybe
110 + * lack of some info or some info are not entirely correct.
111 + * For the basic LED control and hw control these finding
112 + * are enough to support LED control in all the required APIs.
113 + *
114 + * On doing some comparison with implementation with qca807x,
115 + * it was found that it's 1:1 equal to it and confirms all the
116 + * reverse done. It was also found further specification with the
117 + * force mode and the blink modes.
118 + */
119 +#define QCA808X_LED_FORCE_EN BIT(15)
120 +#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
121 +#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
122 +#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
123 +#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
124 +#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
125 +
126 +#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
127 +/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
128 + * LED to active high. It's not clear what BIT 3 and BIT 4 does.
129 + */
130 +#define QCA808X_LED_ACTIVE_HIGH BIT(6)
131 +
132 /* QCA808X 1G chip type */
133 #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
134 #define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
135 @@ -346,6 +427,7 @@ struct at803x_priv {
136 struct regulator_dev *vddio_rdev;
137 struct regulator_dev *vddh_rdev;
138 u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
139 + int led_polarity_mode;
140 };
141
142 struct at803x_context {
143 @@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic
144 if (!priv)
145 return -ENOMEM;
146
147 + /* Init LED polarity mode to -1 */
148 + priv->led_polarity_mode = -1;
149 +
150 phydev->priv = priv;
151
152 ret = at803x_parse_dt(phydev);
153 @@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s
154 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
155 }
156
157 +static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
158 + u16 *offload_trigger)
159 +{
160 + /* Parsing specific to netdev trigger */
161 + if (test_bit(TRIGGER_NETDEV_TX, &rules))
162 + *offload_trigger |= QCA808X_LED_TX_BLINK;
163 + if (test_bit(TRIGGER_NETDEV_RX, &rules))
164 + *offload_trigger |= QCA808X_LED_RX_BLINK;
165 + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
166 + *offload_trigger |= QCA808X_LED_SPEED10_ON;
167 + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
168 + *offload_trigger |= QCA808X_LED_SPEED100_ON;
169 + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
170 + *offload_trigger |= QCA808X_LED_SPEED1000_ON;
171 + if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
172 + *offload_trigger |= QCA808X_LED_SPEED2500_ON;
173 + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
174 + *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
175 + if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
176 + *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
177 +
178 + if (rules && !*offload_trigger)
179 + return -EOPNOTSUPP;
180 +
181 + /* Enable BLINK_CHECK_BYPASS by default to make the LED
182 + * blink even with duplex or speed mode not enabled.
183 + */
184 + *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
185 +
186 + return 0;
187 +}
188 +
189 +static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
190 +{
191 + u16 reg;
192 +
193 + if (index > 2)
194 + return -EINVAL;
195 +
196 + reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
197 +
198 + return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
199 + QCA808X_LED_FORCE_EN);
200 +}
201 +
202 +static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
203 + unsigned long rules)
204 +{
205 + u16 offload_trigger = 0;
206 +
207 + if (index > 2)
208 + return -EINVAL;
209 +
210 + return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
211 +}
212 +
213 +static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
214 + unsigned long rules)
215 +{
216 + u16 reg, offload_trigger = 0;
217 + int ret;
218 +
219 + if (index > 2)
220 + return -EINVAL;
221 +
222 + reg = QCA808X_MMD7_LED_CTRL(index);
223 +
224 + ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
225 + if (ret)
226 + return ret;
227 +
228 + ret = qca808x_led_hw_control_enable(phydev, index);
229 + if (ret)
230 + return ret;
231 +
232 + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
233 + QCA808X_LED_PATTERN_MASK,
234 + offload_trigger);
235 +}
236 +
237 +static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
238 +{
239 + u16 reg;
240 + int val;
241 +
242 + if (index > 2)
243 + return false;
244 +
245 + reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
246 +
247 + val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
248 +
249 + return !(val & QCA808X_LED_FORCE_EN);
250 +}
251 +
252 +static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
253 + unsigned long *rules)
254 +{
255 + u16 reg;
256 + int val;
257 +
258 + if (index > 2)
259 + return -EINVAL;
260 +
261 + /* Check if we have hw control enabled */
262 + if (qca808x_led_hw_control_status(phydev, index))
263 + return -EINVAL;
264 +
265 + reg = QCA808X_MMD7_LED_CTRL(index);
266 +
267 + val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
268 + if (val & QCA808X_LED_TX_BLINK)
269 + set_bit(TRIGGER_NETDEV_TX, rules);
270 + if (val & QCA808X_LED_RX_BLINK)
271 + set_bit(TRIGGER_NETDEV_RX, rules);
272 + if (val & QCA808X_LED_SPEED10_ON)
273 + set_bit(TRIGGER_NETDEV_LINK_10, rules);
274 + if (val & QCA808X_LED_SPEED100_ON)
275 + set_bit(TRIGGER_NETDEV_LINK_100, rules);
276 + if (val & QCA808X_LED_SPEED1000_ON)
277 + set_bit(TRIGGER_NETDEV_LINK_1000, rules);
278 + if (val & QCA808X_LED_SPEED2500_ON)
279 + set_bit(TRIGGER_NETDEV_LINK_2500, rules);
280 + if (val & QCA808X_LED_HALF_DUPLEX_ON)
281 + set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
282 + if (val & QCA808X_LED_FULL_DUPLEX_ON)
283 + set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
284 +
285 + return 0;
286 +}
287 +
288 +static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
289 +{
290 + u16 reg;
291 +
292 + if (index > 2)
293 + return -EINVAL;
294 +
295 + reg = QCA808X_MMD7_LED_CTRL(index);
296 +
297 + return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
298 + QCA808X_LED_PATTERN_MASK);
299 +}
300 +
301 +static int qca808x_led_brightness_set(struct phy_device *phydev,
302 + u8 index, enum led_brightness value)
303 +{
304 + u16 reg;
305 + int ret;
306 +
307 + if (index > 2)
308 + return -EINVAL;
309 +
310 + if (!value) {
311 + ret = qca808x_led_hw_control_reset(phydev, index);
312 + if (ret)
313 + return ret;
314 + }
315 +
316 + reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
317 +
318 + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
319 + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
320 + QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
321 + QCA808X_LED_FORCE_OFF);
322 +}
323 +
324 +static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
325 + unsigned long *delay_on,
326 + unsigned long *delay_off)
327 +{
328 + int ret;
329 + u16 reg;
330 +
331 + if (index > 2)
332 + return -EINVAL;
333 +
334 + reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
335 +
336 + /* Set blink to 50% off, 50% on at 4Hz by default */
337 + ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
338 + QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
339 + QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
340 + if (ret)
341 + return ret;
342 +
343 + /* We use BLINK_1 for normal blinking */
344 + ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
345 + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
346 + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
347 + if (ret)
348 + return ret;
349 +
350 + /* We set blink to 4Hz, aka 250ms */
351 + *delay_on = 250 / 2;
352 + *delay_off = 250 / 2;
353 +
354 + return 0;
355 +}
356 +
357 +static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
358 + unsigned long modes)
359 +{
360 + struct at803x_priv *priv = phydev->priv;
361 + bool active_low = false;
362 + u32 mode;
363 +
364 + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
365 + switch (mode) {
366 + case PHY_LED_ACTIVE_LOW:
367 + active_low = true;
368 + break;
369 + default:
370 + return -EINVAL;
371 + }
372 + }
373 +
374 + /* PHY polarity is global and can't be set per LED.
375 + * To detect this, check if last requested polarity mode
376 + * match the new one.
377 + */
378 + if (priv->led_polarity_mode >= 0 &&
379 + priv->led_polarity_mode != active_low) {
380 + phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
381 + return -EINVAL;
382 + }
383 +
384 + /* Save the last PHY polarity mode */
385 + priv->led_polarity_mode = active_low;
386 +
387 + return phy_modify_mmd(phydev, MDIO_MMD_AN,
388 + QCA808X_MMD7_LED_POLARITY_CTRL,
389 + QCA808X_LED_ACTIVE_HIGH,
390 + active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
391 +}
392 +
393 static struct phy_driver at803x_driver[] = {
394 {
395 /* Qualcomm Atheros AR8035 */
396 @@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[]
397 .cable_test_start = qca808x_cable_test_start,
398 .cable_test_get_status = qca808x_cable_test_get_status,
399 .link_change_notify = qca808x_link_change_notify,
400 + .led_brightness_set = qca808x_led_brightness_set,
401 + .led_blink_set = qca808x_led_blink_set,
402 + .led_hw_is_supported = qca808x_led_hw_is_supported,
403 + .led_hw_control_set = qca808x_led_hw_control_set,
404 + .led_hw_control_get = qca808x_led_hw_control_get,
405 + .led_polarity_set = qca808x_led_polarity_set,
406 }, };
407
408 module_phy_driver(at803x_driver);