/* 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. * * Test lid switch. */ #include "battery_smart.h" #include "charge_state.h" #include "chipset.h" #include "common.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" #include "task.h" #include "test_util.h" #include "util.h" #define WAIT_CHARGER_TASK 600 #define BATTERY_DETACH_DELAY 35000 static int mock_chipset_state = CHIPSET_STATE_ON; static int is_shutdown; static int is_force_discharge; static int is_hibernated; static int override_voltage, override_current, override_usec; /* The simulation doesn't really hibernate, so we must reset this ourselves */ extern timestamp_t shutdown_target_time; static void reset_mocks(void) { mock_chipset_state = CHIPSET_STATE_ON; is_shutdown = is_force_discharge = is_hibernated = 0; override_voltage = override_current = override_usec = 0; shutdown_target_time.val = 0ULL; } int board_cut_off_battery(void) { return EC_SUCCESS; } void chipset_force_shutdown(enum chipset_shutdown_reason reason) { is_shutdown = 1; mock_chipset_state = CHIPSET_STATE_HARD_OFF; } int chipset_in_state(int state_mask) { return state_mask & mock_chipset_state; } int board_discharge_on_ac(int enabled) { is_force_discharge = enabled; return EC_SUCCESS; } void system_hibernate(int sec, int usec) { is_hibernated = 1; } int charger_profile_override(struct charge_state_data *curr) { if (override_voltage) curr->requested_voltage = override_voltage; if (override_current) curr->requested_current = override_current; if (override_usec) return override_usec; /* Don't let it sleep a whole minute when the AP is off */ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) return CHARGE_POLL_PERIOD_LONG; return 0; } static uint32_t meh; enum ec_status charger_profile_override_get_param(uint32_t param, uint32_t *value) { if (param == CS_PARAM_CUSTOM_PROFILE_MIN) { *value = meh; return EC_RES_SUCCESS; } return EC_RES_INVALID_PARAM; } enum ec_status charger_profile_override_set_param(uint32_t param, uint32_t value) { if (param == CS_PARAM_CUSTOM_PROFILE_MIN) { meh = value; return EC_RES_SUCCESS; } return EC_RES_INVALID_PARAM; } static int wait_charging_state(void) { enum charge_state state; task_wake(TASK_ID_CHARGER); msleep(WAIT_CHARGER_TASK); state = charge_get_state(); ccprintf("[CHARGING TEST] state = %d\n", state); return state; } static int charge_control(enum ec_charge_control_mode mode) { struct ec_params_charge_control params; params.mode = mode; return test_send_host_command(EC_CMD_CHARGE_CONTROL, 1, ¶ms, sizeof(params), NULL, 0); } /* Setup init condition */ static void test_setup(int on_ac) { const struct battery_info *bat_info = battery_get_info(); reset_mocks(); /* 50% of charge */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, 50); sb_write(SB_ABSOLUTE_STATE_OF_CHARGE, 50); /* full charge capacity in mAh */ sb_write(SB_FULL_CHARGE_CAPACITY, 0xf000); /* 25 degree Celsius */ sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(25)); /* battery pack voltage */ sb_write(SB_VOLTAGE, bat_info->voltage_normal); /* desired charging voltage/current */ sb_write(SB_CHARGING_VOLTAGE, bat_info->voltage_max); sb_write(SB_CHARGING_CURRENT, 4000); /* battery pack current is positive when charging */ if (on_ac) { sb_write(SB_CURRENT, 1000); gpio_set_level(GPIO_AC_PRESENT, 1); } else { sb_write(SB_CURRENT, -100); gpio_set_level(GPIO_AC_PRESENT, 0); } /* Reset the charger state to initial state */ charge_control(CHARGE_CONTROL_NORMAL); /* Let things stabilize */ wait_charging_state(); } /* Host Event helpers */ static int ev_is_set(int event) { return host_get_events() & EC_HOST_EVENT_MASK(event); } static int ev_is_clear(int event) { return !ev_is_set(event); } static void ev_clear(int event) { host_clear_events(EC_HOST_EVENT_MASK(event)); } static int test_charge_state(void) { enum charge_state state; uint32_t flags; /* On AC */ test_setup(1); ccprintf("[CHARGING TEST] AC on\n"); /* Detach battery, charging error */ ccprintf("[CHARGING TEST] Detach battery\n"); TEST_ASSERT(test_detach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR_FLAGS) == EC_SUCCESS); msleep(BATTERY_DETACH_DELAY); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_ERROR); /* Attach battery again, charging */ ccprintf("[CHARGING TEST] Attach battery\n"); test_attach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR_FLAGS); /* And changing full capacity should trigger a host event */ ev_clear(EC_HOST_EVENT_BATTERY); sb_write(SB_FULL_CHARGE_CAPACITY, 0xeff0); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY)); /* Unplug AC, discharging at 1000mAh */ ccprintf("[CHARGING TEST] AC off\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); flags = charge_get_flags(); TEST_ASSERT(!(flags & CHARGE_FLAG_EXTERNAL_POWER)); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Discharging waaaay overtemp is ignored */ ccprintf("[CHARGING TEST] AC off, batt temp = 0xffff\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, 0xffff); state = wait_charging_state(); TEST_ASSERT(!is_shutdown); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40)); /* Discharging overtemp */ ccprintf("[CHARGING TEST] AC off, batt temp = 90 C\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(90)); state = wait_charging_state(); sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_shutdown); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40)); /* Force idle */ ccprintf("[CHARGING TEST] AC on, force idle\n"); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); charge_control(CHARGE_CONTROL_IDLE); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(flags & CHARGE_FLAG_FORCE_IDLE); charge_control(CHARGE_CONTROL_NORMAL); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); /* Force discharge */ ccprintf("[CHARGING TEST] AC on, force discharge\n"); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); charge_control(CHARGE_CONTROL_DISCHARGE); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); TEST_ASSERT(is_force_discharge); charge_control(CHARGE_CONTROL_NORMAL); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); TEST_ASSERT(!is_force_discharge); return EC_SUCCESS; } static int test_low_battery(void) { test_setup(1); 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(CONFIG_BATTERY_CRITICAL_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); hook_notify(HOOK_CHIPSET_STARTUP); gpio_set_level(GPIO_AC_PRESENT, 0); is_hibernated = 0; sb_write(SB_CURRENT, -1000); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); mock_chipset_state = CHIPSET_STATE_SOFT_OFF; hook_notify(HOOK_CHIPSET_SHUTDOWN); wait_charging_state(); /* after a while, the EC should hibernate */ sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery shutdown S5\n"); is_hibernated = 0; sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10); wait_charging_state(); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); /* after a while, the EC should hibernate */ sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery AP shutdown\n"); is_shutdown = 0; mock_chipset_state = CHIPSET_STATE_ON; sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); wait_charging_state(); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); usleep(32 * SECOND); wait_charging_state(); TEST_ASSERT(is_shutdown); 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(CONFIG_BATTERY_CRITICAL_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; uint32_t flags; int state; /* Connect the AC */ test_setup(1); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Invalid or do-nothing commands first */ UART_INJECT("chg\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); UART_INJECT("chg blahblah\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); UART_INJECT("chg idle\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); UART_INJECT("chg idle blargh\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Now let's force idle on and off */ UART_INJECT("chg idle on\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(flags & CHARGE_FLAG_FORCE_IDLE); UART_INJECT("chg idle off\n"); wait_charging_state(); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* and the rest */ TEST_ASSERT(charge_get_state() == PWR_STATE_CHARGE); TEST_ASSERT(!charge_want_shutdown()); TEST_ASSERT(charge_get_percent() == 50); temp = 0; rv = charge_get_battery_temp(0, &temp); TEST_ASSERT(rv == EC_SUCCESS); TEST_ASSERT(K_TO_C(temp) == 25); return EC_SUCCESS; } #define CHG_OPT1 0x2000 #define CHG_OPT2 0x4000 static int test_hc_charge_state(void) { enum charge_state state; int i, rv, tmp; struct ec_params_charge_state params; struct ec_response_charge_state resp; /* Let's connect the AC again. */ test_setup(1); /* Initialize the charger options with some nonzero value */ TEST_ASSERT(charger_set_option(CHG_OPT1) == EC_SUCCESS); /* Get the state */ memset(&resp, 0, sizeof(resp)); params.cmd = CHARGE_STATE_CMD_GET_STATE; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); TEST_ASSERT(resp.get_state.ac); TEST_ASSERT(resp.get_state.chg_voltage); TEST_ASSERT(resp.get_state.chg_current); TEST_ASSERT(resp.get_state.chg_input_current); TEST_ASSERT(resp.get_state.batt_state_of_charge); /* Check all the params */ for (i = 0; i < CS_NUM_BASE_PARAMS; i++) { /* Read it */ memset(&resp, 0, sizeof(resp)); params.cmd = CHARGE_STATE_CMD_GET_PARAM; params.get_param.param = i; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); if (i != CS_PARAM_LIMIT_POWER) TEST_ASSERT(resp.get_param.value); else TEST_ASSERT(!resp.get_param.value); /* Bump it up a bit */ tmp = resp.get_param.value; switch (i) { case CS_PARAM_CHG_VOLTAGE: case CS_PARAM_CHG_CURRENT: case CS_PARAM_CHG_INPUT_CURRENT: tmp -= 128; /* Should be valid delta */ break; case CS_PARAM_CHG_STATUS: case CS_PARAM_LIMIT_POWER: /* These ones can't be set */ break; case CS_PARAM_CHG_OPTION: tmp = CHG_OPT2; break; } params.cmd = CHARGE_STATE_CMD_SET_PARAM; params.set_param.param = i; params.set_param.value = tmp; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); if (i == CS_PARAM_CHG_STATUS || i == CS_PARAM_LIMIT_POWER) TEST_ASSERT(rv == EC_RES_ACCESS_DENIED); else TEST_ASSERT(rv == EC_RES_SUCCESS); /* Allow the change to take effect */ state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); /* Read it back again*/ memset(&resp, 0, sizeof(resp)); params.cmd = CHARGE_STATE_CMD_GET_PARAM; params.get_param.param = i; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); TEST_ASSERT(resp.get_param.value == tmp); } /* And a custom profile param */ meh = 0xdeadbeef; memset(&resp, 0, sizeof(resp)); params.cmd = CHARGE_STATE_CMD_GET_PARAM; params.get_param.param = CS_PARAM_CUSTOM_PROFILE_MIN; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); TEST_ASSERT(resp.get_param.value == meh); params.cmd = CHARGE_STATE_CMD_SET_PARAM; params.set_param.param = CS_PARAM_CUSTOM_PROFILE_MIN; params.set_param.value = 0xc0def00d; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); /* Allow the change to take effect */ state = wait_charging_state(); TEST_ASSERT(meh == params.set_param.value); /* param out of range */ params.cmd = CHARGE_STATE_CMD_GET_PARAM; params.get_param.param = CS_NUM_BASE_PARAMS; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_INVALID_PARAM); params.cmd = CHARGE_STATE_CMD_SET_PARAM; params.set_param.param = CS_NUM_BASE_PARAMS; params.set_param.value = 0x1000; /* random value */ rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_INVALID_PARAM); /* command out of range */ params.cmd = CHARGE_STATE_NUM_CMDS; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); TEST_ASSERT(rv == EC_RES_INVALID_PARAM); /* * We've screwed with the charger settings, so let the state machine * reset itself before we stop. */ test_setup(0); test_setup(1); return EC_SUCCESS; } static int test_hc_current_limit(void) { int rv, norm_current, lower_current; struct ec_params_charge_state cs_params; struct ec_response_charge_state cs_resp; struct ec_params_current_limit cl_params; /* On AC */ test_setup(1); /* See what current the charger is delivering */ cs_params.cmd = CHARGE_STATE_CMD_GET_STATE; rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, &cs_params, sizeof(cs_params), &cs_resp, sizeof(cs_resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); norm_current = cs_resp.get_state.chg_current; /* Lower it a bit */ lower_current = norm_current - 256; cl_params.limit = lower_current; rv = test_send_host_command(EC_CMD_CHARGE_CURRENT_LIMIT, 0, &cl_params, sizeof(cl_params), 0, 0); TEST_ASSERT(rv == EC_RES_SUCCESS); wait_charging_state(); /* See that it's changed */ rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, &cs_params, sizeof(cs_params), &cs_resp, sizeof(cs_resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); TEST_ASSERT(lower_current == cs_resp.get_state.chg_current); /* Remove the limit */ cl_params.limit = -1U; rv = test_send_host_command(EC_CMD_CHARGE_CURRENT_LIMIT, 0, &cl_params, sizeof(cl_params), 0, 0); TEST_ASSERT(rv == EC_RES_SUCCESS); wait_charging_state(); /* See that it's back */ rv = test_send_host_command(EC_CMD_CHARGE_STATE, 0, &cs_params, sizeof(cs_params), &cs_resp, sizeof(cs_resp)); TEST_ASSERT(rv == EC_RES_SUCCESS); TEST_ASSERT(norm_current == cs_resp.get_state.chg_current); return EC_SUCCESS; } static int test_low_battery_hostevents(void) { int state; test_setup(0); ccprintf("[CHARGING TEST] Low battery host events\n"); /* You know you make me wanna */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_LOW + 1); ev_clear(EC_HOST_EVENT_BATTERY_LOW); ev_clear(EC_HOST_EVENT_BATTERY_CRITICAL); ev_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_LOW - 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_CRITICAL + 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_CRITICAL - 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_SHUTDOWN + 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_SHUTDOWN - 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_CRITICAL)); /* hey-hey-HEY-hey. Doesn't immediately shut down */ TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* after a while, the AP should shut down */ sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_shutdown); return EC_SUCCESS; } 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); RUN_TEST(test_low_battery_hostevents); test_print_result(); }