summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/charge_state_v2.c55
-rw-r--r--include/charge_state.h2
-rw-r--r--test/sbs_charging_v2.c33
3 files changed, 71 insertions, 19 deletions
diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c
index 74d8e910f5..531cc8bf83 100644
--- a/common/charge_state_v2.c
+++ b/common/charge_state_v2.c
@@ -29,6 +29,7 @@
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
#define LOW_BATTERY_SHUTDOWN_TIMEOUT_US (LOW_BATTERY_SHUTDOWN_TIMEOUT * SECOND)
+#define HIGH_TEMP_SHUTDOWN_TIMEOUT_US (HIGH_TEMP_SHUTDOWN_TIMEOUT * SECOND)
#define PRECHARGE_TIMEOUT_US (PRECHARGE_TIMEOUT * SECOND)
#define LFCC_EVENT_THRESH 5 /* Full-capacity change reqd for host event */
@@ -43,6 +44,7 @@ static int state_machine_force_idle;
static int manual_mode; /* volt/curr are no longer maintained by charger */
static unsigned int user_current_limit = -1U;
test_export_static timestamp_t shutdown_warning_time;
+test_export_static timestamp_t shutdown_batttemp_warning_time;
static timestamp_t precharge_start_time;
static int battery_seems_to_be_dead;
static int battery_seems_to_be_disconnected;
@@ -360,23 +362,19 @@ static int charge_force_idle(int enable)
return EC_SUCCESS;
}
+/* True if we know the battery temp is too high or too low */
+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 ||
+ batt_temp_c < batt_info->discharging_min_c));
+}
+
static void prevent_hot_discharge(void)
{
int batt_temp_c;
- /* If the AP is off already, the thermal task should handle it. */
- if (!chipset_in_state(CHIPSET_STATE_ON))
- return;
-
- /* Same if we can't read the battery temp. */
- if (curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE)
- return;
-
/*
- * TODO(crosbug.com/p/27641): Shouldn't we do this in stages, like
- * prevent_deep_discharge()? Send an event, give the AP time to react,
- * maybe even hibernate the EC if things are really bad?
- *
* TODO(crosbug.com/p/27642): The thermal loop should watch the battery
* temp anyway, so it can turn fans on. It could also force an AP
* shutdown if it's too hot, but AFAIK we don't have anything in place
@@ -384,12 +382,35 @@ static void prevent_hot_discharge(void)
* should, just in case.
*/
batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature);
- if (batt_temp_c > batt_info->discharging_max_c ||
- batt_temp_c < batt_info->discharging_min_c) {
- CPRINTS("charge force shutdown due to battery temp %dC",
+
+ if (!battery_too_hot(batt_temp_c)) {
+ /* Reset shutdown warning time */
+ shutdown_batttemp_warning_time.val = 0;
+ return;
+ }
+
+ CPRINTS("Batt temp out of range %dC", batt_temp_c);
+
+ if (!shutdown_batttemp_warning_time.val) {
+ CPRINTS("charge warn shutdown due to battery temp %dC",
batt_temp_c);
- chipset_force_shutdown();
- host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
+ shutdown_batttemp_warning_time.val = get_time().val;
+ if (!chipset_in_state(CHIPSET_STATE_ANY_OFF))
+ host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
+ } else if (get_time().val > shutdown_batttemp_warning_time.val +
+ HIGH_TEMP_SHUTDOWN_TIMEOUT_US) {
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+#ifdef CONFIG_HIBERNATE
+ /* Timeout waiting for temp to change */
+ CPRINTS("charge force EC hib due to batt temp %dC",
+ batt_temp_c);
+ system_hibernate(0, 0);
+#endif
+ } else {
+ CPRINTS("charge force shutdown due to batt temp %dC",
+ batt_temp_c);
+ chipset_force_shutdown();
+ }
}
}
diff --git a/include/charge_state.h b/include/charge_state.h
index c23e70c260..5c81decf10 100644
--- a/include/charge_state.h
+++ b/include/charge_state.h
@@ -12,6 +12,8 @@
/* Seconds after AP battery shutdown warning before we kill the AP */
#define LOW_BATTERY_SHUTDOWN_TIMEOUT 30
+#define HIGH_TEMP_SHUTDOWN_TIMEOUT 30
+
/* Seconds to spend trying to wake a non-responsive battery */
#define PRECHARGE_TIMEOUT 30
diff --git a/test/sbs_charging_v2.c b/test/sbs_charging_v2.c
index bb15a8d18b..bb5097948c 100644
--- a/test/sbs_charging_v2.c
+++ b/test/sbs_charging_v2.c
@@ -222,6 +222,7 @@ static int test_charge_state(void)
TEST_ASSERT(state == PWR_STATE_DISCHARGE);
sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(90));
state = wait_charging_state();
+ sleep(HIGH_TEMP_SHUTDOWN_TIMEOUT);
TEST_ASSERT(is_shutdown);
TEST_ASSERT(state == PWR_STATE_DISCHARGE);
sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40));
@@ -265,14 +266,20 @@ static int test_low_battery(void)
{
test_setup(1);
- ccprintf("[CHARGING TEST] Low battery with AC\n");
-
+ ccprintf("[CHARGING TEST] Low battery with AC and positive current\n");
sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
+ sb_write(SB_CURRENT, 1000);
wait_charging_state();
mock_chipset_state = CHIPSET_STATE_SOFT_OFF;
hook_notify(HOOK_CHIPSET_SHUTDOWN);
TEST_ASSERT(!is_hibernated);
+ ccprintf("[CHARGING TEST] Low battery with AC and negative current\n");
+ sb_write(SB_CURRENT, -1000);
+ wait_charging_state();
+ sleep(LOW_BATTERY_SHUTDOWN_TIMEOUT);
+ TEST_ASSERT(is_hibernated);
+
ccprintf("[CHARGING TEST] Low battery shutdown S0->S5\n");
mock_chipset_state = CHIPSET_STATE_ON;
hook_notify(HOOK_CHIPSET_PRE_INIT);
@@ -317,6 +324,27 @@ static int test_low_battery(void)
return EC_SUCCESS;
}
+static int test_high_temp_battery(void)
+{
+ test_setup(1);
+
+ ccprintf("[CHARGING TEST] High battery temperature shutdown\n");
+ ev_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN);
+ sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(90));
+ wait_charging_state();
+ TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_SHUTDOWN));
+ TEST_ASSERT(!is_shutdown);
+ sleep(HIGH_TEMP_SHUTDOWN_TIMEOUT);
+ TEST_ASSERT(is_shutdown);
+
+ ccprintf("[CHARGING TEST] High battery temp S0->S5 hibernate\n");
+ mock_chipset_state = CHIPSET_STATE_SOFT_OFF;
+ wait_charging_state();
+ TEST_ASSERT(is_hibernated);
+
+ return EC_SUCCESS;
+}
+
static int test_external_funcs(void)
{
int rv, temp;
@@ -650,6 +678,7 @@ void run_test(void)
{
RUN_TEST(test_charge_state);
RUN_TEST(test_low_battery);
+ RUN_TEST(test_high_temp_battery);
RUN_TEST(test_external_funcs);
RUN_TEST(test_hc_charge_state);
RUN_TEST(test_hc_current_limit);