Re: [PATCH v4 03/11] led: implement LED boot API

Simon Glass
2024-09-26T21:33:39Z

Hi Christian,

On Sat, 21 Sept 2024 at 00:51, Christian Marangi <ansuelsmth@gmail.com> wrote:
>
> Implement LED boot API to signal correct boot of the system.
>
> led_boot_on/off/blink() are introduced to turn ON, OFF and BLINK the
> designated boot LED.
>
> New Kconfig is introduced, CONFIG_LED_BOOT to enable the feature.
> This makes use of the /options/u-boot property "boot-led" to the
> define the boot LED.
> It's also introduced a new /options/u-boot property "boot-led-period"
> to define the default period when the LED is set to blink mode.
>
> If "boot-led-period" is not defined, the value of 250 (ms) is
> used by default.
>
> If CONFIG_LED_BLINK or CONFIG_LED_SW_BLINK is not enabled,
> led_boot_blink call will fallback to simple LED ON.
>
> To cache the data we repurpose the now unused led_uc_priv for storage of
> global LED uclass info.

Some things to tweak below

>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> ---
> drivers/led/Kconfig | 7 +++
> drivers/led/led-uclass.c | 100 +++++++++++++++++++++++++++++++++++++++
> include/led.h | 56 +++++++++++++++++++++-
> 3 files changed, 161 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
> index bee74b25751..6149cfa02b8 100644
> --- a/drivers/led/Kconfig
> +++ b/drivers/led/Kconfig
> @@ -9,6 +9,13 @@ config LED
> can provide access to board-specific LEDs. Use of the device tree
> for configuration is encouraged.
>
> +config LED_BOOT
> + bool "Enable LED boot support"
> + help
> + Enable LED boot support.
> +
> + LED boot is a specific LED assigned to signal boot operation status.

Here you should link to the /options binding in
doc/device-tree-bindings/options, perhaps

> +
> config LED_BCM6328
> bool "LED Support for BCM6328"
> depends on LED && ARCH_BMIPS
> diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c
> index 199d68bc25a..c5b560982b0 100644
> --- a/drivers/led/led-uclass.c
> +++ b/drivers/led/led-uclass.c
> @@ -94,17 +94,101 @@ int led_set_period(struct udevice *dev, int period_ms)
> return -ENOSYS;
> }
>
> +#ifdef CONFIG_LED_BOOT
> +int led_boot_on(void)
> +{
> + struct uclass *uc = uclass_find(UCLASS_LED);
> + struct led_uc_priv *priv;
> + struct udevice *dev;
> +
> + if (!uc)
> + return -ENOENT;
> +

The normal way is:

ret = uclass_first_device_ret(UCLASS_LED, &dev);
if (ret)
return ret;

> + priv = uclass_get_priv(uc);
> + if (!priv->boot_led_dev ||
> + uclass_get_device_tail(priv->boot_led_dev, 0, &dev)) {

That is an internal function...I suppose it should really have an
underscore and be in uclass-internal.h

But if you are looking for boot_led_label, don't you need to search
through the LEDs to find it? Or use led_get_by_label() ?

> + printf("Failed to get boot LED %s\n",
> + priv->boot_led_label);
> + return -EINVAL;
> + }
> +
> + return led_set_state(dev, LEDST_ON);
> +}
> +
> +int led_boot_off(void)
> +{
> + struct uclass *uc = uclass_find(UCLASS_LED);
> + struct led_uc_priv *priv;
> + struct udevice *dev;
> +
> + if (!uc)
> + return -ENOENT;
> +

Same here.

> + priv = uclass_get_priv(uc);
> + if (!priv->boot_led_dev ||
> + uclass_get_device_tail(priv->boot_led_dev, 0, &dev)) {
> + printf("Failed to get boot LED %s\n",
> + priv->boot_led_label);
> + return -EINVAL;
> + }
> +
> + return led_set_state(dev, LEDST_OFF);
> +}
> +
> +#if defined(CONFIG_LED_BLINK) || defined(CONFIG_LED_SW_BLINK)
> +int led_boot_blink(void)
> +{
> + struct uclass *uc = uclass_find(UCLASS_LED);
> + struct led_uc_priv *priv;
> + struct udevice *dev;
> + int ret;
> +
> + if (!uc)
> + return -ENOENT;
> +
> + priv = uclass_get_priv(uc);
> + if (!priv->boot_led_dev ||
> + uclass_get_device_tail(priv->boot_led_dev, 0, &dev)) {
> + printf("Failed to get boot LED %s\n",
> + priv->boot_led_label);
> + return -EINVAL;
> + }

This looks like the same code again. I think it should be in a
function so the code is not repeated.

> +
> + ret = led_set_period(dev, priv->boot_led_period);
> + if (ret) {
> + if (ret != -ENOSYS)
> + return ret;
> +
> + /* fallback to ON with no set_period and no SW_BLINK */
> + return led_set_state(dev, LEDST_ON);
> + }
> +
> + return led_set_state(dev, LEDST_BLINK);
> +}
> +#endif
> +#endif
> +
> static int led_post_bind(struct udevice *dev)
> {
> struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
> const char *default_state;
>
> +#ifdef CONFIG_LED_BOOT
> + struct led_uc_priv *priv = uclass_get_priv(dev->uclass);
> +#endif
> +
> if (!uc_plat->label)
> uc_plat->label = dev_read_string(dev, "label");
>
> if (!uc_plat->label && !dev_read_string(dev, "compatible"))
> uc_plat->label = ofnode_get_name(dev_ofnode(dev));
>
> +#ifdef CONFIG_LED_BOOT
> + /* check if we are binding boot LED and assign it */
> + if (!strcmp(priv->boot_led_label, uc_plat->label))
> + priv->boot_led_dev = dev;
> +#endif
> +
> uc_plat->default_state = LEDST_COUNT;
>
> default_state = dev_read_string(dev, "default-state");
> @@ -158,10 +242,26 @@ static int led_post_probe(struct udevice *dev)
> return ret;
> }
>
> +#ifdef CONFIG_LED_BOOT
> +static int led_init(struct uclass *uc)
> +{
> + struct led_uc_priv *priv = uclass_get_priv(uc);
> +
> + priv->boot_led_label = ofnode_options_read_str("boot-led");
> + priv->boot_led_period = ofnode_options_read_int("boot-led-period", 250);
> +
> + return 0;
> +}
> +#endif
> +
> UCLASS_DRIVER(led) = {
> .id = UCLASS_LED,
> .name = "led",
> .per_device_plat_auto = sizeof(struct led_uc_plat),
> .post_bind = led_post_bind,
> .post_probe = led_post_probe,
> +#ifdef CONFIG_LED_BOOT
> + .init = led_init,
> + .priv_auto = sizeof(struct led_uc_priv),
> +#endif

I don't love all these #ifdefs but I suppose it is OK here. Ideally we
would have static inline accessors for the fields, in the header file,
e.g. like asm-generic/global_data.h

> };
> diff --git a/include/led.h b/include/led.h
> index 99f93c5ef86..ca495217777 100644
> --- a/include/led.h
> +++ b/include/led.h
> @@ -9,6 +9,7 @@
>
> #include <stdbool.h>
> #include <cyclic.h>
> +#include <dm/ofnode.h>
>
> struct udevice;
>
> @@ -52,10 +53,16 @@ struct led_uc_plat {
> /**
> * struct led_uc_priv - Private data the uclass stores about each device
> *
> - * @period_ms: Flash period in milliseconds
> + * @boot_led_label: Boot LED label
> + * @boot_led_dev: Boot LED dev
> + * @boot_led_period: Boot LED blink period
> */
> struct led_uc_priv {
> - int period_ms;
> +#ifdef CONFIG_LED_BOOT
> + const char *boot_led_label;
> + struct udevice *boot_led_dev;
> + int boot_led_period;
> +#endif
> };
>
> struct led_ops {
> @@ -141,4 +148,49 @@ int led_sw_set_period(struct udevice *dev, int period_ms);
> bool led_sw_is_blinking(struct udevice *dev);
> bool led_sw_on_state_change(struct udevice *dev, enum led_state_t state);
>
> +#ifdef CONFIG_LED_BOOT
> +
> +/**
> + * led_boot_on() - turn ON the designated LED for booting
> + *
> + * Return: 0 if OK, -ve on error
> + */
> +int led_boot_on(void);
> +
> +/**
> + * led_boot_off() - turn OFF the designated LED for booting
> + *
> + * Return: 0 if OK, -ve on error
> + */
> +int led_boot_off(void);
> +
> +#if defined(CONFIG_LED_BLINK) || defined(CONFIG_LED_SW_BLINK)
> +/**
> + * led_boot_blink() - turn ON the designated LED for booting
> + *
> + * Return: 0 if OK, -ve on error
> + */
> +int led_boot_blink(void);
> +
> +#else
> +/* If LED BLINK is not supported/enabled, fallback to LED ON */
> +#define led_boot_blink led_boot_on
> +#endif
> +#else
> +static inline int led_boot_on(void)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int led_boot_off(void)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int led_boot_blink(void)
> +{
> + return -ENOSYS;
> +}
> +#endif
> +
> #endif
> --
> 2.45.2
>

Regards,
Simon