diff options
-rw-r--r-- | board/samus/board.c | 9 | ||||
-rw-r--r-- | board/samus/board.h | 9 | ||||
-rw-r--r-- | common/charge_state_v2.c | 717 | ||||
-rw-r--r-- | driver/battery/samus.c | 129 | ||||
-rw-r--r-- | driver/battery/smart.c | 4 | ||||
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | include/charge_state_v2.h | 38 | ||||
-rw-r--r-- | include/config.h | 5 | ||||
-rw-r--r-- | test/battery_get_params_smart.c | 87 | ||||
-rw-r--r-- | test/battery_get_params_smart.tasklist | 17 | ||||
-rw-r--r-- | test/build.mk | 4 | ||||
-rw-r--r-- | test/sbs_charging_v2.c | 221 | ||||
-rw-r--r-- | test/sbs_charging_v2.tasklist | 19 | ||||
-rw-r--r-- | test/test_config.h | 22 |
14 files changed, 1266 insertions, 16 deletions
diff --git a/board/samus/board.c b/board/samus/board.c index cfab3e9f16..9e203c19da 100644 --- a/board/samus/board.c +++ b/board/samus/board.c @@ -10,6 +10,7 @@ #include "backlight.h" #include "battery.h" #include "capsense.h" +#include "charger.h" #include "common.h" #include "driver/temp_sensor/tmp006.h" #include "driver/als_isl29035.h" @@ -332,3 +333,11 @@ enum battery_present battery_is_present(void) return analog_val < (9 * ADC_READ_MAX / 10) ? BP_YES : BP_NO; } #endif + +/** + * Discharge battery when on AC power for factory test. + */ +int board_discharge_on_ac(int enable) +{ + return charger_discharge_on_ac(enable); +} diff --git a/board/samus/board.h b/board/samus/board.h index ed0aa4005a..c69ff0959b 100644 --- a/board/samus/board.h +++ b/board/samus/board.h @@ -30,12 +30,14 @@ #define CONFIG_POWER_BUTTON_X86 #define CONFIG_BACKLIGHT_REQ_GPIO GPIO_PCH_BL_EN -#define CONFIG_BATTERY_LINK +#define CONFIG_BATTERY_SAMUS +#define CONFIG_CHARGER_PROFILE_OVERRIDE #define CONFIG_BATTERY_PRESENT_CUSTOM #define CONFIG_BATTERY_SMART #define CONFIG_CHARGER -#define CONFIG_CHARGER_V1 +#define CONFIG_CHARGER_V2 #define CONFIG_CHARGER_BQ24715 +#define CONFIG_CHARGER_DISCHARGE_ON_AC /* 10mOhm sense resitors. */ #define CONFIG_CHARGER_SENSE_RESISTOR 10 #define CONFIG_CHARGER_SENSE_RESISTOR_AC 10 @@ -245,6 +247,9 @@ enum board_version { #define WIRELESS_GPIO_WWAN GPIO_PP3300_LTE_EN #define WIRELESS_GPIO_WLAN_POWER GPIO_PP3300_WLAN_EN +/* Discharge battery when on AC power for factory test. */ +int board_discharge_on_ac(int enable); + #endif /* !__ASSEMBLER__ */ #endif /* __BOARD_H */ diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c index 1f0d4d6350..e28a31c3ad 100644 --- a/common/charge_state_v2.c +++ b/common/charge_state_v2.c @@ -15,6 +15,7 @@ #include "gpio.h" #include "hooks.h" #include "host_command.h" +#include "math_util.h" #include "printf.h" #include "system.h" #include "task.h" @@ -25,42 +26,736 @@ #define CPUTS(outstr) cputs(CC_CHARGER, outstr) #define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) +/* Delay after AP battery shutdown warning before we kill the AP */ +#define LOW_BATTERY_SHUTDOWN_TIMEOUT_US (30 * SECOND) +/* Time to spend trying to wake a non-responsive battery */ +#define PRECHARGE_TIMEOUT_US (30 * SECOND) +/* Power state task polling periods in usec */ +#define POLL_PERIOD_VERY_LONG MINUTE +#define POLL_PERIOD_LONG (MSEC * 500) +#define POLL_PERIOD_CHARGE (MSEC * 250) +#define POLL_PERIOD_SHORT (MSEC * 100) +#define MIN_SLEEP_USEC (MSEC * 50) +#define MAX_SLEEP_USEC MINUTE -void charger_task(void) +/* + * 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 int prev_ac, prev_volt, prev_curr, prev_charge; +static int state_machine_force_idle; +static unsigned int user_current_limit = -1U; +static timestamp_t shutdown_warning_time, precharge_start_time; +static int battery_seems_to_be_dead; +static int problems_exist; + +/* Track problems in communicating with the battery or charger */ +enum problem_type { + PR_STATIC_UPDATE, + PR_SET_VOLTAGE, + PR_SET_CURRENT, + PR_POST_INIT, + PR_CHG_FLAGS, + PR_BATT_FLAGS, + PR_CUSTOM, + + NUM_PROBLEM_TYPES +}; + +/* + * TODO(wfrichar): 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) +{ + ccprintf("[%T %s: problem %d, value 0x%x]\n", __FILE__, p, v); + problems_exist = 1; +} + +/* Returns zero if every item was updated. */ +static int update_static_battery_info(void) +{ + char *batt_str; + int batt_serial; + /* + * 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; + *host_get_memmap(EC_MEMMAP_BATT_FLAG) = 0; + + if (rv) + problem(PR_STATIC_UPDATE, 0); + 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 cap_changed; + + tmp = 0; + if (curr.ac) + tmp |= EC_BATT_FLAG_AC_PRESENT; + + if (curr.batt.is_present == BP_YES) + tmp |= EC_BATT_FLAG_BATT_PRESENT; + + 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)) + *memmap_cap = curr.batt.remaining_capacity; + + cap_changed = 0; + if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) && + curr.batt.full_capacity != *memmap_lfcc) { + *memmap_lfcc = curr.batt.full_capacity; + cap_changed = 1; + } + + 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; + + switch (curr.state) { + case ST_DISCHARGE: + tmp |= EC_BATT_FLAG_DISCHARGING; + break; + case ST_CHARGE: + tmp |= EC_BATT_FLAG_CHARGING; + break; + default: + /* neither charging nor discharging */ + break; + } + + *memmap_flags = tmp; + + /* Poke the AP if the full_capacity changes. */ + if (cap_changed) + host_set_single_event(EC_HOST_EVENT_BATTERY); +} + +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", +}; + +static void dump_charge_state(void) +{ +#define DUMP(FLD, FMT) ccprintf(" " #FLD " = " FMT "\n", curr.FLD) + ccprintf(" state = %s\n", state_list[curr.state]); + DUMP(ac, "%d"); + 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"); + ccprintf(" batt.temperature = %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(" batt.is_present = %s\n", batt_pres[curr.batt.is_present]); + DUMP(requested_voltage, "%dmV"); + DUMP(requested_current, "%dmA"); + ccprintf(" force_idle = %d\n", state_machine_force_idle); + ccprintf(" user_current_limit = %dmA\n", user_current_limit); + ccprintf(" battery_seems_to_be_dead = %d\n", battery_seems_to_be_dead); +#undef DUMP +} + +static void show_charging_progress(void) +{ + int rv, minutes, to_full; + + if (curr.state == ST_IDLE || + curr.state == ST_DISCHARGE) { + rv = battery_time_to_empty(&minutes); + to_full = 0; + } else { + rv = battery_time_to_full(&minutes); + to_full = 1; + } + + if (rv) + CPRINTF("[%T Battery %d%% / ??h:?? %s]\n", + curr.batt.state_of_charge, + to_full ? "to full" : "to empty"); + else + CPRINTF("[%T Battery %d%% / %dh:%d %s]\n", + curr.batt.state_of_charge, + minutes / 60, minutes % 60, + to_full ? "to full" : "to empty"); +} + +/* + * Ask the charger for some voltage and current. If either value is 0, + * charging is disabled; otherwise it's enabled. + */ +static int charge_request(int voltage, int current) +{ + int r1, r2; + + if (!voltage || !current) + /* TODO(wfrichar): should we call charger_set_mode() too? */ + voltage = current = 0; + + CPRINTF("[%T %s(%dmV, %dmA)]\n", __func__, voltage, current); + + r1 = charger_set_voltage(voltage); + if (r1 != EC_SUCCESS) + problem(PR_SET_VOLTAGE, r1); + + r2 = charger_set_current(current); + if (r2 != EC_SUCCESS) + problem(PR_SET_CURRENT, r2); + + return r1 ? r1 : r2; +} + + +/* Force charging off before the battery is full. */ +static int charge_force_idle(int enable) +{ + /* + * Force idle is only meaningful if external power is + * present. If it's not present we can't charge anyway. + */ + if (enable && !curr.ac) + return EC_ERROR_NOT_POWERED; + + state_machine_force_idle = enable; + return EC_SUCCESS; +} + +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(wfrichar): 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(wfrichar): 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 to do a + * battery shutdown if it's really really hot. We probably should, just + * in case. + * + * TODO(wfrichar): It'd also be nice if we had a persistent error log + * for that somewhere too. It would have to be in the EC's eeprom and + * not in the nvram if we're going to cut the battery. + */ + 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) { + CPRINTF("[%T charge force shutdown due to battery temp %dC]\n", + batt_temp_c); + chipset_force_shutdown(); + host_set_single_event(EC_HOST_EVENT_BATTERY_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) { - while (1) - task_wait_event(-1); + 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)); } +/* Shut everything down before the battery completely dies. */ +static void prevent_deep_discharge(void) +{ + if (!battery_too_low()) + return; + + if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { + /* AP is off, so shut down the EC now */ + CPRINTF("[%T charge force EC hibernate due to low battery]\n"); + system_hibernate(0, 0); + } else if (!shutdown_warning_time.val) { + /* Warn AP battery level is so low we'll shut down */ + CPRINTF("[%T charge warn shutdown due to low battery]\n"); + shutdown_warning_time = get_time(); + host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN); + } else if (get_time().val > shutdown_warning_time.val + + LOW_BATTERY_SHUTDOWN_TIMEOUT_US) { + /* Timeout waiting for AP to shut down, so kill it */ + CPRINTF("[%T charge force shutdown due to low battery]\n"); + chipset_force_shutdown(); + } +} -int charge_keep_power_off(void) +/* + * 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(void) { - return 0; + /* We can't tell what the current charge is. Assume it's okay. */ + if (curr.batt.flags && BATT_FLAG_BAD_STATE_OF_CHARGE) + return; + + 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); +} + +/* Main loop */ +void charger_task(void) +{ + int sleep_usec; + int need_static = 1; + + /* Get the battery-specific values */ + batt_info = battery_get_info(); + + /* Initialize all the state */ + memset(&curr, 0, sizeof(curr)); + curr.batt.is_present = BP_NOT_SURE; + prev_ac = prev_volt = prev_curr = prev_charge = -1; + state_machine_force_idle = 0; + shutdown_warning_time.val = 0UL; + battery_seems_to_be_dead = 0; + + while (1) { + + /* Let's see what's going on... */ + curr.ts = get_time(); + sleep_usec = 0; + problems_exist = 0; + curr.ac = extpower_is_present(); + if (curr.ac != prev_ac) { + if (curr.ac) { + /* + * Some chargers are unpowered when the AC is + * off, so we'll reinitialize it when AC + * comes back. Try again if it fails. + */ + int rv = charger_post_init(); + if (rv != EC_SUCCESS) + problem(PR_POST_INIT, rv); + else + prev_ac = curr.ac; + } else { + /* Some things are only meaningful on AC */ + state_machine_force_idle = 0; + battery_seems_to_be_dead = 0; + prev_ac = curr.ac; + } + } + charger_get_params(&curr.chg); + battery_get_params(&curr.batt); + + /* + * 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. + */ + 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) { + ASSERT(curr.ac); /* How are we running? */ + curr.state = ST_IDLE; + 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 (!curr.ac) { + curr.state = ST_DISCHARGE; + /* Don't let the battery hurt itself. */ + prevent_hot_discharge(); + prevent_deep_discharge(); + goto wait_for_it; + } + + /* Okay, we're on AC and we should have a battery. */ + + /* Needed for factory tests. + * + * TODO(wfrichar): But see chrome-os-partner:26418. Can we do + * away with state_machine_force_idle if DPTF current=0 does + * the same thing? + */ + if (state_machine_force_idle) { + curr.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)) { + /* If we've already tried (or for too long) */ + if (battery_seems_to_be_dead || + (curr.state == ST_PRECHARGE && + (get_time().val > precharge_start_time.val + + PRECHARGE_TIMEOUT_US))) { + /* Then do nothing */ + battery_seems_to_be_dead = 1; + curr.state = ST_IDLE; + curr.requested_voltage = 0; + curr.requested_current = 0; + } else { + /* See if we can wake it up */ + if (curr.state != ST_PRECHARGE) + precharge_start_time = get_time(); + curr.state = ST_PRECHARGE; + curr.requested_voltage = + batt_info->voltage_max; + curr.requested_current = + batt_info->precharge_current; + } + goto wait_for_it; + } else { + /* The battery is responding. Yay. Try to use it. */ + battery_seems_to_be_dead = 0; + curr.state = ST_CHARGE; + } + + if (curr.batt.state_of_charge >= BATTERY_LEVEL_FULL) { + /* Full up. Stop charging */ + curr.state = ST_IDLE; + goto wait_for_it; + } + + /* + * TODO(wfrichar): Quit trying if charging too long without + * getting full. See CONFIG_CHARGER_TIMEOUT_HOURS, + * chrome-os-partner:20145 + */ + +#ifdef CONFIG_CHARGER_PROFILE_OVERRIDE + sleep_usec = charger_profile_override(&curr); + if (sleep_usec < 0) + problem(PR_CUSTOM, sleep_usec); +#endif + +wait_for_it: + /* 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(); + + /* And the EC console */ + if (!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && + curr.batt.state_of_charge != prev_charge) { + show_charging_progress(); + prev_charge = curr.batt.state_of_charge; + } + + /* 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; + } + + /* 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); + + /* + * Only update the charger when something changes so that + * temporary overrides are possible through console commands. + */ + if ((prev_volt != curr.requested_voltage || + prev_curr != curr.requested_current) && + EC_SUCCESS == charge_request(curr.requested_voltage, + curr.requested_current)) { + /* + * Only update if the request worked, so we'll keep + * trying on failures. + */ + prev_volt = curr.requested_voltage; + prev_curr = curr.requested_current; + } + + /* How long to sleep? */ + if (problems_exist) + /* If there are errors, don't wait very long. */ + sleep_usec = POLL_PERIOD_SHORT; + else if (sleep_usec <= 0) { + /* default values depend on the state */ + if (curr.state == ST_IDLE || + curr.state == ST_DISCHARGE) { + /* If AP is off, we can sleep a long time */ + if (chipset_in_state(CHIPSET_STATE_ANY_OFF | + CHIPSET_STATE_SUSPEND)) + sleep_usec = POLL_PERIOD_VERY_LONG; + else + /* Discharging, not too urgent */ + sleep_usec = POLL_PERIOD_LONG; + } else { + /* Charging, so pay closer attention */ + sleep_usec = POLL_PERIOD_CHARGE; + } + } + + /* Adjust for time spent in this loop */ + sleep_usec -= (int)(get_time().val - curr.ts.val); + if (sleep_usec < MIN_SLEEP_USEC) + sleep_usec = MIN_SLEEP_USEC; + else if (sleep_usec > MAX_SLEEP_USEC) + sleep_usec = MAX_SLEEP_USEC; + + 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); +} + enum charge_state charge_get_state(void) { - return PWR_STATE_INIT; + switch (curr.state) { + case ST_IDLE: + if (battery_seems_to_be_dead) + return PWR_STATE_ERROR; + return PWR_STATE_IDLE; + case ST_DISCHARGE: + return PWR_STATE_DISCHARGE; + case ST_CHARGE: + /* The only difference here is what the LEDs display. */ + if (curr.batt.state_of_charge >= BATTERY_LEVEL_NEAR_FULL) + return PWR_STATE_CHARGE_NEAR_FULL; + else + return PWR_STATE_CHARGE; + default: + /* Anything else can be considered an error for LED purposes */ + return PWR_STATE_ERROR; + } } uint32_t charge_get_flags(void) { - return 0; + uint32_t flags = 0; + + if (state_machine_force_idle) + flags |= CHARGE_FLAG_FORCE_IDLE; + if (curr.ac) + flags |= CHARGE_FLAG_EXTERNAL_POWER; + + return flags; } int charge_get_percent(void) { - return 0; + /* + * 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 curr.batt.state_of_charge; } int charge_temp_sensor_get_val(int idx, int *temp_ptr) { - return EC_ERROR_UNKNOWN; + 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; +} + +/*****************************************************************************/ +/* Hooks */ + +/* Wake up the task when something important happens */ +static void charge_wakeup(void) +{ + task_wake(TASK_ID_CHARGER); } +DECLARE_HOOK(HOOK_CHIPSET_RESUME, charge_wakeup, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_AC_CHANGE, charge_wakeup, HOOK_PRIO_DEFAULT); -int charge_want_shutdown(void) +/*****************************************************************************/ +/* Host commands */ + +static int charge_command_charge_control(struct host_cmd_handler_args *args) +{ + const struct ec_params_charge_control *p = args->params; + int rv; + + if (system_is_locked()) + return EC_RES_ACCESS_DENIED; + + rv = charge_force_idle(p->mode != CHARGE_CONTROL_NORMAL); + if (rv != EC_SUCCESS) + return rv; + +#ifdef CONFIG_CHARGER_DISCHARGE_ON_AC + rv = board_discharge_on_ac(p->mode == CHARGE_CONTROL_DISCHARGE); + if (rv != EC_SUCCESS) + return rv; +#endif + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_CHARGE_CONTROL, charge_command_charge_control, + EC_VER_MASK(1)); + +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 int charge_command_current_limit(struct host_cmd_handler_args *args) { - return 0; + 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)); + +/*****************************************************************************/ +/* Console commands */ + +static int command_chgstate(int argc, char **argv) +{ + int rv; + int val; + + 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 = charge_force_idle(val); + if (rv) + return rv; + } else { + /* maybe handle board_discharge_on_ac() too? */ + return EC_ERROR_PARAM1; + } + } + + dump_charge_state(); + return EC_SUCCESS; } +DECLARE_CONSOLE_COMMAND(chgstate, command_chgstate, + "[idle on|off]", + "Get/set charge state machine status", + NULL); diff --git a/driver/battery/samus.c b/driver/battery/samus.c new file mode 100644 index 0000000000..d99a2397a6 --- /dev/null +++ b/driver/battery/samus.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2012 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 pack vendor provided charging profile + */ + +#include "charge_state.h" +#include "console.h" +#include "util.h" + +static const struct battery_info info = { + /* + * Design voltage + * max = 8.4V + * normal = 7.4V + * min = 6.0V + */ + .voltage_max = 8400, + .voltage_normal = 7400, + .voltage_min = 6000, + + /* Pre-charge current: I <= 0.01C */ + .precharge_current = 64, /* mA */ + + /* + * Operational temperature range + * 0 <= T_charge <= 50 deg C + * -20 <= T_discharge <= 60 deg C + */ + .start_charging_min_c = 0, + .start_charging_max_c = 50, + .charging_min_c = 0, + .charging_max_c = 50, + .discharging_min_c = -20, + .discharging_max_c = 60, +}; + +const struct battery_info *battery_get_info(void) +{ + return &info; +} + +#ifdef CONFIG_CHARGER_PROFILE_OVERRIDE + +/* + * For Samus, we'd like to set the CONFIG_CHARGER_INPUT_CURRENT to a higher + * value, but the AC adapters freak out if we do. So instead we set it to a + * low value, and it gets reset to that point every time AC is applied. Then we + * bump it up a little bit every time through the loop until it's where we + * wanted it in the first place. + */ +#define MAX_INPUT_CURRENT 3200 +#define INPUT_CURRENT_INCR 64 + +static int fast_charging_allowed; + +/* + * This can override the smart battery's charging profile. To make a change, + * modify one or more of requested_voltage, requested_current, or state. + * Leave everything else unchanged. + * + * Return the next poll period in usec, or zero to use the default (which is + * state dependent). + */ +int charger_profile_override(struct charge_state_data *curr) +{ + int rv; + + /* We only want to override how we charge, nothing else. */ + if (curr->state != ST_CHARGE) + return 0; + + /* Bump the input current up a little at a time if needed. */ + if (curr->chg.input_current < MAX_INPUT_CURRENT) { + rv = charger_set_input_current(curr->chg.input_current + + INPUT_CURRENT_INCR); + /* + * If we can't set the input current, indicate the error + * (negative, since positive changes the poll period) and + * don't override the default behavior. + */ + if (rv) + return -rv; + } + + /* Do we want to mess with the charge profile too? */ + if (!fast_charging_allowed) + return 0; + + /* Okay, impose our custom will */ + curr->requested_current = 9000; + curr->requested_voltage = 8300; + if (curr->batt.current <= 6300) { + curr->requested_current = 6300; + curr->requested_voltage = 8400; + } else if (curr->batt.current <= 4500) { + curr->requested_current = 4500; + curr->requested_voltage = 8500; + } else if (curr->batt.current <= 2700) { + curr->requested_current = 2700; + curr->requested_voltage = 8700; + } else if (curr->batt.current <= 475) { + /* + * Should we stop? If so, how do we start again? + * For now, just use the battery's profile. + */ + curr->requested_current = curr->batt.desired_current; + curr->requested_voltage = curr->batt.desired_voltage; + } + + return 0; +} + +static int command_fastcharge(int argc, char **argv) +{ + if (argc > 1 && !parse_bool(argv[1], &fast_charging_allowed)) + return EC_ERROR_PARAM1; + + ccprintf("fastcharge %s\n", fast_charging_allowed ? "on" : "off"); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(fastcharge, command_fastcharge, + "[on|off]", + "Get or set fast charging profile", + NULL); + +#endif /* CONFIG_CHARGER_PROFILE_OVERRIDE */ diff --git a/driver/battery/smart.c b/driver/battery/smart.c index 90b566009b..976b7b7af5 100644 --- a/driver/battery/smart.c +++ b/driver/battery/smart.c @@ -22,12 +22,12 @@ test_mockable int sbc_write(int cmd, int param) return i2c_write16(I2C_PORT_CHARGER, CHARGER_ADDR, cmd, param); } -int sb_read(int cmd, int *param) +test_mockable int sb_read(int cmd, int *param) { return i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, cmd, param); } -int sb_write(int cmd, int param) +test_mockable int sb_write(int cmd, int param) { return i2c_write16(I2C_PORT_BATTERY, BATTERY_ADDR, cmd, param); } diff --git a/driver/build.mk b/driver/build.mk index ab637f03e2..b8e3c92929 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -16,6 +16,7 @@ driver-$(CONFIG_ALS_ISL29035)+=als_isl29035.o driver-$(CONFIG_BATTERY_BQ20Z453)+=battery/bq20z453.o driver-$(CONFIG_BATTERY_BQ27541)+=battery/bq27541.o driver-$(CONFIG_BATTERY_LINK)+=battery/link.o +driver-$(CONFIG_BATTERY_SAMUS)+=battery/samus.o driver-$(CONFIG_BATTERY_SMART)+=battery/smart.o # Battery charger ICs diff --git a/include/charge_state_v2.h b/include/charge_state_v2.h index 085f2a2047..16d8ee61ad 100644 --- a/include/charge_state_v2.h +++ b/include/charge_state_v2.h @@ -4,11 +4,49 @@ */ #include "battery.h" +#include "charger.h" #include "timer.h" #ifndef __CROS_EC_CHARGE_STATE_V2_H #define __CROS_EC_CHARGE_STATE_V2_H +/* + * The values exported by charge_get_state() and charge_get_flags() are used + * only to control the LEDs (with one not-quite-correct exception). For V2 + * we use a different set of states internally. + */ +enum charge_state_v2 { + ST_IDLE = 0, + ST_DISCHARGE, + ST_CHARGE, + ST_PRECHARGE, + + NUM_STATES_V2 +}; + +struct charge_state_data { + timestamp_t ts; + int ac; + struct charger_params chg; + struct batt_params batt; + enum charge_state_v2 state; + int requested_voltage; + int requested_current; +}; + +/* + * Optional customization. + * + * On input, the struct reflects the default behavior. The function can make + * changes to the state, requested_voltage, or requested_current. + * + * Return value: + * >0 Desired time in usec for this poll period. + * 0 Use the default poll period (which varies with the state). + * <0 An error occurred. The poll time will be shorter than usual. Too + * many errors in a row may trigger some corrective action. + */ +int charger_profile_override(struct charge_state_data *); #endif /* __CROS_EC_CHARGE_STATE_V2_H */ diff --git a/include/config.h b/include/config.h index 9b8d5e362c..e89be9ca53 100644 --- a/include/config.h +++ b/include/config.h @@ -123,6 +123,8 @@ /* * Charger should call battery_vendor_params() to limit/correct the voltage and * current requested by the battery pack before acting on the request. + * + * This is valid with CONFIG_CHARGER_V1 only. */ #undef CONFIG_BATTERY_VENDOR_PARAMS @@ -195,6 +197,9 @@ */ #undef CONFIG_CHARGER_INPUT_CURRENT +/* Equivalent of CONFIG_BATTERY_VENDOR_PARAMS for use with CONFIG_CHARGER_V2 */ +#undef CONFIG_CHARGER_PROFILE_OVERRIDE + /* Value of the charge sense resistor, in mOhms */ #undef CONFIG_CHARGER_SENSE_RESISTOR diff --git a/test/battery_get_params_smart.c b/test/battery_get_params_smart.c new file mode 100644 index 0000000000..7c86252b23 --- /dev/null +++ b/test/battery_get_params_smart.c @@ -0,0 +1,87 @@ +/* Copyright (c) 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 the logic of battery_get_params() to be sure it sets the correct flags + * when i2c reads fail. + */ + +#include "battery.h" +#include "battery_smart.h" +#include "common.h" +#include "console.h" +#include "i2c.h" +#include "test_util.h" +#include "util.h" + +/* Test state */ +static int fail_on_first, fail_on_last; +static int read_count, write_count; +struct batt_params batt; + +static void reset_and_fail_on(int first, int last) +{ + /* We're not initializing the fake battery, so everything reads zero */ + memset(&batt, 0, sizeof(typeof(batt))); + read_count = write_count = 0; + fail_on_first = first; + fail_on_last = last; +} + +/* Mocked functions */ +int sb_read(int cmd, int *param) +{ + read_count++; + if (read_count >= fail_on_first && read_count <= fail_on_last) + return EC_ERROR_UNKNOWN; + + return i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, cmd, param); +} +int sb_write(int cmd, int param) +{ + write_count++; + return i2c_write16(I2C_PORT_BATTERY, BATTERY_ADDR, cmd, param); +} + + +/* Tests */ +static int test_param_failures(void) +{ + int i, num_reads; + + /* No failures */ + reset_and_fail_on(0, 0); + battery_get_params(&batt); + TEST_ASSERT(batt.flags & BATT_FLAG_RESPONSIVE); + TEST_ASSERT(!(batt.flags & BATT_FLAG_BAD_ANY)); + num_reads = read_count; + + /* Just a single failure */ + for (i = 1; i <= num_reads; i++) { + reset_and_fail_on(i, i); + battery_get_params(&batt); + TEST_ASSERT(batt.flags & BATT_FLAG_BAD_ANY); + TEST_ASSERT(batt.flags & BATT_FLAG_RESPONSIVE); + } + + /* Once it fails, it keeps failing */ + for (i = 1; i <= num_reads; i++) { + reset_and_fail_on(i, num_reads); + battery_get_params(&batt); + TEST_ASSERT(batt.flags & BATT_FLAG_BAD_ANY); + if (i == 1) + /* If every read fails, it's not responsive */ + TEST_ASSERT(!(batt.flags & BATT_FLAG_RESPONSIVE)); + else + TEST_ASSERT(batt.flags & BATT_FLAG_RESPONSIVE); + } + + return EC_SUCCESS; +} + +void run_test(void) +{ + RUN_TEST(test_param_failures); + + test_print_result(); +} diff --git a/test/battery_get_params_smart.tasklist b/test/battery_get_params_smart.tasklist new file mode 100644 index 0000000000..c5d4a2a4b3 --- /dev/null +++ b/test/battery_get_params_smart.tasklist @@ -0,0 +1,17 @@ +/* Copyright (c) 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST /* No test task */ diff --git a/test/build.mk b/test/build.mk index f93b5897aa..d21469ac98 100644 --- a/test/build.mk +++ b/test/build.mk @@ -23,7 +23,7 @@ test-list-host=mutex pingpong utils kb_scan kb_mkbp lid_sw power_button hooks test-list-host+=thermal flash queue kb_8042 extpwr_gpio console_edit system test-list-host+=sbs_charging adapter host_command thermal_falco led_spring test-list-host+=bklight_lid bklight_passthru interrupt timer_dos button -test-list-host+=motion_sense math_util +test-list-host+=motion_sense math_util sbs_charging_v2 battery_get_params_smart adapter-y=adapter.o button-y=button.o @@ -48,6 +48,7 @@ power_button-y=power_button.o powerdemo-y=powerdemo.o queue-y=queue.o sbs_charging-y=sbs_charging.o +sbs_charging_v2-y=sbs_charging_v2.o stress-y=stress.o system-y=system.o thermal-y=thermal.o @@ -55,3 +56,4 @@ thermal_falco-y=thermal_falco.o timer_calib-y=timer_calib.o timer_dos-y=timer_dos.o utils-y=utils.o +battery_get_params_smart-y=battery_get_params_smart.o diff --git a/test/sbs_charging_v2.c b/test/sbs_charging_v2.c new file mode 100644 index 0000000000..feedcb117b --- /dev/null +++ b/test/sbs_charging_v2.c @@ -0,0 +1,221 @@ +/* Copyright (c) 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 500 +#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; + +void chipset_force_shutdown(void) +{ + is_shutdown = 1; +} + +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; +} + +/* Setup init condition */ +static void test_setup(void) +{ + const struct battery_info *bat_info = battery_get_info(); + + /* 50% of charge */ + sb_write(SB_RELATIVE_STATE_OF_CHARGE, 50); + sb_write(SB_ABSOLUTE_STATE_OF_CHARGE, 50); + /* 25 degree Celsius */ + sb_write(SB_TEMPERATURE, 250 + 2731); + /* Normal voltage */ + sb_write(SB_VOLTAGE, bat_info->voltage_normal); + sb_write(SB_CHARGING_VOLTAGE, bat_info->voltage_max); + sb_write(SB_CHARGING_CURRENT, 4000); + /* Discharging at 100mAh */ + sb_write(SB_CURRENT, -100); + /* Unplug AC */ + gpio_set_level(GPIO_AC_PRESENT, 0); +} + +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); +} + +static int test_charge_state(void) +{ + enum charge_state state; + + state = wait_charging_state(); + /* Plug AC, charging at 1000mAh */ + ccprintf("[CHARGING TEST] AC on\n"); + gpio_set_level(GPIO_AC_PRESENT, 1); + sb_write(SB_CURRENT, 1000); + state = wait_charging_state(); + TEST_ASSERT(state == PWR_STATE_CHARGE); + + /* Detach battery, charging error */ + ccprintf("[CHARGING TEST] Detach battery\n"); + TEST_ASSERT(test_detach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR) == + 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); + state = wait_charging_state(); + TEST_ASSERT(state == PWR_STATE_CHARGE); + + /* 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); + + /* 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(); + 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); + charge_control(CHARGE_CONTROL_IDLE); + state = wait_charging_state(); + TEST_ASSERT(state == PWR_STATE_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) +{ + ccprintf("[CHARGING TEST] Low battery with AC\n"); + gpio_set_level(GPIO_AC_PRESENT, 1); + 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); + 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(); + 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(); + 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; +} + +void run_test(void) +{ + test_setup(); + + RUN_TEST(test_charge_state); + RUN_TEST(test_low_battery); + + test_print_result(); +} diff --git a/test/sbs_charging_v2.tasklist b/test/sbs_charging_v2.tasklist new file mode 100644 index 0000000000..a87856a123 --- /dev/null +++ b/test/sbs_charging_v2.tasklist @@ -0,0 +1,19 @@ +/* Copyright (c) 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ + TASK_TEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) diff --git a/test/test_config.h b/test/test_config.h index 9a005645f8..1ac42ae9d9 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -63,6 +63,19 @@ int board_discharge_on_ac(int enabled); #define I2C_PORT_CHARGER 1 #endif +#ifdef TEST_SBS_CHARGING_V2 +#define CONFIG_BATTERY_MOCK +#define CONFIG_BATTERY_SMART +#define CONFIG_CHARGER +#define CONFIG_CHARGER_V2 +#define CONFIG_CHARGER_INPUT_CURRENT 4032 +#define CONFIG_CHARGER_DISCHARGE_ON_AC +int board_discharge_on_ac(int enabled); +#define I2C_PORT_MASTER 1 +#define I2C_PORT_BATTERY 1 +#define I2C_PORT_CHARGER 1 +#endif + #ifdef TEST_THERMAL #define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_FANS 1 @@ -89,5 +102,14 @@ int board_discharge_on_ac(int enabled); #define CONFIG_KEYBOARD_PROTOCOL_8042 #endif +#ifdef TEST_BATTERY_GET_PARAMS_SMART +#define CONFIG_BATTERY_MOCK +#define CONFIG_BATTERY_SMART +#define CONFIG_CHARGER_INPUT_CURRENT 4032 +#define I2C_PORT_MASTER 1 +#define I2C_PORT_BATTERY 1 +#define I2C_PORT_CHARGER 1 +#endif + #endif /* TEST_BUILD */ #endif /* __CROS_EC_TEST_CONFIG_H */ |