mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-07 08:01:35 +00:00
Device-specific headers belong in driver/ or chip/. The include/ directory should be for common interfaces. Code should not normally need to include driver-specific headers. If it does, it should use the full relative path from the EC project root (for example, drivers/charger/bq24715.h). Change-Id: Id23db37a431e2d802a74ec601db6f69b613352ba Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/173746 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
194 lines
4.7 KiB
C
194 lines
4.7 KiB
C
/* 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 "battery.h"
|
|
#include "common.h"
|
|
#include "driver/led/lp5562.h"
|
|
#include "extpower.h"
|
|
#include "hooks.h"
|
|
#include "host_command.h"
|
|
#include "pmu_tpschrome.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);
|