From d7befc37ba40a248899b5dc8e99bef2746a957d2 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 2 Apr 2021 09:48:38 -0700 Subject: [PATCH] Add LED control support This commit adds LED control support including customization and improvement on led-gpio and led-uclass driver to support 'blink' mode. LEDs will behave like below. Normal u-boot : Green LED blinks at 1Hz + ID LED blinks at 3Hz FFU u-boot : Amber LED solid on + ID LED solid on MFG detected : Green LED blinks at 3Hz + ID LED blinks at 3Hz Failure Recovery : Amber LED blinks at 3Hz + ID LED solid on Jumping to Kernel : Green LED solid on + ID LED solid on Signed-off-by: Jae Hyun Yoo --- arch/arm/dts/ast2600-intel.dts | 4 +- board/aspeed/ast2600_intel/intel.c | 66 +++++++++++++++++++++++++++++- cmd/net.c | 23 +++++++++-- drivers/led/led-uclass.c | 37 +++++++++++++++++ drivers/led/led_gpio.c | 62 ++++++++++++++++++++++++++++ include/led.h | 42 ++++++++++++++++++- 6 files changed, 226 insertions(+), 8 deletions(-) diff --git a/arch/arm/dts/ast2600-intel.dts b/arch/arm/dts/ast2600-intel.dts index 1f14753056ee..5243d1a0afc3 100644 --- a/arch/arm/dts/ast2600-intel.dts +++ b/arch/arm/dts/ast2600-intel.dts @@ -47,8 +47,8 @@ }; hb-led { label = "hb"; - gpios = <&gpio0 25 GPIO_ACTIVE_LOW>; - linux,default-trigger = "heartbeat"; + gpios = <&gpio0 173 GPIO_ACTIVE_LOW>; + default-state = "on"; }; }; }; diff --git a/board/aspeed/ast2600_intel/intel.c b/board/aspeed/ast2600_intel/intel.c index 849e81ff3fef..fb9075f93945 100644 --- a/board/aspeed/ast2600_intel/intel.c +++ b/board/aspeed/ast2600_intel/intel.c @@ -5,6 +5,7 @@ #include #include #include +#include #include /* use GPIOC0 on intel boards */ @@ -26,7 +27,27 @@ int read_ffuj(void) return ret; ret = dm_gpio_get_value(&desc); dm_gpio_free(desc.dev, &desc); - return ret; + + if (ret) { + struct udevice *dev; + + /* FFU mode: ChassisID - Solid Blue, StatusLED - Solid Amber */ + ret = led_get_by_label("green", &dev); + if (!ret) + led_set_state(dev, LEDST_OFF); + + ret = led_get_by_label("amber", &dev); + if (!ret) + led_set_state(dev, LEDST_ON); + + ret = led_get_by_label("id", &dev); + if (!ret) + led_set_state(dev, LEDST_ON); + + return 1; + } + + return 0; } /* gpio_abort is a weak symbol in common/autoboot.c */ @@ -264,6 +285,11 @@ static void timer_callback(void *cookie) dummy = readl(0x1e78e07c); dummy = readl(0x1e78f07c); break; +#ifdef CONFIG_LED_BLINK + case 1: + led_blink_update(); + break; +#endif } } @@ -286,10 +312,20 @@ int board_early_init_f(void) int board_early_init_r(void) { + struct udevice *dev; + int ret; + debug("board_early_init_r\n"); enable_onboard_tpm(); + led_default_state(); +#ifdef CONFIG_LED_BLINK + ret = led_get_by_label("id", &dev); + if (!ret) + led_set_period(dev, 160); +#endif + return 0; } @@ -366,6 +402,11 @@ int board_late_init(void) if (readl(SCU_BASE | SCU_014) == REV_ID_AST2600A0) timer_enable(0, ONE_MSEC_IN_USEC, timer_callback, (void *)0); +#ifdef CONFIG_LED_BLINK + timer_enable(1, LED_BLINK_UPDATE_TICK_MS * ONE_MSEC_IN_USEC, + timer_callback, (void *)1); +#endif + espi_init(); /* Add reset reason to bootargs */ @@ -391,6 +432,29 @@ void board_init(void) } */ +void board_preboot_os(void) +{ + struct udevice *dev; + int ret; + + /* + * last second before OS booting + * ChassisID - Solid Blue, StatusLED - Solid Green + */ + + ret = led_get_by_label("green", &dev); + if (!ret) + led_set_state(dev, LEDST_ON); + + ret = led_get_by_label("amber", &dev); + if (!ret) + led_set_state(dev, LEDST_OFF); + + ret = led_get_by_label("id", &dev); + if (!ret) + led_set_state(dev, LEDST_ON); +} + #ifdef CONFIG_WATCHDOG /* watchdog stuff */ void watchdog_init(void) diff --git a/cmd/net.c b/cmd/net.c index 7d2c21ba4d22..a6b03654cdbf 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -10,6 +10,7 @@ #include #include #include +#include static int netboot_common(enum proto_t, cmd_tbl_t *, int, char * const []); @@ -183,6 +184,10 @@ static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, int size; ulong addr; +#ifdef CONFIG_LED_BLINK + led_blink_disable(); +#endif + net_boot_file_name_explicit = false; /* pre-set load_addr */ @@ -229,7 +234,8 @@ static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, if (strict_strtoul(argv[1], 16, &save_addr) < 0 || strict_strtoul(argv[2], 16, &save_size) < 0) { printf("Invalid address/size\n"); - return CMD_RET_USAGE; + rcode = CMD_RET_USAGE; + goto exit; } net_boot_file_name_explicit = true; copy_filename(net_boot_file_name, argv[3], @@ -238,14 +244,16 @@ static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, #endif default: bootstage_error(BOOTSTAGE_ID_NET_START); - return CMD_RET_USAGE; + rcode = CMD_RET_USAGE; + goto exit; } bootstage_mark(BOOTSTAGE_ID_NET_START); size = net_loop(proto); if (size < 0) { bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK); - return CMD_RET_FAILURE; + rcode = CMD_RET_FAILURE; + goto exit; } bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK); @@ -255,7 +263,8 @@ static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, /* done if no file was loaded (no errors though) */ if (size == 0) { bootstage_error(BOOTSTAGE_ID_NET_LOADED); - return CMD_RET_SUCCESS; + rcode = CMD_RET_SUCCESS; + goto exit; } bootstage_mark(BOOTSTAGE_ID_NET_LOADED); @@ -266,6 +275,12 @@ static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, bootstage_mark(BOOTSTAGE_ID_NET_DONE); else bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR); + +exit: +#ifdef CONFIG_LED_BLINK + led_blink_enable(); +#endif + return rcode; } diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c index 2859475a6b8e..264e0735c815 100644 --- a/drivers/led/led-uclass.c +++ b/drivers/led/led-uclass.c @@ -62,6 +62,39 @@ int led_set_period(struct udevice *dev, int period_ms) return ops->set_period(dev, period_ms); } + +static bool blink_enable = true; + +void led_blink_enable(void) +{ + blink_enable = true; +} + +void led_blink_disable(void) +{ + blink_enable = false; +} + +int led_blink_update(void) +{ + struct udevice *dev; + + if (!blink_enable) + return 0; + + for (uclass_find_first_device(UCLASS_LED, &dev); + dev; + uclass_find_next_device(&dev)) { + if (device_active(dev) && led_get_state(dev) == LEDST_BLINK) { + struct led_ops *ops = led_get_ops(dev); + + if (ops && ops->update_blink) + ops->update_blink(dev); + } + } + + return 0; +} #endif int led_default_state(void) @@ -87,6 +120,10 @@ int led_default_state(void) led_set_state(dev, LEDST_ON); else if (!strncmp(default_state, "off", 3)) led_set_state(dev, LEDST_OFF); +#ifdef CONFIG_LED_BLINK + else if (!strncmp(default_state, "blink", 5)) + led_set_state(dev, LEDST_BLINK); +#endif /* default-state = "keep" : device is only probed */ } diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index 93f6b913c647..a88efde71a69 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -13,8 +13,45 @@ struct led_gpio_priv { struct gpio_desc gpio; +#ifdef CONFIG_LED_BLINK + int period; + int period_tick_count; + enum led_state_t state; +#endif }; +#ifdef CONFIG_LED_BLINK +static int gpio_led_set_period(struct udevice *dev, int period_ms) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + + if (period_ms < LED_BLINK_UPDATE_TICK_MS) + period_ms = LED_BLINK_PERIOD_DEFAULT_MS; + + priv->period = period_ms / LED_BLINK_UPDATE_TICK_MS; + priv->period_tick_count = priv->period; + + return 0; +} + +static int gpio_led_update_blink(struct udevice *dev) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->period_tick_count) { + priv->period_tick_count--; + } else { + ret = dm_gpio_get_value(&priv->gpio); + if (ret >= 0) + dm_gpio_set_value(&priv->gpio, !ret); + priv->period_tick_count = priv->period; + } + + 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); @@ -25,6 +62,9 @@ static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) switch (state) { case LEDST_OFF: case LEDST_ON: +#ifdef CONFIG_LED_BLINK + case LEDST_BLINK: +#endif break; case LEDST_TOGGLE: ret = dm_gpio_get_value(&priv->gpio); @@ -36,6 +76,20 @@ static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) return -ENOSYS; } +#ifdef CONFIG_LED_BLINK + priv->state = state; + + if (priv->state == LEDST_BLINK) { + if (priv->period < LED_BLINK_UPDATE_TICK_MS) { + priv->period = LED_BLINK_PERIOD_DEFAULT_MS / + LED_BLINK_UPDATE_TICK_MS; + priv->period_tick_count = priv->period; + } + + return dm_gpio_set_value(&priv->gpio, LEDST_ON); + } +#endif + return dm_gpio_set_value(&priv->gpio, state); } @@ -46,6 +100,10 @@ static enum led_state_t gpio_led_get_state(struct udevice *dev) if (!dm_gpio_is_valid(&priv->gpio)) return -EREMOTEIO; +#ifdef CONFIG_LED_BLINK + if (priv->state == LEDST_BLINK) + return LEDST_BLINK; +#endif ret = dm_gpio_get_value(&priv->gpio); if (ret < 0) return ret; @@ -117,6 +175,10 @@ 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_BLINK + .set_period = gpio_led_set_period, + .update_blink = gpio_led_update_blink, +#endif }; static const struct udevice_id led_gpio_ids[] = { diff --git a/include/led.h b/include/led.h index 7bfdddfd6fab..fb072c8b9f1a 100644 --- a/include/led.h +++ b/include/led.h @@ -32,7 +32,6 @@ enum led_state_t { #ifdef CONFIG_LED_BLINK LEDST_BLINK, #endif - LEDST_COUNT, }; @@ -66,6 +65,17 @@ struct led_ops { * @return 0 if OK, -ve on error */ int (*set_period)(struct udevice *dev, int period_ms); + + /** + * update_blink() - update blink output of an LED + * + * This should be called in every tick for updating blink behavior of an + * LED. + * + * @dev: LED device to change + * @return 0 if OK, -ve on error + */ + int (*update_blink)(struct udevice *dev); #endif }; @@ -115,4 +125,34 @@ int led_set_period(struct udevice *dev, int period_ms); */ int led_default_state(void); +#ifdef CONFIG_LED_BLINK +#define LED_BLINK_UPDATE_TICK_MS 10 +#define LED_BLINK_PERIOD_DEFAULT_MS 500 + +/** + * led_blink_enable() - enable blinking for all LEDs that have the blink state + * + * This enables blinking for all LEDs that have the blink state. + * + */ +void led_blink_enable(void); + +/** + * led_blink_disable() - disable blinking for all LEDs that have the blink state + * + * This disables blinking for all LEDs that have the blink state. + * + */ +void led_blink_disable(void); + +/** + * led_blink_update() - timer tick callback for updating blink behavior + * + * This should be called on every LED_BLINK_UPDATE_TICK_MS for updating blink + * behavior of all LEDs that have the blink state. + * + */ +int led_blink_update(void); +#endif + #endif -- 2.17.1