Add support for GPIO-connected LEDs to make use of Cyclic API to
simulate Blink by software.

A new Kconfig is introduced to enable this, CONFIG_LED_GPIO_SW_BLINK.

Signed-off-by: Christian Marangi <ansuels...@gmail.com>
---
 drivers/led/Kconfig    |  9 +++++++
 drivers/led/led_gpio.c | 56 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
index 9837960198d..13d6eb40cea 100644
--- a/drivers/led/Kconfig
+++ b/drivers/led/Kconfig
@@ -91,6 +91,15 @@ config LED_GPIO
          The GPIO driver must used driver model. LEDs are configured using
          the device tree.
 
+config LED_GPIO_SW_BLINK
+       bool "LED support for GPIO software blink"
+       depends on LED_GPIO
+       select CYCLIC
+       select LED_BLINK
+       help
+         Enable support for GPIO-connected LEDs to make use of Cyclic API
+         to simulate LED blink by software.
+
 config SPL_LED_GPIO
        bool "LED support for GPIO-connected LEDs in SPL"
         depends on SPL_LED && SPL_DM_GPIO
diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c
index ce22fb49f2a..20a6c149e4a 100644
--- a/drivers/led/led_gpio.c
+++ b/drivers/led/led_gpio.c
@@ -4,6 +4,7 @@
  * Written by Simon Glass <s...@chromium.org>
  */
 
+#include <cyclic.h>
 #include <dm.h>
 #include <errno.h>
 #include <led.h>
@@ -13,13 +14,60 @@
 
 struct led_gpio_priv {
        struct gpio_desc gpio;
+#ifdef CONFIG_LED_GPIO_SW_BLINK
+       bool sw_blink;
+       struct cyclic_info cyclic;
+#endif
 };
 
+#ifdef CONFIG_LED_GPIO_SW_BLINK
+static void gpio_led_toggle(struct cyclic_info *ctx)
+{
+       struct led_gpio_priv *priv = container_of(ctx, struct led_gpio_priv, 
cyclic);
+       struct gpio_desc *gpio = &priv->gpio;
+       int state;
+
+       state = dm_gpio_get_value(gpio);
+       if (state < 0) {
+               printf("Error getting value for GPIO %d\n",
+                      gpio->offset);
+               return;
+       }
+
+       dm_gpio_set_value(gpio, !state);
+}
+
+static int gpio_led_set_period(struct udevice *dev, int period_ms)
+{
+       struct led_gpio_priv *priv = dev_get_priv(dev);
+       char cyclic_name[16];
+
+       if (priv->sw_blink)
+               cyclic_unregister(&priv->cyclic);
+
+       snprintf(cyclic_name, sizeof(cyclic_name),
+                "gpio_cyclic%u", priv->gpio.offset);
+       cyclic_register(&priv->cyclic, gpio_led_toggle,
+                       period_ms * 500, cyclic_name);
+
+       /* Init the LED on */
+       dm_gpio_set_value(&priv->gpio, LEDST_ON);
+
+       priv->sw_blink = true;
+       return 0;
+}
+#endif
+
 static int gpio_led_set_state(struct udevice *dev, enum led_state_t state)
 {
        struct led_gpio_priv *priv = dev_get_priv(dev);
        int ret;
 
+#ifdef CONFIG_LED_GPIO_SW_BLINK
+       if (priv->sw_blink)
+               cyclic_unregister(&priv->cyclic);
+#endif
+
        if (!dm_gpio_is_valid(&priv->gpio))
                return -EREMOTEIO;
        switch (state) {
@@ -50,6 +98,11 @@ static enum led_state_t gpio_led_get_state(struct udevice 
*dev)
        if (ret < 0)
                return ret;
 
+#ifdef CONFIG_LED_GPIO_SW_BLINK
+       if (priv->sw_blink)
+               return LEDST_BLINK;
+#endif
+
        return ret ? LEDST_ON : LEDST_OFF;
 }
 
@@ -84,6 +137,9 @@ static int led_gpio_bind(struct udevice *parent)
 static const struct led_ops gpio_led_ops = {
        .set_state      = gpio_led_set_state,
        .get_state      = gpio_led_get_state,
+#ifdef CONFIG_LED_GPIO_SW_BLINK
+       .set_period     = gpio_led_set_period,
+#endif
 };
 
 U_BOOT_DRIVER(led_gpio) = {
-- 
2.45.1

Reply via email to