diff options
Diffstat (limited to 'board/samus/extpower.c')
-rw-r--r-- | board/samus/extpower.c | 441 |
1 files changed, 0 insertions, 441 deletions
diff --git a/board/samus/extpower.c b/board/samus/extpower.c deleted file mode 100644 index 22cd13b44d..0000000000 --- a/board/samus/extpower.c +++ /dev/null @@ -1,441 +0,0 @@ -/* Copyright 2013 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. - */ - -/* - * Pure GPIO-based external power detection, buffered to PCH. - * 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" -#include "console.h" -#include "extpower.h" -#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; - -/* Extpower task has been initialized */ -static int extpower_task_initialized; - -/* - * 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); -} - -static void extpower_buffer_to_pch(void) -{ - if (chipset_in_state(CHIPSET_STATE_HARD_OFF)) { - /* Drive low in G3 state */ - gpio_set_level(GPIO_PCH_ACOK, 0); - } else { - /* Buffer from extpower in S5+ (where 3.3DSW enabled) */ - gpio_set_level(GPIO_PCH_ACOK, extpower_is_present()); - } -} -DECLARE_HOOK(HOOK_CHIPSET_PRE_INIT, extpower_buffer_to_pch, HOOK_PRIO_DEFAULT); - -static void extpower_shutdown(void) -{ - /* Drive ACOK buffer to PCH low when shutting down */ - gpio_set_level(GPIO_PCH_ACOK, 0); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, extpower_shutdown, HOOK_PRIO_DEFAULT); - -void extpower_interrupt(enum gpio_signal signal) -{ - /* Trigger notification of external power change */ - extpower_buffer_to_pch(); - - /* Wake extpower task only if task has been initialized */ - if (extpower_task_initialized) - task_wake(TASK_ID_EXTPOWER); -} - -static void extpower_init(void) -{ - extpower_buffer_to_pch(); - - /* Enable interrupts, now that we've initialized */ - gpio_enable_interrupt(GPIO_AC_PRESENT); -} -DECLARE_HOOK(HOOK_INIT, extpower_init, HOOK_PRIO_DEFAULT); - -/* - * 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) -{ - /* 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); - -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)) { - host_command_pd_send_status(PD_CHARGE_NO_CHANGE); - 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); - } else { - /* Leave charging alone, but update battery SOC */ - host_command_pd_send_status(PD_CHARGE_NO_CHANGE); - } -} -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_FLAGS, - 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 allow_min_charging(void) -{ - if (!charge_is_disabled && charge_circuit_state == CHARGE_CIRCUIT_OK) - host_command_pd_send_status(PD_CHARGE_5V); -} -DECLARE_DEFERRED(allow_min_charging); - -static void extpower_board_hacks(int extpower, int extpower_prev) -{ - /* Cancel deferred attempt to enable max charge request */ - hook_call_deferred(&allow_max_request_data, -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, disable charging - * for a brief time, allowing charge state machine time to - * see AC has gone away, and then 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) { - /* AC connected */ - charger_disable(0); - hook_call_deferred(&allow_max_request_data, 500*MSEC); - set_pp5000_in_g3(PP5000_IN_G3_AC, 1); - } else if (extpower && extpower_prev) { - /* - * Glitch on AC_PRESENT, attempt to recover from - * backboost - */ - host_command_pd_send_status(PD_CHARGE_NONE); - } else { - /* AC disconnected */ - if (!charge_is_disabled && - charge_circuit_state == CHARGE_CIRCUIT_OK) - host_command_pd_send_status(PD_CHARGE_NONE); - - charger_disable(1); - - hook_call_deferred(&allow_min_charging_data, 100*MSEC); - set_pp5000_in_g3(PP5000_IN_G3_AC, 0); - } - extpower_prev = extpower; -} - -/* Return boostin_voltage or negative if error */ -static int get_boostin_voltage(void) -{ - /* Static structs to save stack space */ - static struct ec_response_usb_pd_power_info pd_power_ret; - static struct ec_params_usb_pd_power_info pd_power_args; - int ret; - int err; - - /* Boost-in voltage is maximum of voltage now on each port */ - pd_power_args.port = 0; - err = pd_host_command(EC_CMD_USB_PD_POWER_INFO, 0, - &pd_power_args, - sizeof(struct ec_params_usb_pd_power_info), - &pd_power_ret, - sizeof(struct ec_response_usb_pd_power_info)); - if (err < 0) - return err; - ret = pd_power_ret.meas.voltage_now; - - pd_power_args.port = 1; - err = pd_host_command(EC_CMD_USB_PD_POWER_INFO, 0, - &pd_power_args, - sizeof(struct ec_params_usb_pd_power_info), - &pd_power_ret, - sizeof(struct ec_response_usb_pd_power_info)); - if (err < 0) - return err; - - /* Get max of two measuremente */ - if (pd_power_ret.meas.voltage_now > ret) - ret = pd_power_ret.meas.voltage_now; - - return ret; -} - -/* - * Send command to PD to write a custom persistent log entry indicating that - * charging was wedged. Returns pd_host_command success status. - */ -static int log_charge_wedged(void) -{ - static struct ec_params_pd_write_log_entry log_args; - - log_args.type = PD_EVENT_MCU_BOARD_CUSTOM; - log_args.port = 0; - - return pd_host_command(EC_CMD_PD_WRITE_LOG_ENTRY, 0, - &log_args, - sizeof(struct ec_params_pd_write_log_entry), - NULL, 0); -} - -/* Time interval between checking if charge circuit is wedged */ -#define CHARGE_WEDGE_CHECK_INTERVAL (2*SECOND) - -/* - * Number of iterations through check_charge_wedged() with charging stalled - * before attempting unwedge. - */ -#define CHARGE_STALLED_COUNT 5 -/* - * Number of iterations through check_charge_wedged() with charging stalled - * after we already just tried unwedging the circuit, before we try again. - */ -#define CHARGE_STALLED_REPEATEDLY_COUNT 60 - -/* - * Minimum number of iterations through check_charge_wedged() between - * unwedge attempts. - */ -#define MIN_COUNTS_BETWEEN_UNWEDGES 3 - -static void check_charge_wedged(void) -{ - int rv, prochot_status, batt_discharging_on_ac, boostin_voltage = 0; - static int counts_since_wedged; - static int charge_stalled_count = CHARGE_STALLED_COUNT; - uint8_t *batt_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); - - if (charge_circuit_state == CHARGE_CIRCUIT_OK) { - /* Check PROCHOT warning */ - rv = i2c_read8(I2C_PORT_CHARGER, BQ24773_ADDR_FLAGS, - BQ24773_PROCHOT_STATUS, &prochot_status); - if (rv) - prochot_status = 0; - - batt_discharging_on_ac = - (*batt_flags & EC_BATT_FLAG_AC_PRESENT) && - (*batt_flags & EC_BATT_FLAG_DISCHARGING); - - /* - * If PROCHOT is set or we are discharging on AC, then we - * need to know boostin_voltage. - */ - if (prochot_status || batt_discharging_on_ac) - boostin_voltage = get_boostin_voltage(); - - /* - * If AC is present, and battery is discharging, and - * boostin voltage is above 5V, then we might be wedged. - */ - if (batt_discharging_on_ac) { - if (boostin_voltage > 6000) - charge_stalled_count--; - else if (boostin_voltage >= 0) - charge_stalled_count = CHARGE_STALLED_COUNT; - /* If boostin_voltage < 0, don't change stalled count */ - } else { - charge_stalled_count = CHARGE_STALLED_COUNT; - } - - /* - * If we were recently wedged, then give ourselves a free pass - * here. This gives an opportunity for reading the PROCHOT - * status to clear it if the error has gone away. - */ - if (counts_since_wedged < MIN_COUNTS_BETWEEN_UNWEDGES) - counts_since_wedged++; - - /* - * If PROCHOT is asserted AND boost_in voltage is above 5V, - * then charge circuit is wedged. If charging has been stalled - * long enough, then also consider the circuit wedged. - * - * To unwedge the charge circuit 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 && boostin_voltage > 6000 && - counts_since_wedged >= MIN_COUNTS_BETWEEN_UNWEDGES) || - charge_stalled_count <= 0) { - counts_since_wedged = 0; - host_command_pd_send_status(PD_CHARGE_NONE); - charger_disable(1); - charge_circuit_state = CHARGE_CIRCUIT_WEDGED; - log_charge_wedged(); - CPRINTS("Charge wedged! PROCHOT %02x, Stalled: %d", - prochot_status, charge_stalled_count); - - /* - * If this doesn't clear the problem, then start - * the stall counter higher so that we don't retry - * unwedging for a while. Note, if we do start charging - * properly, then stall counter will be set to - * default, so that we will trigger faster the first - * time it stalls out. - */ - charge_stalled_count = CHARGE_STALLED_REPEATEDLY_COUNT; - } - } 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(); - int extpower_prev = 0; - - extpower_board_hacks(extpower, extpower_prev); - extpower_prev = extpower; - extpower_task_initialized = 1; - - /* Enable backboost detection interrupt */ - gpio_enable_interrupt(GPIO_BKBOOST_DET); - - while (1) { - if (task_wait_event(CHARGE_WEDGE_CHECK_INTERVAL) == - TASK_EVENT_TIMER) { - /* - * If we are NOT purposely discharging on AC, then - * periodically check if charge circuit is wedged. - */ - if (!board_is_discharging_on_ac()) - 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, extpower_prev); - extpower_prev = extpower; - - 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); - } - } -} - -void bkboost_det_interrupt(enum gpio_signal signal) -{ - /* Backboost has been detected, save it, and disable interrupt */ - bkboost_detected = 1; - gpio_disable_interrupt(GPIO_BKBOOST_DET); -} - -static int command_backboost_det(int argc, char **argv) -{ - ccprintf("Backboost detected: %d\n", bkboost_detected); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(bkboost, command_backboost_det, NULL, - "Read backboost detection"); |