Skip to content

Commit e025664

Browse files
Ansueldavem330
authored andcommitted
net: dsa: qca8k: implement hw_control ops
Implement hw_control ops to drive Switch LEDs based on hardware events. Netdev trigger is the declared supported trigger for hw control operation and supports the following mode: - tx - rx When hw_control_set is called, LEDs are set to follow the requested mode. Each LEDs will blink at 4Hz by default. Signed-off-by: Christian Marangi <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 947acac commit e025664

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

drivers/net/dsa/qca/qca8k-leds.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,43 @@ qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en
3131
return 0;
3232
}
3333

34+
static int
35+
qca8k_get_control_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
36+
{
37+
reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
38+
39+
/* 6 total control rule:
40+
* 3 control rules for phy0-3 that applies to all their leds
41+
* 3 control rules for phy4
42+
*/
43+
if (port_num == 4)
44+
reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT;
45+
else
46+
reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT;
47+
48+
return 0;
49+
}
50+
51+
static int
52+
qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger)
53+
{
54+
/* Parsing specific to netdev trigger */
55+
if (test_bit(TRIGGER_NETDEV_TX, &rules))
56+
*offload_trigger |= QCA8K_LED_TX_BLINK_MASK;
57+
if (test_bit(TRIGGER_NETDEV_RX, &rules))
58+
*offload_trigger |= QCA8K_LED_RX_BLINK_MASK;
59+
60+
if (rules && !*offload_trigger)
61+
return -EOPNOTSUPP;
62+
63+
/* Enable some default rule by default to the requested mode:
64+
* - Blink at 4Hz by default
65+
*/
66+
*offload_trigger |= QCA8K_LED_BLINK_4HZ;
67+
68+
return 0;
69+
}
70+
3471
static int
3572
qca8k_led_brightness_set(struct qca8k_led *led,
3673
enum led_brightness brightness)
@@ -164,6 +201,119 @@ qca8k_cled_blink_set(struct led_classdev *ldev,
164201
return 0;
165202
}
166203

204+
static int
205+
qca8k_cled_trigger_offload(struct led_classdev *ldev, bool enable)
206+
{
207+
struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
208+
209+
struct qca8k_led_pattern_en reg_info;
210+
struct qca8k_priv *priv = led->priv;
211+
u32 mask, val = QCA8K_LED_ALWAYS_OFF;
212+
213+
qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
214+
215+
if (enable)
216+
val = QCA8K_LED_RULE_CONTROLLED;
217+
218+
if (led->port_num == 0 || led->port_num == 4) {
219+
mask = QCA8K_LED_PATTERN_EN_MASK;
220+
val <<= QCA8K_LED_PATTERN_EN_SHIFT;
221+
} else {
222+
mask = QCA8K_LED_PHY123_PATTERN_EN_MASK;
223+
}
224+
225+
return regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift,
226+
val << reg_info.shift);
227+
}
228+
229+
static bool
230+
qca8k_cled_hw_control_status(struct led_classdev *ldev)
231+
{
232+
struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
233+
234+
struct qca8k_led_pattern_en reg_info;
235+
struct qca8k_priv *priv = led->priv;
236+
u32 val;
237+
238+
qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
239+
240+
regmap_read(priv->regmap, reg_info.reg, &val);
241+
242+
val >>= reg_info.shift;
243+
244+
if (led->port_num == 0 || led->port_num == 4) {
245+
val &= QCA8K_LED_PATTERN_EN_MASK;
246+
val >>= QCA8K_LED_PATTERN_EN_SHIFT;
247+
} else {
248+
val &= QCA8K_LED_PHY123_PATTERN_EN_MASK;
249+
}
250+
251+
return val == QCA8K_LED_RULE_CONTROLLED;
252+
}
253+
254+
static int
255+
qca8k_cled_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
256+
{
257+
u32 offload_trigger = 0;
258+
259+
return qca8k_parse_netdev(rules, &offload_trigger);
260+
}
261+
262+
static int
263+
qca8k_cled_hw_control_set(struct led_classdev *ldev, unsigned long rules)
264+
{
265+
struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
266+
struct qca8k_led_pattern_en reg_info;
267+
struct qca8k_priv *priv = led->priv;
268+
u32 offload_trigger = 0;
269+
int ret;
270+
271+
ret = qca8k_parse_netdev(rules, &offload_trigger);
272+
if (ret)
273+
return ret;
274+
275+
ret = qca8k_cled_trigger_offload(ldev, true);
276+
if (ret)
277+
return ret;
278+
279+
qca8k_get_control_led_reg(led->port_num, led->led_num, &reg_info);
280+
281+
return regmap_update_bits(priv->regmap, reg_info.reg,
282+
QCA8K_LED_RULE_MASK << reg_info.shift,
283+
offload_trigger << reg_info.shift);
284+
}
285+
286+
static int
287+
qca8k_cled_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
288+
{
289+
struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
290+
struct qca8k_led_pattern_en reg_info;
291+
struct qca8k_priv *priv = led->priv;
292+
u32 val;
293+
int ret;
294+
295+
/* With hw control not active return err */
296+
if (!qca8k_cled_hw_control_status(ldev))
297+
return -EINVAL;
298+
299+
qca8k_get_control_led_reg(led->port_num, led->led_num, &reg_info);
300+
301+
ret = regmap_read(priv->regmap, reg_info.reg, &val);
302+
if (ret)
303+
return ret;
304+
305+
val >>= reg_info.shift;
306+
val &= QCA8K_LED_RULE_MASK;
307+
308+
/* Parsing specific to netdev trigger */
309+
if (val & QCA8K_LED_TX_BLINK_MASK)
310+
set_bit(TRIGGER_NETDEV_TX, rules);
311+
if (val & QCA8K_LED_RX_BLINK_MASK)
312+
set_bit(TRIGGER_NETDEV_RX, rules);
313+
314+
return 0;
315+
}
316+
167317
static int
168318
qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
169319
{
@@ -224,6 +374,10 @@ qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int p
224374
port_led->cdev.max_brightness = 1;
225375
port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking;
226376
port_led->cdev.blink_set = qca8k_cled_blink_set;
377+
port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported;
378+
port_led->cdev.hw_control_set = qca8k_cled_hw_control_set;
379+
port_led->cdev.hw_control_get = qca8k_cled_hw_control_get;
380+
port_led->cdev.hw_control_trigger = "netdev";
227381
init_data.default_label = ":port";
228382
init_data.fwnode = led;
229383
init_data.devname_mandatory = true;

0 commit comments

Comments
 (0)