diff options
author | Alec Berg <alecaberg@chromium.org> | 2015-02-01 15:48:48 -0800 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-02-03 08:21:36 +0000 |
commit | fd9dd63e5c9cb76692501280dcb09a31f3154b12 (patch) | |
tree | c64234a6d43b0d6c47b641eb8eb0c551dff49ef6 /board/samus/extpower.c | |
parent | 0cfc6a087f103dfa1a03d5e6671844ddd4ac8c14 (diff) | |
download | chrome-ec-fd9dd63e5c9cb76692501280dcb09a31f3154b12.tar.gz |
samus: automatically recover from charge circuit failuresstabilize-6752.B
Occasionally the charge circuit on samus gets wedged and will not
charge. This change detects when the charge circuit has failed
and automatically recovers from it. It uses the BQ PROCHOT warning
to detect the failure by setting PROCHOT to trigger when the BQ
thinks input current is higher than the input current limit. When
the failure is detected, the EC disables charging and tells PD MCU
to disable CHARGE_EN, then a couple seconds later, it re-enables
charging.
This CL also adds more communication between EC and PD for the EC
to be able to set the charge state for the PD. Valid charge states
are: No charging allowed, 5V charging only, and max charging. The
EC uses this as such:
- When the EC gets AC present interrupt, it sets off a deferred
function to change charge state to max charging after some delay
to give time for the charge circuit to settle down.
- When the EC gets AC disconnect interrupt, it disables charging
briefly, enables learn mode, and then sets 5V charging allowed.
This allows for the same starting conditions in the charge circuit
for every AC attach.
- When the EC detects a wedged charge circuit, it disables charging
and waits a few seconds before re-enabling 5V only charging.
Additionally, this change moves the charging cutoff in S3/S5/G3 when
the battery is full to the EC. With the added control for the EC
to set the PD charging state, it is more convenient for the EC to
manage cutting off charging when battery is full.
BUG=chrome-os-partner:36081
BRANCH=samus
TEST=test the basics:
- connect/disconnect zinger a bunch of times
- connect and disconnect two zingers in different order
- connect two zingers and charge override between the two
test the automatic charge wedge recover:
- wedge the charge circuit by setting charger voltage under battery
voltage: "charger voltage 7000"
- wait a few seconds for the system to recover and check it is charging
with "battery" command
test full battery charge cutoff:
- added console command to change battery soc in board/samus/extpower.c:
static int cmd_battfake(int argc, char **argv)
{
char *e;
battery_soc = strtoi(argv[1], &e, 10);
batt_soc_change();
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(battfake, cmd_battfake, "", "", NULL);
- in S0, tested "battfake 100" does not disable charging.
- in G3, tested "battfake 100" disables charging and "battfake 99"
re-enables charging.
- set "battfake 100" and tested transitioning to S0 enables charging and
transitioning to S5 disables charging.
- attached two chargers and used charge override to select active port.
then toggled "battfake 100" to "battfake 99" back and forth and verified
charge override port is still the same.
test third-party 12V charger:
- plug in a bunch of times and make sure we stay at 5V for 500ms and then
transition to 12V
test with no battery:
- tested five different units with no battery and just zinger. 3/5 boot,
while the other 2 don't. But, the 2 that don't boot without battery also
can't boot w/o battery when this CL is reverted, so I don't think this
change is causing the problem, I think there is an electrical limitation.
test with EVT zinger:
- EVT zingers (P2 - C2) negotiate very quickly after connection, which
can cause INA problems w/o this CL. Tested an EVT zinger with samus and
did a bunch of connections and disconnections and verified that we always
wait at 5V for 500ms and that we don't wedge the INA circuit on connect.
test backwards compatibility:
- test new PD with old EC and make sure we can charge with zinger.
(note that if the charge circuit wedges, we won't be able to unwedge it).
- test old PD with new EC and make sure we can charge with zinger.
Change-Id: I7703b7a2ab1209d7f559b265b03517e79c74b16a
Signed-off-by: Alec Berg <alecaberg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/245253
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
Diffstat (limited to 'board/samus/extpower.c')
-rw-r--r-- | board/samus/extpower.c | 204 |
1 files changed, 171 insertions, 33 deletions
diff --git a/board/samus/extpower.c b/board/samus/extpower.c index 8eb2494795..51cf5454e8 100644 --- a/board/samus/extpower.c +++ b/board/samus/extpower.c @@ -8,6 +8,8 @@ * Drive high in S5-S0 when AC_PRESENT is high, otherwise drive low. */ +#include "bq24773.h" +#include "charge_state.h" #include "charger.h" #include "chipset.h" #include "common.h" @@ -16,13 +18,32 @@ #include "gpio.h" #include "hooks.h" #include "host_command.h" +#include "i2c.h" #include "system.h" #include "task.h" #include "util.h" +/* Console output macros */ +#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) + +/* Max number of attempts to enable/disable NVDC charger */ +#define CHARGER_MODE_ATTEMPTS 3 + /* Backboost has been detected */ static int bkboost_detected; +/* Charging is disabled */ +static int charge_is_disabled; + +/* + * Charge circuit occasionally gets wedged and doesn't charge. + * This variable keeps track of the state of the circuit. + */ +static enum { + CHARGE_CIRCUIT_OK, + CHARGE_CIRCUIT_WEDGED, +} charge_circuit_state = CHARGE_CIRCUIT_OK; + int extpower_is_present(void) { return gpio_get_level(GPIO_AC_PRESENT); @@ -64,71 +85,188 @@ static void extpower_init(void) } DECLARE_HOOK(HOOK_INIT, extpower_init, HOOK_PRIO_DEFAULT); -static void extpower_board_hacks(int extpower) +/* + * Save power in S3/S5/G3 by disabling charging when the battery is + * full. Restore charging when battery is not full anymore. This saves + * power because our input AC path is inefficient. + */ + +static void check_charging_cutoff(void) { - static int extpower_prev; + /* If battery is full disable charging */ + if (charge_get_percent() == 100) { + charge_is_disabled = 1; + host_command_pd_send_status(PD_CHARGE_NONE); + } +} +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, check_charging_cutoff, HOOK_PRIO_DEFAULT); - /* - * Use discharge_on_ac() to workaround hardware backboosting - * charge circuit problems. - */ - int use_bkboost_workaround = (system_get_board_version() < 1); +static void cancel_charging_cutoff(void) +{ + /* If charging is disabled, enable it */ + if (charge_is_disabled) { + charge_is_disabled = 0; + host_command_pd_send_status(PD_CHARGE_5V); + } +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, cancel_charging_cutoff, HOOK_PRIO_DEFAULT); + +static void batt_soc_change(void) +{ + /* If in S0, leave charging alone */ + if (chipset_in_state(CHIPSET_STATE_ON)) + return; + + /* Check to disable or enable charging based on batt state of charge */ + if (!charge_is_disabled && charge_get_percent() == 100) { + host_command_pd_send_status(PD_CHARGE_NONE); + charge_is_disabled = 1; + } else if (charge_is_disabled && charge_get_percent() < 100) { + charge_is_disabled = 0; + host_command_pd_send_status(PD_CHARGE_5V); + } +} +DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, batt_soc_change, HOOK_PRIO_DEFAULT); + +/** + * Enable/disable NVDC charger to control AC to system and battery. + */ +static void charger_disable(int disable) +{ + int i, rv; + + for (i = 0; i < CHARGER_MODE_ATTEMPTS; i++) { + rv = charger_discharge_on_ac(disable); + if (rv == EC_SUCCESS) + return; + } + + CPRINTS("Setting learn mode %d failed!", disable); +} + +static void allow_max_request(void) +{ + int prochot_status; + if (charge_circuit_state == CHARGE_CIRCUIT_WEDGED) { + /* Read PROCHOT status register to clear it */ + i2c_read8(I2C_PORT_CHARGER, BQ24773_ADDR, + BQ24773_PROCHOT_STATUS, &prochot_status); + charge_circuit_state = CHARGE_CIRCUIT_OK; + } + host_command_pd_send_status(PD_CHARGE_MAX); +} +DECLARE_DEFERRED(allow_max_request); + +static void extpower_board_hacks(int extpower, int extpower_prev) +{ + /* Cancel deferred attempt to enable max charge request */ + hook_call_deferred(allow_max_request, -1); /* + * When AC is detected, delay briefly before allowing PD + * to negotiate up to the max voltage to give charge circuit + * time to settle down. When AC goes away, set PD to only allow + * 5V charging for the next time AC is connected. + * + * Use NVDC charger learn mode (charger_disable()) when AC + * is not present to avoid backboosting when AC is plugged in. + * * When in G3, PP5000 needs to be enabled to accurately sense * CC voltage when AC is attached. When AC is disconnceted * it needs to be off to save power. */ if (extpower && !extpower_prev) { - if (use_bkboost_workaround) - charger_discharge_on_ac(0); - + /* AC connected */ + charger_disable(0); + hook_call_deferred(allow_max_request, 500*MSEC); set_pp5000_in_g3(PP5000_IN_G3_AC, 1); } else if (extpower && extpower_prev) { - if (use_bkboost_workaround) { - /* - * Glitch on AC_PRESENT, attempt to recover from - * backboost - */ - charger_discharge_on_ac(1); - charger_discharge_on_ac(0); - } + /* + * Glitch on AC_PRESENT, attempt to recover from + * backboost + */ + host_command_pd_send_status(PD_CHARGE_NONE); } else { - if (use_bkboost_workaround) - charger_discharge_on_ac(1); + /* AC disconnected */ + if (!charge_is_disabled && + charge_circuit_state == CHARGE_CIRCUIT_OK) + host_command_pd_send_status(PD_CHARGE_NONE); + + charger_disable(1); + + if (!charge_is_disabled && + charge_circuit_state == CHARGE_CIRCUIT_OK) + host_command_pd_send_status(PD_CHARGE_5V); set_pp5000_in_g3(PP5000_IN_G3_AC, 0); } extpower_prev = extpower; } +static void check_charge_wedged(void) +{ + int rv, prochot_status; + + if (charge_circuit_state == CHARGE_CIRCUIT_OK) { + /* Check PROCHOT warning */ + rv = i2c_read8(I2C_PORT_CHARGER, BQ24773_ADDR, + BQ24773_PROCHOT_STATUS, &prochot_status); + if (rv) + return; + + /* + * If PROCHOT is asserted, then charge circuit is wedged, turn + * on learn mode and notify PD to disable charging on all ports. + * Note: learn mode is critical here because when in this state + * backboosting causes >20V on boostin even after PD disables + * CHARGE_EN lines. + */ + if (prochot_status) { + host_command_pd_send_status(PD_CHARGE_NONE); + charge_circuit_state = CHARGE_CIRCUIT_WEDGED; + CPRINTS("Charge circuit wedged!"); + } + } else { + /* + * Charge circuit is wedged and we already disabled charging, + * Now start to recover from wedged state by allowing 5V. + */ + host_command_pd_send_status(PD_CHARGE_5V); + } +} + /** * Task to handle external power change */ void extpower_task(void) { int extpower = extpower_is_present(); - extpower_board_hacks(extpower); + int extpower_prev = 0; + + extpower_board_hacks(extpower, extpower_prev); /* Enable backboost detection interrupt */ gpio_enable_interrupt(GPIO_BKBOOST_DET); while (1) { - /* Wait until next extpower interrupt */ - task_wait_event(-1); - - extpower = extpower_is_present(); + if (task_wait_event(2*SECOND) == TASK_EVENT_TIMER) { + /* Periodically check if charge circuit is wedged */ + check_charge_wedged(); + } else { + /* Must have received power change interrupt */ + extpower = extpower_is_present(); - /* Various board hacks to run on extpower change */ - extpower_board_hacks(extpower); + /* Various board hacks to run on extpower change */ + extpower_board_hacks(extpower, extpower_prev); + extpower_prev = extpower; - hook_notify(HOOK_AC_CHANGE); + hook_notify(HOOK_AC_CHANGE); - /* Forward notification to host */ - if (extpower) - host_set_single_event(EC_HOST_EVENT_AC_CONNECTED); - else - host_set_single_event(EC_HOST_EVENT_AC_DISCONNECTED); + /* Forward notification to host */ + host_set_single_event(extpower ? + EC_HOST_EVENT_AC_CONNECTED : + EC_HOST_EVENT_AC_DISCONNECTED); + } } } |