diff options
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/led_onoff_states.c | 211 | ||||
-rw-r--r-- | include/config.h | 18 | ||||
-rw-r--r-- | include/led_onoff_states.h | 93 |
4 files changed, 323 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index e834647562..3b25e0bc99 100644 --- a/common/build.mk +++ b/common/build.mk @@ -81,6 +81,7 @@ common-$(CONFIG_KEYBOARD_TEST)+=keyboard_test.o common-$(CONFIG_LED_COMMON)+=led_common.o common-$(CONFIG_LED_POLICY_STD)+=led_policy_std.o common-$(CONFIG_LED_PWM)+=led_pwm.o +common-$(CONFIG_LED_ONOFF_STATES)+=led_onoff_states.o common-$(CONFIG_LID_ANGLE)+=motion_lid.o math_util.o common-$(CONFIG_LID_ANGLE_UPDATE)+=lid_angle.o common-$(CONFIG_LID_SWITCH)+=lid_switch.o diff --git a/common/led_onoff_states.c b/common/led_onoff_states.c new file mode 100644 index 0000000000..e2e2724647 --- /dev/null +++ b/common/led_onoff_states.c @@ -0,0 +1,211 @@ +/* Copyright 2018 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 state control + */ + +#include "battery.h" +#include "charge_state.h" +#include "chipset.h" +#include "console.h" +#include "ec_commands.h" +#include "extpower.h" +#include "hooks.h" +#include "led_common.h" +#include "led_onoff_states.h" + +#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) + +static enum led_states led_get_state(void) +{ + int charge_lvl; + enum led_states new_state = LED_NUM_STATES; + + switch (charge_get_state()) { + case PWR_STATE_CHARGE: + /* Get percent charge */ + charge_lvl = charge_get_percent(); + /* Determine which charge state to use */ + if (charge_lvl < led_charge_lvl_1) + new_state = STATE_CHARGING_LVL_1; + else if (charge_lvl < led_charge_lvl_2) + new_state = STATE_CHARGING_LVL_2; + else + new_state = STATE_CHARGING_FULL_CHARGE; + break; + case PWR_STATE_DISCHARGE_FULL: + if (extpower_is_present()) { + new_state = STATE_CHARGING_FULL_CHARGE; + break; + } + /* Intentional fall-through */ + case PWR_STATE_DISCHARGE /* and PWR_STATE_DISCHARGE_FULL */: + if (chipset_in_state(CHIPSET_STATE_ON)) { +#ifdef CONFIG_LED_ONOFF_STATES_BAT_LOW + if (charge_get_percent() < + CONFIG_LED_ONOFF_STATES_BAT_LOW) + new_state = STATE_DISCHARGE_S0_BAT_LOW; + else +#endif + new_state = STATE_DISCHARGE_S0; + } else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) + new_state = STATE_DISCHARGE_S3; + else + new_state = STATE_DISCHARGE_S5; + break; + case PWR_STATE_ERROR: + new_state = STATE_BATTERY_ERROR; + break; + case PWR_STATE_CHARGE_NEAR_FULL: + new_state = STATE_CHARGING_FULL_CHARGE; + break; + case PWR_STATE_IDLE: /* External power connected in IDLE */ + if (charge_get_flags() & CHARGE_FLAG_FORCE_IDLE) + new_state = STATE_FACTORY_TEST; + else + new_state = STATE_DISCHARGE_S0; + break; + default: + /* Other states don't alter LED behavior */ + break; + } + + return new_state; +} + +static void led_update_battery(void) +{ + static uint8_t ticks, period; + static int led_state = LED_NUM_STATES; + int phase; + enum led_states desired_state = led_get_state(); + + /* + * We always need to check the current state since the value could + * have been manually overwritten. If we're in a new valid state, + * update our ticks and period info. If our new state isn't defined, + * continue using the previous one. + */ + if (desired_state != led_state && desired_state < LED_NUM_STATES) { + /* State is changing */ + led_state = desired_state; + /* Reset ticks and period when state changes */ + ticks = 0; + + period = led_bat_state_table[led_state][LED_PHASE_0].time + + led_bat_state_table[led_state][LED_PHASE_1].time; + + } + + /* If this state is undefined, turn the LED off */ + if (period == 0) { + CPRINTS("Undefined LED behavior for battery state %d," + "turning off LED", led_state); + led_set_color_battery(LED_OFF); + return; + } + + /* + * Determine which phase of the state table to use. The phase is + * determined if it falls within first phase time duration. + */ + phase = ticks < led_bat_state_table[led_state][LED_PHASE_0].time ? + 0 : 1; + ticks = (ticks + 1) % period; + + /* Set the color for the given state and phase */ + led_set_color_battery(led_bat_state_table[led_state][phase].color); +} + +#ifdef CONFIG_LED_POWER_LED +static enum pwr_led_states pwr_led_get_state(void) +{ + if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) { + if (extpower_is_present()) + return PWR_LED_STATE_SUSPEND_AC; + else + return PWR_LED_STATE_SUSPEND_NO_AC; + } else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { + return PWR_LED_STATE_OFF; + } else if (chipset_in_state(CHIPSET_STATE_ON)) { + return PWR_LED_STATE_ON; + } + + return PWR_LED_NUM_STATES; +} + +static void led_update_power(void) +{ + static uint8_t ticks, period; + static enum pwr_led_states led_state = PWR_LED_NUM_STATES; + int phase; + enum pwr_led_states desired_state = pwr_led_get_state(); + + /* + * If we're in a new valid state, update our ticks and period info. + * Otherwise, continue to use old state + */ + if (desired_state != led_state && desired_state < PWR_LED_NUM_STATES) { + /* State is changing */ + led_state = desired_state; + /* Reset ticks and period when state changes */ + ticks = 0; + + period = led_pwr_state_table[led_state][LED_PHASE_0].time + + led_pwr_state_table[led_state][LED_PHASE_1].time; + + } + + /* If this state is undefined, turn the LED off */ + if (period == 0) { + CPRINTS("Undefined LED behavior for power state %d," + "turning off LED", led_state); + led_set_color_power(LED_OFF); + return; + } + + /* + * Determine which phase of the state table to use. The phase is + * determined if it falls within first phase time duration. + */ + phase = ticks < led_pwr_state_table[led_state][LED_PHASE_0].time ? + 0 : 1; + ticks = (ticks + 1) % period; + + /* Set the color for the given state and phase */ + led_set_color_power(led_pwr_state_table[led_state][phase].color); + +} +#endif + +static void led_init(void) +{ + /* If battery LED is enabled, set it to "off" to start with */ + if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) + led_set_color_battery(LED_OFF); + + /* If power LED is enabled, set it to "off" to start with */ +#ifdef CONFIG_LED_POWER_LED + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + led_set_color_power(LED_OFF); +#endif /* CONFIG_LED_POWER_LED */ + +} +DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); + +/* Called by hook task every hook tick (200 msec) */ +static void led_update(void) +{ + /* + * If battery LED is enabled, set its state based on our power and + * charge + */ + if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) + led_update_battery(); +#ifdef CONFIG_LED_POWER_LED + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + led_update_power(); +#endif +} +DECLARE_HOOK(HOOK_TICK, led_update, HOOK_PRIO_DEFAULT); diff --git a/include/config.h b/include/config.h index 7ac08ad6b1..44af10b3a3 100644 --- a/include/config.h +++ b/include/config.h @@ -2202,6 +2202,24 @@ #undef CONFIG_LED_PWM_COUNT /* + * Support GPIO-controlled LEDs for common battery/power + * states through a board-defined lookup table. + */ +#undef CONFIG_LED_ONOFF_STATES + +/* + * Set the battery charge percentage for optional STATE_DISCHARGE_S0_BAT_LOW + * provided by CONFIG_LED_ONOFF_STATES. + */ +#undef CONFIG_LED_ONOFF_STATES_BAT_LOW + +/* + * Adds a power LED under the control of the board-defined lookup table. + * Must be used with the CONFIG_LED_ONOFF_STATES option. + */ +#undef CONFIG_LED_POWER_LED + +/* * LEDs for LED_POLICY STD may be inverted. In this case they are active low * and the GPIO names will be GPIO_LED..._L. */ diff --git a/include/led_onoff_states.h b/include/led_onoff_states.h new file mode 100644 index 0000000000..2cbb1c1121 --- /dev/null +++ b/include/led_onoff_states.h @@ -0,0 +1,93 @@ +/* Copyright 2018 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. + * + * Common functions for stateful LEDs (charger and power) + */ + +#ifndef __CROS_EC_ONOFFSTATES_LED_H +#define __CROS_EC_ONOFFSTATES_LED_H + +#include "ec_commands.h" + +#define LED_INDEFINITE UINT8_MAX +#define LED_ONE_SEC (1000 / HOOK_TICK_INTERVAL_MS) +#define LED_OFF EC_LED_COLOR_COUNT + +/* + * All LED states should have one phase defined, + * and an additional phase can be defined for blinking + */ +enum led_phase { + LED_PHASE_0, + LED_PHASE_1, + LED_NUM_PHASES +}; + +/* + * STATE_CHARGING_LVL_1 is when 0 <= charge_percentage < led_charge_level_1 + * STATE_CHARGING_LVL_2 is when led_charge_level_1 <= + * charge_percentage < led_charge_level_2. + * STATE_CHARGING_FULL_CHARGE is when + * led_charge_level_2 <= charge_percentage < 100. + */ +enum led_states { + STATE_CHARGING_LVL_1, + STATE_CHARGING_LVL_2, + STATE_CHARGING_FULL_CHARGE, + STATE_DISCHARGE_S0, + STATE_DISCHARGE_S0_BAT_LOW, + STATE_DISCHARGE_S3, + STATE_DISCHARGE_S5, + STATE_BATTERY_ERROR, + STATE_FACTORY_TEST, + LED_NUM_STATES +}; + +struct led_descriptor { + enum ec_led_colors color; + uint8_t time; +}; + + +/* Charging LED state table - defined in board's led.c */ +extern const struct led_descriptor + led_bat_state_table[LED_NUM_STATES][LED_NUM_PHASES]; + +/* Charging LED state level 1 - defined in board's led.c */ +extern const int led_charge_lvl_1; + +/* Charging LED state level 2 - defined in board's led.c */ +extern const int led_charge_lvl_2; + +#ifdef CONFIG_LED_POWER_LED +enum pwr_led_states { + PWR_LED_STATE_ON, + PWR_LED_STATE_SUSPEND_AC, + PWR_LED_STATE_SUSPEND_NO_AC, + PWR_LED_STATE_OFF, + PWR_LED_NUM_STATES +}; + +/* Power LED state table - defined in board's led.c */ +extern const struct led_descriptor + led_pwr_state_table[PWR_LED_NUM_STATES][LED_NUM_PHASES]; + +#endif + +/** + * Set battery LED color - defined in board's led.c + * + * @param color Color to set on battery LED + * + */ +void led_set_color_battery(enum ec_led_colors color); + +#ifdef CONFIG_LED_POWER_LED +/** + * Set power LED color - defined in board's led.c + */ +void led_set_color_power(enum ec_led_colors color); +#endif + +#endif /* __CROS_EC_ONOFFSTATES_LED_H */ |