diff options
Diffstat (limited to 'board')
-rw-r--r-- | board/samus/board.c | 2 | ||||
-rw-r--r-- | board/samus/extpower.c | 204 | ||||
-rw-r--r-- | board/samus_pd/board.c | 167 | ||||
-rw-r--r-- | board/samus_pd/board.h | 1 |
4 files changed, 260 insertions, 114 deletions
diff --git a/board/samus/board.c b/board/samus/board.c index 7199d60c6b..7f606516b0 100644 --- a/board/samus/board.c +++ b/board/samus/board.c @@ -47,7 +47,7 @@ static void pd_mcu_interrupt(enum gpio_signal signal) { /* Exchange status with PD MCU. */ - host_command_pd_send_status(); + host_command_pd_send_status(PD_CHARGE_NO_CHANGE); } #include "gpio_list.h" 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); + } } } diff --git a/board/samus_pd/board.c b/board/samus_pd/board.c index d89527dfc2..3fd3f0e219 100644 --- a/board/samus_pd/board.c +++ b/board/samus_pd/board.c @@ -37,11 +37,9 @@ static enum power_state ps; /* Battery state of charge */ static int batt_soc; -static int fake_state_of_charge = -1; /* use real soc by default */ -/* Last charge port override when charging turned off due to full battery */ -static int chg_override_port = OVERRIDE_OFF; -static int chg_is_cutoff; +/* Default to 5V charging allowed for dead battery case */ +enum pd_charge_state charge_state = PD_CHARGE_5V; /* PD MCU status and host event status for host command */ static struct ec_response_pd_status pd_status; @@ -266,31 +264,6 @@ void usb1_evt(enum gpio_signal signal) wake_usb_charger_task(1); } -/* When battery is full, cutoff charging by disabling AC input current */ -static void check_charging_cutoff(void) -{ - int port; - - /* Only check if charging needs to be turned off when not in S0 */ - if (ps == POWER_S0) - return; - - port = charge_manager_get_active_charge_port(); - - /* - * If battery is full disable charging, if battery is not full, restore - * charge port. - */ - if (!chg_is_cutoff && port != CHARGE_PORT_NONE && batt_soc == 100) { - charge_manager_set_override(OVERRIDE_DONT_CHARGE); - chg_is_cutoff = 1; - } else if (chg_is_cutoff && batt_soc < 100) { - charge_manager_set_override(chg_override_port); - chg_is_cutoff = 0; - } -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, check_charging_cutoff, HOOK_PRIO_DEFAULT); - static void chipset_s5_to_s3(void) { ps = POWER_S3; @@ -301,9 +274,6 @@ static void chipset_s3_to_s0(void) { /* Disable deep sleep and restore charge override port */ disable_sleep(SLEEP_MASK_AP_RUN); - charge_manager_set_override(chg_override_port); - chg_is_cutoff = 0; - ps = POWER_S0; hook_notify(HOOK_CHIPSET_RESUME); } @@ -318,8 +288,6 @@ static void chipset_s0_to_s3(void) { /* Enable deep sleep and store charge override port */ enable_sleep(SLEEP_MASK_AP_RUN); - chg_override_port = charge_manager_get_override(); - ps = POWER_S3; hook_notify(HOOK_CHIPSET_SUSPEND); } @@ -438,6 +406,9 @@ static void board_init(void) gpio_enable_interrupt(GPIO_PCH_SLP_S3_L); gpio_enable_interrupt(GPIO_PCH_SLP_S5_L); + /* Initialize active charge port to none */ + pd_status.active_charge_port = CHARGE_PORT_NONE; + /* * Do not enable PD communication in RO as a security measure. * We don't want to allow communication to outside world until @@ -596,7 +567,6 @@ void board_flip_usb_mux(int port) void board_update_battery_soc(int soc) { batt_soc = soc; - check_charging_cutoff(); } int board_get_battery_soc(void) @@ -643,26 +613,42 @@ int board_set_active_charge_port(int charge_port) return EC_ERROR_INVAL; } - pd_status.active_charge_port = charge_port; - gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, !(charge_port == 0)); - gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, !(charge_port == 1)); + CPRINTS("New chg p%d", charge_port); /* - * If new charge port when charge is cutoff, then user must have - * plugged in a new dedicated charger. This resets the charge - * override port and clears the charge cutoff flag. + * If charging and the active charge port is changed, then disable + * charging to guarantee charge circuit starts up cleanly. */ - if (chg_is_cutoff && is_real_port) { - chg_override_port = OVERRIDE_OFF; - chg_is_cutoff = 0; + if (pd_status.active_charge_port != CHARGE_PORT_NONE && + (charge_port == CHARGE_PORT_NONE || + charge_port != pd_status.active_charge_port)) { + gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, 1); + gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, 1); + charge_state = PD_CHARGE_NONE; + pd_status.active_charge_port = charge_port; + CPRINTS("Chg: None\n"); + return EC_SUCCESS; + } + + /* Save active charge port and enable charging if allowed */ + pd_status.active_charge_port = charge_port; + if (charge_state != PD_CHARGE_NONE) { + gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, !(charge_port == 0)); + gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, !(charge_port == 1)); } - check_charging_cutoff(); - CPRINTS("New chg p%d", charge_port); return EC_SUCCESS; } /** + * Return if max voltage charging is allowed. + */ +int pd_is_max_request_allowed(void) +{ + return charge_state == PD_CHARGE_MAX; +} + +/** * Set the charge limit based upon desired maximum. * * @param charge_ma Desired charge limit (mA). @@ -732,36 +718,6 @@ DECLARE_CONSOLE_COMMAND(pdevent, command_pd_host_event, "Send PD host event", NULL); -static int command_battfake(int argc, char **argv) -{ - char *e; - int v; - - if (argc == 2) { - v = strtoi(argv[1], &e, 0); - if (*e || v < -1 || v > 100) - return EC_ERROR_PARAM1; - - fake_state_of_charge = v; - } - - if (fake_state_of_charge < 0) { - ccprintf("Using real batt level\n"); - } else { - ccprintf("Using fake batt level %d%%\n", - fake_state_of_charge); - } - - /* Send EC int to get batt info from EC */ - pd_send_ec_int(); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(battfake, command_battfake, - "percent (-1 = use real level)", - "Set fake battery level", - NULL); - /****************************************************************************/ /* Host commands */ static int ec_status_host_cmd(struct host_cmd_handler_args *args) @@ -769,9 +725,60 @@ static int ec_status_host_cmd(struct host_cmd_handler_args *args) const struct ec_params_pd_status *p = args->params; struct ec_response_pd_status *r = args->response; - /* if not using fake soc, then update battery soc */ - board_update_battery_soc(fake_state_of_charge < 0 ? - p->batt_soc : fake_state_of_charge); + /* update battery soc */ + board_update_battery_soc(p->batt_soc); + + if (args->version == 1) { + if (p->charge_state != charge_state) { + switch (p->charge_state) { + case PD_CHARGE_NONE: + /* + * No current allowed in, set new power request + * so that PD negotiates down to vSafe5V. + */ + charge_state = p->charge_state; + gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, 1); + gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, 1); + pd_set_new_power_request( + pd_status.active_charge_port); + CPRINTS("Chg: None"); + break; + case PD_CHARGE_5V: + /* Allow current on the active charge port */ + charge_state = p->charge_state; + gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, + !(pd_status.active_charge_port == 0)); + gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, + !(pd_status.active_charge_port == 1)); + CPRINTS("Chg: 5V"); + break; + case PD_CHARGE_MAX: + /* + * Allow negotiation above vSafe5V. Should only + * ever get this command when 5V charging is + * already allowed. + */ + if (charge_state == PD_CHARGE_5V) { + charge_state = p->charge_state; + pd_set_new_power_request( + pd_status.active_charge_port); + CPRINTS("Chg: Max"); + } + break; + default: + break; + } + } + } else { + /* + * If the EC is using this command version, then it won't ever + * set charging allowed, so we should just assume charging at + * the max is allowed. + */ + charge_state = PD_CHARGE_MAX; + pd_set_new_power_request(pd_status.active_charge_port); + CPRINTS("Chg: Max"); + } *r = pd_status; @@ -783,7 +790,7 @@ static int ec_status_host_cmd(struct host_cmd_handler_args *args) return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_PD_EXCHANGE_STATUS, ec_status_host_cmd, - EC_VER_MASK(0)); + EC_VER_MASK(0) | EC_VER_MASK(1)); static int host_event_status_host_cmd(struct host_cmd_handler_args *args) { diff --git a/board/samus_pd/board.h b/board/samus_pd/board.h index 702dfcc9f5..641f78ece1 100644 --- a/board/samus_pd/board.h +++ b/board/samus_pd/board.h @@ -40,6 +40,7 @@ #define CONFIG_USB_POWER_DELIVERY #define CONFIG_USB_PD_ALT_MODE #define CONFIG_USB_PD_ALT_MODE_DFP +#define CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED #undef CONFIG_USB_PD_COMM_ENABLED #define CONFIG_USB_PD_COMM_ENABLED 0 #define CONFIG_USB_PD_CUSTOM_VDM |