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