linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers
@ 2022-05-03 15:16 Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs Ansuel Smith
                   ` (10 more replies)
  0 siblings, 11 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

This is another attempt on adding this feature on LEDs.

Most of the times Switch/PHY have connected multiple LEDs that are
controlled by HW based on some rules/event. Currently we lack any
support for a generic way to control the HW part and normally we
either never implement the feature or only add control for brightness
or hw blink.

This is based on Marek idea of providing some API to cled but use a
different implementation that in theory should be more generilized.

The current idea is:
- LED driver implement 3 API (hw_control_status/start/stop).
  They are used to put the LED in hardware mode and to configure the
  various trigger.
- We have hardware triggers that are used to expose to userspace the
  supported hardware mode and set the hardware mode on trigger
  activation.
- We can also have triggers that both support hardware and software mode.
- The LED driver will declare each supported hardware blink mode and
  communicate with the trigger all the supported blink modes that will
  be available by sysfs.
- A trigger will use blink_set to configure the blink mode to active
  in hardware mode.
- On hardware trigger activation, only the hardware mode is enabled but
  the blink modes are not configured. The LED driver should reset any
  link mode active by default.

Each LED driver will have to declare explicit support for the offload
trigger (or return not supported error code) as we the trigger_data that
the LED driver will elaborate and understand what is referring to (based
on the current active trigger).

I posted a user for this new implementation that will benefit from this
and will add a big feature to it. Currently qca8k can have up to 3 LEDs
connected to each PHY port and we have some device that have only one of
them connected and the default configuration won't work for that.

The netdev trigger is expanded and it does now support hardware only
triggers.
The idea is to use hardware mode when a device_name is not defined.
An additional sysfs entry is added to give some info about the available
trigger modes supported in the current configuration.

More polish is required but this is just to understand if I'm taking
the correct path with this implementation hoping we find a correct
implementation and we start working on the ""small details""

v6:
- Back to RFC.
- Drop additional trigger
- Rework netdev trigger to support common modes used by switch and
  hardware only triggers
- Refresh qca8k leds logic and driver
v5:
- Move out of RFC. (no comments from Andrew this is the right path?)
- Fix more spelling mistake (thx Randy)
- Fix error reported by kernel test bot
- Drop the additional HW_CONTROL flag. It does simplify CONFIG
  handling and hw control should be available anyway to support
  triggers as module.
v4:
- Rework implementation and drop hw_configure logic.
  We now expand blink_set.
- Address even more spelling mistake. (thx a lot Randy)
- Drop blink option and use blink_set delay.
- Rework phy-activity trigger to actually make the groups dynamic.
v3:
- Rework start/stop as Andrew asked.
- Introduce more logic to permit a trigger to run in hardware mode.
- Add additional patch with netdev hardware support.
- Use test_bit API to check flag passed to hw_control_configure.
- Added a new cmd to hw_control_configure to reset any active blink_mode.
- Refactor all the patches to follow this new implementation.
v2:
- Fix spelling mistake (sorry)
- Drop patch 02 "permit to declare supported offload triggers".
  Change the logic, now the LED driver declare support for them
  using the configure_offload with the cmd TRIGGER_SUPPORTED.
- Rework code to follow this new implementation.
- Update Documentation to better describe how this offload
  implementation work.

Ansuel Smith (11):
  leds: add support for hardware driven LEDs
  leds: add function to configure hardware controlled LED
  leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode
  leds: trigger: netdev: rename and expose NETDEV trigger enum modes
  leds: trigger: netdev: convert device attr to macro
  leds: trigger: netdev: add hardware control support
  leds: trigger: netdev: use mutex instead of spinlocks
  leds: trigger: netdev: add available mode sysfs attr
  leds: trigger: netdev: add additional hardware only triggers
  net: dsa: qca8k: add LEDs support
  dt-bindings: net: dsa: qca8k: add LEDs definition example

 .../devicetree/bindings/net/dsa/qca8k.yaml    |  20 +
 Documentation/leds/leds-class.rst             |  53 +++
 drivers/leds/Kconfig                          |  11 +
 drivers/leds/led-class.c                      |  27 ++
 drivers/leds/led-triggers.c                   |  29 ++
 drivers/leds/trigger/ledtrig-netdev.c         | 385 ++++++++++++-----
 drivers/net/dsa/Kconfig                       |   9 +
 drivers/net/dsa/Makefile                      |   1 +
 drivers/net/dsa/qca8k-leds.c                  | 408 ++++++++++++++++++
 drivers/net/dsa/qca8k.c                       |   4 +
 drivers/net/dsa/qca8k.h                       |  61 +++
 include/linux/leds.h                          | 103 ++++-
 12 files changed, 1012 insertions(+), 99 deletions(-)
 create mode 100644 drivers/net/dsa/qca8k-leds.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-04 22:19   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED Ansuel Smith
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds
  Cc: Marek Behún

Some LEDs can be driven by hardware (for example a LED connected to
an ethernet PHY or an ethernet switch can be configured to blink on
activity on the network, which in software is done by the netdev trigger).

To do such offloading, LED driver must support this and a supported
trigger must be used.

LED driver should declare the correct blink_mode supported and should set
the blink_mode parameter to one of HARDWARE_CONTROLLED or
SOFTWARE_HARDWARE_CONTROLLED.
The trigger will check this option and fail to activate if the blink_mode
is not supported. By default if a LED driver doesn't declare blink_mode,
SOFTWARE_CONTROLLED is assumed.

The LED must implement 3 main API:
- trigger_offload_status(): This asks the LED driver if offload mode is
    enabled or not.
    Triggers will check if the offload mode is supported and will be
    activated accordingly. If the trigger can't run in software mode,
    return -EOPNOTSUPP as the blinking can't be simulated by software.
- trigger_offload_start(): This will simply enable the offload mode for
    the LED.
    With this not declared and trigger_offload_status() returning true,
    it's assumed that the LED is always in offload mode.
- trigger_offload_stop(): This will simply disable the offload mode for
    the LED.
    With this not declared and trigger_offload_status() returning true,
    it's assumed that the LED is always in offload mode.
    It's advised to the driver to put the LED in the old state but this
    is not enforcerd and putting the LED off is also accepted.

With HARDWARE_CONTROLLED blink_mode trigger_offload_status/start/stop is
optional and any software only trigger will reject activation as the LED
supports only hardware mode.

An additional config CONFIG_LEDS_HARDWARE_CONTROL is added to add support
for LEDs that can be controlled by hardware.

Cc: Marek Behún <kabel@kernel.org>
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 Documentation/leds/leds-class.rst | 28 ++++++++++++++++++
 drivers/leds/Kconfig              | 11 ++++++++
 drivers/leds/led-class.c          | 27 ++++++++++++++++++
 drivers/leds/led-triggers.c       | 29 +++++++++++++++++++
 include/linux/leds.h              | 47 ++++++++++++++++++++++++++++++-
 5 files changed, 141 insertions(+), 1 deletion(-)

diff --git a/Documentation/leds/leds-class.rst b/Documentation/leds/leds-class.rst
index cd155ead8703..645940b78d81 100644
--- a/Documentation/leds/leds-class.rst
+++ b/Documentation/leds/leds-class.rst
@@ -169,6 +169,34 @@ Setting the brightness to zero with brightness_set() callback function
 should completely turn off the LED and cancel the previously programmed
 hardware blinking function, if any.
 
+Hardware driven LEDs
+===================================
+
+Some LEDs can be driven by hardware (for example a LED connected to
+an ethernet PHY or an ethernet switch can be configured to blink on activity on
+the network, which in software is done by the netdev trigger).
+
+To do such offloading, LED driver must support this and a supported trigger must
+be used.
+
+LED driver should declare the correct blink_mode supported and should set the
+blink_mode parameter to one of HARDWARE_CONTROLLED or SOFTWARE_HARDWARE_CONTROLLED.
+The trigger will check this option and fail to activate if the blink_mode is not
+supported. By default if a LED driver doesn't declare blink_mode, SOFTWARE_CONTROLLED
+is assumed.
+
+The LED must implement 3 main API:
+- hw_control_status(): This asks the LED driver if hardware mode is enabled
+    or not. Triggers will check if the hardware mode is active and will try
+    to offload their triggers if supported by the driver.
+- hw_control_start(): This will simply enable the hardware mode for the LED.
+- hw_control_stop(): This will simply disable the hardware mode for the LED.
+    It's advised to the driver to put the LED in the old state but this is not
+    enforcerd and putting the LED off is also accepted.
+
+With HARDWARE_CONTROLLED blink_mode hw_control_status/start/stop is optional
+and any software only trigger will reject activation as the LED supports only
+hardware mode.
 
 Known Issues
 ============
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6090e647daee..965aa8ba5164 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -49,6 +49,17 @@ config LEDS_BRIGHTNESS_HW_CHANGED
 
 	  See Documentation/ABI/testing/sysfs-class-led for details.
 
+config LEDS_HARDWARE_CONTROL
+	bool "LED Hardware Control support"
+	help
+	  This option enabled Hardware control support used by leds that
+	  can be driven in hardware by using supported triggers.
+
+	  Hardware blink modes will be exposed by sysfs class in
+	  /sys/class/leds based on the trigger currently active.
+
+	  If unsure, say Y.
+
 comment "LED drivers"
 
 config LEDS_88PM860X
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 6a8ea94834fa..3516ae3c4c3c 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -164,6 +164,27 @@ static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
 }
 #endif
 
+#ifdef CONFIG_LEDS_HARDWARE_CONTROL
+static int led_classdev_check_blink_mode_functions(struct led_classdev *led_cdev)
+{
+	int mode = led_cdev->blink_mode;
+
+	if (mode == SOFTWARE_HARDWARE_CONTROLLED &&
+	    (!led_cdev->hw_control_status ||
+	    !led_cdev->hw_control_start ||
+	    !led_cdev->hw_control_stop))
+		return -EINVAL;
+
+	if (mode == SOFTWARE_CONTROLLED &&
+	    (led_cdev->hw_control_status ||
+	    led_cdev->hw_control_start ||
+	    led_cdev->hw_control_stop))
+		return -EINVAL;
+
+	return 0;
+}
+#endif
+
 /**
  * led_classdev_suspend - suspend an led_classdev.
  * @led_cdev: the led_classdev to suspend.
@@ -367,6 +388,12 @@ int led_classdev_register_ext(struct device *parent,
 	if (ret < 0)
 		return ret;
 
+#ifdef CONFIG_LEDS_HARDWARE_CONTROL
+	ret = led_classdev_check_blink_mode_functions(led_cdev);
+	if (ret < 0)
+		return ret;
+#endif
+
 	mutex_init(&led_cdev->led_access);
 	mutex_lock(&led_cdev->led_access);
 	led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 072491d3e17b..693c5d0fa980 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -154,6 +154,27 @@ ssize_t led_trigger_read(struct file *filp, struct kobject *kobj,
 }
 EXPORT_SYMBOL_GPL(led_trigger_read);
 
+static bool led_trigger_is_supported(struct led_classdev *led_cdev,
+				     struct led_trigger *trigger)
+{
+	switch (led_cdev->blink_mode) {
+	case SOFTWARE_CONTROLLED:
+		if (trigger->supported_blink_modes == HARDWARE_ONLY)
+			return 0;
+		break;
+	case HARDWARE_CONTROLLED:
+		if (trigger->supported_blink_modes == SOFTWARE_ONLY)
+			return 0;
+		break;
+	case SOFTWARE_HARDWARE_CONTROLLED:
+		break;
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
 /* Caller must ensure led_cdev->trigger_lock held */
 int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
 {
@@ -179,6 +200,10 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
 
 		cancel_work_sync(&led_cdev->set_brightness_work);
 		led_stop_software_blink(led_cdev);
+		/* Disable hardware mode on trigger change if supported */
+		if (led_cdev->blink_mode != SOFTWARE_CONTROLLED &&
+		    led_cdev->hw_control_status(led_cdev))
+			led_cdev->hw_control_stop(led_cdev);
 		if (led_cdev->trigger->deactivate)
 			led_cdev->trigger->deactivate(led_cdev);
 		device_remove_groups(led_cdev->dev, led_cdev->trigger->groups);
@@ -188,6 +213,10 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
 		led_set_brightness(led_cdev, LED_OFF);
 	}
 	if (trig) {
+		/* Make sure the trigger support the LED blink mode */
+		if (!led_trigger_is_supported(led_cdev, trig))
+			return -EINVAL;
+
 		spin_lock(&trig->leddev_list_lock);
 		list_add_tail_rcu(&led_cdev->trig_list, &trig->led_cdevs);
 		spin_unlock(&trig->leddev_list_lock);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index ba4861ec73d3..09ff1dc6f48d 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -67,6 +67,12 @@ struct led_hw_trigger_type {
 	int dummy;
 };
 
+enum led_blink_modes {
+	SOFTWARE_CONTROLLED = 0x0,
+	HARDWARE_CONTROLLED,
+	SOFTWARE_HARDWARE_CONTROLLED,
+};
+
 struct led_classdev {
 	const char		*name;
 	unsigned int brightness;
@@ -154,6 +160,32 @@ struct led_classdev {
 
 	/* LEDs that have private triggers have this set */
 	struct led_hw_trigger_type	*trigger_type;
+
+	/* This report the supported blink_mode. The driver should report the
+	 * correct LED capabilities.
+	 * With this set to HARDWARE_CONTROLLED, LED is always in offload mode
+	 * and triggers can't be simulated by software.
+	 * If the led is HARDWARE_CONTROLLED, status/start/stop function
+	 * are optional.
+	 * By default SOFTWARE_CONTROLLED is set as blink_mode.
+	 */
+	enum led_blink_modes	blink_mode;
+#ifdef CONFIG_LEDS_HARDWARE_CONTROL
+	/* Ask the LED driver if hardware mode is enabled or not.
+	 * If the option is not declared by the LED driver, SOFTWARE_CONTROLLED
+	 * is assumed.
+	 * Triggers will check if the hardware mode is supported and will be
+	 * activated accordingly. If the trigger can't run in hardware mode,
+	 * return -EOPNOTSUPP as the blinking can't be simulated by software.
+	 */
+	bool			(*hw_control_status)(struct led_classdev *led_cdev);
+	/* Set LED in hardware mode */
+	int			(*hw_control_start)(struct led_classdev *led_cdev);
+	/* Disable hardware mode for LED. It's advised to the LED driver to put it to
+	 * the old status but that is not mandatory and also putting it off is accepted.
+	 */
+	int			(*hw_control_stop)(struct led_classdev *led_cdev);
+#endif
 #endif
 
 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
@@ -215,7 +247,6 @@ extern struct led_classdev *of_led_get(struct device_node *np, int index);
 extern void led_put(struct led_classdev *led_cdev);
 struct led_classdev *__must_check devm_of_led_get(struct device *dev,
 						  int index);
-
 /**
  * led_blink_set - set blinking with software fallback
  * @led_cdev: the LED to start blinking
@@ -350,12 +381,26 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev)
 
 #define TRIG_NAME_MAX 50
 
+enum led_trigger_blink_supported_modes {
+	SOFTWARE_ONLY = SOFTWARE_CONTROLLED,
+	HARDWARE_ONLY = HARDWARE_CONTROLLED,
+	SOFTWARE_HARDWARE = SOFTWARE_HARDWARE_CONTROLLED,
+};
+
 struct led_trigger {
 	/* Trigger Properties */
 	const char	 *name;
 	int		(*activate)(struct led_classdev *led_cdev);
 	void		(*deactivate)(struct led_classdev *led_cdev);
 
+	/* Declare if the Trigger supports hardware control to
+	 * offload triggers or supports only software control.
+	 * A trigger can also declare support for hardware control
+	 * if its task is to only configure LED blink modes and expose
+	 * them in sysfs.
+	 */
+	enum led_trigger_blink_supported_modes supported_blink_modes;
+
 	/* LED-private triggers have this set */
 	struct led_hw_trigger_type *trigger_type;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-04 23:23   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode Ansuel Smith
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Add hw_control_configure helper to configure how the LED should work in
hardware mode. The function require to support the particular trigger and
will use the passed flag to elaborate the data and apply the
correct configuration. This function will then be used by the trigger to
request and update hardware configuration.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 Documentation/leds/leds-class.rst | 25 ++++++++++++++++++++
 include/linux/leds.h              | 39 +++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/Documentation/leds/leds-class.rst b/Documentation/leds/leds-class.rst
index 645940b78d81..efd2f68c46a7 100644
--- a/Documentation/leds/leds-class.rst
+++ b/Documentation/leds/leds-class.rst
@@ -198,6 +198,31 @@ With HARDWARE_CONTROLLED blink_mode hw_control_status/start/stop is optional
 and any software only trigger will reject activation as the LED supports only
 hardware mode.
 
+A trigger once he declared support for hardware controlled blinks, will use the function
+hw_control_configure() provided by the driver to check support for a particular blink mode.
+This function passes as the first argument (flag) a u32 flag.
+The second argument (cmd) of hw_control_configure() method can be used to do various
+operations for the specific blink mode. We currently support ENABLE, DISABLE, READ, ZERO
+and SUPPORTED to enable, disable, read the state of the blink mode, ask the LED
+driver if it does supports the specific blink mode and to reset any blink mode active.
+
+In ENABLE/DISABLE hw_control_configure() should configure the LED to enable/disable the
+requested blink mode (flag).
+In READ hw_control_configure() should return 0 or 1 based on the status of the requested
+blink mode (flag).
+In SUPPORTED hw_control_configure() should return 0 or 1 if the LED driver supports the
+requested blink mode (flags) or not.
+In ZERO hw_control_configure() should return 0 with success operation or error.
+
+The unsigned long flag is specific to the trigger and change across them. It's in the LED
+driver interest know how to elaborate this flag and to declare support for a
+particular trigger. For this exact reason explicit support for the specific
+trigger is mandatory or the driver returns -EOPNOTSUPP if asked to enter offload mode
+with a not supported trigger.
+If the driver returns -EOPNOTSUPP on hw_control_configure(), the trigger activation will
+fail as the driver doesn't support that specific offload trigger or doesn't know
+how to handle the provided flags.
+
 Known Issues
 ============
 
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 09ff1dc6f48d..b5aad67fecfb 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -73,6 +73,16 @@ enum led_blink_modes {
 	SOFTWARE_HARDWARE_CONTROLLED,
 };
 
+#ifdef CONFIG_LEDS_HARDWARE_CONTROL
+enum blink_mode_cmd {
+	BLINK_MODE_ENABLE, /* Enable the hardware blink mode */
+	BLINK_MODE_DISABLE, /* Disable the hardware blink mode */
+	BLINK_MODE_READ, /* Read the status of the hardware blink mode */
+	BLINK_MODE_SUPPORTED, /* Ask the driver if the hardware blink mode is supported */
+	BLINK_MODE_ZERO, /* Disable any hardware blink active */
+};
+#endif
+
 struct led_classdev {
 	const char		*name;
 	unsigned int brightness;
@@ -185,6 +195,17 @@ struct led_classdev {
 	 * the old status but that is not mandatory and also putting it off is accepted.
 	 */
 	int			(*hw_control_stop)(struct led_classdev *led_cdev);
+	/* This will be used to configure the various blink modes LED support in hardware
+	 * mode.
+	 * The LED driver require to support the active trigger and will elaborate the
+	 * unsigned long flag and do the operation based on the provided cmd.
+	 * Current operation are enable,disable,supported and status.
+	 * A trigger will use this to enable or disable the asked blink mode, check the
+	 * status of the blink mode or ask if the blink mode can run in hardware mode.
+	 */
+	int			(*hw_control_configure)(struct led_classdev *led_cdev,
+							unsigned long flag,
+							enum blink_mode_cmd cmd);
 #endif
 #endif
 
@@ -454,6 +475,24 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
 	return led_cdev->trigger_data;
 }
 
+#ifdef CONFIG_LEDS_HARDWARE_CONTROL
+static inline bool led_trigger_blink_mode_is_supported(struct led_classdev *led_cdev,
+						       unsigned long flag)
+{
+	int ret;
+
+	/* Sanity check: make sure led support hw mode */
+	if (led_cdev->blink_mode == SOFTWARE_CONTROLLED)
+		return false;
+
+	ret = led_cdev->hw_control_configure(led_cdev, flag, BLINK_MODE_SUPPORTED);
+	if (ret > 0)
+		return true;
+
+	return false;
+}
+#endif
+
 /**
  * led_trigger_rename_static - rename a trigger
  * @name: the new trigger name
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-04 23:25   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 04/11] leds: trigger: netdev: rename and expose NETDEV trigger enum modes Ansuel Smith
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Drop NETDEV_LED_MODE_LINKUP from mode list and convert to a simple bool
that will be true or false based on the carrier link. No functional
change intended.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index d5e774d83021..66a81cc9b64d 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -50,10 +50,10 @@ struct led_netdev_data {
 	unsigned int last_activity;
 
 	unsigned long mode;
+	bool carrier_link_up;
 #define NETDEV_LED_LINK	0
 #define NETDEV_LED_TX	1
 #define NETDEV_LED_RX	2
-#define NETDEV_LED_MODE_LINKUP	3
 };
 
 enum netdev_led_attr {
@@ -73,9 +73,9 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
 	if (!led_cdev->blink_brightness)
 		led_cdev->blink_brightness = led_cdev->max_brightness;
 
-	if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode))
+	if (!trigger_data->carrier_link_up) {
 		led_set_brightness(led_cdev, LED_OFF);
-	else {
+	} else {
 		if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
 			led_set_brightness(led_cdev,
 					   led_cdev->blink_brightness);
@@ -131,10 +131,9 @@ static ssize_t device_name_store(struct device *dev,
 		trigger_data->net_dev =
 		    dev_get_by_name(&init_net, trigger_data->device_name);
 
-	clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
+	trigger_data->carrier_link_up = false;
 	if (trigger_data->net_dev != NULL)
-		if (netif_carrier_ok(trigger_data->net_dev))
-			set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
+		trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
 
 	trigger_data->last_activity = 0;
 
@@ -315,7 +314,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
 
 	spin_lock_bh(&trigger_data->lock);
 
-	clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
+	trigger_data->carrier_link_up = false;
 	switch (evt) {
 	case NETDEV_CHANGENAME:
 	case NETDEV_REGISTER:
@@ -330,8 +329,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
 		break;
 	case NETDEV_UP:
 	case NETDEV_CHANGE:
-		if (netif_carrier_ok(dev))
-			set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
+		trigger_data->carrier_link_up = netif_carrier_ok(dev);
 		break;
 	}
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 04/11] leds: trigger: netdev: rename and expose NETDEV trigger enum modes
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (2 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-05  0:29   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 05/11] leds: trigger: netdev: convert device attr to macro Ansuel Smith
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Rename NETDEV trigger enum modes to a more simbolic name and move them
in leds.h to make them accessible by any user.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 53 +++++++++------------------
 include/linux/leds.h                  |  7 ++++
 2 files changed, 25 insertions(+), 35 deletions(-)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 66a81cc9b64d..6872da08676b 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -51,15 +51,6 @@ struct led_netdev_data {
 
 	unsigned long mode;
 	bool carrier_link_up;
-#define NETDEV_LED_LINK	0
-#define NETDEV_LED_TX	1
-#define NETDEV_LED_RX	2
-};
-
-enum netdev_led_attr {
-	NETDEV_ATTR_LINK,
-	NETDEV_ATTR_TX,
-	NETDEV_ATTR_RX
 };
 
 static void set_baseline_state(struct led_netdev_data *trigger_data)
@@ -76,7 +67,7 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
 	if (!trigger_data->carrier_link_up) {
 		led_set_brightness(led_cdev, LED_OFF);
 	} else {
-		if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
+		if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
 			led_set_brightness(led_cdev,
 					   led_cdev->blink_brightness);
 		else
@@ -85,8 +76,8 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
 		/* If we are looking for RX/TX start periodically
 		 * checking stats
 		 */
-		if (test_bit(NETDEV_LED_TX, &trigger_data->mode) ||
-		    test_bit(NETDEV_LED_RX, &trigger_data->mode))
+		if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
+		    test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode))
 			schedule_delayed_work(&trigger_data->work, 0);
 	}
 }
@@ -146,20 +137,16 @@ static ssize_t device_name_store(struct device *dev,
 static DEVICE_ATTR_RW(device_name);
 
 static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
-	enum netdev_led_attr attr)
+				    enum led_trigger_netdev_modes attr)
 {
 	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 	int bit;
 
 	switch (attr) {
-	case NETDEV_ATTR_LINK:
-		bit = NETDEV_LED_LINK;
-		break;
-	case NETDEV_ATTR_TX:
-		bit = NETDEV_LED_TX;
-		break;
-	case NETDEV_ATTR_RX:
-		bit = NETDEV_LED_RX;
+	case TRIGGER_NETDEV_LINK:
+	case TRIGGER_NETDEV_TX:
+	case TRIGGER_NETDEV_RX:
+		bit = attr;
 		break;
 	default:
 		return -EINVAL;
@@ -169,7 +156,7 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
 }
 
 static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
-	size_t size, enum netdev_led_attr attr)
+				     size_t size, enum led_trigger_netdev_modes attr)
 {
 	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 	unsigned long state;
@@ -181,14 +168,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 		return ret;
 
 	switch (attr) {
-	case NETDEV_ATTR_LINK:
-		bit = NETDEV_LED_LINK;
-		break;
-	case NETDEV_ATTR_TX:
-		bit = NETDEV_LED_TX;
-		break;
-	case NETDEV_ATTR_RX:
-		bit = NETDEV_LED_RX;
+	case TRIGGER_NETDEV_LINK:
+	case TRIGGER_NETDEV_TX:
+	case TRIGGER_NETDEV_RX:
+		bit = attr;
 		break;
 	default:
 		return -EINVAL;
@@ -358,21 +341,21 @@ static void netdev_trig_work(struct work_struct *work)
 	}
 
 	/* If we are not looking for RX/TX then return  */
-	if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
-	    !test_bit(NETDEV_LED_RX, &trigger_data->mode))
+	if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
+	    !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode))
 		return;
 
 	dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
 	new_activity =
-	    (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
+	    (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ?
 		dev_stats->tx_packets : 0) +
-	    (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
+	    (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ?
 		dev_stats->rx_packets : 0);
 
 	if (trigger_data->last_activity != new_activity) {
 		led_stop_software_blink(trigger_data->led_cdev);
 
-		invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
+		invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode);
 		interval = jiffies_to_msecs(
 				atomic_read(&trigger_data->interval));
 		/* base state is ON (link present) */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index b5aad67fecfb..13862f8b1e07 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -548,6 +548,13 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
 
 #endif /* CONFIG_LEDS_TRIGGERS */
 
+/* Trigger specific enum */
+enum led_trigger_netdev_modes {
+	TRIGGER_NETDEV_LINK = 1,
+	TRIGGER_NETDEV_TX,
+	TRIGGER_NETDEV_RX,
+};
+
 /* Trigger specific functions */
 #ifdef CONFIG_LEDS_TRIGGER_DISK
 void ledtrig_disk_activity(bool write);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 05/11] leds: trigger: netdev: convert device attr to macro
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (3 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 04/11] leds: trigger: netdev: rename and expose NETDEV trigger enum modes Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support Ansuel Smith
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Convert link tx and rx device attr to a common macro to reduce common
code and in preparation for additional attr.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 57 ++++++++-------------------
 1 file changed, 16 insertions(+), 41 deletions(-)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 6872da08676b..dd63cadb896e 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -189,47 +189,22 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 	return size;
 }
 
-static ssize_t link_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
-}
-
-static ssize_t link_store(struct device *dev,
-	struct device_attribute *attr, const char *buf, size_t size)
-{
-	return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
-}
-
-static DEVICE_ATTR_RW(link);
-
-static ssize_t tx_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
-}
-
-static ssize_t tx_store(struct device *dev,
-	struct device_attribute *attr, const char *buf, size_t size)
-{
-	return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
-}
-
-static DEVICE_ATTR_RW(tx);
-
-static ssize_t rx_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
-}
-
-static ssize_t rx_store(struct device *dev,
-	struct device_attribute *attr, const char *buf, size_t size)
-{
-	return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
-}
-
-static DEVICE_ATTR_RW(rx);
+#define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \
+	static ssize_t trigger_name##_show(struct device *dev, \
+		struct device_attribute *attr, char *buf) \
+	{ \
+		return netdev_led_attr_show(dev, buf, trigger); \
+	} \
+	static ssize_t trigger_name##_store(struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t size) \
+	{ \
+		return netdev_led_attr_store(dev, buf, size, trigger); \
+	} \
+	static DEVICE_ATTR_RW(trigger_name)
+
+DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
+DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
+DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
 
 static ssize_t interval_show(struct device *dev,
 			     struct device_attribute *attr, char *buf)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (4 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 05/11] leds: trigger: netdev: convert device attr to macro Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-05  1:00   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks Ansuel Smith
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Add hardware control support for the Netdev trigger.
The trigger on config change will check if the requested trigger can set
to blink mode using LED hardware mode and if every blink mode is supported,
the trigger will enable hardware mode with the requested configuration.
If there is at least one trigger that is not supported and can't run in
hardware mode, then software mode will be used instead.
A validation is done on every value change and on fail the old value is
restored and -EINVAL is returned.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 155 +++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index dd63cadb896e..ed019cb5867c 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -37,6 +37,7 @@
  */
 
 struct led_netdev_data {
+	enum led_blink_modes blink_mode;
 	spinlock_t lock;
 
 	struct delayed_work work;
@@ -53,11 +54,105 @@ struct led_netdev_data {
 	bool carrier_link_up;
 };
 
+struct netdev_led_attr_detail {
+	char *name;
+	bool hardware_only;
+	enum led_trigger_netdev_modes bit;
+};
+
+static struct netdev_led_attr_detail attr_details[] = {
+	{ .name = "link", .bit = TRIGGER_NETDEV_LINK},
+	{ .name = "tx", .bit = TRIGGER_NETDEV_TX},
+	{ .name = "rx", .bit = TRIGGER_NETDEV_RX},
+};
+
+static bool validate_baseline_state(struct led_netdev_data *trigger_data)
+{
+	struct led_classdev *led_cdev = trigger_data->led_cdev;
+	struct netdev_led_attr_detail *detail;
+	u32 hw_blink_mode_supported = 0;
+	bool force_sw = false;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(attr_details); i++) {
+		detail = &attr_details[i];
+
+		/* Mode not active, skip */
+		if (!test_bit(detail->bit, &trigger_data->mode))
+			continue;
+
+		/* Hardware only mode enabled on software controlled led */
+		if (led_cdev->blink_mode == SOFTWARE_CONTROLLED &&
+		    detail->hardware_only)
+			return false;
+
+		/* Check if the mode supports hardware mode */
+		if (led_cdev->blink_mode != SOFTWARE_CONTROLLED) {
+			/* With a net dev set, force software mode.
+			 * With modes are handled by hardware, led will blink
+			 * based on his own events and will ignore any event
+			 * from the provided dev.
+			 */
+			if (trigger_data->net_dev) {
+				force_sw = true;
+				continue;
+			}
+
+			/* With empty dev, check if the mode is supported */
+			if (led_trigger_blink_mode_is_supported(led_cdev, detail->bit))
+				hw_blink_mode_supported |= BIT(detail->bit);
+		}
+	}
+
+	/* We can't run modes handled by both software and hardware.
+	 * Check if we run hardware modes and check if all the modes
+	 * can be handled by hardware.
+	 */
+	if (hw_blink_mode_supported && hw_blink_mode_supported != trigger_data->mode)
+		return false;
+
+	/* Modes are valid. Decide now the running mode to later
+	 * set the baseline.
+	 * Software mode is enforced with net_dev set. With an empty
+	 * one hardware mode is selected by default (if supported).
+	 */
+	if (force_sw || led_cdev->blink_mode == SOFTWARE_CONTROLLED)
+		trigger_data->blink_mode = SOFTWARE_CONTROLLED;
+	else
+		trigger_data->blink_mode = HARDWARE_CONTROLLED;
+
+	return true;
+}
+
 static void set_baseline_state(struct led_netdev_data *trigger_data)
 {
+	int i;
 	int current_brightness;
+	struct netdev_led_attr_detail *detail;
 	struct led_classdev *led_cdev = trigger_data->led_cdev;
 
+	/* Modes already validated. Directly apply hw trigger modes */
+	if (trigger_data->blink_mode == HARDWARE_CONTROLLED) {
+		/* We are refreshing the blink modes. Reset them */
+		led_cdev->hw_control_configure(led_cdev, BIT(TRIGGER_NETDEV_LINK),
+					       BLINK_MODE_ZERO);
+
+		for (i = 0; i < ARRAY_SIZE(attr_details); i++) {
+			detail = &attr_details[i];
+
+			if (!test_bit(detail->bit, &trigger_data->mode))
+				continue;
+
+			led_cdev->hw_control_configure(led_cdev, BIT(detail->bit),
+						       BLINK_MODE_ENABLE);
+		}
+
+		led_cdev->hw_control_start(led_cdev);
+
+		return;
+	}
+
+	/* Handle trigger modes by software */
 	current_brightness = led_cdev->brightness;
 	if (current_brightness)
 		led_cdev->blink_brightness = current_brightness;
@@ -100,10 +195,15 @@ static ssize_t device_name_store(struct device *dev,
 				 size_t size)
 {
 	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
+	struct net_device *old_net = trigger_data->net_dev;
+	char old_device_name[IFNAMSIZ];
 
 	if (size >= IFNAMSIZ)
 		return -EINVAL;
 
+	/* Backup old device name */
+	memcpy(old_device_name, trigger_data->device_name, IFNAMSIZ);
+
 	cancel_delayed_work_sync(&trigger_data->work);
 
 	spin_lock_bh(&trigger_data->lock);
@@ -122,6 +222,19 @@ static ssize_t device_name_store(struct device *dev,
 		trigger_data->net_dev =
 		    dev_get_by_name(&init_net, trigger_data->device_name);
 
+	if (!validate_baseline_state(trigger_data)) {
+		/* Restore old net_dev and device_name */
+		if (trigger_data->net_dev)
+			dev_put(trigger_data->net_dev);
+
+		dev_hold(old_net);
+		trigger_data->net_dev = old_net;
+		memcpy(trigger_data->device_name, old_device_name, IFNAMSIZ);
+
+		spin_unlock_bh(&trigger_data->lock);
+		return -EINVAL;
+	}
+
 	trigger_data->carrier_link_up = false;
 	if (trigger_data->net_dev != NULL)
 		trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
@@ -159,7 +272,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 				     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, old_mode = trigger_data->mode;
 	int ret;
 	int bit;
 
@@ -184,6 +297,12 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 	else
 		clear_bit(bit, &trigger_data->mode);
 
+	if (!validate_baseline_state(trigger_data)) {
+		/* Restore old mode on validation fail */
+		trigger_data->mode = old_mode;
+		return -EINVAL;
+	}
+
 	set_baseline_state(trigger_data);
 
 	return size;
@@ -220,6 +339,8 @@ static ssize_t interval_store(struct device *dev,
 			      size_t size)
 {
 	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
+	int old_interval = atomic_read(&trigger_data->interval);
+	u32 old_mode = trigger_data->mode;
 	unsigned long value;
 	int ret;
 
@@ -228,13 +349,22 @@ static ssize_t interval_store(struct device *dev,
 		return ret;
 
 	/* impose some basic bounds on the timer interval */
-	if (value >= 5 && value <= 10000) {
-		cancel_delayed_work_sync(&trigger_data->work);
+	if (value < 5 || value > 10000)
+		return -EINVAL;
+
+	cancel_delayed_work_sync(&trigger_data->work);
+
+	atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
 
-		atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
-		set_baseline_state(trigger_data);	/* resets timer */
+	if (!validate_baseline_state(trigger_data)) {
+		/* Restore old interval on validation error */
+		atomic_set(&trigger_data->interval, old_interval);
+		trigger_data->mode = old_mode;
+		return -EINVAL;
 	}
 
+	set_baseline_state(trigger_data);	/* resets timer */
+
 	return size;
 }
 
@@ -368,13 +498,25 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
 	trigger_data->mode = 0;
 	atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
 	trigger_data->last_activity = 0;
+	if (led_cdev->blink_mode != SOFTWARE_CONTROLLED) {
+		/* With hw mode enabled reset any rule set by default */
+		if (led_cdev->hw_control_status(led_cdev)) {
+			rc = led_cdev->hw_control_configure(led_cdev, BIT(TRIGGER_NETDEV_LINK),
+							    BLINK_MODE_ZERO);
+			if (rc)
+				goto err;
+		}
+	}
 
 	led_set_trigger_data(led_cdev, trigger_data);
 
 	rc = register_netdevice_notifier(&trigger_data->notifier);
 	if (rc)
-		kfree(trigger_data);
+		goto err;
 
+	return 0;
+err:
+	kfree(trigger_data);
 	return rc;
 }
 
@@ -394,6 +536,7 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev)
 
 static struct led_trigger netdev_led_trigger = {
 	.name = "netdev",
+	.supported_blink_modes = SOFTWARE_HARDWARE,
 	.activate = netdev_trig_activate,
 	.deactivate = netdev_trig_deactivate,
 	.groups = netdev_trig_groups,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (5 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-05  1:10   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 08/11] leds: trigger: netdev: add available mode sysfs attr Ansuel Smith
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Some LEDs may require to sleep to apply their hardware rules. Convert to
mutex lock to fix warning for sleeping unser spinlock softirq.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index ed019cb5867c..a471e0cde836 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -20,7 +20,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/timer.h>
 #include "../leds.h"
 
@@ -38,7 +38,7 @@
 
 struct led_netdev_data {
 	enum led_blink_modes blink_mode;
-	spinlock_t lock;
+	struct mutex lock;
 
 	struct delayed_work work;
 	struct notifier_block notifier;
@@ -183,9 +183,9 @@ static ssize_t device_name_show(struct device *dev,
 	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 	ssize_t len;
 
-	spin_lock_bh(&trigger_data->lock);
+	mutex_lock(&trigger_data->lock);
 	len = sprintf(buf, "%s\n", trigger_data->device_name);
-	spin_unlock_bh(&trigger_data->lock);
+	mutex_unlock(&trigger_data->lock);
 
 	return len;
 }
@@ -206,7 +206,7 @@ static ssize_t device_name_store(struct device *dev,
 
 	cancel_delayed_work_sync(&trigger_data->work);
 
-	spin_lock_bh(&trigger_data->lock);
+	mutex_lock(&trigger_data->lock);
 
 	if (trigger_data->net_dev) {
 		dev_put(trigger_data->net_dev);
@@ -231,7 +231,7 @@ static ssize_t device_name_store(struct device *dev,
 		trigger_data->net_dev = old_net;
 		memcpy(trigger_data->device_name, old_device_name, IFNAMSIZ);
 
-		spin_unlock_bh(&trigger_data->lock);
+		mutex_unlock(&trigger_data->lock);
 		return -EINVAL;
 	}
 
@@ -242,7 +242,7 @@ static ssize_t device_name_store(struct device *dev,
 	trigger_data->last_activity = 0;
 
 	set_baseline_state(trigger_data);
-	spin_unlock_bh(&trigger_data->lock);
+	mutex_unlock(&trigger_data->lock);
 
 	return size;
 }
@@ -400,7 +400,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
 
 	cancel_delayed_work_sync(&trigger_data->work);
 
-	spin_lock_bh(&trigger_data->lock);
+	mutex_lock(&trigger_data->lock);
 
 	trigger_data->carrier_link_up = false;
 	switch (evt) {
@@ -423,7 +423,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
 
 	set_baseline_state(trigger_data);
 
-	spin_unlock_bh(&trigger_data->lock);
+	mutex_unlock(&trigger_data->lock);
 
 	return NOTIFY_DONE;
 }
@@ -484,7 +484,7 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
 	if (!trigger_data)
 		return -ENOMEM;
 
-	spin_lock_init(&trigger_data->lock);
+	mutex_init(&trigger_data->lock);
 
 	trigger_data->notifier.notifier_call = netdev_trig_notify;
 	trigger_data->notifier.priority = 10;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 08/11] leds: trigger: netdev: add available mode sysfs attr
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (6 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers Ansuel Smith
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Add avaiable_mode sysfs attr to show and give some details about the
supported modes and how they can be handled by the trigger.
This is in preparation for hardware only modes that doesn't support
software fallback.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 32 +++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index a471e0cde836..d88b0c6a910e 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -33,6 +33,8 @@
  *         (has carrier) or not
  * tx -  LED blinks on transmitted data
  * rx -  LED blinks on receive data
+ * available_mode - Display available mode and how they can be handled
+ *                  by the LED
  *
  */
 
@@ -370,12 +372,42 @@ static ssize_t interval_store(struct device *dev,
 
 static DEVICE_ATTR_RW(interval);
 
+static ssize_t available_mode_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
+	struct netdev_led_attr_detail *detail;
+	int i, len = 0;
+
+	for (i = 0; i < ARRAY_SIZE(attr_details); i++) {
+		detail = &attr_details[i];
+
+		if (led_trigger_blink_mode_is_supported(trigger_data->led_cdev, detail->bit)) {
+			if (!trigger_data->net_dev) {
+				if (detail->hardware_only)
+					len += sprintf(buf+len, "%s [hardware]\n",
+						       detail->name);
+				else
+					len += sprintf(buf+len, "%s [software-hardware]\n",
+						       detail->name);
+			}
+		} else {
+			len += sprintf(buf+len, "%s [software]\n", detail->name);
+		}
+	}
+
+	return len;
+}
+
+static DEVICE_ATTR_RO(available_mode);
+
 static struct attribute *netdev_trig_attrs[] = {
 	&dev_attr_device_name.attr,
 	&dev_attr_link.attr,
 	&dev_attr_rx.attr,
 	&dev_attr_tx.attr,
 	&dev_attr_interval.attr,
+	&dev_attr_available_mode.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(netdev_trig);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (7 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 08/11] leds: trigger: netdev: add available mode sysfs attr Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-05  1:29   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support Ansuel Smith
  2022-05-03 15:16 ` [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example Ansuel Smith
  10 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Add additional hardware only triggers commonly supported by switch LEDs.

Additional modes:
link_10: LED on with link up AND speed 10mbps
link_100: LED on with link up AND speed 100mbps
link_1000: LED on with link up AND speed 1000mbps
half_duplex: LED on with link up AND half_duplex mode
full_duplex: LED on with link up AND full duplex mode

Additional blink interval modes:
blink_2hz: LED blink on any even at 2Hz (250ms)
blink_4hz: LED blink on any even at 4Hz (125ms)
blink_8hz: LED blink on any even at 8Hz (62ms)

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 58 +++++++++++++++++++++++++++
 include/linux/leds.h                  | 10 +++++
 2 files changed, 68 insertions(+)

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index d88b0c6a910e..bced05fafb1c 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -29,8 +29,20 @@
  *
  * device_name - network device name to monitor
  * interval - duration of LED blink, in milliseconds
+ *            (in hardware mode 2hz (62ms), 4hz (125ms) or 8hz (250ms)
+ *             are supported)
  * link -  LED's normal state reflects whether the link is up
  *         (has carrier) or not
+ * link_10 - LED's normal state reflects whether the link is
+ *           up and at 10mbps speed (hardware only)
+ * link_100 - LED's normal state reflects whether the link is
+ *            up and at 100mbps speed (hardware only)
+ * link_1000 - LED's normal state reflects whether the link is
+ *             up and at 1000mbps speed (hardware only)
+ * half_duplex - LED's normal state reflects whether the link is
+ *               up and hafl duplex (hardware only)
+ * full_duplex - LED's normal state reflects whether the link is
+ *               up and full duplex (hardware only)
  * tx -  LED blinks on transmitted data
  * rx -  LED blinks on receive data
  * available_mode - Display available mode and how they can be handled
@@ -64,8 +76,19 @@ struct netdev_led_attr_detail {
 
 static struct netdev_led_attr_detail attr_details[] = {
 	{ .name = "link", .bit = TRIGGER_NETDEV_LINK},
+	{ .name = "link_10", .hardware_only = true, .bit = TRIGGER_NETDEV_LINK_10},
+	{ .name = "link_100", .hardware_only = true, .bit = TRIGGER_NETDEV_LINK_100},
+	{ .name = "link_1000", .hardware_only = true, .bit = TRIGGER_NETDEV_LINK_1000},
+	{ .name = "half_duplex", .hardware_only = true, .bit = TRIGGER_NETDEV_HALF_DUPLEX},
+	{ .name = "full_duplex", .hardware_only = true, .bit = TRIGGER_NETDEV_FULL_DUPLEX},
 	{ .name = "tx", .bit = TRIGGER_NETDEV_TX},
 	{ .name = "rx", .bit = TRIGGER_NETDEV_RX},
+	{ .name = "hw blink 2hz (interval set to 62)", .hardware_only = true,
+	  .bit = TRIGGER_NETDEV_BLINK_2HZ},
+	{ .name = "hw blink 4hz (interval set to 125)", .hardware_only = true,
+	  .bit = TRIGGER_NETDEV_BLINK_4HZ},
+	{ .name = "hw blink 8hz (interval set to 250)", .hardware_only = true,
+	  .bit = TRIGGER_NETDEV_BLINK_8HZ},
 };
 
 static bool validate_baseline_state(struct led_netdev_data *trigger_data)
@@ -259,6 +282,11 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
 
 	switch (attr) {
 	case TRIGGER_NETDEV_LINK:
+	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;
@@ -284,6 +312,11 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 
 	switch (attr) {
 	case TRIGGER_NETDEV_LINK:
+	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;
@@ -324,6 +357,11 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 	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(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);
 
@@ -356,6 +394,21 @@ static ssize_t interval_store(struct device *dev,
 
 	cancel_delayed_work_sync(&trigger_data->work);
 
+	if (trigger_data->blink_mode == HARDWARE_CONTROLLED) {
+		/* Interval are handled as triggers. Reset them. */
+		trigger_data->mode &= ~(BIT(TRIGGER_NETDEV_BLINK_8HZ) |
+					BIT(TRIGGER_NETDEV_BLINK_4HZ) |
+					BIT(TRIGGER_NETDEV_BLINK_2HZ));
+
+		/* Support a common value of 2Hz, 4Hz and 8Hz. */
+		if (value > 5 && value <= 62) /* 8Hz */
+			trigger_data->mode |= BIT(TRIGGER_NETDEV_BLINK_8HZ);
+		else if (value > 63 && value <= 125) /* 4Hz */
+			trigger_data->mode |= BIT(TRIGGER_NETDEV_BLINK_4HZ);
+		else /* 2Hz */
+			trigger_data->mode |= BIT(TRIGGER_NETDEV_BLINK_2HZ);
+	}
+
 	atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
 
 	if (!validate_baseline_state(trigger_data)) {
@@ -404,6 +457,11 @@ static DEVICE_ATTR_RO(available_mode);
 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_half_duplex.attr,
+	&dev_attr_full_duplex.attr,
 	&dev_attr_rx.attr,
 	&dev_attr_tx.attr,
 	&dev_attr_interval.attr,
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 13862f8b1e07..5fcc6d233757 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -551,8 +551,18 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
 /* Trigger specific enum */
 enum led_trigger_netdev_modes {
 	TRIGGER_NETDEV_LINK = 1,
+	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,
+
+	/* Hardware Interval options */
+	TRIGGER_NETDEV_BLINK_2HZ,
+	TRIGGER_NETDEV_BLINK_4HZ,
+	TRIGGER_NETDEV_BLINK_8HZ,
 };
 
 /* Trigger specific functions */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (8 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-05  1:46   ` Andrew Lunn
  2022-05-05  1:55   ` Andrew Lunn
  2022-05-03 15:16 ` [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example Ansuel Smith
  10 siblings, 2 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Add LEDs support for qca8k Switch Family. This will provide the LEDs
hardware API to permit the PHY LED to support hardware mode.
Each port have at least 3 LEDs and they can HW blink, set on/off or
follow blink modes configured with the LED in hardware mode..
This adds support for 2 hardware trigger netdev and
hardware-phy-activity.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/net/dsa/Kconfig      |   9 +
 drivers/net/dsa/Makefile     |   1 +
 drivers/net/dsa/qca8k-leds.c | 408 +++++++++++++++++++++++++++++++++++
 drivers/net/dsa/qca8k.c      |   4 +
 drivers/net/dsa/qca8k.h      |  61 ++++++
 5 files changed, 483 insertions(+)
 create mode 100644 drivers/net/dsa/qca8k-leds.c

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 37a3dabdce31..6e9d730c5e00 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -68,6 +68,15 @@ config NET_DSA_QCA8K
 	  This enables support for the Qualcomm Atheros QCA8K Ethernet
 	  switch chips.
 
+config NET_DSA_QCA8K_LEDS_SUPPORT
+	tristate "Qualcomm Atheros QCA8K Ethernet switch family LEDs support"
+	select NET_DSA_QCA8K
+	select LEDS_OFFLOAD_TRIGGERS
+	help
+	  This enabled support for LEDs present on the Qualcomm Atheros
+	  QCA8K Ethernet switch chips. This require the LEDs offload
+	  triggers support as it can run in offload mode.
+
 source "drivers/net/dsa/realtek/Kconfig"
 
 config NET_DSA_SMSC_LAN9303
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index e73838c12256..5965b59e1ff7 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
 obj-$(CONFIG_NET_DSA_MT7530)	+= mt7530.o
 obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
 obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o
+obj-$(CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT) += qca8k-leds.o
 obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
 obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o
 obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
diff --git a/drivers/net/dsa/qca8k-leds.c b/drivers/net/dsa/qca8k-leds.c
new file mode 100644
index 000000000000..261888ad95a9
--- /dev/null
+++ b/drivers/net/dsa/qca8k-leds.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <net/dsa.h>
+#include <linux/regmap.h>
+
+#include "qca8k.h"
+
+static int
+qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
+{
+	int shift;
+
+	switch (port_num) {
+	case 0:
+		reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
+		reg_info->shift = 14;
+		break;
+	case 1:
+	case 2:
+	case 3:
+		reg_info->reg = QCA8K_LED_CTRL_REG(3);
+		shift = 2 * led_num + (6 * (port_num - 1));
+
+		reg_info->shift = 8 + shift;
+
+		break;
+	case 4:
+		reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
+		reg_info->shift = 30;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+qca8k_get_control_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
+{
+	reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
+
+	/* 6 total control rule:
+	 * 3 control rules for phy0-3 that applies to all their leds
+	 * 3 control rules for phy4
+	 */
+	if (port_num == 4)
+		reg_info->shift = 16;
+	else
+		reg_info->shift = 0;
+
+	return 0;
+}
+
+static int
+qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger, u32 *mask)
+{
+	/* Parsing specific to netdev trigger */
+	if (test_bit(TRIGGER_NETDEV_LINK, &rules))
+		*offload_trigger = QCA8K_LED_LINK_10M_EN_MASK |
+				   QCA8K_LED_LINK_100M_EN_MASK |
+				   QCA8K_LED_LINK_1000M_EN_MASK;
+	if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+		*offload_trigger = QCA8K_LED_LINK_10M_EN_MASK;
+	if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+		*offload_trigger = QCA8K_LED_LINK_100M_EN_MASK;
+	if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+		*offload_trigger = QCA8K_LED_LINK_1000M_EN_MASK;
+	if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
+		*offload_trigger = QCA8K_LED_HALF_DUPLEX_MASK;
+	if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
+		*offload_trigger = QCA8K_LED_FULL_DUPLEX_MASK;
+	if (test_bit(TRIGGER_NETDEV_TX, &rules))
+		*offload_trigger = QCA8K_LED_TX_BLINK_MASK;
+	if (test_bit(TRIGGER_NETDEV_RX, &rules))
+		*offload_trigger = QCA8K_LED_RX_BLINK_MASK;
+	if (test_bit(TRIGGER_NETDEV_BLINK_2HZ, &rules))
+		*offload_trigger = QCA8K_LED_BLINK_2HZ;
+	if (test_bit(TRIGGER_NETDEV_BLINK_4HZ, &rules))
+		*offload_trigger = QCA8K_LED_BLINK_4HZ;
+	if (test_bit(TRIGGER_NETDEV_BLINK_8HZ, &rules))
+		*offload_trigger = QCA8K_LED_BLINK_8HZ;
+
+	pr_info("OFFLOAD TRIGGER %x\n", *offload_trigger);
+
+	if (!*offload_trigger)
+		return -EOPNOTSUPP;
+
+	if (test_bit(TRIGGER_NETDEV_BLINK_2HZ, &rules) ||
+	    test_bit(TRIGGER_NETDEV_BLINK_4HZ, &rules) ||
+	    test_bit(TRIGGER_NETDEV_BLINK_8HZ, &rules)) {
+		*mask = QCA8K_LED_BLINK_FREQ_MASK;
+	} else {
+		*mask = *offload_trigger;
+	}
+
+	return 0;
+}
+
+static int
+qca8k_cled_hw_control_configure(struct led_classdev *ldev, unsigned long rules,
+				enum blink_mode_cmd cmd)
+{
+	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
+	struct led_trigger *trigger = ldev->trigger;
+	struct qca8k_led_pattern_en reg_info;
+	struct qca8k_priv *priv = led->priv;
+	u32 offload_trigger = 0, mask, val;
+	int ret;
+
+	/* Check trigger compatibility */
+	if (strcmp(trigger->name, "netdev"))
+		return -EOPNOTSUPP;
+
+	if (!strcmp(trigger->name, "netdev"))
+		ret = qca8k_parse_netdev(rules, &offload_trigger, &mask);
+
+	if (ret)
+		return ret;
+
+	qca8k_get_control_led_reg(led->port_num, led->led_num, &reg_info);
+
+	switch (cmd) {
+	case BLINK_MODE_SUPPORTED:
+		/* We reach this point, we are sure the trigger is supported */
+		return 1;
+	case BLINK_MODE_ZERO:
+		/* We set 4hz by default */
+		u32 default_reg = QCA8K_LED_BLINK_4HZ;
+
+		ret = regmap_update_bits(priv->regmap, reg_info.reg,
+					 QCA8K_LED_RULE_MASK << reg_info.shift,
+					 default_reg << reg_info.shift);
+		break;
+	case BLINK_MODE_ENABLE:
+		ret = regmap_update_bits(priv->regmap, reg_info.reg,
+					 mask << reg_info.shift,
+					 offload_trigger << reg_info.shift);
+		break;
+	case BLINK_MODE_DISABLE:
+		ret = regmap_update_bits(priv->regmap, reg_info.reg,
+					 mask << reg_info.shift,
+					 0);
+		break;
+	case BLINK_MODE_READ:
+		ret = regmap_read(priv->regmap, reg_info.reg, &val);
+		if (ret)
+			return ret;
+
+		val >>= reg_info.shift;
+		val &= offload_trigger;
+
+		/* Special handling for LED_BLINK_2HZ */
+		if (!val && offload_trigger == QCA8K_LED_BLINK_2HZ)
+			val = 1;
+
+		return val;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+static void
+qca8k_led_brightness_set(struct qca8k_led *led,
+			 enum led_brightness b)
+{
+	struct qca8k_led_pattern_en reg_info;
+	struct qca8k_priv *priv = led->priv;
+	u32 val = QCA8K_LED_ALWAYS_OFF;
+
+	qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
+
+	if (b)
+		val = QCA8K_LED_ALWAYS_ON;
+
+	regmap_update_bits(priv->regmap, reg_info.reg,
+			   GENMASK(1, 0) << reg_info.shift,
+			   val << reg_info.shift);
+}
+
+static void
+qca8k_cled_brightness_set(struct led_classdev *ldev,
+			  enum led_brightness b)
+{
+	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
+
+	return qca8k_led_brightness_set(led, b);
+}
+
+static enum led_brightness
+qca8k_led_brightness_get(struct qca8k_led *led)
+{
+	struct qca8k_led_pattern_en reg_info;
+	struct qca8k_priv *priv = led->priv;
+	u32 val;
+	int ret;
+
+	qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
+
+	ret = regmap_read(priv->regmap, reg_info.reg, &val);
+	if (ret)
+		return 0;
+
+	val >>= reg_info.shift;
+	val &= GENMASK(1, 0);
+
+	return val > 0 ? 1 : 0;
+}
+
+static enum led_brightness
+qca8k_cled_brightness_get(struct led_classdev *ldev)
+{
+	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
+
+	return qca8k_led_brightness_get(led);
+}
+
+static int
+qca8k_cled_blink_set(struct led_classdev *ldev,
+		     unsigned long *delay_on,
+		     unsigned long *delay_off)
+{
+	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
+	struct qca8k_led_pattern_en reg_info;
+	struct qca8k_priv *priv = led->priv;
+
+	if (*delay_on == 0 && *delay_off == 0) {
+		*delay_on = 125;
+		*delay_off = 125;
+	}
+
+	if (*delay_on != 125 || *delay_off != 125) {
+		/* The hardware only supports blinking at 4Hz. Fall back
+		 * to software implementation in other cases.
+		 */
+		return -EINVAL;
+	}
+
+	qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
+
+	regmap_update_bits(priv->regmap, reg_info.reg,
+			   GENMASK(1, 0) << reg_info.shift,
+			   QCA8K_LED_ALWAYS_BLINK_4HZ << reg_info.shift);
+
+	return 0;
+}
+
+static int
+qca8k_cled_trigger_offload(struct led_classdev *ldev, bool enable)
+{
+	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
+
+	struct qca8k_led_pattern_en reg_info;
+	struct qca8k_priv *priv = led->priv;
+	u32 val = QCA8K_LED_ALWAYS_OFF;
+
+	qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
+
+	if (enable)
+		val = QCA8K_LED_RULE_CONTROLLED;
+
+	return regmap_update_bits(priv->regmap, reg_info.reg,
+				  GENMASK(1, 0) << reg_info.shift,
+				  val << reg_info.shift);
+}
+
+static int
+qca8k_cled_hw_control_start(struct led_classdev *led_cdev)
+{
+	return qca8k_cled_trigger_offload(led_cdev, true);
+}
+
+static int
+qca8k_cled_hw_control_stop(struct led_classdev *led_cdev)
+{
+	return qca8k_cled_trigger_offload(led_cdev, false);
+}
+
+static bool
+qca8k_cled_hw_control_status(struct led_classdev *ldev)
+{
+	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
+
+	struct qca8k_led_pattern_en reg_info;
+	struct qca8k_priv *priv = led->priv;
+	u32 val;
+
+	qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
+
+	regmap_read(priv->regmap, reg_info.reg, &val);
+
+	val >>= reg_info.shift;
+	val &= GENMASK(1, 0);
+
+	return val == QCA8K_LED_RULE_CONTROLLED;
+}
+
+static int
+qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
+{
+	struct fwnode_handle *led = NULL, *leds = NULL;
+	struct led_init_data init_data = { };
+	struct qca8k_led *port_led;
+	int led_num, port_index;
+	const char *state;
+	int ret;
+
+	leds = fwnode_get_named_child_node(port, "leds");
+	if (!leds) {
+		dev_dbg(priv->dev, "No Leds node specified in device tree for port %d!\n",
+			port_num);
+		return 0;
+	}
+
+	fwnode_for_each_child_node(leds, led) {
+		/* Reg represent the led number of the port.
+		 * Each port can have at least 3 leds attached
+		 * Commonly:
+		 * 1. is gigabit led
+		 * 2. is mbit led
+		 * 3. additional status led
+		 */
+		if (fwnode_property_read_u32(led, "reg", &led_num))
+			continue;
+
+		if (led_num >= QCA8K_LED_PORT_COUNT) {
+			dev_warn(priv->dev, "Invalid LED reg defined %d", port_num);
+			continue;
+		}
+
+		port_index = 3 * port_num + led_num;
+
+		port_led = &priv->ports_led[port_index];
+		port_led->port_num = port_num;
+		port_led->led_num = led_num;
+		port_led->priv = priv;
+
+		ret = fwnode_property_read_string(led, "default-state", &state);
+		if (!ret) {
+			if (!strcmp(state, "on")) {
+				port_led->cdev.brightness = 1;
+				qca8k_led_brightness_set(port_led, 1);
+			} else if (!strcmp(state, "off")) {
+				port_led->cdev.brightness = 0;
+				qca8k_led_brightness_set(port_led, 0);
+			} else if (!strcmp(state, "keep")) {
+				port_led->cdev.brightness =
+					qca8k_led_brightness_get(port_led);
+			}
+		}
+
+		/* 3 brightness settings can be applied from Documentation:
+		 * 0 always off
+		 * 1 blink at 4Hz
+		 * 2 always on
+		 * 3 rule controlled
+		 * Suppots only 2 mode: (pcb limitation, with always on and blink
+		 * only the last led is set to this mode)
+		 * 0 always off (sets all leds off)
+		 * 3 rule controlled
+		 */
+		port_led->cdev.blink_mode = SOFTWARE_HARDWARE_CONTROLLED;
+		port_led->cdev.max_brightness = 1;
+		port_led->cdev.brightness_set = qca8k_cled_brightness_set;
+		port_led->cdev.brightness_get = qca8k_cled_brightness_get;
+		port_led->cdev.blink_set = qca8k_cled_blink_set;
+		port_led->cdev.hw_control_start = qca8k_cled_hw_control_start;
+		port_led->cdev.hw_control_stop = qca8k_cled_hw_control_stop;
+		port_led->cdev.hw_control_status = qca8k_cled_hw_control_status;
+		port_led->cdev.hw_control_configure = qca8k_cled_hw_control_configure;
+		init_data.default_label = ":port";
+		init_data.devicename = "qca8k";
+		init_data.fwnode = led;
+
+		ret = devm_led_classdev_register_ext(priv->dev, &port_led->cdev, &init_data);
+		if (ret)
+			dev_warn(priv->dev, "Failed to int led");
+	}
+
+	return 0;
+}
+
+int
+qca8k_setup_led_ctrl(struct qca8k_priv *priv)
+{
+	struct fwnode_handle *mdio, *port;
+	int port_num;
+	int ret;
+
+	mdio = device_get_named_child_node(priv->dev, "mdio");
+	if (!mdio) {
+		dev_info(priv->dev, "No MDIO node specified in device tree!\n");
+		return 0;
+	}
+
+	fwnode_for_each_child_node(mdio, port) {
+		if (fwnode_property_read_u32(port, "reg", &port_num))
+			continue;
+
+		/* Each port can have at least 3 different leds attached */
+		ret = qca8k_parse_port_leds(priv, port, port_num);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index d3ed0a7f8077..ea658f1c586a 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -2889,6 +2889,10 @@ qca8k_setup(struct dsa_switch *ds)
 	if (ret)
 		return ret;
 
+	ret = qca8k_setup_led_ctrl(priv);
+	if (ret)
+		return ret;
+
 	qca8k_setup_pcs(priv, &priv->pcs_port_0, 0);
 	qca8k_setup_pcs(priv, &priv->pcs_port_6, 6);
 
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index f375627174c8..92c5ae12e4b0 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/regmap.h>
 #include <linux/gpio.h>
+#include <linux/leds.h>
 #include <linux/dsa/tag_qca.h>
 
 #define QCA8K_ETHERNET_MDIO_PRIORITY			7
@@ -85,6 +86,43 @@
 #define   QCA8K_MDIO_MASTER_DATA(x)			FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
 #define   QCA8K_MDIO_MASTER_MAX_PORTS			5
 #define   QCA8K_MDIO_MASTER_MAX_REG			32
+
+/* LED control register */
+#define QCA8K_LED_COUNT					15
+#define QCA8K_LED_PORT_COUNT				3
+#define QCA8K_LED_RULE_COUNT				6
+#define QCA8K_LED_RULE_MAX				11
+#define QCA8K_LED_CTRL_REG(_i)				(0x050 + (_i) * 4)
+#define QCA8K_LED_CTRL0_REG				0x50
+#define QCA8K_LED_CTRL1_REG				0x54
+#define QCA8K_LED_CTRL2_REG				0x58
+#define QCA8K_LED_CTRL3_REG				0x5C
+#define   QCA8K_LED_CTRL_SHIFT(_i)			(((_i) % 2) * 16)
+#define   QCA8K_LED_CTRL_MASK				GENMASK(15, 0)
+#define QCA8K_LED_RULE_MASK				GENMASK(13, 0)
+#define QCA8K_LED_BLINK_FREQ_MASK			GENMASK(1, 0)
+#define QCA8K_LED_BLINK_FREQ_SHITF			0
+#define   QCA8K_LED_BLINK_2HZ				0
+#define   QCA8K_LED_BLINK_4HZ				1
+#define   QCA8K_LED_BLINK_8HZ				2
+#define   QCA8K_LED_BLINK_AUTO				3
+#define QCA8K_LED_LINKUP_OVER_MASK			BIT(2)
+#define QCA8K_LED_TX_BLINK_MASK				BIT(4)
+#define QCA8K_LED_RX_BLINK_MASK				BIT(5)
+#define QCA8K_LED_COL_BLINK_MASK			BIT(7)
+#define QCA8K_LED_LINK_10M_EN_MASK			BIT(8)
+#define QCA8K_LED_LINK_100M_EN_MASK			BIT(9)
+#define QCA8K_LED_LINK_1000M_EN_MASK			BIT(10)
+#define QCA8K_LED_POWER_ON_LIGHT_MASK			BIT(11)
+#define QCA8K_LED_HALF_DUPLEX_MASK			BIT(12)
+#define QCA8K_LED_FULL_DUPLEX_MASK			BIT(13)
+#define QCA8K_LED_PATTERN_EN_MASK			GENMASK(15, 14)
+#define QCA8K_LED_PATTERN_EN_SHIFT			14
+#define   QCA8K_LED_ALWAYS_OFF				0
+#define   QCA8K_LED_ALWAYS_BLINK_4HZ			1
+#define   QCA8K_LED_ALWAYS_ON				2
+#define   QCA8K_LED_RULE_CONTROLLED			3
+
 #define QCA8K_GOL_MAC_ADDR0				0x60
 #define QCA8K_GOL_MAC_ADDR1				0x64
 #define QCA8K_MAX_FRAME_SIZE				0x78
@@ -382,6 +420,19 @@ struct qca8k_pcs {
 	int port;
 };
 
+struct qca8k_led_pattern_en {
+	u32 reg;
+	u8 shift;
+};
+
+struct qca8k_led {
+	u8 port_num;
+	u8 led_num;
+	u16 old_rule;
+	struct qca8k_priv *priv;
+	struct led_classdev cdev;
+};
+
 struct qca8k_priv {
 	u8 switch_id;
 	u8 switch_revision;
@@ -405,6 +456,7 @@ struct qca8k_priv {
 	struct qca8k_mdio_cache mdio_cache;
 	struct qca8k_pcs pcs_port_0;
 	struct qca8k_pcs pcs_port_6;
+	struct qca8k_led ports_led[QCA8K_LED_COUNT];
 };
 
 struct qca8k_mib_desc {
@@ -420,4 +472,13 @@ struct qca8k_fdb {
 	u8 mac[6];
 };
 
+#ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT
+int qca8k_setup_led_ctrl(struct qca8k_priv *priv);
+#else
+static inline int qca8k_setup_led_ctrl(struct qca8k_priv *priv)
+{
+	return 0;
+}
+#endif
+
 #endif /* __QCA8K_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example
  2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
                   ` (9 preceding siblings ...)
  2022-05-03 15:16 ` [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support Ansuel Smith
@ 2022-05-03 15:16 ` Ansuel Smith
  2022-05-03 22:21   ` Rob Herring
  2022-05-04 17:15   ` Rob Herring
  10 siblings, 2 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-03 15:16 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, Ansuel Smith,
	John Crispin, netdev, devicetree, linux-kernel, linux-doc,
	linux-leds

Add LEDs definition example for qca8k using the offload trigger as the
default trigger and add all the supported offload triggers by the
switch.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 .../devicetree/bindings/net/dsa/qca8k.yaml    | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/dsa/qca8k.yaml b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
index f3c88371d76c..9b46ef645a2d 100644
--- a/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
@@ -65,6 +65,8 @@ properties:
                  internal mdio access is used.
                  With the legacy mapping the reg corresponding to the internal
                  mdio is the switch reg with an offset of -1.
+                 Each phy have at least 3 LEDs connected and can be declared
+                 using the standard LEDs structure.
 
 patternProperties:
   "^(ethernet-)?ports$":
@@ -287,6 +289,24 @@ examples:
 
                 internal_phy_port1: ethernet-phy@0 {
                     reg = <0>;
+
+                    leds {
+                        led@0 {
+                            reg = <0>;
+                            color = <LED_COLOR_ID_WHITE>;
+                            function = LED_FUNCTION_LAN;
+                            function-enumerator = <1>;
+                            linux,default-trigger = "netdev";
+                        };
+
+                        led@1 {
+                            reg = <1>;
+                            color = <LED_COLOR_ID_AMBER>;
+                            function = LED_FUNCTION_LAN;
+                            function-enumerator = <1>;
+                            linux,default-trigger = "netdev";
+                        };
+                    };
                 };
 
                 internal_phy_port2: ethernet-phy@1 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example
  2022-05-03 15:16 ` [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example Ansuel Smith
@ 2022-05-03 22:21   ` Rob Herring
  2022-05-04 17:15   ` Rob Herring
  1 sibling, 0 replies; 34+ messages in thread
From: Rob Herring @ 2022-05-03 22:21 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Jakub Kicinski, Pavel Machek, linux-kernel, linux-doc,
	linux-leds, John Crispin, devicetree, Andrew Lunn, Paolo Abeni,
	Florian Fainelli, Vladimir Oltean, Rob Herring, netdev,
	Krzysztof Kozlowski, Vivien Didelot, Jonathan Corbet,
	David S. Miller

On Tue, 03 May 2022 17:16:33 +0200, Ansuel Smith wrote:
> Add LEDs definition example for qca8k using the offload trigger as the
> default trigger and add all the supported offload triggers by the
> switch.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  .../devicetree/bindings/net/dsa/qca8k.yaml    | 20 +++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/net/dsa/qca8k.example.dts:209.42-43 syntax error
FATAL ERROR: Unable to parse input tree
make[1]: *** [scripts/Makefile.lib:364: Documentation/devicetree/bindings/net/dsa/qca8k.example.dtb] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1401: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example
  2022-05-03 15:16 ` [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example Ansuel Smith
  2022-05-03 22:21   ` Rob Herring
@ 2022-05-04 17:15   ` Rob Herring
  2022-05-05 13:34     ` Ansuel Smith
  1 sibling, 1 reply; 34+ messages in thread
From: Rob Herring @ 2022-05-04 17:15 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Tue, May 03, 2022 at 05:16:33PM +0200, Ansuel Smith wrote:
> Add LEDs definition example for qca8k using the offload trigger as the
> default trigger and add all the supported offload triggers by the
> switch.
> 
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
>  .../devicetree/bindings/net/dsa/qca8k.yaml    | 20 +++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/net/dsa/qca8k.yaml b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
> index f3c88371d76c..9b46ef645a2d 100644
> --- a/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
> +++ b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
> @@ -65,6 +65,8 @@ properties:
>                   internal mdio access is used.
>                   With the legacy mapping the reg corresponding to the internal
>                   mdio is the switch reg with an offset of -1.
> +                 Each phy have at least 3 LEDs connected and can be declared

s/at least/up to/ ?

Or your example is wrong with only 2.

> +                 using the standard LEDs structure.
>  
>  patternProperties:
>    "^(ethernet-)?ports$":
> @@ -287,6 +289,24 @@ examples:
>  
>                  internal_phy_port1: ethernet-phy@0 {
>                      reg = <0>;
> +
> +                    leds {
> +                        led@0 {
> +                            reg = <0>;
> +                            color = <LED_COLOR_ID_WHITE>;
> +                            function = LED_FUNCTION_LAN;
> +                            function-enumerator = <1>;
> +                            linux,default-trigger = "netdev";
> +                        };
> +
> +                        led@1 {
> +                            reg = <1>;
> +                            color = <LED_COLOR_ID_AMBER>;
> +                            function = LED_FUNCTION_LAN;
> +                            function-enumerator = <1>;
> +                            linux,default-trigger = "netdev";
> +                        };
> +                    };
>                  };
>  
>                  internal_phy_port2: ethernet-phy@1 {
> -- 
> 2.34.1
> 
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs
  2022-05-03 15:16 ` [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs Ansuel Smith
@ 2022-05-04 22:19   ` Andrew Lunn
  2022-05-05 13:01     ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-04 22:19 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds,
	Marek Behún

> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 6a8ea94834fa..3516ae3c4c3c 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -164,6 +164,27 @@ static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
>  }
>  #endif
>  
> +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> +static int led_classdev_check_blink_mode_functions(struct led_classdev *led_cdev)
> +{
> +	int mode = led_cdev->blink_mode;
> +

We try to avoid #ifdef in code. I suggest you use

   if (IS_ENABLED(CONFIG_LEDS_HARDWARE_CONTROL)) {
   }

You then get compiler coverage independent of if the option is on or
off.

> +	if (mode == SOFTWARE_HARDWARE_CONTROLLED &&
> +	    (!led_cdev->hw_control_status ||
> +	    !led_cdev->hw_control_start ||
> +	    !led_cdev->hw_control_stop))
> +		return -EINVAL;
> +
> +	if (mode == SOFTWARE_CONTROLLED &&
> +	    (led_cdev->hw_control_status ||
> +	    led_cdev->hw_control_start ||
> +	    led_cdev->hw_control_stop))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +#endif
> +
>  /**
>   * led_classdev_suspend - suspend an led_classdev.
>   * @led_cdev: the led_classdev to suspend.
> @@ -367,6 +388,12 @@ int led_classdev_register_ext(struct device *parent,
>  	if (ret < 0)
>  		return ret;
>  
> +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> +	ret = led_classdev_check_blink_mode_functions(led_cdev);
> +	if (ret < 0)
> +		return ret;
> +#endif

You can then drop this #ifdef, since it will return 0 by default when
disabled, and the compiler should optimize it all out.

> @@ -154,6 +160,32 @@ struct led_classdev {
>  
>  	/* LEDs that have private triggers have this set */
>  	struct led_hw_trigger_type	*trigger_type;
> +
> +	/* This report the supported blink_mode. The driver should report the
> +	 * correct LED capabilities.
> +	 * With this set to HARDWARE_CONTROLLED, LED is always in offload mode
> +	 * and triggers can't be simulated by software.
> +	 * If the led is HARDWARE_CONTROLLED, status/start/stop function
> +	 * are optional.
> +	 * By default SOFTWARE_CONTROLLED is set as blink_mode.
> +	 */
> +	enum led_blink_modes	blink_mode;
> +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> +	/* Ask the LED driver if hardware mode is enabled or not.
> +	 * If the option is not declared by the LED driver, SOFTWARE_CONTROLLED
> +	 * is assumed.
> +	 * Triggers will check if the hardware mode is supported and will be
> +	 * activated accordingly. If the trigger can't run in hardware mode,
> +	 * return -EOPNOTSUPP as the blinking can't be simulated by software.
> +	 */
> +	bool			(*hw_control_status)(struct led_classdev *led_cdev);
> +	/* Set LED in hardware mode */
> +	int			(*hw_control_start)(struct led_classdev *led_cdev);
> +	/* Disable hardware mode for LED. It's advised to the LED driver to put it to
> +	 * the old status but that is not mandatory and also putting it off is accepted.
> +	 */
> +	int			(*hw_control_stop)(struct led_classdev *led_cdev);
> +#endif

I'm surprised this builds. It looked like you accessed these two
members even when the option was disabled. I would keep them even when
the option is disabled. Two pointers don't add much overhead, and it
makes the drivers simpler.

>  #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
> @@ -215,7 +247,6 @@ extern struct led_classdev *of_led_get(struct device_node *np, int index);
>  extern void led_put(struct led_classdev *led_cdev);
>  struct led_classdev *__must_check devm_of_led_get(struct device *dev,
>  						  int index);
> -

Unrelated white space change.

	  Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED
  2022-05-03 15:16 ` [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED Ansuel Smith
@ 2022-05-04 23:23   ` Andrew Lunn
  2022-05-05 13:02     ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-04 23:23 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

> +In SUPPORTED hw_control_configure() should return 0 or 1 if the LED driver supports the
> +requested blink mode (flags) or not.

-EOPNOTSUPP might be clearer.


> +In ZERO hw_control_configure() should return 0 with success operation or error.
> +
> +The unsigned long flag is specific to the trigger and change across them. It's in the LED
> +driver interest know how to elaborate this flag and to declare support for a
> +particular trigger. For this exact reason explicit support for the specific
> +trigger is mandatory or the driver returns -EOPNOTSUPP if asked to enter offload mode
> +with a not supported trigger.
> +If the driver returns -EOPNOTSUPP on hw_control_configure(), the trigger activation will
> +fail as the driver doesn't support that specific offload trigger or doesn't know
> +how to handle the provided flags.
> +
>  Known Issues
>  ============
>  
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 09ff1dc6f48d..b5aad67fecfb 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -73,6 +73,16 @@ enum led_blink_modes {
>  	SOFTWARE_HARDWARE_CONTROLLED,
>  };
>  
> +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> +enum blink_mode_cmd {
> +	BLINK_MODE_ENABLE, /* Enable the hardware blink mode */
> +	BLINK_MODE_DISABLE, /* Disable the hardware blink mode */
> +	BLINK_MODE_READ, /* Read the status of the hardware blink mode */
> +	BLINK_MODE_SUPPORTED, /* Ask the driver if the hardware blink mode is supported */
> +	BLINK_MODE_ZERO, /* Disable any hardware blink active */
> +};
> +#endif

Skip the #ifdef. The enum itself takes no space if never used, and it
makes the driver simpler if they always exist.

> +
>  struct led_classdev {
>  	const char		*name;
>  	unsigned int brightness;
> @@ -185,6 +195,17 @@ struct led_classdev {
>  	 * the old status but that is not mandatory and also putting it off is accepted.
>  	 */
>  	int			(*hw_control_stop)(struct led_classdev *led_cdev);
> +	/* This will be used to configure the various blink modes LED support in hardware
> +	 * mode.
> +	 * The LED driver require to support the active trigger and will elaborate the
> +	 * unsigned long flag and do the operation based on the provided cmd.
> +	 * Current operation are enable,disable,supported and status.
> +	 * A trigger will use this to enable or disable the asked blink mode, check the
> +	 * status of the blink mode or ask if the blink mode can run in hardware mode.
> +	 */
> +	int			(*hw_control_configure)(struct led_classdev *led_cdev,
> +							unsigned long flag,
> +							enum blink_mode_cmd cmd);
>  #endif
>  #endif
>  
> @@ -454,6 +475,24 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
>  	return led_cdev->trigger_data;
>  }
>  
> +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> +static inline bool led_trigger_blink_mode_is_supported(struct led_classdev *led_cdev,
> +						       unsigned long flag)
> +{
> +	int ret;
> +
> +	/* Sanity check: make sure led support hw mode */
> +	if (led_cdev->blink_mode == SOFTWARE_CONTROLLED)
> +		return false;
> +
> +	ret = led_cdev->hw_control_configure(led_cdev, flag, BLINK_MODE_SUPPORTED);
> +	if (ret > 0)
> +		return true;
> +
> +	return false;
> +}
> +#endif

Please add a version which returns false when
CONFIG_LEDS_HARDWARE_CONTROL is disabled.

Does this actually need to be an inline function?

     Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode
  2022-05-03 15:16 ` [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode Ansuel Smith
@ 2022-05-04 23:25   ` Andrew Lunn
  2022-05-05 13:05     ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-04 23:25 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Tue, May 03, 2022 at 05:16:25PM +0200, Ansuel Smith wrote:
> Drop NETDEV_LED_MODE_LINKUP from mode list and convert to a simple bool
> that will be true or false based on the carrier link. No functional
> change intended.

What is missing from the commit message is an explanation why?

     Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 04/11] leds: trigger: netdev: rename and expose NETDEV trigger enum modes
  2022-05-03 15:16 ` [RFC PATCH v6 04/11] leds: trigger: netdev: rename and expose NETDEV trigger enum modes Ansuel Smith
@ 2022-05-05  0:29   ` Andrew Lunn
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05  0:29 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Tue, May 03, 2022 at 05:16:26PM +0200, Ansuel Smith wrote:
> Rename NETDEV trigger enum modes to a more simbolic name and move them

symbolic

	Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support
  2022-05-03 15:16 ` [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support Ansuel Smith
@ 2022-05-05  1:00   ` Andrew Lunn
  2022-05-05 13:27     ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05  1:00 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

> +struct netdev_led_attr_detail {
> +	char *name;
> +	bool hardware_only;
> +	enum led_trigger_netdev_modes bit;
> +};
> +
> +static struct netdev_led_attr_detail attr_details[] = {
> +	{ .name = "link", .bit = TRIGGER_NETDEV_LINK},
> +	{ .name = "tx", .bit = TRIGGER_NETDEV_TX},
> +	{ .name = "rx", .bit = TRIGGER_NETDEV_RX},

hardware_only is never set. Maybe it is used in a later patch? If so,
please introduce it there.

>  static void set_baseline_state(struct led_netdev_data *trigger_data)
>  {
> +	int i;
>  	int current_brightness;
> +	struct netdev_led_attr_detail *detail;
>  	struct led_classdev *led_cdev = trigger_data->led_cdev;

This file mostly keeps with reverse christmas tree, probably because
it was written by a netdev developer. It is probably not required for
the LED subsystem, but it would be nice to keep the file consistent.

> @@ -100,10 +195,15 @@ static ssize_t device_name_store(struct device *dev,
>  				 size_t size)
>  {
>  	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
> +	struct net_device *old_net = trigger_data->net_dev;
> +	char old_device_name[IFNAMSIZ];
>  
>  	if (size >= IFNAMSIZ)
>  		return -EINVAL;
>  
> +	/* Backup old device name */
> +	memcpy(old_device_name, trigger_data->device_name, IFNAMSIZ);
> +
>  	cancel_delayed_work_sync(&trigger_data->work);
>  
>  	spin_lock_bh(&trigger_data->lock);
> @@ -122,6 +222,19 @@ static ssize_t device_name_store(struct device *dev,
>  		trigger_data->net_dev =
>  		    dev_get_by_name(&init_net, trigger_data->device_name);
>  
> +	if (!validate_baseline_state(trigger_data)) {

You probably want to validate trigger_data->net_dev is not NULL first. The current code
is a little odd with that, 

> +		/* Restore old net_dev and device_name */
> +		if (trigger_data->net_dev)
> +			dev_put(trigger_data->net_dev);
> +
> +		dev_hold(old_net);

This dev_hold() looks wrong. It is trying to undo a dev_put()
somewhere? You should not actually do a put until you know you really
do not old_net, otherwise there is a danger it disappears and you
cannot undo.

> @@ -228,13 +349,22 @@ static ssize_t interval_store(struct device *dev,
>  		return ret;
>  
>  	/* impose some basic bounds on the timer interval */
> -	if (value >= 5 && value <= 10000) {
> -		cancel_delayed_work_sync(&trigger_data->work);
> +	if (value < 5 || value > 10000)
> +		return -EINVAL;
> +
> +	cancel_delayed_work_sync(&trigger_data->work);
> +
> +	atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
>  
> -		atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
> -		set_baseline_state(trigger_data);	/* resets timer */
> +	if (!validate_baseline_state(trigger_data)) {
> +		/* Restore old interval on validation error */
> +		atomic_set(&trigger_data->interval, old_interval);
> +		trigger_data->mode = old_mode;

I think you need to schedule the work again, since you cancelled
it. It is at the end of the work that the next work is scheduled, and
so it will not self recover.

   Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks
  2022-05-03 15:16 ` [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks Ansuel Smith
@ 2022-05-05  1:10   ` Andrew Lunn
  2022-05-05 13:29     ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05  1:10 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

> @@ -400,7 +400,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
>  
>  	cancel_delayed_work_sync(&trigger_data->work);
>  
> -	spin_lock_bh(&trigger_data->lock);
> +	mutex_lock(&trigger_data->lock);

I'm not sure you can convert a spin_lock_bh() in a mutex_lock().

Did you check this? What context is the notifier called in?

    Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers
  2022-05-03 15:16 ` [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers Ansuel Smith
@ 2022-05-05  1:29   ` Andrew Lunn
  2022-05-05 13:30     ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05  1:29 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Tue, May 03, 2022 at 05:16:31PM +0200, Ansuel Smith wrote:
> Add additional hardware only triggers commonly supported by switch LEDs.
> 
> Additional modes:
> link_10: LED on with link up AND speed 10mbps
> link_100: LED on with link up AND speed 100mbps
> link_1000: LED on with link up AND speed 1000mbps
> half_duplex: LED on with link up AND half_duplex mode
> full_duplex: LED on with link up AND full duplex mode
> 
> Additional blink interval modes:
> blink_2hz: LED blink on any even at 2Hz (250ms)
> blink_4hz: LED blink on any even at 4Hz (125ms)
> blink_8hz: LED blink on any even at 8Hz (62ms)

I would suggest separating blink intervals into a patch of their own,
because they are orthogonal to the other modes. Most PHYs are not
going to support them, or they have to be the same across all LEDs, or
don't for example make sense with duplex etc. We need to first
concentrate on the basics, get that correct. Then we can add nice to
have features like this.

     Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support
  2022-05-03 15:16 ` [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support Ansuel Smith
@ 2022-05-05  1:46   ` Andrew Lunn
  2022-05-05  1:55   ` Andrew Lunn
  1 sibling, 0 replies; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05  1:46 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

> +config NET_DSA_QCA8K_LEDS_SUPPORT
> +	tristate "Qualcomm Atheros QCA8K Ethernet switch family LEDs support"
> +	select NET_DSA_QCA8K

The should be a depends, not a select. It will then become visible
when the NET_DSA_QCA8K directly above it is enabled.

> +	select LEDS_OFFLOAD_TRIGGERS

and this should also be a depends. If the LED core does not have
support, the QCA8K driver should not enable its support.

> +static int
> +qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger, u32 *mask)
> +{
> +	/* Parsing specific to netdev trigger */
> +	if (test_bit(TRIGGER_NETDEV_LINK, &rules))
> +		*offload_trigger = QCA8K_LED_LINK_10M_EN_MASK |
> +				   QCA8K_LED_LINK_100M_EN_MASK |
> +				   QCA8K_LED_LINK_1000M_EN_MASK;
> +	if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
> +		*offload_trigger = QCA8K_LED_LINK_10M_EN_MASK;
> +	if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
> +		*offload_trigger = QCA8K_LED_LINK_100M_EN_MASK;
> +	if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
> +		*offload_trigger = QCA8K_LED_LINK_1000M_EN_MASK;
> +	if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
> +		*offload_trigger = QCA8K_LED_HALF_DUPLEX_MASK;
> +	if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
> +		*offload_trigger = QCA8K_LED_FULL_DUPLEX_MASK;
> +	if (test_bit(TRIGGER_NETDEV_TX, &rules))
> +		*offload_trigger = QCA8K_LED_TX_BLINK_MASK;
> +	if (test_bit(TRIGGER_NETDEV_RX, &rules))
> +		*offload_trigger = QCA8K_LED_RX_BLINK_MASK;
> +	if (test_bit(TRIGGER_NETDEV_BLINK_2HZ, &rules))
> +		*offload_trigger = QCA8K_LED_BLINK_2HZ;
> +	if (test_bit(TRIGGER_NETDEV_BLINK_4HZ, &rules))
> +		*offload_trigger = QCA8K_LED_BLINK_4HZ;
> +	if (test_bit(TRIGGER_NETDEV_BLINK_8HZ, &rules))
> +		*offload_trigger = QCA8K_LED_BLINK_8HZ;
> +
> +	pr_info("OFFLOAD TRIGGER %x\n", *offload_trigger);

leftover debug print.

	 Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support
  2022-05-03 15:16 ` [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support Ansuel Smith
  2022-05-05  1:46   ` Andrew Lunn
@ 2022-05-05  1:55   ` Andrew Lunn
  2022-05-05 13:33     ` Ansuel Smith
  1 sibling, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05  1:55 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

> +		ret = fwnode_property_read_string(led, "default-state", &state);

You should probably use led_default_state led_init_default_state_get()

    Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs
  2022-05-04 22:19   ` Andrew Lunn
@ 2022-05-05 13:01     ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:01 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds,
	Marek Behún

On Thu, May 05, 2022 at 12:19:11AM +0200, Andrew Lunn wrote:
> > diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> > index 6a8ea94834fa..3516ae3c4c3c 100644
> > --- a/drivers/leds/led-class.c
> > +++ b/drivers/leds/led-class.c
> > @@ -164,6 +164,27 @@ static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
> >  }
> >  #endif
> >  
> > +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> > +static int led_classdev_check_blink_mode_functions(struct led_classdev *led_cdev)
> > +{
> > +	int mode = led_cdev->blink_mode;
> > +
> 
> We try to avoid #ifdef in code. I suggest you use
> 
>    if (IS_ENABLED(CONFIG_LEDS_HARDWARE_CONTROL)) {
>    }
> 
> You then get compiler coverage independent of if the option is on or
> off.
> 
> > +	if (mode == SOFTWARE_HARDWARE_CONTROLLED &&
> > +	    (!led_cdev->hw_control_status ||
> > +	    !led_cdev->hw_control_start ||
> > +	    !led_cdev->hw_control_stop))
> > +		return -EINVAL;
> > +
> > +	if (mode == SOFTWARE_CONTROLLED &&
> > +	    (led_cdev->hw_control_status ||
> > +	    led_cdev->hw_control_start ||
> > +	    led_cdev->hw_control_stop))
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +#endif
> > +
> >  /**
> >   * led_classdev_suspend - suspend an led_classdev.
> >   * @led_cdev: the led_classdev to suspend.
> > @@ -367,6 +388,12 @@ int led_classdev_register_ext(struct device *parent,
> >  	if (ret < 0)
> >  		return ret;
> >  
> > +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> > +	ret = led_classdev_check_blink_mode_functions(led_cdev);
> > +	if (ret < 0)
> > +		return ret;
> > +#endif
> 
> You can then drop this #ifdef, since it will return 0 by default when
> disabled, and the compiler should optimize it all out.
> 
> > @@ -154,6 +160,32 @@ struct led_classdev {
> >  
> >  	/* LEDs that have private triggers have this set */
> >  	struct led_hw_trigger_type	*trigger_type;
> > +
> > +	/* This report the supported blink_mode. The driver should report the
> > +	 * correct LED capabilities.
> > +	 * With this set to HARDWARE_CONTROLLED, LED is always in offload mode
> > +	 * and triggers can't be simulated by software.
> > +	 * If the led is HARDWARE_CONTROLLED, status/start/stop function
> > +	 * are optional.
> > +	 * By default SOFTWARE_CONTROLLED is set as blink_mode.
> > +	 */
> > +	enum led_blink_modes	blink_mode;
> > +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> > +	/* Ask the LED driver if hardware mode is enabled or not.
> > +	 * If the option is not declared by the LED driver, SOFTWARE_CONTROLLED
> > +	 * is assumed.
> > +	 * Triggers will check if the hardware mode is supported and will be
> > +	 * activated accordingly. If the trigger can't run in hardware mode,
> > +	 * return -EOPNOTSUPP as the blinking can't be simulated by software.
> > +	 */
> > +	bool			(*hw_control_status)(struct led_classdev *led_cdev);
> > +	/* Set LED in hardware mode */
> > +	int			(*hw_control_start)(struct led_classdev *led_cdev);
> > +	/* Disable hardware mode for LED. It's advised to the LED driver to put it to
> > +	 * the old status but that is not mandatory and also putting it off is accepted.
> > +	 */
> > +	int			(*hw_control_stop)(struct led_classdev *led_cdev);
> > +#endif
> 
> I'm surprised this builds. It looked like you accessed these two
> members even when the option was disabled. I would keep them even when
> the option is disabled. Two pointers don't add much overhead, and it
> makes the drivers simpler.
> 

Yhea sorry about this... I proposed this as an RFC as it was old code
that I just refreshed and I'm really checking the implementation...
Will do the ifdef changes in the next version.

> >  #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
> > @@ -215,7 +247,6 @@ extern struct led_classdev *of_led_get(struct device_node *np, int index);
> >  extern void led_put(struct led_classdev *led_cdev);
> >  struct led_classdev *__must_check devm_of_led_get(struct device *dev,
> >  						  int index);
> > -
> 
> Unrelated white space change.
> 
> 	  Andrew

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED
  2022-05-04 23:23   ` Andrew Lunn
@ 2022-05-05 13:02     ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:02 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 01:23:36AM +0200, Andrew Lunn wrote:
> > +In SUPPORTED hw_control_configure() should return 0 or 1 if the LED driver supports the
> > +requested blink mode (flags) or not.
> 
> -EOPNOTSUPP might be clearer.
> 
> 
> > +In ZERO hw_control_configure() should return 0 with success operation or error.
> > +
> > +The unsigned long flag is specific to the trigger and change across them. It's in the LED
> > +driver interest know how to elaborate this flag and to declare support for a
> > +particular trigger. For this exact reason explicit support for the specific
> > +trigger is mandatory or the driver returns -EOPNOTSUPP if asked to enter offload mode
> > +with a not supported trigger.
> > +If the driver returns -EOPNOTSUPP on hw_control_configure(), the trigger activation will
> > +fail as the driver doesn't support that specific offload trigger or doesn't know
> > +how to handle the provided flags.
> > +
> >  Known Issues
> >  ============
> >  
> > diff --git a/include/linux/leds.h b/include/linux/leds.h
> > index 09ff1dc6f48d..b5aad67fecfb 100644
> > --- a/include/linux/leds.h
> > +++ b/include/linux/leds.h
> > @@ -73,6 +73,16 @@ enum led_blink_modes {
> >  	SOFTWARE_HARDWARE_CONTROLLED,
> >  };
> >  
> > +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> > +enum blink_mode_cmd {
> > +	BLINK_MODE_ENABLE, /* Enable the hardware blink mode */
> > +	BLINK_MODE_DISABLE, /* Disable the hardware blink mode */
> > +	BLINK_MODE_READ, /* Read the status of the hardware blink mode */
> > +	BLINK_MODE_SUPPORTED, /* Ask the driver if the hardware blink mode is supported */
> > +	BLINK_MODE_ZERO, /* Disable any hardware blink active */
> > +};
> > +#endif
> 
> Skip the #ifdef. The enum itself takes no space if never used, and it
> makes the driver simpler if they always exist.
> 
> > +
> >  struct led_classdev {
> >  	const char		*name;
> >  	unsigned int brightness;
> > @@ -185,6 +195,17 @@ struct led_classdev {
> >  	 * the old status but that is not mandatory and also putting it off is accepted.
> >  	 */
> >  	int			(*hw_control_stop)(struct led_classdev *led_cdev);
> > +	/* This will be used to configure the various blink modes LED support in hardware
> > +	 * mode.
> > +	 * The LED driver require to support the active trigger and will elaborate the
> > +	 * unsigned long flag and do the operation based on the provided cmd.
> > +	 * Current operation are enable,disable,supported and status.
> > +	 * A trigger will use this to enable or disable the asked blink mode, check the
> > +	 * status of the blink mode or ask if the blink mode can run in hardware mode.
> > +	 */
> > +	int			(*hw_control_configure)(struct led_classdev *led_cdev,
> > +							unsigned long flag,
> > +							enum blink_mode_cmd cmd);
> >  #endif
> >  #endif
> >  
> > @@ -454,6 +475,24 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
> >  	return led_cdev->trigger_data;
> >  }
> >  
> > +#ifdef CONFIG_LEDS_HARDWARE_CONTROL
> > +static inline bool led_trigger_blink_mode_is_supported(struct led_classdev *led_cdev,
> > +						       unsigned long flag)
> > +{
> > +	int ret;
> > +
> > +	/* Sanity check: make sure led support hw mode */
> > +	if (led_cdev->blink_mode == SOFTWARE_CONTROLLED)
> > +		return false;
> > +
> > +	ret = led_cdev->hw_control_configure(led_cdev, flag, BLINK_MODE_SUPPORTED);
> > +	if (ret > 0)
> > +		return true;
> > +
> > +	return false;
> > +}
> > +#endif
> 
> Please add a version which returns false when
> CONFIG_LEDS_HARDWARE_CONTROL is disabled.
> 
> Does this actually need to be an inline function?

Not at all... Originally it was a smaller function. Will drop it.

>
>      Andrew

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode
  2022-05-04 23:25   ` Andrew Lunn
@ 2022-05-05 13:05     ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:05 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 01:25:31AM +0200, Andrew Lunn wrote:
> On Tue, May 03, 2022 at 05:16:25PM +0200, Ansuel Smith wrote:
> > Drop NETDEV_LED_MODE_LINKUP from mode list and convert to a simple bool
> > that will be true or false based on the carrier link. No functional
> > change intended.
> 
> What is missing from the commit message is an explanation why?
> 
>      Andrew

Will add the reason.
Just in case it doesn't make sense...
The reason is that putting a state in the mode bitmap doesn't look
correct. It's ""acceptable"" if we have only 3 state (rx, tx and link).
It become problematic when we start to have 7 modes and a link up state
should be handled differently.

Does it make sense?

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support
  2022-05-05  1:00   ` Andrew Lunn
@ 2022-05-05 13:27     ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:27 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 03:00:03AM +0200, Andrew Lunn wrote:
> > +struct netdev_led_attr_detail {
> > +	char *name;
> > +	bool hardware_only;
> > +	enum led_trigger_netdev_modes bit;
> > +};
> > +
> > +static struct netdev_led_attr_detail attr_details[] = {
> > +	{ .name = "link", .bit = TRIGGER_NETDEV_LINK},
> > +	{ .name = "tx", .bit = TRIGGER_NETDEV_TX},
> > +	{ .name = "rx", .bit = TRIGGER_NETDEV_RX},
> 
> hardware_only is never set. Maybe it is used in a later patch? If so,
> please introduce it there.
>

Is it better to introduce the hardware_only bool in the patch where the
additional "hardware only" modes are added?

> >  static void set_baseline_state(struct led_netdev_data *trigger_data)
> >  {
> > +	int i;
> >  	int current_brightness;
> > +	struct netdev_led_attr_detail *detail;
> >  	struct led_classdev *led_cdev = trigger_data->led_cdev;
> 
> This file mostly keeps with reverse christmas tree, probably because
> it was written by a netdev developer. It is probably not required for
> the LED subsystem, but it would be nice to keep the file consistent.
> 

The order is a bit mixed as you notice. Ok will stick to reverse
christmas.

> > @@ -100,10 +195,15 @@ static ssize_t device_name_store(struct device *dev,
> >  				 size_t size)
> >  {
> >  	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
> > +	struct net_device *old_net = trigger_data->net_dev;
> > +	char old_device_name[IFNAMSIZ];
> >  
> >  	if (size >= IFNAMSIZ)
> >  		return -EINVAL;
> >  
> > +	/* Backup old device name */
> > +	memcpy(old_device_name, trigger_data->device_name, IFNAMSIZ);
> > +
> >  	cancel_delayed_work_sync(&trigger_data->work);
> >  
> >  	spin_lock_bh(&trigger_data->lock);
> > @@ -122,6 +222,19 @@ static ssize_t device_name_store(struct device *dev,
> >  		trigger_data->net_dev =
> >  		    dev_get_by_name(&init_net, trigger_data->device_name);
> >  
> > +	if (!validate_baseline_state(trigger_data)) {
> 
> You probably want to validate trigger_data->net_dev is not NULL first. The current code
> is a little odd with that, 
> 

The thing is that net_dev can be NULL and actually is a requirement for
hardware_mode to be triggered. (net_dev must be NULL or software mode is
forced)

> > +		/* Restore old net_dev and device_name */
> > +		if (trigger_data->net_dev)
> > +			dev_put(trigger_data->net_dev);
> > +
> > +		dev_hold(old_net);
> 
> This dev_hold() looks wrong. It is trying to undo a dev_put()
> somewhere? You should not actually do a put until you know you really
> do not old_net, otherwise there is a danger it disappears and you
> cannot undo.
> 

Yes if you notice some lines above, the first thing done is to dev_put
the current net_dev set. So on validation fail we restore the old state
with holding the old_net again and restoring the device_name.

But thanks for poiting it out... I should check if old_net is not NULL.
Also should i change the logic and just dev_put if all goes well? (for
example before the return size?) That way I should be able to skip this
additional dev_hold.

> > @@ -228,13 +349,22 @@ static ssize_t interval_store(struct device *dev,
> >  		return ret;
> >  
> >  	/* impose some basic bounds on the timer interval */
> > -	if (value >= 5 && value <= 10000) {
> > -		cancel_delayed_work_sync(&trigger_data->work);
> > +	if (value < 5 || value > 10000)
> > +		return -EINVAL;
> > +
> > +	cancel_delayed_work_sync(&trigger_data->work);
> > +
> > +	atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
> >  
> > -		atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
> > -		set_baseline_state(trigger_data);	/* resets timer */
> > +	if (!validate_baseline_state(trigger_data)) {
> > +		/* Restore old interval on validation error */
> > +		atomic_set(&trigger_data->interval, old_interval);
> > +		trigger_data->mode = old_mode;
> 
> I think you need to schedule the work again, since you cancelled
> it. It is at the end of the work that the next work is scheduled, and
> so it will not self recover.
> 

Ok I assume the correct way to handle this is to return error and still
use the set_baseline_state... Or Also move the validate_baseline_state
up before the cancel_delayed_work_sync. But considering we require
atomic_set for the validation to work I think the right way is to
set_baseline_state even with errors (as it will reschedule the work)

>    Andrew

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks
  2022-05-05  1:10   ` Andrew Lunn
@ 2022-05-05 13:29     ` Ansuel Smith
  2022-05-05 14:21       ` Andrew Lunn
  0 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:29 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 03:10:21AM +0200, Andrew Lunn wrote:
> > @@ -400,7 +400,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
> >  
> >  	cancel_delayed_work_sync(&trigger_data->work);
> >  
> > -	spin_lock_bh(&trigger_data->lock);
> > +	mutex_lock(&trigger_data->lock);
> 
> I'm not sure you can convert a spin_lock_bh() in a mutex_lock().
> 
> Did you check this? What context is the notifier called in?
> 
>     Andrew

I had to do this because qca8k use completion to set the value and that
can sleep... Mhhh any idea how to handle this?

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers
  2022-05-05  1:29   ` Andrew Lunn
@ 2022-05-05 13:30     ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:30 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 03:29:26AM +0200, Andrew Lunn wrote:
> On Tue, May 03, 2022 at 05:16:31PM +0200, Ansuel Smith wrote:
> > Add additional hardware only triggers commonly supported by switch LEDs.
> > 
> > Additional modes:
> > link_10: LED on with link up AND speed 10mbps
> > link_100: LED on with link up AND speed 100mbps
> > link_1000: LED on with link up AND speed 1000mbps
> > half_duplex: LED on with link up AND half_duplex mode
> > full_duplex: LED on with link up AND full duplex mode
> > 
> > Additional blink interval modes:
> > blink_2hz: LED blink on any even at 2Hz (250ms)
> > blink_4hz: LED blink on any even at 4Hz (125ms)
> > blink_8hz: LED blink on any even at 8Hz (62ms)
> 
> I would suggest separating blink intervals into a patch of their own,
> because they are orthogonal to the other modes. Most PHYs are not
> going to support them, or they have to be the same across all LEDs, or
> don't for example make sense with duplex etc. We need to first
> concentrate on the basics, get that correct. Then we can add nice to
> have features like this.
> 
>      Andrew

Okok will just drop this. I agree that adds additional complexity to the
code and would make this even harder to merge.

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support
  2022-05-05  1:55   ` Andrew Lunn
@ 2022-05-05 13:33     ` Ansuel Smith
  2022-05-05 14:24       ` Andrew Lunn
  0 siblings, 1 reply; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:33 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 03:55:26AM +0200, Andrew Lunn wrote:
> > +		ret = fwnode_property_read_string(led, "default-state", &state);
> 
> You should probably use led_default_state led_init_default_state_get()
> 
>     Andrew

Oh, didn't know it was a thing, is this new? Anyway thanks.

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example
  2022-05-04 17:15   ` Rob Herring
@ 2022-05-05 13:34     ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 13:34 UTC (permalink / raw)
  To: Rob Herring
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Wed, May 04, 2022 at 12:15:48PM -0500, Rob Herring wrote:
> On Tue, May 03, 2022 at 05:16:33PM +0200, Ansuel Smith wrote:
> > Add LEDs definition example for qca8k using the offload trigger as the
> > default trigger and add all the supported offload triggers by the
> > switch.
> > 
> > Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> > ---
> >  .../devicetree/bindings/net/dsa/qca8k.yaml    | 20 +++++++++++++++++++
> >  1 file changed, 20 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/net/dsa/qca8k.yaml b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
> > index f3c88371d76c..9b46ef645a2d 100644
> > --- a/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
> > +++ b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
> > @@ -65,6 +65,8 @@ properties:
> >                   internal mdio access is used.
> >                   With the legacy mapping the reg corresponding to the internal
> >                   mdio is the switch reg with an offset of -1.
> > +                 Each phy have at least 3 LEDs connected and can be declared
> 
> s/at least/up to/ ?
> 
> Or your example is wrong with only 2.
>

Up to. Internally the regs are there but 99% of the times OEM just
connect 2 of 3 LEDs. Will fix. 

> > +                 using the standard LEDs structure.
> >  
> >  patternProperties:
> >    "^(ethernet-)?ports$":
> > @@ -287,6 +289,24 @@ examples:
> >  
> >                  internal_phy_port1: ethernet-phy@0 {
> >                      reg = <0>;
> > +
> > +                    leds {
> > +                        led@0 {
> > +                            reg = <0>;
> > +                            color = <LED_COLOR_ID_WHITE>;
> > +                            function = LED_FUNCTION_LAN;
> > +                            function-enumerator = <1>;
> > +                            linux,default-trigger = "netdev";
> > +                        };
> > +
> > +                        led@1 {
> > +                            reg = <1>;
> > +                            color = <LED_COLOR_ID_AMBER>;
> > +                            function = LED_FUNCTION_LAN;
> > +                            function-enumerator = <1>;
> > +                            linux,default-trigger = "netdev";
> > +                        };
> > +                    };
> >                  };
> >  
> >                  internal_phy_port2: ethernet-phy@1 {
> > -- 
> > 2.34.1
> > 
> > 

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks
  2022-05-05 13:29     ` Ansuel Smith
@ 2022-05-05 14:21       ` Andrew Lunn
  2022-05-05 14:43         ` Ansuel Smith
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05 14:21 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 03:29:09PM +0200, Ansuel Smith wrote:
> On Thu, May 05, 2022 at 03:10:21AM +0200, Andrew Lunn wrote:
> > > @@ -400,7 +400,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
> > >  
> > >  	cancel_delayed_work_sync(&trigger_data->work);
> > >  
> > > -	spin_lock_bh(&trigger_data->lock);
> > > +	mutex_lock(&trigger_data->lock);
> > 
> > I'm not sure you can convert a spin_lock_bh() in a mutex_lock().
> > 
> > Did you check this? What context is the notifier called in?
> > 
> >     Andrew
> 
> I had to do this because qca8k use completion to set the value and that
> can sleep... Mhhh any idea how to handle this?

First step is to define what the lock is protecting. Once you know
that, you should be able to see what you can do without actually
holding the lock.

Do you need the lock when actually setting the LED?

Or is the lock protecting state information inside trigger_data?

Can you make a copy of what you need from trigger_data while holding
the lock, release it and then set the LED?

Maybe a nested mutex and a spin lock? The spin lock protecting
trigger_data, and the mutex protecting setting the LED?

	      Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support
  2022-05-05 13:33     ` Ansuel Smith
@ 2022-05-05 14:24       ` Andrew Lunn
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew Lunn @ 2022-05-05 14:24 UTC (permalink / raw)
  To: Ansuel Smith
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 03:33:07PM +0200, Ansuel Smith wrote:
> On Thu, May 05, 2022 at 03:55:26AM +0200, Andrew Lunn wrote:
> > > +		ret = fwnode_property_read_string(led, "default-state", &state);
> > 
> > You should probably use led_default_state led_init_default_state_get()
> > 
> >     Andrew
> 
> Oh, didn't know it was a thing, is this new? Anyway thanks.

No idea. But my thinking was, you cannot be the first to implement the
binding, there probably exists some helpers somewhere...

General rule of thumb: Assume somebody has already been there and done
it, you just need to find it and reuse it.

    Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks
  2022-05-05 14:21       ` Andrew Lunn
@ 2022-05-05 14:43         ` Ansuel Smith
  0 siblings, 0 replies; 34+ messages in thread
From: Ansuel Smith @ 2022-05-05 14:43 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Vladimir Oltean,
	David S. Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Jonathan Corbet, Pavel Machek, John Crispin,
	netdev, devicetree, linux-kernel, linux-doc, linux-leds

On Thu, May 05, 2022 at 04:21:54PM +0200, Andrew Lunn wrote:
> On Thu, May 05, 2022 at 03:29:09PM +0200, Ansuel Smith wrote:
> > On Thu, May 05, 2022 at 03:10:21AM +0200, Andrew Lunn wrote:
> > > > @@ -400,7 +400,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
> > > >  
> > > >  	cancel_delayed_work_sync(&trigger_data->work);
> > > >  
> > > > -	spin_lock_bh(&trigger_data->lock);
> > > > +	mutex_lock(&trigger_data->lock);
> > > 
> > > I'm not sure you can convert a spin_lock_bh() in a mutex_lock().
> > > 
> > > Did you check this? What context is the notifier called in?
> > > 
> > >     Andrew
> > 
> > I had to do this because qca8k use completion to set the value and that
> > can sleep... Mhhh any idea how to handle this?
> 
> First step is to define what the lock is protecting. Once you know
> that, you should be able to see what you can do without actually
> holding the lock.
> 

From what I can see in the code, the lock is really used for the
work. It there to handle the device_name store/show and to not remove
the dev while a work is in progress...

But I can also see that on store and on netdev_trig the work is
cancelled, so in theory the problem of "removing dev while a work is in
progress" should never happen (as we cancel the work before anyway).

So I see the only real use for the lock is the device_name_show. 

> Do you need the lock when actually setting the LED?
> 
> Or is the lock protecting state information inside trigger_data?
> 
> Can you make a copy of what you need from trigger_data while holding
> the lock, release it and then set the LED?
> 
> Maybe a nested mutex and a spin lock? The spin lock protecting
> trigger_data, and the mutex protecting setting the LED?

I need to check what can I do to move the configuration phase outside
the lock.
Just to make sure the spinlock ot mutex conversion is not doable cause
we are locking unter a netdev notify or for other reason?

> 
> 	      Andrew

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2022-05-05 14:43 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-03 15:16 [RFC PATCH v6 00/11] Adds support for PHY LEDs with offload triggers Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 01/11] leds: add support for hardware driven LEDs Ansuel Smith
2022-05-04 22:19   ` Andrew Lunn
2022-05-05 13:01     ` Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 02/11] leds: add function to configure hardware controlled LED Ansuel Smith
2022-05-04 23:23   ` Andrew Lunn
2022-05-05 13:02     ` Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 03/11] leds: trigger: netdev: drop NETDEV_LED_MODE_LINKUP from mode Ansuel Smith
2022-05-04 23:25   ` Andrew Lunn
2022-05-05 13:05     ` Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 04/11] leds: trigger: netdev: rename and expose NETDEV trigger enum modes Ansuel Smith
2022-05-05  0:29   ` Andrew Lunn
2022-05-03 15:16 ` [RFC PATCH v6 05/11] leds: trigger: netdev: convert device attr to macro Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 06/11] leds: trigger: netdev: add hardware control support Ansuel Smith
2022-05-05  1:00   ` Andrew Lunn
2022-05-05 13:27     ` Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 07/11] leds: trigger: netdev: use mutex instead of spinlocks Ansuel Smith
2022-05-05  1:10   ` Andrew Lunn
2022-05-05 13:29     ` Ansuel Smith
2022-05-05 14:21       ` Andrew Lunn
2022-05-05 14:43         ` Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 08/11] leds: trigger: netdev: add available mode sysfs attr Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 09/11] leds: trigger: netdev: add additional hardware only triggers Ansuel Smith
2022-05-05  1:29   ` Andrew Lunn
2022-05-05 13:30     ` Ansuel Smith
2022-05-03 15:16 ` [RFC PATCH v6 10/11] net: dsa: qca8k: add LEDs support Ansuel Smith
2022-05-05  1:46   ` Andrew Lunn
2022-05-05  1:55   ` Andrew Lunn
2022-05-05 13:33     ` Ansuel Smith
2022-05-05 14:24       ` Andrew Lunn
2022-05-03 15:16 ` [RFC PATCH v6 11/11] dt-bindings: net: dsa: qca8k: add LEDs definition example Ansuel Smith
2022-05-03 22:21   ` Rob Herring
2022-05-04 17:15   ` Rob Herring
2022-05-05 13:34     ` Ansuel Smith

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).