led: add function naming option from linux
authorHeiko Schocher <hs@denx.de>
Tue, 28 Jan 2025 13:52:46 +0000 (14:52 +0100)
committerTom Rini <trini@konsulko.com>
Fri, 7 Feb 2025 16:53:39 +0000 (10:53 -0600)
in linux we have the option to create the name of a led
optionally through the following properties:

- function
- color
- function-enumerator

This patch adds support for parsing this properties if there
is no label property.

The led name is created in led_post_bind() and we need some
storage place for it. Currently this patch prevents to use
malloc() instead it stores the name in new member :

        char name[LED_MAX_NAME_SIZE];

of struct led_uc_plat. While at it append led tests for the
new feature.

Signed-off-by: Heiko Schocher <hs@denx.de>
Reviewed-by: Tom Rini <trini@konsulko.com>
arch/sandbox/dts/test.dts
drivers/led/led-uclass.c
include/led.h
test/dm/led.c
test/dm/ofnode.c

index ae52b37..b8f3012 100644 (file)
@@ -13,6 +13,7 @@
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/gpio/sandbox-gpio.h>
 #include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
 #include <dt-bindings/pinctrl/sandbox-pinmux.h>
 #include <dt-bindings/mux/mux.h>
 
                        gpio-controller;
                        #gpio-cells = <1>;
                        gpio-bank-name = "a";
-                       sandbox,gpio-count = <20>;
+                       sandbox,gpio-count = <25>;
                        hog_input_active_low {
                                gpio-hog;
                                input;
                        /* label intentionally omitted */
                        default-state = "off";
                };
+
+               led-20 {
+                       gpios = <&gpio_a 20 0>;
+                       /* label intentionally omitted */
+                       function = LED_FUNCTION_STATUS;
+                       color = <LED_COLOR_ID_RED>;
+                       function-enumerator = <20>;
+               };
+
+               led-21 {
+                       gpios = <&gpio_a 21 0>;
+                       /* label intentionally omitted */
+                       function = LED_FUNCTION_STATUS;
+                       color = <LED_COLOR_ID_GREEN>;
+               };
+
+               led-22 {
+                       gpios = <&gpio_a 22 0>;
+                       /* label intentionally omitted */
+                       function = LED_FUNCTION_STATUS;
+               };
+
+               led-23 {
+                       gpios = <&gpio_a 23 0>;
+                       /* label intentionally omitted */
+                       color = <LED_COLOR_ID_GREEN>;
+               };
+
+               led-24 {
+                       gpios = <&gpio_a 24 0>;
+                       label = "sandbox:function";
+                       function = LED_FUNCTION_STATUS;
+                       color = <LED_COLOR_ID_GREEN>;
+               };
        };
 
        wdt-gpio-toggle {
index 7607505..27ef890 100644 (file)
 #include <dm/lists.h>
 #include <dm/root.h>
 #include <dm/uclass-internal.h>
+#include <dt-bindings/leds/common.h>
+
+static const char * const led_colors[LED_COLOR_ID_MAX] = {
+       [LED_COLOR_ID_WHITE] = "white",
+       [LED_COLOR_ID_RED] = "red",
+       [LED_COLOR_ID_GREEN] = "green",
+       [LED_COLOR_ID_BLUE] = "blue",
+       [LED_COLOR_ID_AMBER] = "amber",
+       [LED_COLOR_ID_VIOLET] = "violet",
+       [LED_COLOR_ID_YELLOW] = "yellow",
+       [LED_COLOR_ID_IR] = "ir",
+       [LED_COLOR_ID_MULTI] = "multicolor",
+       [LED_COLOR_ID_RGB] = "rgb",
+       [LED_COLOR_ID_PURPLE] = "purple",
+       [LED_COLOR_ID_ORANGE] = "orange",
+       [LED_COLOR_ID_PINK] = "pink",
+       [LED_COLOR_ID_CYAN] = "cyan",
+       [LED_COLOR_ID_LIME] = "lime",
+};
 
 int led_bind_generic(struct udevice *parent, const char *driver_name)
 {
@@ -232,11 +251,54 @@ int led_activity_blink(void)
 #endif
 #endif
 
-static const char *led_get_label(ofnode node)
+static const char *led_get_function_name(struct udevice *dev)
+{
+       struct led_uc_plat *uc_plat;
+       const char *func;
+       u32 color;
+       u32 enumerator;
+       int ret;
+       int cp;
+
+       if (!dev)
+               return NULL;
+
+       uc_plat = dev_get_uclass_plat(dev);
+       if (!uc_plat)
+               return NULL;
+
+       if (uc_plat->label)
+               return uc_plat->label;
+
+       /* Now try to detect function label name */
+       func = dev_read_string(dev, "function");
+       cp = dev_read_u32(dev, "color", &color);
+       if (cp == 0 || func) {
+               ret = dev_read_u32(dev, "function-enumerator", &enumerator);
+               if (!ret) {
+                       snprintf(uc_plat->name, LED_MAX_NAME_SIZE,
+                                "%s:%s-%d",
+                                cp ? "" : led_colors[color],
+                                func ? func : "", enumerator);
+               } else {
+                       snprintf(uc_plat->name, LED_MAX_NAME_SIZE,
+                                "%s:%s",
+                                cp ? "" : led_colors[color],
+                                func ? func : "");
+               }
+               uc_plat->label = uc_plat->name;
+       }
+
+       return uc_plat->label;
+}
+
+static const char *led_get_label(struct udevice *dev, ofnode node)
 {
        const char *label;
 
        label = ofnode_read_string(node, "label");
+       if (!label)
+               label = led_get_function_name(dev);
        if (!label && !ofnode_read_string(node, "compatible"))
                label = ofnode_get_name(node);
 
@@ -249,7 +311,7 @@ static int led_post_bind(struct udevice *dev)
        const char *default_state;
 
        if (!uc_plat->label)
-               uc_plat->label = led_get_label(dev_ofnode(dev));
+               uc_plat->label = led_get_label(dev, dev_ofnode(dev));
 
        uc_plat->default_state = LEDST_COUNT;
 
@@ -314,14 +376,14 @@ static int led_init(struct uclass *uc)
 #ifdef CONFIG_LED_BOOT
        ret = ofnode_options_get_by_phandle("boot-led", &led_node);
        if (!ret)
-               priv->boot_led_label = led_get_label(led_node);
+               priv->boot_led_label = led_get_label(NULL, led_node);
        priv->boot_led_period = ofnode_options_read_int("boot-led-period-ms", 250);
 #endif
 
 #ifdef CONFIG_LED_ACTIVITY
        ret = ofnode_options_get_by_phandle("activity-led", &led_node);
        if (!ret)
-               priv->activity_led_label = led_get_label(led_node);
+               priv->activity_led_label = led_get_label(NULL, led_node);
        priv->activity_led_period = ofnode_options_read_int("activity-led-period-ms",
                                                            250);
 #endif
index 64247cd..a67db7a 100644 (file)
 
 struct udevice;
 
+/*
+ * value imported from linux:include/linux/uapi/linux/uleds.h
+ */
+#define LED_MAX_NAME_SIZE      64
+
 enum led_state_t {
        LEDST_OFF = 0,
        LEDST_ON = 1,
@@ -86,6 +91,7 @@ struct led_sw_blink {
 struct led_uc_plat {
        const char *label;
        enum led_state_t default_state;
+       char name[LED_MAX_NAME_SIZE];
 #ifdef CONFIG_LED_SW_BLINK
        struct led_sw_blink *sw_blink;
 #endif
index e5b8632..36652c2 100644 (file)
@@ -20,7 +20,12 @@ static int dm_test_led_base(struct unit_test_state *uts)
        ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev));
        ut_assertok(uclass_get_device(UCLASS_LED, 2, &dev));
        ut_assertok(uclass_get_device(UCLASS_LED, 3, &dev));
-       ut_asserteq(-ENODEV, uclass_get_device(UCLASS_LED, 4, &dev));
+       ut_assertok(uclass_get_device(UCLASS_LED, 4, &dev));
+       ut_assertok(uclass_get_device(UCLASS_LED, 5, &dev));
+       ut_assertok(uclass_get_device(UCLASS_LED, 6, &dev));
+       ut_assertok(uclass_get_device(UCLASS_LED, 7, &dev));
+       ut_assertok(uclass_get_device(UCLASS_LED, 8, &dev));
+       ut_asserteq(-ENODEV, uclass_get_device(UCLASS_LED, 9, &dev));
 
        return 0;
 }
@@ -110,6 +115,21 @@ static int dm_test_led_label(struct unit_test_state *uts)
 
        ut_asserteq(-ENODEV, led_get_by_label("sandbox:blue", &dev));
 
+       /* Test if function, color and function-enumerator naming works */
+       ut_assertok(led_get_by_label("red:status-20", &dev));
+
+       /* Test if function, color naming works */
+       ut_assertok(led_get_by_label("green:status", &dev));
+
+       /* Test if function, without color naming works */
+       ut_assertok(led_get_by_label(":status", &dev));
+
+       /* Test if color without function naming works */
+       ut_assertok(led_get_by_label("green:", &dev));
+
+       /* Test if function, color naming is ignored if label is found */
+       ut_assertok(led_get_by_label("sandbox:function", &dev));
+
        return 0;
 }
 DM_TEST(dm_test_led_label, UTF_SCAN_PDATA | UTF_SCAN_FDT);
index 4a23a3c..cc8b444 100644 (file)
@@ -1599,7 +1599,7 @@ static int dm_test_ofnode_delete(struct unit_test_state *uts)
        ut_assert(!ofnode_valid(node));
        ut_assert(!ofnode_valid(ofnode_path("/leds/default_on")));
 
-       ut_asserteq(2, ofnode_get_child_count(ofnode_path("/leds")));
+       ut_asserteq(7, ofnode_get_child_count(ofnode_path("/leds")));
 
        return 0;
 }