summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@chromium.org>2017-09-08 12:05:33 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-01-26 13:25:38 -0800
commite5c6f97d1a4a1fa5ad17c2bab4e316a47b8def9a (patch)
tree88986d177cc58fd4c4f039cca41bc532ccb1a418
parentecfa69663813ec41aa79ed01c4af2f36723fe194 (diff)
downloadchrome-ec-e5c6f97d1a4a1fa5ad17c2bab4e316a47b8def9a.tar.gz
charge_state_v2: Basic dual-battery charging algorithm
First version of the algorithm, some TODOs are left in the code but this, generally, works reasonably well. When charging, we allocate input current in this general order: - Base system (fixed, low, number) - Lid system (based on PSYS) - Lid battery (estimating how much current the battery actually requires) - Base battery (similar estimation) - Provide everything else to lid When discharging, we generally: - First discharge the base battery - Then discharge the lid battery BRANCH=none BUG=b:71881017 TEST=Flash lux and wand, EC-EC communication works, adapter power is split in a sensible way, and discharging works fine. Change-Id: I8a4f87963962fc5466b2fedf1347eb4dadd35740 Signed-off-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/659460 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/poppy/board.c4
-rw-r--r--common/charge_state_v2.c485
-rw-r--r--include/charge_state_v2.h15
-rw-r--r--include/ec_commands.h2
-rw-r--r--util/ectool.c5
5 files changed, 499 insertions, 12 deletions
diff --git a/board/poppy/board.c b/board/poppy/board.c
index 8103ed5d67..9ed130fc03 100644
--- a/board/poppy/board.c
+++ b/board/poppy/board.c
@@ -632,6 +632,10 @@ int board_set_active_charge_port(int charge_port)
gpio_set_level(GPIO_USB_C0_CHARGE_L, 1);
gpio_set_level(GPIO_USB_C1_CHARGE_L, 1);
} else {
+ /*
+ * TODO(b:67029560): Here, we should make sure that base/lid
+ * power is diconnected.
+ */
/* Make sure non-charging port is disabled */
gpio_set_level(charge_port ? GPIO_USB_C0_CHARGE_L :
GPIO_USB_C1_CHARGE_L, 1);
diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c
index 7774978cd0..3e704b8b6b 100644
--- a/common/charge_state_v2.c
+++ b/common/charge_state_v2.c
@@ -14,6 +14,7 @@
#include "chipset.h"
#include "common.h"
#include "console.h"
+#include "ec_ec_comm_master.h"
#include "ec_ec_comm_slave.h"
#include "extpower.h"
#include "gpio.h"
@@ -30,6 +31,10 @@
/* Console output macros */
#define CPUTS(outstr) cputs(CC_CHARGER, outstr)
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
+
+/* Extra debugging prints when allocating power between lid and base. */
+#undef CHARGE_ALLOCATE_EXTRA_DEBUG
#define CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US \
(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT * SECOND)
@@ -39,6 +44,8 @@
/* Prior to negotiating PD, most PD chargers advertise 15W */
#define LIKELY_PD_USBC_POWER_MW 15000
+static int charge_request(int voltage, int current);
+
/*
* State for charger_task(). Here so we can reset it on a HOOK_INIT, and
* because stack space is more limited than .bss
@@ -54,6 +61,14 @@ static unsigned int user_current_limit = -1U;
test_export_static timestamp_t shutdown_warning_time;
static timestamp_t precharge_start_time;
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+static int base_connected;
+static int prev_base_connected;
+static int charge_base;
+static int prev_charge_base;
+static int prev_current_base;
+#endif
+
/* Is battery connected but unresponsive after precharge? */
static int battery_seems_to_be_dead;
@@ -119,6 +134,386 @@ static void problem(enum problem_type p, int v)
problems_exist = 1;
}
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+/*
+ * Parameters for dual-battery policy.
+ * TODO(b:71881017): This should be made configurable by AP in the future.
+ */
+struct dual_battery_policy {
+ /*** Policies when AC is not connected. ***/
+ /* Voltage to use when using OTG mode between lid and base (mV) */
+ uint16_t otg_voltage;
+ /* Maximum current to apply from base to lid (mA) */
+ uint16_t max_base_to_lid_current;
+ /* When base battery is low, current to provide from lid to base (mA) */
+ uint16_t lid_to_base_current_charge_base_low;
+ /*
+ * Margin to apply between provided OTG output current and input current
+ * limit, to make sure that input charger does not overcurrent output
+ * charger. input_current = (1-margin) * output_current. (/128)
+ */
+ uint8_t margin_otg_current;
+
+ /* Only do base to lid OTG when base battery above this value (%) */
+ uint8_t min_charge_base_otg;
+
+ /*
+ * When base/lid battery percentage is below this value, do
+ * battery-to-battery charging. (%)
+ */
+ uint8_t max_charge_base_batt_to_batt;
+ uint8_t max_charge_lid_batt_to_batt;
+
+ /*** Policies when AC is connected. ***/
+ /* Minimum power to allocate to base (mW) */
+ uint16_t min_base_system_power;
+
+ /* Smoothing factor for lid power (/128) */
+ uint8_t lid_system_power_smooth;
+ /*
+ * Smoothing factor for base/lid battery power, when the battery power
+ * is decreasing only: we try to estimate the maximum power that the
+ * battery is willing to take and always reset it when it draws more
+ * than the estimate. (/128)
+ */
+ uint8_t battery_power_smooth;
+
+ /*
+ * Margin to add to requested base/lid battery power, to figure out how
+ * much current to allocate. allocation = (1+margin) * request. (/128)
+ */
+ uint8_t margin_base_battery_power;
+ uint8_t margin_lid_battery_power;
+
+ /* Maximum current to apply from lid to base (mA) */
+ uint16_t max_lid_to_base_current;
+};
+
+static const struct dual_battery_policy db_policy = {
+ .otg_voltage = 15000, /* mV */
+ .max_base_to_lid_current = 1800, /* mA, about 2000mA with margin. */
+ .lid_to_base_current_charge_base_low = 200, /* mA, so about 3W. */
+ .margin_otg_current = 13, /* /128 = 10.1% */
+ .min_charge_base_otg = 5, /* % */
+ .max_charge_base_batt_to_batt = 4, /* % */
+ .max_charge_lid_batt_to_batt = 10, /* % */
+ .min_base_system_power = 1100, /* mW */
+ .lid_system_power_smooth = 32, /* 32/128 = 0.25 */
+ .battery_power_smooth = 1, /* 1/128 = 0.008 */
+ .margin_base_battery_power = 32, /* 32/128 = 0.25 */
+ .margin_lid_battery_power = 32, /* 32/128 = 0.25 */
+ .max_lid_to_base_current = 2000, /* mA */
+};
+
+/* Add at most "value" to power_var, subtracting from total_power budget. */
+#define CHG_ALLOCATE(power_var, total_power, value) do { \
+ int val_capped = MIN(value, total_power); \
+ (power_var) += val_capped; \
+ (total_power) -= val_capped; \
+} while (0)
+
+static int charge_get_base_percent(void)
+{
+ if (base_battery_dynamic.flags & (BATT_FLAG_BAD_FULL_CAPACITY |
+ BATT_FLAG_BAD_REMAINING_CAPACITY))
+ return -1;
+
+ if (base_battery_dynamic.full_capacity > 0)
+ return 100 * base_battery_dynamic.remaining_capacity
+ / base_battery_dynamic.full_capacity;
+
+ return 0;
+}
+
+/**
+ * Setup current settings for lid and base, in a safe way.
+ *
+ * @param current_base Current to be drawn by base (negative to provide power)
+ * @param allow_charge_base Whether base battery should be charged (only makes
+ * sense with positive current)
+ * @param current_lid Current to be drawn by lid (negative to provide power)
+ * @param allow_charge_lid Whether lid battery should be charged
+ */
+static void set_base_lid_current(int current_base, int allow_charge_base,
+ int current_lid, int allow_charge_lid)
+{
+ /* "OTG" voltage from base to lid or from lid to base. */
+ const int otg_voltage = db_policy.otg_voltage;
+
+ static int prev_allow_charge_base;
+ static int prev_current_lid;
+
+ int lid_first;
+ int ret;
+
+ /* TODO(b:71881017): This is still quite verbose during charging. */
+ if (prev_current_base != current_base ||
+ prev_allow_charge_base != allow_charge_base ||
+ prev_current_lid != current_lid) {
+ CPRINTS("Base/Lid: %d%s/%d%s mA",
+ current_base, allow_charge_base ? "+" : "",
+ current_lid, allow_charge_lid ? "+" : "");
+ }
+
+ /*
+ * To decide whether to first control the lid or the base, we first
+ * control the side that _reduces_ current that would be drawn, then
+ * setup one that would start providing power, then increase current.
+ */
+ if (current_lid >= 0 && current_lid < prev_current_lid)
+ lid_first = 1; /* Lid decreases current */
+ else if (current_base >= 0 && current_base < prev_current_base)
+ lid_first = 0; /* Base decreases current */
+ else if (current_lid < 0)
+ lid_first = 1; /* Lid provide power */
+ else
+ lid_first = 0; /* All other cases: control the base first */
+
+ if (!lid_first && base_connected) {
+ ret = ec_ec_master_base_charge_control(current_base,
+ otg_voltage, allow_charge_base);
+ if (ret)
+ return;
+ }
+
+ if (current_lid >= 0) {
+ ret = charge_set_output_current_limit(0, 0);
+ if (ret)
+ return;
+ ret = charger_set_input_current(current_lid);
+ if (ret)
+ return;
+ if (allow_charge_lid)
+ ret = charge_request(curr.requested_voltage,
+ curr.requested_current);
+ else
+ ret = charge_request(0, 0);
+ } else {
+ ret = charge_set_output_current_limit(
+ -current_lid, otg_voltage);
+ }
+
+ if (ret)
+ return;
+
+ if (lid_first && base_connected) {
+ ret = ec_ec_master_base_charge_control(current_base,
+ otg_voltage, allow_charge_base);
+ if (ret)
+ return;
+ }
+
+ prev_current_base = current_base;
+ prev_allow_charge_base = allow_charge_base;
+ prev_current_lid = current_lid;
+}
+
+/**
+ * Smooth power value, covering some edge cases.
+ * Compute s*curr+(1-s)*prev, where s is in 1/128 unit.
+ */
+static int smooth_value(int prev, int curr, int s)
+{
+ if (curr < 0)
+ curr = 0;
+ if (prev < 0)
+ return curr;
+
+ return prev + s * (curr - prev) / 128;
+}
+
+/**
+ * Add margin m to value. Compute (1+m)*value, where m is in 1/128 unit.
+ */
+static int add_margin(int value, int m)
+{
+ return value + m * value / 128;
+}
+
+static void charge_allocate_input_current_limit(void)
+{
+ /*
+ * All the power numbers are in mW.
+ *
+ * Since we work with current and voltage in mA and mV, multiplying them
+ * gives numbers in uW, which are dangerously close to overflowing when
+ * doing intermediate computations (60W * 100 overflows a 32-bit int,
+ * for example). We therefore divide the product by 1000 and re-multiply
+ * the power numbers by 1000 when converting them back to current.
+ */
+ int total_power = 0;
+
+ static int prev_base_battery_power = -1;
+ int base_battery_power = 0;
+ int base_battery_power_max = 0;
+
+ static int prev_lid_system_power = -1;
+ int lid_system_power;
+
+ static int prev_lid_battery_power = -1;
+ int lid_battery_power = 0;
+ int lid_battery_power_max = 0;
+
+ int power_base = 0;
+ int power_lid = 0;
+
+ int current_base = 0;
+ int current_lid = 0;
+
+ int charge_lid = charge_get_percent();
+
+ if (!base_connected) {
+ set_base_lid_current(0, 0, curr.desired_input_current, 1);
+ prev_base_battery_power = -1;
+ return;
+ }
+
+ /* Charging */
+ if (curr.desired_input_current > 0 && curr.input_voltage > 0)
+ total_power =
+ curr.desired_input_current * curr.input_voltage / 1000;
+
+ /*
+ * TODO(b:71723024): We should be able to replace this test by curr.ac,
+ * but the value is currently wrong, especially during transitions.
+ */
+ if (total_power <= 0) {
+ /* Discharging */
+ prev_base_battery_power = -1;
+ prev_lid_system_power = -1;
+ prev_lid_battery_power = -1;
+
+ if (charge_base > db_policy.min_charge_base_otg) {
+ int lid_current = db_policy.max_base_to_lid_current;
+ int base_current = add_margin(lid_current,
+ db_policy.margin_otg_current);
+ /* Draw current from base to lid */
+ set_base_lid_current(-base_current, 0, lid_current,
+ charge_lid < db_policy.max_charge_lid_batt_to_batt);
+ } else {
+ /*
+ * Base battery is too low, apply power to it, and allow
+ * it to charge if it connected, and it is critically
+ * low.
+ *
+ * TODO(b:71881017): This will make the battery charge
+ * oscillate between 3 and 4 percent, which might not be
+ * great for battery life. We need some hysteresis.
+ */
+ int base_current = db_policy.min_base_system_power;
+ int lid_current = add_margin(base_current,
+ db_policy.margin_otg_current);
+
+ int allow_charge = charge_base >= 0 &&
+ charge_base < db_policy.max_charge_base_batt_to_batt;
+
+ set_base_lid_current(base_current, allow_charge,
+ -lid_current, 0);
+ }
+
+ return;
+ }
+
+ /* Estimate system power. */
+ lid_system_power = charger_get_system_power() / 1000;
+
+ /* Smooth system power, as it is very spiky */
+ lid_system_power = smooth_value(prev_lid_system_power,
+ lid_system_power, db_policy.lid_system_power_smooth);
+ prev_lid_system_power = lid_system_power;
+
+ /*
+ * TODO(b:71881017): Smoothing the battery power isn't necessarily a
+ * good idea: if the system takes up too much power, we may reduce the
+ * estimate power too quickly, leading to oscillations when the system
+ * power goes down. Instead, we should probably estimate the current
+ * based on remaining capacity.
+ */
+ /* Estimate lid battery power. */
+ if (!(curr.batt.flags &
+ (BATT_FLAG_BAD_VOLTAGE | BATT_FLAG_BAD_CURRENT)))
+ lid_battery_power = curr.batt.current *
+ curr.batt.voltage / 1000;
+ if (lid_battery_power < prev_lid_battery_power)
+ lid_battery_power = smooth_value(prev_lid_battery_power,
+ lid_battery_power, db_policy.battery_power_smooth);
+ if (!(curr.batt.flags &
+ (BATT_FLAG_BAD_DESIRED_VOLTAGE |
+ BATT_FLAG_BAD_DESIRED_CURRENT)))
+ lid_battery_power_max = curr.batt.desired_current *
+ curr.batt.desired_voltage / 1000;
+
+ lid_battery_power = MIN(lid_battery_power, lid_battery_power_max);
+
+ /* Estimate base battery power. */
+ if (!(base_battery_dynamic.flags & EC_BATT_FLAG_INVALID_DATA)) {
+ base_battery_power = base_battery_dynamic.actual_current *
+ base_battery_dynamic.actual_voltage / 1000;
+ base_battery_power_max = base_battery_dynamic.desired_current *
+ base_battery_dynamic.desired_voltage / 1000;
+ }
+ if (base_battery_power < prev_base_battery_power)
+ base_battery_power = smooth_value(prev_base_battery_power,
+ base_battery_power, db_policy.battery_power_smooth);
+ base_battery_power = MIN(base_battery_power, base_battery_power_max);
+
+ if (debugging) {
+ CPRINTF("%s:\n", __func__);
+ CPRINTF("total power: %d\n", total_power);
+ CPRINTF("base battery power: %d (%d)\n",
+ base_battery_power, base_battery_power_max);
+ CPRINTF("lid system power: %d\n", lid_system_power);
+ CPRINTF("lid battery power: %d\n", lid_battery_power);
+ CPRINTF("percent base/lid: %d%% %d%%\n",
+ charge_base, charge_lid);
+ }
+
+ prev_lid_battery_power = lid_battery_power;
+ prev_base_battery_power = base_battery_power;
+
+ if (total_power > 0) { /* Charging */
+ /* Allocate system power */
+ CHG_ALLOCATE(power_base, total_power,
+ db_policy.min_base_system_power);
+ CHG_ALLOCATE(power_lid, total_power, lid_system_power);
+
+ /* Allocate lid, then base battery power */
+ lid_battery_power = add_margin(lid_battery_power,
+ db_policy.margin_lid_battery_power);
+ CHG_ALLOCATE(power_lid, total_power, lid_battery_power);
+
+ base_battery_power = add_margin(base_battery_power,
+ db_policy.margin_base_battery_power);
+ CHG_ALLOCATE(power_base, total_power, base_battery_power);
+
+ /* Give everything else to the lid. */
+ CHG_ALLOCATE(power_lid, total_power, total_power);
+ if (debugging)
+ CPRINTF("power: base %d mW / lid %d mW\n",
+ power_base, power_lid);
+
+ current_base = 1000 * power_base / curr.input_voltage;
+ current_lid = 1000 * power_lid / curr.input_voltage;
+
+ if (current_base > db_policy.max_lid_to_base_current) {
+ current_lid += (current_base
+ - db_policy.max_lid_to_base_current);
+ current_base = db_policy.max_lid_to_base_current;
+ }
+
+ if (debugging)
+ CPRINTF("current: base %d mA / lid %d mA\n",
+ current_base, current_lid);
+
+ set_base_lid_current(current_base, 1, current_lid, 1);
+ } else { /* Discharging */
+ }
+
+ if (debugging)
+ CPRINTF("====\n");
+}
+#endif /* CONFIG_EC_EC_COMM_BATTERY_MASTER */
+
#ifdef HAS_TASK_HOSTCMD
/* Returns zero if every item was updated. */
static int update_static_battery_info(void)
@@ -451,6 +846,9 @@ static void dump_charge_state(void)
#ifdef CONFIG_CHARGER_OTG
DUMP(output_current, "%dmA");
#endif
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ DUMP(input_voltage, "%dmV");
+#endif
ccprintf("chg_ctl_mode = %d\n", chg_ctl_mode);
ccprintf("manual_mode = %d\n", manual_mode);
ccprintf("user_current_limit = %dmA\n", user_current_limit);
@@ -510,6 +908,10 @@ static void show_charging_progress(void)
to_full ? "to full" : "to empty",
is_full ? ", not accepting current" : "");
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ CPRINTS("Base battery %d%%", charge_base);
+#endif
+
if (debugging) {
ccprintf("battery:\n");
print_battery_debug();
@@ -744,6 +1146,8 @@ const struct batt_params *charger_current_battery_params(void)
return &curr.batt;
}
+/*****************************************************************************/
+/* Hooks */
void charger_init(void)
{
/* Initialize current state */
@@ -752,6 +1156,15 @@ void charger_init(void)
}
DECLARE_HOOK(HOOK_INIT, charger_init, HOOK_PRIO_DEFAULT);
+/* Wake up the task when something important happens */
+static void charge_wakeup(void)
+{
+ task_wake(TASK_ID_CHARGER);
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, charge_wakeup, HOOK_PRIO_DEFAULT);
+DECLARE_HOOK(HOOK_AC_CHANGE, charge_wakeup, HOOK_PRIO_DEFAULT);
+
+
static int get_desired_input_current(enum battery_present batt_present,
const struct charger_info * const info)
{
@@ -788,6 +1201,10 @@ void charger_task(void *u)
chg_ctl_mode = CHARGE_CONTROL_NORMAL;
shutdown_warning_time.val = 0UL;
battery_seems_to_be_dead = 0;
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ prev_base_connected = -1;
+ curr.input_voltage = CHARGE_VOLTAGE_UNINITIALIZED;
+#endif
/*
* If system is not locked and we don't have a battery to live on,
@@ -806,6 +1223,15 @@ void charger_task(void *u)
problems_exist = 0;
battery_critical = 0;
curr.ac = extpower_is_present();
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ /*
+ * When base is powering the system, make sure curr.ac stays 0.
+ * TODO(b:71723024): Fix extpower_is_present() in hardware
+ * instead.
+ */
+ if (base_connected && prev_current_base < 0)
+ curr.ac = 0;
+#endif
if (curr.ac != prev_ac) {
if (curr.ac) {
/*
@@ -834,6 +1260,36 @@ void charger_task(void *u)
prev_ac = curr.ac;
}
}
+
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ base_connected = board_is_base_connected();
+
+ if (prev_base_connected != base_connected) {
+ /* Invalidate static/dynamic information */
+ base_battery_dynamic.flags = EC_BATT_FLAG_INVALID_DATA;
+ charge_base = -1;
+
+ if (base_connected) {
+ /* Redo power allocation (e.g. enable OTG). */
+ charge_allocate_input_current_limit();
+ /* Apply power to the base */
+ board_enable_base_power(1);
+ }
+
+ prev_base_connected = base_connected;
+ }
+
+ if (base_connected) {
+ /*
+ * TODO(b:71881017): Be smart about static info and do
+ * not fetch it over and over again.
+ */
+ ec_ec_master_base_get_static_info();
+ ec_ec_master_base_get_dynamic_info();
+ charge_base = charge_get_base_percent();
+ }
+#endif
+
charger_get_params(&curr.chg);
battery_get_params(&curr.batt);
@@ -1033,9 +1489,15 @@ wait_for_it:
is_full = calc_is_full();
if ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
curr.batt.state_of_charge != prev_charge) ||
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ (charge_base != prev_charge_base) ||
+#endif
(is_full != prev_full)) {
show_charging_progress();
prev_charge = curr.batt.state_of_charge;
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ prev_charge_base = charge_base;
+#endif
hook_notify(HOOK_BATTERY_SOC_CHANGE);
}
prev_full = is_full;
@@ -1094,7 +1556,12 @@ wait_for_it:
curr.requested_current = 0;
#endif
}
+
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ charge_allocate_input_current_limit();
+#else
charge_request(curr.requested_voltage, curr.requested_current);
+#endif
/* How long to sleep? */
if (problems_exist)
@@ -1364,21 +1831,17 @@ int charge_set_input_current_limit(int ma, int mv)
ma = MIN(ma, CONFIG_CHARGER_MAX_INPUT_CURRENT);
#endif
curr.desired_input_current = ma;
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ curr.input_voltage = mv;
+ /* Wake up charger task to allocate current between lid and base. */
+ charge_wakeup();
+ return EC_SUCCESS;
+#else
return charger_set_input_current(ma);
+#endif
}
/*****************************************************************************/
-/* Hooks */
-
-/* Wake up the task when something important happens */
-static void charge_wakeup(void)
-{
- task_wake(TASK_ID_CHARGER);
-}
-DECLARE_HOOK(HOOK_CHIPSET_RESUME, charge_wakeup, HOOK_PRIO_DEFAULT);
-DECLARE_HOOK(HOOK_AC_CHANGE, charge_wakeup, HOOK_PRIO_DEFAULT);
-
-/*****************************************************************************/
/* Host commands */
static int charge_command_charge_control(struct host_cmd_handler_args *args)
diff --git a/include/charge_state_v2.h b/include/charge_state_v2.h
index f9f5d39edf..3fc9a94181 100644
--- a/include/charge_state_v2.h
+++ b/include/charge_state_v2.h
@@ -6,6 +6,7 @@
#include "battery.h"
#include "battery_smart.h"
#include "charger.h"
+#include "ec_ec_comm_master.h"
#include "timer.h"
#ifndef __CROS_EC_CHARGE_STATE_V2_H
@@ -41,6 +42,9 @@ struct charge_state_data {
#ifdef CONFIG_CHARGER_OTG
int output_current;
#endif
+#ifdef CONFIG_EC_EC_COMM_BATTERY_MASTER
+ int input_voltage;
+#endif
};
/**
@@ -74,5 +78,16 @@ int charge_set_input_current_limit(int ma, int mv);
int charge_get_charge_state_debug(int param, uint32_t *value);
#endif /* CONFIG_CHARGE_STATE_DEBUG */
+/**
+ * Board-specific routine to indicate if the base is connected.
+ */
+int board_is_base_connected(void);
+
+/**
+ * Board-specific routine to enable power distribution between lid and base
+ * (current can flow both ways).
+ */
+void board_enable_base_power(int enable);
+
#endif /* __CROS_EC_CHARGE_STATE_V2_H */
diff --git a/include/ec_commands.h b/include/ec_commands.h
index 10ac439215..00c6dad089 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -163,7 +163,7 @@
#define EC_BATT_FLAG_DISCHARGING 0x04
#define EC_BATT_FLAG_CHARGING 0x08
#define EC_BATT_FLAG_LEVEL_CRITICAL 0x10
-/* Set if some of the dynamic data is invalid (or outdated). */
+/* Set if some of the static/dynamic data is invalid (or outdated). */
#define EC_BATT_FLAG_INVALID_DATA 0x20
/* Switch flags at EC_MEMMAP_SWITCHES */
diff --git a/util/ectool.c b/util/ectool.c
index c0aa05e06c..155947d4a2 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -6076,6 +6076,11 @@ int get_battery_command(int index)
if (rv < 0)
return -1;
+ if (dynamic_r.flags & EC_BATT_FLAG_INVALID_DATA) {
+ printf(" Invalid data (not present?)\n");
+ return -1;
+ }
+
if (!is_string_printable(static_r.manufacturer))
goto cmd_error;
printf(" OEM name: %s\n", static_r.manufacturer);