diff options
Diffstat (limited to 'drivers/led')
-rw-r--r-- | drivers/led/Kconfig | 16 | ||||
-rw-r--r-- | drivers/led/Makefile | 1 | ||||
-rw-r--r-- | drivers/led/led-uclass.c | 52 | ||||
-rw-r--r-- | drivers/led/led_bcm6328.c | 1 | ||||
-rw-r--r-- | drivers/led/led_bcm6358.c | 1 | ||||
-rw-r--r-- | drivers/led/led_bcm6753.c | 1 | ||||
-rw-r--r-- | drivers/led/led_bcm6858.c | 1 | ||||
-rw-r--r-- | drivers/led/led_cortina.c | 1 | ||||
-rw-r--r-- | drivers/led/led_gpio.c | 1 | ||||
-rw-r--r-- | drivers/led/led_lp5562.c | 2 | ||||
-rw-r--r-- | drivers/led/led_pwm.c | 1 | ||||
-rw-r--r-- | drivers/led/led_sw_blink.c | 117 |
12 files changed, 174 insertions, 21 deletions
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 9837960198d..bee74b25751 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -65,7 +65,7 @@ config LED_PWM Linux compatible ofdata. config LED_BLINK - bool "Support LED blinking" + bool "Support hardware LED blinking" depends on LED help Some drivers can support automatic blinking of LEDs with a given @@ -73,6 +73,20 @@ config LED_BLINK This option enables support for this which adds slightly to the code size. +config LED_SW_BLINK + bool "Support software LED blinking" + depends on LED + select CYCLIC + help + Turns on led blinking implemented in the software, useful when + the hardware doesn't support led blinking. Half of the period + led will be ON and the rest time it will be OFF. Standard + led commands can be used to configure blinking. Does nothing + if driver supports hardware blinking. + WARNING: Blinking may be inaccurate during execution of time + consuming commands (ex. flash reading). Also it completely + stops during OS booting. + config SPL_LED bool "Enable LED support in SPL" depends on SPL_DM diff --git a/drivers/led/Makefile b/drivers/led/Makefile index 2bcb8589087..e27aa488482 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -4,6 +4,7 @@ # Written by Simon Glass <sjg@chromium.org> obj-y += led-uclass.o +obj-$(CONFIG_LED_SW_BLINK) += led_sw_blink.o obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_LED_BCM6753) += led_bcm6753.o diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c index a4be56fc258..199d68bc25a 100644 --- a/drivers/led/led-uclass.c +++ b/drivers/led/led-uclass.c @@ -6,7 +6,6 @@ #define LOG_CATEGORY UCLASS_LED -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> @@ -59,6 +58,10 @@ int led_set_state(struct udevice *dev, enum led_state_t state) if (!ops->set_state) return -ENOSYS; + if (IS_ENABLED(CONFIG_LED_SW_BLINK) && + led_sw_on_state_change(dev, state)) + return 0; + return ops->set_state(dev, state); } @@ -69,20 +72,27 @@ enum led_state_t led_get_state(struct udevice *dev) if (!ops->get_state) return -ENOSYS; + if (IS_ENABLED(CONFIG_LED_SW_BLINK) && + led_sw_is_blinking(dev)) + return LEDST_BLINK; + return ops->get_state(dev); } -#ifdef CONFIG_LED_BLINK int led_set_period(struct udevice *dev, int period_ms) { +#ifdef CONFIG_LED_BLINK struct led_ops *ops = led_get_ops(dev); - if (!ops->set_period) - return -ENOSYS; + if (ops->set_period) + return ops->set_period(dev, period_ms); +#endif + + if (IS_ENABLED(CONFIG_LED_SW_BLINK)) + return led_sw_set_period(dev, period_ms); - return ops->set_period(dev, period_ms); + return -ENOSYS; } -#endif static int led_post_bind(struct udevice *dev) { @@ -108,6 +118,14 @@ static int led_post_bind(struct udevice *dev) else return 0; + if (IS_ENABLED(CONFIG_LED_BLINK)) { + const char *trigger; + + trigger = dev_read_string(dev, "linux,default-trigger"); + if (trigger && !strncmp(trigger, "pattern", 7)) + uc_plat->default_state = LEDST_BLINK; + } + /* * In case the LED has default-state DT property, trigger * probe() to configure its default state during startup. @@ -120,12 +138,24 @@ static int led_post_bind(struct udevice *dev) static int led_post_probe(struct udevice *dev) { struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + int default_period_ms = 1000; + int ret = 0; + + switch (uc_plat->default_state) { + case LEDST_ON: + case LEDST_OFF: + ret = led_set_state(dev, uc_plat->default_state); + break; + case LEDST_BLINK: + ret = led_set_period(dev, default_period_ms); + if (!ret) + ret = led_set_state(dev, uc_plat->default_state); + break; + default: + break; + } - if (uc_plat->default_state == LEDST_ON || - uc_plat->default_state == LEDST_OFF) - led_set_state(dev, uc_plat->default_state); - - return 0; + return ret; } UCLASS_DRIVER(led) = { diff --git a/drivers/led/led_bcm6328.c b/drivers/led/led_bcm6328.c index f59a92fb1fd..dcc5741195c 100644 --- a/drivers/led/led_bcm6328.c +++ b/drivers/led/led_bcm6328.c @@ -3,7 +3,6 @@ * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_bcm6358.c b/drivers/led/led_bcm6358.c index 25aa3994d0e..b1373ab7426 100644 --- a/drivers/led/led_bcm6358.c +++ b/drivers/led/led_bcm6358.c @@ -3,7 +3,6 @@ * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_bcm6753.c b/drivers/led/led_bcm6753.c index 2466d930116..170caf7bdca 100644 --- a/drivers/led/led_bcm6753.c +++ b/drivers/led/led_bcm6753.c @@ -6,7 +6,6 @@ * drivers/led/led_bcm6858.c */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_bcm6858.c b/drivers/led/led_bcm6858.c index 397dc0d8693..a6efdcf6405 100644 --- a/drivers/led/led_bcm6858.c +++ b/drivers/led/led_bcm6858.c @@ -7,7 +7,6 @@ * drivers/led/led_bcm6358.c */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_cortina.c b/drivers/led/led_cortina.c index bcbe78d632a..2d3ad323d33 100644 --- a/drivers/led/led_cortina.c +++ b/drivers/led/led_cortina.c @@ -6,7 +6,6 @@ * */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index 71421de628c..ce22fb49f2a 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -4,7 +4,6 @@ * Written by Simon Glass <sjg@chromium.org> */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_lp5562.c b/drivers/led/led_lp5562.c index 0c5f9bc4300..a5776d3174f 100644 --- a/drivers/led/led_lp5562.c +++ b/drivers/led/led_lp5562.c @@ -122,7 +122,6 @@ static int lp5562_led_reg_update(struct udevice *dev, int regnum, else ret = dm_i2c_reg_clrset(dev, regnum, mask, value); - /* * Data sheet says "Delay between consecutive I2C writes to * ENABLE register (00h) need to be longer than 488 us @@ -521,7 +520,6 @@ U_BOOT_DRIVER(lp5562_led) = { .ops = &lp5562_led_ops, }; - static int lp5562_led_wrap_probe(struct udevice *dev) { struct lp5562_led_wrap_priv *priv = dev_get_priv(dev); diff --git a/drivers/led/led_pwm.c b/drivers/led/led_pwm.c index ae6de3087ab..15dd836509b 100644 --- a/drivers/led/led_pwm.c +++ b/drivers/led/led_pwm.c @@ -4,7 +4,6 @@ * Author: Ivan Vozvakhov <i.vozvakhov@vk.team> */ -#include <common.h> #include <dm.h> #include <errno.h> #include <led.h> diff --git a/drivers/led/led_sw_blink.c b/drivers/led/led_sw_blink.c new file mode 100644 index 00000000000..9e36edbee47 --- /dev/null +++ b/drivers/led/led_sw_blink.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Software blinking helpers + * Copyright (C) 2024 IOPSYS Software Solutions AB + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> + */ + +#include <dm.h> +#include <led.h> +#include <time.h> +#include <stdlib.h> + +#define CYCLIC_NAME_PREFIX "led_sw_blink_" + +static void led_sw_blink(struct cyclic_info *c) +{ + struct led_sw_blink *sw_blink; + struct udevice *dev; + struct led_ops *ops; + + sw_blink = container_of(c, struct led_sw_blink, cyclic); + dev = sw_blink->dev; + ops = led_get_ops(dev); + + switch (sw_blink->state) { + case LED_SW_BLINK_ST_OFF: + sw_blink->state = LED_SW_BLINK_ST_ON; + ops->set_state(dev, LEDST_ON); + break; + case LED_SW_BLINK_ST_ON: + sw_blink->state = LED_SW_BLINK_ST_OFF; + ops->set_state(dev, LEDST_OFF); + break; + case LED_SW_BLINK_ST_NOT_READY: + /* + * led_set_period has been called, but + * led_set_state(LDST_BLINK) has not yet, + * so doing nothing + */ + break; + default: + break; + } +} + +int led_sw_set_period(struct udevice *dev, int period_ms) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct led_sw_blink *sw_blink = uc_plat->sw_blink; + struct led_ops *ops = led_get_ops(dev); + int half_period_us; + + half_period_us = period_ms * 1000 / 2; + + if (!sw_blink) { + int len = sizeof(struct led_sw_blink) + + strlen(CYCLIC_NAME_PREFIX) + + strlen(uc_plat->label) + 1; + + sw_blink = calloc(1, len); + if (!sw_blink) + return -ENOMEM; + + sw_blink->dev = dev; + sw_blink->state = LED_SW_BLINK_ST_DISABLED; + strcpy((char *)sw_blink->cyclic_name, CYCLIC_NAME_PREFIX); + strcat((char *)sw_blink->cyclic_name, uc_plat->label); + + uc_plat->sw_blink = sw_blink; + } + + if (sw_blink->state == LED_SW_BLINK_ST_DISABLED) { + cyclic_register(&sw_blink->cyclic, led_sw_blink, + half_period_us, sw_blink->cyclic_name); + } else { + sw_blink->cyclic.delay_us = half_period_us; + sw_blink->cyclic.start_time_us = timer_get_us(); + } + + sw_blink->state = LED_SW_BLINK_ST_NOT_READY; + ops->set_state(dev, LEDST_OFF); + + return 0; +} + +bool led_sw_is_blinking(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct led_sw_blink *sw_blink = uc_plat->sw_blink; + + if (!sw_blink) + return false; + + return sw_blink->state > LED_SW_BLINK_ST_NOT_READY; +} + +bool led_sw_on_state_change(struct udevice *dev, enum led_state_t state) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct led_sw_blink *sw_blink = uc_plat->sw_blink; + + if (!sw_blink || sw_blink->state == LED_SW_BLINK_ST_DISABLED) + return false; + + if (state == LEDST_BLINK) { + /* start blinking on next led_sw_blink() call */ + sw_blink->state = LED_SW_BLINK_ST_OFF; + return true; + } + + /* stop blinking */ + uc_plat->sw_blink = NULL; + cyclic_unregister(&sw_blink->cyclic); + free(sw_blink); + + return false; +} |