Quellcode durchsuchen

generic: 6.1: backport patch adding support for LED PHY

Backport patch adding support for LED PHY directly in PHY ops struct.
Add new PHYLIB_LEDS config and refresh patches.

Signed-off-by: Christian Marangi <[email protected]>
Christian Marangi vor 2 Jahren
Ursprung
Commit
6b63fcf8df

+ 65 - 0
target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch

@@ -0,0 +1,65 @@
+From 4bb7aac70b5d8a4bddf4ee0791b834f9f56883d2 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <[email protected]>
+Date: Thu, 20 Apr 2023 10:45:51 +0200
+Subject: [PATCH] net: phy: fix circular LEDS_CLASS dependencies
+
+The CONFIG_PHYLIB symbol is selected by a number of device drivers that
+need PHY support, but it now has a dependency on CONFIG_LEDS_CLASS,
+which may not be enabled, causing build failures.
+
+Avoid the risk of missing and circular dependencies by guarding the
+phylib LED support itself in another Kconfig symbol that can only be
+enabled if the dependency is met.
+
+This could be made a hidden symbol and always enabled when both CONFIG_OF
+and CONFIG_LEDS_CLASS are reachable from the phylib, but there may be an
+advantage in having users see this option when they have a misconfigured
+kernel without built-in LED support.
+
+Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
+Signed-off-by: Arnd Bergmann <[email protected]>
+Reviewed-by: Andrew Lunn <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/Kconfig      | 9 ++++++++-
+ drivers/net/phy/phy_device.c | 3 ++-
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -18,7 +18,6 @@ menuconfig PHYLIB
+ 	depends on NETDEVICES
+ 	select MDIO_DEVICE
+ 	select MDIO_DEVRES
+-	depends on LEDS_CLASS || LEDS_CLASS=n
+ 	help
+ 	  Ethernet controllers are usually attached to PHY
+ 	  devices.  This option provides infrastructure for
+@@ -45,6 +44,14 @@ config LED_TRIGGER_PHY
+ 		<Speed in megabits>Mbps OR <Speed in gigabits>Gbps OR link
+ 		for any speed known to the PHY.
+ 
++config PHYLIB_LEDS
++	bool "Support probing LEDs from device tree"
++	depends on LEDS_CLASS=y || LEDS_CLASS=PHYLIB
++	depends on OF
++	default y
++	help
++	  When LED class support is enabled, phylib can automatically
++	  probe LED setting from device tree.
+ 
+ config FIXED_PHY
+ 	tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs"
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -3208,7 +3208,8 @@ static int phy_probe(struct device *dev)
+ 	/* Get the LEDs from the device tree, and instantiate standard
+ 	 * LEDs for them.
+ 	 */
+-	err = of_phy_leds(phydev);
++	if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
++		err = of_phy_leds(phydev);
+ 
+ out:
+ 	/* Re-assert the reset signal on error */

+ 43 - 0
target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch

@@ -0,0 +1,43 @@
+From aed8fdad2152d946add50bec00a6b07c457bdcdf Mon Sep 17 00:00:00 2001
+From: Alexander Stein <[email protected]>
+Date: Mon, 24 Apr 2023 16:16:48 +0200
+Subject: [PATCH] net: phy: Fix reading LED reg property
+
+'reg' is always encoded in 32 bits, thus it has to be read using the
+function with the corresponding bit width.
+
+Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
+Signed-off-by: Alexander Stein <[email protected]>
+Reviewed-by: Andrew Lunn <[email protected]>
+Reviewed-by: Florian Fainelli <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/phy_device.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -2971,6 +2971,7 @@ static int of_phy_led(struct phy_device
+ 	struct led_init_data init_data = {};
+ 	struct led_classdev *cdev;
+ 	struct phy_led *phyled;
++	u32 index;
+ 	int err;
+ 
+ 	phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL);
+@@ -2980,10 +2981,13 @@ static int of_phy_led(struct phy_device
+ 	cdev = &phyled->led_cdev;
+ 	phyled->phydev = phydev;
+ 
+-	err = of_property_read_u8(led, "reg", &phyled->index);
++	err = of_property_read_u32(led, "reg", &index);
+ 	if (err)
+ 		return err;
++	if (index > U8_MAX)
++		return -EINVAL;
+ 
++	phyled->index = index;
+ 	if (phydev->drv->led_brightness_set)
+ 		cdev->brightness_set_blocking = phy_led_set_brightness;
+ 	if (phydev->drv->led_blink_set)

+ 67 - 0
target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch

@@ -0,0 +1,67 @@
+From c938ab4da0eb1620ae3243b0b24c572ddfc318fc Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <[email protected]>
+Date: Sat, 17 Jun 2023 17:55:00 +0200
+Subject: [PATCH] net: phy: Manual remove LEDs to ensure correct ordering
+
+If the core is left to remove the LEDs via devm_, it is performed too
+late, after the PHY driver is removed from the PHY. This results in
+dereferencing a NULL pointer when the LED core tries to turn the LED
+off before destroying the LED.
+
+Manually unregister the LEDs at a safe point in phy_remove.
+
+Cc: [email protected]
+Reported-by: Florian Fainelli <[email protected]>
+Suggested-by: Florian Fainelli <[email protected]>
+Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
+Signed-off-by: Andrew Lunn <[email protected]>
+Signed-off-by: David S. Miller <[email protected]>
+---
+ drivers/net/phy/phy_device.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -2964,6 +2964,15 @@ static int phy_led_blink_set(struct led_
+ 	return err;
+ }
+ 
++static void phy_leds_unregister(struct phy_device *phydev)
++{
++	struct phy_led *phyled;
++
++	list_for_each_entry(phyled, &phydev->leds, list) {
++		led_classdev_unregister(&phyled->led_cdev);
++	}
++}
++
+ static int of_phy_led(struct phy_device *phydev,
+ 		      struct device_node *led)
+ {
+@@ -2997,7 +3006,7 @@ static int of_phy_led(struct phy_device
+ 	init_data.fwnode = of_fwnode_handle(led);
+ 	init_data.devname_mandatory = true;
+ 
+-	err = devm_led_classdev_register_ext(dev, cdev, &init_data);
++	err = led_classdev_register_ext(dev, cdev, &init_data);
+ 	if (err)
+ 		return err;
+ 
+@@ -3026,6 +3035,7 @@ static int of_phy_leds(struct phy_device
+ 		err = of_phy_led(phydev, led);
+ 		if (err) {
+ 			of_node_put(led);
++			phy_leds_unregister(phydev);
+ 			return err;
+ 		}
+ 	}
+@@ -3229,6 +3239,9 @@ static int phy_remove(struct device *dev
+ 
+ 	cancel_delayed_work_sync(&phydev->state_queue);
+ 
++	if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
++		phy_leds_unregister(phydev);
++
+ 	phydev->state = PHY_DOWN;
+ 
+ 	sfp_bus_del_upstream(phydev->sfp_bus);

+ 44 - 0
target/linux/generic/backport-6.1/824-v6.5-leds-trigger-netdev-Remove-NULL-check-before-dev_-pu.patch

@@ -0,0 +1,44 @@
+From af7320ecae0ce646fd2c4a88341a3fbc243553da Mon Sep 17 00:00:00 2001
+From: Yang Li <[email protected]>
+Date: Thu, 11 May 2023 15:08:20 +0800
+Subject: [PATCH] leds: trigger: netdev: Remove NULL check before dev_{put,
+ hold}
+
+The call netdev_{put, hold} of dev_{put, hold} will check NULL,
+so there is no need to check before using dev_{put, hold},
+remove it to silence the warnings:
+
+./drivers/leds/trigger/ledtrig-netdev.c:291:3-10: WARNING: NULL check before dev_{put, hold} functions is not needed.
+./drivers/leds/trigger/ledtrig-netdev.c:401:2-9: WARNING: NULL check before dev_{put, hold} functions is not needed.
+
+Reported-by: Abaci Robot <[email protected]>
+Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=4929
+Signed-off-by: Yang Li <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Lee Jones <[email protected]>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -462,8 +462,7 @@ static int netdev_trig_notify(struct not
+ 		get_device_state(trigger_data);
+ 		fallthrough;
+ 	case NETDEV_REGISTER:
+-		if (trigger_data->net_dev)
+-			dev_put(trigger_data->net_dev);
++		dev_put(trigger_data->net_dev);
+ 		dev_hold(dev);
+ 		trigger_data->net_dev = dev;
+ 		break;
+@@ -594,8 +593,7 @@ static void netdev_trig_deactivate(struc
+ 
+ 	cancel_delayed_work_sync(&trigger_data->work);
+ 
+-	if (trigger_data->net_dev)
+-		dev_put(trigger_data->net_dev);
++	dev_put(trigger_data->net_dev);
+ 
+ 	kfree(trigger_data);
+ }

+ 31 - 0
target/linux/generic/backport-6.1/825-v6.5-leds-trigger-netdev-uninitialized-variable-in-netdev.patch

@@ -0,0 +1,31 @@
+From 97c5209b3d374a25ebdb4c2ea9e9c1b121768da0 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <[email protected]>
+Date: Wed, 14 Jun 2023 10:03:59 +0300
+Subject: [PATCH] leds: trigger: netdev: uninitialized variable in
+ netdev_trig_activate()
+
+The qca8k_cled_hw_control_get() function which implements ->hw_control_get
+sets the appropriate bits but does not clear them.  This leads to an
+uninitialized variable bug.  Fix this by setting mode to zero at the
+start.
+
+Fixes: e0256648c831 ("net: dsa: qca8k: implement hw_control ops")
+Signed-off-by: Dan Carpenter <[email protected]>
+Reviewed-by: Andrew Lunn <[email protected]>
+Acked-by: Lee Jones <[email protected]>
+Signed-off-by: David S. Miller <[email protected]>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -538,7 +538,7 @@ static void netdev_trig_work(struct work
+ static int netdev_trig_activate(struct led_classdev *led_cdev)
+ {
+ 	struct led_netdev_data *trigger_data;
+-	unsigned long mode;
++	unsigned long mode = 0;
+ 	struct device *dev;
+ 	int rc;
+ 

+ 57 - 0
target/linux/generic/backport-6.1/826-v6.6-01-led-trig-netdev-Fix-requesting-offload-device.patch

@@ -0,0 +1,57 @@
+From 7df1f14c04cbb1950e79c19793420f87227c3e80 Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <[email protected]>
+Date: Tue, 8 Aug 2023 23:04:33 +0200
+Subject: [PATCH 1/4] led: trig: netdev: Fix requesting offload device
+
+When the netdev trigger is activates, it tries to determine what
+device the LED blinks for, and what the current blink mode is.
+
+The documentation for hw_control_get() says:
+
+	 * Return 0 on success, a negative error number on failing parsing the
+	 * initial mode. Error from this function is NOT FATAL as the device
+	 * may be in a not supported initial state by the attached LED trigger.
+	 */
+
+For the Marvell PHY and the Armada 370-rd board, the initial LED blink
+mode is not supported by the trigger, so it returns an error. This
+resulted in not getting the device the LED is blinking for. As a
+result, the device is unknown and offloaded is never performed.
+
+Change to condition to always get the device if offloading is
+supported, and reduce the scope of testing for an error from
+hw_control_get() to skip setting trigger internal state if there is an
+error.
+
+Reviewed-by: Simon Horman <[email protected]>
+Signed-off-by: Andrew Lunn <[email protected]>
+Tested-by: Daniel Golle <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -564,15 +564,17 @@ static int netdev_trig_activate(struct l
+ 	/* Check if hw control is active by default on the LED.
+ 	 * Init already enabled mode in hw control.
+ 	 */
+-	if (supports_hw_control(led_cdev) &&
+-	    !led_cdev->hw_control_get(led_cdev, &mode)) {
++	if (supports_hw_control(led_cdev)) {
+ 		dev = led_cdev->hw_control_get_device(led_cdev);
+ 		if (dev) {
+ 			const char *name = dev_name(dev);
+ 
+ 			set_device_name(trigger_data, name, strlen(name));
+ 			trigger_data->hw_control = true;
+-			trigger_data->mode = mode;
++
++			rc = led_cdev->hw_control_get(led_cdev, &mode);
++			if (!rc)
++				trigger_data->mode = mode;
+ 		}
+ 	}
+ 

+ 149 - 0
target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch

@@ -0,0 +1,149 @@
+From 1dcc03c9a7a824a31eaaecdfaa03542fb25feb6c Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <[email protected]>
+Date: Tue, 8 Aug 2023 23:04:34 +0200
+Subject: [PATCH 2/4] net: phy: phy_device: Call into the PHY driver to set LED
+ offload
+
+Linux LEDs can be requested to perform hardware accelerated blinking
+to indicate link, RX, TX etc. Pass the rules for blinking to the PHY
+driver, if it implements the ops needed to determine if a given
+pattern can be offloaded, to offload it, and what the current offload
+is. Additionally implement the op needed to get what device the LED is
+for.
+
+Reviewed-by: Simon Horman <[email protected]>
+Signed-off-by: Andrew Lunn <[email protected]>
+Tested-by: Daniel Golle <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/phy_device.c | 68 ++++++++++++++++++++++++++++++++++++
+ include/linux/phy.h          | 33 +++++++++++++++++
+ 2 files changed, 101 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -2964,6 +2964,61 @@ static int phy_led_blink_set(struct led_
+ 	return err;
+ }
+ 
++static __maybe_unused struct device *
++phy_led_hw_control_get_device(struct led_classdev *led_cdev)
++{
++	struct phy_led *phyled = to_phy_led(led_cdev);
++	struct phy_device *phydev = phyled->phydev;
++
++	if (phydev->attached_dev)
++		return &phydev->attached_dev->dev;
++	return NULL;
++}
++
++static int __maybe_unused
++phy_led_hw_control_get(struct led_classdev *led_cdev,
++		       unsigned long *rules)
++{
++	struct phy_led *phyled = to_phy_led(led_cdev);
++	struct phy_device *phydev = phyled->phydev;
++	int err;
++
++	mutex_lock(&phydev->lock);
++	err = phydev->drv->led_hw_control_get(phydev, phyled->index, rules);
++	mutex_unlock(&phydev->lock);
++
++	return err;
++}
++
++static int __maybe_unused
++phy_led_hw_control_set(struct led_classdev *led_cdev,
++		       unsigned long rules)
++{
++	struct phy_led *phyled = to_phy_led(led_cdev);
++	struct phy_device *phydev = phyled->phydev;
++	int err;
++
++	mutex_lock(&phydev->lock);
++	err = phydev->drv->led_hw_control_set(phydev, phyled->index, rules);
++	mutex_unlock(&phydev->lock);
++
++	return err;
++}
++
++static __maybe_unused int phy_led_hw_is_supported(struct led_classdev *led_cdev,
++						  unsigned long rules)
++{
++	struct phy_led *phyled = to_phy_led(led_cdev);
++	struct phy_device *phydev = phyled->phydev;
++	int err;
++
++	mutex_lock(&phydev->lock);
++	err = phydev->drv->led_hw_is_supported(phydev, phyled->index, rules);
++	mutex_unlock(&phydev->lock);
++
++	return err;
++}
++
+ static void phy_leds_unregister(struct phy_device *phydev)
+ {
+ 	struct phy_led *phyled;
+@@ -3001,6 +3056,19 @@ static int of_phy_led(struct phy_device
+ 		cdev->brightness_set_blocking = phy_led_set_brightness;
+ 	if (phydev->drv->led_blink_set)
+ 		cdev->blink_set = phy_led_blink_set;
++
++#ifdef CONFIG_LEDS_TRIGGERS
++	if (phydev->drv->led_hw_is_supported &&
++	    phydev->drv->led_hw_control_set &&
++	    phydev->drv->led_hw_control_get) {
++		cdev->hw_control_is_supported = phy_led_hw_is_supported;
++		cdev->hw_control_set = phy_led_hw_control_set;
++		cdev->hw_control_get = phy_led_hw_control_get;
++		cdev->hw_control_trigger = "netdev";
++	}
++
++	cdev->hw_control_get_device = phy_led_hw_control_get_device;
++#endif
+ 	cdev->max_brightness = 1;
+ 	init_data.devicename = dev_name(&phydev->mdio.dev);
+ 	init_data.fwnode = of_fwnode_handle(led);
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1013,6 +1013,39 @@ struct phy_driver {
+ 	int (*led_blink_set)(struct phy_device *dev, u8 index,
+ 			     unsigned long *delay_on,
+ 			     unsigned long *delay_off);
++	/**
++	 * @led_hw_is_supported: Can the HW support the given rules.
++	 * @dev: PHY device which has the LED
++	 * @index: Which LED of the PHY device
++	 * @rules The core is interested in these rules
++	 *
++	 * Return 0 if yes,  -EOPNOTSUPP if not, or an error code.
++	 */
++	int (*led_hw_is_supported)(struct phy_device *dev, u8 index,
++				   unsigned long rules);
++	/**
++	 * @led_hw_control_set: Set the HW to control the LED
++	 * @dev: PHY device which has the LED
++	 * @index: Which LED of the PHY device
++	 * @rules The rules used to control the LED
++	 *
++	 * Returns 0, or a an error code.
++	 */
++	int (*led_hw_control_set)(struct phy_device *dev, u8 index,
++				  unsigned long rules);
++	/**
++	 * @led_hw_control_get: Get how the HW is controlling the LED
++	 * @dev: PHY device which has the LED
++	 * @index: Which LED of the PHY device
++	 * @rules Pointer to the rules used to control the LED
++	 *
++	 * Set *@rules to how the HW is currently blinking. Returns 0
++	 * on success, or a error code if the current blinking cannot
++	 * be represented in rules, or some other error happens.
++	 */
++	int (*led_hw_control_get)(struct phy_device *dev, u8 index,
++				  unsigned long *rules);
++
+ };
+ #define to_phy_driver(d) container_of(to_mdio_common_driver(d),		\
+ 				      struct phy_driver, mdiodrv)

+ 344 - 0
target/linux/generic/backport-6.1/826-v6.6-03-net-phy-marvell-Add-support-for-offloading-LED-blink.patch

@@ -0,0 +1,344 @@
+From 460b0b648fab24f576c481424e0de5479ffb9786 Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <[email protected]>
+Date: Tue, 8 Aug 2023 23:04:35 +0200
+Subject: [PATCH 3/4] net: phy: marvell: Add support for offloading LED
+ blinking
+
+Add the code needed to indicate if a given blinking pattern can be
+offloaded, to offload a pattern and to try to return the current
+pattern.
+
+Reviewed-by: Simon Horman <[email protected]>
+Signed-off-by: Andrew Lunn <[email protected]>
+Tested-by: Daniel Golle <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/marvell.c | 281 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 281 insertions(+)
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -2893,6 +2893,272 @@ static int m88e1318_led_blink_set(struct
+ 			       MII_88E1318S_PHY_LED_FUNC, reg);
+ }
+ 
++struct marvell_led_rules {
++	int mode;
++	unsigned long rules;
++};
++
++static const struct marvell_led_rules marvell_led0[] = {
++	{
++		.mode = 0,
++		.rules = BIT(TRIGGER_NETDEV_LINK),
++	},
++	{
++		.mode = 1,
++		.rules = (BIT(TRIGGER_NETDEV_LINK) |
++			  BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 3,
++		.rules = (BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 4,
++		.rules = (BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 5,
++		.rules = BIT(TRIGGER_NETDEV_TX),
++	},
++	{
++		.mode = 6,
++		.rules = BIT(TRIGGER_NETDEV_LINK),
++	},
++	{
++		.mode = 7,
++		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
++	},
++	{
++		.mode = 8,
++		.rules = 0,
++	},
++};
++
++static const struct marvell_led_rules marvell_led1[] = {
++	{
++		.mode = 1,
++		.rules = (BIT(TRIGGER_NETDEV_LINK) |
++			  BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 2,
++		.rules = (BIT(TRIGGER_NETDEV_LINK) |
++			  BIT(TRIGGER_NETDEV_RX)),
++	},
++	{
++		.mode = 3,
++		.rules = (BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 4,
++		.rules = (BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 6,
++		.rules = (BIT(TRIGGER_NETDEV_LINK_100) |
++			  BIT(TRIGGER_NETDEV_LINK_1000)),
++	},
++	{
++		.mode = 7,
++		.rules = BIT(TRIGGER_NETDEV_LINK_100),
++	},
++	{
++		.mode = 8,
++		.rules = 0,
++	},
++};
++
++static const struct marvell_led_rules marvell_led2[] = {
++	{
++		.mode = 0,
++		.rules = BIT(TRIGGER_NETDEV_LINK),
++	},
++	{
++		.mode = 1,
++		.rules = (BIT(TRIGGER_NETDEV_LINK) |
++			  BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 3,
++		.rules = (BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 4,
++		.rules = (BIT(TRIGGER_NETDEV_RX) |
++			  BIT(TRIGGER_NETDEV_TX)),
++	},
++	{
++		.mode = 5,
++		.rules = BIT(TRIGGER_NETDEV_TX),
++	},
++	{
++		.mode = 6,
++		.rules = (BIT(TRIGGER_NETDEV_LINK_10) |
++			  BIT(TRIGGER_NETDEV_LINK_1000)),
++	},
++	{
++		.mode = 7,
++		.rules = BIT(TRIGGER_NETDEV_LINK_10),
++	},
++	{
++		.mode = 8,
++		.rules = 0,
++	},
++};
++
++static int marvell_find_led_mode(unsigned long rules,
++				 const struct marvell_led_rules *marvell_rules,
++				 int count,
++				 int *mode)
++{
++	int i;
++
++	for (i = 0; i < count; i++) {
++		if (marvell_rules[i].rules == rules) {
++			*mode = marvell_rules[i].mode;
++			return 0;
++		}
++	}
++	return -EOPNOTSUPP;
++}
++
++static int marvell_get_led_mode(u8 index, unsigned long rules, int *mode)
++{
++	int ret;
++
++	switch (index) {
++	case 0:
++		ret = marvell_find_led_mode(rules, marvell_led0,
++					    ARRAY_SIZE(marvell_led0), mode);
++		break;
++	case 1:
++		ret = marvell_find_led_mode(rules, marvell_led1,
++					    ARRAY_SIZE(marvell_led1), mode);
++		break;
++	case 2:
++		ret = marvell_find_led_mode(rules, marvell_led2,
++					    ARRAY_SIZE(marvell_led2), mode);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static int marvell_find_led_rules(unsigned long *rules,
++				  const struct marvell_led_rules *marvell_rules,
++				  int count,
++				  int mode)
++{
++	int i;
++
++	for (i = 0; i < count; i++) {
++		if (marvell_rules[i].mode == mode) {
++			*rules = marvell_rules[i].rules;
++			return 0;
++		}
++	}
++	return -EOPNOTSUPP;
++}
++
++static int marvell_get_led_rules(u8 index, unsigned long *rules, int mode)
++{
++	int ret;
++
++	switch (index) {
++	case 0:
++		ret = marvell_find_led_rules(rules, marvell_led0,
++					     ARRAY_SIZE(marvell_led0), mode);
++		break;
++	case 1:
++		ret = marvell_find_led_rules(rules, marvell_led1,
++					     ARRAY_SIZE(marvell_led1), mode);
++		break;
++	case 2:
++		ret = marvell_find_led_rules(rules, marvell_led2,
++					     ARRAY_SIZE(marvell_led2), mode);
++		break;
++	default:
++		ret = -EOPNOTSUPP;
++	}
++
++	return ret;
++}
++
++static int m88e1318_led_hw_is_supported(struct phy_device *phydev, u8 index,
++					unsigned long rules)
++{
++	int mode, ret;
++
++	switch (index) {
++	case 0:
++	case 1:
++	case 2:
++		ret = marvell_get_led_mode(index, rules, &mode);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static int m88e1318_led_hw_control_set(struct phy_device *phydev, u8 index,
++				       unsigned long rules)
++{
++	int mode, ret, reg;
++
++	switch (index) {
++	case 0:
++	case 1:
++	case 2:
++		ret = marvell_get_led_mode(index, rules, &mode);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	if (ret < 0)
++		return ret;
++
++	reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
++			     MII_88E1318S_PHY_LED_FUNC);
++	if (reg < 0)
++		return reg;
++
++	reg &= ~(0xf << (4 * index));
++	reg |= mode << (4 * index);
++	return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
++			       MII_88E1318S_PHY_LED_FUNC, reg);
++}
++
++static int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index,
++				       unsigned long *rules)
++{
++	int mode, reg;
++
++	if (index > 2)
++		return -EINVAL;
++
++	reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
++			     MII_88E1318S_PHY_LED_FUNC);
++	if (reg < 0)
++		return reg;
++
++	mode = (reg >> (4 * index)) & 0xf;
++
++	return marvell_get_led_rules(index, rules, mode);
++}
++
+ static int marvell_probe(struct phy_device *phydev)
+ {
+ 	struct marvell_priv *priv;
+@@ -3144,6 +3410,9 @@ static struct phy_driver marvell_drivers
+ 		.get_stats = marvell_get_stats,
+ 		.led_brightness_set = m88e1318_led_brightness_set,
+ 		.led_blink_set = m88e1318_led_blink_set,
++		.led_hw_is_supported = m88e1318_led_hw_is_supported,
++		.led_hw_control_set = m88e1318_led_hw_control_set,
++		.led_hw_control_get = m88e1318_led_hw_control_get,
+ 	},
+ 	{
+ 		.phy_id = MARVELL_PHY_ID_88E1145,
+@@ -3252,6 +3521,9 @@ static struct phy_driver marvell_drivers
+ 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
+ 		.led_brightness_set = m88e1318_led_brightness_set,
+ 		.led_blink_set = m88e1318_led_blink_set,
++		.led_hw_is_supported = m88e1318_led_hw_is_supported,
++		.led_hw_control_set = m88e1318_led_hw_control_set,
++		.led_hw_control_get = m88e1318_led_hw_control_get,
+ 	},
+ 	{
+ 		.phy_id = MARVELL_PHY_ID_88E1540,
+@@ -3280,6 +3552,9 @@ static struct phy_driver marvell_drivers
+ 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
+ 		.led_brightness_set = m88e1318_led_brightness_set,
+ 		.led_blink_set = m88e1318_led_blink_set,
++		.led_hw_is_supported = m88e1318_led_hw_is_supported,
++		.led_hw_control_set = m88e1318_led_hw_control_set,
++		.led_hw_control_get = m88e1318_led_hw_control_get,
+ 	},
+ 	{
+ 		.phy_id = MARVELL_PHY_ID_88E1545,
+@@ -3308,6 +3583,9 @@ static struct phy_driver marvell_drivers
+ 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
+ 		.led_brightness_set = m88e1318_led_brightness_set,
+ 		.led_blink_set = m88e1318_led_blink_set,
++		.led_hw_is_supported = m88e1318_led_hw_is_supported,
++		.led_hw_control_set = m88e1318_led_hw_control_set,
++		.led_hw_control_get = m88e1318_led_hw_control_get,
+ 	},
+ 	{
+ 		.phy_id = MARVELL_PHY_ID_88E3016,
+@@ -3451,6 +3729,9 @@ static struct phy_driver marvell_drivers
+ 		.set_tunable = m88e1540_set_tunable,
+ 		.led_brightness_set = m88e1318_led_brightness_set,
+ 		.led_blink_set = m88e1318_led_blink_set,
++		.led_hw_is_supported = m88e1318_led_hw_is_supported,
++		.led_hw_control_set = m88e1318_led_hw_control_set,
++		.led_hw_control_get = m88e1318_led_hw_control_get,
+ 	},
+ };
+ 

+ 31 - 0
target/linux/generic/backport-6.1/826-v6.6-04-leds-trig-netdev-Disable-offload-on-deactivation-of-.patch

@@ -0,0 +1,31 @@
+From e8fbcc47a8e935f36f044d85f21a99acecbd7bfb Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <[email protected]>
+Date: Tue, 8 Aug 2023 23:04:36 +0200
+Subject: [PATCH 4/4] leds: trig-netdev: Disable offload on deactivation of
+ trigger
+
+Ensure that the offloading of blinking is stopped when the trigger is
+deactivated. Calling led_set_brightness() is documented as stopping
+offload and setting the LED to a constant brightness.
+
+Suggested-by: Daniel Golle <[email protected]>
+Signed-off-by: Andrew Lunn <[email protected]>
+Reviewed-by: Simon Horman <[email protected]>
+Tested-by: Daniel Golle <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -595,6 +595,8 @@ static void netdev_trig_deactivate(struc
+ 
+ 	cancel_delayed_work_sync(&trigger_data->work);
+ 
++	led_set_brightness(led_cdev, LED_OFF);
++
+ 	dev_put(trigger_data->net_dev);
+ 
+ 	kfree(trigger_data);

+ 1 - 0
target/linux/generic/config-6.1

@@ -4831,6 +4831,7 @@ CONFIG_PCI_SYSCALL=y
 # CONFIG_PHANTOM is not set
 # CONFIG_PHONET is not set
 # CONFIG_PHYLIB is not set
+# CONFIG_PHYLIB_LEDS is not set
 # CONFIG_PHYS_ADDR_T_64BIT is not set
 # CONFIG_PHY_CADENCE_DP is not set
 # CONFIG_PHY_CADENCE_DPHY is not set

+ 1 - 1
target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch

@@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -62,6 +62,80 @@ config SFP
+@@ -69,6 +69,80 @@ config SFP
  	depends on HWMON || HWMON=n
  	select MDIO_I2C