[PATCH 1/2] led: Implement software led blinking
Mikhail Kshevetskiy
mikhail.kshevetskiy at iopsys.eu
Wed Jul 3 03:01:30 CEST 2024
From: Michael Polyntsov <michael.polyntsov at iopsys.eu>
If hardware (or driver) doesn't support leds blinking, it's
now possible to use software implementation of blinking instead.
This relies on cyclic functions.
v2 changes:
* Drop sw_blink_state structure, move its necessary fields to
led_uc_plat structure.
* Add cyclic_info pointer to led_uc_plat structure. This
simplify code a lot.
* Remove cyclic function search logic. Not needed anymore.
* Fix blinking period. It was twice large.
* Other cleanups.
Signed-off-by: Michael Polyntsov <michael.polyntsov at iopsys.eu>
Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
---
drivers/led/Kconfig | 14 ++++++
drivers/led/led-uclass.c | 102 +++++++++++++++++++++++++++++++++++++++
include/led.h | 12 +++++
3 files changed, 128 insertions(+)
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
index 9837960198d..1afb081df11 100644
--- a/drivers/led/Kconfig
+++ b/drivers/led/Kconfig
@@ -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_BLINK
+ 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 blinking.
+ WARNING: Blinking may be inaccurate during execution of time
+ consuming commands (ex. flash reading). Also it will completely
+ stops during OS booting.
+
config SPL_LED
bool "Enable LED support in SPL"
depends on SPL_DM
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c
index a4be56fc258..d021c3bbf20 100644
--- a/drivers/led/led-uclass.c
+++ b/drivers/led/led-uclass.c
@@ -52,6 +52,94 @@ int led_get_by_label(const char *label, struct udevice **devp)
return -ENODEV;
}
+#ifdef CONFIG_LED_SW_BLINK
+static void led_sw_blink(void *data)
+{
+ struct udevice *dev = data;
+ struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
+ struct led_ops *ops = led_get_ops(dev);
+
+ switch (uc_plat->sw_blink_state) {
+ case LED_SW_BLINK_ST_OFF:
+ uc_plat->sw_blink_state = LED_SW_BLINK_ST_ON;
+ ops->set_state(dev, LEDST_ON);
+ break;
+ case LED_SW_BLINK_ST_ON:
+ uc_plat->sw_blink_state = LED_SW_BLINK_ST_OFF;
+ ops->set_state(dev, LEDST_OFF);
+ break;
+ case LED_SW_BLINK_ST_NONE:
+ /*
+ * led_set_period has been called, but
+ * led_set_state(LDST_BLINK) has not yet,
+ * so doing nothing
+ */
+ break;
+ }
+}
+
+static int led_sw_set_period(struct udevice *dev, int period_ms)
+{
+ struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
+ struct cyclic_info *cyclic = uc_plat->cyclic;
+ struct led_ops *ops = led_get_ops(dev);
+ char cyclic_name[64];
+ int half_period_us;
+
+ uc_plat->sw_blink_state = LED_SW_BLINK_ST_NONE;
+ ops->set_state(dev, LEDST_OFF);
+
+ half_period_us = period_ms * 1000 / 2;
+
+ if (cyclic) {
+ cyclic->delay_us = half_period_us;
+ cyclic->start_time_us = timer_get_us();
+ } else {
+ snprintf(cyclic_name, sizeof(cyclic_name),
+ "led_sw_blink_%s", uc_plat->label);
+
+ cyclic = cyclic_register(led_sw_blink, half_period_us,
+ cyclic_name, dev);
+ if (!cyclic) {
+ log_err("Registering of blinking function for %s failed\n",
+ uc_plat->label);
+ return -ENOMEM;
+ }
+
+ uc_plat->cyclic = cyclic;
+ }
+
+ return 0;
+}
+
+static bool led_sw_is_blinking(struct udevice *dev)
+{
+ struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
+
+ return (uc_plat->sw_blink_state != LED_SW_BLINK_ST_NONE);
+}
+
+static 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);
+
+ if (uc_plat->cyclic) {
+ if (state == LEDST_BLINK) {
+ /* start blinking on next led_sw_blink() call */
+ uc_plat->sw_blink_state = LED_SW_BLINK_ST_OFF;
+ return true;
+ }
+
+ /* stop blinking */
+ cyclic_unregister(uc_plat->cyclic);
+ uc_plat->cyclic = NULL;
+ uc_plat->sw_blink_state = LED_SW_BLINK_ST_NONE;
+ }
+
+ return false;
+}
+#endif /* CONFIG_LED_SW_BLINK */
+
int led_set_state(struct udevice *dev, enum led_state_t state)
{
struct led_ops *ops = led_get_ops(dev);
@@ -59,6 +147,11 @@ int led_set_state(struct udevice *dev, enum led_state_t state)
if (!ops->set_state)
return -ENOSYS;
+#ifdef CONFIG_LED_SW_BLINK
+ if (led_sw_on_state_change(dev, state))
+ return 0;
+#endif
+
return ops->set_state(dev, state);
}
@@ -69,6 +162,11 @@ enum led_state_t led_get_state(struct udevice *dev)
if (!ops->get_state)
return -ENOSYS;
+#ifdef CONFIG_LED_SW_BLINK
+ if (led_sw_is_blinking(dev))
+ return LEDST_BLINK;
+#endif
+
return ops->get_state(dev);
}
@@ -78,7 +176,11 @@ int led_set_period(struct udevice *dev, int period_ms)
struct led_ops *ops = led_get_ops(dev);
if (!ops->set_period)
+#ifdef CONFIG_LED_SW_BLINK
+ return led_sw_set_period(dev, period_ms);
+#else
return -ENOSYS;
+#endif
return ops->set_period(dev, period_ms);
}
diff --git a/include/led.h b/include/led.h
index a6353166289..bd50fdf33ef 100644
--- a/include/led.h
+++ b/include/led.h
@@ -20,6 +20,14 @@ enum led_state_t {
LEDST_COUNT,
};
+#ifdef CONFIG_LED_SW_BLINK
+enum led_sw_blink_state_t {
+ LED_SW_BLINK_ST_NONE = 0,
+ LED_SW_BLINK_ST_OFF = 1,
+ LED_SW_BLINK_ST_ON = 2,
+};
+#endif
+
/**
* struct led_uc_plat - Platform data the uclass stores about each device
*
@@ -29,6 +37,10 @@ enum led_state_t {
struct led_uc_plat {
const char *label;
enum led_state_t default_state;
+#ifdef CONFIG_LED_SW_BLINK
+ enum led_sw_blink_state_t sw_blink_state;
+ struct cyclic_info *cyclic;
+#endif
};
/**
--
2.39.2
More information about the U-Boot
mailing list