summaryrefslogtreecommitdiff
path: root/board/shotzo/led.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/shotzo/led.c')
-rw-r--r--board/shotzo/led.c306
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.");