From b079e0b5b867afd34d88de2f749710d0dbcb11a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=B6rner?= Date: Sat, 12 Oct 2024 23:56:14 +0200 Subject: [PATCH] rtl83xx: configure leds based on device-tree configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reads the port- and port-led configuration from the device-tree and configures the switch soc to represent the requested initial configuration. Signed-off-by: Peter Körner --- .../files-6.6/drivers/net/dsa/rtl83xx/dsa.c | 1 + .../drivers/net/dsa/rtl83xx/rtl838x.c | 173 ++++++++++++++++++ .../drivers/net/dsa/rtl83xx/rtl838x.h | 13 ++ .../include/dt-bindings/leds/rtl838x-leds.h | 31 ++-- 4 files changed, 203 insertions(+), 15 deletions(-) diff --git a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c index d61122e330d..28a2321cf78 100644 --- a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c @@ -254,6 +254,7 @@ static int rtl83xx_setup(struct dsa_switch *ds) pr_debug("Please wait until PHY is settled\n"); msleep(1000); priv->r->pie_init(priv); + priv->r->led_init(priv); return 0; } diff --git a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.c index d93087f5b1a..c527854cc18 100644 --- a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.c +++ b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include +#include #include #include "rtl83xx.h" @@ -1687,6 +1689,176 @@ void rtl838x_set_receive_management_action(int port, rma_ctrl_t type, action_typ } } +static void rtl838x_led_analyze_portgroup_config(struct rtl838x_switch_priv *priv, struct fwnode_handle *port, struct fwnode_handle *port_leds, struct rtl838x_portgroup_led_config *portgroup_config) { + struct fwnode_handle *led; + u32 port_index = 0; + + fwnode_property_read_u32(port, "reg", &port_index); + dev_dbg(priv->dev, "Analyzing led-config of port %d", port_index); + + fwnode_for_each_child_node(port_leds, led) { + u32 led_index = 0; + u32 trigger = RTL838X_LEDS_TRIGGER_DISABLED; + + fwnode_property_read_u32(led, "reg", &led_index); + if(led_index > 3) { + dev_warn(priv->dev, "Led %d of Port %d out of range for led configuration, ignoring", led_index, port_index); + goto next; + } + + fwnode_property_read_u32(led, "realtek,port-led-trigger", &trigger); + dev_dbg(priv->dev, "Found led-config of led %d of port %d with port-led-trigger %08x", led_index, port_index, trigger); + portgroup_config->installed_leds_mask |= BIT(led_index); + portgroup_config->led_triggers[led_index] = trigger; + +next: + if(led) fwnode_handle_put(led); + } +} + +static void rtl838x_led_analyze_config(struct rtl838x_switch_priv *priv, struct fwnode_handle *ports, struct rtl838x_led_config *led_config) { + struct fwnode_handle *port; + u32 port_index = 0; + bool low_ports_done = false, high_ports_done = false; + + dev_dbg(priv->dev, "Analyzing led-config"); + + fwnode_for_each_child_node(ports, port) { + struct fwnode_handle *port_leds; + bool is_low_port; + + fwnode_property_read_u32(port, "reg", &port_index); + + if(port_index > 27) { + // port 28 is the cpu-port + dev_dbg(priv->dev, "Port %d out of range for led configuration, ignoring", port_index); + goto next; + } + is_low_port = (port_index < 24); + + port_leds = fwnode_get_named_child_node(port, "leds"); + if (!port_leds) { + dev_dbg(priv->dev, "No leds node for port %d, ignoring", port_index); + goto next; + } + + led_config->enabled_ports_mask |= BIT(port_index); + + // the leds are configured in two groups: low (port 0-23, copper) and high (ports 24-27, fiber/dual personality) + // for all ports in a group only one setting can be applied. We select the one from the device tree per group as + // representative and use its configuration. For the rtl838x devices it is expected that all ports in a group are + // configured the same way (same leds, same colors). + if(is_low_port) { + if(!low_ports_done) { + dev_dbg(priv->dev, "Analyzing led-config of port %d as representative of the low ports", port_index); + rtl838x_led_analyze_portgroup_config(priv, port, port_leds, &led_config->low_ports); + } + low_ports_done = true; + } + else { + if(!high_ports_done) { + dev_dbg(priv->dev, "Analyzing led-config of port %d as representative of the high ports", port_index); + rtl838x_led_analyze_portgroup_config(priv, port, port_leds, &led_config->high_ports); + } + high_ports_done = true; + } + +next: + if(port_leds) fwnode_handle_put(port_leds); + if(port) fwnode_handle_put(port); + } +} + +// initializes the port-leds to a known state according to the device tree +static void rtl838x_led_init(struct rtl838x_switch_priv *priv) { + struct fwnode_handle *global_port_leds_config, *ports; + + // rtl838x suppoprts multiple ways to connect the leds to the SoC + // this value is loaded from the device tree. + u32 led_control_mode = RTL838X_LEDS_CONTROL_MODE_DISABLED; + + // perform a single blink of all LEDs on startup + u32 power_on_blink = true; + + // LED cotnrol registers of the rtl838x + // see https://svanheule.net/realtek/maple/feature/led + // this init routine calculates the correct values for these registers by + // looking at the device tree and finally sets them accordingly. + u32 led_mode_sel = 0x0, led_mode_ctrl = 0x0; + u32 led_glb_ctrl = 0x0, led_p_en_ctrl = 0x0; + + // initial led modes + struct rtl838x_led_config led_config = {0}; + + // global leds configuration + global_port_leds_config = device_get_named_child_node(priv->dev, "port-leds"); + if (!global_port_leds_config) { + dev_info(priv->dev, "No port-leds node in device-tree, not configuring port-leds"); + goto out; + } + + fwnode_property_read_u32(global_port_leds_config, "led-control-mode", &led_control_mode); + if(led_control_mode == RTL838X_LEDS_CONTROL_MODE_DISABLED) { + dev_info(priv->dev, "led_control_mode == DISABLED in device-tree, not configuring port-leds"); + goto out; + } + + ports = device_get_named_child_node(priv->dev, "ports"); + if (!ports) { + dev_info(priv->dev, "No ports node in device-tree, not configuring port-leds"); + goto out; + } + + // analyze port-leds configuration + rtl838x_led_analyze_config(priv, ports, &led_config); + + dev_dbg(priv->dev, "Analyzed configuraton in device-tree: " + "enabled_ports_mask=%08x " + "installed_leds_mask=%08x / %08x " + "led_triggers=[%d,%d,%d] / [%d,%d,%d]", + led_config.enabled_ports_mask, + led_config.low_ports.installed_leds_mask, + led_config.high_ports.installed_leds_mask, + led_config.low_ports.led_triggers[0], led_config.low_ports.led_triggers[1], led_config.low_ports.led_triggers[2], + led_config.high_ports.led_triggers[0], led_config.high_ports.led_triggers[1], led_config.high_ports.led_triggers[2]); + + // calculate register values + led_glb_ctrl = + // using low_ports twice here is not an accident -- according to the docs the enabled-leds for the high ports + // should be the same as for the low ports, even if the high ports are not used, and in fact setting + // the high-port led-count to 0 results in a broken led configuration, even on a device without high ports. + (led_config.low_ports.installed_leds_mask << 3) | + (led_config.low_ports.installed_leds_mask << 0); + + led_mode_sel = (power_on_blink << 2) | led_control_mode; + + led_mode_ctrl = + (led_config.high_ports.led_triggers[2] << 25) | + (led_config.high_ports.led_triggers[1] << 20) | + (led_config.high_ports.led_triggers[0] << 15) | + (led_config.low_ports.led_triggers[2] << 10) | + (led_config.low_ports.led_triggers[1] << 5) | + (led_config.low_ports.led_triggers[0] << 0); + + led_p_en_ctrl = led_config.enabled_ports_mask; + + dev_dbg(priv->dev, "led_glb_ctrl=%08x/%08x led_mode_sel=%08x " + "led_p_en_ctrl=%08x led_mode_ctrl=%08x", + led_glb_ctrl, (u32)GENMASK(5, 0), led_mode_sel, + led_p_en_ctrl, led_mode_ctrl); + + // write config registers in the right order + dev_info(priv->dev, "Configuring leds"); + sw_w32_mask(GENMASK(5, 0), led_glb_ctrl, RTL838X_LED_GLB_CTRL); + sw_w32(led_mode_sel, RTL838X_LED_MODE_SEL); + sw_w32(led_mode_ctrl, RTL838X_LED_MODE_CTRL); + sw_w32(led_p_en_ctrl, RTL838X_LED_P_EN_CTRL); + +out: + if(ports) fwnode_handle_put(ports); + if(global_port_leds_config) fwnode_handle_put(global_port_leds_config); +} + const struct rtl838x_reg rtl838x_reg = { .mask_port_reg_be = rtl838x_mask_port_reg, .set_port_reg_be = rtl838x_set_port_reg, @@ -1772,6 +1944,7 @@ const struct rtl838x_reg rtl838x_reg = { .l3_setup = rtl838x_l3_setup, .set_distribution_algorithm = rtl838x_set_distribution_algorithm, .set_receive_management_action = rtl838x_set_receive_management_action, + .led_init = rtl838x_led_init, }; irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) diff --git a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.h index ea61e4dac5d..e8e439e2ac8 100644 --- a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.h +++ b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.h @@ -632,6 +632,19 @@ enum pbvlan_mode { PBVLAN_MODE_ALL_PKT, }; +struct rtl838x_portgroup_led_config { + u32 installed_leds_mask; + + // trigger of led 0, 1 and 2 + u32 led_triggers[3]; +}; + +struct rtl838x_led_config { + u32 enabled_ports_mask; + struct rtl838x_portgroup_led_config low_ports; + struct rtl838x_portgroup_led_config high_ports; +}; + struct rtl838x_port { bool enable; u64 pm; diff --git a/target/linux/realtek/files-6.6/include/dt-bindings/leds/rtl838x-leds.h b/target/linux/realtek/files-6.6/include/dt-bindings/leds/rtl838x-leds.h index 52a5bae6539..7d644042373 100644 --- a/target/linux/realtek/files-6.6/include/dt-bindings/leds/rtl838x-leds.h +++ b/target/linux/realtek/files-6.6/include/dt-bindings/leds/rtl838x-leds.h @@ -11,20 +11,21 @@ #define RTL838X_LEDS_CONTROL_MODE_BI_COLOR_SCAN (0x2) #define RTL838X_LEDS_CONTROL_MODE_DISABLED (0x3) -#define RTL838X_LEDS_FUNCTION_LINK_ACT "link_act" -#define RTL838X_LEDS_FUNCTION_LINK "link" -#define RTL838X_LEDS_FUNCTION_ACT "act" -#define RTL838X_LEDS_FUNCTION_ACT_RX "act_rx" -#define RTL838X_LEDS_FUNCTION_ACT_TX "act_tx" -#define RTL838X_LEDS_FUNCTION_LINK_1G "link_1g" -#define RTL838X_LEDS_FUNCTION_LINK_100M "link_100m" -#define RTL838X_LEDS_FUNCTION_LINK_10M "link_10m" -#define RTL838X_LEDS_FUNCTION_LINK_ACT_1G "link_act_1g" -#define RTL838X_LEDS_FUNCTION_LINK_ACT_100M "link_act_100m" -#define RTL838X_LEDS_FUNCTION_LINK_ACT_10M "link_act_10m" -#define RTL838X_LEDS_FUNCTION_LINK_ACT_1G_100M "link_act_1g_100m" -#define RTL838X_LEDS_FUNCTION_LINK_ACT_1G_10M "link_act_1g_10m" -#define RTL838X_LEDS_FUNCTION_LINK_ACT_100M_10M "link_act_100m_10m" -#define RTL838X_LEDS_FUNCTION_DISABLED "disabled" +// https://svanheule.net/realtek/maple/register/led_mode_ctrl +#define RTL838X_LEDS_TRIGGER_LINK_ACT (0) +#define RTL838X_LEDS_TRIGGER_LINK (1) +#define RTL838X_LEDS_TRIGGER_ACT (2) +#define RTL838X_LEDS_TRIGGER_ACT_RX (3) +#define RTL838X_LEDS_TRIGGER_ACT_TX (4) +#define RTL838X_LEDS_TRIGGER_LINK_1G (7) +#define RTL838X_LEDS_TRIGGER_LINK_100M (8) +#define RTL838X_LEDS_TRIGGER_LINK_10M (9) +#define RTL838X_LEDS_TRIGGER_LINK_ACT_1G (10) +#define RTL838X_LEDS_TRIGGER_LINK_ACT_100M (11) +#define RTL838X_LEDS_TRIGGER_LINK_ACT_10M (12) +#define RTL838X_LEDS_TRIGGER_LINK_ACT_1G_100M (13) +#define RTL838X_LEDS_TRIGGER_LINK_ACT_1G_10M (14) +#define RTL838X_LEDS_TRIGGER_LINK_ACT_100M_10M (15) +#define RTL838X_LEDS_TRIGGER_DISABLED (31) #endif /* __DT_BINDINGS_LEDS_RTL83XX_H */