summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
authorChristian Marangi <ansuelsmth@gmail.com>2023-05-29 18:32:38 +0200
committerDavid S. Miller <davem@davemloft.net>2023-05-31 09:42:09 +0100
commit7c145a34ba6e380616af93262fcab9fc7261d851 (patch)
tree72792a58f917316015096204753427b3a059f46b /drivers/leds
parentc84c80c7388f887b10dafd70fc55bc6c5fe9fa5a (diff)
leds: trigger: netdev: add support for LED hw control
Add support for LED hw control for the netdev trigger. The trigger on calling set_baseline_state to configure a new mode, will do various check to verify if hw control can be used for the requested mode in can_hw_control() function. It will first check if the LED driver supports hw control for the netdev trigger, then will use hw_control_is_supported() and finally will call hw_control_set() to apply the requested mode. To use such mode, interval MUST be set to the default value and net_dev MUST be set. If one of these 2 value are not valid, hw control will never be used and normal software fallback is used. The default interval value is moved to a define to make sure they are always synced. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/trigger/ledtrig-netdev.c43
1 files changed, 41 insertions, 2 deletions
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index cb2ec33abc4e..8f592a77cbef 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -24,6 +24,8 @@
#include <linux/timer.h>
#include "../leds.h"
+#define NETDEV_LED_DEFAULT_INTERVAL 50
+
/*
* Configurable sysfs attributes:
*
@@ -68,6 +70,13 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
int current_brightness;
struct led_classdev *led_cdev = trigger_data->led_cdev;
+ /* Already validated, hw control is possible with the requested mode */
+ if (trigger_data->hw_control) {
+ led_cdev->hw_control_set(led_cdev, trigger_data->mode);
+
+ return;
+ }
+
current_brightness = led_cdev->brightness;
if (current_brightness)
led_cdev->blink_brightness = current_brightness;
@@ -103,12 +112,42 @@ static bool supports_hw_control(struct led_classdev *led_cdev)
static bool can_hw_control(struct led_netdev_data *trigger_data)
{
+ unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL);
+ unsigned int interval = atomic_read(&trigger_data->interval);
struct led_classdev *led_cdev = trigger_data->led_cdev;
+ int ret;
if (!supports_hw_control(led_cdev))
return false;
- return false;
+ /*
+ * Interval must be set to the default
+ * value. Any different value is rejected if in hw
+ * control.
+ */
+ if (interval != default_interval)
+ return false;
+
+ /*
+ * net_dev must be set with hw control, otherwise no
+ * blinking can be happening and there is nothing to
+ * offloaded.
+ */
+ if (!trigger_data->net_dev)
+ return false;
+
+ /* Check if the requested mode is supported */
+ ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode);
+ /* Fall back to software blinking if not supported */
+ if (ret == -EOPNOTSUPP)
+ return false;
+ if (ret) {
+ dev_warn(led_cdev->dev,
+ "Current mode check failed with error %d\n", ret);
+ return false;
+ }
+
+ return true;
}
static ssize_t device_name_show(struct device *dev,
@@ -413,7 +452,7 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
trigger_data->device_name[0] = 0;
trigger_data->mode = 0;
- atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
+ atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL));
trigger_data->last_activity = 0;
led_set_trigger_data(led_cdev, trigger_data);