diff options
Diffstat (limited to 'target/linux/generic/backport-5.15')
28 files changed, 2672 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/814-v6.3-leds-Move-led_init_default_state_get-to-the-global-h.patch b/target/linux/generic/backport-5.15/814-v6.3-leds-Move-led_init_default_state_get-to-the-global-h.patch new file mode 100644 index 0000000000..592111fb95 --- /dev/null +++ b/target/linux/generic/backport-5.15/814-v6.3-leds-Move-led_init_default_state_get-to-the-global-h.patch @@ -0,0 +1,39 @@ +From 156a5bb89ca6f3edd2be0bfd0de15e575442927e Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> +Date: Tue, 3 Jan 2023 15:12:47 +0200 +Subject: [PATCH] leds: Move led_init_default_state_get() to the global header + +There are users inside and outside LED framework that have implemented +a local copy of led_init_default_state_get(). In order to deduplicate +that, as the first step move the declaration from LED header to the +global one. + +Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> +Signed-off-by: Lee Jones <lee@kernel.org> +Link: https://lore.kernel.org/r/20230103131256.33894-3-andriy.shevchenko@linux.intel.com +--- + drivers/leds/leds.h | 1 - + include/linux/leds.h | 2 ++ + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/leds/leds.h ++++ b/drivers/leds/leds.h +@@ -27,7 +27,6 @@ ssize_t led_trigger_read(struct file *fi + ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count); +-enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode); + + extern struct rw_semaphore leds_list_lock; + extern struct list_head leds_list; +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -63,6 +63,8 @@ struct led_init_data { + bool devname_mandatory; + }; + ++enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode); ++ + struct led_hw_trigger_type { + int dummy; + }; diff --git a/target/linux/generic/backport-5.15/815-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch b/target/linux/generic/backport-5.15/815-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch new file mode 100644 index 0000000000..dcdca90442 --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch @@ -0,0 +1,67 @@ +From 3e8b4d6277fd19d98c817576954dd6a4ff3caa2b Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 17 Apr 2023 17:17:23 +0200 +Subject: [PATCH 1/9] net: dsa: qca8k: move qca8k_port_to_phy() to header + +Move qca8k_port_to_phy() to qca8k header as it's useful for future +reference in Switch LEDs module since the same logic is applied to get +the right index of the switch port. +Make it inline as it's simple function that just decrease the port. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Michal Kubiak <michal.kubiak@intel.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 15 --------------- + drivers/net/dsa/qca/qca8k.h | 14 ++++++++++++++ + 2 files changed, 14 insertions(+), 15 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -716,21 +716,6 @@ err_clear_skb: + return ret; + } + +-static u32 +-qca8k_port_to_phy(int port) +-{ +- /* From Andrew Lunn: +- * Port 0 has no internal phy. +- * Port 1 has an internal PHY at MDIO address 0. +- * Port 2 has an internal PHY at MDIO address 1. +- * ... +- * Port 5 has an internal PHY at MDIO address 4. +- * Port 6 has no internal PHY. +- */ +- +- return port - 1; +-} +- + static int + qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) + { +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -414,6 +414,20 @@ struct qca8k_fdb { + u8 mac[6]; + }; + ++static inline u32 qca8k_port_to_phy(int port) ++{ ++ /* From Andrew Lunn: ++ * Port 0 has no internal phy. ++ * Port 1 has an internal PHY at MDIO address 0. ++ * Port 2 has an internal PHY at MDIO address 1. ++ * ... ++ * Port 5 has an internal PHY at MDIO address 4. ++ * Port 6 has no internal PHY. ++ */ ++ ++ return port - 1; ++} ++ + /* Common setup function */ + extern const struct qca8k_mib_desc ar8327_mib[]; + extern const struct regmap_access_table qca8k_readable_table; diff --git a/target/linux/generic/backport-5.15/815-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch b/target/linux/generic/backport-5.15/815-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch new file mode 100644 index 0000000000..baf4c2e4ba --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch @@ -0,0 +1,435 @@ +From 1e264f9d2918b5737023c44a23ae04def1095210 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 17 Apr 2023 17:17:24 +0200 +Subject: [PATCH 2/9] net: dsa: qca8k: add LEDs basic support + +Add LEDs basic support for qca8k Switch Family by adding basic +brightness_set() support. + +Since these LEDs refelect port status, the default label is set to +":port". DT binding should describe the color and function of the +LEDs using standard LEDs api. +Each LED always have the device name as prefix. The device name is +composed from the mii bus id and the PHY addr resulting in example +names like: +- qca8k-0.0:00:amber:lan +- qca8k-0.0:00:white:lan +- qca8k-0.0:01:amber:lan +- qca8k-0.0:01:white:lan + +These LEDs supports only blocking variant of the brightness_set() +function since they can sleep during access of the switch leds to set +the brightness. + +While at it add to the qca8k header file each mode defined by the Switch +Documentation for future use. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca/Kconfig | 8 ++ + drivers/net/dsa/qca/Makefile | 3 + + drivers/net/dsa/qca/qca8k-8xxx.c | 5 + + drivers/net/dsa/qca/qca8k-leds.c | 239 +++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 60 ++++++++ + drivers/net/dsa/qca/qca8k_leds.h | 16 +++ + 6 files changed, 331 insertions(+) + create mode 100644 drivers/net/dsa/qca/qca8k-leds.c + create mode 100644 drivers/net/dsa/qca/qca8k_leds.h + +--- a/drivers/net/dsa/qca/Kconfig ++++ b/drivers/net/dsa/qca/Kconfig +@@ -15,3 +15,11 @@ config NET_DSA_QCA8K + help + This enables support for the Qualcomm Atheros QCA8K Ethernet + switch chips. ++ ++config NET_DSA_QCA8K_LEDS_SUPPORT ++ bool "Qualcomm Atheros QCA8K Ethernet switch family LEDs support" ++ depends on NET_DSA_QCA8K ++ depends on LEDS_CLASS ++ help ++ This enabled support for LEDs present on the Qualcomm Atheros ++ QCA8K Ethernet switch chips. +--- a/drivers/net/dsa/qca/Makefile ++++ b/drivers/net/dsa/qca/Makefile +@@ -2,3 +2,6 @@ + obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o + qca8k-y += qca8k-common.o qca8k-8xxx.o ++ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT ++qca8k-y += qca8k-leds.o ++endif +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -22,6 +22,7 @@ + #include <linux/dsa/tag_qca.h> + + #include "qca8k.h" ++#include "qca8k_leds.h" + + static void + qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) +@@ -1185,6 +1186,10 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ ret = qca8k_setup_led_ctrl(priv); ++ if (ret) ++ return ret; ++ + /* Make sure MAC06 is disabled */ + ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, + QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k-leds.c +@@ -0,0 +1,239 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include <linux/regmap.h> ++#include <net/dsa.h> ++ ++#include "qca8k.h" ++#include "qca8k_leds.h" ++ ++static int ++qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info) ++{ ++ switch (port_num) { ++ case 0: ++ reg_info->reg = QCA8K_LED_CTRL_REG(led_num); ++ reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ /* Port 123 are controlled on a different reg */ ++ reg_info->reg = QCA8K_LED_CTRL3_REG; ++ reg_info->shift = QCA8K_LED_PHY123_PATTERN_EN_SHIFT(port_num, led_num); ++ break; ++ case 4: ++ reg_info->reg = QCA8K_LED_CTRL_REG(led_num); ++ reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_led_brightness_set(struct qca8k_led *led, ++ enum led_brightness brightness) ++{ ++ struct qca8k_led_pattern_en reg_info; ++ struct qca8k_priv *priv = led->priv; ++ u32 mask, val; ++ ++ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); ++ ++ val = QCA8K_LED_ALWAYS_OFF; ++ if (brightness) ++ val = QCA8K_LED_ALWAYS_ON; ++ ++ /* HW regs to control brightness is special and port 1-2-3 ++ * are placed in a different reg. ++ * ++ * To control port 0 brightness: ++ * - the 2 bit (15, 14) of: ++ * - QCA8K_LED_CTRL0_REG for led1 ++ * - QCA8K_LED_CTRL1_REG for led2 ++ * - QCA8K_LED_CTRL2_REG for led3 ++ * ++ * To control port 4: ++ * - the 2 bit (31, 30) of: ++ * - QCA8K_LED_CTRL0_REG for led1 ++ * - QCA8K_LED_CTRL1_REG for led2 ++ * - QCA8K_LED_CTRL2_REG for led3 ++ * ++ * To control port 1: ++ * - the 2 bit at (9, 8) of QCA8K_LED_CTRL3_REG are used for led1 ++ * - the 2 bit at (11, 10) of QCA8K_LED_CTRL3_REG are used for led2 ++ * - the 2 bit at (13, 12) of QCA8K_LED_CTRL3_REG are used for led3 ++ * ++ * To control port 2: ++ * - the 2 bit at (15, 14) of QCA8K_LED_CTRL3_REG are used for led1 ++ * - the 2 bit at (17, 16) of QCA8K_LED_CTRL3_REG are used for led2 ++ * - the 2 bit at (19, 18) of QCA8K_LED_CTRL3_REG are used for led3 ++ * ++ * To control port 3: ++ * - the 2 bit at (21, 20) of QCA8K_LED_CTRL3_REG are used for led1 ++ * - the 2 bit at (23, 22) of QCA8K_LED_CTRL3_REG are used for led2 ++ * - the 2 bit at (25, 24) of QCA8K_LED_CTRL3_REG are used for led3 ++ * ++ * To abstract this and have less code, we use the port and led numm ++ * to calculate the shift and the correct reg due to this problem of ++ * not having a 1:1 map of LED with the regs. ++ */ ++ if (led->port_num == 0 || led->port_num == 4) { ++ mask = QCA8K_LED_PATTERN_EN_MASK; ++ val <<= QCA8K_LED_PATTERN_EN_SHIFT; ++ } else { ++ mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; ++ } ++ ++ return regmap_update_bits(priv->regmap, reg_info.reg, ++ mask << reg_info.shift, ++ val << reg_info.shift); ++} ++ ++static int ++qca8k_cled_brightness_set_blocking(struct led_classdev *ldev, ++ enum led_brightness brightness) ++{ ++ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); ++ ++ return qca8k_led_brightness_set(led, brightness); ++} ++ ++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, ®_info); ++ ++ ret = regmap_read(priv->regmap, reg_info.reg, &val); ++ if (ret) ++ return 0; ++ ++ val >>= reg_info.shift; ++ ++ if (led->port_num == 0 || led->port_num == 4) { ++ val &= QCA8K_LED_PATTERN_EN_MASK; ++ val >>= QCA8K_LED_PATTERN_EN_SHIFT; ++ } else { ++ val &= QCA8K_LED_PHY123_PATTERN_EN_MASK; ++ } ++ ++ /* Assume brightness ON only when the LED is set to always ON */ ++ return val == QCA8K_LED_ALWAYS_ON; ++} ++ ++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 dsa_switch *ds = priv->ds; ++ enum led_default_state state; ++ struct qca8k_led *port_led; ++ int led_num, led_index; ++ 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 most 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 %d defined for port %d", ++ led_num, port_num); ++ continue; ++ } ++ ++ led_index = QCA8K_LED_PORT_INDEX(port_num, led_num); ++ ++ port_led = &priv->ports_led[led_index]; ++ port_led->port_num = port_num; ++ port_led->led_num = led_num; ++ port_led->priv = priv; ++ ++ state = led_init_default_state_get(led); ++ switch (state) { ++ case LEDS_DEFSTATE_ON: ++ port_led->cdev.brightness = 1; ++ qca8k_led_brightness_set(port_led, 1); ++ break; ++ case LEDS_DEFSTATE_KEEP: ++ port_led->cdev.brightness = ++ qca8k_led_brightness_get(port_led); ++ break; ++ default: ++ port_led->cdev.brightness = 0; ++ qca8k_led_brightness_set(port_led, 0); ++ } ++ ++ port_led->cdev.max_brightness = 1; ++ port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking; ++ init_data.default_label = ":port"; ++ init_data.fwnode = led; ++ init_data.devname_mandatory = true; ++ init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", ds->slave_mii_bus->id, ++ port_num); ++ if (!init_data.devicename) ++ return -ENOMEM; ++ ++ ret = devm_led_classdev_register_ext(priv->dev, &port_led->cdev, &init_data); ++ if (ret) ++ dev_warn(priv->dev, "Failed to init LED %d for port %d", led_num, port_num); ++ ++ kfree(init_data.devicename); ++ } ++ ++ return 0; ++} ++ ++int ++qca8k_setup_led_ctrl(struct qca8k_priv *priv) ++{ ++ struct fwnode_handle *ports, *port; ++ int port_num; ++ int ret; ++ ++ ports = device_get_named_child_node(priv->dev, "ports"); ++ if (!ports) { ++ dev_info(priv->dev, "No ports node specified in device tree!"); ++ return 0; ++ } ++ ++ fwnode_for_each_child_node(ports, port) { ++ if (fwnode_property_read_u32(port, "reg", &port_num)) ++ continue; ++ ++ /* Skip checking for CPU port 0 and CPU port 6 as not supported */ ++ if (port_num == 0 || port_num == 6) ++ continue; ++ ++ /* Each port can have at most 3 different leds attached. ++ * Switch port starts from 0 to 6, but port 0 and 6 are CPU ++ * port. The port index needs to be decreased by one to identify ++ * the correct port for LED setup. ++ */ ++ ret = qca8k_parse_port_leds(priv, port, qca8k_port_to_phy(port_num)); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/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,51 @@ + #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_PORT_COUNT 3 ++#define QCA8K_LED_COUNT ((QCA8K_NUM_PORTS - QCA8K_NUM_CPU_PORTS) * QCA8K_LED_PORT_COUNT) ++#define QCA8K_LED_RULE_COUNT 6 ++#define QCA8K_LED_RULE_MAX 11 ++#define QCA8K_LED_PORT_INDEX(_phy, _led) (((_phy) * QCA8K_LED_PORT_COUNT) + (_led)) ++ ++#define QCA8K_LED_PHY123_PATTERN_EN_SHIFT(_phy, _led) ((((_phy) - 1) * 6) + 8 + (2 * (_led))) ++#define QCA8K_LED_PHY123_PATTERN_EN_MASK GENMASK(1, 0) ++ ++#define QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT 0 ++#define QCA8K_LED_PHY4_CONTROL_RULE_SHIFT 16 ++ ++#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 +@@ -377,6 +423,19 @@ struct qca8k_mdio_cache { + u16 page; + }; + ++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; +@@ -399,6 +458,7 @@ struct qca8k_priv { + struct qca8k_mib_eth_data mib_eth_data; + struct qca8k_mdio_cache mdio_cache; + const struct qca8k_match_data *info; ++ struct qca8k_led ports_led[QCA8K_LED_COUNT]; + }; + + struct qca8k_mib_desc { +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k_leds.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef __QCA8K_LEDS_H ++#define __QCA8K_LEDS_H ++ ++/* Leds Support function */ ++#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_LEDS_H */ diff --git a/target/linux/generic/backport-5.15/815-v6.4-03-net-dsa-qca8k-add-LEDs-blink_set-support.patch b/target/linux/generic/backport-5.15/815-v6.4-03-net-dsa-qca8k-add-LEDs-blink_set-support.patch new file mode 100644 index 0000000000..231c4156df --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-03-net-dsa-qca8k-add-LEDs-blink_set-support.patch @@ -0,0 +1,74 @@ +From 91acadcc6e599dfc62717abcdad58a459cfb1684 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 17 Apr 2023 17:17:25 +0200 +Subject: [PATCH 3/9] net: dsa: qca8k: add LEDs blink_set() support + +Add LEDs blink_set() support to qca8k Switch Family. +These LEDs support hw accellerated blinking at a fixed rate +of 4Hz. + +Reject any other value since not supported by the LEDs switch. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Acked-by: Pavel Machek <pavel@ucw.cz> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca/qca8k-leds.c | 38 ++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-leds.c ++++ b/drivers/net/dsa/qca/qca8k-leds.c +@@ -128,6 +128,43 @@ qca8k_led_brightness_get(struct qca8k_le + } + + 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); ++ u32 mask, val = QCA8K_LED_ALWAYS_BLINK_4HZ; ++ 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, ®_info); ++ ++ if (led->port_num == 0 || led->port_num == 4) { ++ mask = QCA8K_LED_PATTERN_EN_MASK; ++ val <<= QCA8K_LED_PATTERN_EN_SHIFT; ++ } else { ++ mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; ++ } ++ ++ regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift, ++ val << reg_info.shift); ++ ++ return 0; ++} ++ ++static int + qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num) + { + struct fwnode_handle *led = NULL, *leds = NULL; +@@ -186,6 +223,7 @@ qca8k_parse_port_leds(struct qca8k_priv + + port_led->cdev.max_brightness = 1; + port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking; ++ port_led->cdev.blink_set = qca8k_cled_blink_set; + init_data.default_label = ":port"; + init_data.fwnode = led; + init_data.devname_mandatory = true; diff --git a/target/linux/generic/backport-5.15/815-v6.4-04-leds-Provide-stubs-for-when-CLASS_LED-NEW_LEDS-are-d.patch b/target/linux/generic/backport-5.15/815-v6.4-04-leds-Provide-stubs-for-when-CLASS_LED-NEW_LEDS-are-d.patch new file mode 100644 index 0000000000..bc905b4468 --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-04-leds-Provide-stubs-for-when-CLASS_LED-NEW_LEDS-are-d.patch @@ -0,0 +1,59 @@ +From e5029edd53937a29801ef507cee12e657ff31ea9 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 17 Apr 2023 17:17:26 +0200 +Subject: [PATCH 4/9] leds: Provide stubs for when CLASS_LED & NEW_LEDS are + disabled + +Provide stubs for devm_led_classdev_register_ext() and +led_init_default_state_get() so that LED drivers embedded within other +drivers such as PHYs and Ethernet switches still build when LEDS_CLASS +or NEW_LEDS are disabled. This also helps with Kconfig dependencies, +which are somewhat hairy for phylib and mdio and only get worse when +adding a dependency on LED_CLASS. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/leds.h | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -63,7 +63,15 @@ struct led_init_data { + bool devname_mandatory; + }; + ++#if IS_ENABLED(CONFIG_NEW_LEDS) + enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode); ++#else ++static inline enum led_default_state ++led_init_default_state_get(struct fwnode_handle *fwnode) ++{ ++ return LEDS_DEFSTATE_OFF; ++} ++#endif + + struct led_hw_trigger_type { + int dummy; +@@ -198,9 +206,19 @@ static inline int led_classdev_register( + return led_classdev_register_ext(parent, led_cdev, NULL); + } + ++#if IS_ENABLED(CONFIG_LEDS_CLASS) + int devm_led_classdev_register_ext(struct device *parent, + struct led_classdev *led_cdev, + struct led_init_data *init_data); ++#else ++static inline int ++devm_led_classdev_register_ext(struct device *parent, ++ struct led_classdev *led_cdev, ++ struct led_init_data *init_data) ++{ ++ return 0; ++} ++#endif + + static inline int devm_led_classdev_register(struct device *parent, + struct led_classdev *led_cdev) diff --git a/target/linux/generic/backport-5.15/815-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch b/target/linux/generic/backport-5.15/815-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch new file mode 100644 index 0000000000..3e60f91a2a --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch @@ -0,0 +1,191 @@ +From 01e5b728e9e43ae444e0369695a5f72209906464 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 17 Apr 2023 17:17:27 +0200 +Subject: [PATCH 5/9] net: phy: Add a binding for PHY LEDs + +Define common binding parsing for all PHY drivers with LEDs using +phylib. Parse the DT as part of the phy_probe and add LEDs to the +linux LED class infrastructure. For the moment, provide a dummy +brightness function, which will later be replaced with a call into the +PHY driver. This allows testing since the LED core might otherwise +reject an LED whose brightness cannot be set. + +Add a dependency on LED_CLASS. It either needs to be built in, or not +enabled, since a modular build can result in linker errors. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/Kconfig | 1 + + drivers/net/phy/phy_device.c | 76 ++++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 16 ++++++++ + 3 files changed, 93 insertions(+) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -18,6 +18,7 @@ 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 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -19,10 +19,12 @@ + #include <linux/interrupt.h> + #include <linux/io.h> + #include <linux/kernel.h> ++#include <linux/list.h> + #include <linux/mdio.h> + #include <linux/mii.h> + #include <linux/mm.h> + #include <linux/module.h> ++#include <linux/of.h> + #include <linux/netdevice.h> + #include <linux/phy.h> + #include <linux/phy_led_triggers.h> +@@ -641,6 +643,7 @@ struct phy_device *phy_device_create(str + device_initialize(&mdiodev->dev); + + dev->state = PHY_DOWN; ++ INIT_LIST_HEAD(&dev->leds); + + mutex_init(&dev->lock); + INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); +@@ -2942,6 +2945,74 @@ static bool phy_drv_supports_irq(struct + return phydrv->config_intr && phydrv->handle_interrupt; + } + ++/* Dummy implementation until calls into PHY driver are added */ ++static int phy_led_set_brightness(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ return 0; ++} ++ ++static int of_phy_led(struct phy_device *phydev, ++ struct device_node *led) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct led_init_data init_data = {}; ++ struct led_classdev *cdev; ++ struct phy_led *phyled; ++ int err; ++ ++ phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL); ++ if (!phyled) ++ return -ENOMEM; ++ ++ cdev = &phyled->led_cdev; ++ ++ err = of_property_read_u8(led, "reg", &phyled->index); ++ if (err) ++ return err; ++ ++ cdev->brightness_set_blocking = phy_led_set_brightness; ++ cdev->max_brightness = 1; ++ init_data.devicename = dev_name(&phydev->mdio.dev); ++ init_data.fwnode = of_fwnode_handle(led); ++ init_data.devname_mandatory = true; ++ ++ err = devm_led_classdev_register_ext(dev, cdev, &init_data); ++ if (err) ++ return err; ++ ++ list_add(&phyled->list, &phydev->leds); ++ ++ return 0; ++} ++ ++static int of_phy_leds(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct device_node *leds, *led; ++ int err; ++ ++ if (!IS_ENABLED(CONFIG_OF_MDIO)) ++ return 0; ++ ++ if (!node) ++ return 0; ++ ++ leds = of_get_child_by_name(node, "leds"); ++ if (!leds) ++ return 0; ++ ++ for_each_available_child_of_node(leds, led) { ++ err = of_phy_led(phydev, led); ++ if (err) { ++ of_node_put(led); ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ + /** + * fwnode_mdio_find_device - Given a fwnode, find the mdio_device + * @fwnode: pointer to the mdio_device's fwnode +@@ -3120,6 +3191,11 @@ static int phy_probe(struct device *dev) + /* Set the state to READY by default */ + phydev->state = PHY_READY; + ++ /* Get the LEDs from the device tree, and instantiate standard ++ * LEDs for them. ++ */ ++ err = of_phy_leds(phydev); ++ + out: + /* Re-assert the reset signal on error */ + if (err) +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -14,6 +14,7 @@ + #include <linux/compiler.h> + #include <linux/spinlock.h> + #include <linux/ethtool.h> ++#include <linux/leds.h> + #include <linux/linkmode.h> + #include <linux/netlink.h> + #include <linux/mdio.h> +@@ -582,6 +583,7 @@ struct macsec_ops; + * @phy_num_led_triggers: Number of triggers in @phy_led_triggers + * @led_link_trigger: LED trigger for link up/down + * @last_triggered: last LED trigger for link speed ++ * @leds: list of PHY LED structures + * @master_slave_set: User requested master/slave configuration + * @master_slave_get: Current master/slave advertisement + * @master_slave_state: Current master/slave configuration +@@ -668,6 +670,7 @@ struct phy_device { + + struct phy_led_trigger *led_link_trigger; + #endif ++ struct list_head leds; + + /* + * Interrupt number for this PHY +@@ -739,6 +742,19 @@ struct phy_tdr_config { + #define PHY_PAIR_ALL -1 + + /** ++ * struct phy_led: An LED driven by the PHY ++ * ++ * @list: List of LEDs ++ * @led_cdev: Standard LED class structure ++ * @index: Number of the LED ++ */ ++struct phy_led { ++ struct list_head list; ++ struct led_classdev led_cdev; ++ u8 index; ++}; ++ ++/** + * struct phy_driver - Driver structure for a particular PHY type + * + * @mdiodrv: Data common to all MDIO devices diff --git a/target/linux/generic/backport-5.15/815-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-5.15/815-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch new file mode 100644 index 0000000000..f990557cc7 --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -0,0 +1,97 @@ +From 684818189b04b095b34964ed4a3ea5249a840eab Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 17 Apr 2023 17:17:28 +0200 +Subject: [PATCH 6/9] net: phy: phy_device: Call into the PHY driver to set LED + brightness + +Linux LEDs can be software controlled via the brightness file in /sys. +LED drivers need to implement a brightness_set function which the core +will call. Implement an intermediary in phy_device, which will call +into the phy driver if it implements the necessary function. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/phy_device.c | 15 ++++++++++++--- + include/linux/phy.h | 13 +++++++++++++ + 2 files changed, 25 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2945,11 +2945,18 @@ static bool phy_drv_supports_irq(struct + return phydrv->config_intr && phydrv->handle_interrupt; + } + +-/* Dummy implementation until calls into PHY driver are added */ + static int phy_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) + { +- return 0; ++ 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_brightness_set(phydev, phyled->index, value); ++ mutex_unlock(&phydev->lock); ++ ++ return err; + } + + static int of_phy_led(struct phy_device *phydev, +@@ -2966,12 +2973,14 @@ static int of_phy_led(struct phy_device + return -ENOMEM; + + cdev = &phyled->led_cdev; ++ phyled->phydev = phydev; + + err = of_property_read_u8(led, "reg", &phyled->index); + if (err) + return err; + +- cdev->brightness_set_blocking = phy_led_set_brightness; ++ if (phydev->drv->led_brightness_set) ++ cdev->brightness_set_blocking = phy_led_set_brightness; + 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 +@@ -745,15 +745,19 @@ struct phy_tdr_config { + * struct phy_led: An LED driven by the PHY + * + * @list: List of LEDs ++ * @phydev: PHY this LED is attached to + * @led_cdev: Standard LED class structure + * @index: Number of the LED + */ + struct phy_led { + struct list_head list; ++ struct phy_device *phydev; + struct led_classdev led_cdev; + u8 index; + }; + ++#define to_phy_led(d) container_of(d, struct phy_led, led_cdev) ++ + /** + * struct phy_driver - Driver structure for a particular PHY type + * +@@ -953,6 +957,15 @@ struct phy_driver { + int (*get_sqi)(struct phy_device *dev); + /** @get_sqi_max: Get the maximum signal quality indication */ + int (*get_sqi_max)(struct phy_device *dev); ++ ++ /** ++ * @led_brightness_set: Set a PHY LED brightness. Index ++ * indicates which of the PHYs led should be set. Value ++ * follows the standard LED class meaning, e.g. LED_OFF, ++ * LED_HALF, LED_FULL. ++ */ ++ int (*led_brightness_set)(struct phy_device *dev, ++ u8 index, enum led_brightness value); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/generic/backport-5.15/815-v6.4-07-net-phy-marvell-Add-software-control-of-the-LEDs.patch b/target/linux/generic/backport-5.15/815-v6.4-07-net-phy-marvell-Add-software-control-of-the-LEDs.patch new file mode 100644 index 0000000000..053288ceca --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-07-net-phy-marvell-Add-software-control-of-the-LEDs.patch @@ -0,0 +1,112 @@ +From 2d3960e58ef7c83fe1dbf952f056b9e906cb6df8 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 17 Apr 2023 17:17:29 +0200 +Subject: [PATCH 7/9] net: phy: marvell: Add software control of the LEDs + +Add a brightness function, so the LEDs can be controlled from +software using the standard Linux LED infrastructure. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/marvell.c | 45 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 40 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -144,11 +144,13 @@ + /* WOL Event Interrupt Enable */ + #define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) + +-/* LED Timer Control Register */ +-#define MII_88E1318S_PHY_LED_TCR 0x12 +-#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) +-#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) +-#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) ++#define MII_88E1318S_PHY_LED_FUNC 0x10 ++#define MII_88E1318S_PHY_LED_FUNC_OFF (0x8) ++#define MII_88E1318S_PHY_LED_FUNC_ON (0x9) ++#define MII_88E1318S_PHY_LED_TCR 0x12 ++#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) ++#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) ++#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) + + /* Magic Packet MAC address registers */ + #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17 +@@ -2793,6 +2795,34 @@ static int marvell_hwmon_probe(struct ph + } + #endif + ++static int m88e1318_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ int reg; ++ ++ reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, ++ MII_88E1318S_PHY_LED_FUNC); ++ if (reg < 0) ++ return reg; ++ ++ switch (index) { ++ case 0: ++ case 1: ++ case 2: ++ reg &= ~(0xf << (4 * index)); ++ if (value == LED_OFF) ++ reg |= MII_88E1318S_PHY_LED_FUNC_OFF << (4 * index); ++ else ++ reg |= MII_88E1318S_PHY_LED_FUNC_ON << (4 * index); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return phy_write_paged(phydev, MII_MARVELL_LED_PAGE, ++ MII_88E1318S_PHY_LED_FUNC, reg); ++} ++ + static int marvell_probe(struct phy_device *phydev) + { + struct marvell_priv *priv; +@@ -3041,6 +3071,7 @@ static struct phy_driver marvell_drivers + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, ++ .led_brightness_set = m88e1318_led_brightness_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E1145, +@@ -3147,6 +3178,7 @@ static struct phy_driver marvell_drivers + .cable_test_start = marvell_vct7_cable_test_start, + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, ++ .led_brightness_set = m88e1318_led_brightness_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E1540, +@@ -3173,6 +3205,7 @@ static struct phy_driver marvell_drivers + .cable_test_start = marvell_vct7_cable_test_start, + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, ++ .led_brightness_set = m88e1318_led_brightness_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E1545, +@@ -3199,6 +3232,7 @@ static struct phy_driver marvell_drivers + .cable_test_start = marvell_vct7_cable_test_start, + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, ++ .led_brightness_set = m88e1318_led_brightness_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E3016, +@@ -3340,6 +3374,7 @@ static struct phy_driver marvell_drivers + .get_stats = marvell_get_stats, + .get_tunable = m88e1540_get_tunable, + .set_tunable = m88e1540_set_tunable, ++ .led_brightness_set = m88e1318_led_brightness_set, + }, + }; + diff --git a/target/linux/generic/backport-5.15/815-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-5.15/815-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch new file mode 100644 index 0000000000..4814688de4 --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -0,0 +1,73 @@ +From 4e901018432e38eab35d2a352661ce4727795be1 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 17 Apr 2023 17:17:30 +0200 +Subject: [PATCH 8/9] net: phy: phy_device: Call into the PHY driver to set LED + blinking + +Linux LEDs can be requested to perform hardware accelerated +blinking. Pass this to the PHY driver, if it implements the op. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/phy_device.c | 18 ++++++++++++++++++ + include/linux/phy.h | 12 ++++++++++++ + 2 files changed, 30 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2959,6 +2959,22 @@ static int phy_led_set_brightness(struct + return err; + } + ++static int phy_led_blink_set(struct led_classdev *led_cdev, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ 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_blink_set(phydev, phyled->index, ++ delay_on, delay_off); ++ mutex_unlock(&phydev->lock); ++ ++ return err; ++} ++ + static int of_phy_led(struct phy_device *phydev, + struct device_node *led) + { +@@ -2981,6 +2997,8 @@ static int of_phy_led(struct phy_device + + if (phydev->drv->led_brightness_set) + cdev->brightness_set_blocking = phy_led_set_brightness; ++ if (phydev->drv->led_blink_set) ++ cdev->blink_set = phy_led_blink_set; + 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 +@@ -966,6 +966,18 @@ struct phy_driver { + */ + int (*led_brightness_set)(struct phy_device *dev, + u8 index, enum led_brightness value); ++ ++ /** ++ * @led_blink_set: Set a PHY LED brightness. Index indicates ++ * which of the PHYs led should be configured to blink. Delays ++ * are in milliseconds and if both are zero then a sensible ++ * default should be chosen. The call should adjust the ++ * timings in that case and if it can't match the values ++ * specified exactly. ++ */ ++ int (*led_blink_set)(struct phy_device *dev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/generic/backport-5.15/815-v6.4-09-net-phy-marvell-Implement-led_blink_set.patch b/target/linux/generic/backport-5.15/815-v6.4-09-net-phy-marvell-Implement-led_blink_set.patch new file mode 100644 index 0000000000..0f258c6b92 --- /dev/null +++ b/target/linux/generic/backport-5.15/815-v6.4-09-net-phy-marvell-Implement-led_blink_set.patch @@ -0,0 +1,104 @@ +From ea9e86485decb2ac1750005bd96c166c9b780406 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 17 Apr 2023 17:17:31 +0200 +Subject: [PATCH 9/9] net: phy: marvell: Implement led_blink_set() + +The Marvell PHY can blink the LEDs, simple on/off. All LEDs blink at +the same rate, and the reset default is 84ms per blink, which is +around 12Hz. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/marvell.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -147,6 +147,8 @@ + #define MII_88E1318S_PHY_LED_FUNC 0x10 + #define MII_88E1318S_PHY_LED_FUNC_OFF (0x8) + #define MII_88E1318S_PHY_LED_FUNC_ON (0x9) ++#define MII_88E1318S_PHY_LED_FUNC_HI_Z (0xa) ++#define MII_88E1318S_PHY_LED_FUNC_BLINK (0xb) + #define MII_88E1318S_PHY_LED_TCR 0x12 + #define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) + #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) +@@ -2823,6 +2825,35 @@ static int m88e1318_led_brightness_set(s + MII_88E1318S_PHY_LED_FUNC, reg); + } + ++static int m88e1318_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int reg; ++ ++ reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, ++ MII_88E1318S_PHY_LED_FUNC); ++ if (reg < 0) ++ return reg; ++ ++ switch (index) { ++ case 0: ++ case 1: ++ case 2: ++ reg &= ~(0xf << (4 * index)); ++ reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index); ++ /* Reset default is 84ms */ ++ *delay_on = 84 / 2; ++ *delay_off = 84 / 2; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return phy_write_paged(phydev, MII_MARVELL_LED_PAGE, ++ MII_88E1318S_PHY_LED_FUNC, reg); ++} ++ + static int marvell_probe(struct phy_device *phydev) + { + struct marvell_priv *priv; +@@ -3072,6 +3103,7 @@ static struct phy_driver marvell_drivers + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, + .led_brightness_set = m88e1318_led_brightness_set, ++ .led_blink_set = m88e1318_led_blink_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E1145, +@@ -3179,6 +3211,7 @@ static struct phy_driver marvell_drivers + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, + .led_brightness_set = m88e1318_led_brightness_set, ++ .led_blink_set = m88e1318_led_blink_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E1540, +@@ -3206,6 +3239,7 @@ static struct phy_driver marvell_drivers + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, + .led_brightness_set = m88e1318_led_brightness_set, ++ .led_blink_set = m88e1318_led_blink_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E1545, +@@ -3233,6 +3267,7 @@ static struct phy_driver marvell_drivers + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, + .led_brightness_set = m88e1318_led_brightness_set, ++ .led_blink_set = m88e1318_led_blink_set, + }, + { + .phy_id = MARVELL_PHY_ID_88E3016, +@@ -3375,6 +3410,7 @@ static struct phy_driver marvell_drivers + .get_tunable = m88e1540_get_tunable, + .set_tunable = m88e1540_set_tunable, + .led_brightness_set = m88e1318_led_brightness_set, ++ .led_blink_set = m88e1318_led_blink_set, + }, + }; + diff --git a/target/linux/generic/backport-5.15/816-v6.4-net-phy-marvell-Fix-inconsistent-indenting-in-led_bl.patch b/target/linux/generic/backport-5.15/816-v6.4-net-phy-marvell-Fix-inconsistent-indenting-in-led_bl.patch new file mode 100644 index 0000000000..c5b611a125 --- /dev/null +++ b/target/linux/generic/backport-5.15/816-v6.4-net-phy-marvell-Fix-inconsistent-indenting-in-led_bl.patch @@ -0,0 +1,38 @@ +From 4774ad841bef97cc51df90195338c5b2573dd4cb Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Sun, 23 Apr 2023 19:28:00 +0200 +Subject: [PATCH] net: phy: marvell: Fix inconsistent indenting in + led_blink_set + +Fix inconsistent indeinting in m88e1318_led_blink_set reported by kernel +test robot, probably done by the presence of an if condition dropped in +later revision of the same code. + +Reported-by: kernel test robot <lkp@intel.com> +Link: https://lore.kernel.org/oe-kbuild-all/202304240007.0VEX8QYG-lkp@intel.com/ +Fixes: ea9e86485dec ("net: phy: marvell: Implement led_blink_set()") +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Link: https://lore.kernel.org/r/20230423172800.3470-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/phy/marvell.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -2841,10 +2841,10 @@ static int m88e1318_led_blink_set(struct + case 1: + case 2: + reg &= ~(0xf << (4 * index)); +- reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index); +- /* Reset default is 84ms */ +- *delay_on = 84 / 2; +- *delay_off = 84 / 2; ++ reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index); ++ /* Reset default is 84ms */ ++ *delay_on = 84 / 2; ++ *delay_off = 84 / 2; + break; + default: + return -EINVAL; diff --git a/target/linux/generic/backport-5.15/817-v6.5-02-leds-trigger-netdev-Drop-NETDEV_LED_MODE_LINKUP-from.patch b/target/linux/generic/backport-5.15/817-v6.5-02-leds-trigger-netdev-Drop-NETDEV_LED_MODE_LINKUP-from.patch new file mode 100644 index 0000000000..3170c26058 --- /dev/null +++ b/target/linux/generic/backport-5.15/817-v6.5-02-leds-trigger-netdev-Drop-NETDEV_LED_MODE_LINKUP-from.patch @@ -0,0 +1,87 @@ +From e2f24cb1b5daf9a4f6f3ba574c1fa74aab9807a4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Wed, 19 Apr 2023 23:07:40 +0200 +Subject: [PATCH 2/5] leds: trigger: netdev: Drop NETDEV_LED_MODE_LINKUP from + mode + +Putting NETDEV_LED_MODE_LINKUP in the same list of the netdev trigger +modes is wrong as it's used to set the link state of the device and not +to set a blink mode as it's done by NETDEV_LED_LINK, NETDEV_LED_TX and +NETDEV_LED_RX. It's also wrong to put this state in the same bitmap of the +netdev trigger mode and should be external to it. + +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: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Lee Jones <lee@kernel.org> +Link: https://lore.kernel.org/r/20230419210743.3594-3-ansuelsmth@gmail.com +--- + drivers/leds/trigger/ledtrig-netdev.c | 19 ++++++++----------- + 1 file changed, 8 insertions(+), 11 deletions(-) + +--- 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 le + 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 + 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,11 +314,10 @@ static int netdev_trig_notify(struct not + + 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: +- if (netif_carrier_ok(dev)) +- set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); ++ trigger_data->carrier_link_up = netif_carrier_ok(dev); + fallthrough; + case NETDEV_REGISTER: + if (trigger_data->net_dev) +@@ -333,8 +331,7 @@ static int netdev_trig_notify(struct not + 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; + } + diff --git a/target/linux/generic/backport-5.15/817-v6.5-03-leds-trigger-netdev-Rename-add-namespace-to-netdev-t.patch b/target/linux/generic/backport-5.15/817-v6.5-03-leds-trigger-netdev-Rename-add-namespace-to-netdev-t.patch new file mode 100644 index 0000000000..19cc1d7c9e --- /dev/null +++ b/target/linux/generic/backport-5.15/817-v6.5-03-leds-trigger-netdev-Rename-add-namespace-to-netdev-t.patch @@ -0,0 +1,149 @@ +From bdec9cb83936e0ac4cb87fed5b49fad0175f7dec Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Wed, 19 Apr 2023 23:07:41 +0200 +Subject: [PATCH 3/5] leds: trigger: netdev: Rename add namespace to netdev + trigger enum modes + +Rename NETDEV trigger enum modes to a more symbolic name and add a +namespace to them. + +Also add __TRIGGER_NETDEV_MAX to identify the max modes of the netdev +trigger. + +This is a cleanup to drop the define and no behaviour change are +intended. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Lee Jones <lee@kernel.org> +Link: https://lore.kernel.org/r/20230419210743.3594-4-ansuelsmth@gmail.com +--- + drivers/leds/trigger/ledtrig-netdev.c | 58 ++++++++++++--------------- + 1 file changed, 25 insertions(+), 33 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -51,15 +51,15 @@ 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 ++enum led_trigger_netdev_modes { ++ TRIGGER_NETDEV_LINK = 0, ++ TRIGGER_NETDEV_TX, ++ TRIGGER_NETDEV_RX, ++ ++ /* Keep last */ ++ __TRIGGER_NETDEV_MAX, + }; + + static void set_baseline_state(struct led_netdev_data *trigger_data) +@@ -76,7 +76,7 @@ static void set_baseline_state(struct le + 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 +85,8 @@ static void set_baseline_state(struct le + /* 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 +146,16 @@ static ssize_t device_name_store(struct + 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 +165,7 @@ static ssize_t netdev_led_attr_show(stru + } + + 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 +177,10 @@ static ssize_t netdev_led_attr_store(str + 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; +@@ -360,21 +352,21 @@ static void netdev_trig_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/target/linux/generic/backport-5.15/817-v6.5-04-leds-trigger-netdev-Convert-device-attr-to-macro.patch b/target/linux/generic/backport-5.15/817-v6.5-04-leds-trigger-netdev-Convert-device-attr-to-macro.patch new file mode 100644 index 0000000000..3b45951f57 --- /dev/null +++ b/target/linux/generic/backport-5.15/817-v6.5-04-leds-trigger-netdev-Convert-device-attr-to-macro.patch @@ -0,0 +1,82 @@ +From 164b67d53476a9d114be85c885bd31f783835be4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Wed, 19 Apr 2023 23:07:42 +0200 +Subject: [PATCH 4/5] leds: trigger: netdev: Convert device attr to macro + +Convert link tx and rx device attr to a common macro to reduce common +code and in preparation for additional attr. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Lee Jones <lee@kernel.org> +Link: https://lore.kernel.org/r/20230419210743.3594-5-ansuelsmth@gmail.com +--- + drivers/leds/trigger/ledtrig-netdev.c | 57 ++++++++------------------- + 1 file changed, 16 insertions(+), 41 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -198,47 +198,22 @@ static ssize_t netdev_led_attr_store(str + 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) diff --git a/target/linux/generic/backport-5.15/817-v6.5-05-leds-trigger-netdev-Use-mutex-instead-of-spinlocks.patch b/target/linux/generic/backport-5.15/817-v6.5-05-leds-trigger-netdev-Use-mutex-instead-of-spinlocks.patch new file mode 100644 index 0000000000..180bee9612 --- /dev/null +++ b/target/linux/generic/backport-5.15/817-v6.5-05-leds-trigger-netdev-Use-mutex-instead-of-spinlocks.patch @@ -0,0 +1,106 @@ +From d1b9e1391ab2dc80e9db87fe8b2de015c651e4c9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Wed, 19 Apr 2023 23:07:43 +0200 +Subject: [PATCH 5/5] leds: trigger: netdev: Use mutex instead of spinlocks + +Some LEDs may require to sleep while doing some operation like setting +brightness and other cleanup. + +For this reason, using a spinlock will cause a sleep under spinlock +warning. + +It should be safe to convert this to a sleepable lock since: +- sysfs read/write can sleep +- netdev_trig_work is a work queue and can sleep +- netdev _trig_notify can sleep + +The spinlock was used when brightness didn't support sleeping, but this +changed and now it supported with brightness_set_blocking(). + +Convert to mutex lock to permit sleeping using brightness_set_blocking(). + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Lee Jones <lee@kernel.org> +Link: https://lore.kernel.org/r/20230419210743.3594-6-ansuelsmth@gmail.com +--- + drivers/leds/trigger/ledtrig-netdev.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +--- 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" + +@@ -37,7 +37,7 @@ + */ + + struct led_netdev_data { +- spinlock_t lock; ++ struct mutex lock; + + struct delayed_work work; + struct notifier_block notifier; +@@ -97,9 +97,9 @@ static ssize_t device_name_show(struct d + 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; + } +@@ -115,7 +115,7 @@ static ssize_t device_name_store(struct + + 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); +@@ -138,7 +138,7 @@ static ssize_t device_name_store(struct + trigger_data->last_activity = 0; + + set_baseline_state(trigger_data); +- spin_unlock_bh(&trigger_data->lock); ++ mutex_unlock(&trigger_data->lock); + + return size; + } +@@ -279,7 +279,7 @@ static int netdev_trig_notify(struct not + + 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) { +@@ -304,7 +304,7 @@ static int netdev_trig_notify(struct not + + set_baseline_state(trigger_data); + +- spin_unlock_bh(&trigger_data->lock); ++ mutex_unlock(&trigger_data->lock); + + return NOTIFY_DONE; + } +@@ -365,7 +365,7 @@ static int netdev_trig_activate(struct l + 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; diff --git a/target/linux/generic/backport-5.15/818-v6.5-01-leds-add-APIs-for-LEDs-hw-control.patch b/target/linux/generic/backport-5.15/818-v6.5-01-leds-add-APIs-for-LEDs-hw-control.patch new file mode 100644 index 0000000000..ac18611733 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-01-leds-add-APIs-for-LEDs-hw-control.patch @@ -0,0 +1,74 @@ +From ed554d3f945179c5b159bddfad7be34b403fe11a Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:31 +0200 +Subject: [PATCH 01/13] leds: add APIs for LEDs hw control + +Add an option to permit LED driver to declare support for a specific +trigger to use hw control and setup the LED to blink based on specific +provided modes. + +Add APIs for LEDs hw control. These functions will be used to activate +hardware control where a LED will use the provided flags, from an +unique defined supported trigger, to setup the LED to be driven by +hardware. + +Add hw_control_is_supported() to ask the LED driver if the requested +mode by the trigger are supported and the LED can be setup to follow +the requested modes. + +Deactivate hardware blink control by setting brightness to LED_OFF via +the brightness_set() callback. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/leds.h | 37 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -164,6 +164,43 @@ struct led_classdev { + + /* LEDs that have private triggers have this set */ + struct led_hw_trigger_type *trigger_type; ++ ++ /* Unique trigger name supported by LED set in hw control mode */ ++ const char *hw_control_trigger; ++ /* ++ * Check if the LED driver supports the requested mode provided by the ++ * defined supported trigger to setup the LED to hw control mode. ++ * ++ * Return 0 on success. Return -EOPNOTSUPP when the passed flags are not ++ * supported and software fallback needs to be used. ++ * Return a negative error number on any other case for check fail due ++ * to various reason like device not ready or timeouts. ++ */ ++ int (*hw_control_is_supported)(struct led_classdev *led_cdev, ++ unsigned long flags); ++ /* ++ * Activate hardware control, LED driver will use the provided flags ++ * from the supported trigger and setup the LED to be driven by hardware ++ * following the requested mode from the trigger flags. ++ * Deactivate hardware blink control by setting brightness to LED_OFF via ++ * the brightness_set() callback. ++ * ++ * Return 0 on success, a negative error number on flags apply fail. ++ */ ++ int (*hw_control_set)(struct led_classdev *led_cdev, ++ unsigned long flags); ++ /* ++ * Get from the LED driver the current mode that the LED is set in hw ++ * control mode and put them in flags. ++ * Trigger can use this to get the initial state of a LED already set in ++ * hardware blink control. ++ * ++ * 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. ++ */ ++ int (*hw_control_get)(struct led_classdev *led_cdev, ++ unsigned long *flags); + #endif + + #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED diff --git a/target/linux/generic/backport-5.15/818-v6.5-02-leds-add-API-to-get-attached-device-for-LED-hw-contr.patch b/target/linux/generic/backport-5.15/818-v6.5-02-leds-add-API-to-get-attached-device-for-LED-hw-contr.patch new file mode 100644 index 0000000000..1a221727a0 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-02-leds-add-API-to-get-attached-device-for-LED-hw-contr.patch @@ -0,0 +1,37 @@ +From 052c38eb17e866c5b4cd43924e7a5e20167b55c0 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 29 May 2023 18:32:32 +0200 +Subject: [PATCH 02/13] leds: add API to get attached device for LED hw control + +Some specific LED triggers blink the LED based on events from a device +or subsystem. +For example, an LED could be blinked to indicate a network device is +receiving packets, or a disk is reading blocks. To correctly enable and +request the hw control of the LED, the trigger has to check if the +network interface or block device configured via a /sys/class/led file +match the one the LED driver provide for hw control for. + +Provide an API call to get the device which the LED blinks for. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/leds.h | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -201,6 +201,12 @@ struct led_classdev { + */ + int (*hw_control_get)(struct led_classdev *led_cdev, + unsigned long *flags); ++ /* ++ * Get the device this LED blinks in response to. ++ * e.g. for a PHY LED, it is the network device. If the LED is ++ * not yet associated to a device, return NULL. ++ */ ++ struct device *(*hw_control_get_device)(struct led_classdev *led_cdev); + #endif + + #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED diff --git a/target/linux/generic/backport-5.15/818-v6.5-03-Documentation-leds-leds-class-Document-new-Hardware-.patch b/target/linux/generic/backport-5.15/818-v6.5-03-Documentation-leds-leds-class-Document-new-Hardware-.patch new file mode 100644 index 0000000000..af9fb7fdc3 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-03-Documentation-leds-leds-class-Document-new-Hardware-.patch @@ -0,0 +1,113 @@ +From 8aa2fd7b66980ecd2e45e95af61cf7eafede1211 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:33 +0200 +Subject: [PATCH 03/13] Documentation: leds: leds-class: Document new Hardware + driven LEDs APIs + +Document new Hardware driven LEDs APIs. + +Some LEDs can be programmed to be driven by hardware. This is not +limited to blink but also to turn off or on autonomously. +To support this feature, a LED needs to implement various additional +ops and needs to declare specific support for the supported triggers. + +Add documentation for each required value and API to make hw control +possible and implementable by both LEDs and triggers. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/leds/leds-class.rst | 81 +++++++++++++++++++++++++++++++ + 1 file changed, 81 insertions(+) + +--- a/Documentation/leds/leds-class.rst ++++ b/Documentation/leds/leds-class.rst +@@ -169,6 +169,87 @@ Setting the brightness to zero with brig + should completely turn off the LED and cancel the previously programmed + hardware blinking function, if any. + ++Hardware driven LEDs ++==================== ++ ++Some LEDs can be programmed to be driven by hardware. This is not ++limited to blink but also to turn off or on autonomously. ++To support this feature, a LED needs to implement various additional ++ops and needs to declare specific support for the supported triggers. ++ ++With hw control we refer to the LED driven by hardware. ++ ++LED driver must define the following value to support hw control: ++ ++ - hw_control_trigger: ++ unique trigger name supported by the LED in hw control ++ mode. ++ ++LED driver must implement the following API to support hw control: ++ - hw_control_is_supported: ++ check if the flags passed by the supported trigger can ++ be parsed and activate hw control on the LED. ++ ++ Return 0 if the passed flags mask is supported and ++ can be set with hw_control_set(). ++ ++ If the passed flags mask is not supported -EOPNOTSUPP ++ must be returned, the LED trigger will use software ++ fallback in this case. ++ ++ Return a negative error in case of any other error like ++ device not ready or timeouts. ++ ++ - hw_control_set: ++ activate hw control. LED driver will use the provided ++ flags passed from the supported trigger, parse them to ++ a set of mode and setup the LED to be driven by hardware ++ following the requested modes. ++ ++ Set LED_OFF via the brightness_set to deactivate hw control. ++ ++ Return 0 on success, a negative error number on failing to ++ apply flags. ++ ++ - hw_control_get: ++ get active modes from a LED already in hw control, parse ++ them and set in flags the current active flags for the ++ supported trigger. ++ ++ 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. ++ ++ - hw_control_get_device: ++ return the device associated with the LED driver in ++ hw control. A trigger might use this to match the ++ returned device from this function with a configured ++ device for the trigger as the source for blinking ++ events and correctly enable hw control. ++ (example a netdev trigger configured to blink for a ++ particular dev match the returned dev from get_device ++ to set hw control) ++ ++ Returns a pointer to a struct device or NULL if nothing ++ is currently attached. ++ ++LED driver can activate additional modes by default to workaround the ++impossibility of supporting each different mode on the supported trigger. ++Examples are hardcoding the blink speed to a set interval, enable special ++feature like bypassing blink if some requirements are not met. ++ ++A trigger should first check if the hw control API are supported by the LED ++driver and check if the trigger is supported to verify if hw control is possible, ++use hw_control_is_supported to check if the flags are supported and only at ++the end use hw_control_set to activate hw control. ++ ++A trigger can use hw_control_get to check if a LED is already in hw control ++and init their flags. ++ ++When the LED is in hw control, no software blink is possible and doing so ++will effectively disable hw control. + + Known Issues + ============ diff --git a/target/linux/generic/backport-5.15/818-v6.5-04-leds-trigger-netdev-refactor-code-setting-device-nam.patch b/target/linux/generic/backport-5.15/818-v6.5-04-leds-trigger-netdev-refactor-code-setting-device-nam.patch new file mode 100644 index 0000000000..3c804c0b41 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-04-leds-trigger-netdev-refactor-code-setting-device-nam.patch @@ -0,0 +1,69 @@ +From 28a6a2ef18ad840a390d519840c303b03040961c Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 29 May 2023 18:32:34 +0200 +Subject: [PATCH 04/13] leds: trigger: netdev: refactor code setting device + name + +Move the code into a helper, ready for it to be called at +other times. No intended behaviour change. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 29 ++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -104,15 +104,9 @@ static ssize_t device_name_show(struct d + return len; + } + +-static ssize_t device_name_store(struct device *dev, +- struct device_attribute *attr, const char *buf, +- size_t size) ++static int set_device_name(struct led_netdev_data *trigger_data, ++ const char *name, size_t size) + { +- struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); +- +- if (size >= IFNAMSIZ) +- return -EINVAL; +- + cancel_delayed_work_sync(&trigger_data->work); + + mutex_lock(&trigger_data->lock); +@@ -122,7 +116,7 @@ static ssize_t device_name_store(struct + trigger_data->net_dev = NULL; + } + +- memcpy(trigger_data->device_name, buf, size); ++ memcpy(trigger_data->device_name, name, size); + trigger_data->device_name[size] = 0; + if (size > 0 && trigger_data->device_name[size - 1] == '\n') + trigger_data->device_name[size - 1] = 0; +@@ -140,6 +134,23 @@ static ssize_t device_name_store(struct + set_baseline_state(trigger_data); + mutex_unlock(&trigger_data->lock); + ++ return 0; ++} ++ ++static ssize_t device_name_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t size) ++{ ++ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); ++ int ret; ++ ++ if (size >= IFNAMSIZ) ++ return -EINVAL; ++ ++ ret = set_device_name(trigger_data, buf, size); ++ ++ if (ret < 0) ++ return ret; + return size; + } + diff --git a/target/linux/generic/backport-5.15/818-v6.5-05-leds-trigger-netdev-introduce-check-for-possible-hw-.patch b/target/linux/generic/backport-5.15/818-v6.5-05-leds-trigger-netdev-introduce-check-for-possible-hw-.patch new file mode 100644 index 0000000000..284b519482 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-05-leds-trigger-netdev-introduce-check-for-possible-hw-.patch @@ -0,0 +1,54 @@ +From 4fd1b6d47a7a38e81fdc6f8be2ccd4216b3f93db Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:35 +0200 +Subject: [PATCH 05/13] leds: trigger: netdev: introduce check for possible hw + control + +Introduce function to check if the requested mode can use hw control in +preparation for hw control support. Currently everything is handled in +software so can_hw_control will always return false. + +Add knob with the new value hw_control in trigger_data struct to +set hw control possible. Useful for future implementation to implement +in set_baseline_state() the required function to set the requested mode +using LEDs hw control ops and in other function to reject set if hw +control is currently active. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -51,6 +51,7 @@ struct led_netdev_data { + + unsigned long mode; + bool carrier_link_up; ++ bool hw_control; + }; + + enum led_trigger_netdev_modes { +@@ -91,6 +92,11 @@ static void set_baseline_state(struct le + } + } + ++static bool can_hw_control(struct led_netdev_data *trigger_data) ++{ ++ return false; ++} ++ + static ssize_t device_name_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +@@ -204,6 +210,8 @@ static ssize_t netdev_led_attr_store(str + else + clear_bit(bit, &trigger_data->mode); + ++ trigger_data->hw_control = can_hw_control(trigger_data); ++ + set_baseline_state(trigger_data); + + return size; diff --git a/target/linux/generic/backport-5.15/818-v6.5-06-leds-trigger-netdev-add-basic-check-for-hw-control-s.patch b/target/linux/generic/backport-5.15/818-v6.5-06-leds-trigger-netdev-add-basic-check-for-hw-control-s.patch new file mode 100644 index 0000000000..09759bc623 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-06-leds-trigger-netdev-add-basic-check-for-hw-control-s.patch @@ -0,0 +1,42 @@ +From 6352f25f9fadba59d5df2ba7139495759ccc81d5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:36 +0200 +Subject: [PATCH 06/13] leds: trigger: netdev: add basic check for hw control + support + +Add basic check for hw control support. Check if the required API are +defined and check if the defined trigger supported in hw control for the +LED driver match netdev. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -92,8 +92,22 @@ static void set_baseline_state(struct le + } + } + ++static bool supports_hw_control(struct led_classdev *led_cdev) ++{ ++ if (!led_cdev->hw_control_get || !led_cdev->hw_control_set || ++ !led_cdev->hw_control_is_supported) ++ return false; ++ ++ return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name); ++} ++ + static bool can_hw_control(struct led_netdev_data *trigger_data) + { ++ struct led_classdev *led_cdev = trigger_data->led_cdev; ++ ++ if (!supports_hw_control(led_cdev)) ++ return false; ++ + return false; + } + diff --git a/target/linux/generic/backport-5.15/818-v6.5-07-leds-trigger-netdev-reject-interval-store-for-hw_con.patch b/target/linux/generic/backport-5.15/818-v6.5-07-leds-trigger-netdev-reject-interval-store-for-hw_con.patch new file mode 100644 index 0000000000..6634906800 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-07-leds-trigger-netdev-reject-interval-store-for-hw_con.patch @@ -0,0 +1,28 @@ +From c84c80c7388f887b10dafd70fc55bc6c5fe9fa5a Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:37 +0200 +Subject: [PATCH 07/13] leds: trigger: netdev: reject interval store for + hw_control + +Reject interval store with hw_control enabled. It's are currently not +supported and MUST be set to the default value with hw control enabled. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -265,6 +265,9 @@ static ssize_t interval_store(struct dev + unsigned long value; + int ret; + ++ if (trigger_data->hw_control) ++ return -EINVAL; ++ + ret = kstrtoul(buf, 0, &value); + if (ret) + return ret; diff --git a/target/linux/generic/backport-5.15/818-v6.5-08-leds-trigger-netdev-add-support-for-LED-hw-control.patch b/target/linux/generic/backport-5.15/818-v6.5-08-leds-trigger-netdev-add-support-for-LED-hw-control.patch new file mode 100644 index 0000000000..52faa4809b --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-08-leds-trigger-netdev-add-support-for-LED-hw-control.patch @@ -0,0 +1,107 @@ +From 7c145a34ba6e380616af93262fcab9fc7261d851 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:38 +0200 +Subject: [PATCH 08/13] leds: trigger: netdev: add support for LED hw control + +Add support for LED hw control for the netdev trigger. + +The trigger on calling set_baseline_state to configure a new mode, will +do various check to verify if hw control can be used for the requested +mode in can_hw_control() function. + +It will first check if the LED driver supports hw control for the netdev +trigger, then will use hw_control_is_supported() and finally will call +hw_control_set() to apply the requested mode. + +To use such mode, interval MUST be set to the default value and net_dev +MUST be set. If one of these 2 value are not valid, hw control will +never be used and normal software fallback is used. + +The default interval value is moved to a define to make sure they are +always synced. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 43 +++++++++++++++++++++++++-- + 1 file changed, 41 insertions(+), 2 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -24,6 +24,8 @@ + #include <linux/timer.h> + #include "../leds.h" + ++#define NETDEV_LED_DEFAULT_INTERVAL 50 ++ + /* + * Configurable sysfs attributes: + * +@@ -68,6 +70,13 @@ static void set_baseline_state(struct le + int current_brightness; + struct led_classdev *led_cdev = trigger_data->led_cdev; + ++ /* Already validated, hw control is possible with the requested mode */ ++ if (trigger_data->hw_control) { ++ led_cdev->hw_control_set(led_cdev, trigger_data->mode); ++ ++ return; ++ } ++ + current_brightness = led_cdev->brightness; + if (current_brightness) + led_cdev->blink_brightness = current_brightness; +@@ -103,12 +112,42 @@ static bool supports_hw_control(struct l + + static bool can_hw_control(struct led_netdev_data *trigger_data) + { ++ unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL); ++ unsigned int interval = atomic_read(&trigger_data->interval); + struct led_classdev *led_cdev = trigger_data->led_cdev; ++ int ret; + + if (!supports_hw_control(led_cdev)) + return false; + +- return false; ++ /* ++ * Interval must be set to the default ++ * value. Any different value is rejected if in hw ++ * control. ++ */ ++ if (interval != default_interval) ++ return false; ++ ++ /* ++ * net_dev must be set with hw control, otherwise no ++ * blinking can be happening and there is nothing to ++ * offloaded. ++ */ ++ if (!trigger_data->net_dev) ++ return false; ++ ++ /* Check if the requested mode is supported */ ++ ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode); ++ /* Fall back to software blinking if not supported */ ++ if (ret == -EOPNOTSUPP) ++ return false; ++ if (ret) { ++ dev_warn(led_cdev->dev, ++ "Current mode check failed with error %d\n", ret); ++ return false; ++ } ++ ++ return true; + } + + static ssize_t device_name_show(struct device *dev, +@@ -413,7 +452,7 @@ static int netdev_trig_activate(struct l + trigger_data->device_name[0] = 0; + + trigger_data->mode = 0; +- atomic_set(&trigger_data->interval, msecs_to_jiffies(50)); ++ atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL)); + trigger_data->last_activity = 0; + + led_set_trigger_data(led_cdev, trigger_data); diff --git a/target/linux/generic/backport-5.15/818-v6.5-09-leds-trigger-netdev-validate-configured-netdev.patch b/target/linux/generic/backport-5.15/818-v6.5-09-leds-trigger-netdev-validate-configured-netdev.patch new file mode 100644 index 0000000000..c129ffa4f5 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-09-leds-trigger-netdev-validate-configured-netdev.patch @@ -0,0 +1,58 @@ +From 33ec0b53befff2c0a7f3aa19ff08556d60585d6b Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 29 May 2023 18:32:39 +0200 +Subject: [PATCH 09/13] leds: trigger: netdev: validate configured netdev + +The netdev which the LED should blink for is configurable in +/sys/class/led/foo/device_name. Ensure when offloading that the +configured netdev is the same as the netdev the LED is associated +with. If it is not, only perform software blinking. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 24 ++++++++++++++++++++++-- + 1 file changed, 22 insertions(+), 2 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -110,6 +110,24 @@ static bool supports_hw_control(struct l + return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name); + } + ++/* ++ * Validate the configured netdev is the same as the one associated with ++ * the LED driver in hw control. ++ */ ++static bool validate_net_dev(struct led_classdev *led_cdev, ++ struct net_device *net_dev) ++{ ++ struct device *dev = led_cdev->hw_control_get_device(led_cdev); ++ struct net_device *ndev; ++ ++ if (!dev) ++ return false; ++ ++ ndev = to_net_dev(dev); ++ ++ return ndev == net_dev; ++} ++ + static bool can_hw_control(struct led_netdev_data *trigger_data) + { + unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL); +@@ -131,9 +149,11 @@ static bool can_hw_control(struct led_ne + /* + * net_dev must be set with hw control, otherwise no + * blinking can be happening and there is nothing to +- * offloaded. ++ * offloaded. Additionally, for hw control to be ++ * valid, the configured netdev must be the same as ++ * netdev associated to the LED. + */ +- if (!trigger_data->net_dev) ++ if (!validate_net_dev(led_cdev, trigger_data->net_dev)) + return false; + + /* Check if the requested mode is supported */ diff --git a/target/linux/generic/backport-5.15/818-v6.5-10-leds-trigger-netdev-init-mode-if-hw-control-already-.patch b/target/linux/generic/backport-5.15/818-v6.5-10-leds-trigger-netdev-init-mode-if-hw-control-already-.patch new file mode 100644 index 0000000000..e20594c99a --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-10-leds-trigger-netdev-init-mode-if-hw-control-already-.patch @@ -0,0 +1,53 @@ +From 0316cc5629d15880dd3f097d221c55ca648bcd61 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:40 +0200 +Subject: [PATCH 10/13] leds: trigger: netdev: init mode if hw control already + active + +On netdev trigger activation, hw control may be already active by +default. If this is the case and a device is actually provided by +hw_control_get_device(), init the already active mode and set the +bool to hw_control bool to true to reflect the already set mode in the +trigger_data. + +Co-developed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -454,6 +454,8 @@ 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; ++ struct device *dev; + int rc; + + trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); +@@ -475,6 +477,21 @@ static int netdev_trig_activate(struct l + atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL)); + trigger_data->last_activity = 0; + ++ /* 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)) { ++ 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; ++ } ++ } ++ + led_set_trigger_data(led_cdev, trigger_data); + + rc = register_netdevice_notifier(&trigger_data->notifier); diff --git a/target/linux/generic/backport-5.15/818-v6.5-11-leds-trigger-netdev-expose-netdev-trigger-modes-in-l.patch b/target/linux/generic/backport-5.15/818-v6.5-11-leds-trigger-netdev-expose-netdev-trigger-modes-in-l.patch new file mode 100644 index 0000000000..70aed850d1 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-11-leds-trigger-netdev-expose-netdev-trigger-modes-in-l.patch @@ -0,0 +1,54 @@ +From 947acacab5ea151291b861cdfbde16ff5cf1b08c Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:41 +0200 +Subject: [PATCH 11/13] leds: trigger: netdev: expose netdev trigger modes in + linux include + +Expose netdev trigger modes to make them accessible by LED driver that +will support netdev trigger for hw control. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/leds/trigger/ledtrig-netdev.c | 9 --------- + include/linux/leds.h | 10 ++++++++++ + 2 files changed, 10 insertions(+), 9 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -56,15 +56,6 @@ struct led_netdev_data { + bool hw_control; + }; + +-enum led_trigger_netdev_modes { +- TRIGGER_NETDEV_LINK = 0, +- TRIGGER_NETDEV_TX, +- TRIGGER_NETDEV_RX, +- +- /* Keep last */ +- __TRIGGER_NETDEV_MAX, +-}; +- + static void set_baseline_state(struct led_netdev_data *trigger_data) + { + int current_brightness; +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -527,6 +527,16 @@ static inline void *led_get_trigger_data + + #endif /* CONFIG_LEDS_TRIGGERS */ + ++/* Trigger specific enum */ ++enum led_trigger_netdev_modes { ++ TRIGGER_NETDEV_LINK = 0, ++ TRIGGER_NETDEV_TX, ++ TRIGGER_NETDEV_RX, ++ ++ /* Keep last */ ++ __TRIGGER_NETDEV_MAX, ++}; ++ + /* Trigger specific functions */ + #ifdef CONFIG_LEDS_TRIGGER_DISK + void ledtrig_disk_activity(bool write); diff --git a/target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch b/target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch new file mode 100644 index 0000000000..ad76d89b7b --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch @@ -0,0 +1,200 @@ +From e0256648c831af13cbfe4a1787327fcec01c2807 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:42 +0200 +Subject: [PATCH 12/13] net: dsa: qca8k: implement hw_control ops + +Implement hw_control ops to drive Switch LEDs based on hardware events. + +Netdev trigger is the declared supported trigger for hw control +operation and supports the following mode: +- tx +- rx + +When hw_control_set is called, LEDs are set to follow the requested +mode. +Each LEDs will blink at 4Hz by default. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca/qca8k-leds.c | 154 +++++++++++++++++++++++++++++++ + 1 file changed, 154 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-leds.c ++++ b/drivers/net/dsa/qca/qca8k-leds.c +@@ -32,6 +32,43 @@ qca8k_get_enable_led_reg(int port_num, i + } + + 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 = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT; ++ else ++ reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT; ++ ++ return 0; ++} ++ ++static int ++qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ 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 (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable some default rule by default to the requested mode: ++ * - Blink at 4Hz by default ++ */ ++ *offload_trigger |= QCA8K_LED_BLINK_4HZ; ++ ++ return 0; ++} ++ ++static int + qca8k_led_brightness_set(struct qca8k_led *led, + enum led_brightness brightness) + { +@@ -165,6 +202,119 @@ qca8k_cled_blink_set(struct led_classdev + } + + 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 mask, val = QCA8K_LED_ALWAYS_OFF; ++ ++ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); ++ ++ if (enable) ++ val = QCA8K_LED_RULE_CONTROLLED; ++ ++ if (led->port_num == 0 || led->port_num == 4) { ++ mask = QCA8K_LED_PATTERN_EN_MASK; ++ val <<= QCA8K_LED_PATTERN_EN_SHIFT; ++ } else { ++ mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; ++ } ++ ++ return regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift, ++ val << reg_info.shift); ++} ++ ++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, ®_info); ++ ++ regmap_read(priv->regmap, reg_info.reg, &val); ++ ++ val >>= reg_info.shift; ++ ++ if (led->port_num == 0 || led->port_num == 4) { ++ val &= QCA8K_LED_PATTERN_EN_MASK; ++ val >>= QCA8K_LED_PATTERN_EN_SHIFT; ++ } else { ++ val &= QCA8K_LED_PHY123_PATTERN_EN_MASK; ++ } ++ ++ return val == QCA8K_LED_RULE_CONTROLLED; ++} ++ ++static int ++qca8k_cled_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules) ++{ ++ u32 offload_trigger = 0; ++ ++ return qca8k_parse_netdev(rules, &offload_trigger); ++} ++ ++static int ++qca8k_cled_hw_control_set(struct led_classdev *ldev, unsigned long rules) ++{ ++ 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 offload_trigger = 0; ++ int ret; ++ ++ ret = qca8k_parse_netdev(rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_cled_trigger_offload(ldev, true); ++ if (ret) ++ return ret; ++ ++ qca8k_get_control_led_reg(led->port_num, led->led_num, ®_info); ++ ++ return regmap_update_bits(priv->regmap, reg_info.reg, ++ QCA8K_LED_RULE_MASK << reg_info.shift, ++ offload_trigger << reg_info.shift); ++} ++ ++static int ++qca8k_cled_hw_control_get(struct led_classdev *ldev, unsigned long *rules) ++{ ++ 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; ++ int ret; ++ ++ /* With hw control not active return err */ ++ if (!qca8k_cled_hw_control_status(ldev)) ++ return -EINVAL; ++ ++ qca8k_get_control_led_reg(led->port_num, led->led_num, ®_info); ++ ++ ret = regmap_read(priv->regmap, reg_info.reg, &val); ++ if (ret) ++ return ret; ++ ++ val >>= reg_info.shift; ++ val &= QCA8K_LED_RULE_MASK; ++ ++ /* Parsing specific to netdev trigger */ ++ if (val & QCA8K_LED_TX_BLINK_MASK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA8K_LED_RX_BLINK_MASK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ ++ return 0; ++} ++ ++static int + qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num) + { + struct fwnode_handle *led = NULL, *leds = NULL; +@@ -224,6 +374,10 @@ qca8k_parse_port_leds(struct qca8k_priv + port_led->cdev.max_brightness = 1; + port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking; + port_led->cdev.blink_set = qca8k_cled_blink_set; ++ port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported; ++ port_led->cdev.hw_control_set = qca8k_cled_hw_control_set; ++ port_led->cdev.hw_control_get = qca8k_cled_hw_control_get; ++ port_led->cdev.hw_control_trigger = "netdev"; + init_data.default_label = ":port"; + init_data.fwnode = led; + init_data.devname_mandatory = true; diff --git a/target/linux/generic/backport-5.15/818-v6.5-13-net-dsa-qca8k-add-op-to-get-ports-netdev.patch b/target/linux/generic/backport-5.15/818-v6.5-13-net-dsa-qca8k-add-op-to-get-ports-netdev.patch new file mode 100644 index 0000000000..feb6b9e1e4 --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-13-net-dsa-qca8k-add-op-to-get-ports-netdev.patch @@ -0,0 +1,70 @@ +From 4f53c27f772e27e4cf4e5507d6f4d5980002cb6a Mon Sep 17 00:00:00 2001 +From: Andrew Lunn <andrew@lunn.ch> +Date: Mon, 29 May 2023 18:32:43 +0200 +Subject: [PATCH 13/13] net: dsa: qca8k: add op to get ports netdev + +In order that the LED trigger can blink the switch MAC ports LED, it +needs to know the netdev associated to the port. Add the callback to +return the struct device of the netdev. + +Add an helper function qca8k_phy_to_port() to convert the phy back to +dsa_port index, as we reference LED port based on the internal PHY +index and needs to be converted back. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca/qca8k-leds.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-leds.c ++++ b/drivers/net/dsa/qca/qca8k-leds.c +@@ -5,6 +5,18 @@ + #include "qca8k.h" + #include "qca8k_leds.h" + ++static u32 qca8k_phy_to_port(int phy) ++{ ++ /* Internal PHY 0 has port at index 1. ++ * Internal PHY 1 has port at index 2. ++ * Internal PHY 2 has port at index 3. ++ * Internal PHY 3 has port at index 4. ++ * Internal PHY 4 has port at index 5. ++ */ ++ ++ return phy + 1; ++} ++ + static int + qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info) + { +@@ -314,6 +326,20 @@ qca8k_cled_hw_control_get(struct led_cla + return 0; + } + ++static struct device *qca8k_cled_hw_control_get_device(struct led_classdev *ldev) ++{ ++ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); ++ struct qca8k_priv *priv = led->priv; ++ struct dsa_port *dp; ++ ++ dp = dsa_to_port(priv->ds, qca8k_phy_to_port(led->port_num)); ++ if (!dp) ++ return NULL; ++ if (dp->slave) ++ return &dp->slave->dev; ++ return NULL; ++} ++ + static int + qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num) + { +@@ -377,6 +403,7 @@ qca8k_parse_port_leds(struct qca8k_priv + port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported; + port_led->cdev.hw_control_set = qca8k_cled_hw_control_set; + port_led->cdev.hw_control_get = qca8k_cled_hw_control_get; ++ port_led->cdev.hw_control_get_device = qca8k_cled_hw_control_get_device; + port_led->cdev.hw_control_trigger = "netdev"; + init_data.default_label = ":port"; + init_data.fwnode = led; |