diff options
author | Jeff Chase <jnchase@google.com> | 2019-11-01 17:23:13 -0400 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-12 01:06:04 +0000 |
commit | ddca51c3ede9dce5af08d13cf69e86402f5ee997 (patch) | |
tree | bea67b67ef7de9582abb7f35a6968059f180c7ef /board/endeavour/led.c | |
parent | ee7e8558e7abad9cad5149afb0c14d843343144a (diff) | |
download | chrome-ec-ddca51c3ede9dce5af08d13cf69e86402f5ee997.tar.gz |
endeavour: initial build from kalista/karmastabilize-12669.B
BUG=b:143780700
TEST=emerge-endeavour chromeos-ec
Change-Id: Ib2cec6df3ab6a89a1b7bb278f231cb6b6da4f3ce
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1893543
Tested-by: Jeff Chase <jnchase@google.com>
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Commit-Queue: Jeff Chase <jnchase@google.com>
Auto-Submit: Jeff Chase <jnchase@google.com>
Diffstat (limited to 'board/endeavour/led.c')
-rw-r--r-- | board/endeavour/led.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/board/endeavour/led.c b/board/endeavour/led.c new file mode 100644 index 0000000000..f6d270c25b --- /dev/null +++ b/board/endeavour/led.c @@ -0,0 +1,232 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Power and battery LED control for Endeavour + */ + +#include "chipset.h" +#include "console.h" +#include "ec_commands.h" +#include "gpio.h" +#include "hooks.h" +#include "led_common.h" +#include "pwm.h" +#include "timer.h" +#include "util.h" + +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_RED, + LED_BLUE, + LED_AMBER, + + /* Number of colors, not a color itself */ + LED_COLOR_COUNT +}; + +static int set_color_power(enum led_color color, int duty) +{ + int blue = 0; + int red = 0; + + if (duty < 0 || 100 < duty) + return EC_ERROR_UNKNOWN; + + switch (color) { + case LED_OFF: + break; + case LED_BLUE: + blue = 1; + break; + case LED_RED: + red = 1; + break; + case LED_AMBER: + blue = 1; + red = 1; + break; + default: + return EC_ERROR_UNKNOWN; + } + + if (red) + pwm_set_duty(PWM_CH_LED_RED, duty); + else + pwm_set_duty(PWM_CH_LED_RED, 0); + + if (blue) + pwm_set_duty(PWM_CH_LED_BLUE, duty); + else + pwm_set_duty(PWM_CH_LED_BLUE, 0); + + return EC_SUCCESS; +} + +static int set_color(enum ec_led_id id, enum led_color color, int duty) +{ + switch (id) { + case EC_LED_ID_POWER_LED: + return set_color_power(color, duty); + default: + return EC_ERROR_UNKNOWN; + } +} + +#define LED_PULSE_US (2 * SECOND) +/* 40 msec for nice and smooth transition. */ +#define LED_PULSE_TICK_US (40 * MSEC) + +/* When pulsing is enabled, brightness is incremented by <duty_inc> every + * <interval> usec from 0 to 100% in LED_PULSE_US usec. Then it's decremented + * likewise in LED_PULSE_US usec. */ +static struct { + uint32_t interval; + int duty_inc; + enum led_color color; + int duty; +} led_pulse; + +#define LED_PULSE_TICK(interval, color) \ + config_tick((interval), 100 / (LED_PULSE_US / (interval)), (color)) + +static void config_tick(uint32_t interval, int duty_inc, enum led_color color) +{ + led_pulse.interval = interval; + led_pulse.duty_inc = duty_inc; + led_pulse.color = color; + led_pulse.duty = 0; +} + +static void pulse_power_led(enum led_color color) +{ + set_color(EC_LED_ID_POWER_LED, color, led_pulse.duty); + if (led_pulse.duty + led_pulse.duty_inc > 100) + led_pulse.duty_inc = led_pulse.duty_inc * -1; + else if (led_pulse.duty + led_pulse.duty_inc < 0) + led_pulse.duty_inc = led_pulse.duty_inc * -1; + led_pulse.duty += led_pulse.duty_inc; +} + +static void led_tick(void); +DECLARE_DEFERRED(led_tick); +static void led_tick(void) +{ + uint32_t elapsed; + uint32_t next = 0; + uint32_t start = get_time().le.lo; + static uint8_t pwm_enabled = 0; + + if (!pwm_enabled) { + pwm_enable(PWM_CH_LED_RED, 1); + pwm_enable(PWM_CH_LED_BLUE, 1); + pwm_enabled = 1; + } + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + pulse_power_led(led_pulse.color); + elapsed = get_time().le.lo - start; + next = led_pulse.interval > elapsed ? led_pulse.interval - elapsed : 0; + hook_call_deferred(&led_tick_data, next); +} + +static void led_suspend(void) +{ + LED_PULSE_TICK(LED_PULSE_TICK_US, LED_BLUE); + led_tick(); +} +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, led_suspend, HOOK_PRIO_DEFAULT); + +static void led_shutdown(void) +{ + hook_call_deferred(&led_tick_data, -1); + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + set_color(EC_LED_ID_POWER_LED, LED_OFF, 0); +} +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, led_shutdown, HOOK_PRIO_DEFAULT); + +static void led_resume(void) +{ + /* Assume there is no race condition with led_tick, which also + * runs in hook_task. */ + hook_call_deferred(&led_tick_data, -1); + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + set_color(EC_LED_ID_POWER_LED, LED_BLUE, 100); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, led_resume, HOOK_PRIO_DEFAULT); + +void led_alert(int enable) +{ + if (enable) { + /* Overwrite the current signal */ + config_tick(1 * SECOND, 100, LED_RED); + led_tick(); + } else { + /* Restore the previous signal */ + if (chipset_in_state(CHIPSET_STATE_ON)) + led_resume(); + else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) + led_suspend(); + else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + led_shutdown(); + } +} + +void led_critical(void) +{ + hook_call_deferred(&led_tick_data, -1); + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + set_color(EC_LED_ID_POWER_LED, LED_RED, 100); +} + +static int command_led(int argc, char **argv) +{ + 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")) { + set_color(id, LED_OFF, 0); + } else if (!strcasecmp(argv[1], "red")) { + set_color(id, LED_RED, 100); + } else if (!strcasecmp(argv[1], "blue")) { + set_color(id, LED_BLUE, 100); + } else if (!strcasecmp(argv[1], "amber")) { + set_color(id, LED_AMBER, 100); + } else if (!strcasecmp(argv[1], "alert")) { + led_alert(1); + } else if (!strcasecmp(argv[1], "crit")) { + led_critical(); + } else { + return EC_ERROR_PARAM1; + } + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(led, command_led, + "[debug|red|blue|amber|off|alert|crit]", + "Turn on/off LED."); + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + brightness_range[EC_LED_COLOR_RED] = 100; + brightness_range[EC_LED_COLOR_BLUE] = 100; + brightness_range[EC_LED_COLOR_AMBER] = 100; +} + +int led_set_brightness(enum ec_led_id id, const uint8_t *brightness) +{ + if (brightness[EC_LED_COLOR_RED]) + return set_color(id, LED_RED, brightness[EC_LED_COLOR_RED]); + else if (brightness[EC_LED_COLOR_BLUE]) + return set_color(id, LED_BLUE, brightness[EC_LED_COLOR_BLUE]); + else if (brightness[EC_LED_COLOR_AMBER]) + return set_color(id, LED_AMBER, brightness[EC_LED_COLOR_AMBER]); + else + return set_color(id, LED_OFF, 0); +} |