diff options
Diffstat (limited to 'board/shotzo/led.c')
-rw-r--r-- | board/shotzo/led.c | 306 |
1 files changed, 172 insertions, 134 deletions
diff --git a/board/shotzo/led.c b/board/shotzo/led.c index 761b4b4047..9ca9e7a498 100644 --- a/board/shotzo/led.c +++ b/board/shotzo/led.c @@ -1,58 +1,45 @@ -/* Copyright 2022 The ChromiumOS Authors. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -/* Shotzo specific LED settings. */ +/* Power LED control for Shotzo. + * Solid white - active power + * 25% duty cycle white, 1s on and 3s off- suspend + * Blinking quicky white, 0.5s on and 0.5s off - alert + * 2 long 2 short white, long for 1s, short for 0.5s and interval + * is 0.5s - critical + * Off - shut down + */ -#include "cbi_fw_config.h" -#include "charge_state.h" -#include "extpower.h" +#include "chipset.h" +#include "console.h" #include "gpio.h" #include "hooks.h" #include "led_common.h" +#include "timer.h" +#include "util.h" -#define BAT_LED_ON 0 -#define BAT_LED_OFF 1 +/* + * Due to the CSME-Lite processing, upon startup the CPU transitions through + * S0->S3->S5->S3->S0, causing the LED to turn on/off/on, so + * delay turning off the LED during suspend/shutdown. + */ +#define LED_CPU_DELAY_MS (2000 * MSEC) #define POWER_LED_ON 0 #define POWER_LED_OFF 1 -const enum ec_led_id supported_led_ids[] = { - EC_LED_ID_BATTERY_LED, - EC_LED_ID_POWER_LED -}; +const enum ec_led_id supported_led_ids[] = { EC_LED_ID_POWER_LED }; const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); enum led_color { LED_OFF = 0, - LED_AMBER, LED_WHITE, - LED_COLOR_COUNT /* Number of colors, not a color itself */ + LED_COLOR_COUNT /* Number of colors, not a color itself */ }; -static int led_set_color_battery(enum led_color color) -{ - switch (color) { - case LED_OFF: - gpio_set_level(GPIO_BAT_LED_WHITE_L, BAT_LED_OFF); - gpio_set_level(GPIO_BAT_LED_AMBER_L, BAT_LED_OFF); - break; - case LED_WHITE: - gpio_set_level(GPIO_BAT_LED_WHITE_L, BAT_LED_ON); - gpio_set_level(GPIO_BAT_LED_AMBER_L, BAT_LED_OFF); - break; - case LED_AMBER: - gpio_set_level(GPIO_BAT_LED_WHITE_L, BAT_LED_OFF); - gpio_set_level(GPIO_BAT_LED_AMBER_L, BAT_LED_ON); - break; - default: - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - static int led_set_color_power(enum led_color color) { switch (color) { @@ -68,29 +55,11 @@ static int led_set_color_power(enum led_color color) return EC_SUCCESS; } -void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) -{ - switch (led_id) { - case EC_LED_ID_BATTERY_LED: - brightness_range[EC_LED_COLOR_WHITE] = 1; - brightness_range[EC_LED_COLOR_AMBER] = 1; - break; - case EC_LED_ID_POWER_LED: - brightness_range[EC_LED_COLOR_WHITE] = 1; - break; - default: - break; - } -} - static int led_set_color(enum ec_led_id led_id, enum led_color color) { int rv; switch (led_id) { - case EC_LED_ID_BATTERY_LED: - rv = led_set_color_battery(color); - break; case EC_LED_ID_POWER_LED: rv = led_set_color_power(color); break; @@ -100,105 +69,174 @@ static int led_set_color(enum ec_led_id led_id, enum led_color color) return rv; } -int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +/* When blinking is enabled, led will blinking according to led_blinking_array. + * 1 means led on, 0 means led off, restart from head after reaching the tail. + * The interval is LED_BLINKING_MS. + */ +#define LED_BLINKING_MS (500 * MSEC) +static int *led_blinking_array; +static int led_blinking_count; +static int led_blinking_index; +static void led_blinking(void); +DECLARE_DEFERRED(led_blinking); +static void led_blinking(void) { - if (brightness[EC_LED_COLOR_WHITE] != 0) - led_set_color(led_id, LED_WHITE); - else if (brightness[EC_LED_COLOR_AMBER] != 0) - led_set_color(led_id, LED_AMBER); - else - led_set_color(led_id, LED_OFF); + uint32_t elapsed; + uint32_t next = 0; + uint32_t start = get_time().le.lo; + int signal; - return EC_SUCCESS; + if (led_blinking_array == NULL) + return; + + if (led_blinking_index > (led_blinking_count - 1)) + led_blinking_index = 0; + + signal = *(led_blinking_array + led_blinking_index); + + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) { + switch (signal) { + case 0: + led_set_color(EC_LED_ID_POWER_LED, LED_OFF); + led_blinking_index += 1; + break; + case 1: + led_set_color(EC_LED_ID_POWER_LED, LED_WHITE); + led_blinking_index += 1; + break; + default: + led_blinking_index = 0; + } + } + + elapsed = get_time().le.lo - start; + next = elapsed < LED_BLINKING_MS ? LED_BLINKING_MS - elapsed : 0; + hook_call_deferred(&led_blinking_data, next); } -static void led_set_battery(void) +static int led_suspend_array[] = { 1, 1, 0, 0, 0, 0, 0, 0 }; +const int led_suspend_count = ARRAY_SIZE(led_suspend_array); +static void led_suspend(void) { - static int battery_ticks; - static int power_ticks; - uint32_t chflags = charge_get_flags(); - - battery_ticks++; - - /* - * Override battery LED for Drawlet/Drawman, Drawlet/Drawman - * don't have power LED, blinking battery white LED to indicate - * system suspend without charging. - */ - if (get_cbi_fw_config_tablet_mode() == TABLET_MODE_ABSENT) { - if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND) && - charge_get_state() != PWR_STATE_CHARGE) { - led_set_color_battery(power_ticks++ & 0x2 ? - LED_WHITE : LED_OFF); - return; - } + led_blinking_array = led_suspend_array; + led_blinking_count = led_suspend_count; + led_blinking_index = 0; + led_blinking(); +} +DECLARE_DEFERRED(led_suspend); + +static void led_shutdown(void) +{ + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + led_set_color(EC_LED_ID_POWER_LED, LED_OFF); +} +DECLARE_DEFERRED(led_shutdown); + +static void led_suspend_hook(void) +{ + hook_call_deferred(&led_blinking_data, -1); + hook_call_deferred(&led_shutdown_data, -1); + hook_call_deferred(&led_suspend_data, LED_CPU_DELAY_MS); +} +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, led_suspend_hook, HOOK_PRIO_DEFAULT); + +static void led_shutdown_hook(void) +{ + hook_call_deferred(&led_blinking_data, -1); + hook_call_deferred(&led_suspend_data, -1); + hook_call_deferred(&led_shutdown_data, LED_CPU_DELAY_MS); +} +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, led_shutdown_hook, HOOK_PRIO_DEFAULT); + +static void led_resume_hook(void) +{ + hook_call_deferred(&led_blinking_data, -1); + hook_call_deferred(&led_suspend_data, -1); + hook_call_deferred(&led_shutdown_data, -1); + + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + led_set_color(EC_LED_ID_POWER_LED, LED_WHITE); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, led_resume_hook, HOOK_PRIO_DEFAULT); + +static int led_alert_array[] = { 1, 0 }; +const int led_alert_count = ARRAY_SIZE(led_alert_array); +void led_alert(int enable) +{ + if (enable) { + /* Overwrite the current signal */ + hook_call_deferred(&led_blinking_data, -1); + led_blinking_array = led_alert_array; + led_blinking_count = led_alert_count; + led_blinking_index = 0; + led_blinking(); + } else { + /* Restore the previous signal */ + if (chipset_in_state(CHIPSET_STATE_ON)) + led_resume_hook(); + else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) + led_suspend_hook(); + else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + led_shutdown_hook(); } +} - power_ticks = 0; +static int led_critical_array[] = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 0 }; +const int led_critical_count = ARRAY_SIZE(led_critical_array); +void show_critical_error(void) +{ + hook_call_deferred(&led_blinking_data, -1); + led_blinking_array = led_critical_array; + led_blinking_count = led_critical_count; + led_blinking_index = 0; + led_blinking(); +} - switch (charge_get_state()) { - case PWR_STATE_CHARGE: - led_set_color_battery(LED_AMBER); - break; - case PWR_STATE_DISCHARGE_FULL: - if (extpower_is_present()) { - led_set_color_battery(LED_WHITE); - break; - } - /* Intentional fall-through */ - case PWR_STATE_DISCHARGE: - /* - * Blink white light (1 sec on, 1 sec off) - * when battery capacity is less than 10% - */ - if (charge_get_percent() < 10) - led_set_color_battery( - (battery_ticks & 0x2) ? LED_WHITE : LED_OFF); - else - led_set_color_battery(LED_OFF); - break; - case PWR_STATE_ERROR: - led_set_color_battery( - (battery_ticks % 0x2) ? LED_WHITE : LED_OFF); - break; - case PWR_STATE_CHARGE_NEAR_FULL: - led_set_color_battery(LED_WHITE); - break; - case PWR_STATE_IDLE: /* External power connected in IDLE */ - if (chflags & CHARGE_FLAG_FORCE_IDLE) - led_set_color_battery( - (battery_ticks & 0x2) ? LED_AMBER : LED_OFF); - else - led_set_color_battery(LED_WHITE); +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + switch (led_id) { + case EC_LED_ID_POWER_LED: + brightness_range[EC_LED_COLOR_WHITE] = 1; break; default: - /* Other states don't alter LED behavior */ break; } } -static void led_set_power(void) +int led_set_brightness(enum ec_led_id id, const uint8_t *brightness) { - static int power_tick; - - power_tick++; - - if (chipset_in_state(CHIPSET_STATE_ON)) - led_set_color_power(LED_WHITE); - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - led_set_color_power( - (power_tick & 0x2) ? LED_WHITE : LED_OFF); + if (brightness[EC_LED_COLOR_WHITE] != 0) + led_set_color(id, LED_WHITE); else - led_set_color_power(LED_OFF); + led_set_color(id, LED_OFF); + + return EC_SUCCESS; } -/* Called by hook task every TICK */ -static void led_tick(void) +static int command_led(int argc, const char **argv) { - if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) - led_set_power(); - - if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) - led_set_battery(); + enum ec_led_id id = EC_LED_ID_POWER_LED; + + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + + if (!strcasecmp(argv[1], "debug")) { + led_auto_control(id, !led_auto_control_is_enabled(id)); + ccprintf("o%s\n", led_auto_control_is_enabled(id) ? "ff" : "n"); + } else if (!strcasecmp(argv[1], "off")) { + led_set_color(id, LED_OFF); + } else if (!strcasecmp(argv[1], "white")) { + led_set_color(id, LED_WHITE); + } else if (!strcasecmp(argv[1], "alert")) { + led_alert(1); + } else if (!strcasecmp(argv[1], "crit")) { + show_critical_error(); + } else if (!strcasecmp(argv[1], "resume")) { + led_resume_hook(); + } else { + return EC_ERROR_PARAM1; + } + return EC_SUCCESS; } -DECLARE_HOOK(HOOK_TICK, led_tick, HOOK_PRIO_DEFAULT); +DECLARE_CONSOLE_COMMAND(led, command_led, "[debug|white|off|alert|crit|resume]", + "Turn on/off LED."); |