summaryrefslogtreecommitdiff
path: root/zephyr/shim/src/led_driver/led.c
diff options
context:
space:
mode:
authorParth Malkan <parthmalkan@google.com>2022-04-01 14:22:59 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-04-02 00:30:04 +0000
commit187a8b391d8103ef57b23543417c2235dc766fb4 (patch)
tree26d4affd498767b2d30f64df806880f18183335d /zephyr/shim/src/led_driver/led.c
parent0728ca37596203dbc9def3a4d442bd3d7aaac6ba (diff)
downloadchrome-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.c304
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);
+}