summaryrefslogtreecommitdiff
path: root/board/spring/led.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/spring/led.c')
-rw-r--r--board/spring/led.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/board/spring/led.c b/board/spring/led.c
new file mode 100644
index 0000000000..3d6e316426
--- /dev/null
+++ b/board/spring/led.c
@@ -0,0 +1,194 @@
+/* Copyright (c) 2013 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.
+ *
+ * Battery LED state machine to drive RGB LED on LP5562
+ */
+
+#include "common.h"
+#include "ec_commands.h"
+#include "extpower.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "lp5562.h"
+#include "pmu_tpschrome.h"
+#include "smart_battery.h"
+#include "timer.h"
+#include "util.h"
+
+#define GREEN_LED_THRESHOLD 94
+
+/* Minimal interval between changing LED color to green and yellow. */
+#define LED_WAIT_INTERVAL (15 * SECOND)
+
+/* We use yellow LED instead of blue LED. Re-map colors here. */
+#define LED_COLOR_NONE LP5562_COLOR_NONE
+#define LED_COLOR_GREEN LP5562_COLOR_GREEN(0x10)
+#define LED_COLOR_YELLOW LP5562_COLOR_BLUE(0x40)
+#define LED_COLOR_RED LP5562_COLOR_RED(0x80)
+
+/* LED states */
+enum led_state_t {
+ LED_STATE_SOLID_RED,
+ LED_STATE_SOLID_GREEN,
+ LED_STATE_SOLID_YELLOW,
+
+ /* Not an actual state */
+ LED_STATE_OFF,
+
+ /* Used to force next LED color update */
+ LED_STATE_INVALID,
+};
+
+static enum led_state_t last_state = LED_STATE_OFF;
+static int led_auto_control = 1;
+
+static int set_led_color(enum led_state_t state)
+{
+ int rv = EC_SUCCESS;
+
+ if (!led_auto_control || state == last_state)
+ return EC_SUCCESS;
+
+ switch (state) {
+ case LED_STATE_SOLID_RED:
+ rv = lp5562_set_color(LED_COLOR_RED);
+ break;
+ case LED_STATE_SOLID_GREEN:
+ rv = lp5562_set_color(LED_COLOR_GREEN);
+ break;
+ case LED_STATE_SOLID_YELLOW:
+ rv = lp5562_set_color(LED_COLOR_YELLOW);
+ break;
+ default:
+ break;
+ }
+
+ if (rv == EC_SUCCESS)
+ last_state = state;
+ else
+ last_state = LED_STATE_INVALID;
+ return rv;
+}
+
+/*****************************************************************************/
+/* Host commands */
+
+static int led_command_control(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_led_control *p = args->params;
+ struct ec_response_led_control *r = args->response;
+ int i;
+ uint8_t clipped[EC_LED_COLOR_COUNT];
+
+ /* Only support battery LED control */
+ if (p->led_id != EC_LED_ID_BATTERY_LED)
+ return EC_RES_INVALID_PARAM;
+
+ if (p->flags & EC_LED_FLAGS_AUTO) {
+ if (!extpower_is_present())
+ lp5562_poweroff();
+ last_state = LED_STATE_OFF;
+ led_auto_control = 1;
+ } else if (!(p->flags & EC_LED_FLAGS_QUERY)) {
+ for (i = 0; i < EC_LED_COLOR_COUNT; ++i)
+ clipped[i] = MIN(p->brightness[i], 0x80);
+ led_auto_control = 0;
+ if (!extpower_is_present())
+ lp5562_poweron();
+ if (lp5562_set_color((clipped[EC_LED_COLOR_RED] << 16) +
+ (clipped[EC_LED_COLOR_GREEN] << 8) +
+ clipped[EC_LED_COLOR_YELLOW]))
+ return EC_RES_ERROR;
+ }
+
+ r->brightness_range[EC_LED_COLOR_RED] = 0x80;
+ r->brightness_range[EC_LED_COLOR_GREEN] = 0x80;
+ r->brightness_range[EC_LED_COLOR_BLUE] = 0x0;
+ r->brightness_range[EC_LED_COLOR_YELLOW] = 0x80;
+ r->brightness_range[EC_LED_COLOR_WHITE] = 0x0;
+ args->response_size = sizeof(struct ec_response_led_control);
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_LED_CONTROL,
+ led_command_control,
+ EC_VER_MASK(1));
+
+/*****************************************************************************/
+/* Hooks */
+
+static void battery_led_update(void)
+{
+ int rv;
+ int state_of_charge;
+ enum led_state_t state = LED_STATE_OFF;
+
+ /* Current states and next states */
+ static int led_power = -1;
+ int new_led_power;
+
+ /*
+ * The time before which we should not change LED
+ * color between green and yellow.
+ */
+ static timestamp_t led_update_deadline = {.val = 0};
+
+ /* Determine LED power */
+ new_led_power = extpower_is_present();
+ if (new_led_power != led_power) {
+ if (new_led_power) {
+ rv = lp5562_poweron();
+ } else {
+ rv = lp5562_poweroff();
+ set_led_color(LED_STATE_OFF);
+ led_update_deadline.val = 0;
+ }
+ if (!rv)
+ led_power = new_led_power;
+ }
+ if (!new_led_power)
+ return;
+
+ /*
+ * LED power is controlled by accessory detection. We only
+ * set color here.
+ */
+ switch (charge_get_state()) {
+ case ST_IDLE0:
+ case ST_BAD_COND:
+ case ST_PRE_CHARGING:
+ state = LED_STATE_SOLID_YELLOW;
+ break;
+ case ST_IDLE:
+ case ST_DISCHARGING:
+ case ST_CHARGING:
+ if (battery_state_of_charge(&state_of_charge)) {
+ /* Cannot talk to the battery. Set LED to red. */
+ state = LED_STATE_SOLID_RED;
+ break;
+ }
+
+ if (state_of_charge < GREEN_LED_THRESHOLD)
+ state = LED_STATE_SOLID_YELLOW;
+ else
+ state = LED_STATE_SOLID_GREEN;
+ break;
+ case ST_CHARGING_ERROR:
+ state = LED_STATE_SOLID_RED;
+ break;
+ }
+
+ if (state == LED_STATE_SOLID_GREEN ||
+ state == LED_STATE_SOLID_YELLOW) {
+ if (!timestamp_expired(led_update_deadline, NULL))
+ return;
+ led_update_deadline.val =
+ get_time().val + LED_WAIT_INTERVAL;
+ } else {
+ led_update_deadline.val = 0;
+ }
+
+ set_led_color(state);
+}
+DECLARE_HOOK(HOOK_SECOND, battery_led_update, HOOK_PRIO_DEFAULT);