generic: 6.1: backport LEDs patch adding additional modes
authorChristian Marangi <ansuelsmth@gmail.com>
Wed, 21 Jun 2023 09:35:33 +0000 (11:35 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Wed, 21 Jun 2023 10:43:33 +0000 (12:43 +0200)
Backport LEDs patch adding additional modes for split link speed and
half/full duplex state.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/generic/backport-6.1/805-v6.5-01-leds-trigger-netdev-add-additional-specific-link-spe.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/805-v6.5-02-leds-trigger-netdev-add-additional-specific-link-dup.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/805-v6.5-03-leds-trigger-netdev-expose-hw_control-status-via-sys.patch [new file with mode: 0644]

diff --git a/target/linux/generic/backport-6.1/805-v6.5-01-leds-trigger-netdev-add-additional-specific-link-spe.patch b/target/linux/generic/backport-6.1/805-v6.5-01-leds-trigger-netdev-add-additional-specific-link-spe.patch
new file mode 100644 (file)
index 0000000..1c564b3
--- /dev/null
@@ -0,0 +1,242 @@
+From d5e01266e7f5fa12400d4c8aa4e86fe89dcc61e9 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 19 Jun 2023 22:46:58 +0200
+Subject: [PATCH 1/3] leds: trigger: netdev: add additional specific link speed
+ mode
+
+Add additional modes for specific link speed. Use ethtool APIs to get the
+current link speed and enable the LED accordingly. Under netdev event
+handler the rtnl lock is already held and is not needed to be set to
+access ethtool APIs.
+
+This is especially useful for PHY and Switch that supports LEDs hw
+control for specific link speed. (example scenario a PHY that have 2 LED
+connected one green and one orange where the green is turned on with
+1000mbps speed and orange is turned on with 10mpbs speed)
+
+On mode set from sysfs we check if we have enabled split link speed mode
+and reject enabling generic link mode to prevent wrong and redundant
+configuration.
+
+Rework logic on the set baseline state to support these new modes to
+select if we need to turn on or off the LED.
+
+Add additional modes:
+- link_10: Turn on LED when link speed is 10mbps
+- link_100: Turn on LED when link speed is 100mbps
+- link_1000: Turn on LED when link speed is 1000mbps
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Acked-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 80 +++++++++++++++++++++++----
+ include/linux/leds.h                  |  3 +
+ 2 files changed, 73 insertions(+), 10 deletions(-)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -13,6 +13,7 @@
+ #include <linux/atomic.h>
+ #include <linux/ctype.h>
+ #include <linux/device.h>
++#include <linux/ethtool.h>
+ #include <linux/init.h>
+ #include <linux/jiffies.h>
+ #include <linux/kernel.h>
+@@ -21,6 +22,7 @@
+ #include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/mutex.h>
++#include <linux/rtnetlink.h>
+ #include <linux/timer.h>
+ #include "../leds.h"
+@@ -52,6 +54,8 @@ struct led_netdev_data {
+       unsigned int last_activity;
+       unsigned long mode;
++      int link_speed;
++
+       bool carrier_link_up;
+       bool hw_control;
+ };
+@@ -77,7 +81,24 @@ static void set_baseline_state(struct le
+       if (!trigger_data->carrier_link_up) {
+               led_set_brightness(led_cdev, LED_OFF);
+       } else {
++              bool blink_on = false;
++
+               if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
++                      blink_on = true;
++
++              if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) &&
++                  trigger_data->link_speed == SPEED_10)
++                      blink_on = true;
++
++              if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) &&
++                  trigger_data->link_speed == SPEED_100)
++                      blink_on = true;
++
++              if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) &&
++                  trigger_data->link_speed == SPEED_1000)
++                      blink_on = true;
++
++              if (blink_on)
+                       led_set_brightness(led_cdev,
+                                          led_cdev->blink_brightness);
+               else
+@@ -161,6 +182,18 @@ static bool can_hw_control(struct led_ne
+       return true;
+ }
++static void get_device_state(struct led_netdev_data *trigger_data)
++{
++      struct ethtool_link_ksettings cmd;
++
++      trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
++      if (!trigger_data->carrier_link_up)
++              return;
++
++      if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
++              trigger_data->link_speed = cmd.base.speed;
++}
++
+ static ssize_t device_name_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+ {
+@@ -196,8 +229,12 @@ static int set_device_name(struct led_ne
+                   dev_get_by_name(&init_net, trigger_data->device_name);
+       trigger_data->carrier_link_up = false;
+-      if (trigger_data->net_dev != NULL)
+-              trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
++      trigger_data->link_speed = SPEED_UNKNOWN;
++      if (trigger_data->net_dev != NULL) {
++              rtnl_lock();
++              get_device_state(trigger_data);
++              rtnl_unlock();
++      }
+       trigger_data->last_activity = 0;
+@@ -234,6 +271,9 @@ static ssize_t netdev_led_attr_show(stru
+       switch (attr) {
+       case TRIGGER_NETDEV_LINK:
++      case TRIGGER_NETDEV_LINK_10:
++      case TRIGGER_NETDEV_LINK_100:
++      case TRIGGER_NETDEV_LINK_1000:
+       case TRIGGER_NETDEV_TX:
+       case TRIGGER_NETDEV_RX:
+               bit = attr;
+@@ -249,7 +289,7 @@ static ssize_t netdev_led_attr_store(str
+                                    size_t size, enum led_trigger_netdev_modes attr)
+ {
+       struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
+-      unsigned long state;
++      unsigned long state, mode = trigger_data->mode;
+       int ret;
+       int bit;
+@@ -259,6 +299,9 @@ static ssize_t netdev_led_attr_store(str
+       switch (attr) {
+       case TRIGGER_NETDEV_LINK:
++      case TRIGGER_NETDEV_LINK_10:
++      case TRIGGER_NETDEV_LINK_100:
++      case TRIGGER_NETDEV_LINK_1000:
+       case TRIGGER_NETDEV_TX:
+       case TRIGGER_NETDEV_RX:
+               bit = attr;
+@@ -267,13 +310,20 @@ static ssize_t netdev_led_attr_store(str
+               return -EINVAL;
+       }
+-      cancel_delayed_work_sync(&trigger_data->work);
+-
+       if (state)
+-              set_bit(bit, &trigger_data->mode);
++              set_bit(bit, &mode);
+       else
+-              clear_bit(bit, &trigger_data->mode);
++              clear_bit(bit, &mode);
++
++      if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
++          (test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
++           test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
++           test_bit(TRIGGER_NETDEV_LINK_1000, &mode)))
++              return -EINVAL;
++
++      cancel_delayed_work_sync(&trigger_data->work);
++      trigger_data->mode = mode;
+       trigger_data->hw_control = can_hw_control(trigger_data);
+       set_baseline_state(trigger_data);
+@@ -295,6 +345,9 @@ static ssize_t netdev_led_attr_store(str
+       static DEVICE_ATTR_RW(trigger_name)
+ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
++DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
++DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
++DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
+ DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
+ DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
+@@ -338,6 +391,9 @@ static DEVICE_ATTR_RW(interval);
+ static struct attribute *netdev_trig_attrs[] = {
+       &dev_attr_device_name.attr,
+       &dev_attr_link.attr,
++      &dev_attr_link_10.attr,
++      &dev_attr_link_100.attr,
++      &dev_attr_link_1000.attr,
+       &dev_attr_rx.attr,
+       &dev_attr_tx.attr,
+       &dev_attr_interval.attr,
+@@ -368,9 +424,10 @@ static int netdev_trig_notify(struct not
+       mutex_lock(&trigger_data->lock);
+       trigger_data->carrier_link_up = false;
++      trigger_data->link_speed = SPEED_UNKNOWN;
+       switch (evt) {
+       case NETDEV_CHANGENAME:
+-              trigger_data->carrier_link_up = netif_carrier_ok(dev);
++              get_device_state(trigger_data);
+               fallthrough;
+       case NETDEV_REGISTER:
+               if (trigger_data->net_dev)
+@@ -384,7 +441,7 @@ static int netdev_trig_notify(struct not
+               break;
+       case NETDEV_UP:
+       case NETDEV_CHANGE:
+-              trigger_data->carrier_link_up = netif_carrier_ok(dev);
++              get_device_state(trigger_data);
+               break;
+       }
+@@ -427,7 +484,10 @@ static void netdev_trig_work(struct work
+       if (trigger_data->last_activity != new_activity) {
+               led_stop_software_blink(trigger_data->led_cdev);
+-              invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode);
++              invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode);
+               interval = jiffies_to_msecs(
+                               atomic_read(&trigger_data->interval));
+               /* base state is ON (link present) */
+--- a/include/linux/leds.h
++++ b/include/linux/leds.h
+@@ -530,6 +530,9 @@ static inline void *led_get_trigger_data
+ /* Trigger specific enum */
+ enum led_trigger_netdev_modes {
+       TRIGGER_NETDEV_LINK = 0,
++      TRIGGER_NETDEV_LINK_10,
++      TRIGGER_NETDEV_LINK_100,
++      TRIGGER_NETDEV_LINK_1000,
+       TRIGGER_NETDEV_TX,
+       TRIGGER_NETDEV_RX,
diff --git a/target/linux/generic/backport-6.1/805-v6.5-02-leds-trigger-netdev-add-additional-specific-link-dup.patch b/target/linux/generic/backport-6.1/805-v6.5-02-leds-trigger-netdev-add-additional-specific-link-dup.patch
new file mode 100644 (file)
index 0000000..a5ab461
--- /dev/null
@@ -0,0 +1,138 @@
+From f22f95b9ff1551c9bab13104131929f33d51f23f Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 19 Jun 2023 22:46:59 +0200
+Subject: [PATCH 2/3] leds: trigger: netdev: add additional specific link
+ duplex mode
+
+Add additional modes for specific link duplex. Use ethtool APIs to get the
+current link duplex and enable the LED accordingly. Under netdev event
+handler the rtnl lock is already held and is not needed to be set to
+access ethtool APIs.
+
+This is especially useful for PHY and Switch that supports LEDs hw
+control for specific link duplex.
+
+Add additional modes:
+- half_duplex: Turn on LED when link is half duplex
+- full_duplex: Turn on LED when link is full duplex
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Acked-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 27 +++++++++++++++++++++++++--
+ include/linux/leds.h                  |  2 ++
+ 2 files changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -55,6 +55,7 @@ struct led_netdev_data {
+       unsigned long mode;
+       int link_speed;
++      u8 duplex;
+       bool carrier_link_up;
+       bool hw_control;
+@@ -98,6 +99,14 @@ static void set_baseline_state(struct le
+                   trigger_data->link_speed == SPEED_1000)
+                       blink_on = true;
++              if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
++                  trigger_data->duplex == DUPLEX_HALF)
++                      blink_on = true;
++
++              if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) &&
++                  trigger_data->duplex == DUPLEX_FULL)
++                      blink_on = true;
++
+               if (blink_on)
+                       led_set_brightness(led_cdev,
+                                          led_cdev->blink_brightness);
+@@ -190,8 +199,10 @@ static void get_device_state(struct led_
+       if (!trigger_data->carrier_link_up)
+               return;
+-      if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
++      if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) {
+               trigger_data->link_speed = cmd.base.speed;
++              trigger_data->duplex = cmd.base.duplex;
++      }
+ }
+ static ssize_t device_name_show(struct device *dev,
+@@ -230,6 +241,7 @@ static int set_device_name(struct led_ne
+       trigger_data->carrier_link_up = false;
+       trigger_data->link_speed = SPEED_UNKNOWN;
++      trigger_data->duplex = DUPLEX_UNKNOWN;
+       if (trigger_data->net_dev != NULL) {
+               rtnl_lock();
+               get_device_state(trigger_data);
+@@ -274,6 +286,8 @@ static ssize_t netdev_led_attr_show(stru
+       case TRIGGER_NETDEV_LINK_10:
+       case TRIGGER_NETDEV_LINK_100:
+       case TRIGGER_NETDEV_LINK_1000:
++      case TRIGGER_NETDEV_HALF_DUPLEX:
++      case TRIGGER_NETDEV_FULL_DUPLEX:
+       case TRIGGER_NETDEV_TX:
+       case TRIGGER_NETDEV_RX:
+               bit = attr;
+@@ -302,6 +316,8 @@ static ssize_t netdev_led_attr_store(str
+       case TRIGGER_NETDEV_LINK_10:
+       case TRIGGER_NETDEV_LINK_100:
+       case TRIGGER_NETDEV_LINK_1000:
++      case TRIGGER_NETDEV_HALF_DUPLEX:
++      case TRIGGER_NETDEV_FULL_DUPLEX:
+       case TRIGGER_NETDEV_TX:
+       case TRIGGER_NETDEV_RX:
+               bit = attr;
+@@ -348,6 +364,8 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETD
+ DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
+ DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
+ DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
++DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
++DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
+ DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
+ DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
+@@ -394,6 +412,8 @@ static struct attribute *netdev_trig_att
+       &dev_attr_link_10.attr,
+       &dev_attr_link_100.attr,
+       &dev_attr_link_1000.attr,
++      &dev_attr_full_duplex.attr,
++      &dev_attr_half_duplex.attr,
+       &dev_attr_rx.attr,
+       &dev_attr_tx.attr,
+       &dev_attr_interval.attr,
+@@ -425,6 +445,7 @@ static int netdev_trig_notify(struct not
+       trigger_data->carrier_link_up = false;
+       trigger_data->link_speed = SPEED_UNKNOWN;
++      trigger_data->duplex = DUPLEX_UNKNOWN;
+       switch (evt) {
+       case NETDEV_CHANGENAME:
+               get_device_state(trigger_data);
+@@ -487,7 +508,9 @@ static void netdev_trig_work(struct work
+               invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
+                        test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
+                        test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
+-                       test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode);
++                       test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
+               interval = jiffies_to_msecs(
+                               atomic_read(&trigger_data->interval));
+               /* base state is ON (link present) */
+--- a/include/linux/leds.h
++++ b/include/linux/leds.h
+@@ -533,6 +533,8 @@ enum led_trigger_netdev_modes {
+       TRIGGER_NETDEV_LINK_10,
+       TRIGGER_NETDEV_LINK_100,
+       TRIGGER_NETDEV_LINK_1000,
++      TRIGGER_NETDEV_HALF_DUPLEX,
++      TRIGGER_NETDEV_FULL_DUPLEX,
+       TRIGGER_NETDEV_TX,
+       TRIGGER_NETDEV_RX,
diff --git a/target/linux/generic/backport-6.1/805-v6.5-03-leds-trigger-netdev-expose-hw_control-status-via-sys.patch b/target/linux/generic/backport-6.1/805-v6.5-03-leds-trigger-netdev-expose-hw_control-status-via-sys.patch
new file mode 100644 (file)
index 0000000..67528ca
--- /dev/null
@@ -0,0 +1,45 @@
+From b655892ffd6d89b0c7407e099c40dbde82ee3f03 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 19 Jun 2023 22:47:00 +0200
+Subject: [PATCH 3/3] leds: trigger: netdev: expose hw_control status via sysfs
+
+Expose hw_control status via sysfs for the netdev trigger to give
+userspace better understanding of the current state of the trigger and
+the LED.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>
+Acked-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -406,6 +406,16 @@ static ssize_t interval_store(struct dev
+ static DEVICE_ATTR_RW(interval);
++static ssize_t hw_control_show(struct device *dev,
++                             struct device_attribute *attr, char *buf)
++{
++      struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
++
++      return sprintf(buf, "%d\n", trigger_data->hw_control);
++}
++
++static DEVICE_ATTR_RO(hw_control);
++
+ static struct attribute *netdev_trig_attrs[] = {
+       &dev_attr_device_name.attr,
+       &dev_attr_link.attr,
+@@ -417,6 +427,7 @@ static struct attribute *netdev_trig_att
+       &dev_attr_rx.attr,
+       &dev_attr_tx.attr,
+       &dev_attr_interval.attr,
++      &dev_attr_hw_control.attr,
+       NULL
+ };
+ ATTRIBUTE_GROUPS(netdev_trig);