mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-29 18:11:05 +00:00
This adds support for EC_CMD_BATTERY_GET_STATIC and EC_CMD_BATTERY_GET_DYNAMIC host commands, that can currently only fetch the base battery information using index = 1. In the future, all battery information can be passed to AP using these host commands (i.e. lid could provide its own battery information on index = 0). BRANCH=none BUG=b:65697620 TEST=ectool battery shows lid battery information (no change) TEST=ectool battery 1 shows base battery information Change-Id: Ib819e4917b3acc337348764f6cc2aa7380bed700 Signed-off-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/863863 Reviewed-by: Randall Spangler <rspangler@chromium.org>
475 lines
11 KiB
C
475 lines
11 KiB
C
/* Copyright (c) 2012 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 battery command.
|
|
*/
|
|
|
|
#include "battery.h"
|
|
#include "charge_state.h"
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "ec_ec_comm_master.h"
|
|
#include "extpower.h"
|
|
#include "gpio.h"
|
|
#include "hooks.h"
|
|
#include "host_command.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
#include "watchdog.h"
|
|
|
|
#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
|
|
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
|
|
|
|
#ifdef CONFIG_BATTERY_CUT_OFF
|
|
|
|
#ifndef CONFIG_BATTERY_CUTOFF_DELAY_US
|
|
#define CONFIG_BATTERY_CUTOFF_DELAY_US (1 * SECOND)
|
|
#endif
|
|
|
|
static enum battery_cutoff_states battery_cutoff_state =
|
|
BATTERY_CUTOFF_STATE_NORMAL;
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_BATTERY_PRESENT_GPIO
|
|
#ifdef CONFIG_BATTERY_PRESENT_CUSTOM
|
|
#error "Don't define both CONFIG_BATTERY_PRESENT_CUSTOM and" \
|
|
"CONFIG_BATTERY_PRESENT_GPIO"
|
|
#endif
|
|
/**
|
|
* Physical detection of battery.
|
|
*/
|
|
enum battery_present battery_is_present(void)
|
|
{
|
|
/* The GPIO is low when the battery is present */
|
|
return gpio_get_level(CONFIG_BATTERY_PRESENT_GPIO) ? BP_NO : BP_YES;
|
|
}
|
|
#endif
|
|
|
|
static const char *get_error_text(int rv)
|
|
{
|
|
if (rv == EC_ERROR_UNIMPLEMENTED)
|
|
return "(unsupported)";
|
|
else
|
|
return "(error)";
|
|
}
|
|
|
|
static void print_item_name(const char *name)
|
|
{
|
|
ccprintf(" %-11s", name);
|
|
}
|
|
|
|
static int check_print_error(int rv)
|
|
{
|
|
if (rv != EC_SUCCESS)
|
|
ccprintf("%s\n", get_error_text(rv));
|
|
return rv == EC_SUCCESS;
|
|
}
|
|
|
|
static void print_battery_status(void)
|
|
{
|
|
static const char * const st[] = {"EMPTY", "FULL", "DCHG", "INIT",};
|
|
static const char * const al[] = {"RT", "RC", "--", "TD",
|
|
"OT", "--", "TC", "OC"};
|
|
|
|
int value, i;
|
|
|
|
print_item_name("Status:");
|
|
if (check_print_error(battery_status(&value))) {
|
|
ccprintf("0x%04x", value);
|
|
|
|
/* bits 0-3 are only valid when the previous transaction
|
|
* failed, so ignore them */
|
|
|
|
/* bits 4-7 are status */
|
|
for (i = 0; i < 4; i++)
|
|
if (value & (1 << (i+4)))
|
|
ccprintf(" %s", st[i]);
|
|
|
|
/* bits 15-8 are alarms */
|
|
for (i = 0; i < 8; i++)
|
|
if (value & (1 << (i+8)))
|
|
ccprintf(" %s", al[i]);
|
|
|
|
ccprintf("\n");
|
|
}
|
|
}
|
|
|
|
static void print_battery_strings(void)
|
|
{
|
|
char text[32];
|
|
|
|
print_item_name("Manuf:");
|
|
if (check_print_error(battery_manufacturer_name(text, sizeof(text))))
|
|
ccprintf("%s\n", text);
|
|
|
|
print_item_name("Device:");
|
|
if (check_print_error(battery_device_name(text, sizeof(text))))
|
|
ccprintf("%s\n", text);
|
|
|
|
print_item_name("Chem:");
|
|
if (check_print_error(battery_device_chemistry(text, sizeof(text))))
|
|
ccprintf("%s\n", text);
|
|
}
|
|
|
|
static void print_battery_params(void)
|
|
{
|
|
#if defined(HAS_TASK_CHARGER)
|
|
/* Ask charger so that we don't need to ask battery again. */
|
|
const struct batt_params *batt = charger_current_battery_params();
|
|
#else
|
|
/* This is for test code, where doesn't have charger task. */
|
|
struct batt_params _batt;
|
|
const struct batt_params *batt = &_batt;
|
|
|
|
battery_get_params(&_batt);
|
|
#endif
|
|
|
|
print_item_name("Param flags:");
|
|
ccprintf("%08x\n", batt->flags);
|
|
|
|
print_item_name("Temp:");
|
|
ccprintf("0x%04x = %.1d K (%.1d C)\n",
|
|
batt->temperature,
|
|
batt->temperature,
|
|
batt->temperature - 2731);
|
|
|
|
print_item_name("V:");
|
|
ccprintf("0x%04x = %d mV\n", batt->voltage, batt->voltage);
|
|
|
|
print_item_name("V-desired:");
|
|
ccprintf("0x%04x = %d mV\n", batt->desired_voltage,
|
|
batt->desired_voltage);
|
|
|
|
print_item_name("I:");
|
|
ccprintf("0x%04x = %d mA", batt->current & 0xffff, batt->current);
|
|
if (batt->current > 0)
|
|
ccputs("(CHG)");
|
|
else if (batt->current < 0)
|
|
ccputs("(DISCHG)");
|
|
ccputs("\n");
|
|
|
|
print_item_name("I-desired:");
|
|
ccprintf("0x%04x = %d mA\n", batt->desired_current,
|
|
batt->desired_current);
|
|
|
|
print_item_name("Charging:");
|
|
ccprintf("%sAllowed\n",
|
|
batt->flags & BATT_FLAG_WANT_CHARGE ? "" : "Not ");
|
|
|
|
print_item_name("Charge:");
|
|
ccprintf("%d %%\n", batt->state_of_charge);
|
|
}
|
|
|
|
static void print_battery_info(void)
|
|
{
|
|
int value;
|
|
int hour, minute;
|
|
|
|
print_item_name("Serial:");
|
|
if (check_print_error(battery_serial_number(&value)))
|
|
ccprintf("0x%04x\n", value);
|
|
|
|
print_item_name("V-design:");
|
|
if (check_print_error(battery_design_voltage(&value)))
|
|
ccprintf("0x%04x = %d mV\n", value, value);
|
|
|
|
print_item_name("Mode:");
|
|
if (check_print_error(battery_get_mode(&value)))
|
|
ccprintf("0x%04x\n", value);
|
|
|
|
print_item_name("Abs charge:");
|
|
if (check_print_error(battery_state_of_charge_abs(&value)))
|
|
ccprintf("%d %%\n", value);
|
|
|
|
print_item_name("Remaining:");
|
|
if (check_print_error(battery_remaining_capacity(&value)))
|
|
ccprintf("%d mAh\n", value);
|
|
|
|
print_item_name("Cap-full:");
|
|
if (check_print_error(battery_full_charge_capacity(&value)))
|
|
ccprintf("%d mAh\n", value);
|
|
|
|
print_item_name(" Design:");
|
|
if (check_print_error(battery_design_capacity(&value)))
|
|
ccprintf("%d mAh\n", value);
|
|
|
|
print_item_name("Time-full:");
|
|
if (check_print_error(battery_time_to_full(&value))) {
|
|
if (value == 65535) {
|
|
hour = 0;
|
|
minute = 0;
|
|
} else {
|
|
hour = value / 60;
|
|
minute = value % 60;
|
|
}
|
|
ccprintf("%dh:%d\n", hour, minute);
|
|
}
|
|
|
|
print_item_name(" Empty:");
|
|
if (check_print_error(battery_time_to_empty(&value))) {
|
|
if (value == 65535) {
|
|
hour = 0;
|
|
minute = 0;
|
|
} else {
|
|
hour = value / 60;
|
|
minute = value % 60;
|
|
}
|
|
ccprintf("%dh:%d\n", hour, minute);
|
|
}
|
|
}
|
|
|
|
void print_battery_debug(void)
|
|
{
|
|
print_battery_status();
|
|
print_battery_params();
|
|
print_battery_strings();
|
|
print_battery_info();
|
|
}
|
|
|
|
static int command_battery(int argc, char **argv)
|
|
{
|
|
int repeat = 1;
|
|
int loop;
|
|
int sleep_ms = 0;
|
|
char *e;
|
|
|
|
if (argc > 1) {
|
|
repeat = strtoi(argv[1], &e, 0);
|
|
if (*e) {
|
|
ccputs("Invalid repeat count\n");
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
}
|
|
|
|
if (argc > 2) {
|
|
sleep_ms = strtoi(argv[2], &e, 0);
|
|
if (*e) {
|
|
ccputs("Invalid sleep ms\n");
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
}
|
|
|
|
for (loop = 0; loop < repeat; loop++) {
|
|
print_battery_debug();
|
|
|
|
/*
|
|
* Running with a high repeat count will take so long the
|
|
* watchdog timer fires. So reset the watchdog timer each
|
|
* iteration.
|
|
*/
|
|
watchdog_reload();
|
|
|
|
if (sleep_ms)
|
|
msleep(sleep_ms);
|
|
}
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(battery, command_battery,
|
|
"<repeat_count> <sleep_ms>",
|
|
"Print battery info");
|
|
|
|
#ifdef CONFIG_BATTERY_CUT_OFF
|
|
int battery_is_cut_off(void)
|
|
{
|
|
return (battery_cutoff_state == BATTERY_CUTOFF_STATE_CUT_OFF);
|
|
}
|
|
|
|
static void pending_cutoff_deferred(void)
|
|
{
|
|
int rv;
|
|
|
|
rv = board_cut_off_battery();
|
|
|
|
if (rv == EC_SUCCESS)
|
|
CPRINTF("[%T Battery cut off succeeded.]\n");
|
|
else
|
|
CPRINTF("[%T Battery cut off failed!]\n");
|
|
}
|
|
DECLARE_DEFERRED(pending_cutoff_deferred);
|
|
|
|
static void clear_pending_cutoff(void)
|
|
{
|
|
if (extpower_is_present()) {
|
|
battery_cutoff_state = BATTERY_CUTOFF_STATE_NORMAL;
|
|
hook_call_deferred(&pending_cutoff_deferred_data, -1);
|
|
}
|
|
}
|
|
DECLARE_HOOK(HOOK_AC_CHANGE, clear_pending_cutoff, HOOK_PRIO_DEFAULT);
|
|
|
|
static int battery_command_cutoff(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_battery_cutoff *p;
|
|
int rv;
|
|
|
|
if (args->version == 1) {
|
|
p = args->params;
|
|
if (p->flags & EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN) {
|
|
battery_cutoff_state = BATTERY_CUTOFF_STATE_PENDING;
|
|
CPRINTS("Battery cut off at-shutdown is scheduled");
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
}
|
|
|
|
rv = board_cut_off_battery();
|
|
if (!rv) {
|
|
CPRINTS("Battery cut off is successful.");
|
|
battery_cutoff_state = BATTERY_CUTOFF_STATE_CUT_OFF;
|
|
} else {
|
|
CPRINTS("Battery cut off has failed.");
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_BATTERY_CUT_OFF, battery_command_cutoff,
|
|
EC_VER_MASK(0) | EC_VER_MASK(1));
|
|
|
|
static void check_pending_cutoff(void)
|
|
{
|
|
if (battery_cutoff_state == BATTERY_CUTOFF_STATE_PENDING) {
|
|
CPRINTF("[%T Cutting off battery in %d second(s)]\n",
|
|
CONFIG_BATTERY_CUTOFF_DELAY_US / SECOND);
|
|
hook_call_deferred(&pending_cutoff_deferred_data,
|
|
CONFIG_BATTERY_CUTOFF_DELAY_US);
|
|
}
|
|
}
|
|
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, check_pending_cutoff, HOOK_PRIO_LAST);
|
|
|
|
static int command_cutoff(int argc, char **argv)
|
|
{
|
|
int rv;
|
|
|
|
if (argc > 1) {
|
|
if (!strcasecmp(argv[1], "at-shutdown")) {
|
|
battery_cutoff_state = BATTERY_CUTOFF_STATE_PENDING;
|
|
return EC_SUCCESS;
|
|
} else {
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
}
|
|
|
|
rv = board_cut_off_battery();
|
|
if (!rv) {
|
|
ccprintf("[%T Battery cut off]\n");
|
|
battery_cutoff_state = BATTERY_CUTOFF_STATE_CUT_OFF;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(cutoff, command_cutoff,
|
|
"[at-shutdown]",
|
|
"Cut off the battery output");
|
|
#else
|
|
int battery_is_cut_off(void)
|
|
{
|
|
return 0; /* Always return NOT cut off */
|
|
}
|
|
#endif /* CONFIG_BATTERY_CUT_OFF */
|
|
|
|
#ifdef CONFIG_BATTERY_VENDOR_PARAM
|
|
static int console_command_battery_vendor_param(int argc, char **argv)
|
|
{
|
|
uint32_t param;
|
|
uint32_t value;
|
|
char *e;
|
|
int rv;
|
|
|
|
if (argc < 2)
|
|
return EC_ERROR_INVAL;
|
|
|
|
param = strtoi(argv[1], &e, 0);
|
|
if (*e) {
|
|
ccputs("Invalid param\n");
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
|
|
if (argc > 2) {
|
|
value = strtoi(argv[2], &e, 0);
|
|
if (*e) {
|
|
ccputs("Invalid value\n");
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
rv = battery_set_vendor_param(param, value);
|
|
if (rv != EC_SUCCESS)
|
|
return rv;
|
|
}
|
|
|
|
rv = battery_get_vendor_param(param, &value);
|
|
if (rv != EC_SUCCESS)
|
|
return rv;
|
|
|
|
ccprintf("0x%08x\n", value);
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(battparam, console_command_battery_vendor_param,
|
|
"<param> [value]",
|
|
"Get or set battery vendor parameters");
|
|
|
|
static int host_command_battery_vendor_param(struct host_cmd_handler_args *args)
|
|
{
|
|
int rv;
|
|
const struct ec_params_battery_vendor_param *p = args->params;
|
|
struct ec_response_battery_vendor_param *r = args->response;
|
|
|
|
args->response_size = sizeof(*r);
|
|
|
|
if (p->mode != BATTERY_VENDOR_PARAM_MODE_GET &&
|
|
p->mode != BATTERY_VENDOR_PARAM_MODE_SET)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
if (p->mode == BATTERY_VENDOR_PARAM_MODE_SET) {
|
|
rv = battery_set_vendor_param(p->param, p->value);
|
|
if (rv != EC_SUCCESS)
|
|
return rv;
|
|
}
|
|
|
|
rv = battery_get_vendor_param(p->param, &r->value);
|
|
return rv;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_BATTERY_VENDOR_PARAM,
|
|
host_command_battery_vendor_param,
|
|
EC_VER_MASK(0));
|
|
#endif /* CONFIG_BATTERY_VENDOR_PARAM */
|
|
|
|
#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
|
|
/*
|
|
* TODO(b:65697620): Add support for returning main battery information through
|
|
* these commands, as well.
|
|
*/
|
|
static int host_command_battery_get_static(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_battery_static_info *p = args->params;
|
|
struct ec_response_battery_static_info *r = args->response;
|
|
|
|
if (p->index != 1)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
args->response_size = sizeof(*r);
|
|
memcpy(r, &base_battery_static, sizeof(*r));
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_BATTERY_GET_STATIC,
|
|
host_command_battery_get_static,
|
|
EC_VER_MASK(0));
|
|
|
|
static int host_command_battery_get_dynamic(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_battery_dynamic_info *p = args->params;
|
|
struct ec_response_battery_dynamic_info *r = args->response;
|
|
|
|
if (p->index != 1)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
args->response_size = sizeof(*r);
|
|
memcpy(r, &base_battery_dynamic, sizeof(*r));
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_BATTERY_GET_DYNAMIC,
|
|
host_command_battery_get_dynamic,
|
|
EC_VER_MASK(0));
|
|
#endif /* CONFIG_EC_EC_COMM_BATTERY */
|