summaryrefslogtreecommitdiff
path: root/common/charge_state_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/charge_state_v2.c')
-rw-r--r--common/charge_state_v2.c3108
1 files changed, 0 insertions, 3108 deletions
diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c
deleted file mode 100644
index 110d63c7bf..0000000000
--- a/common/charge_state_v2.c
+++ /dev/null
@@ -1,3108 +0,0 @@
-/* Copyright 2014 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 charging task and state machine.
- */
-
-#include "battery.h"
-#include "battery_smart.h"
-#include "charge_manager.h"
-#include "charger_profile_override.h"
-#include "charge_state.h"
-#include "charger.h"
-#include "chipset.h"
-#include "common.h"
-#include "console.h"
-#include "ec_commands.h"
-#include "ec_ec_comm_client.h"
-#include "ec_ec_comm_server.h"
-#include "extpower.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "i2c.h"
-#include "math_util.h"
-#include "power.h"
-#include "printf.h"
-#include "system.h"
-#include "task.h"
-#include "throttle_ap.h"
-#include "timer.h"
-#include "usb_common.h"
-#include "usb_pd.h"
-#include "util.h"
-
-/* 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)
-#define PRECHARGE_TIMEOUT_US (PRECHARGE_TIMEOUT * SECOND)
-#define LFCC_EVENT_THRESH 5 /* Full-capacity change reqd for host event */
-
-#ifdef CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT
-#ifndef CONFIG_HOSTCMD_EVENTS
-#error "CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT needs CONFIG_HOSTCMD_EVENTS"
-#endif /* CONFIG_HOSTCMD_EVENTS */
-#define BAT_OCP_TIMEOUT_US (60 * SECOND)
-/* BAT_OCP_HYSTERESIS_PCT can be optionally overridden in board.h. */
-#ifndef BAT_OCP_HYSTERESIS_PCT
-#define BAT_OCP_HYSTERESIS_PCT 10
-#endif /* BAT_OCP_HYSTERESIS_PCT */
-#define BAT_OCP_HYSTERESIS \
- (BAT_MAX_DISCHG_CURRENT * BAT_OCP_HYSTERESIS_PCT / 100) /* mA */
-#endif /* CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT */
-
-#ifdef CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE
-#ifndef CONFIG_HOSTCMD_EVENTS
-#error "CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE needs CONFIG_HOSTCMD_EVENTS"
-#endif /* CONFIG_HOSTCMD_EVENTS */
-#define BAT_UVP_TIMEOUT_US (60 * SECOND)
-/* BAT_UVP_HYSTERESIS_PCT can be optionally overridden in board.h. */
-#ifndef BAT_UVP_HYSTERESIS_PCT
-#define BAT_UVP_HYSTERESIS_PCT 3
-#endif /* BAT_UVP_HYSTERESIS_PCT */
-#define BAT_UVP_HYSTERESIS \
- (BAT_LOW_VOLTAGE_THRESH * BAT_UVP_HYSTERESIS_PCT / 100) /* mV */
-static timestamp_t uvp_throttle_start_time;
-#endif /* CONFIG_THROTTLE_AP_ON_BAT_OLTAGE */
-
-static int charge_request(int voltage, int current);
-
-static uint8_t battery_level_shutdown;
-
-/*
- * State for charger_task(). Here so we can reset it on a HOOK_INIT, and
- * because stack space is more limited than .bss
- */
-static const struct battery_info *batt_info;
-static struct charge_state_data curr;
-static enum charge_state_v2 prev_state;
-static int prev_ac, prev_charge, prev_full, prev_disp_charge;
-static enum battery_present prev_bp;
-static int is_full; /* battery not accepting current */
-static enum ec_charge_control_mode chg_ctl_mode;
-static int manual_voltage; /* Manual voltage override (-1 = no override) */
-static int manual_current; /* Manual current override (-1 = no override) */
-static unsigned int user_current_limit = -1U;
-test_export_static timestamp_t shutdown_target_time;
-static timestamp_t precharge_start_time;
-static struct sustain_soc sustain_soc;
-
-/*
- * The timestamp when the battery charging current becomes stable.
- * When a new charging status happens, charger needs several seconds to
- * stabilize the battery charging current.
- * stable_current should be evaluated when stable_ts expired.
- * stable_ts should be reset if the charger input voltage/current changes,
- * or a new battery charging voltage/request happened.
- * By evaluating stable_current, we can evaluate the battery's desired charging
- * power desired_mw. This allow us to have a better charging efficiency by
- * negotiating the most fit PDO, i.e. the PDO provides the power just enough for
- * the system and battery, or the PDO with preferred voltage.
- */
-STATIC_IF(CONFIG_USB_PD_PREFER_MV) timestamp_t stable_ts;
-/* battery charging current evaluated after stable_ts expired */
-STATIC_IF(CONFIG_USB_PD_PREFER_MV) int stable_current;
-/* battery desired power in mW. This is used to negotiate the suitable PDO */
-STATIC_IF(CONFIG_USB_PD_PREFER_MV) int desired_mw;
-STATIC_IF_NOT(CONFIG_USB_PD_PREFER_MV) struct pd_pref_config_t pd_pref_config;
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
-static int base_connected;
-/* Base has responded to one of our commands already. */
-static int base_responsive;
-static int charge_base;
-static int prev_charge_base;
-static int prev_current_base;
-static int prev_allow_charge_base;
-static int prev_current_lid;
-
-/*
- * In debugging mode, with AC, input current to allocate to base. Negative
- * value disables manual mode.
- */
-static int manual_ac_current_base = -1;
-/*
- * In debugging mode, when discharging, current to transfer from lid to base
- * (negative to transfer from base to lid). Only valid when enabled is true.
- */
-static int manual_noac_enabled;
-static int manual_noac_current_base;
-#else
-static const int base_connected;
-#endif
-
-/* Is battery connected but unresponsive after precharge? */
-static int battery_seems_dead;
-
-static int battery_seems_disconnected;
-
-/*
- * Was battery removed? Set when we see BP_NO, cleared after the battery is
- * reattached and becomes responsive. Used to indicate an error state after
- * removal and trigger re-reading the battery static info when battery is
- * reattached and responsive.
- */
-static int battery_was_removed;
-
-static int problems_exist;
-static int debugging;
-
-
-/* Track problems in communicating with the battery or charger */
-enum problem_type {
- PR_STATIC_UPDATE,
- PR_SET_VOLTAGE,
- PR_SET_CURRENT,
- PR_SET_MODE,
- PR_SET_INPUT_CURR,
- PR_POST_INIT,
- PR_CHG_FLAGS,
- PR_BATT_FLAGS,
- PR_CUSTOM,
- PR_CFG_SEC_CHG,
-
- NUM_PROBLEM_TYPES
-};
-static const char * const prob_text[] = {
- "static update",
- "set voltage",
- "set current",
- "set mode",
- "set input current",
- "post init",
- "chg params",
- "batt params",
- "custom profile",
- "cfg secondary chg"
-};
-BUILD_ASSERT(ARRAY_SIZE(prob_text) == NUM_PROBLEM_TYPES);
-
-/*
- * TODO(crosbug.com/p/27639): When do we decide a problem is real and not
- * just intermittent? And what do we do about it?
- */
-static void problem(enum problem_type p, int v)
-{
- static int __bss_slow last_prob_val[NUM_PROBLEM_TYPES];
- static timestamp_t __bss_slow last_prob_time[NUM_PROBLEM_TYPES];
- timestamp_t t_now, t_diff;
-
- if (last_prob_val[p] != v) {
- t_now = get_time();
- t_diff.val = t_now.val - last_prob_time[p].val;
- CPRINTS("charge problem: %s, 0x%x -> 0x%x after %.6" PRId64 "s",
- prob_text[p], last_prob_val[p], v, t_diff.val);
- last_prob_val[p] = v;
- last_prob_time[p] = t_now;
- }
- problems_exist = 1;
-}
-
-test_export_static enum ec_charge_control_mode get_chg_ctrl_mode(void)
-{
- return chg_ctl_mode;
-}
-
-static int battery_sustainer_set(int8_t lower, int8_t upper)
-{
- if (lower == -1 || upper == -1) {
- CPRINTS("Sustain mode disabled");
- sustain_soc.lower = -1;
- sustain_soc.upper = -1;
- return EC_SUCCESS;
- }
-
- if (lower <= upper && 0 <= lower && upper <= 100) {
- /* Currently sustainer requires discharge_on_ac. */
- if (!IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC))
- return EC_RES_UNAVAILABLE;
- sustain_soc.lower = lower;
- sustain_soc.upper = upper;
- return EC_SUCCESS;
- }
-
- CPRINTS("Invalid param: %s(%d, %d)", __func__, lower, upper);
- return EC_ERROR_INVAL;
-}
-
-static void battery_sustainer_disable(void)
-{
- battery_sustainer_set(-1, -1);
-}
-
-static bool battery_sustainer_enabled(void)
-{
- return sustain_soc.lower != -1 && sustain_soc.upper != -1;
-}
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
-/*
- * 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;
- /*
- * 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), includes some margin to allow
- * base to charge when critically low.
- */
- 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 = 12000, /* mV */
- .max_base_to_lid_current = 1800, /* mA, about 2000mA with margin. */
- .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 = 1300, /* 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)
-
-/* Update base battery information */
-static void update_base_battery_info(void)
-{
- struct ec_response_battery_dynamic_info *const bd =
- &battery_dynamic[BATT_IDX_BASE];
-
- base_connected = board_is_base_connected();
-
- if (!base_connected) {
- const int invalid_flags = EC_BATT_FLAG_INVALID_DATA;
- /* Invalidate static/dynamic information */
- if (bd->flags != invalid_flags) {
- bd->flags = invalid_flags;
-
- host_set_single_event(EC_HOST_EVENT_BATTERY);
- host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS);
- }
- charge_base = -1;
- base_responsive = 0;
- prev_current_base = 0;
- prev_allow_charge_base = 0;
- } else if (base_responsive) {
- int old_flags = bd->flags;
- int flags_changed;
- int old_full_capacity = bd->full_capacity;
-
- ec_ec_client_base_get_dynamic_info();
- flags_changed = (old_flags != bd->flags);
- /* Fetch static information when flags change. */
- if (flags_changed)
- ec_ec_client_base_get_static_info();
-
- battery_memmap_refresh(BATT_IDX_BASE);
-
- /* Newly connected battery, or change in capacity. */
- if (old_flags & EC_BATT_FLAG_INVALID_DATA ||
- ((old_flags & EC_BATT_FLAG_BATT_PRESENT) !=
- (bd->flags & EC_BATT_FLAG_BATT_PRESENT)) ||
- old_full_capacity != bd->full_capacity)
- host_set_single_event(EC_HOST_EVENT_BATTERY);
-
- if (flags_changed)
- host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS);
-
- /* Update charge_base */
- if (bd->flags & (BATT_FLAG_BAD_FULL_CAPACITY |
- BATT_FLAG_BAD_REMAINING_CAPACITY))
- charge_base = -1;
- else if (bd->full_capacity > 0)
- charge_base = 100 * bd->remaining_capacity
- / bd->full_capacity;
- else
- charge_base = 0;
- }
-}
-
-/**
- * Setup current settings for base, and record previous values, if the base
- * is responsive.
- *
- * @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)
- */
-static int set_base_current(int current_base, int allow_charge_base)
-{
- /* "OTG" voltage from base to lid. */
- const int otg_voltage = db_policy.otg_voltage;
- int ret;
-
- ret = ec_ec_client_base_charge_control(current_base,
- otg_voltage, allow_charge_base);
- if (ret) {
- /* Ignore errors until the base is responsive. */
- if (base_responsive)
- return ret;
- } else {
- base_responsive = 1;
- prev_current_base = current_base;
- prev_allow_charge_base = allow_charge_base;
- }
-
- return EC_RES_SUCCESS;
-}
-
-/**
- * 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 lid to base. */
- const int otg_voltage = db_policy.otg_voltage;
-
- int lid_first;
- int ret;
- int chgnum = 0;
-
- /* 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 = set_base_current(current_base, allow_charge_base);
- if (ret)
- return;
- }
-
- if (current_lid >= 0) {
- ret = charge_set_output_current_limit(CHARGER_SOLO, 0, 0);
- if (ret)
- return;
- ret = charger_set_input_current_limit(chgnum, 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(CHARGER_SOLO,
- -current_lid, otg_voltage);
- }
-
- if (ret)
- return;
-
- prev_current_lid = current_lid;
-
- if (lid_first && base_connected) {
- ret = set_base_current(current_base, allow_charge_base);
- if (ret)
- return;
- }
-
- /*
- * Make sure cross-power is enabled (it might not be enabled right after
- * plugging the base, or when an adapter just got connected).
- */
- if (base_connected && current_base != 0)
- board_enable_base_power(1);
-}
-
-/**
- * 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();
-
- const struct ec_response_battery_dynamic_info *const base_bd =
- &battery_dynamic[BATT_IDX_BASE];
-
-
- 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) {
- int base_critical = charge_base >= 0 &&
- charge_base < db_policy.max_charge_base_batt_to_batt;
-
- /* Discharging */
- prev_base_battery_power = -1;
- prev_lid_system_power = -1;
- prev_lid_battery_power = -1;
-
- /* Manual control */
- if (manual_noac_enabled) {
- int lid_current, base_current;
-
- if (manual_noac_current_base > 0) {
- base_current = -manual_noac_current_base;
- lid_current =
- add_margin(manual_noac_current_base,
- db_policy.margin_otg_current);
- } else {
- lid_current = manual_noac_current_base;
- base_current =
- add_margin(-manual_noac_current_base,
- db_policy.margin_otg_current);
- }
-
- set_base_lid_current(base_current, 0, lid_current, 0);
- return;
- }
-
- /*
- * System is off, cut power to the base. We'll reset the base
- * when system restarts, or when AC is plugged.
- */
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
- set_base_lid_current(0, 0, 0, 0);
- if (base_responsive) {
- /* Base still responsive, put it to sleep. */
- CPRINTF("Hibernating base\n");
- ec_ec_client_hibernate();
- base_responsive = 0;
- board_enable_base_power(0);
- }
- return;
- }
-
- /*
- * System is suspended, let the lid and base run on their
- * own power. However, if the base battery is critically low, we
- * still want to provide power to the base, to make sure it
- * stays alive to be able to wake the system on keyboard or
- * touchpad events.
- */
- if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND) &&
- !base_critical) {
- set_base_lid_current(0, 0, 0, 0);
- return;
- }
-
- 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 is critically low.
- *
- * TODO(b:71881017): When suspended, this will make the
- * battery charge oscillate between 3 and 4 percent,
- * which might not be great for battery life. We need
- * some hysteresis.
- */
- /*
- * TODO(b:71881017): Precompute (ideally, at build time)
- * the base_current, so we do not need to do a division
- * here.
- */
- int base_current =
- (db_policy.min_base_system_power * 1000) /
- db_policy.otg_voltage;
- int lid_current = add_margin(base_current,
- db_policy.margin_otg_current);
-
- set_base_lid_current(base_current, base_critical,
- -lid_current, 0);
- }
-
- return;
- }
-
- /* Manual control */
- if (manual_ac_current_base >= 0) {
- int current_base = manual_ac_current_base;
- int current_lid =
- curr.desired_input_current - manual_ac_current_base;
-
- if (current_lid < 0) {
- current_base = curr.desired_input_current;
- current_lid = 0;
- }
-
- set_base_lid_current(current_base, 1, current_lid, 1);
- 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_bd->flags & EC_BATT_FLAG_INVALID_DATA)) {
- base_battery_power = base_bd->actual_current *
- base_bd->actual_voltage / 1000;
- base_battery_power_max = base_bd->desired_current *
- base_bd->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_CLIENT */
-
-#ifndef CONFIG_BATTERY_V2
-/* Returns zero if every item was updated. */
-static int update_static_battery_info(void)
-{
- char *batt_str;
- int batt_serial;
- uint8_t batt_flags = 0;
- /*
- * The return values have type enum ec_error_list, but EC_SUCCESS is
- * zero. We'll just look for any failures so we can try them all again.
- */
- int rv;
-
- /* Smart battery serial number is 16 bits */
- batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL);
- memset(batt_str, 0, EC_MEMMAP_TEXT_MAX);
- rv = battery_serial_number(&batt_serial);
- if (!rv)
- snprintf(batt_str, EC_MEMMAP_TEXT_MAX, "%04X", batt_serial);
-
- /* Design Capacity of Full */
- rv |= battery_design_capacity(
- (int *)host_get_memmap(EC_MEMMAP_BATT_DCAP));
-
- /* Design Voltage */
- rv |= battery_design_voltage(
- (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT));
-
- /* Last Full Charge Capacity (this is only mostly static) */
- rv |= battery_full_charge_capacity(
- (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC));
-
- /* Cycle Count */
- rv |= battery_cycle_count((int *)host_get_memmap(EC_MEMMAP_BATT_CCNT));
-
- /* Battery Manufacturer string */
- batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR);
- memset(batt_str, 0, EC_MEMMAP_TEXT_MAX);
- rv |= battery_manufacturer_name(batt_str, EC_MEMMAP_TEXT_MAX);
-
- /* Battery Model string */
- batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL);
- memset(batt_str, 0, EC_MEMMAP_TEXT_MAX);
- rv |= battery_device_name(batt_str, EC_MEMMAP_TEXT_MAX);
-
- /* Battery Type string */
- batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE);
- rv |= battery_device_chemistry(batt_str, EC_MEMMAP_TEXT_MAX);
-
- /* Zero the dynamic entries. They'll come next. */
- *(int *)host_get_memmap(EC_MEMMAP_BATT_VOLT) = 0;
- *(int *)host_get_memmap(EC_MEMMAP_BATT_RATE) = 0;
- *(int *)host_get_memmap(EC_MEMMAP_BATT_CAP) = 0;
- *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = 0;
- if (extpower_is_present())
- batt_flags |= EC_BATT_FLAG_AC_PRESENT;
- *host_get_memmap(EC_MEMMAP_BATT_FLAG) = batt_flags;
-
- if (rv)
- problem(PR_STATIC_UPDATE, rv);
- else
- /* No errors seen. Battery data is now present */
- *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 1;
-
- return rv;
-}
-
-static void update_dynamic_battery_info(void)
-{
- /* The memmap address is constant. We should fix these calls somehow. */
- 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);
- uint8_t tmp;
- int send_batt_status_event = 0;
- int send_batt_info_event = 0;
- static int __bss_slow batt_present;
-
- tmp = 0;
- if (curr.ac)
- tmp |= EC_BATT_FLAG_AC_PRESENT;
-
- if (curr.batt.is_present == BP_YES) {
- tmp |= EC_BATT_FLAG_BATT_PRESENT;
- batt_present = 1;
- /* Tell the AP to read battery info if it is newly present. */
- if (!(*memmap_flags & EC_BATT_FLAG_BATT_PRESENT))
- send_batt_info_event++;
- } else {
- /*
- * Require two consecutive updates with BP_NOT_SURE
- * before reporting it gone to the host.
- */
- if (batt_present)
- tmp |= EC_BATT_FLAG_BATT_PRESENT;
- else if (*memmap_flags & EC_BATT_FLAG_BATT_PRESENT)
- send_batt_info_event++;
- batt_present = 0;
- }
-
- if (curr.batt.flags & EC_BATT_FLAG_INVALID_DATA)
- tmp |= EC_BATT_FLAG_INVALID_DATA;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE))
- *memmap_volt = curr.batt.voltage;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT))
- *memmap_rate = ABS(curr.batt.current);
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) {
- /*
- * If we're running off the battery, it must have some charge.
- * Don't report zero charge, as that has special meaning
- * to Chrome OS powerd.
- */
- if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging)
- *memmap_cap = 1;
- else
- *memmap_cap = curr.batt.remaining_capacity;
- }
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) &&
- (curr.batt.full_capacity <= (*memmap_lfcc - LFCC_EVENT_THRESH) ||
- curr.batt.full_capacity >= (*memmap_lfcc + LFCC_EVENT_THRESH))) {
- *memmap_lfcc = curr.batt.full_capacity;
- /* Poke the AP if the full_capacity changes. */
- send_batt_info_event++;
- }
-
- if (curr.batt.is_present == BP_YES &&
- !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
- curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL)
- tmp |= EC_BATT_FLAG_LEVEL_CRITICAL;
-
- tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING :
- EC_BATT_FLAG_DISCHARGING;
-
- /* Tell the AP to re-read battery status if charge state changes */
- if (*memmap_flags != tmp)
- send_batt_status_event++;
-
- /* Update flags before sending host events. */
- *memmap_flags = tmp;
-
- if (send_batt_info_event)
- host_set_single_event(EC_HOST_EVENT_BATTERY);
- if (send_batt_status_event)
- host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS);
-}
-#else /* CONFIG_BATTERY_V2 */
-
-static int is_battery_string_reliable(const char *buf)
-{
- /*
- * From is_string_printable rule, 0xFF is not printable.
- * So, EC should think battery string is unreliable if string
- * include 0xFF.
- */
- while (*buf) {
- if ((*buf) == 0xFF)
- return 0;
- buf++;
- }
-
- return 1;
-}
-
-static int update_static_battery_info(void)
-{
- int batt_serial;
- int val;
- /*
- * The return values have type enum ec_error_list, but EC_SUCCESS is
- * zero. We'll just look for any failures so we can try them all again.
- */
- int rv, ret;
-
- struct ec_response_battery_static_info_v1 *const bs =
- &battery_static[BATT_IDX_MAIN];
-
- /* Clear all static information. */
- memset(bs, 0, sizeof(*bs));
-
- /* Smart battery serial number is 16 bits */
- rv = battery_serial_number(&batt_serial);
- if (!rv)
- snprintf(bs->serial_ext, sizeof(bs->serial_ext),
- "%04X", batt_serial);
-
- /* Design Capacity of Full */
- ret = battery_design_capacity(&val);
- if (!ret)
- bs->design_capacity = val;
- rv |= ret;
-
- /* Design Voltage */
- ret = battery_design_voltage(&val);
- if (!ret)
- bs->design_voltage = val;
- rv |= ret;
-
- /* Cycle Count */
- ret = battery_cycle_count(&val);
- if (!ret)
- bs->cycle_count = val;
- rv |= ret;
-
- /* Battery Manufacturer string */
- rv |= battery_manufacturer_name(bs->manufacturer_ext,
- sizeof(bs->manufacturer_ext));
-
- /* Battery Model string */
- rv |= battery_device_name(bs->model_ext, sizeof(bs->model_ext));
-
- /* Battery Type string */
- rv |= battery_device_chemistry(bs->type_ext, sizeof(bs->type_ext));
-
- /*
- * b/181639264: Battery gauge follow SMBus SPEC and SMBus define
- * cumulative clock low extend time for both controller (master) and
- * peripheral (slave). However, I2C doesn't.
- * Regarding this issue, we observe EC sometimes pull I2C CLK low
- * a while after EC start running. Actually, we are not sure the
- * reason until now.
- * If EC pull I2C CLK low too long, and it may cause battery fw timeout
- * because battery count cumulative clock extend time over 25ms.
- * When it happened, battery will release both its CLK and DATA and
- * reset itself. So, EC may get 0xFF when EC keep reading data from
- * battery. Battery static information will be unreliable and need to
- * be updated.
- * This change is improvement that EC should retry if battery string is
- * unreliable.
- */
- if (!is_battery_string_reliable(bs->serial_ext) ||
- !is_battery_string_reliable(bs->manufacturer_ext) ||
- !is_battery_string_reliable(bs->model_ext) ||
- !is_battery_string_reliable(bs->type_ext))
- rv |= EC_ERROR_UNKNOWN;
-
- /* Zero the dynamic entries. They'll come next. */
- memset(&battery_dynamic[BATT_IDX_MAIN], 0,
- sizeof(battery_dynamic[BATT_IDX_MAIN]));
-
- if (rv)
- problem(PR_STATIC_UPDATE, rv);
-
-#ifdef HAS_TASK_HOSTCMD
- battery_memmap_refresh(BATT_IDX_MAIN);
-#endif
-
- return rv;
-}
-
-static void update_dynamic_battery_info(void)
-{
- static int __bss_slow batt_present;
- uint8_t tmp;
- int send_batt_status_event = 0;
- int send_batt_info_event = 0;
-
- struct ec_response_battery_dynamic_info *const bd =
- &battery_dynamic[BATT_IDX_MAIN];
-
- tmp = 0;
- if (curr.ac)
- tmp |= EC_BATT_FLAG_AC_PRESENT;
-
- if (curr.batt.is_present == BP_YES) {
- tmp |= EC_BATT_FLAG_BATT_PRESENT;
- batt_present = 1;
- /* Tell the AP to read battery info if it is newly present. */
- if (!(bd->flags & EC_BATT_FLAG_BATT_PRESENT))
- send_batt_info_event++;
- } else {
- /*
- * Require two consecutive updates with BP_NOT_SURE
- * before reporting it gone to the host.
- */
- if (batt_present)
- tmp |= EC_BATT_FLAG_BATT_PRESENT;
- else if (bd->flags & EC_BATT_FLAG_BATT_PRESENT)
- send_batt_info_event++;
- batt_present = 0;
- }
-
- if (curr.batt.flags & EC_BATT_FLAG_INVALID_DATA)
- tmp |= EC_BATT_FLAG_INVALID_DATA;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE))
- bd->actual_voltage = curr.batt.voltage;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT))
- bd->actual_current = curr.batt.current;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_DESIRED_VOLTAGE))
- bd->desired_voltage = curr.batt.desired_voltage;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_DESIRED_CURRENT))
- bd->desired_current = curr.batt.desired_current;
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) {
- /*
- * If we're running off the battery, it must have some charge.
- * Don't report zero charge, as that has special meaning
- * to Chrome OS powerd.
- */
- if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging)
- bd->remaining_capacity = 1;
- else
- bd->remaining_capacity = curr.batt.remaining_capacity;
- }
-
- if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) &&
- (curr.batt.full_capacity <=
- (bd->full_capacity - LFCC_EVENT_THRESH) ||
- curr.batt.full_capacity >=
- (bd->full_capacity + LFCC_EVENT_THRESH))) {
- bd->full_capacity = curr.batt.full_capacity;
- /* Poke the AP if the full_capacity changes. */
- send_batt_info_event++;
- }
-
- if (curr.batt.is_present == BP_YES &&
- !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
- curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL)
- tmp |= EC_BATT_FLAG_LEVEL_CRITICAL;
-
- tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING :
- EC_BATT_FLAG_DISCHARGING;
-
- /* Tell the AP to re-read battery status if charge state changes */
- if (bd->flags != tmp)
- send_batt_status_event++;
-
- bd->flags = tmp;
-
-#ifdef HAS_TASK_HOSTCMD
- battery_memmap_refresh(BATT_IDX_MAIN);
-#endif
-
-#ifdef CONFIG_HOSTCMD_EVENTS
- if (send_batt_info_event)
- host_set_single_event(EC_HOST_EVENT_BATTERY);
- if (send_batt_status_event)
- host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS);
-#endif
-}
-#endif /* CONFIG_BATTERY_V2 */
-
-static const char * const state_list[] = {
- "idle", "discharge", "charge", "precharge"
-};
-BUILD_ASSERT(ARRAY_SIZE(state_list) == NUM_STATES_V2);
-static const char * const batt_pres[] = {
- "NO", "YES", "NOT_SURE",
-};
-
-const char *mode_text[] = EC_CHARGE_MODE_TEXT;
-BUILD_ASSERT(ARRAY_SIZE(mode_text) == CHARGE_CONTROL_COUNT);
-
-static void dump_charge_state(void)
-{
-#define DUMP(FLD, FMT) ccprintf(#FLD " = " FMT "\n", curr.FLD)
-#define DUMP_CHG(FLD, FMT) ccprintf("\t" #FLD " = " FMT "\n", curr.chg. FLD)
-#define DUMP_BATT(FLD, FMT) ccprintf("\t" #FLD " = " FMT "\n", curr.batt. FLD)
-#define DUMP_OCPC(FLD, FMT) ccprintf("\t" #FLD " = " FMT "\n", curr.ocpc. FLD)
-
- enum ec_charge_control_mode cmode = get_chg_ctrl_mode();
-
- ccprintf("state = %s\n", state_list[curr.state]);
- DUMP(ac, "%d");
- DUMP(batt_is_charging, "%d");
- ccprintf("chg.*:\n");
- DUMP_CHG(voltage, "%dmV");
- DUMP_CHG(current, "%dmA");
- DUMP_CHG(input_current, "%dmA");
- DUMP_CHG(status, "0x%x");
- DUMP_CHG(option, "0x%x");
- DUMP_CHG(flags, "0x%x");
- cflush();
- ccprintf("batt.*:\n");
- ccprintf("\ttemperature = %dC\n",
- DECI_KELVIN_TO_CELSIUS(curr.batt.temperature));
- DUMP_BATT(state_of_charge, "%d%%");
- DUMP_BATT(voltage, "%dmV");
- DUMP_BATT(current, "%dmA");
- DUMP_BATT(desired_voltage, "%dmV");
- DUMP_BATT(desired_current, "%dmA");
- DUMP_BATT(flags, "0x%x");
- DUMP_BATT(remaining_capacity, "%dmAh");
- DUMP_BATT(full_capacity, "%dmAh");
- ccprintf("\tis_present = %s\n", batt_pres[curr.batt.is_present]);
- cflush();
-#ifdef CONFIG_OCPC
- ccprintf("ocpc.*:\n");
- DUMP_OCPC(active_chg_chip, "%d");
- DUMP_OCPC(combined_rsys_rbatt_mo, "%dmOhm");
- if ((curr.ocpc.active_chg_chip != -1) &&
- !(curr.ocpc.chg_flags[curr.ocpc.active_chg_chip] &
- OCPC_NO_ISYS_MEAS_CAP)) {
- DUMP_OCPC(rbatt_mo, "%dmOhm");
- DUMP_OCPC(rsys_mo, "%dmOhm");
- DUMP_OCPC(isys_ma, "%dmA");
- }
- DUMP_OCPC(vsys_aux_mv, "%dmV");
- DUMP_OCPC(vsys_mv, "%dmV");
- DUMP_OCPC(primary_vbus_mv, "%dmV");
- DUMP_OCPC(primary_ibus_ma, "%dmA");
- DUMP_OCPC(secondary_vbus_mv, "%dmV");
- DUMP_OCPC(secondary_ibus_ma, "%dmA");
- DUMP_OCPC(last_error, "%d");
- DUMP_OCPC(integral, "%d");
- DUMP_OCPC(last_vsys, "%dmV");
- cflush();
-#endif /* CONFIG_OCPC */
- DUMP(requested_voltage, "%dmV");
- DUMP(requested_current, "%dmA");
-#ifdef CONFIG_CHARGER_OTG
- DUMP(output_current, "%dmA");
-#endif
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- DUMP(input_voltage, "%dmV");
-#endif
- ccprintf("chg_ctl_mode = %s (%d)\n",
- cmode < CHARGE_CONTROL_COUNT ? mode_text[cmode] : "UNDEF",
- cmode);
- ccprintf("manual_voltage = %d\n", manual_voltage);
- ccprintf("manual_current = %d\n", manual_current);
- ccprintf("user_current_limit = %dmA\n", user_current_limit);
- ccprintf("battery_seems_dead = %d\n", battery_seems_dead);
- ccprintf("battery_seems_disconnected = %d\n",
- battery_seems_disconnected);
- ccprintf("battery_was_removed = %d\n", battery_was_removed);
- ccprintf("debug output = %s\n", debugging ? "on" : "off");
- ccprintf("Battery sustainer = %s (%d%% ~ %d%%)\n",
- battery_sustainer_enabled() ? "on" : "off",
- sustain_soc.lower, sustain_soc.upper);
-#undef DUMP
-}
-
-static void show_charging_progress(void)
-{
- int rv = 0, minutes, to_full, chgnum = 0;
- int dsoc;
-
-#ifdef CONFIG_BATTERY_SMART
- /*
- * Predicted remaining battery capacity based on AverageCurrent().
- * 65535 = Battery is not being discharged.
- */
- if (!battery_time_to_empty(&minutes) && minutes != 65535)
- to_full = 0;
- /*
- * Predicted time-to-full charge based on AverageCurrent().
- * 65535 = Battery is not being discharged.
- */
- else if (!battery_time_to_full(&minutes) && minutes != 65535)
- to_full = 1;
- /*
- * If both time to empty and time to full have invalid data, consider
- * measured current from the coulomb counter and ac present status to
- * decide whether battery is about to full or empty.
- */
- else {
- to_full = curr.batt_is_charging;
- rv = EC_ERROR_UNKNOWN;
- }
-#else
- if (!curr.batt_is_charging) {
- rv = battery_time_to_empty(&minutes);
- to_full = 0;
- } else {
- rv = battery_time_to_full(&minutes);
- to_full = 1;
- }
-#endif
-
- dsoc = charge_get_display_charge();
- if (rv)
- CPRINTS("Battery %d%% (Display %d.%d %%) / ??h:?? %s%s",
- curr.batt.state_of_charge,
- dsoc / 10, dsoc % 10,
- to_full ? "to full" : "to empty",
- is_full ? ", not accepting current" : "");
- else
- CPRINTS("Battery %d%% (Display %d.%d %%) / %dh:%d %s%s",
- curr.batt.state_of_charge,
- dsoc / 10, dsoc % 10, minutes / 60, minutes % 60,
- to_full ? "to full" : "to empty",
- is_full ? ", not accepting current" : "");
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- CPRINTS("Base battery %d%%", charge_base);
-#endif
-
- if (debugging) {
- ccprintf("battery:\n");
- print_battery_debug();
- ccprintf("charger:\n");
- if (IS_ENABLED(CONFIG_OCPC))
- chgnum = charge_get_active_chg_chip();
- print_charger_debug(chgnum);
- ccprintf("chg:\n");
- dump_charge_state();
- }
-}
-
-/* Calculate if battery is full based on whether it is accepting charge */
-test_mockable int calc_is_full(void)
-{
- static int __bss_slow ret;
-
- /* If bad state of charge reading, return last value */
- if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE ||
- curr.batt.state_of_charge > 100)
- return ret;
- /*
- * Battery is full when SoC is above 90% and battery desired current
- * is 0. This is necessary because some batteries stop charging when
- * the SoC still reports <100%, so we need to check desired current
- * to know if it is actually full.
- */
- ret = (curr.batt.state_of_charge >= 90 &&
- curr.batt.desired_current == 0);
- return ret;
-}
-
-/*
- * Ask the charger for some voltage and current. If either value is 0,
- * charging is disabled; otherwise it's enabled. Negative values are ignored.
- */
-static int charge_request(int voltage, int current)
-{
- int r1 = EC_SUCCESS, r2 = EC_SUCCESS, r3 = EC_SUCCESS, r4 = EC_SUCCESS;
- static int __bss_slow prev_volt, prev_curr;
-
- if (!voltage || !current) {
-#ifdef CONFIG_CHARGER_NARROW_VDC
- current = 0;
- /*
- * With NVDC charger, keep VSYS voltage higher than battery,
- * otherwise the BGATE FET body diode would conduct and
- * discharge the battery.
- */
- voltage = charger_closest_voltage(
- curr.batt.voltage + charger_get_info()->voltage_step);
- /* If the battery is full, request the max voltage. */
- if (is_full)
- voltage = battery_get_info()->voltage_max;
- /* And handle dead battery case */
- voltage = MAX(voltage, battery_get_info()->voltage_normal);
-#else
- voltage = current = 0;
-#endif
- }
-
- if (curr.ac) {
- if (prev_volt != voltage || prev_curr != current)
- CPRINTS("%s(%dmV, %dmA)", __func__, voltage, current);
- }
-
- /*
- * Set current before voltage so that if we are just starting
- * to charge, we allow some time (i2c delay) for charging circuit to
- * start at a voltage just above battery voltage before jumping
- * up. This helps avoid large current spikes when connecting
- * battery.
- */
- if (current >= 0) {
-#ifdef CONFIG_OCPC
- /*
- * For OCPC systems, don't unconditionally modify the primary
- * charger IC's charge current. It may be handled by the
- * charger drivers directly.
- */
- if (curr.ocpc.active_chg_chip == CHARGER_PRIMARY)
-#endif
- r2 = charger_set_current(0, current);
- }
- if (r2 != EC_SUCCESS)
- problem(PR_SET_CURRENT, r2);
-
- if (voltage >= 0)
- r1 = charger_set_voltage(0, voltage);
- if (r1 != EC_SUCCESS)
- problem(PR_SET_VOLTAGE, r1);
-
-#ifdef CONFIG_OCPC
- /*
- * For OCPC systems, if the secondary charger is active, we need to
- * configure that charge IC as well. Note that if OCPC ever supports
- * more than 2 charger ICs, we'll need to refactor things a bit. The
- * following check should be comparing against CHARGER_PRIMARY and
- * config_secondary_charger should probably be config_auxiliary_charger
- * and take the active chgnum as a parameter.
- */
- if (curr.ocpc.active_chg_chip == CHARGER_SECONDARY) {
- if ((current >= 0) || (voltage >= 0))
- r3 = ocpc_config_secondary_charger(&curr.desired_input_current,
- &curr.ocpc,
- voltage, current);
- if (r3 != EC_SUCCESS)
- problem(PR_CFG_SEC_CHG, r3);
- }
-#endif /* CONFIG_OCPC */
-
- /*
- * Set the charge inhibit bit when possible as it appears to save
- * power in some cases (e.g. Nyan with BQ24735).
- */
- if (voltage > 0 || current > 0)
- r4 = charger_set_mode(0);
- else
- r4 = charger_set_mode(CHARGE_FLAG_INHIBIT_CHARGE);
- if (r4 != EC_SUCCESS)
- problem(PR_SET_MODE, r4);
-
- /*
- * Only update if the request worked, so we'll keep trying on failures.
- */
- if (r1 || r2)
- return r1 ? r1 : r2;
- if (IS_ENABLED(CONFIG_OCPC) && r3)
- return r3;
-
- if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV) &&
- (prev_volt != voltage || prev_curr != current))
- charge_reset_stable_current();
-
- prev_volt = voltage;
- prev_curr = current;
-
- return EC_SUCCESS;
-}
-
-void chgstate_set_manual_current(int curr_ma)
-{
- if (curr_ma < 0)
- manual_current = -1;
- else
- manual_current = charger_closest_current(curr_ma);
-}
-
-void chgstate_set_manual_voltage(int volt_mv)
-{
- manual_voltage = charger_closest_voltage(volt_mv);
-}
-
-/* Force charging off before the battery is full. */
-static int set_chg_ctrl_mode(enum ec_charge_control_mode mode)
-{
- bool discharge_on_ac = false;
- int current, voltage;
- int rv;
-
- current = manual_current;
- voltage = manual_voltage;
-
- if (mode >= CHARGE_CONTROL_COUNT)
- return EC_ERROR_INVAL;
-
- if (mode == CHARGE_CONTROL_NORMAL) {
- current = -1;
- voltage = -1;
- } else {
- /* Changing mode is only meaningful if AC is present. */
- if (!curr.ac)
- return EC_ERROR_NOT_POWERED;
-
- if (mode == CHARGE_CONTROL_DISCHARGE) {
- if (!IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC))
- return EC_ERROR_UNIMPLEMENTED;
- discharge_on_ac = true;
- } else if (mode == CHARGE_CONTROL_IDLE) {
- current = 0;
- voltage = 0;
- }
- }
-
- if (IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC)) {
- rv = charger_discharge_on_ac(discharge_on_ac);
- if (rv != EC_SUCCESS)
- return rv;
- }
-
- /* Commit all atomically */
- chg_ctl_mode = mode;
- manual_current = current;
- manual_voltage = voltage;
-
- return EC_SUCCESS;
-}
-
-static inline int battery_too_hot(int batt_temp_c)
-{
- return (!(curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE) &&
- (batt_temp_c > batt_info->discharging_max_c));
-}
-
-static inline int battery_too_cold_for_discharge(int batt_temp_c)
-{
- return (!(curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE) &&
- (batt_temp_c < batt_info->discharging_min_c));
-}
-
-__attribute__((weak)) uint8_t board_set_battery_level_shutdown(void)
-{
- return BATTERY_LEVEL_SHUTDOWN;
-}
-
-/* True if we know the charge is too low, or we know the voltage is too low. */
-static inline int battery_too_low(void)
-{
- return ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
- curr.batt.state_of_charge < battery_level_shutdown) ||
- (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE) &&
- curr.batt.voltage <= batt_info->voltage_min));
-}
-
-__attribute__((weak))
-enum critical_shutdown board_critical_shutdown_check(
- struct charge_state_data *curr)
-{
-#ifdef CONFIG_BATTERY_CRITICAL_SHUTDOWN_CUT_OFF
- return CRITICAL_SHUTDOWN_CUTOFF;
-#elif defined(CONFIG_HIBERNATE)
- return CRITICAL_SHUTDOWN_HIBERNATE;
-#else
- return CRITICAL_SHUTDOWN_IGNORE;
-#endif
-}
-
-static int is_battery_critical(void)
-{
- int batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature);
-
- /*
- * TODO(crosbug.com/p/27642): The thermal loop should watch the battery
- * temp, so it can turn fans on.
- */
- if (battery_too_hot(batt_temp_c)) {
- CPRINTS("Batt too hot: %dC", batt_temp_c);
- return 1;
- }
-
- /* Note: the battery may run on AC without discharging when too cold */
- if (!curr.ac && battery_too_cold_for_discharge(batt_temp_c)) {
- CPRINTS("Batt too cold: %dC", batt_temp_c);
- return 1;
- }
-
- if (battery_too_low() && !curr.batt_is_charging) {
- CPRINTS("Low battery: %d%%, %dmV",
- curr.batt.state_of_charge, curr.batt.voltage);
- return 1;
- }
-
- return 0;
-}
-
- /*
- * If the battery is at extremely low charge (and discharging) or extremely
- * high temperature, the EC will notify the AP and start a timer. If the
- * critical condition is not corrected before the timeout expires, the EC
- * will shut down the AP (if the AP is not already off) and then optionally
- * hibernate or cut off battery.
- */
-static int shutdown_on_critical_battery(void)
-{
- if (!is_battery_critical()) {
- /* Reset shutdown warning time */
- shutdown_target_time.val = 0;
- return 0;
- }
-
- if (!shutdown_target_time.val) {
- /* Start count down timer */
- CPRINTS("Start shutdown due to critical battery");
- shutdown_target_time.val = get_time().val
- + CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US;
-#ifdef CONFIG_HOSTCMD_EVENTS
- if (!chipset_in_state(CHIPSET_STATE_ANY_OFF))
- host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
-#endif
- return 1;
- }
-
- if (!timestamp_expired(shutdown_target_time, 0))
- return 1;
-
- /* Timer has expired */
- if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) {
- switch (board_critical_shutdown_check(&curr)) {
- case CRITICAL_SHUTDOWN_HIBERNATE:
- if (IS_ENABLED(CONFIG_HIBERNATE)) {
- if (power_get_state() == POWER_S3S5)
- sleep(1);
- CPRINTS("Hibernate due to critical battery");
- cflush();
- system_hibernate(0, 0);
- }
- break;
- case CRITICAL_SHUTDOWN_CUTOFF:
- if (power_get_state() == POWER_S3S5)
- sleep(1);
- CPRINTS("Cutoff due to critical battery");
- cflush();
- board_cut_off_battery();
- break;
- case CRITICAL_SHUTDOWN_IGNORE:
- default:
- break;
- }
- } else {
- /* Timeout waiting for AP to shut down, so kill it */
- CPRINTS(
- "charge force shutdown due to critical battery");
- chipset_force_shutdown(CHIPSET_SHUTDOWN_BATTERY_CRIT);
- }
-
- return 1;
-}
-
-/*
- * Send host events as the battery charge drops below certain thresholds.
- * We handle forced shutdown and other actions elsewhere; this is just for the
- * host events. We send these even if the AP is off, since the AP will read and
- * discard any events it doesn't care about the next time it wakes up.
- */
-static void notify_host_of_low_battery_charge(void)
-{
- /* We can't tell what the current charge is. Assume it's okay. */
- if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE)
- return;
-
-#ifdef CONFIG_HOSTCMD_EVENTS
- if (curr.batt.state_of_charge <= BATTERY_LEVEL_LOW &&
- prev_charge > BATTERY_LEVEL_LOW)
- host_set_single_event(EC_HOST_EVENT_BATTERY_LOW);
-
- if (curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL &&
- prev_charge > BATTERY_LEVEL_CRITICAL)
- host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL);
-#endif
-}
-
-static void set_charge_state(enum charge_state_v2 state)
-{
- prev_state = curr.state;
- curr.state = state;
-}
-
-static void notify_host_of_low_battery_voltage(void)
-{
-#ifdef CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE
- if ((curr.batt.flags & BATT_FLAG_BAD_VOLTAGE) ||
- chipset_in_state(CHIPSET_STATE_ANY_OFF))
- return;
-
- if (!uvp_throttle_start_time.val &&
- (curr.batt.voltage < BAT_LOW_VOLTAGE_THRESH)) {
- throttle_ap(THROTTLE_ON, THROTTLE_SOFT,
- THROTTLE_SRC_BAT_VOLTAGE);
- uvp_throttle_start_time = get_time();
- } else if (uvp_throttle_start_time.val &&
- (curr.batt.voltage < BAT_LOW_VOLTAGE_THRESH +
- BAT_UVP_HYSTERESIS)) {
- /*
- * Reset the timer when we are not sure if VBAT can stay
- * above BAT_LOW_VOLTAGE_THRESH after we stop throttling.
- */
- uvp_throttle_start_time = get_time();
- } else if (uvp_throttle_start_time.val &&
- (get_time().val > uvp_throttle_start_time.val +
- BAT_UVP_TIMEOUT_US)) {
- throttle_ap(THROTTLE_OFF, THROTTLE_SOFT,
- THROTTLE_SRC_BAT_VOLTAGE);
- uvp_throttle_start_time.val = 0;
- }
-#endif
-}
-
-static void notify_host_of_over_current(struct batt_params *batt)
-{
-#ifdef CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT
- static timestamp_t ocp_throttle_start_time;
-
- if (batt->flags & BATT_FLAG_BAD_CURRENT)
- return;
-
- if ((!ocp_throttle_start_time.val &&
- (batt->current < -BAT_MAX_DISCHG_CURRENT)) ||
- (ocp_throttle_start_time.val &&
- (batt->current < -BAT_MAX_DISCHG_CURRENT + BAT_OCP_HYSTERESIS))) {
- ocp_throttle_start_time = get_time();
- throttle_ap(THROTTLE_ON, THROTTLE_SOFT,
- THROTTLE_SRC_BAT_DISCHG_CURRENT);
- } else if (ocp_throttle_start_time.val &&
- (get_time().val > ocp_throttle_start_time.val +
- BAT_OCP_TIMEOUT_US)) {
- /*
- * Clear the timer and notify AP to stop throttling if
- * we haven't seen over current for BAT_OCP_TIMEOUT_US.
- */
- ocp_throttle_start_time.val = 0;
- throttle_ap(THROTTLE_OFF, THROTTLE_SOFT,
- THROTTLE_SRC_BAT_DISCHG_CURRENT);
- }
-#endif
-}
-
-const struct batt_params *charger_current_battery_params(void)
-{
- return &curr.batt;
-}
-
-/* Determine if the battery is outside of allowable temperature range */
-static int battery_outside_charging_temperature(void)
-{
- const struct battery_info *batt_info = battery_get_info();
- int batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature);
- int max_c, min_c;
-
- if (curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE)
- return 0;
-
- if((curr.batt.desired_voltage == 0) &&
- (curr.batt.desired_current == 0)){
- max_c = batt_info->start_charging_max_c;
- min_c = batt_info->start_charging_min_c;
- } else {
- max_c = batt_info->charging_max_c;
- min_c = batt_info->charging_min_c;
- }
-
-
- if ((batt_temp_c >= max_c) ||
- (batt_temp_c <= min_c)) {
- return 1;
- }
- return 0;
-}
-
-static void sustain_battery_soc(void)
-{
- enum ec_charge_control_mode mode = get_chg_ctrl_mode();
- int soc;
- int rv;
-
- /* If either AC or battery is not present, nothing to do. */
- if (!curr.ac || curr.batt.is_present != BP_YES
- || !battery_sustainer_enabled())
- return;
-
- soc = charge_get_display_charge() / 10;
-
- /*
- * When lower < upper, the sustainer discharges using DISCHARGE. When
- * lower == upper, the sustainer discharges using IDLE. The following
- * switch statement handle both cases but in reality either DISCHARGE
- * or IDLE is used but not both.
- */
- switch (mode) {
- case CHARGE_CONTROL_NORMAL:
- /* Going up */
- if (sustain_soc.upper < soc)
- mode = sustain_soc.upper == sustain_soc.lower ?
- CHARGE_CONTROL_IDLE : CHARGE_CONTROL_DISCHARGE;
- break;
- case CHARGE_CONTROL_IDLE:
- /* Discharging naturally */
- if (soc < sustain_soc.lower)
- mode = CHARGE_CONTROL_NORMAL;
- break;
- case CHARGE_CONTROL_DISCHARGE:
- /* Discharging actively. */
- if (soc < sustain_soc.lower)
- mode = CHARGE_CONTROL_NORMAL;
- break;
- default:
- return;
- }
-
- if (mode == get_chg_ctrl_mode())
- return;
-
- rv = set_chg_ctrl_mode(mode);
- CPRINTS("%s: %s control mode to %s",
- __func__, rv == EC_SUCCESS ? "Switched" : "Failed to switch",
- mode_text[mode]);
-}
-
-/*****************************************************************************/
-/* Hooks */
-void charger_init(void)
-{
- /* Initialize current state */
- memset(&curr, 0, sizeof(curr));
- curr.batt.is_present = BP_NOT_SURE;
- /* Manual voltage/current set to off */
- manual_voltage = -1;
- manual_current = -1;
- /*
- * Other tasks read the params like state_of_charge at the beginning of
- * their tasks. Make them ready first.
- */
- battery_get_params(&curr.batt);
-
- battery_sustainer_disable();
-}
-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);
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
-/* Reset the base on S5->S0 transition. */
-DECLARE_HOOK(HOOK_CHIPSET_STARTUP, board_base_reset, HOOK_PRIO_DEFAULT);
-#endif
-
-#ifdef CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE
-static void bat_low_voltage_throttle_reset(void)
-{
- uvp_throttle_start_time.val = 0;
-}
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN,
- bat_low_voltage_throttle_reset,
- HOOK_PRIO_DEFAULT);
-#endif
-
-static int get_desired_input_current(enum battery_present batt_present,
- const struct charger_info * const info)
-{
- if (batt_present == BP_YES || system_is_locked() || base_connected) {
-#ifdef CONFIG_CHARGE_MANAGER
- int ilim = charge_manager_get_charger_current();
- return ilim == CHARGE_CURRENT_UNINITIALIZED ?
- CHARGE_CURRENT_UNINITIALIZED :
- MAX(CONFIG_CHARGER_INPUT_CURRENT, ilim);
-#else
- return CONFIG_CHARGER_INPUT_CURRENT;
-#endif
- } else {
-#ifdef CONFIG_USB_POWER_DELIVERY
- return MIN(PD_MAX_CURRENT_MA, info->input_current_max);
-#else
- return info->input_current_max;
-#endif
- }
-}
-
-static void wakeup_battery(int *need_static)
-{
- if (battery_seems_dead || battery_is_cut_off()) {
- /* It's dead, do nothing */
- set_charge_state(ST_IDLE);
- curr.requested_voltage = 0;
- curr.requested_current = 0;
- } else if (curr.state == ST_PRECHARGE
- && (get_time().val > precharge_start_time.val +
- PRECHARGE_TIMEOUT_US)) {
- /* We've tried long enough, give up */
- CPRINTS("battery seems to be dead");
- battery_seems_dead = 1;
- set_charge_state(ST_IDLE);
- curr.requested_voltage = 0;
- curr.requested_current = 0;
- } else {
- /* See if we can wake it up */
- if (curr.state != ST_PRECHARGE) {
- CPRINTS("try to wake battery");
- precharge_start_time = get_time();
- *need_static = 1;
- }
- set_charge_state(ST_PRECHARGE);
- curr.requested_voltage = batt_info->voltage_max;
- curr.requested_current = batt_info->precharge_current;
- }
-}
-
-static void revive_battery(int *need_static)
-{
- if (IS_ENABLED(CONFIG_BATTERY_REQUESTS_NIL_WHEN_DEAD)
- && curr.requested_voltage == 0
- && curr.requested_current == 0
- && curr.batt.state_of_charge == 0) {
- /*
- * Battery is dead, give precharge current
- * TODO (crosbug.com/p/29467): remove this workaround
- * for dead battery that requests no voltage/current
- */
- curr.requested_voltage = batt_info->voltage_max;
- curr.requested_current = batt_info->precharge_current;
- } else if (IS_ENABLED(CONFIG_BATTERY_REVIVE_DISCONNECT)
- && curr.requested_voltage == 0
- && curr.requested_current == 0
- && battery_seems_disconnected) {
- /*
- * Battery is in disconnect state. Apply a
- * current to kick it out of this state.
- */
- CPRINTS("found battery in disconnect state");
- curr.requested_voltage = batt_info->voltage_max;
- curr.requested_current = batt_info->precharge_current;
- } else if (curr.state == ST_PRECHARGE
- || battery_seems_dead || battery_was_removed) {
- CPRINTS("battery woke up");
- /* Update the battery-specific values */
- batt_info = battery_get_info();
- *need_static = 1;
- }
-
- battery_seems_dead = battery_was_removed = 0;
-}
-
-/* Main loop */
-void charger_task(void *u)
-{
- int sleep_usec;
- int battery_critical;
- int need_static = 1;
- const struct charger_info * const info = charger_get_info();
- int prev_plt_and_desired_mw;
- int chgnum = 0;
-
- /* Get the battery-specific values */
- batt_info = battery_get_info();
-
- prev_ac = prev_charge = prev_disp_charge = -1;
- chg_ctl_mode = CHARGE_CONTROL_NORMAL;
- shutdown_target_time.val = 0UL;
- battery_seems_dead = 0;
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- base_responsive = 0;
- curr.input_voltage = CHARGE_VOLTAGE_UNINITIALIZED;
- battery_dynamic[BATT_IDX_BASE].flags = EC_BATT_FLAG_INVALID_DATA;
- charge_base = -1;
-#endif
-#ifdef CONFIG_OCPC
- ocpc_init(&curr.ocpc);
- charge_set_active_chg_chip(CHARGE_PORT_NONE);
-#endif /* CONFIG_OCPC */
-
- /*
- * If system is not locked and we don't have a battery to live on,
- * then use max input current limit so that we can pull as much power
- * as needed.
- */
- prev_bp = BP_NOT_INIT;
- curr.desired_input_current = get_desired_input_current(
- curr.batt.is_present, info);
-
- if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) {
- /* init battery desired power */
- desired_mw =
- curr.batt.desired_current * curr.batt.desired_voltage;
- /*
- * Battery charging current needs time to be stable when a
- * new charge happens. Start the timer so we can evaluate the
- * stable current when timeout.
- */
- charge_reset_stable_current();
- }
-
- battery_level_shutdown = board_set_battery_level_shutdown();
-
- while (1) {
-
- /* Let's see what's going on... */
- curr.ts = get_time();
- sleep_usec = 0;
- problems_exist = 0;
- battery_critical = 0;
- curr.ac = extpower_is_present();
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- /*
- * When base is powering the system, make sure curr.ac stays 0.
- * TODO(b:71723024): Fix extpower_is_present() in hardware
- * instead.
- */
- if (base_responsive && prev_current_base < 0)
- curr.ac = 0;
-
- /* System is off: if AC gets connected, reset the base. */
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF) &&
- !prev_ac && curr.ac)
- board_base_reset();
-#endif
- if (curr.ac != prev_ac) {
- /*
- * We've noticed a change in AC presence, let the board
- * know.
- */
- board_check_extpower();
- if (curr.ac) {
- /*
- * Some chargers are unpowered when the AC is
- * off, so we'll reinitialize it when AC
- * comes back and set the input current limit.
- * Try again if it fails.
- */
- int rv = charger_post_init();
-
- if (rv != EC_SUCCESS) {
- problem(PR_POST_INIT, rv);
- } else if (curr.desired_input_current !=
- CHARGE_CURRENT_UNINITIALIZED) {
- rv = charger_set_input_current_limit(
- chgnum,
- curr.desired_input_current);
- if (rv != EC_SUCCESS)
- problem(PR_SET_INPUT_CURR, rv);
- }
-
- if (rv == EC_SUCCESS)
- prev_ac = curr.ac;
- } else {
- /* Some things are only meaningful on AC */
- set_chg_ctrl_mode(CHARGE_CONTROL_NORMAL);
- battery_seems_dead = 0;
- prev_ac = curr.ac;
-
- /*
- * b/187967523, we should clear charge current,
- * otherwise it will effect typeC output.this
- * should be ok for all chargers.
- */
- charger_set_current(chgnum, 0);
- }
- }
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- update_base_battery_info();
-#endif
-
- charger_get_params(&curr.chg);
- battery_get_params(&curr.batt);
-#ifdef CONFIG_OCPC
- if (curr.ac)
- ocpc_get_adcs(&curr.ocpc);
-#endif /* CONFIG_OCPC */
-
- if (prev_bp != curr.batt.is_present) {
- prev_bp = curr.batt.is_present;
-
- /* Update battery info due to change of battery */
- batt_info = battery_get_info();
- need_static = 1;
-
- curr.desired_input_current =
- get_desired_input_current(prev_bp, info);
- if (curr.desired_input_current !=
- CHARGE_CURRENT_UNINITIALIZED)
- charger_set_input_current_limit(chgnum,
- curr.desired_input_current);
- hook_notify(HOOK_BATTERY_SOC_CHANGE);
- }
-
- battery_validate_params(&curr.batt);
-
- notify_host_of_over_current(&curr.batt);
-
- /* battery current stable now, saves the current. */
- if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV) &&
- get_time().val > stable_ts.val && curr.batt.current >= 0)
- stable_current = curr.batt.current;
-
- /*
- * Now decide what we want to do about it. We'll normally just
- * pass along whatever the battery wants to the charger. Note
- * that if battery_get_params() can't get valid values from the
- * battery it uses (0, 0), which is probably safer than blindly
- * applying power to a battery we can't talk to.
- */
- if (curr.batt.flags & (BATT_FLAG_BAD_DESIRED_VOLTAGE |
- BATT_FLAG_BAD_DESIRED_CURRENT)) {
- curr.requested_voltage = 0;
- curr.requested_current = 0;
- } else {
- curr.requested_voltage = curr.batt.desired_voltage;
- curr.requested_current = curr.batt.desired_current;
- }
-
- /* If we *know* there's no battery, wait for one to appear. */
- if (curr.batt.is_present == BP_NO) {
- if (!curr.ac)
- CPRINTS("running with no battery and no AC");
- set_charge_state(ST_IDLE);
- curr.batt_is_charging = 0;
- battery_was_removed = 1;
- goto wait_for_it;
- }
-
- /*
- * If we had trouble talking to the battery or the charger, we
- * should probably do nothing for a bit, and if it doesn't get
- * better then flag it as an error.
- */
- if (curr.chg.flags & CHG_FLAG_BAD_ANY)
- problem(PR_CHG_FLAGS, curr.chg.flags);
- if (curr.batt.flags & BATT_FLAG_BAD_ANY)
- problem(PR_BATT_FLAGS, curr.batt.flags);
-
- /*
- * If AC is present, check if input current is sufficient to
- * actually charge battery.
- */
- curr.batt_is_charging = curr.ac && (curr.batt.current >= 0);
-
- /* Don't let the battery hurt itself. */
- battery_critical = shutdown_on_critical_battery();
-
- if (!curr.ac) {
- set_charge_state(ST_DISCHARGE);
- goto wait_for_it;
- }
-
- /* Okay, we're on AC and we should have a battery. */
-
- /* Used for factory tests. */
- if (get_chg_ctrl_mode() != CHARGE_CONTROL_NORMAL) {
- set_charge_state(ST_IDLE);
- goto wait_for_it;
- }
-
- /* If the battery is not responsive, try to wake it up. */
- if (!(curr.batt.flags & BATT_FLAG_RESPONSIVE)) {
- wakeup_battery(&need_static);
- goto wait_for_it;
- }
-
- /* The battery is responding. Yay. Try to use it. */
-
- /*
- * Always check the disconnect state. This is because
- * the battery disconnect state is one of the items used
- * to decide whether or not to leave safe mode.
- */
- battery_seems_disconnected =
- battery_get_disconnect_state() == BATTERY_DISCONNECTED;
-
- revive_battery(&need_static);
-
- set_charge_state(ST_CHARGE);
-
-wait_for_it:
- if (IS_ENABLED(CONFIG_CHARGER_PROFILE_OVERRIDE)
- && get_chg_ctrl_mode() == CHARGE_CONTROL_NORMAL) {
- sleep_usec = charger_profile_override(&curr);
- if (sleep_usec < 0)
- problem(PR_CUSTOM, sleep_usec);
- }
-
- if (IS_ENABLED(CONFIG_BATTERY_CHECK_CHARGE_TEMP_LIMITS)
- && battery_outside_charging_temperature()) {
- curr.requested_current = 0;
- curr.requested_voltage = 0;
- curr.batt.flags &= ~BATT_FLAG_WANT_CHARGE;
- if (curr.state != ST_DISCHARGE)
- curr.state = ST_IDLE;
- }
-
-#ifdef CONFIG_CHARGE_MANAGER
- if (curr.batt.state_of_charge >=
- CONFIG_CHARGE_MANAGER_BAT_PCT_SAFE_MODE_EXIT &&
- !battery_seems_disconnected) {
- /*
- * Sometimes the fuel gauge will report that it has
- * sufficient state of charge and remaining capacity,
- * but in actuality it doesn't. When the EC sees that
- * information, it trusts it and leaves charge manager
- * safe mode. Doing so will allow CHARGE_PORT_NONE to
- * be selected, thereby cutting off the input FETs.
- * When the battery cannot provide the charge it claims,
- * the system loses power, shuts down, and the battery
- * is not charged even though the charger is plugged in.
- * By waiting 500ms, we can avoid the selection of
- * CHARGE_PORT_NONE around init time and not cut off the
- * input FETs.
- */
- msleep(500);
- charge_manager_leave_safe_mode();
- }
-#endif
-
- /* Keep the AP informed */
- if (need_static)
- need_static = update_static_battery_info();
- /* Wait on the dynamic info until the static info is good. */
- if (!need_static)
- update_dynamic_battery_info();
- notify_host_of_low_battery_charge();
- notify_host_of_low_battery_voltage();
-
- /* And the EC console */
- is_full = calc_is_full();
-
- /* Run battery sustainer (no-op if not applicable). */
- sustain_battery_soc();
-
- if ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
- curr.batt.state_of_charge != prev_charge) ||
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- (charge_base != prev_charge_base) ||
-#endif
- (is_full != prev_full) ||
- (curr.state != prev_state) ||
- (charge_get_display_charge() != prev_disp_charge)) {
- show_charging_progress();
- prev_charge = curr.batt.state_of_charge;
- prev_disp_charge = charge_get_display_charge();
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- prev_charge_base = charge_base;
-#endif
- hook_notify(HOOK_BATTERY_SOC_CHANGE);
- }
- prev_full = is_full;
-
-#ifndef CONFIG_CHARGER_MAINTAIN_VBAT
- /* Turn charger off if it's not needed */
- if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) {
- curr.requested_voltage = 0;
- curr.requested_current = 0;
- }
-#endif
-
- /* Apply external limits */
- if (curr.requested_current > user_current_limit)
- curr.requested_current = user_current_limit;
-
- /* Round to valid values */
- curr.requested_voltage =
- charger_closest_voltage(curr.requested_voltage);
- curr.requested_current =
- charger_closest_current(curr.requested_current);
-
- /* Charger only accpets request when AC is on. */
- if (curr.ac) {
- /*
- * Some batteries would wake up after cut-off if we keep
- * charging it. Thus, we only charge when AC is on and
- * battery is not cut off yet.
- */
- if (battery_is_cut_off()) {
- curr.requested_voltage = 0;
- curr.requested_current = 0;
- }
- /*
- * As a safety feature, some chargers will stop
- * charging if we don't communicate with it frequently
- * enough. In manual mode, we'll just tell it what it
- * knows.
- */
- else {
- if (manual_voltage != -1)
- curr.requested_voltage = manual_voltage;
- if (manual_current != -1)
- curr.requested_current = manual_current;
- }
- } else {
-#ifndef CONFIG_CHARGER_MAINTAIN_VBAT
- curr.requested_voltage = charger_closest_voltage(
- curr.batt.voltage + info->voltage_step);
- curr.requested_current = -1;
-#endif
-#ifdef CONFIG_EC_EC_COMM_BATTERY_SERVER
- /*
- * On EC-EC server, do not charge if curr.ac is 0: there
- * might still be some external power available but we
- * do not want to use it for charging.
- */
- curr.requested_current = 0;
-#endif
- }
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- charge_allocate_input_current_limit();
-#else
- charge_request(curr.requested_voltage, curr.requested_current);
-#endif
-
- /* How long to sleep? */
- if (problems_exist)
- /* If there are errors, don't wait very long. */
- sleep_usec = CHARGE_POLL_PERIOD_SHORT;
- else if (sleep_usec <= 0) {
- /* default values depend on the state */
- if (!curr.ac &&
- (curr.state == ST_IDLE ||
- curr.state == ST_DISCHARGE)) {
-#ifdef CONFIG_CHARGER_OTG
- int output_current = curr.output_current;
-#else
- int output_current = 0;
-#endif
- /*
- * If AP is off and we do not provide power, we
- * can sleep a long time.
- */
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF |
- CHIPSET_STATE_ANY_SUSPEND)
- && output_current == 0)
- sleep_usec =
- CHARGE_POLL_PERIOD_VERY_LONG;
- else
- /* Discharging, not too urgent */
- sleep_usec = CHARGE_POLL_PERIOD_LONG;
- } else {
- /* AC present, so pay closer attention */
- sleep_usec = CHARGE_POLL_PERIOD_CHARGE;
- }
- }
-
- if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) {
- int is_pd_supply = charge_manager_get_supplier() ==
- CHARGE_SUPPLIER_PD;
- int port = charge_manager_get_active_charge_port();
- int bat_spec_desired_mw = curr.batt.desired_current *
- curr.batt.desired_voltage /
- 1000;
-
- /*
- * save the previous plt_and_desired_mw, since it
- * will be updated below
- */
- prev_plt_and_desired_mw =
- charge_get_plt_plus_bat_desired_mw();
-
- /*
- * Update desired power by the following rules:
- * 1. If the battery is not charging with PD, we reset
- * the desired_mw to the battery spec. The actual
- * desired_mw will be evaluated when it starts charging
- * with PD again.
- * 2. If the battery SoC under battery's constant
- * voltage percent (this is a rough value that can be
- * applied to most batteries), the battery can fully
- * sink the power, the desired power should be the
- * same as the battery spec, and we don't need to use
- * evaluated value stable_current.
- * 3. If the battery SoC is above battery's constant
- * voltage percent, the real battery desired charging
- * power will decrease slowly and so does the charging
- * current. We can evaluate the battery desired power
- * by the product of stable_current and battery voltage.
- */
- if (!is_pd_supply)
- desired_mw = bat_spec_desired_mw;
- else if (curr.batt.state_of_charge < pd_pref_config.cv)
- desired_mw = bat_spec_desired_mw;
- else if (stable_current != CHARGE_CURRENT_UNINITIALIZED)
- desired_mw = curr.batt.voltage *
- stable_current / 1000;
-
- /* if the plt_and_desired_mw changes, re-evaluate PDO */
- if (is_pd_supply &&
- prev_plt_and_desired_mw !=
- charge_get_plt_plus_bat_desired_mw())
- pd_set_new_power_request(port);
- }
-
- /* Adjust for time spent in this loop */
- sleep_usec -= (int)(get_time().val - curr.ts.val);
- if (sleep_usec < CHARGE_MIN_SLEEP_USEC)
- sleep_usec = CHARGE_MIN_SLEEP_USEC;
- else if (sleep_usec > CHARGE_MAX_SLEEP_USEC)
- sleep_usec = CHARGE_MAX_SLEEP_USEC;
-
- /*
- * If battery is critical, ensure that the sleep time is not
- * very long since we might want to hibernate or cut-off
- * battery sooner.
- */
- if (battery_critical &&
- (sleep_usec > CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US))
- sleep_usec = CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US;
-
- task_wait_event(sleep_usec);
- }
-}
-
-
-/*****************************************************************************/
-/* Exported functions */
-
-int charge_want_shutdown(void)
-{
- return (curr.state == ST_DISCHARGE) &&
- !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
- (curr.batt.state_of_charge < battery_level_shutdown);
-}
-
-int charge_prevent_power_on(int power_button_pressed)
-{
- int prevent_power_on = 0;
- struct batt_params params;
- struct batt_params *current_batt_params = &curr.batt;
-#ifdef CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON
- static int automatic_power_on = 1;
-#endif
-
- /* If battery params seem uninitialized then retrieve them */
- if (current_batt_params->is_present == BP_NOT_SURE) {
- battery_get_params(&params);
- current_batt_params = &params;
- }
-
-#ifdef CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON
-
- /*
- * Remember that a power button was pressed, and assume subsequent
- * power-ups are user-requested and non-automatic.
- */
- if (power_button_pressed)
- automatic_power_on = 0;
- /*
- * Require a minimum battery level to power on and ensure that the
- * battery can provide power to the system.
- */
- if (current_batt_params->is_present != BP_YES ||
-#ifdef CONFIG_BATTERY_MEASURE_IMBALANCE
- (current_batt_params->flags & BATT_FLAG_IMBALANCED_CELL &&
- current_batt_params->state_of_charge <
- CONFIG_CHARGER_MIN_BAT_PCT_IMBALANCED_POWER_ON) ||
-#endif
-#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT
- battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED ||
-#endif
- current_batt_params->state_of_charge <
- CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON)
- prevent_power_on = 1;
-
-#if defined(CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON) && \
- defined(CONFIG_CHARGE_MANAGER)
- /* However, we can power on if a sufficient charger is present. */
- if (prevent_power_on) {
- if (charge_manager_get_power_limit_uw() >=
- CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON * 1000)
- prevent_power_on = 0;
-#if defined(CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON_WITH_BATT) && \
- defined(CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON_WITH_AC)
- else if (charge_manager_get_power_limit_uw() >=
- CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON_WITH_BATT * 1000
-#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT
- && battery_get_disconnect_state() ==
- BATTERY_NOT_DISCONNECTED
-#endif
- && (current_batt_params->state_of_charge >=
- CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON_WITH_AC))
- prevent_power_on = 0;
-#endif
- }
-#endif /* CONFIG_CHARGE_MANAGER && CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON */
-
- /*
- * Factory override: Always allow power on if WP is disabled,
- * except when auto-power-on at EC startup and the battery
- * is physically present.
- */
- prevent_power_on &= (system_is_locked() || (automatic_power_on
-#ifdef CONFIG_BATTERY_HW_PRESENT_CUSTOM
- && battery_hw_present() == BP_YES
-#endif
- ));
-#endif /* CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON */
-
-#ifdef CONFIG_CHARGE_MANAGER
- /* Always prevent power on until charge current is initialized */
- if (extpower_is_present() &&
- (charge_manager_get_charger_current() ==
- CHARGE_CURRENT_UNINITIALIZED))
- prevent_power_on = 1;
-#ifdef CONFIG_BATTERY_HW_PRESENT_CUSTOM
- /*
- * If battery is NOT physically present then prevent power on until
- * a sufficient charger is present.
- */
- if (extpower_is_present() && battery_hw_present() == BP_NO
-#ifdef CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON
- && charge_manager_get_power_limit_uw() <
- CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON * 1000
-#endif /* CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON */
- )
- prevent_power_on = 1;
-#endif /* CONFIG_BATTERY_HW_PRESENT_CUSTOM */
-#endif /* CONFIG_CHARGE_MANAGER */
-
- /*
- * Prevent power on if there is no battery nor ac power. This
- * happens when the servo is powering the EC to flash it. Only include
- * this logic for boards in initial bring up phase since this won't
- * happen for released boards.
- */
-#ifdef CONFIG_SYSTEM_UNLOCKED
- if (!current_batt_params->is_present && !curr.ac)
- prevent_power_on = 1;
-#endif /* CONFIG_SYSTEM_UNLOCKED */
-
- return prevent_power_on;
-}
-
-static int battery_near_full(void)
-{
- if (charge_get_percent() < BATTERY_LEVEL_NEAR_FULL)
- return 0;
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- if (charge_base > -1 && charge_base < BATTERY_LEVEL_NEAR_FULL)
- return 0;
-#endif
-
- return 1;
-}
-
-enum charge_state charge_get_state(void)
-{
- switch (curr.state) {
- case ST_IDLE:
- if (battery_seems_dead || curr.batt.is_present == BP_NO)
- return PWR_STATE_ERROR;
- return PWR_STATE_IDLE;
- case ST_DISCHARGE:
-#ifdef CONFIG_PWR_STATE_DISCHARGE_FULL
- if (battery_near_full())
- return PWR_STATE_DISCHARGE_FULL;
- else
-#endif
- return PWR_STATE_DISCHARGE;
- case ST_CHARGE:
- /* The only difference here is what the LEDs display. */
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER) &&
- charge_manager_get_active_charge_port() == CHARGE_PORT_NONE)
- return PWR_STATE_DISCHARGE;
- else if (battery_near_full())
- return PWR_STATE_CHARGE_NEAR_FULL;
- else
- return PWR_STATE_CHARGE;
- case ST_PRECHARGE:
- /* we're in battery discovery mode */
- return PWR_STATE_IDLE;
- default:
- /* Anything else can be considered an error for LED purposes */
- return PWR_STATE_ERROR;
- }
-}
-
-uint32_t charge_get_flags(void)
-{
- uint32_t flags = 0;
-
- if (get_chg_ctrl_mode() != CHARGE_CONTROL_NORMAL)
- flags |= CHARGE_FLAG_FORCE_IDLE;
- if (curr.ac)
- flags |= CHARGE_FLAG_EXTERNAL_POWER;
- if (curr.batt.flags & BATT_FLAG_RESPONSIVE)
- flags |= CHARGE_FLAG_BATT_RESPONSIVE;
-
- return flags;
-}
-
-int charge_get_percent(void)
-{
- /*
- * Since there's no way to indicate an error to the caller, we'll just
- * return the last known value. Even if we've never been able to talk
- * to the battery, that'll be zero, which is probably as good as
- * anything.
- */
- return is_full ? 100 : curr.batt.state_of_charge;
-}
-
-test_mockable int charge_get_display_charge(void)
-{
- return curr.batt.display_charge;
-}
-
-int charge_get_battery_temp(int idx, int *temp_ptr)
-{
- if (curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE)
- return EC_ERROR_UNKNOWN;
-
- /* Battery temp is 10ths of degrees K, temp wants degrees K */
- *temp_ptr = curr.batt.temperature / 10;
- return EC_SUCCESS;
-}
-
-__overridable int charge_is_consuming_full_input_current(void)
-{
- int chg_pct = charge_get_percent();
-
- return chg_pct > 2 && chg_pct < 95;
-}
-
-#ifdef CONFIG_CHARGER_OTG
-int charge_set_output_current_limit(int chgnum, int ma, int mv)
-{
- int ret;
- int enable = ma > 0;
-
- if (enable) {
- ret = charger_set_otg_current_voltage(chgnum, ma, mv);
- if (ret != EC_SUCCESS)
- return ret;
- }
-
- ret = charger_enable_otg_power(chgnum, enable);
- if (ret != EC_SUCCESS)
- return ret;
-
- /* If we start/stop providing power, wake the charger task. */
- if ((curr.output_current == 0 && enable) ||
- (curr.output_current > 0 && !enable))
- task_wake(TASK_ID_CHARGER);
-
- curr.output_current = ma;
-
- return EC_SUCCESS;
-}
-#endif
-
-int charge_set_input_current_limit(int ma, int mv)
-{
- __maybe_unused int chgnum = 0;
-
- if (IS_ENABLED(CONFIG_OCPC))
- chgnum = charge_get_active_chg_chip();
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- curr.input_voltage = mv;
-#endif
- /*
- * If battery is not present, we are not locked, and base is not
- * connected then allow system to pull as much input current as needed.
- * Yes, we might overcurrent the charger but this is no worse than
- * browning out due to insufficient input current.
- */
- if (curr.batt.is_present != BP_YES && !system_is_locked() &&
- !base_connected) {
-
- int prev_input = 0;
-
- charger_get_input_current_limit(chgnum, &prev_input);
-
-#ifdef CONFIG_USB_POWER_DELIVERY
-#if ((PD_MAX_POWER_MW * 1000) / PD_MAX_VOLTAGE_MV != PD_MAX_CURRENT_MA)
- /*
- * If battery is not present, input current is set to
- * PD_MAX_CURRENT_MA. If the input power set is greater than
- * the maximum allowed system power, system might get damaged.
- * Hence, limit the input current to meet maximum allowed
- * input system power.
- */
-
- if (mv > 0 && mv * curr.desired_input_current >
- PD_MAX_POWER_MW * 1000)
- ma = (PD_MAX_POWER_MW * 1000) / mv;
- /*
- * If the active charger has already been initialized to at
- * least this current level, nothing left to do.
- */
- else if (prev_input >= ma)
- return EC_SUCCESS;
-#else
- if (prev_input >= ma)
- return EC_SUCCESS;
-#endif
- /*
- * If the current needs lowered due to PD max power
- * considerations, or needs raised for the selected active
- * charger chip, fall through to set.
- */
-#endif /* CONFIG_USB_POWER_DELIVERY */
- }
-
-#ifdef CONFIG_CHARGER_MAX_INPUT_CURRENT
- /* Limit input current limit to max limit for this board */
- ma = MIN(ma, CONFIG_CHARGER_MAX_INPUT_CURRENT);
-#endif
- curr.desired_input_current = ma;
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
- /* Wake up charger task to allocate current between lid and base. */
- charge_wakeup();
- return EC_SUCCESS;
-#else
- return charger_set_input_current_limit(chgnum, ma);
-#endif
-}
-
-#ifdef CONFIG_OCPC
-void charge_set_active_chg_chip(int idx)
-{
- ASSERT(idx < (int)board_get_charger_chip_count());
-
- if (idx == curr.ocpc.active_chg_chip)
- return;
-
- CPRINTS("Act Chg: %d", idx);
- curr.ocpc.active_chg_chip = idx;
-}
-#endif /* CONFIG_OCPC */
-
-int charge_get_active_chg_chip(void)
-{
-#ifdef CONFIG_OCPC
- return curr.ocpc.active_chg_chip;
-#else
- return 0;
-#endif
-}
-
-#ifdef CONFIG_USB_PD_PREFER_MV
-bool charge_is_current_stable(void)
-{
- return get_time().val >= stable_ts.val;
-}
-
-int charge_get_plt_plus_bat_desired_mw(void)
-{
- /*
- * Ideally, the system consuming power could be evaluated by
- * "IBus * VBus - battery charging power". But in practice,
- * most charger drivers don't implement IBUS ADC reading,
- * so we use system PLT instead as an alterntaive approach.
- */
- return pd_pref_config.plt_mw + desired_mw;
-}
-
-int charge_get_stable_current(void)
-{
- return stable_current;
-}
-
-void charge_set_stable_current(int ma)
-{
- stable_current = ma;
-}
-
-void charge_reset_stable_current_us(uint64_t us)
-{
- timestamp_t now = get_time();
-
- if (stable_ts.val < now.val + us)
- stable_ts.val = now.val + us;
-
- stable_current = CHARGE_CURRENT_UNINITIALIZED;
-}
-
-void charge_reset_stable_current(void)
-{
- /* it takes 8 to 10 seconds to stabilize battery current in practice */
- charge_reset_stable_current_us(10 * SECOND);
-}
-#endif
-
-#ifdef CONFIG_OCPC
-void trigger_ocpc_reset(void)
-{
- ocpc_reset(&curr.ocpc);
-}
-#endif
-
-/*****************************************************************************/
-/* Host commands */
-
-static enum ec_status
-charge_command_charge_control(struct host_cmd_handler_args *args)
-{
- const struct ec_params_charge_control *p = args->params;
- struct ec_response_charge_control *r = args->response;
- int rv;
-
- if (args->version >= 2) {
- if (p->cmd == EC_CHARGE_CONTROL_CMD_SET) {
- if (p->mode == CHARGE_CONTROL_NORMAL) {
- rv = battery_sustainer_set(
- p->sustain_soc.lower,
- p->sustain_soc.upper);
- if (rv == EC_RES_UNAVAILABLE)
- return EC_RES_UNAVAILABLE;
- if (rv)
- return EC_RES_INVALID_PARAM;
- } else {
- battery_sustainer_disable();
- }
- } else if (p->cmd == EC_CHARGE_CONTROL_CMD_GET) {
- r->mode = get_chg_ctrl_mode();
- r->sustain_soc.lower = sustain_soc.lower;
- r->sustain_soc.upper = sustain_soc.upper;
- args->response_size = sizeof(*r);
- return EC_RES_SUCCESS;
- } else {
- return EC_RES_INVALID_PARAM;
- }
- }
-
- rv = set_chg_ctrl_mode(p->mode);
- if (rv != EC_SUCCESS)
- return EC_RES_ERROR;
-
- return EC_RES_SUCCESS;
-}
-DECLARE_HOST_COMMAND(EC_CMD_CHARGE_CONTROL, charge_command_charge_control,
- EC_VER_MASK(1) | EC_VER_MASK(2));
-
-static void reset_current_limit(void)
-{
- user_current_limit = -1U;
-}
-DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, reset_current_limit, HOOK_PRIO_DEFAULT);
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, reset_current_limit, HOOK_PRIO_DEFAULT);
-
-static enum ec_status
-charge_command_current_limit(struct host_cmd_handler_args *args)
-{
- const struct ec_params_current_limit *p = args->params;
-
- user_current_limit = p->limit;
-
- return EC_RES_SUCCESS;
-}
-DECLARE_HOST_COMMAND(EC_CMD_CHARGE_CURRENT_LIMIT, charge_command_current_limit,
- EC_VER_MASK(0));
-
-/*
- * Expose charge/battery related state
- *
- * @param param command to get corresponding data
- * @param value the corresponding data
- * @return EC_SUCCESS or error
- */
-static int charge_get_charge_state_debug(int param, uint32_t *value)
-{
- switch (param) {
- case CS_PARAM_DEBUG_CTL_MODE:
- *value = get_chg_ctrl_mode();
- break;
- case CS_PARAM_DEBUG_MANUAL_CURRENT:
- *value = manual_current;
- break;
- case CS_PARAM_DEBUG_MANUAL_VOLTAGE:
- *value = manual_voltage;
- break;
- case CS_PARAM_DEBUG_SEEMS_DEAD:
- *value = battery_seems_dead;
- break;
- case CS_PARAM_DEBUG_SEEMS_DISCONNECTED:
- *value = battery_seems_disconnected;
- break;
- case CS_PARAM_DEBUG_BATT_REMOVED:
- *value = battery_was_removed;
- break;
- default:
- *value = 0;
- return EC_ERROR_INVAL;
- }
-
- return EC_SUCCESS;
-}
-
-static enum ec_status
-charge_command_charge_state(struct host_cmd_handler_args *args)
-{
- const struct ec_params_charge_state *in = args->params;
- struct ec_response_charge_state *out = args->response;
- uint32_t val;
- int rv = EC_RES_SUCCESS;
- int chgnum = 0;
-
- if (args->version > 0)
- chgnum = in->chgnum;
-
- switch (in->cmd) {
-
- case CHARGE_STATE_CMD_GET_STATE:
- out->get_state.ac = curr.ac;
- out->get_state.chg_voltage = curr.chg.voltage;
- out->get_state.chg_current = curr.chg.current;
- out->get_state.chg_input_current = curr.chg.input_current;
- out->get_state.batt_state_of_charge = curr.batt.state_of_charge;
- args->response_size = sizeof(out->get_state);
- break;
-
- case CHARGE_STATE_CMD_GET_PARAM:
- val = 0;
- if (IS_ENABLED(CONFIG_CHARGER_PROFILE_OVERRIDE)
- && in->get_param.param >= CS_PARAM_CUSTOM_PROFILE_MIN
- && in->get_param.param <= CS_PARAM_CUSTOM_PROFILE_MAX) {
- /* custom profile params */
- rv = charger_profile_override_get_param(
- in->get_param.param, &val);
- } else if (IS_ENABLED(CONFIG_CHARGE_STATE_DEBUG)
- && in->get_param.param >= CS_PARAM_DEBUG_MIN
- && in->get_param.param <= CS_PARAM_DEBUG_MAX) {
- /* debug params */
- rv = charge_get_charge_state_debug(
- in->get_param.param, &val);
- } else {
- /* standard params */
- switch (in->get_param.param) {
- case CS_PARAM_CHG_VOLTAGE:
- val = curr.chg.voltage;
- break;
- case CS_PARAM_CHG_CURRENT:
- val = curr.chg.current;
- break;
- case CS_PARAM_CHG_INPUT_CURRENT:
- val = curr.chg.input_current;
- break;
- case CS_PARAM_CHG_STATUS:
- val = curr.chg.status;
- break;
- case CS_PARAM_CHG_OPTION:
- val = curr.chg.option;
- break;
- case CS_PARAM_LIMIT_POWER:
-#ifdef CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW
- /*
- * LIMIT_POWER status is based on battery level
- * and external charger power.
- */
- if ((curr.batt.is_present != BP_YES ||
- curr.batt.state_of_charge <
- CONFIG_CHARGER_LIMIT_POWER_THRESH_BAT_PCT)
- && charge_manager_get_power_limit_uw() <
- CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW
- * 1000 && system_is_locked())
- val = 1;
- else
-#endif
- val = 0;
- break;
- default:
- rv = EC_RES_INVALID_PARAM;
- }
- }
-
- /* got something */
- out->get_param.value = val;
- args->response_size = sizeof(out->get_param);
- break;
-
- case CHARGE_STATE_CMD_SET_PARAM:
- if (system_is_locked())
- return EC_RES_ACCESS_DENIED;
-
- val = in->set_param.value;
- if (IS_ENABLED(CONFIG_CHARGER_PROFILE_OVERRIDE)
- && in->set_param.param >= CS_PARAM_CUSTOM_PROFILE_MIN
- && in->set_param.param <= CS_PARAM_CUSTOM_PROFILE_MAX) {
- /* custom profile params */
- rv = charger_profile_override_set_param(
- in->set_param.param, val);
- } else {
- switch (in->set_param.param) {
- case CS_PARAM_CHG_VOLTAGE:
- chgstate_set_manual_voltage(val);
- break;
- case CS_PARAM_CHG_CURRENT:
- chgstate_set_manual_current(val);
- break;
- case CS_PARAM_CHG_INPUT_CURRENT:
- if (charger_set_input_current_limit(chgnum,
- val))
- rv = EC_RES_ERROR;
- break;
- case CS_PARAM_CHG_STATUS:
- case CS_PARAM_LIMIT_POWER:
- /* Can't set this */
- rv = EC_RES_ACCESS_DENIED;
- break;
- case CS_PARAM_CHG_OPTION:
- if (charger_set_option(val))
- rv = EC_RES_ERROR;
- break;
- default:
- rv = EC_RES_INVALID_PARAM;
-
- }
- }
- break;
-
- default:
- CPRINTS("EC_CMD_CHARGE_STATE: bad cmd 0x%x", in->cmd);
- rv = EC_RES_INVALID_PARAM;
- }
-
- return rv;
-}
-
-DECLARE_HOST_COMMAND(EC_CMD_CHARGE_STATE, charge_command_charge_state,
- EC_VER_MASK(0) | EC_VER_MASK(1));
-
-/*****************************************************************************/
-/* Console commands */
-
-#ifdef CONFIG_CMD_PWR_AVG
-
-static int command_pwr_avg(int argc, char **argv)
-{
- int avg_mv;
- int avg_ma;
- int avg_mw;
-
- if (argc != 1)
- return EC_ERROR_PARAM_COUNT;
-
- avg_mv = battery_get_avg_voltage();
- if (avg_mv < 0)
- return EC_ERROR_UNKNOWN;
- avg_ma = battery_get_avg_current();
- avg_mw = avg_mv * avg_ma / 1000;
-
- ccprintf("mv = %d\nma = %d\nmw = %d\n",
- avg_mv, avg_ma, avg_mw);
- return EC_SUCCESS;
-}
-
-DECLARE_CONSOLE_COMMAND(pwr_avg, command_pwr_avg,
- NULL,
- "Get 1 min power average");
-
-#endif /* CONFIG_CMD_PWR_AVG */
-
-static int command_chgstate(int argc, char **argv)
-{
- int rv;
- int val;
- char *e;
-
- if (argc > 1) {
- if (!strcasecmp(argv[1], "idle")) {
- if (argc <= 2)
- return EC_ERROR_PARAM_COUNT;
- if (!parse_bool(argv[2], &val))
- return EC_ERROR_PARAM2;
- rv = set_chg_ctrl_mode(val ? CHARGE_CONTROL_IDLE :
- CHARGE_CONTROL_NORMAL);
- if (rv)
- return rv;
- } else if (!strcasecmp(argv[1], "discharge")) {
- if (argc <= 2)
- return EC_ERROR_PARAM_COUNT;
- if (!parse_bool(argv[2], &val))
- return EC_ERROR_PARAM2;
- rv = set_chg_ctrl_mode(val ? CHARGE_CONTROL_DISCHARGE :
- CHARGE_CONTROL_NORMAL);
- if (rv)
- return rv;
- } else if (!strcasecmp(argv[1], "debug")) {
- if (argc <= 2)
- return EC_ERROR_PARAM_COUNT;
- if (!parse_bool(argv[2], &debugging))
- return EC_ERROR_PARAM2;
- } else if (!strcasecmp(argv[1], "sustain")) {
- int lower, upper;
-
- if (argc <= 3)
- return EC_ERROR_PARAM_COUNT;
- lower = strtoi(argv[2], &e, 0);
- if (*e)
- return EC_ERROR_PARAM2;
- upper = strtoi(argv[3], &e, 0);
- if (*e)
- return EC_ERROR_PARAM3;
- rv = battery_sustainer_set(lower, upper);
- if (rv)
- return EC_ERROR_INVAL;
- } else {
- return EC_ERROR_PARAM1;
- }
- }
-
- dump_charge_state();
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(chgstate, command_chgstate,
- "[idle|discharge|debug on|off]"
- "\n[sustain <lower> <upper>]",
- "Get/set charge state machine status");
-
-#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT
-static int command_chgdualdebug(int argc, char **argv)
-{
- int val;
- char *e;
-
- if (argc > 1) {
- if (argv[1][0] == 'c') {
- if (argc <= 2)
- return EC_ERROR_PARAM_COUNT;
-
- if (!strcasecmp(argv[2], "auto")) {
- val = -1;
- } else {
- val = strtoi(argv[2], &e, 0);
- if (*e || val < 0)
- return EC_ERROR_PARAM2;
- }
-
- manual_ac_current_base = val;
- charge_wakeup();
- } else if (argv[1][0] == 'd') {
- if (argc <= 2)
- return EC_ERROR_PARAM_COUNT;
-
- if (!strcasecmp(argv[2], "auto")) {
- manual_noac_enabled = 0;
- } else {
- val = strtoi(argv[2], &e, 0);
- if (*e)
- return EC_ERROR_PARAM2;
- manual_noac_current_base = val;
- manual_noac_enabled = 1;
- }
- charge_wakeup();
- } else {
- return EC_ERROR_PARAM1;
- }
- } else {
- ccprintf("Base/Lid: %d%s/%d mA\n",
- prev_current_base, prev_allow_charge_base ? "+" : "",
- prev_current_lid);
- }
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(chgdualdebug, command_chgdualdebug,
- "[charge (auto|<current>)|discharge (auto|<current>)]",
- "Manually control dual-battery charging algorithm.");
-#endif