diff options
Diffstat (limited to 'board/nami/led.c')
-rw-r--r-- | board/nami/led.c | 613 |
1 files changed, 0 insertions, 613 deletions
diff --git a/board/nami/led.c b/board/nami/led.c deleted file mode 100644 index 17af4d5f82..0000000000 --- a/board/nami/led.c +++ /dev/null @@ -1,613 +0,0 @@ -/* Copyright 2017 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 Nami and its variants - * - * This is an event-driven LED control library. It does not use tasks or - * periodical hooks (HOOK_TICK, HOOK_SECOND), thus, it's more resource - * efficient. - * - * The library defines LED states and assigns an LED behavior to each state. - * The state space consists of tuple of (charge state, power state). - * In each LED state, a color and a pulse interval can be defined. - * - * Charging states are queried each time there is a state transition, thus, not - * stored. We hook power state transitions (e.g. s0->s3) and save the - * destination states (e.g. s3) in power_state. - * - * When system is suspending and AC is unplugged, there will be race condition - * between a power state hook and a charge state hook but whichever is called - * first or last the result will be the same. - * - * Currently, it supports two LEDs, called 'battery LED' and 'power LED'. - * It assumes the battery LED is connected to a PWM pin and the power LED is - * connected to a regular GPIO pin. - */ - -#include "cros_board_info.h" -#include "charge_state.h" -#include "chipset.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "led_common.h" -#include "power.h" -#include "pwm.h" -#include "timer.h" -#include "util.h" - -const enum ec_led_id supported_led_ids[] = { - EC_LED_ID_BATTERY_LED, 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_GREEN, - LED_AMBER, - LED_WHITE, - LED_WARM_WHITE, - LED_FACTORY, - /* Number of colors, not a color itself */ - LED_COLOR_COUNT -}; - -/* Charging states of LED's interests */ -enum led_charge_state { - LED_STATE_DISCHARGE = 0, - LED_STATE_CHARGE, - LED_STATE_FULL, - LED_CHARGE_STATE_COUNT, -}; - -/* Power states of LED's interests */ -enum led_power_state { - LED_STATE_S0 = 0, - LED_STATE_S3, - LED_STATE_S5, - LED_POWER_STATE_COUNT, -}; - -/* Defines a LED pattern for a single state */ -struct led_pattern { - uint8_t color; - /* Bit 0-5: Interval in 100 msec. 0=solid. Max is 3.2 sec. - * Bit 6: 1=alternate (on-off-off-off), 0=regular (on-off-on-off) - * Bit 7: 1=pulse, 0=blink */ - uint8_t pulse; -}; - -#define PULSE_NO 0 -#define PULSE(interval) (BIT(7) | (interval)) -#define BLINK(interval) (interval) -#define ALTERNATE(interval) (BIT(6) | (interval)) -#define IS_PULSING(pulse) ((pulse) & 0x80) -#define IS_ALTERNATE(pulse) ((pulse) & 0x40) -#define PULSE_INTERVAL(pulse) (((pulse) & 0x3f) * 100 * MSEC) - -/* 40 msec for nice and smooth transition. */ -#define LED_PULSE_TICK_US (40 * MSEC) - -typedef struct led_pattern led_patterns[LED_CHARGE_STATE_COUNT] - [LED_POWER_STATE_COUNT]; - -/* - * Nami/Vayne - One dual color LED: - * Charging Amber on (S0/S3/S5) - * Charging (full) White on (S0/S3/S5) - * Discharge in S0 White on - * Discharge in S3/S0ix Pulsing (rising for 2 sec , falling for 2 sec) - * Discharge in S5 Off - * Battery Error Amber on 1sec off 1sec - * Factory mode White on 2sec, Amber on 2sec - */ -const static led_patterns battery_pattern_0 = { - /* discharging: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE(10)}, {LED_OFF, PULSE_NO}}, - /* charging: s0, s3, s5 */ - {{LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}}, -}; - -/* - * Sona - Battery LED (dual color) - */ -const static led_patterns battery_pattern_1 = { - /* discharging: s0, s3, s5 */ - {{LED_OFF, PULSE_NO}, {LED_OFF, PULSE_NO}, {LED_OFF, PULSE_NO}}, - /* charging: s0, s3, s5 */ - {{LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}}, -}; - -/* - * Pantheon - AC In/Battery LED(dual color): - * Connected to AC power / Charged (100%) White (solid on) - * Connected to AC power / Charging(1% -99%) Amber (solid on) - * Not connected to AC power Off - */ -const static led_patterns battery_pattern_2 = { - /* discharging: s0, s3, s5 */ - {{LED_OFF, PULSE_NO}, {LED_OFF, PULSE_NO}, {LED_OFF, PULSE_NO}}, - /* charging: s0, s3, s5 */ - {{LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}}, -}; - -/* - * Sona - Power LED (single color) - */ -const static led_patterns power_pattern_1 = { - /* discharging: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, BLINK(10)}, {LED_OFF, PULSE_NO}}, - /* charging: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, BLINK(10)}, {LED_OFF, PULSE_NO}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, BLINK(10)}, {LED_OFF, PULSE_NO}}, -}; - -/* - * Pantheon - Power LED - * S0: White on - * S3/S0ix: White 1 second on, 3 second off - * S5: Off - */ -const static led_patterns power_pattern_2 = { - /* discharging: s0, s3, s5 */ - {{LED_WHITE, 0}, {LED_WHITE, ALTERNATE(BLINK(10))}, {LED_OFF, 0}}, - /* charging: s0, s3, s5 */ - {{LED_WHITE, 0}, {LED_WHITE, ALTERNATE(BLINK(10))}, {LED_OFF, 0}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, 0}, {LED_WHITE, ALTERNATE(BLINK(10))}, {LED_OFF, 0}}, -}; - -/* - * Akali - battery LED - * Charge: Amber on (s0/s3/s5) - * Full: Blue on (s0/s3/s5) - * Discharge in S0: Blue on - * Discharge in S3: Amber on 1 sec off 3 sec - * Discharge in S5: Off - * Battery Error: Amber on 1sec off 1sec - * Factory mode : Blue on 2sec, Amber on 2sec - */ -const static led_patterns battery_pattern_3 = { - /* discharging: s0, s3, s5 */ - {{LED_WHITE, 0}, {LED_AMBER, ALTERNATE(BLINK(10))}, {LED_OFF, 0}}, - /* charging: s0, s3, s5 */ - {{LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}}, -}; - -const static led_patterns battery_pattern_4 = { - /* discharging: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, BLINK(10)}, {LED_OFF, PULSE_NO}}, - /* charging: s0, s3, s5 */ - {{LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}, {LED_AMBER, PULSE_NO}}, - /* full: s0, s3, s5 */ - {{LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}, {LED_WHITE, PULSE_NO}}, -}; - -/* Patterns for battery LED and power LED. Initialized at run-time. */ -static led_patterns const *patterns[2]; -/* Pattern for battery error. Only blinking battery LED is supported. */ -static struct led_pattern battery_error = {LED_AMBER, BLINK(10)}; -/* Pattern for low state of charge. Only battery LED is supported. */ -static struct led_pattern low_battery = {LED_WHITE, BLINK(10)}; -/* Pattern for factory mode. Blinking 2-color battery LED. */ -static struct led_pattern battery_factory = {LED_FACTORY, BLINK(20)}; -static int low_battery_soc; -static void led_charge_hook(void); -static enum led_power_state power_state; - -static void led_init(void) -{ - switch (oem) { - case PROJECT_NAMI: - case PROJECT_VAYNE: - patterns[0] = &battery_pattern_0; - break; - case PROJECT_SONA: - if (model == MODEL_SYNDRA) { - /* Syndra doesn't have power LED */ - patterns[0] = &battery_pattern_4; - } else { - patterns[0] = &battery_pattern_1; - patterns[1] = &power_pattern_1; - } - battery_error.pulse = BLINK(5); - low_battery_soc = 100; /* 10.0% */ - break; - case PROJECT_PANTHEON: - patterns[0] = &battery_pattern_2; - patterns[1] = &power_pattern_2; - battery_error.color = LED_OFF; - battery_error.pulse = 0; - break; - case PROJECT_AKALI: - patterns[0] = &battery_pattern_3; - break; - default: - break; - } - - pwm_enable(PWM_CH_LED1, 1); - pwm_enable(PWM_CH_LED2, 1); - - /* After sysjump, power_state is cleared. Thus, we need to actively - * retrieve it. */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - power_state = LED_STATE_S5; - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - power_state = LED_STATE_S3; - else - power_state = LED_STATE_S0; -} -DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); - -static int set_color_battery(enum led_color color, int duty) -{ - int led1 = 0; - int led2 = 0; - - if (duty < 0 || 100 < duty) - return EC_ERROR_UNKNOWN; - - switch (color) { - case LED_OFF: - break; - case LED_AMBER: - led2 = 1; - break; - case LED_WHITE: - led1 = 1; - break; - case LED_WARM_WHITE: - led1 = 1; - led2 = 1; - break; - case LED_FACTORY: - break; - default: - return EC_ERROR_UNKNOWN; - } - - if (color != LED_FACTORY) { - pwm_set_duty(PWM_CH_LED1, led1 ? duty : 0); - pwm_set_duty(PWM_CH_LED2, led2 ? duty : 0); - } else { - pwm_set_duty(PWM_CH_LED1, duty ? 100 : 0); - pwm_set_duty(PWM_CH_LED2, duty ? 0 : 100); - } - - return EC_SUCCESS; -} - -static int set_color_power(enum led_color color, int duty) -{ - if (color == LED_OFF) - duty = 0; - gpio_set_level(GPIO_LED1, !duty /* Reversed logic */); - return EC_SUCCESS; -} - -static int set_color(enum ec_led_id id, enum led_color color, int duty) -{ - switch (id) { - case EC_LED_ID_BATTERY_LED: - return set_color_battery(color, duty); - case EC_LED_ID_POWER_LED: - return set_color_power(color, duty); - default: - return EC_ERROR_UNKNOWN; - } -} - -static struct { - uint32_t interval; - int duty_inc; - enum led_color color; - int duty; - int alternate; - uint8_t pulse; -} tick[2]; - -static void tick_battery(void); -DECLARE_DEFERRED(tick_battery); -static void tick_power(void); -DECLARE_DEFERRED(tick_power); -static void cancel_tick(enum ec_led_id id) -{ - if (id == EC_LED_ID_BATTERY_LED) - hook_call_deferred(&tick_battery_data, -1); - else - hook_call_deferred(&tick_power_data, -1); -} - -static int config_tick(enum ec_led_id id, const struct led_pattern *pattern) -{ - static const struct led_pattern *patterns[2]; - uint32_t stride; - - if (pattern == patterns[id]) - /* This pattern was already set */ - return -1; - - patterns[id] = pattern; - - if (!pattern->pulse) { - /* This is a steady pattern. cancel the tick */ - cancel_tick(id); - set_color(id, pattern->color, 100); - return 1; - } - - stride = PULSE_INTERVAL(pattern->pulse); - if (IS_PULSING(pattern->pulse)) { - tick[id].interval = LED_PULSE_TICK_US; - tick[id].duty_inc = 100 / (stride / LED_PULSE_TICK_US); - } else { - tick[id].interval = stride; - tick[id].duty_inc = 100; - } - tick[id].color = pattern->color; - tick[id].duty = 0; - tick[id].alternate = 0; - tick[id].pulse = pattern->pulse; - - return 0; -} - -/* - * When pulsing, brightness is incremented by <duty_inc> every <interval> usec - * from 0 to 100%. Then it's decremented from 100% to 0. - */ -static void pulse_led(enum ec_led_id id) -{ - if (tick[id].duty + tick[id].duty_inc > 100) { - tick[id].duty_inc = tick[id].duty_inc * -1; - } else if (tick[id].duty + tick[id].duty_inc < 0) { - if (IS_ALTERNATE(tick[id].pulse)) { - /* Falling phase landing. Flip the alternate flag. */ - tick[id].alternate = !tick[id].alternate; - if (tick[id].alternate) - return; - } - tick[id].duty_inc = tick[id].duty_inc * -1; - } - tick[id].duty += tick[id].duty_inc; - set_color(id, tick[id].color, tick[id].duty); -} - -static uint32_t tick_led(enum ec_led_id id) -{ - uint32_t elapsed; - uint32_t start = get_time().le.lo; - uint32_t next; - - if (led_auto_control_is_enabled(id)) - pulse_led(id); - if (tick[id].alternate) - /* Skip 2 phases (rising & falling) */ - next = PULSE_INTERVAL(tick[id].pulse) * 2; - else - next = tick[id].interval; - elapsed = get_time().le.lo - start; - return next > elapsed ? next - elapsed : 0; -} - -static void tick_battery(void) -{ - hook_call_deferred(&tick_battery_data, tick_led(EC_LED_ID_BATTERY_LED)); -} - -static void tick_power(void) -{ - hook_call_deferred(&tick_power_data, tick_led(EC_LED_ID_POWER_LED)); -} - -static void start_tick(enum ec_led_id id, const struct led_pattern *pattern) -{ - if (config_tick(id, pattern)) - /* - * If this pattern is already active, ticking must have started - * already. So, we don't re-start ticking to prevent LED from - * blinking at every SOC change. - * - * If this pattern is static, we skip ticking as well. - */ - return; - - if (id == EC_LED_ID_BATTERY_LED) - tick_battery(); - else - tick_power(); -} - -static void led_alert(int enable) -{ - if (enable) - start_tick(EC_LED_ID_BATTERY_LED, &battery_error); - else - led_charge_hook(); -} - -static void led_factory(int enable) -{ - if (enable) - start_tick(EC_LED_ID_BATTERY_LED, &battery_factory); - else - led_charge_hook(); -} - -void config_led(enum ec_led_id id, enum led_charge_state charge) -{ - const led_patterns *pattern; - - pattern = patterns[id]; - if (!pattern) - return; /* This LED isn't present */ - - start_tick(id, &(*pattern)[charge][power_state]); -} - -void config_leds(enum led_charge_state charge) -{ - config_led(EC_LED_ID_BATTERY_LED, charge); - config_led(EC_LED_ID_POWER_LED, charge); -} - -static void call_handler(void) -{ - int soc; - enum charge_state cs; - - if (!led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) - return; - - cs = charge_get_state(); - soc = charge_get_display_charge(); - if (soc < 0) - cs = PWR_STATE_ERROR; - - switch (cs) { - case PWR_STATE_DISCHARGE: - case PWR_STATE_DISCHARGE_FULL: - if (soc < low_battery_soc) - start_tick(EC_LED_ID_BATTERY_LED, &low_battery); - else - config_led(EC_LED_ID_BATTERY_LED, LED_STATE_DISCHARGE); - config_led(EC_LED_ID_POWER_LED, LED_STATE_DISCHARGE); - break; - case PWR_STATE_CHARGE_NEAR_FULL: - case PWR_STATE_CHARGE: - if (soc >= 1000) - config_leds(LED_STATE_FULL); - else - config_leds(LED_STATE_CHARGE); - break; - case PWR_STATE_ERROR: - /* It doesn't matter what 'charge' state we pass because power - * LED (if it exists) is orthogonal to battery state. */ - config_led(EC_LED_ID_POWER_LED, 0); - led_alert(1); - break; - case PWR_STATE_IDLE: - /* External power connected in IDLE. This is also used to show - * factory mode when 'ectool chargecontrol idle' is run during - * factory process. */ - if (charge_get_flags() & CHARGE_FLAG_FORCE_IDLE) - led_factory(1); - break; - default: - ; - } -} - -/* LED state transition handlers */ -static void s0(void) -{ - power_state = LED_STATE_S0; - call_handler(); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, s0, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, s0, HOOK_PRIO_DEFAULT); - -static void s3(void) -{ - power_state = LED_STATE_S3; - call_handler(); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, s3, HOOK_PRIO_DEFAULT); - -static void s5(void) -{ - power_state = LED_STATE_S5; - call_handler(); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, s5, HOOK_PRIO_DEFAULT); - -static void led_charge_hook(void) -{ - call_handler(); -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, led_charge_hook, HOOK_PRIO_DEFAULT); - -static void print_config(enum ec_led_id id) -{ - ccprintf("ID:%d\n", id); - ccprintf(" Color:%d\n", tick[id].color); - ccprintf(" Duty:%d\n", tick[id].duty); - ccprintf(" Duty Increment:%d\n", tick[id].duty_inc); - ccprintf(" Interval:%d\n", tick[id].interval); -} - -static int command_led(int argc, char **argv) -{ - enum ec_led_id id = EC_LED_ID_BATTERY_LED; - static int alert = 0; - static int factory; - - 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], "white")) { - set_color(id, LED_WHITE, 100); - } else if (!strcasecmp(argv[1], "amber")) { - set_color(id, LED_AMBER, 100); - } else if (!strcasecmp(argv[1], "alert")) { - alert = !alert; - led_alert(alert); - } else if (!strcasecmp(argv[1], "s0")) { - s0(); - } else if (!strcasecmp(argv[1], "s3")) { - s3(); - } else if (!strcasecmp(argv[1], "s5")) { - s5(); - } else if (!strcasecmp(argv[1], "conf")) { - print_config(id); - } else if (!strcasecmp(argv[1], "factory")) { - factory = !factory; - led_factory(factory); - } else { - return EC_ERROR_PARAM1; - } - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(led, command_led, - "[debug|red|green|amber|off|alert|s0|s3|s5|conf|factory]", - "Turn on/off LED."); - -void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) -{ - /* - * We return amber=100, white=100 regardless of OEM ID or led_id. This - * function is for ectool led command, which is used to test LED - * functionality. - */ - brightness_range[EC_LED_COLOR_AMBER] = 100; - brightness_range[EC_LED_COLOR_WHITE] = 100; -} - -int led_set_brightness(enum ec_led_id id, const uint8_t *brightness) -{ - if (brightness[EC_LED_COLOR_AMBER]) - return set_color(id, LED_AMBER, brightness[EC_LED_COLOR_AMBER]); - else if (brightness[EC_LED_COLOR_WHITE]) - return set_color(id, LED_WHITE, brightness[EC_LED_COLOR_WHITE]); - else - return set_color(id, LED_OFF, 0); -} |