diff options
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); + } } } |