rtl930x: Rework per port LED configuration
authorHarshal Gohel <hg@simonwunderlich.de>
Tue, 12 Dec 2023 14:48:56 +0000 (15:48 +0100)
committerSander Vanheule <sander@svanheule.net>
Tue, 9 Jan 2024 20:18:50 +0000 (21:18 +0100)
Use led_setX to determine number of LEDs per port. Introduce macros to
calculate register value and shift for particular LED in a particular
set.

Problem with previous implementation is that it uses is10G status to
determine leds per port. However with usxgmii, driver sets 10g, 5g and
2.5g so even though there are only 2 leds per port it selects 4 leds per
port

This implementation relies on configured led_set node.

Acked-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
target/linux/realtek/dts-5.15/rtl9302_zyxel_xgs1250-12.dts
target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c
target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.h
target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl930x.c

index 0fe8f1997ea904eb60d85a25898656876391235c..98ac08b26a9147d9bd5bd580021c3b3045d0e92c 100644 (file)
                compatible = "realtek,rtl9300-leds";
                active-low;
 
-               led_set0 = <0x0000 0xffff 0x0a20 0x0b80>; // LED set 0: 1000Mbps,  10/100Mbps
+               led_set0 = <0x0a20 0x0b80>; // LED set 0: 1000Mbps,  10/100Mbps
                led_set1 = <0x0a0b 0x0a28 0x0a82 0x0a0b>; // LED set 1: (10G, 5G, 2.5G) (2.5G, 1G)
                                                          // (5G, 10/100) (10G, 5G, 2.5G)
-               led_set2 = <0x0000 0xffff 0x0a20 0x0a01>; // LED set 2: 1000MBit, 10GBit
+               led_set2 = <0x0a20 0x0a01>; // LED set 2: 1000MBit, 10GBit
        };
 };
 
index 0434312afcef46aab62d30db5d0de0b673a8f94f..221428cc771e116e10760d29e5a6d65c77d5c985 100644 (file)
@@ -271,7 +271,7 @@ int write_phy(u32 port, u32 page, u32 reg, u32 val)
 static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
 {
        struct device *dev = priv->dev;
-       struct device_node *dn, *phy_node, *mii_np = dev->of_node;
+       struct device_node *dn, *phy_node, *led_node, *mii_np = dev->of_node;
        struct mii_bus *bus;
        int ret;
        u32 pn;
@@ -325,9 +325,12 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
                return -ENODEV;
        }
 
+       led_node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300-leds");
+
        for_each_node_by_name(dn, "port") {
                phy_interface_t interface;
                u32 led_set;
+               char led_set_str[16] = {0};
 
                if (!of_device_is_available(dn))
                        continue;
@@ -355,9 +358,18 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
                if (interface == PHY_INTERFACE_MODE_10GBASER)
                        priv->ports[pn].is10G = true;
 
-               if (of_property_read_u32(dn, "led-set", &led_set))
-                       led_set = 0;
-               priv->ports[pn].led_set = led_set;
+               priv->ports[pn].leds_on_this_port = 0;
+               if (led_node) {
+                       if (of_property_read_u32(dn, "led-set", &led_set))
+                               led_set = 0;
+                       priv->ports[pn].led_set = led_set;
+                       sprintf(led_set_str, "led_set%d", led_set);
+                       priv->ports[pn].leds_on_this_port = of_property_count_u32_elems(led_node, led_set_str);
+                       if (priv->ports[pn].leds_on_this_port > 4) {
+                               dev_err(priv->dev, "led_set %d for port %d configuration is invalid\n", led_set, pn);
+                               return -ENODEV;
+                       }
+               }
 
                /* Check for the integrated SerDes of the RTL8380M first */
                if (of_property_read_bool(phy_node, "phy-is-integrated")
index 24d18d61fafde3eb9f9218a7b39620441638901c..a642c747756c70c2db4a105dc64ce058acd5ebff 100644 (file)
@@ -635,6 +635,7 @@ struct rtl838x_port {
        bool is2G5;
        int sds_num;
        int led_set;
+       int leds_on_this_port;
        const struct dsa_port *dp;
 };
 
index c4f200bc0e02e91b763e2d78ea8e063cffdb6377..4a4a926bb21e1dd0f84bf70cc2065bab3c594241 100644 (file)
 
 #define RTL930X_LED_GLB_ACTIVE_LOW                             BIT(22)
 
+#define RTL930X_LED_SETX_0_CTRL(x) (RTL930X_LED_SET0_0_CTRL - (x * 8))
+#define RTL930X_LED_SETX_1_CTRL(x) (RTL930X_LED_SETX_0_CTRL(x) - 4)
+
+/* get register for given set and led in the set */
+#define RTL930X_LED_SETX_LEDY(x,y) (RTL930X_LED_SETX_0_CTRL(x) - 4 * (y / 2))
+
+/* get shift for given led in any set */
+#define RTL930X_LED_SET_LEDX_SHIFT(x) (16 * (x % 2))
+
 extern struct mutex smi_lock;
 extern struct rtl83xx_soc_info soc_info;
 
@@ -2396,10 +2405,44 @@ static void rtl930x_led_init(struct rtl838x_switch_priv *priv)
                return;
        }
 
+       for (int set = 0; set < 4; set++) {
+               char set_name[16] = {0};
+               u32 set_config[4];
+               int leds_in_this_set = 0;
+
+               /* Reset LED set configuration */
+               sw_w32(0, RTL930X_LED_SETX_0_CTRL(set));
+               sw_w32(0, RTL930X_LED_SETX_1_CTRL(set));
+
+               /**
+                * Each led set has 4 number of leds, and each LED is configured with 16 bits
+                * So each 32bit register holds configuration for 2 leds
+                * And therefore each set requires 2 registers for configuring 4 LEDs
+                *
+               */
+               sprintf(set_name, "led_set%d", set);
+               leds_in_this_set = of_property_count_u32_elems(node, set_name);
+
+               if (leds_in_this_set == 0 || leds_in_this_set > sizeof(set_config)) {
+                       pr_err("%s led_set configuration invalid skipping over this set\n", __func__);
+                       continue;
+               }
+
+               if (of_property_read_u32_array(node, set_name, set_config, leds_in_this_set)) {
+                       break;
+               }
+
+               /* Write configuration as per number of LEDs */
+               for (int i=0, led = leds_in_this_set-1; led >= 0; led--,i++) {
+                       sw_w32_mask(0xffff << RTL930X_LED_SET_LEDX_SHIFT(led),
+                                               (0xffff & set_config[i]) << RTL930X_LED_SET_LEDX_SHIFT(led),
+                                               RTL930X_LED_SETX_LEDY(set, led));
+               }
+       }
+
        for (int i = 0; i < priv->cpu_port; i++) {
                int pos = (i << 1) % 32;
                u32 set;
-               u32 v;
 
                sw_w32_mask(0x3 << pos, 0, RTL930X_LED_PORT_FIB_SET_SEL_CTRL(i));
                sw_w32_mask(0x3 << pos, 0, RTL930X_LED_PORT_COPR_SET_SEL_CTRL(i));
@@ -2407,12 +2450,8 @@ static void rtl930x_led_init(struct rtl838x_switch_priv *priv)
                if (!priv->ports[i].phy)
                        continue;
 
-               v = 0x1;
-               if (priv->ports[i].is10G)
-                       v = 0x3;
-               if (priv->ports[i].phy_is_integrated)
-                       v = 0x1;
-               sw_w32_mask(0x3 << pos, v << pos, RTL930X_LED_PORT_NUM_CTRL(i));
+               /* 0x0 = 1 led, 0x1 = 2 leds, 0x2 = 3 leds, 0x3 = 4 leds per port */
+               sw_w32_mask(0x3 << pos, (priv->ports[i].leds_on_this_port -1) << pos, RTL930X_LED_PORT_NUM_CTRL(i));
 
                pm |= BIT(i);
 
@@ -2421,22 +2460,6 @@ static void rtl930x_led_init(struct rtl838x_switch_priv *priv)
                sw_w32_mask(0, set << pos, RTL930X_LED_PORT_FIB_SET_SEL_CTRL(i));
        }
 
-       for (int i = 0; i < 4; i++) {
-               const __be32 *led_set;
-               char set_name[9];
-               u32 setlen;
-               u32 v;
-
-               sprintf(set_name, "led_set%d", i);
-               led_set = of_get_property(node, set_name, &setlen);
-               if (!led_set || setlen != 16)
-                       break;
-               v = be32_to_cpup(led_set) << 16 | be32_to_cpup(led_set + 1);
-               sw_w32(v, RTL930X_LED_SET0_0_CTRL - 4 - i * 8);
-               v = be32_to_cpup(led_set + 2) << 16 | be32_to_cpup(led_set + 3);
-               sw_w32(v, RTL930X_LED_SET0_0_CTRL - i * 8);
-       }
-
        /* Set LED mode to serial (0x1) */
        sw_w32_mask(0x3, 0x1, RTL930X_LED_GLB_CTRL);