1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/property.h> |
3 | #include <linux/regmap.h> |
4 | #include <net/dsa.h> |
5 | |
6 | #include "qca8k.h" |
7 | #include "qca8k_leds.h" |
8 | |
9 | static u32 qca8k_phy_to_port(int phy) |
10 | { |
11 | /* Internal PHY 0 has port at index 1. |
12 | * Internal PHY 1 has port at index 2. |
13 | * Internal PHY 2 has port at index 3. |
14 | * Internal PHY 3 has port at index 4. |
15 | * Internal PHY 4 has port at index 5. |
16 | */ |
17 | |
18 | return phy + 1; |
19 | } |
20 | |
21 | static int |
22 | qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info) |
23 | { |
24 | switch (port_num) { |
25 | case 0: |
26 | reg_info->reg = QCA8K_LED_CTRL_REG(led_num); |
27 | reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT; |
28 | break; |
29 | case 1: |
30 | case 2: |
31 | case 3: |
32 | /* Port 123 are controlled on a different reg */ |
33 | reg_info->reg = QCA8K_LED_CTRL3_REG; |
34 | reg_info->shift = QCA8K_LED_PHY123_PATTERN_EN_SHIFT(port_num, led_num); |
35 | break; |
36 | case 4: |
37 | reg_info->reg = QCA8K_LED_CTRL_REG(led_num); |
38 | reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT; |
39 | break; |
40 | default: |
41 | return -EINVAL; |
42 | } |
43 | |
44 | return 0; |
45 | } |
46 | |
47 | static int |
48 | qca8k_get_control_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info) |
49 | { |
50 | reg_info->reg = QCA8K_LED_CTRL_REG(led_num); |
51 | |
52 | /* 6 total control rule: |
53 | * 3 control rules for phy0-3 that applies to all their leds |
54 | * 3 control rules for phy4 |
55 | */ |
56 | if (port_num == 4) |
57 | reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT; |
58 | else |
59 | reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT; |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | static int |
65 | qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger) |
66 | { |
67 | /* Parsing specific to netdev trigger */ |
68 | if (test_bit(TRIGGER_NETDEV_TX, &rules)) |
69 | *offload_trigger |= QCA8K_LED_TX_BLINK_MASK; |
70 | if (test_bit(TRIGGER_NETDEV_RX, &rules)) |
71 | *offload_trigger |= QCA8K_LED_RX_BLINK_MASK; |
72 | if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) |
73 | *offload_trigger |= QCA8K_LED_LINK_10M_EN_MASK; |
74 | if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) |
75 | *offload_trigger |= QCA8K_LED_LINK_100M_EN_MASK; |
76 | if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) |
77 | *offload_trigger |= QCA8K_LED_LINK_1000M_EN_MASK; |
78 | if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) |
79 | *offload_trigger |= QCA8K_LED_HALF_DUPLEX_MASK; |
80 | if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) |
81 | *offload_trigger |= QCA8K_LED_FULL_DUPLEX_MASK; |
82 | |
83 | if (rules && !*offload_trigger) |
84 | return -EOPNOTSUPP; |
85 | |
86 | /* Enable some default rule by default to the requested mode: |
87 | * - Blink at 4Hz by default |
88 | */ |
89 | *offload_trigger |= QCA8K_LED_BLINK_4HZ; |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | static int |
95 | qca8k_led_brightness_set(struct qca8k_led *led, |
96 | enum led_brightness brightness) |
97 | { |
98 | struct qca8k_led_pattern_en reg_info; |
99 | struct qca8k_priv *priv = led->priv; |
100 | u32 mask, val; |
101 | |
102 | qca8k_get_enable_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
103 | |
104 | val = QCA8K_LED_ALWAYS_OFF; |
105 | if (brightness) |
106 | val = QCA8K_LED_ALWAYS_ON; |
107 | |
108 | /* HW regs to control brightness is special and port 1-2-3 |
109 | * are placed in a different reg. |
110 | * |
111 | * To control port 0 brightness: |
112 | * - the 2 bit (15, 14) of: |
113 | * - QCA8K_LED_CTRL0_REG for led1 |
114 | * - QCA8K_LED_CTRL1_REG for led2 |
115 | * - QCA8K_LED_CTRL2_REG for led3 |
116 | * |
117 | * To control port 4: |
118 | * - the 2 bit (31, 30) of: |
119 | * - QCA8K_LED_CTRL0_REG for led1 |
120 | * - QCA8K_LED_CTRL1_REG for led2 |
121 | * - QCA8K_LED_CTRL2_REG for led3 |
122 | * |
123 | * To control port 1: |
124 | * - the 2 bit at (9, 8) of QCA8K_LED_CTRL3_REG are used for led1 |
125 | * - the 2 bit at (11, 10) of QCA8K_LED_CTRL3_REG are used for led2 |
126 | * - the 2 bit at (13, 12) of QCA8K_LED_CTRL3_REG are used for led3 |
127 | * |
128 | * To control port 2: |
129 | * - the 2 bit at (15, 14) of QCA8K_LED_CTRL3_REG are used for led1 |
130 | * - the 2 bit at (17, 16) of QCA8K_LED_CTRL3_REG are used for led2 |
131 | * - the 2 bit at (19, 18) of QCA8K_LED_CTRL3_REG are used for led3 |
132 | * |
133 | * To control port 3: |
134 | * - the 2 bit at (21, 20) of QCA8K_LED_CTRL3_REG are used for led1 |
135 | * - the 2 bit at (23, 22) of QCA8K_LED_CTRL3_REG are used for led2 |
136 | * - the 2 bit at (25, 24) of QCA8K_LED_CTRL3_REG are used for led3 |
137 | * |
138 | * To abstract this and have less code, we use the port and led numm |
139 | * to calculate the shift and the correct reg due to this problem of |
140 | * not having a 1:1 map of LED with the regs. |
141 | */ |
142 | if (led->port_num == 0 || led->port_num == 4) { |
143 | mask = QCA8K_LED_PATTERN_EN_MASK; |
144 | val <<= QCA8K_LED_PATTERN_EN_SHIFT; |
145 | } else { |
146 | mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; |
147 | } |
148 | |
149 | return regmap_update_bits(map: priv->regmap, reg: reg_info.reg, |
150 | mask: mask << reg_info.shift, |
151 | val: val << reg_info.shift); |
152 | } |
153 | |
154 | static int |
155 | qca8k_cled_brightness_set_blocking(struct led_classdev *ldev, |
156 | enum led_brightness brightness) |
157 | { |
158 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
159 | |
160 | return qca8k_led_brightness_set(led, brightness); |
161 | } |
162 | |
163 | static enum led_brightness |
164 | qca8k_led_brightness_get(struct qca8k_led *led) |
165 | { |
166 | struct qca8k_led_pattern_en reg_info; |
167 | struct qca8k_priv *priv = led->priv; |
168 | u32 val; |
169 | int ret; |
170 | |
171 | qca8k_get_enable_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
172 | |
173 | ret = regmap_read(map: priv->regmap, reg: reg_info.reg, val: &val); |
174 | if (ret) |
175 | return 0; |
176 | |
177 | val >>= reg_info.shift; |
178 | |
179 | if (led->port_num == 0 || led->port_num == 4) { |
180 | val &= QCA8K_LED_PATTERN_EN_MASK; |
181 | val >>= QCA8K_LED_PATTERN_EN_SHIFT; |
182 | } else { |
183 | val &= QCA8K_LED_PHY123_PATTERN_EN_MASK; |
184 | } |
185 | |
186 | /* Assume brightness ON only when the LED is set to always ON */ |
187 | return val == QCA8K_LED_ALWAYS_ON; |
188 | } |
189 | |
190 | static int |
191 | qca8k_cled_blink_set(struct led_classdev *ldev, |
192 | unsigned long *delay_on, |
193 | unsigned long *delay_off) |
194 | { |
195 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
196 | u32 mask, val = QCA8K_LED_ALWAYS_BLINK_4HZ; |
197 | struct qca8k_led_pattern_en reg_info; |
198 | struct qca8k_priv *priv = led->priv; |
199 | |
200 | if (*delay_on == 0 && *delay_off == 0) { |
201 | *delay_on = 125; |
202 | *delay_off = 125; |
203 | } |
204 | |
205 | if (*delay_on != 125 || *delay_off != 125) { |
206 | /* The hardware only supports blinking at 4Hz. Fall back |
207 | * to software implementation in other cases. |
208 | */ |
209 | return -EINVAL; |
210 | } |
211 | |
212 | qca8k_get_enable_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
213 | |
214 | if (led->port_num == 0 || led->port_num == 4) { |
215 | mask = QCA8K_LED_PATTERN_EN_MASK; |
216 | val <<= QCA8K_LED_PATTERN_EN_SHIFT; |
217 | } else { |
218 | mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; |
219 | } |
220 | |
221 | regmap_update_bits(map: priv->regmap, reg: reg_info.reg, mask: mask << reg_info.shift, |
222 | val: val << reg_info.shift); |
223 | |
224 | return 0; |
225 | } |
226 | |
227 | static int |
228 | qca8k_cled_trigger_offload(struct led_classdev *ldev, bool enable) |
229 | { |
230 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
231 | |
232 | struct qca8k_led_pattern_en reg_info; |
233 | struct qca8k_priv *priv = led->priv; |
234 | u32 mask, val = QCA8K_LED_ALWAYS_OFF; |
235 | |
236 | qca8k_get_enable_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
237 | |
238 | if (enable) |
239 | val = QCA8K_LED_RULE_CONTROLLED; |
240 | |
241 | if (led->port_num == 0 || led->port_num == 4) { |
242 | mask = QCA8K_LED_PATTERN_EN_MASK; |
243 | val <<= QCA8K_LED_PATTERN_EN_SHIFT; |
244 | } else { |
245 | mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; |
246 | } |
247 | |
248 | return regmap_update_bits(map: priv->regmap, reg: reg_info.reg, mask: mask << reg_info.shift, |
249 | val: val << reg_info.shift); |
250 | } |
251 | |
252 | static bool |
253 | qca8k_cled_hw_control_status(struct led_classdev *ldev) |
254 | { |
255 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
256 | |
257 | struct qca8k_led_pattern_en reg_info; |
258 | struct qca8k_priv *priv = led->priv; |
259 | u32 val; |
260 | |
261 | qca8k_get_enable_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
262 | |
263 | regmap_read(map: priv->regmap, reg: reg_info.reg, val: &val); |
264 | |
265 | val >>= reg_info.shift; |
266 | |
267 | if (led->port_num == 0 || led->port_num == 4) { |
268 | val &= QCA8K_LED_PATTERN_EN_MASK; |
269 | val >>= QCA8K_LED_PATTERN_EN_SHIFT; |
270 | } else { |
271 | val &= QCA8K_LED_PHY123_PATTERN_EN_MASK; |
272 | } |
273 | |
274 | return val == QCA8K_LED_RULE_CONTROLLED; |
275 | } |
276 | |
277 | static int |
278 | qca8k_cled_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules) |
279 | { |
280 | u32 offload_trigger = 0; |
281 | |
282 | return qca8k_parse_netdev(rules, offload_trigger: &offload_trigger); |
283 | } |
284 | |
285 | static int |
286 | qca8k_cled_hw_control_set(struct led_classdev *ldev, unsigned long rules) |
287 | { |
288 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
289 | struct qca8k_led_pattern_en reg_info; |
290 | struct qca8k_priv *priv = led->priv; |
291 | u32 offload_trigger = 0; |
292 | int ret; |
293 | |
294 | ret = qca8k_parse_netdev(rules, offload_trigger: &offload_trigger); |
295 | if (ret) |
296 | return ret; |
297 | |
298 | ret = qca8k_cled_trigger_offload(ldev, enable: true); |
299 | if (ret) |
300 | return ret; |
301 | |
302 | qca8k_get_control_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
303 | |
304 | return regmap_update_bits(map: priv->regmap, reg: reg_info.reg, |
305 | QCA8K_LED_RULE_MASK << reg_info.shift, |
306 | val: offload_trigger << reg_info.shift); |
307 | } |
308 | |
309 | static int |
310 | qca8k_cled_hw_control_get(struct led_classdev *ldev, unsigned long *rules) |
311 | { |
312 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
313 | struct qca8k_led_pattern_en reg_info; |
314 | struct qca8k_priv *priv = led->priv; |
315 | u32 val; |
316 | int ret; |
317 | |
318 | /* With hw control not active return err */ |
319 | if (!qca8k_cled_hw_control_status(ldev)) |
320 | return -EINVAL; |
321 | |
322 | qca8k_get_control_led_reg(port_num: led->port_num, led_num: led->led_num, reg_info: ®_info); |
323 | |
324 | ret = regmap_read(map: priv->regmap, reg: reg_info.reg, val: &val); |
325 | if (ret) |
326 | return ret; |
327 | |
328 | val >>= reg_info.shift; |
329 | val &= QCA8K_LED_RULE_MASK; |
330 | |
331 | /* Parsing specific to netdev trigger */ |
332 | if (val & QCA8K_LED_TX_BLINK_MASK) |
333 | set_bit(nr: TRIGGER_NETDEV_TX, addr: rules); |
334 | if (val & QCA8K_LED_RX_BLINK_MASK) |
335 | set_bit(nr: TRIGGER_NETDEV_RX, addr: rules); |
336 | if (val & QCA8K_LED_LINK_10M_EN_MASK) |
337 | set_bit(nr: TRIGGER_NETDEV_LINK_10, addr: rules); |
338 | if (val & QCA8K_LED_LINK_100M_EN_MASK) |
339 | set_bit(nr: TRIGGER_NETDEV_LINK_100, addr: rules); |
340 | if (val & QCA8K_LED_LINK_1000M_EN_MASK) |
341 | set_bit(nr: TRIGGER_NETDEV_LINK_1000, addr: rules); |
342 | if (val & QCA8K_LED_HALF_DUPLEX_MASK) |
343 | set_bit(nr: TRIGGER_NETDEV_HALF_DUPLEX, addr: rules); |
344 | if (val & QCA8K_LED_FULL_DUPLEX_MASK) |
345 | set_bit(nr: TRIGGER_NETDEV_FULL_DUPLEX, addr: rules); |
346 | |
347 | return 0; |
348 | } |
349 | |
350 | static struct device *qca8k_cled_hw_control_get_device(struct led_classdev *ldev) |
351 | { |
352 | struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); |
353 | struct qca8k_priv *priv = led->priv; |
354 | struct dsa_port *dp; |
355 | |
356 | dp = dsa_to_port(ds: priv->ds, p: qca8k_phy_to_port(phy: led->port_num)); |
357 | if (!dp) |
358 | return NULL; |
359 | if (dp->user) |
360 | return &dp->user->dev; |
361 | return NULL; |
362 | } |
363 | |
364 | static int |
365 | qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num) |
366 | { |
367 | struct fwnode_handle *led = NULL, *leds = NULL; |
368 | struct led_init_data init_data = { }; |
369 | enum led_default_state state; |
370 | struct qca8k_led *port_led; |
371 | int led_num, led_index; |
372 | int ret; |
373 | |
374 | leds = fwnode_get_named_child_node(fwnode: port, childname: "leds" ); |
375 | if (!leds) { |
376 | dev_dbg(priv->dev, "No Leds node specified in device tree for port %d!\n" , |
377 | port_num); |
378 | return 0; |
379 | } |
380 | |
381 | fwnode_for_each_child_node(leds, led) { |
382 | /* Reg represent the led number of the port. |
383 | * Each port can have at most 3 leds attached |
384 | * Commonly: |
385 | * 1. is gigabit led |
386 | * 2. is mbit led |
387 | * 3. additional status led |
388 | */ |
389 | if (fwnode_property_read_u32(fwnode: led, propname: "reg" , val: &led_num)) |
390 | continue; |
391 | |
392 | if (led_num >= QCA8K_LED_PORT_COUNT) { |
393 | dev_warn(priv->dev, "Invalid LED reg %d defined for port %d" , |
394 | led_num, port_num); |
395 | continue; |
396 | } |
397 | |
398 | led_index = QCA8K_LED_PORT_INDEX(port_num, led_num); |
399 | |
400 | port_led = &priv->ports_led[led_index]; |
401 | port_led->port_num = port_num; |
402 | port_led->led_num = led_num; |
403 | port_led->priv = priv; |
404 | |
405 | state = led_init_default_state_get(fwnode: led); |
406 | switch (state) { |
407 | case LEDS_DEFSTATE_ON: |
408 | port_led->cdev.brightness = 1; |
409 | qca8k_led_brightness_set(led: port_led, brightness: 1); |
410 | break; |
411 | case LEDS_DEFSTATE_KEEP: |
412 | port_led->cdev.brightness = |
413 | qca8k_led_brightness_get(led: port_led); |
414 | break; |
415 | default: |
416 | port_led->cdev.brightness = 0; |
417 | qca8k_led_brightness_set(led: port_led, brightness: 0); |
418 | } |
419 | |
420 | port_led->cdev.max_brightness = 1; |
421 | port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking; |
422 | port_led->cdev.blink_set = qca8k_cled_blink_set; |
423 | port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported; |
424 | port_led->cdev.hw_control_set = qca8k_cled_hw_control_set; |
425 | port_led->cdev.hw_control_get = qca8k_cled_hw_control_get; |
426 | port_led->cdev.hw_control_get_device = qca8k_cled_hw_control_get_device; |
427 | port_led->cdev.hw_control_trigger = "netdev" ; |
428 | init_data.default_label = ":port" ; |
429 | init_data.fwnode = led; |
430 | init_data.devname_mandatory = true; |
431 | init_data.devicename = kasprintf(GFP_KERNEL, fmt: "%s:0%d" , |
432 | priv->internal_mdio_bus->id, |
433 | port_num); |
434 | if (!init_data.devicename) |
435 | return -ENOMEM; |
436 | |
437 | ret = devm_led_classdev_register_ext(parent: priv->dev, led_cdev: &port_led->cdev, init_data: &init_data); |
438 | if (ret) |
439 | dev_warn(priv->dev, "Failed to init LED %d for port %d" , led_num, port_num); |
440 | |
441 | kfree(objp: init_data.devicename); |
442 | } |
443 | |
444 | return 0; |
445 | } |
446 | |
447 | int |
448 | qca8k_setup_led_ctrl(struct qca8k_priv *priv) |
449 | { |
450 | struct fwnode_handle *ports, *port; |
451 | int port_num; |
452 | int ret; |
453 | |
454 | ports = device_get_named_child_node(dev: priv->dev, childname: "ports" ); |
455 | if (!ports) { |
456 | dev_info(priv->dev, "No ports node specified in device tree!" ); |
457 | return 0; |
458 | } |
459 | |
460 | fwnode_for_each_child_node(ports, port) { |
461 | if (fwnode_property_read_u32(fwnode: port, propname: "reg" , val: &port_num)) |
462 | continue; |
463 | |
464 | /* Skip checking for CPU port 0 and CPU port 6 as not supported */ |
465 | if (port_num == 0 || port_num == 6) |
466 | continue; |
467 | |
468 | /* Each port can have at most 3 different leds attached. |
469 | * Switch port starts from 0 to 6, but port 0 and 6 are CPU |
470 | * port. The port index needs to be decreased by one to identify |
471 | * the correct port for LED setup. |
472 | */ |
473 | ret = qca8k_parse_port_leds(priv, port, port_num: qca8k_port_to_phy(port: port_num)); |
474 | if (ret) |
475 | return ret; |
476 | } |
477 | |
478 | return 0; |
479 | } |
480 | |