627 lines
16 KiB
C
627 lines
16 KiB
C
|
/* Copyright 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)
|
||
|
|
||
|
/* See config.h for details */
|
||
|
const static int batt_full_factor = CONFIG_BATT_FULL_FACTOR;
|
||
|
const static int batt_host_full_factor = CONFIG_BATT_HOST_FULL_FACTOR;
|
||
|
const static int batt_host_shutdown_pct = CONFIG_BATT_HOST_SHUTDOWN_PERCENTAGE;
|
||
|
|
||
|
#ifdef CONFIG_BATTERY_V2
|
||
|
/*
|
||
|
* Store battery information in these 2 structures. Main (lid) battery is always
|
||
|
* at index 0, and secondary (base) battery at index 1.
|
||
|
*/
|
||
|
struct ec_response_battery_static_info battery_static[CONFIG_BATTERY_COUNT];
|
||
|
struct ec_response_battery_dynamic_info battery_dynamic[CONFIG_BATTERY_COUNT];
|
||
|
#endif
|
||
|
|
||
|
#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 (%d mAh with %d %% compensation)\n",
|
||
|
value, value*batt_full_factor/100, batt_full_factor);
|
||
|
|
||
|
#ifdef CONFIG_CHARGER
|
||
|
print_item_name("Display:");
|
||
|
value = charge_get_display_charge();
|
||
|
ccprintf("%d.%d %%\n", value / 10, value % 10);
|
||
|
#endif
|
||
|
|
||
|
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_RES_SUCCESS)
|
||
|
CPRINTS("Battery cut off succeeded.");
|
||
|
else
|
||
|
CPRINTS("Battery cut off failed!");
|
||
|
}
|
||
|
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 enum ec_status 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 == EC_RES_SUCCESS) {
|
||
|
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 == EC_RES_SUCCESS) {
|
||
|
ccprints("Battery cut off");
|
||
|
battery_cutoff_state = BATTERY_CUTOFF_STATE_CUT_OFF;
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return EC_ERROR_UNKNOWN;
|
||
|
}
|
||
|
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 enum ec_status
|
||
|
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_BATTERY_V2
|
||
|
#ifdef CONFIG_HOSTCMD_BATTERY_V2
|
||
|
static void battery_update(enum battery_index i);
|
||
|
static enum ec_status
|
||
|
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 < 0 || p->index >= CONFIG_BATTERY_COUNT)
|
||
|
return EC_RES_INVALID_PARAM;
|
||
|
battery_update(p->index);
|
||
|
args->response_size = sizeof(*r);
|
||
|
memcpy(r, &battery_static[p->index], sizeof(*r));
|
||
|
|
||
|
return EC_RES_SUCCESS;
|
||
|
}
|
||
|
DECLARE_HOST_COMMAND(EC_CMD_BATTERY_GET_STATIC,
|
||
|
host_command_battery_get_static,
|
||
|
EC_VER_MASK(0));
|
||
|
|
||
|
static enum ec_status
|
||
|
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 < 0 || p->index >= CONFIG_BATTERY_COUNT)
|
||
|
return EC_RES_INVALID_PARAM;
|
||
|
|
||
|
args->response_size = sizeof(*r);
|
||
|
memcpy(r, &battery_dynamic[p->index], 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_HOSTCMD_BATTERY_V2 */
|
||
|
|
||
|
#ifdef HAS_TASK_HOSTCMD
|
||
|
static void battery_update(enum battery_index i)
|
||
|
{
|
||
|
char *batt_str;
|
||
|
int *memmap_dcap = (int *)host_get_memmap(EC_MEMMAP_BATT_DCAP);
|
||
|
int *memmap_dvlt = (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT);
|
||
|
int *memmap_ccnt = (int *)host_get_memmap(EC_MEMMAP_BATT_CCNT);
|
||
|
int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT);
|
||
|
int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE);
|
||
|
int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP);
|
||
|
int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC);
|
||
|
uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG);
|
||
|
|
||
|
/* Smart battery serial number is 16 bits */
|
||
|
batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL);
|
||
|
memcpy(batt_str, battery_static[i].serial, EC_MEMMAP_TEXT_MAX);
|
||
|
|
||
|
/* Design Capacity of Full */
|
||
|
*memmap_dcap = battery_static[i].design_capacity;
|
||
|
|
||
|
/* Design Voltage */
|
||
|
*memmap_dvlt = battery_static[i].design_voltage;
|
||
|
|
||
|
/* Cycle Count */
|
||
|
*memmap_ccnt = battery_static[i].cycle_count;
|
||
|
|
||
|
/* Battery Manufacturer string */
|
||
|
batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR);
|
||
|
memcpy(batt_str, battery_static[i].manufacturer, EC_MEMMAP_TEXT_MAX);
|
||
|
|
||
|
/* Battery Model string */
|
||
|
batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL);
|
||
|
memcpy(batt_str, battery_static[i].model, EC_MEMMAP_TEXT_MAX);
|
||
|
|
||
|
/* Battery Type string */
|
||
|
batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE);
|
||
|
memcpy(batt_str, battery_static[i].type, EC_MEMMAP_TEXT_MAX);
|
||
|
|
||
|
*memmap_volt = battery_dynamic[i].actual_voltage;
|
||
|
*memmap_rate = battery_dynamic[i].actual_current;
|
||
|
*memmap_cap = battery_dynamic[i].remaining_capacity;
|
||
|
*memmap_lfcc = battery_dynamic[i].full_capacity;
|
||
|
*memmap_flags = battery_dynamic[i].flags;
|
||
|
}
|
||
|
|
||
|
void battery_memmap_refresh(enum battery_index index)
|
||
|
{
|
||
|
if (*host_get_memmap(EC_MEMMAP_BATT_INDEX) == index)
|
||
|
battery_update(index);
|
||
|
}
|
||
|
|
||
|
void battery_memmap_set_index(enum battery_index index)
|
||
|
{
|
||
|
if (*host_get_memmap(EC_MEMMAP_BATT_INDEX) == index)
|
||
|
return;
|
||
|
|
||
|
*host_get_memmap(EC_MEMMAP_BATT_INDEX) = BATT_IDX_INVALID;
|
||
|
if (index < 0 || index >= CONFIG_BATTERY_COUNT)
|
||
|
return;
|
||
|
|
||
|
battery_update(index);
|
||
|
*host_get_memmap(EC_MEMMAP_BATT_INDEX) = index;
|
||
|
}
|
||
|
|
||
|
static void battery_init(void)
|
||
|
{
|
||
|
*host_get_memmap(EC_MEMMAP_BATT_INDEX) = BATT_IDX_INVALID;
|
||
|
*host_get_memmap(EC_MEMMAP_BATT_COUNT) = CONFIG_BATTERY_COUNT;
|
||
|
*host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 2;
|
||
|
|
||
|
battery_memmap_set_index(BATT_IDX_MAIN);
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_INIT, battery_init, HOOK_PRIO_DEFAULT);
|
||
|
#endif /* HAS_TASK_HOSTCMD */
|
||
|
#endif /* CONFIG_BATTERY_V2 */
|
||
|
|
||
|
void battery_compensate_params(struct batt_params *batt)
|
||
|
{
|
||
|
int numer, denom;
|
||
|
int remain = batt->remaining_capacity;
|
||
|
int full = batt->full_capacity;
|
||
|
int lfcc = *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC);
|
||
|
|
||
|
if ((batt->flags & BATT_FLAG_BAD_FULL_CAPACITY) ||
|
||
|
(batt->flags & BATT_FLAG_BAD_REMAINING_CAPACITY))
|
||
|
return;
|
||
|
|
||
|
if (remain <= 0 || full <= 0)
|
||
|
return;
|
||
|
|
||
|
/* full_factor != 100 isn't supported. EC and host are not able to
|
||
|
* act on soc changes synchronously. */
|
||
|
if (batt_host_full_factor != 100)
|
||
|
return;
|
||
|
|
||
|
/* full_factor is effectively disabled in powerd. */
|
||
|
batt->full_capacity = full * batt_full_factor / 100;
|
||
|
if (lfcc == 0)
|
||
|
/* EC just reset. Assume host full is equal. */
|
||
|
lfcc = batt->full_capacity;
|
||
|
if (remain > lfcc) {
|
||
|
batt->remaining_capacity = lfcc;
|
||
|
remain = batt->remaining_capacity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Powerd uses the following equation to calculate display percentage:
|
||
|
* charge = 100 * remain/full;
|
||
|
* 100 * (charge - shutdown_pct) / (full_factor - shutdown_pct);
|
||
|
*/
|
||
|
numer = (100 * remain - lfcc * batt_host_shutdown_pct) * 1000;
|
||
|
denom = lfcc * (100 - batt_host_shutdown_pct);
|
||
|
/* Rounding (instead of truncating) */
|
||
|
batt->display_charge = (numer + denom / 2) / denom;
|
||
|
if (batt->display_charge < 0)
|
||
|
batt->display_charge = 0;
|
||
|
}
|
||
|
|
||
|
__attribute__((weak)) int get_battery_manufacturer_name(char *dest, int size)
|
||
|
{
|
||
|
strzcpy(dest, "<unkn>", size);
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int battery_manufacturer_name(char *dest, int size)
|
||
|
{
|
||
|
return get_battery_manufacturer_name(dest, size);
|
||
|
}
|