From f514c0451a06324f59a32332ffbd01ceeeb1003d Mon Sep 17 00:00:00 2001 From: Nathan Wong Date: Thu, 5 Nov 2026 16:44:53 +0800 Subject: [PATCH] fwk: reapply an explicitly requested static charge limit Another control path can disable or replace the live sustainer while the cached requested percentage stays unchanged. Re-sending that percentage then succeeds without restoring the requested static thresholds. Invalidate the cache on an accepted explicit SET in stage 0. Preserve the caps of active Battery Extender stages and leave timer/GET behavior alone. Add a host regression to the existing utility runner and export optional GCC coverage. Use Zephyr time units and the repository formatter to pass source checks. Keep minute/hour arithmetic 64-bit so the accepted 9999-minute reset interval is not truncated. Exercise extender deadlines, configuration round-trips and console timer controls as well as charge-limit recovery. Related: https://github.com/FrameworkComputer/SoftwareFirmwareIssueTracker/issues/284 BUG=None TEST=python3 util/test_framework_battery_extender.py (10 pass) TEST=bash util/run_tests.sh (pass) TEST=lotus RO/RW build with Zephyr SDK 0.17.2 (pass) Change-Id: I3d0946955ea6b9c401674b42ae65ded7e531dc54 --- util/run_tests.sh | 1 + util/test_framework_battery_extender.py | 351 ++++++++++++++++++ .../program/framework/src/battery_extender.c | 178 +++++---- 3 files changed, 454 insertions(+), 76 deletions(-) create mode 100644 util/test_framework_battery_extender.py diff --git a/util/run_tests.sh b/util/run_tests.sh index d2c60599ec..7679e77f1e 100755 --- a/util/run_tests.sh +++ b/util/run_tests.sh @@ -17,6 +17,7 @@ cd "$(dirname "$(realpath -e "${BASH_SOURCE[0]}")")"/.. # Run shell tests cd util +python3 test_framework_battery_extender.py ./test-inject-keys.sh # Run the Zephyr config tests diff --git a/util/test_framework_battery_extender.py b/util/test_framework_battery_extender.py new file mode 100644 index 0000000000..b1cb2dd131 --- /dev/null +++ b/util/test_framework_battery_extender.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +# Copyright 2026 The ChromiumOS Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Host regression for Framework's charge-limit command. + +Compile the complete production translation unit and real protocol headers. +Mock only its EC services. No hardware or firmware flashing is involved. +Run with: python3 util/test_framework_battery_extender.py +Requires Python 3.8+ and a C compiler (cc, or set CC). Coverage needs GCC/gcov. +""" + +import argparse +import os +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest + + +ROOT = Path(__file__).resolve().parents[1] +SOURCE = ROOT / "zephyr/program/framework/src/battery_extender.c" +STUB_HEADERS = ( + "battery.h battery_smart.h board_charger.h battery_fuel_gauge.h " + "board_function.h charge_state.h charger.h console.h " + "customized_shared_memory.h extpower.h factory.h host_command.h " + "system.h timer.h util.h hooks.h zephyr/sys/time_units.h" +).split() + +HARNESS = r""" +#include +#include +#include +#include +#include +#include "ec_commands.h" +#include "board_host_command.h" + +#define USEC_PER_SEC 1000000U +#define SEC_PER_MIN 60U +#define SEC_PER_HOUR 3600U +/* Match legacy timer.h when running this regression against the baseline. */ +#define SECOND 1000000 +#define MINUTE 60000000 +#define HOUR 3600000000ULL +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define EC_SUCCESS 0 +#define EC_ERROR_PARAM1 1 +#define EC_ERROR_PARAM2 2 +#define EC_ERROR_PARAM3 3 +#define EC_ERROR_PARAM_COUNT 4 +#define SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX 0 +#define DECLARE_HOOK(...) +#define DECLARE_HOST_COMMAND(...) +#define DECLARE_CONSOLE_COMMAND(...) +#define cprints(...) ((void)0) +#define cprintf(...) ((void)0) + +typedef union { uint64_t val; } timestamp_t; +struct host_cmd_handler_args { + const void *params; + void *response; + int response_size; + int version; +}; +static uint8_t saved_limit = 80; +static int lower = -1, upper = -1, mode; +static uint64_t now; +static int ac_present = 1; +static timestamp_t get_time(void) { return (timestamp_t){ .val = now }; } +static bool timestamp_expired(timestamp_t deadline, const timestamp_t *time) +{ return deadline.val <= time->val; } +static int system_get_bbram(int idx, uint8_t *value) +{ *value = saved_limit; return 0; } +static int system_set_bbram(int idx, uint8_t value) +{ saved_limit = value; return 0; } +static bool battery_sustainer_enabled(void) +{ return lower != -1 && upper != -1; } +static int battery_sustainer_set(int8_t lo, int8_t hi) +{ lower = lo; upper = hi; return 0; } +static int set_chg_ctrl_mode(enum ec_charge_control_mode value) +{ mode = value; return 0; } +static int factory_status(void) { return 0; } +static int extpower_is_present(void) { return ac_present; } +static int strtoi(const char *s, char **e, int base) +{ return strtol(s, e, base); } +static int parse_bool(const char *s, int *value) +{ *value = !strcmp(s, "enable"); return 1; } + +#include "PRODUCTION_SOURCE" + +#define CHECK(condition, message) do { \ + if (!(condition)) { fprintf(stderr, "%s\n", message); return 1; } \ +} while (0) + +static enum ec_status send(uint8_t operation, uint8_t limit) +{ + struct ec_params_ec_chg_limit_control p = { + .modes = operation, .max_percentage = limit + }; + struct ec_response_chg_limit_control r = {0}; + struct host_cmd_handler_args args = { .params = &p, .response = &r }; + return cmd_charging_limit_control(&args); +} + +int main(int argc, char **argv) +{ + extender_init(); + batt_extender_disable = true; + CHECK(send(CHG_LIMIT_SET_LIMIT, 80) == EC_RES_SUCCESS, "initial SET failed"); + CHECK(lower == 75 && upper == 80, "initial SET did not apply 75..80"); + if (!strcmp(argv[1], "disabled")) { + battery_sustainer_set(-1, -1); + send(CHG_LIMIT_SET_LIMIT, 80); + CHECK(lower == 75 && upper == 80, + "same-limit SET left the sustainer disabled"); + } else if (!strcmp(argv[1], "replaced")) { + battery_sustainer_set(90, 95); + send(CHG_LIMIT_SET_LIMIT, 80); + CHECK(lower == 75 && upper == 80, + "same-limit SET kept another policy's thresholds"); + } else if (!strcmp(argv[1], "host_override")) { + battery_sustainer_set(-1, -1); + battery_extender(); + send(CHG_LIMIT_GET_LIMIT, 0); + CHECK(!battery_sustainer_enabled(), + "timer or GET cancelled intentional host control"); + } else if (!strncmp(argv[1], "stage", 5)) { + send(CHG_LIMIT_SET_LIMIT, 98); + batt_extender_disable = false; + stage = !strcmp(argv[1], "stage1") ? + BATT_EXTENDER_STAGE_1 : BATT_EXTENDER_STAGE_2; + int cap_lower = stage == BATT_EXTENDER_STAGE_1 ? 90 : 85; + int cap_upper = stage == BATT_EXTENDER_STAGE_1 ? 95 : 87; + sustainer_lower = 93; + sustainer_upper = 98; + batt_extender_deadline.val = 0; + batt_extender_deadline_stage2.val = 0; + battery_sustainer_set(cap_lower, cap_upper); + send(CHG_LIMIT_SET_LIMIT, 98); + CHECK(lower == cap_lower && upper == cap_upper, + "same-limit SET removed the active extender cap"); + battery_extender(); + CHECK(lower == cap_lower && upper == cap_upper, + "timer removed the active extender cap"); + } else if (!strcmp(argv[1], "full_and_invalid")) { + CHECK(send(CHG_LIMIT_SET_LIMIT, 19) == EC_RES_ERROR, + "invalid SET was accepted"); + CHECK(lower == 75 && upper == 80, "invalid SET changed thresholds"); + send(CHG_LIMIT_SET_LIMIT, 100); + CHECK(!battery_sustainer_enabled(), "100 percent did not disable limiting"); + send(CHG_LIMIT_SET_LIMIT, 80); + CHECK(lower == 75 && upper == 80, "changed-limit SET failed"); + } else if (!strcmp(argv[1], "extender_timing")) { + send(CHG_LIMIT_SET_LIMIT, 100); + batt_extender_disable = false; + batt_extender_deadline.val = 1000000; + batt_extender_deadline_stage2.val = 2000000; + now = 999999; + battery_extender(); + CHECK(stage == BATT_EXTENDER_STAGE_0, "stage one began early"); + now = 1000000; + battery_extender(); + CHECK(stage == BATT_EXTENDER_STAGE_1 && lower == 90 && upper == 95, + "stage one missed its deadline or cap"); + now = 2000000; + battery_extender(); + CHECK(stage == BATT_EXTENDER_STAGE_2 && lower == 85 && upper == 87, + "stage two missed its deadline or cap"); + ac_present = 0; + now = reset_deadline.val; + battery_extender(); + CHECK(stage == BATT_EXTENDER_STAGE_0 && !battery_sustainer_enabled(), + "AC absence did not reset the extender after its interval"); + } else if (!strcmp(argv[1], "configuration")) { + struct ec_params_battery_extender p = { + .cmd = BATT_EXTENDER_WRITE_CMD, .trigger_days = 99, + .reset_minutes = 9999, .disable = 0 + }; + struct ec_response_battery_extender r = {0}; + struct host_cmd_handler_args args = { .params = &p, .response = &r }; + CHECK(battery_extender_hc(&args) == EC_SUCCESS, "configuration failed"); + CHECK(battery_extender_trigger == 8553600000000ULL, + "99-day trigger was truncated"); + CHECK(battery_extender_reset == 599940000000ULL, + "9999-minute reset was truncated"); + p.cmd = BATT_EXTENDER_READ_CMD; + CHECK(battery_extender_hc(&args) == EC_SUCCESS, "readback failed"); + CHECK(r.trigger_days == 99 && r.reset_minutes == 9999, + "configuration did not round-trip"); + } else if (!strcmp(argv[1], "console_timing")) { + const char *args[] = { "batt_extender", "trigger", "2", "s" }; + CHECK(cmd_parse_timestamp(4, args) == 2000000ULL, + "seconds changed during time-unit migration"); + args[3] = "m"; + CHECK(cmd_parse_timestamp(4, args) == 120000000ULL, "minutes changed"); + args[3] = "h"; + CHECK(cmd_parse_timestamp(4, args) == 7200000000ULL, "hours changed"); + args[3] = "d"; + CHECK(cmd_batt_extender(4, args) == EC_SUCCESS, "console trigger failed"); + CHECK(battery_extender_trigger == 172800000000ULL, "days changed"); + CHECK(cmd_batt_extender(1, args) == EC_SUCCESS, "console status failed"); + } else if (!strcmp(argv[1], "console_controls")) { + const char *args[] = { "batt_extender", "enable", "2", "m" }; + now = 123; + CHECK(cmd_batt_extender(2, args) == EC_SUCCESS, + "console enable failed"); + CHECK(!batt_extender_disable && batt_extender_deadline.val > now, + "enable did not arm the extender"); + args[1] = "timeext"; + CHECK(cmd_batt_extender(4, args) == EC_SUCCESS, "stage-one timer failed"); + CHECK(batt_extender_deadline.val == 120000123ULL, + "stage-one timer used the wrong unit"); + args[1] = "timeext2"; + CHECK(cmd_batt_extender(4, args) == EC_SUCCESS, "stage-two timer failed"); + CHECK(batt_extender_deadline_stage2.val == 120000123ULL, + "stage-two timer used the wrong unit"); + args[1] = "timerst"; + CHECK(cmd_batt_extender(4, args) == EC_SUCCESS, "reset timer failed"); + CHECK(reset_deadline.val == 120000123ULL, + "reset timer used the wrong unit"); + args[1] = "reset"; + args[3] = "h"; + CHECK(cmd_batt_extender(4, args) == EC_SUCCESS, "reset setting failed"); + CHECK(battery_extender_reset == 7200000000ULL, + "reset setting used the wrong unit"); + args[1] = "disable"; + CHECK(cmd_batt_extender(2, args) == EC_SUCCESS, "disable failed"); + CHECK(batt_extender_disable && stage == BATT_EXTENDER_STAGE_0, + "disable did not return to stage zero"); + } else { + return 2; + } + return 0; +} +""" + + +class ChargeLimitTest(unittest.TestCase): + """Exercise explicit SET and the policies it must preserve.""" + + coverage_dir = None + + @classmethod + def setUpClass(cls): + # unittest owns and closes this directory after the class finishes. + # pylint: disable=consider-using-with + temporary = tempfile.TemporaryDirectory() + # pylint: enable=consider-using-with + cls.addClassCleanup(temporary.cleanup) + path = Path(temporary.name) + for header in STUB_HEADERS: + (path / header).parent.mkdir(parents=True, exist_ok=True) + (path / header).write_text( + "/* EC service mocked by the host test. */\n" + ) + c = path / "regression.c" + c.write_text(HARNESS.replace("PRODUCTION_SOURCE", SOURCE.as_posix())) + cls.binary = path / "regression" + coverage_flags = ["--coverage"] if cls.coverage_dir else [] + subprocess.run( + [ + os.environ.get("CC", "cc"), + "-std=gnu11", + "-Wall", + "-Werror", + *coverage_flags, + "-Wno-unused-function", + "-Wno-unused-variable", + "-I", + str(path), + "-I", + str(ROOT / "zephyr/program/framework/include"), + "-I", + str(ROOT / "include"), + str(c), + "-o", + str(cls.binary), + ], + check=True, + ) + if cls.coverage_dir: + cls.coverage_dir.mkdir(parents=True, exist_ok=True) + cls.addClassCleanup(cls.export_coverage, c) + + @classmethod + def export_coverage(cls, source): + """Save GCC's coverage data before the temporary files are removed.""" + subprocess.run( + ["gcov", "--json-format", "--branch-probabilities", str(source)], + cwd=cls.coverage_dir, + check=True, + ) + + def check_case(self, case): + """Run an isolated production-code scenario and expose its assertion.""" + result = subprocess.run( + [self.binary, case], capture_output=True, text=True, check=False + ) + self.assertEqual(result.returncode, 0, result.stderr) + + def test_same_limit_restores_disabled_sustainer(self): + """An explicit repeated SET must restore disabled static thresholds.""" + self.check_case("disabled") + + def test_same_limit_restores_replaced_thresholds(self): + """A repeated static SET must replace another policy's thresholds.""" + self.check_case("replaced") + + def test_timer_and_get_preserve_host_override(self): + """Readback and timer activity must not cancel intentional host control.""" + self.check_case("host_override") + + def test_same_limit_preserves_stage_one_cap(self): + """A repeated SET must preserve the first active extender cap.""" + self.check_case("stage1") + + def test_same_limit_preserves_stage_two_cap(self): + """A repeated SET must preserve the second active extender cap.""" + self.check_case("stage2") + + def test_full_charge_and_invalid_limit(self): + """Reject invalid limits and retain full-charge/static transitions.""" + self.check_case("full_and_invalid") + + def test_extender_deadlines_and_ac_reset(self): + """Keep both cap deadlines and the AC-absence reset interval.""" + self.check_case("extender_timing") + + def test_configuration_round_trips_maximum_intervals(self): + """Keep valid multi-day/minute intervals wider than 32 bits.""" + self.check_case("configuration") + + def test_console_time_units(self): + """Keep seconds, minutes, hours and days in microseconds.""" + self.check_case("console_timing") + + def test_console_enable_disable_and_timers(self): + """Keep public console controls and their configured intervals.""" + self.check_case("console_controls") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument("--coverage-dir", type=Path) + options, test_args = parser.parse_known_args() + ChargeLimitTest.coverage_dir = options.coverage_dir + unittest.main(argv=[sys.argv[0], *test_args], verbosity=2) diff --git a/zephyr/program/framework/src/battery_extender.c b/zephyr/program/framework/src/battery_extender.c index d68f95ae9c..f2b9f17c08 100644 --- a/zephyr/program/framework/src/battery_extender.c +++ b/zephyr/program/framework/src/battery_extender.c @@ -1,15 +1,14 @@ -/* - * Copyright 2024 The Chromium OS Authors. All rights reserved. +/* Copyright 2024 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "battery.h" +#include "battery_fuel_gauge.h" #include "battery_smart.h" -#include "board_host_command.h" #include "board_charger.h" -#include "battery_fuel_gauge.h" #include "board_function.h" +#include "board_host_command.h" #include "charge_state.h" #include "charger.h" #include "console.h" @@ -17,31 +16,34 @@ #include "ec_commands.h" #include "extpower.h" #include "factory.h" +#include "hooks.h" #include "host_command.h" #include "system.h" #include "timer.h" #include "util.h" -#include "hooks.h" +#include -#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args) +#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ##args) -#define DAY (24 * HOUR) +#define MINUTE_US (1ULL * USEC_PER_SEC * SEC_PER_MIN) +#define HOUR_US (1ULL * USEC_PER_SEC * SEC_PER_HOUR) +#define DAY (24 * HOUR_US) #define TIMES2DAYS(d) (d / DAY) -#define TIMES2MINUTES(m) (m / MINUTE) -#define TIMES2SECOND(s) (s / SECOND) +#define TIMES2MINUTES(m) (m / MINUTE_US) +#define TIMES2SECOND(s) (s / USEC_PER_SEC) #define BATTERY_EXTENDER_STAGE1_VOLTAGE(v) (v * 97 / 100) #define BATTERY_EXTENDER_STAGE2_VOLTAGE(v) (v * 96 / 100) - /* * When in stage_0, will not control the charger * in stage_1 will set charger mode to idle and keeps battery at 90%~95% * in stage_2 will set charger mode to idle and keeps battery at 85%~87% * if user setting value lower than stage_1 or 2 will use the setting value - * ex: if user set charging_maximum_level to 70% battery will keeps at 65%~70% (min=max - 5) + * ex: if user set charging_maximum_level to 70% battery will keeps at 65%~70% + * (min=max - 5) */ enum battery_extender_stage_t { BATT_EXTENDER_STAGE_0, @@ -50,8 +52,8 @@ enum battery_extender_stage_t { }; static bool batt_extender_disable; -static uint64_t battery_extender_trigger = 5*DAY; -static uint64_t battery_extender_reset = 30*MINUTE; +static uint64_t battery_extender_trigger = 5 * DAY; +static uint64_t battery_extender_reset = 30 * MINUTE_US; static int stage; static timestamp_t batt_extender_deadline; static timestamp_t batt_extender_deadline_stage2; @@ -67,10 +69,9 @@ static void extender_init(void) { timestamp_t now = get_time(); - batt_extender_deadline.val = - now.val + battery_extender_trigger; + batt_extender_deadline.val = now.val + battery_extender_trigger; batt_extender_deadline_stage2.val = - now.val + battery_extender_trigger + 2*DAY; + now.val + battery_extender_trigger + 2 * DAY; reset_deadline.val = 0; /* not active */ } DECLARE_HOOK(HOOK_INIT, extender_init, HOOK_PRIO_DEFAULT); @@ -92,7 +93,8 @@ void charger_sustainer_reset(void) static void battery_percentage_control(void) { if (charging_maximum_level == EC_CHARGE_LIMIT_RESTORE) { - system_get_bbram(SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX, &charging_maximum_level); + system_get_bbram(SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX, + &charging_maximum_level); if (charging_maximum_level & CHG_LIMIT_OVERRIDE) charging_maximum_level = charging_maximum_level & 0x64; } @@ -102,8 +104,7 @@ static void battery_percentage_control(void) return; } - if (!charging_maximum_level || - charging_maximum_level == 100) { + if (!charging_maximum_level || charging_maximum_level == 100) { if (stage == BATT_EXTENDER_STAGE_0) charger_sustainer_reset(); return; @@ -112,7 +113,7 @@ static void battery_percentage_control(void) if (old_charger_limit != charging_maximum_level) { old_charger_limit = charging_maximum_level; battery_sustainer_set(MAX(20, (charging_maximum_level - 5)), - MAX(20, charging_maximum_level)); + MAX(20, charging_maximum_level)); } } @@ -133,50 +134,52 @@ void battery_extender(void) } if (extpower_is_present()) { - /* just keep pushing the reset timer into the future if we are on AC */ + /* just keep pushing the reset timer into the future if we are + * on AC */ reset_deadline.val = now.val + battery_extender_reset; } if (sustainer_upper != charger_sustainer_percentage()) { sustainer_upper = charger_sustainer_percentage(); sustainer_lower = sustainer_upper - 5; - /* if we already trigger stage, set 5 SECOND to update the sustainer again */ + /* Update an active stage's sustainer after five seconds. */ if (stage == BATT_EXTENDER_STAGE_1) { - batt_extender_deadline.val = now.val + 5 * SECOND; + batt_extender_deadline.val = now.val + 5 * USEC_PER_SEC; } else if (stage == BATT_EXTENDER_STAGE_2) - batt_extender_deadline_stage2.val = now.val + 5 * SECOND; + batt_extender_deadline_stage2.val = + now.val + 5 * USEC_PER_SEC; } - if (reset_deadline.val && - timestamp_expired(reset_deadline, &now)) { + if (reset_deadline.val && timestamp_expired(reset_deadline, &now)) { reset_deadline.val = 0; stage = BATT_EXTENDER_STAGE_0; - batt_extender_deadline.val = - now.val + battery_extender_trigger; + batt_extender_deadline.val = now.val + battery_extender_trigger; batt_extender_deadline_stage2.val = - now.val + battery_extender_trigger + 2*DAY; - /* if charger limit has been set don't clear sustainer and charger mode */ + now.val + battery_extender_trigger + 2 * DAY; + /* if charger limit has been set don't clear sustainer and + * charger mode */ if (charger_sustainer_percentage() == 100) { charger_sustainer_reset(); } } if (batt_extender_deadline_stage2.val && - timestamp_expired(batt_extender_deadline_stage2, &now)) { + timestamp_expired(batt_extender_deadline_stage2, &now)) { batt_extender_deadline_stage2.val = 0; stage = BATT_EXTENDER_STAGE_2; - battery_sustainer_set(MIN(85, sustainer_lower), MIN(87, sustainer_upper)); + battery_sustainer_set(MIN(85, sustainer_lower), + MIN(87, sustainer_upper)); } else if (batt_extender_deadline.val && - timestamp_expired(batt_extender_deadline, &now)) { + timestamp_expired(batt_extender_deadline, &now)) { batt_extender_deadline.val = 0; stage = BATT_EXTENDER_STAGE_1; - battery_sustainer_set(MIN(90, sustainer_lower), MIN(95, sustainer_upper)); + battery_sustainer_set(MIN(90, sustainer_lower), + MIN(95, sustainer_upper)); } } DECLARE_HOOK(HOOK_SECOND, battery_extender, HOOK_PRIO_DEFAULT); - /* Host command for battery extender feature */ static enum ec_status battery_extender_hc(struct host_cmd_handler_args *args) { @@ -185,24 +188,25 @@ static enum ec_status battery_extender_hc(struct host_cmd_handler_args *args) timestamp_t now = get_time(); if (p->cmd == BATT_EXTENDER_WRITE_CMD) { - if ((p->trigger_days != TIMES2DAYS(battery_extender_trigger)) && - (p->trigger_days >= 1 && p->trigger_days <= 99)) { + (p->trigger_days >= 1 && p->trigger_days <= 99)) { battery_extender_trigger = p->trigger_days * DAY; if (battery_extender_trigger != 0) { batt_extender_deadline.val = - now.val + battery_extender_trigger; + now.val + battery_extender_trigger; batt_extender_deadline_stage2.val = - now.val + battery_extender_trigger + 2*DAY; + now.val + battery_extender_trigger + + 2 * DAY; } else { batt_extender_deadline.val = 0; batt_extender_deadline_stage2.val = 0; } } - if ((p->reset_minutes != TIMES2MINUTES(battery_extender_reset)) && - (p->reset_minutes >= 1 && p->reset_minutes <= 9999)) { - battery_extender_reset = p->reset_minutes * MINUTE; + if ((p->reset_minutes != + TIMES2MINUTES(battery_extender_reset)) && + (p->reset_minutes >= 1 && p->reset_minutes <= 9999)) { + battery_extender_reset = p->reset_minutes * MINUTE_US; if (battery_extender_reset) { reset_deadline.val = now.val + battery_extender_reset; @@ -214,7 +218,8 @@ static enum ec_status battery_extender_hc(struct host_cmd_handler_args *args) if (batt_extender_disable != p->disable) { batt_extender_disable = p->disable; if (batt_extender_disable) { - /* if charger limit has been set don't control sustainer again */ + /* if charger limit has been set don't control + * sustainer again */ if (charger_sustainer_percentage() == 100) { charger_sustainer_reset(); } @@ -228,33 +233,35 @@ static enum ec_status battery_extender_hc(struct host_cmd_handler_args *args) r->disable = batt_extender_disable; if (!timestamp_expired(batt_extender_deadline, &now)) { - r->trigger_timedelta = batt_extender_deadline.val - now.val; + r->trigger_timedelta = + batt_extender_deadline.val - now.val; } else r->trigger_timedelta = 0; - r->trigger_days = (uint16_t)(battery_extender_trigger/DAY); - + r->trigger_days = (uint16_t)(battery_extender_trigger / DAY); if (!timestamp_expired(reset_deadline, &now)) { r->reset_timedelta = reset_deadline.val - now.val; } else r->reset_timedelta = 0; - r->reset_minutes = (uint16_t)(battery_extender_reset/MINUTE); + r->reset_minutes = + (uint16_t)(battery_extender_reset / MINUTE_US); args->response_size = sizeof(*r); return EC_SUCCESS; } else return EC_ERROR_PARAM1; } -DECLARE_HOST_COMMAND(EC_CMD_BATTERY_EXTENDER, battery_extender_hc, EC_VER_MASK(0)); +DECLARE_HOST_COMMAND(EC_CMD_BATTERY_EXTENDER, battery_extender_hc, + EC_VER_MASK(0)); /*****************************************************************************/ /* Host command */ -static enum ec_status cmd_charging_limit_control(struct host_cmd_handler_args *args) +static enum ec_status +cmd_charging_limit_control(struct host_cmd_handler_args *args) { - const struct ec_params_ec_chg_limit_control *p = args->params; struct ec_response_chg_limit_control *r = args->response; @@ -268,14 +275,21 @@ static enum ec_status cmd_charging_limit_control(struct host_cmd_handler_args *a return EC_RES_ERROR; charging_maximum_level = p->max_percentage; - system_set_bbram(SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX, charging_maximum_level); + system_set_bbram(SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX, + charging_maximum_level); + /* Reapply static limits without replacing an active extender + * cap. */ + if (stage == BATT_EXTENDER_STAGE_0) + old_charger_limit = 0; } if (p->modes & CHG_LIMIT_OVERRIDE) - charging_maximum_level = charging_maximum_level | CHG_LIMIT_OVERRIDE; + charging_maximum_level = charging_maximum_level | + CHG_LIMIT_OVERRIDE; if (p->modes & CHG_LIMIT_GET_LIMIT) { - system_get_bbram(SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX, &charging_maximum_level); + system_get_bbram(SYSTEM_BBRAM_IDX_CHARGE_LIMIT_MAX, + &charging_maximum_level); r->max_percentage = charging_maximum_level; args->response_size = sizeof(*r); } @@ -285,7 +299,7 @@ static enum ec_status cmd_charging_limit_control(struct host_cmd_handler_args *a return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_CHARGE_LIMIT_CONTROL, cmd_charging_limit_control, - EC_VER_MASK(0)); + EC_VER_MASK(0)); static uint64_t cmd_parse_timestamp(int argc, const char **argv) { @@ -295,11 +309,11 @@ static uint64_t cmd_parse_timestamp(int argc, const char **argv) if (argc >= 3) { time_val = strtoi(argv[2], &e, 0); if (!strncmp(argv[3], "s", 1)) { - time_val *= SECOND; - } else if (!strncmp(argv[3], "m", 1)) { - time_val *= MINUTE; - } else if (!strncmp(argv[3], "h", 1)) { - time_val *= HOUR; + time_val *= USEC_PER_SEC; + } else if (!strncmp(argv[3], "m", 1)) { + time_val *= MINUTE_US; + } else if (!strncmp(argv[3], "h", 1)) { + time_val *= HOUR_US; } else if (!strncmp(argv[3], "d", 1)) { time_val *= DAY; } else { @@ -318,9 +332,9 @@ static void print_time_offset(uint64_t t_end, uint64_t t_start) { uint64_t t = t_end - t_start; uint64_t d = TIMES2DAYS(t); - uint64_t h = (t % DAY) / HOUR; - uint64_t m = (t % HOUR) / MINUTE; - uint64_t s = (t % MINUTE) / SECOND; + uint64_t h = (t % DAY) / HOUR_US; + uint64_t m = (t % HOUR_US) / MINUTE_US; + uint64_t s = (t % MINUTE_US) / USEC_PER_SEC; if (t_end < t_start) { CPRINTF("Expired\n"); @@ -349,40 +363,49 @@ static int cmd_batt_extender(int argc, const char **argv) } else { if (battery_extender_reset) { reset_deadline.val = - now.val + battery_extender_reset; + now.val + + battery_extender_reset; } if (battery_extender_trigger != 0) { batt_extender_deadline.val = - now.val + battery_extender_trigger; + now.val + + battery_extender_trigger; batt_extender_deadline_stage2.val = - now.val + battery_extender_trigger + 2*DAY; + now.val + + battery_extender_trigger + + 2 * DAY; } } } else if (!strncmp(argv[1], "timeext2", 8)) { batt_extender_deadline_stage2.val = now.val + cmd_parse_timestamp(argc, argv); } else if (!strncmp(argv[1], "timeext", 7)) { - batt_extender_deadline.val = now.val + cmd_parse_timestamp(argc, argv); + batt_extender_deadline.val = + now.val + cmd_parse_timestamp(argc, argv); } else if (!strncmp(argv[1], "timerst", 7)) { - reset_deadline.val = now.val + cmd_parse_timestamp(argc, argv); + reset_deadline.val = + now.val + cmd_parse_timestamp(argc, argv); } else if (!strncmp(argv[1], "trigger", 7)) { if (argc < 3) return EC_ERROR_PARAM2; - battery_extender_trigger = cmd_parse_timestamp(argc, argv); + battery_extender_trigger = + cmd_parse_timestamp(argc, argv); CPRINTF("update battery extender trigger "); print_time_offset(battery_extender_trigger, 0); } else if (!strncmp(argv[1], "reset", 5)) { if (argc < 3) return EC_ERROR_PARAM2; - battery_extender_reset = cmd_parse_timestamp(argc, argv); + battery_extender_reset = + cmd_parse_timestamp(argc, argv); CPRINTF("update battery extender reset "); print_time_offset(battery_extender_reset, 0); } else return EC_ERROR_PARAM_COUNT; } else { - CPRINTF("Battery extender %sabled\n", batt_extender_disable ? "dis" : "en"); + CPRINTF("Battery extender %sabled\n", + batt_extender_disable ? "dis" : "en"); CPRINTF("\tTrigger:"); print_time_offset(battery_extender_trigger, 0); CPRINTF("\tReset:"); @@ -396,7 +419,8 @@ static int cmd_batt_extender(int argc, const char **argv) } if (batt_extender_deadline_stage2.val) { CPRINTF("\t - Stage 2 expires in: "); - print_time_offset(batt_extender_deadline_stage2.val, now.val); + print_time_offset(batt_extender_deadline_stage2.val, + now.val); } CPRINTF("\tBattery extender reset timer %sable\n", reset_deadline.val ? "en" : "dis"); @@ -413,12 +437,14 @@ static int cmd_batt_extender(int argc, const char **argv) sustainer_upper = MIN(87, sustainer_upper); } - CPRINTF("\tlower: %d, upper: %d\n", sustainer_lower, sustainer_upper); + CPRINTF("\tlower: %d, upper: %d\n", sustainer_lower, + sustainer_upper); CPRINTF("\tUser charge limit:%d\n", charging_maximum_level); } return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(battextender, cmd_batt_extender, - "[enable/disable/days/reset/manual][days:1-99][reset:1-9999][manual:1/0]", - "battery extender control"); +DECLARE_CONSOLE_COMMAND( + battextender, cmd_batt_extender, + "[enable/disable/days/reset/manual][days:1-99][reset:1-9999][manual:1/0]", + "battery extender control");