diff options
author | Parth Malkan <parthmalkan@google.com> | 2022-04-01 14:22:59 -0700 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2022-04-02 00:30:04 +0000 |
commit | 187a8b391d8103ef57b23543417c2235dc766fb4 (patch) | |
tree | 26d4affd498767b2d30f64df806880f18183335d /zephyr/shim/src/led_driver/led.c | |
parent | 0728ca37596203dbc9def3a4d442bd3d7aaac6ba (diff) | |
download | chrome-ec-187a8b391d8103ef57b23543417c2235dc766fb4.tar.gz |
zephyr: LED: Split GPIO implementation
Currently, policy logic and setting GPIO pins are in the same file.
This patch splits setting GPIO pins logic into a separate implementation.
Also, the nodes common to both PWM and GPIO are renamed.
BUG=b:227706373, b:194430340
TEST=zmake build -a, manual LED test on Lazor
BRANCH=none
Signed-off-by: Parth Malkan <parthmalkan@google.com>
Change-Id: I2bf38153b9f5db9113670d9c76f5667ed4d8725c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3566211
Reviewed-by: Wai-Hong Tam <waihong@google.com>
Diffstat (limited to 'zephyr/shim/src/led_driver/led.c')
-rw-r--r-- | zephyr/shim/src/led_driver/led.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/zephyr/shim/src/led_driver/led.c b/zephyr/shim/src/led_driver/led.c new file mode 100644 index 0000000000..a21284bfae --- /dev/null +++ b/zephyr/shim/src/led_driver/led.c @@ -0,0 +1,304 @@ +/* Copyright 2022 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. + */ + +#include <drivers/gpio.h> + +#include "battery.h" +#include "charge_manager.h" +#include "charge_state.h" +#include "chipset.h" +#include "ec_commands.h" +#include "hooks.h" +#include "host_command.h" +#include "led.h" +#include "led_common.h" +#include "power.h" +#include "system.h" +#include "util.h" + +#include <devicetree.h> +#include <logging/log.h> +LOG_MODULE_REGISTER(gpio_led, LOG_LEVEL_ERR); + +#define LED_ONE_SEC (1000 / HOOK_TICK_INTERVAL_MS) + +#define BAT_LED_ON 1 +#define BAT_LED_OFF 0 + +#define LED_COLOR_NODE DT_PATH(led, led_colors) + +const enum ec_led_id supported_led_ids[] = { + EC_LED_ID_BATTERY_LED, +}; + +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +struct led_color_node_t { + int led_color; + int acc_period; +}; + +enum led_extra_flag_t { + NONE = 0, + LED_CHFLAG_FORCE_IDLE, + LED_CHFLAG_DEFAULT, + LED_BATT_BELOW_10_PCT, + LED_BATT_ABOVE_10_PCT, +}; + +/* + * Currently 4 different colors are supported for blinking LED, each of which + * can have different periods. Each period slot is the accumulation of previous + * periods as described below. Last slot is the total accumulation which is + * used as a dividing factor to calculate ticks to switch color + * Eg LED_COLOR_1 1 sec, LED_COLOR_2 2 sec, LED_COLOR_3 3 sec, LED_COLOR_4 3 sec + * period_1 = 1, period_2 = 1 + 2, period_3 = 1 + 2 + 3, period_4 =1 + 2 + 3 + 3 + * ticks -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2 and so on (ticks % 9) + * 0 < period_1 -> LED_COLOR_1 for 1 sec + * 1, 2 < period_2 -> LED_COLOR_2 for 2 secs + * 3, 4, 5 < period_3 -> LED_COLOR_3 for 3 secs + * 6, 7, 8 < period_4 -> LED_COLOR_4 for 3 secs + */ +#define MAX_COLOR 4 + +struct node_prop_t { + enum charge_state pwr_state; + enum power_state chipset_state; + enum led_extra_flag_t led_extra_flag; + struct led_color_node_t led_colors[MAX_COLOR]; +}; + +/* + * acc_period is the accumulated period value of all color-x children + * led_colors[0].acc_period = period value of color-0 node + * led_colors[1].acc_period = period value of color-0 + color-1 nodes + * led_colors[2].acc_period = period value of color-0 + color-1 + color-2 nodes + * and so on. If period prop or color node doesn't exist, period val is 0 + */ + +#define PERIOD_VAL(id) COND_CODE_1(DT_NODE_HAS_PROP(id, period), \ + (DT_PROP(id, period)), \ + (0)) + +#define LED_PERIOD(color_num, state_id) \ + PERIOD_VAL(DT_CHILD(state_id, color_##color_num)) + +#define LED_PLUS_PERIOD(color_num, state_id) \ + + LED_PERIOD(color_num, state_id) + +#define ACC_PERIOD(color_num, state_id) \ + (0 LISTIFY(color_num, LED_PLUS_PERIOD, (), state_id)) + +#define LED_COLOR_INIT(color_num, color_num_plus_one, state_id) \ +{ \ + .led_color = GET_PROP(DT_CHILD(state_id, color_##color_num), \ + led_color), \ + .acc_period = ACC_PERIOD(color_num_plus_one, state_id) \ +} + +/* + * Initialize node_array struct with prop listed in dts + */ +#define SET_LED_VALUES(state_id) \ +{ \ + .pwr_state = GET_PROP(state_id, charge_state), \ + .chipset_state = GET_PROP(state_id, chipset_state), \ + .led_extra_flag = GET_PROP(state_id, extra_flag), \ + .led_colors = {LED_COLOR_INIT(0, 1, state_id), \ + LED_COLOR_INIT(1, 2, state_id), \ + LED_COLOR_INIT(2, 3, state_id), \ + LED_COLOR_INIT(3, 4, state_id), \ + } \ +}, + +struct node_prop_t node_array[] = { + DT_FOREACH_CHILD(LED_COLOR_NODE, SET_LED_VALUES) +}; + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + brightness_range[EC_LED_COLOR_AMBER] = 1; + brightness_range[EC_LED_COLOR_BLUE] = 1; +} + +int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +{ + if (brightness[EC_LED_COLOR_BLUE] != 0) + led_set_color(LED_BLUE); + else if (brightness[EC_LED_COLOR_AMBER] != 0) + led_set_color(LED_AMBER); + else + led_set_color(LED_OFF); + + return EC_SUCCESS; +} + +static enum power_state get_chipset_state(void) +{ + enum power_state chipset_state = 0; + + /* + * Only covers subset of power states as other states don't + * alter LED behavior + */ + if (chipset_in_state(CHIPSET_STATE_ON)) + /* S0 */ + chipset_state = POWER_S0; + else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) + /* S3 */ + chipset_state = POWER_S3; + else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + /* S5 */ + chipset_state = POWER_S5; + + return chipset_state; +} + +static bool find_node_with_extra_flag(int i) +{ + uint32_t chflags = charge_get_flags(); + bool found_node = false; + + switch (node_array[i].led_extra_flag) { + case LED_CHFLAG_FORCE_IDLE: + case LED_CHFLAG_DEFAULT: + if (chflags & CHARGE_FLAG_FORCE_IDLE) { + if (node_array[i].led_extra_flag == + LED_CHFLAG_FORCE_IDLE) + found_node = true; + } else { + if (node_array[i].led_extra_flag == LED_CHFLAG_DEFAULT) + found_node = true; + } + break; + case LED_BATT_BELOW_10_PCT: + case LED_BATT_ABOVE_10_PCT: + if (charge_get_percent() < 10) { + if (node_array[i].led_extra_flag == + LED_BATT_BELOW_10_PCT) + found_node = true; + } else { + if (node_array[i].led_extra_flag != + LED_BATT_ABOVE_10_PCT) + found_node = true; + } + break; + default: + LOG_ERR("Invalid led extra flag %d", + node_array[i].led_extra_flag); + break; + } + + return found_node; +} + +static int find_node(void) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(node_array); i++) { + /* Check if this node depends on power state */ + if (node_array[i].pwr_state != PWR_STATE_UNCHANGE) { + enum charge_state pwr_state = charge_get_state(); + + if (node_array[i].pwr_state != pwr_state) + continue; + } + + /* Check if this node depends on chipset state */ + if (node_array[i].chipset_state != 0) { + enum power_state chipset_state = + get_chipset_state(); + + /* Continue at current index as nodes are in sequence */ + if (node_array[i].chipset_state != chipset_state) + continue; + } + + /* Check if the node depends on any special flags */ + if (node_array[i].led_extra_flag != NONE) + if (!find_node_with_extra_flag(i)) + continue; + + /* We found the node */ + return i; + } + + /* + * Didn't find a valid node that matches all the properties + * Return -1 to signify error + */ + return -1; +} + +#define GET_PERIOD(n_idx, c_idx) node_array[n_idx].led_colors[c_idx].acc_period +#define GET_COLOR(n_idx, c_idx) node_array[n_idx].led_colors[c_idx].led_color + +static int find_color(int node_idx, int ticks) +{ + int color_idx = 0; + + /* If period value at index 0 is not 0, it's a blinking LED */ + if (GET_PERIOD(node_idx, 0) != 0) { + /* Period is accumulated at the last index */ + ticks = ticks % GET_PERIOD(node_idx, MAX_COLOR - 1); + + for (color_idx = 0; color_idx < MAX_COLOR; color_idx++) { + if (ticks < GET_PERIOD(node_idx, color_idx)) + break; + } + } + + return GET_COLOR(node_idx, color_idx); +} + +static void board_led_set_color(void) +{ + int color = LED_OFF; + int node = 0; + static int ticks; + + ticks++; + + node = find_node(); + + if (node < 0) + LOG_ERR("Invalid node id, node with matching prop not found"); + else + color = find_color(node, ticks); + + led_set_color(color); +} + +/* Called by hook task every second */ +static void led_tick(void) +{ + if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) + board_led_set_color(); +} +DECLARE_HOOK(HOOK_SECOND, led_tick, HOOK_PRIO_DEFAULT); + +void led_control(enum ec_led_id led_id, enum ec_led_state state) +{ + enum led_color color; + + if ((led_id != EC_LED_ID_RECOVERY_HW_REINIT_LED) && + (led_id != EC_LED_ID_SYSRQ_DEBUG_LED)) + return; + + if (state == LED_STATE_RESET) { + led_auto_control(EC_LED_ID_BATTERY_LED, 1); + board_led_set_color(); + return; + } + + color = state ? LED_BLUE : LED_OFF; + + led_auto_control(EC_LED_ID_BATTERY_LED, 0); + + led_set_color(color); +} |