diff options
Diffstat (limited to 'common/charge_manager.c')
-rw-r--r-- | common/charge_manager.c | 1625 |
1 files changed, 0 insertions, 1625 deletions
diff --git a/common/charge_manager.c b/common/charge_manager.c deleted file mode 100644 index 862bb28725..0000000000 --- a/common/charge_manager.c +++ /dev/null @@ -1,1625 +0,0 @@ -/* Copyright 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. - */ - -#include "adc.h" -#include "atomic.h" -#include "battery.h" -#include "charge_manager.h" -#include "charge_ramp.h" -#include "charge_state_v2.h" -#include "charger.h" -#include "console.h" -#include "dps.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "tcpm/tcpm.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#ifdef HAS_MOCK_CHARGE_MANAGER -#error Mock defined HAS_MOCK_CHARGE_MANAGER -#endif - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -#define POWER(charge_port) ((charge_port.current) * (charge_port.voltage)) - -/* Timeout for delayed override power swap, allow for 500ms extra */ -#define POWER_SWAP_TIMEOUT (PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON + \ - PD_T_SAFE_0V + 500 * MSEC) - -/* - * Default charge supplier priority - * - * - Always pick dedicated charge if present since that is the best product - * decision. - * - Pick PD negotiated chargers over everything else since they have the most - * power potential and they may not currently be negotiated at a high power. - * (and they can at least provide 15W) - * - Pick Type-C which supplier current >= 1.5A, which has higher prioirty - * than the BC1.2 and Type-C with current under 1.5A. (USB-C spec 1.3 - * Table 4-17: TYPEC 3.0A, 1.5A > BC1.2 > TYPEC under 1.5A) - * - Then pick among the propreitary and BC1.2 chargers which ever has the - * highest available power. - * - Last, pick one from the rest suppliers. Also note that some boards assume - * wireless suppliers as low priority. - */ -__overridable const int supplier_priority[] = { -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - [CHARGE_SUPPLIER_DEDICATED] = 0, -#endif - [CHARGE_SUPPLIER_PD] = 1, - [CHARGE_SUPPLIER_TYPEC] = 2, - [CHARGE_SUPPLIER_TYPEC_DTS] = 2, -#ifdef CHARGE_MANAGER_BC12 - [CHARGE_SUPPLIER_PROPRIETARY] = 3, - [CHARGE_SUPPLIER_BC12_DCP] = 3, - [CHARGE_SUPPLIER_BC12_CDP] = 3, - [CHARGE_SUPPLIER_BC12_SDP] = 3, - [CHARGE_SUPPLIER_TYPEC_UNDER_1_5A] = 4, - [CHARGE_SUPPLIER_OTHER] = 4, - [CHARGE_SUPPLIER_VBUS] = 4, -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - [CHARGE_SUPPLIER_WPC_BPP] = 5, - [CHARGE_SUPPLIER_WPC_EPP] = 5, - [CHARGE_SUPPLIER_WPC_GPP] = 5, -#endif - -}; -BUILD_ASSERT(ARRAY_SIZE(supplier_priority) == CHARGE_SUPPLIER_COUNT); - -/* Keep track of available charge for each charge port. */ -static struct charge_port_info available_charge[CHARGE_SUPPLIER_COUNT] - [CHARGE_PORT_COUNT]; - -/* Keep track of when the supplier on each port is registered. */ -static timestamp_t registration_time[CHARGE_PORT_COUNT]; - -/* - * Charge current ceiling (mA) for ports. This can be set to temporarily limit - * the charge pulled from a port, without influencing the port selection logic. - * The ceiling can be set independently from several requestors, with the - * minimum ceiling taking effect. - */ -static int charge_ceil[CHARGE_PORT_COUNT][CEIL_REQUESTOR_COUNT]; - -/* Dual-role capability of attached partner port */ -static enum dualrole_capabilities dualrole_capability[CHARGE_PORT_COUNT]; - -#ifdef CONFIG_USB_PD_LOGGING -/* Mark port as dirty when making changes, for later logging */ -static int save_log[CHARGE_PORT_COUNT]; -#endif - -/* Store current state of port enable / charge current. */ -static int charge_port = CHARGE_PORT_NONE; -static int charge_current = CHARGE_CURRENT_UNINITIALIZED; -static int charge_current_uncapped = CHARGE_CURRENT_UNINITIALIZED; -static int charge_voltage; -static int charge_supplier = CHARGE_SUPPLIER_NONE; -static int override_port = OVERRIDE_OFF; - -static int delayed_override_port = OVERRIDE_OFF; -static timestamp_t delayed_override_deadline; - -/* Source-out Rp values for TCPMv1 */ -__maybe_unused static uint8_t source_port_rp[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT -/* 3A on one port and 1.5A on the rest */ -BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT * 1500 + 1500 <= - CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT); -#endif - -/* - * charge_manager initially operates in safe mode until asked to leave (through - * charge_manager_leave_safe_mode()). While in safe mode, the following - * behavior is altered: - * - * 1) All chargers are considered dedicated (and thus are valid charge source - * candidates) for the purpose of port selection. - * 2) Charge ceilings are ignored. Most significantly, ILIM won't drop on PD - * voltage transition. If current load is high during transition, some - * chargers may brown-out. - * 3) CHARGE_PORT_NONE will not be selected (POR default charge port will - * remain selected rather than CHARGE_PORT_NONE). - * - * After leaving safe mode, charge_manager reverts to its normal behavior and - * immediately selects charge port and current using standard rules. - */ -#ifdef CONFIG_CHARGE_MANAGER_SAFE_MODE -static int left_safe_mode; -#else -static const int left_safe_mode = 1; -#endif - -enum charge_manager_change_type { - CHANGE_CHARGE, - CHANGE_DUALROLE, -}; - -static int is_pd_port(int port) -{ - return port >= 0 && port < board_get_usb_pd_port_count(); -} - -static int is_sink(int port) -{ - if (!is_pd_port(port)) - return board_charge_port_is_sink(port); - - return pd_get_power_role(port) == PD_ROLE_SINK; -} - -/** - * Some of the SKUs in certain boards have less number of USB PD ports than - * defined in CONFIG_USB_PD_PORT_MAX_COUNT. With the charge port configuration - * for DEDICATED_PORT towards the end, this will lead to holes in the static - * configuration. The ports that fall in that hole are invalid and this function - * is used to check the validity of the ports. - */ -static int is_valid_port(int port) -{ - if (port < 0 || port >= CHARGE_PORT_COUNT) - return 0; - - /* Check if the port falls in the hole */ - if (port >= board_get_usb_pd_port_count() && - port < CONFIG_USB_PD_PORT_MAX_COUNT) - return 0; - return 1; -} - -#ifndef TEST_BUILD -static int is_connected(int port) -{ - if (!is_pd_port(port)) - return board_charge_port_is_connected(port); - - return pd_is_connected(port); -} -#endif /* !TEST_BUILD */ - -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING -/** - * In certain cases we need to override the default behavior of not charging - * from non-dedicated chargers. If the system is in RO and locked, we have no - * way of determining the actual dualrole capability of the charger because - * PD communication is not allowed, so we must assume that it is dedicated. - * Also, if no battery is present, the charger may be our only source of power, - * so again we must assume that the charger is dedicated. - * - * @return 1 when we need to override the a non-dedicated charger - * to be a dedicated one, 0 otherwise. - */ -static int charge_manager_spoof_dualrole_capability(void) -{ - return (system_get_image_copy() == EC_IMAGE_RO && - system_is_locked()) || !left_safe_mode; - -} -#endif /* !CONFIG_CHARGE_MANAGER_DRP_CHARGING */ - -/** - * Initialize available charge. Run before board init, so board init can - * initialize data, if needed. - */ -static void charge_manager_init(void) -{ - int i, j; - - for (i = 0; i < CHARGE_PORT_COUNT; ++i) { - if (!is_valid_port(i)) - continue; - for (j = 0; j < CHARGE_SUPPLIER_COUNT; ++j) { - available_charge[j][i].current = - CHARGE_CURRENT_UNINITIALIZED; - available_charge[j][i].voltage = - CHARGE_VOLTAGE_UNINITIALIZED; - } - for (j = 0; j < CEIL_REQUESTOR_COUNT; ++j) - charge_ceil[i][j] = CHARGE_CEIL_NONE; - if (!is_pd_port(i)) - dualrole_capability[i] = CAP_DEDICATED; - if (is_pd_port(i) && !IS_ENABLED(CONFIG_USB_PD_TCPMV2)) - source_port_rp[i] = CONFIG_USB_PD_PULLUP; - } -} -DECLARE_HOOK(HOOK_INIT, charge_manager_init, HOOK_PRIO_CHARGE_MANAGER_INIT); - -/** - * Check if the charge manager is seeded. - * - * @return 1 if all ports/suppliers have reported - * with some initial charge, 0 otherwise. - */ -static int charge_manager_is_seeded(void) -{ - /* Once we're seeded, we don't need to check again. */ - static int is_seeded; - int i, j; - - if (is_seeded) - return 1; - - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - for (j = 0; j < CHARGE_PORT_COUNT; ++j) { - if (!is_valid_port(j)) - continue; - if (available_charge[i][j].current == - CHARGE_CURRENT_UNINITIALIZED || - available_charge[i][j].voltage == - CHARGE_VOLTAGE_UNINITIALIZED) - return 0; - } - } - is_seeded = 1; - return 1; -} - -#ifndef TEST_BUILD -/** - * Get the maximum charge current for a port. - * - * @param port Charge port. - * @return Charge current (mA). - */ -__maybe_unused static int charge_manager_get_source_current(int port) -{ - if (!is_pd_port(port)) - return 0; - - switch (source_port_rp[port]) { - case TYPEC_RP_3A0: - return 3000; - case TYPEC_RP_1A5: - return 1500; - case TYPEC_RP_USB: - default: - return 500; - } -} - -/* - * Find a supplier considering available current, voltage, power, and priority. - */ -static enum charge_supplier find_supplier(int port, enum charge_supplier sup, - int min_cur) -{ - int i; - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - if (available_charge[i][port].current <= min_cur || - available_charge[i][port].voltage <= 0) - /* Doesn't meet volt or current requirement. Skip it. */ - continue; - if (sup == CHARGE_SUPPLIER_NONE) - /* Haven't found any yet. Take it unconditionally. */ - sup = i; - else if (supplier_priority[sup] < supplier_priority[i]) - /* There is already a higher priority supplier. */ - continue; - else if (supplier_priority[i] < supplier_priority[sup]) - /* This has a higher priority. Take it. */ - sup = i; - else if (POWER(available_charge[i][port]) > - POWER(available_charge[sup][port])) - /* Priority is tie. Take it if power is higher. */ - sup = i; - } - return sup; -} - -static enum charge_supplier get_current_supplier(int port) -{ - enum charge_supplier supplier = CHARGE_SUPPLIER_NONE; - - /* Determine supplier information to show. */ - if (port == charge_port) { - supplier = charge_supplier; - } else { - /* Consider available current */ - supplier = find_supplier(port, supplier, 0); - if (supplier == CHARGE_SUPPLIER_NONE) - /* Ignore available current */ - supplier = find_supplier(port, supplier, -1); - } - - return supplier; -} -static enum usb_power_roles get_current_power_role(int port, - enum charge_supplier supplier) -{ - enum usb_power_roles role; - if (charge_port == port) - role = USB_PD_PORT_POWER_SINK; - else if (is_connected(port) && !is_sink(port)) - role = USB_PD_PORT_POWER_SOURCE; - else if (supplier != CHARGE_SUPPLIER_NONE) - role = USB_PD_PORT_POWER_SINK_NOT_CHARGING; - else - role = USB_PD_PORT_POWER_DISCONNECTED; - return role; -} - -__overridable int board_get_vbus_voltage(int port) -{ - return 0; -} - -static int get_vbus_voltage(int port, enum usb_power_roles current_role) -{ - int voltage_mv; - - /* - * If we are sourcing power or sinking but not charging, then VBUS must - * be 5V. If we are charging, then read VBUS ADC. - */ - if (current_role == USB_PD_PORT_POWER_SINK_NOT_CHARGING) { - voltage_mv = 5000; - } else { -#if defined(CONFIG_USB_PD_VBUS_MEASURE_CHARGER) - /* - * Try to get VBUS from the charger. If that fails, default to 0 - * mV. - */ - if (charger_get_vbus_voltage(port, &voltage_mv)) - voltage_mv = 0; -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_TCPC) - voltage_mv = tcpc_get_vbus_voltage(port); -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_ADC_EACH_PORT) - voltage_mv = adc_read_channel(board_get_vbus_adc(port)); -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_NOT_PRESENT) - /* No VBUS ADC channel - voltage is unknown */ - voltage_mv = 0; -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_BY_BOARD) - voltage_mv = board_get_vbus_voltage(port); -#else - /* There is a single ADC that measures joint Vbus */ - voltage_mv = adc_read_channel(ADC_VBUS); -#endif - } - return voltage_mv; -} - -int charge_manager_get_vbus_voltage(int port) -{ - return get_vbus_voltage(port, get_current_power_role(port, - get_current_supplier(port))); -} - -/** - * Fills passed power_info structure with current info about the passed port. - * - * @param port Charge port. - * @param r USB PD power info to be updated. - */ -static void charge_manager_fill_power_info(int port, - struct ec_response_usb_pd_power_info *r) -{ - enum charge_supplier sup = get_current_supplier(port); - - /* Fill in power role */ - r->role = get_current_power_role(port, sup); - - /* Is port partner dual-role capable */ - r->dualrole = (dualrole_capability[port] == CAP_DUALROLE); - - if (sup == CHARGE_SUPPLIER_NONE || - r->role == USB_PD_PORT_POWER_SOURCE) { - if (is_pd_port(port)) { - r->type = USB_CHG_TYPE_NONE; - r->meas.voltage_max = 0; - r->meas.voltage_now = - r->role == USB_PD_PORT_POWER_SOURCE ? 5000 : 0; - /* TCPMv2 tracks source-out current in the DPM */ - if (IS_ENABLED(CONFIG_USB_PD_TCPMV2)) - r->meas.current_max = - dpm_get_source_current(port); - else - r->meas.current_max = - charge_manager_get_source_current(port); - r->max_power = 0; - } else { - r->type = USB_CHG_TYPE_NONE; - board_fill_source_power_info(port, r); - } - } else { - int use_ramp_current; - switch (sup) { - case CHARGE_SUPPLIER_PD: - r->type = USB_CHG_TYPE_PD; - break; - case CHARGE_SUPPLIER_TYPEC: - case CHARGE_SUPPLIER_TYPEC_DTS: - r->type = USB_CHG_TYPE_C; - break; -#ifdef CHARGE_MANAGER_BC12 - case CHARGE_SUPPLIER_PROPRIETARY: - r->type = USB_CHG_TYPE_PROPRIETARY; - break; - case CHARGE_SUPPLIER_BC12_DCP: - r->type = USB_CHG_TYPE_BC12_DCP; - break; - case CHARGE_SUPPLIER_BC12_CDP: - r->type = USB_CHG_TYPE_BC12_CDP; - break; - case CHARGE_SUPPLIER_BC12_SDP: - r->type = USB_CHG_TYPE_BC12_SDP; - break; - case CHARGE_SUPPLIER_VBUS: - r->type = USB_CHG_TYPE_VBUS; - break; -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - /* - * Todo:need kernel add wpc device node in power_supply - * before that use USB_CHG_TYPE_PROPRIETARY to present WPC. - */ - case CHARGE_SUPPLIER_WPC_BPP: - case CHARGE_SUPPLIER_WPC_EPP: - case CHARGE_SUPPLIER_WPC_GPP: - r->type = USB_CHG_TYPE_PROPRIETARY; - break; -#endif -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - case CHARGE_SUPPLIER_DEDICATED: - r->type = USB_CHG_TYPE_DEDICATED; - break; -#endif - default: -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - r->type = USB_CHG_TYPE_VBUS; -#else - r->type = USB_CHG_TYPE_OTHER; -#endif - } - r->meas.voltage_max = available_charge[sup][port].voltage; - - /* - * Report unknown charger CHARGE_DETECT_DELAY after supplier - * change since PD negotiation may take time. - * - * Do not debounce on batteryless systems because - * USB_CHG_TYPE_UNKNOWN implies the system is still on battery - * while some kind of negotiation happens, but by the time the - * host might request this in a battery-free configuration we - * must be stable (if not, the system is either up or about to - * lose power again). - */ -#ifdef CONFIG_BATTERY - if (get_time().val < registration_time[port].val + - CHARGE_DETECT_DELAY) - r->type = USB_CHG_TYPE_UNKNOWN; -#endif - -#if defined(HAS_TASK_CHG_RAMP) || defined(CONFIG_CHARGE_RAMP_HW) - /* Read ramped current if active charging port */ - use_ramp_current = - (charge_port == port) && chg_ramp_allowed(port, sup); -#else - use_ramp_current = 0; -#endif - if (use_ramp_current) { - /* Current limit is output of ramp module */ - r->meas.current_lim = chg_ramp_get_current_limit(); - - /* - * If ramp is allowed, then the max current depends - * on if ramp is stable. If ramp is stable, then - * max current is same as input current limit. If - * ramp is not stable, then we report the maximum - * current we could ramp up to for this supplier. - * If ramp is not allowed, max current is just the - * available charge current. - */ - r->meas.current_max = chg_ramp_is_stable() ? - r->meas.current_lim : chg_ramp_max(port, sup, - available_charge[sup][port].current); - - r->max_power = - r->meas.current_max * r->meas.voltage_max; - } else { - r->meas.current_max = r->meas.current_lim = - available_charge[sup][port].current; - r->max_power = POWER(available_charge[sup][port]); - } - - r->meas.voltage_now = get_vbus_voltage(port, r->role); - } -} -#endif /* TEST_BUILD */ - -#ifdef CONFIG_USB_PD_LOGGING -/** - * Saves a power state log entry with the current info about the passed port. - */ -void charge_manager_save_log(int port) -{ - uint16_t flags = 0; - struct ec_response_usb_pd_power_info pinfo; - - if (!is_pd_port(port)) - return; - - save_log[port] = 0; - charge_manager_fill_power_info(port, &pinfo); - - /* Flags are stored in the data field */ - if (port == override_port) - flags |= CHARGE_FLAGS_OVERRIDE; - if (port == delayed_override_port) - flags |= CHARGE_FLAGS_DELAYED_OVERRIDE; - flags |= pinfo.role | (pinfo.type << CHARGE_FLAGS_TYPE_SHIFT) | - (pinfo.dualrole ? CHARGE_FLAGS_DUAL_ROLE : 0); - - pd_log_event(PD_EVENT_MCU_CHARGE, - PD_LOG_PORT_SIZE(port, sizeof(pinfo.meas)), - flags, &pinfo.meas); -} -#endif /* CONFIG_USB_PD_LOGGING */ - -/** - * Attempt to switch to power source on port if applicable. - * - * @param port USB-C port to be swapped. - */ -static void charge_manager_switch_to_source(int port) -{ - if (!is_pd_port(port)) - return; - - /* If connected to dual-role device, then ask for a swap */ - if (dualrole_capability[port] == CAP_DUALROLE && is_sink(port)) - pd_request_power_swap(port); -} - -/** - * Return the computed charge ceiling for a port, which represents the - * minimum ceiling among all valid requestors. - * - * @param port Charge port. - * @return Charge ceiling (mA) or CHARGE_CEIL_NONE. - */ -static int charge_manager_get_ceil(int port) -{ - int ceil = CHARGE_CEIL_NONE; - int val, i; - - if (!is_valid_port(port)) - return ceil; - - for (i = 0; i < CEIL_REQUESTOR_COUNT; ++i) { - val = charge_ceil[port][i]; - if (val != CHARGE_CEIL_NONE && - (ceil == CHARGE_CEIL_NONE || val < ceil)) - ceil = val; - } - - return ceil; -} - -/** - * Select the 'best' charge port, as defined by the supplier heirarchy and the - * ability of the port to provide power. - * - * @param new_port Pointer to the best charge port by definition. - * @param new_supplier Pointer to the best charge supplier by definition. - */ -static void charge_manager_get_best_charge_port(int *new_port, - int *new_supplier) -{ - int supplier = CHARGE_SUPPLIER_NONE; - int port = CHARGE_PORT_NONE; - int best_port_power = -1, candidate_port_power; - int i, j; - - /* Skip port selection on OVERRIDE_DONT_CHARGE. */ - if (override_port != OVERRIDE_DONT_CHARGE) { - - /* - * Charge supplier selection logic: - * 1. Prefer DPS charge port. - * 2. Prefer higher priority supply. - * 3. Prefer higher power over lower in case priority is tied. - * 4. Prefer current charge port over new port in case (1) - * and (2) are tied. - * available_charge can be changed at any time by other tasks, - * so make no assumptions about its consistency. - */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) - for (j = 0; j < CHARGE_PORT_COUNT; ++j) { - /* Skip this port if it is not valid. */ - if (!is_valid_port(j)) - continue; - - /* - * Skip this supplier if there is no - * available charge. - */ - if (available_charge[i][j].current == 0 || - available_charge[i][j].voltage == 0) - continue; - - /* - * Don't select this port if we have a - * charge on another override port. - */ - if (override_port != OVERRIDE_OFF && - override_port == port && - override_port != j) - continue; - -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - /* - * Don't charge from a dual-role port unless - * it is our override port. - */ - if (dualrole_capability[j] != CAP_DEDICATED && - override_port != j && - !charge_manager_spoof_dualrole_capability()) - continue; -#endif - - candidate_port_power = - POWER(available_charge[i][j]); - - /* Select DPS port if provided. */ - if (IS_ENABLED(CONFIG_USB_PD_DPS) && - override_port == OVERRIDE_OFF && - i == CHARGE_SUPPLIER_PD && - j == dps_get_charge_port()) { - supplier = i; - port = j; - break; - /* Select if no supplier chosen yet. */ - } else if (supplier == CHARGE_SUPPLIER_NONE || - /* ..or if supplier priority is higher. */ - supplier_priority[i] < - supplier_priority[supplier] || - /* ..or if this is our override port. */ - (j == override_port && - port != override_port) || - /* ..or if priority is tied and.. */ - (supplier_priority[i] == - supplier_priority[supplier] && - /* candidate port can supply more power or.. */ - (candidate_port_power > best_port_power || - /* - * candidate port is the active port and can - * supply the same amount of power. - */ - (candidate_port_power == best_port_power && - charge_port == j)))) { - supplier = i; - port = j; - best_port_power = candidate_port_power; - } - } - - } - -#ifdef CONFIG_BATTERY - /* - * if no battery present then retain same charge port - * and charge supplier to avoid the port switching - */ - if (charge_port != CHARGE_SUPPLIER_NONE && - charge_port != port && - (battery_is_present() == BP_NO || - (battery_is_present() == BP_YES && - battery_is_cut_off() != BATTERY_CUTOFF_STATE_NORMAL))) { - port = charge_port; - supplier = charge_supplier; - } -#endif - - *new_port = port; - *new_supplier = supplier; -} - -/** - * Charge manager refresh -- responsible for selecting the active charge port - * and charge power. Called as a deferred task. - */ -static void charge_manager_refresh(void) -{ - /* Always initialize charge port on first pass */ - static int active_charge_port_initialized; - int new_supplier, new_port; - int new_charge_current, new_charge_current_uncapped; - int new_charge_voltage, i; - int updated_new_port = CHARGE_PORT_NONE; - int updated_old_port = CHARGE_PORT_NONE; - int ceil; - int power_changed = 0; - - /* Hunt for an acceptable charge port */ - while (1) { - charge_manager_get_best_charge_port(&new_port, &new_supplier); - - if (!left_safe_mode && new_port == CHARGE_PORT_NONE) - return; - - /* - * If the port or supplier changed, make an attempt to switch to - * the port. We will re-set the active port on a supplier change - * to give the board-level function another chance to reject - * the port, for example, if the port has become a charge - * source. - */ - if (active_charge_port_initialized && - new_port == charge_port && - new_supplier == charge_supplier) - break; - - /* - * For OCPC systems, reset the OCPC state to prevent current - * spikes. - */ - if (IS_ENABLED(CONFIG_OCPC)) { - charge_set_active_chg_chip(new_port); - trigger_ocpc_reset(); - } - - if (board_set_active_charge_port(new_port) == EC_SUCCESS) { - if (IS_ENABLED(CONFIG_EXTPOWER)) - board_check_extpower(); - break; - } - - /* 'Dont charge' request must be accepted. */ - ASSERT(new_port != CHARGE_PORT_NONE); - - /* - * Zero the available charge on the rejected port so that - * it is no longer chosen. - */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - available_charge[i][new_port].current = 0; - available_charge[i][new_port].voltage = 0; - } - } - - active_charge_port_initialized = 1; - - /* - * Clear override if it wasn't selected as the 'best' port -- it means - * that no charge is available on the port, or the port was rejected. - */ - if (override_port >= 0 && override_port != new_port) - override_port = OVERRIDE_OFF; - - if (new_supplier == CHARGE_SUPPLIER_NONE) { - new_charge_current = 0; - new_charge_current_uncapped = 0; - new_charge_voltage = 0; - } else { - new_charge_current_uncapped = - available_charge[new_supplier][new_port].current; -#ifdef CONFIG_CHARGE_RAMP_HW - /* - * Allow to set the maximum current value, so the hardware can - * know the range of acceptable current values for its ramping. - */ - if (chg_ramp_allowed(new_port, new_supplier)) - new_charge_current_uncapped = - chg_ramp_max(new_port, new_supplier, - new_charge_current_uncapped); -#endif /* CONFIG_CHARGE_RAMP_HW */ - /* Enforce port charge ceiling. */ - ceil = charge_manager_get_ceil(new_port); - if (left_safe_mode && ceil != CHARGE_CEIL_NONE) - new_charge_current = MIN(ceil, - new_charge_current_uncapped); - else - new_charge_current = new_charge_current_uncapped; - - new_charge_voltage = - available_charge[new_supplier][new_port].voltage; - } - - /* Change the charge limit + charge port/supplier if modified. */ - if (new_port != charge_port || new_charge_current != charge_current || - new_supplier != charge_supplier) { -#ifdef HAS_TASK_CHG_RAMP - chg_ramp_charge_supplier_change( - new_port, new_supplier, new_charge_current, - registration_time[new_port], - new_charge_voltage); -#else -#ifdef CONFIG_CHARGE_RAMP_HW - /* Enable or disable charge ramp */ - charger_set_hw_ramp(chg_ramp_allowed(new_port, new_supplier)); -#endif - board_set_charge_limit(new_port, new_supplier, - new_charge_current, - new_charge_current_uncapped, - new_charge_voltage); -#endif /* HAS_TASK_CHG_RAMP */ - - power_changed = 1; - - CPRINTS("CL: p%d s%d i%d v%d", new_port, new_supplier, - new_charge_current, new_charge_voltage); - - /* - * (b:192638664) We try to check AC OK again to avoid - * unsuccessful detection in the initial detection. - */ - if (IS_ENABLED(CONFIG_EXTPOWER)) - board_check_extpower(); - } - - /* - * Signal new power request only if the port changed, the voltage - * on the same port changed, or the actual uncapped current - * on the same port changed (don't consider ceil). - */ - if (new_port != CHARGE_PORT_NONE && - (new_port != charge_port || - new_charge_current_uncapped != charge_current_uncapped || - new_charge_voltage != charge_voltage)) - updated_new_port = new_port; - - /* If charge port changed, cleanup old port */ - if (charge_port != new_port && charge_port != CHARGE_PORT_NONE) { - /* Check if need power swap */ - charge_manager_switch_to_source(charge_port); - /* Signal new power request on old port */ - updated_old_port = charge_port; - } - - /* Update globals to reflect current state. */ - charge_current = new_charge_current; - charge_current_uncapped = new_charge_current_uncapped; - charge_voltage = new_charge_voltage; - charge_supplier = new_supplier; - charge_port = new_port; - -#ifdef CONFIG_USB_PD_LOGGING - /* - * Write a log under the following conditions: - * 1. A port becomes active or - * 2. A port becomes inactive or - * 3. The active charge port power limit changes or - * 4. Any supplier change on an inactive port - */ - if (updated_new_port != CHARGE_PORT_NONE) - save_log[updated_new_port] = 1; - /* Don't log non-meaningful changes on charge port */ - else if (charge_port != CHARGE_PORT_NONE) - save_log[charge_port] = 0; - - if (updated_old_port != CHARGE_PORT_NONE) - save_log[updated_old_port] = 1; - - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (save_log[i]) - charge_manager_save_log(i); -#endif - - /* New power requests must be set only after updating the globals. */ - if (is_pd_port(updated_new_port)) { - /* Check if we can get requested voltage/current */ - if ((IS_ENABLED(CONFIG_USB_PD_TCPMV1) && - IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) || - (IS_ENABLED(CONFIG_USB_PD_TCPMV2) && - IS_ENABLED(CONFIG_USB_PE_SM))) { - uint32_t pdo; - uint32_t max_voltage; - uint32_t max_current; - uint32_t unused; - /* - * Check if new voltage/current is different - * than requested. If yes, send new power request - */ - if (pd_get_requested_voltage(updated_new_port) != - charge_voltage || - pd_get_requested_current(updated_new_port) != - charge_current_uncapped) - pd_set_new_power_request(updated_new_port); - - /* - * Check if we can get more power from this port. - * If yes, send new power request - */ - pd_find_pdo_index(pd_get_src_cap_cnt(updated_new_port), - pd_get_src_caps(updated_new_port), - pd_get_max_voltage(), &pdo); - pd_extract_pdo_power(pdo, &max_current, &max_voltage, - &unused); - if (charge_voltage != max_voltage || - charge_current_uncapped != max_current) - pd_set_new_power_request(updated_new_port); - } else { - /* - * Functions for getting requested voltage/current - * are not available. Send new power request. - */ - pd_set_new_power_request(updated_new_port); - } - } - if (is_pd_port(updated_old_port)) - pd_set_new_power_request(updated_old_port); - - if (power_changed) - /* notify host of power info change */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charge_manager_refresh); - -/** - * Called when charge override times out waiting for power swap. - */ -static void charge_override_timeout(void) -{ - delayed_override_port = OVERRIDE_OFF; - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charge_override_timeout); - -/** - * Called CHARGE_DETECT_DELAY after the most recent charge change on a port. - */ -static void charger_detect_debounced(void) -{ - /* Inform host that charger detection is debounced. */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charger_detect_debounced); - -/** - * Update charge parameters for a given port / supplier. - * - * @param change Type of change. - * @param supplier Charge supplier to be updated. - * @param port Charge port to be updated. - * @param charge Charge port current / voltage. - */ -static void charge_manager_make_change(enum charge_manager_change_type change, - int supplier, - int port, - const struct charge_port_info *charge) -{ - int i; - int clear_override = 0; - - if (!is_valid_port(port)) { - CPRINTS("%s: p%d invalid", __func__, port); - return; - } - - /* Determine if this is a change which can affect charge status */ - switch (change) { - case CHANGE_CHARGE: - /* Ignore changes where charge is identical */ - if (available_charge[supplier][port].current == - charge->current && - available_charge[supplier][port].voltage == - charge->voltage) - return; - if (charge->current > 0 && - available_charge[supplier][port].current == 0) - clear_override = 1; -#ifdef CONFIG_USB_PD_LOGGING - save_log[port] = 1; -#endif - break; - case CHANGE_DUALROLE: - /* - * Ignore all except for transition to non-dualrole, - * which may occur some time after we see a charge - */ -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - if (dualrole_capability[port] != CAP_DEDICATED) -#endif - return; - /* Clear override only if a charge is present on the port */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) - if (available_charge[i][port].current > 0) { - clear_override = 1; - break; - } - /* - * If there is no charge present on the port, the dualrole - * change is meaningless to charge_manager. - */ - if (!clear_override) - return; - break; - } - - /* Remove override when a charger is plugged */ - if (clear_override && override_port != port -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - /* only remove override when it's a dedicated charger */ - && dualrole_capability[port] == CAP_DEDICATED -#endif - ) { - override_port = OVERRIDE_OFF; - if (delayed_override_port != OVERRIDE_OFF) { - delayed_override_port = OVERRIDE_OFF; - hook_call_deferred(&charge_override_timeout_data, -1); - } - } - - if (change == CHANGE_CHARGE) { - available_charge[supplier][port].current = charge->current; - available_charge[supplier][port].voltage = charge->voltage; - registration_time[port] = get_time(); - - /* - * After CHARGE_DETECT_DELAY, inform the host that charger - * detection has been debounced. Since only one deferred - * routine exists for all ports, the deferred call for a given - * port may potentially be cancelled. This is mostly harmless - * since cancellation implies that PD_EVENT_POWER_CHANGE was - * just sent due to the power change on another port. - */ - if (charge->current > 0) - hook_call_deferred(&charger_detect_debounced_data, - CHARGE_DETECT_DELAY); - - /* - * If we have a charge on our delayed override port within - * the deadline, make it our override port. - */ - if (port == delayed_override_port && charge->current > 0 && - is_sink(delayed_override_port) && - get_time().val < delayed_override_deadline.val) { - delayed_override_port = OVERRIDE_OFF; - hook_call_deferred(&charge_override_timeout_data, -1); - charge_manager_set_override(port); - } - } - - /* - * Don't call charge_manager_refresh unless all ports + - * suppliers have reported in. We don't want to make changes - * to our charge port until we are certain we know what is - * attached. - */ - if (charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); -} - -void pd_set_input_current_limit(int port, uint32_t max_ma, - uint32_t supply_voltage) -{ - struct charge_port_info charge; - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) - charge_reset_stable_current(); - - charge.current = max_ma; - charge.voltage = supply_voltage; - charge_manager_update_charge(CHARGE_SUPPLIER_PD, port, &charge); -} - -void typec_set_input_current_limit(int port, typec_current_t max_ma, - uint32_t supply_voltage) -{ - struct charge_port_info charge; - int i; - int supplier; - int dts = !!(max_ma & TYPEC_CURRENT_DTS_MASK); - static const enum charge_supplier typec_suppliers[] = { - CHARGE_SUPPLIER_TYPEC, - CHARGE_SUPPLIER_TYPEC_DTS, -#ifdef CHARGE_MANAGER_BC12 - CHARGE_SUPPLIER_TYPEC_UNDER_1_5A, -#endif /* CHARGE_MANAGER_BC12 */ - }; - - charge.current = max_ma & TYPEC_CURRENT_ILIM_MASK; - charge.voltage = supply_voltage; -#if !defined(HAS_TASK_CHG_RAMP) && !defined(CONFIG_CHARGE_RAMP_HW) - /* - * DTS sources such as suzy-q may not be able to actually deliver - * their advertised current, so limit it to reduce chance of OC, - * if we can't ramp. - */ - if (dts) - charge.current = MIN(charge.current, 500); -#endif - - supplier = dts ? CHARGE_SUPPLIER_TYPEC_DTS : CHARGE_SUPPLIER_TYPEC; - -#ifdef CHARGE_MANAGER_BC12 - /* - * According to USB-C spec 1.3 Table 4-17 "Precedence of power source - * usage", the priority should be: USB-C 3.0A, 1.5A > BC1.2 > USB-C - * under 1.5A. Choosed the corresponding supplier type, according to - * charge current, to update. - */ - if (charge.current < 1500) - supplier = CHARGE_SUPPLIER_TYPEC_UNDER_1_5A; -#endif /* CHARGE_MANAGER_BC12 */ - - charge_manager_update_charge(supplier, port, &charge); - - /* - * TYPEC / TYPEC-DTS / TYPEC-UNDER_1_5A should be mutually exclusive. - * Zero'ing all the other suppliers. - */ - for (i = 0; i < ARRAY_SIZE(typec_suppliers); ++i) - if (supplier != typec_suppliers[i]) - charge_manager_update_charge(typec_suppliers[i], port, - NULL); -} - -void charge_manager_update_charge(int supplier, - int port, - const struct charge_port_info *charge) -{ - struct charge_port_info zero = {0}; - if (!charge) - charge = &zero; - charge_manager_make_change(CHANGE_CHARGE, supplier, port, charge); -} - -void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap) -{ - if (!is_pd_port(port)) - return; - - /* Ignore when capability is unchanged */ - if (cap != dualrole_capability[port]) { - dualrole_capability[port] = cap; - charge_manager_make_change(CHANGE_DUALROLE, 0, port, NULL); - } -} - -#ifdef CONFIG_CHARGE_MANAGER_SAFE_MODE -void charge_manager_leave_safe_mode(void) -{ - if (left_safe_mode) - return; - - CPRINTS("%s()", __func__); - cflush(); - left_safe_mode = 1; - if (charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); -} -#endif - -void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil) -{ - if (!is_valid_port(port)) - return; - - if (charge_ceil[port][requestor] != ceil) { - charge_ceil[port][requestor] = ceil; - if (port == charge_port && charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); - } -} - -void charge_manager_force_ceil(int port, int ceil) -{ - /* - * Force our input current to ceil if we're exceeding it, without - * waiting for our deferred task to run. - */ - if (left_safe_mode && port == charge_port && ceil < charge_current) - board_set_charge_limit(port, CHARGE_SUPPLIER_PD, ceil, - charge_current_uncapped, charge_voltage); - - /* - * Now inform charge_manager so it stays in sync with the state of - * the world. - */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, ceil); -} - -int charge_manager_set_override(int port) -{ - int retval = EC_SUCCESS; - - CPRINTS("Charge Override: %d", port); - - /* - * If attempting to change the override port, then return - * error. Since we may be in the middle of a power swap on - * the original override port, it's too complicated to - * guarantee that the original override port is switched back - * to source. - */ - if (delayed_override_port != OVERRIDE_OFF) - return EC_ERROR_BUSY; - - /* Set the override port if it's a sink. */ - if (port < 0 || is_sink(port)) { - if (override_port != port) { - override_port = port; - if (charge_manager_is_seeded()) - hook_call_deferred( - &charge_manager_refresh_data, 0); - } - } - /* - * If the attached device is capable of being a sink, request a - * power swap and set the delayed override for swap completion. - */ - else if (!is_sink(port) && dualrole_capability[port] == CAP_DUALROLE) { - delayed_override_deadline.val = get_time().val + - POWER_SWAP_TIMEOUT; - delayed_override_port = port; - hook_call_deferred(&charge_override_timeout_data, - POWER_SWAP_TIMEOUT); - pd_request_power_swap(port); - /* Can't charge from requested port -- return error. */ - } else - retval = EC_ERROR_INVAL; - - return retval; -} - -int charge_manager_get_override(void) -{ - return override_port; -} - -int charge_manager_get_active_charge_port(void) -{ - return charge_port; -} - -int charge_manager_get_selected_charge_port(void) -{ - int port, supplier; - - charge_manager_get_best_charge_port(&port, &supplier); - return port; -} - -int charge_manager_get_charger_current(void) -{ - return charge_current; -} - -int charge_manager_get_charger_voltage(void) -{ - return charge_voltage; -} - -enum charge_supplier charge_manager_get_supplier(void) -{ - return charge_supplier; -} - -int charge_manager_get_power_limit_uw(void) -{ - int current_ma = charge_current; - int voltage_mv = charge_voltage; - - if (current_ma == CHARGE_CURRENT_UNINITIALIZED || - voltage_mv == CHARGE_VOLTAGE_UNINITIALIZED) - return 0; - else - return current_ma * voltage_mv; -} - -#if defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) && \ - !defined(CONFIG_USB_PD_TCPMV2) -/* Note: this functionality is a part of the TCPMv2 Device Poicy Manager */ - -/* Bitmap of ports used as power source */ -static volatile uint32_t source_port_bitmap; -BUILD_ASSERT(sizeof(source_port_bitmap)*8 >= CONFIG_USB_PD_PORT_MAX_COUNT); - -static inline int has_other_active_source(int port) -{ - return source_port_bitmap & ~BIT(port); -} - -static inline int is_active_source(int port) -{ - return source_port_bitmap & BIT(port); -} - -static int can_supply_max_current(int port) -{ -#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT - /* - * This guarantees active 3A source continues to supply 3A. - * - * Since redistribution occurs sequentially, younger ports get - * priority. Priority surfaces only when 3A source is released. - * That is, when 3A source is released, the youngest active - * port gets 3A. - */ - int p; - if (!is_active_source(port)) - /* Non-active ports don't get 3A */ - return 0; - for (p = 0; p < board_get_usb_pd_port_count(); p++) { - if (p == port) - continue; - if (source_port_rp[p] == - CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - return 0; - } - return 1; -#else - return is_active_source(port) && !has_other_active_source(port); -#endif /* CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT */ -} - -void charge_manager_source_port(int port, int enable) -{ - uint32_t prev_bitmap = source_port_bitmap; - int p, rp; - - if (enable) - atomic_or((uint32_t *)&source_port_bitmap, 1 << port); - else - atomic_clear_bits((uint32_t *)&source_port_bitmap, 1 << port); - - /* No change, exit early. */ - if (prev_bitmap == source_port_bitmap) - return; - - /* Set port limit according to policy */ - for (p = 0; p < board_get_usb_pd_port_count(); p++) { - rp = can_supply_max_current(p) ? - CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT : - CONFIG_USB_PD_PULLUP; - source_port_rp[p] = rp; - -#ifdef CONFIG_USB_PD_LOGGING - if (is_connected(p) && !is_sink(p)) - charge_manager_save_log(p); -#endif - - typec_set_source_current_limit(p, rp); - if (IS_ENABLED(CONFIG_USB_PD_TCPMV2)) - typec_select_src_current_limit_rp(p, rp); - else - tcpm_select_rp_value(p, rp); - pd_update_contract(p); - } -} - -int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port) -{ - if (can_supply_max_current(port)) { - *src_pdo = pd_src_pdo_max; - return pd_src_pdo_max_cnt; - } - - *src_pdo = pd_src_pdo; - return pd_src_pdo_cnt; -} -#endif /* CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT && !CONFIG_USB_PD_TCPMV2 */ - -#ifndef TEST_BUILD -static enum ec_status hc_pd_power_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_power_info *p = args->params; - struct ec_response_usb_pd_power_info *r = args->response; - int port = p->port; - - /* If host is asking for the charging port, set port appropriately */ - if (port == PD_POWER_CHARGING_PORT) - port = charge_port; - - /* - * Not checking for invalid port here, because it might break existing - * contract with ectool users. The invalid ports will have the response - * voltage, current and power parameters set to 0. - */ - if (port >= CHARGE_PORT_COUNT) - return EC_RES_INVALID_PARAM; - - charge_manager_fill_power_info(port, r); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_POWER_INFO, - hc_pd_power_info, - EC_VER_MASK(0)); -#endif /* TEST_BUILD */ - -static enum ec_status hc_charge_port_count(struct host_cmd_handler_args *args) -{ - struct ec_response_charge_port_count *resp = args->response; - - args->response_size = sizeof(*resp); - resp->port_count = CHARGE_PORT_COUNT; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CHARGE_PORT_COUNT, - hc_charge_port_count, - EC_VER_MASK(0)); - -static enum ec_status -hc_charge_port_override(struct host_cmd_handler_args *args) -{ - const struct ec_params_charge_port_override *p = args->params; - const int16_t override_port = p->override_port; - - if (override_port < OVERRIDE_DONT_CHARGE || - override_port >= CHARGE_PORT_COUNT) - return EC_RES_INVALID_PARAM; - - return charge_manager_set_override(override_port) == EC_SUCCESS ? - EC_RES_SUCCESS : EC_RES_ERROR; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHARGE_PORT_OVERRIDE, - hc_charge_port_override, - EC_VER_MASK(0)); - -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 -static enum ec_status hc_override_dedicated_charger_limit( - struct host_cmd_handler_args *args) -{ - const struct ec_params_dedicated_charger_limit *p = args->params; - struct charge_port_info ci = { - .current = p->current_lim, - .voltage = p->voltage_lim, - }; - - /* - * Allow a change only if the dedicated charge port is used. Host needs - * to apply a change every time a dedicated charger is plugged. - */ - if (charge_port != DEDICATED_CHARGE_PORT) - return EC_RES_UNAVAILABLE; - - charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, - DEDICATED_CHARGE_PORT, &ci); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_OVERRIDE_DEDICATED_CHARGER_LIMIT, - hc_override_dedicated_charger_limit, - EC_VER_MASK(0)); -#endif - -static int command_charge_port_override(int argc, char **argv) -{ - int port = OVERRIDE_OFF; - int ret = EC_SUCCESS; - char *e; - - if (argc >= 2) { - port = strtoi(argv[1], &e, 0); - if (*e || port < OVERRIDE_DONT_CHARGE || - port >= CHARGE_PORT_COUNT) - return EC_ERROR_PARAM1; - ret = charge_manager_set_override(port); - } - - ccprintf("Override: %d\n", (argc >= 2 && ret == EC_SUCCESS) ? - port : override_port); - return ret; -} -DECLARE_CONSOLE_COMMAND(chgoverride, command_charge_port_override, - "[port | -1 | -2]", - "Force charging from a given port (-1 = off, -2 = disable charging)"); - -#ifdef CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT -static void charge_manager_set_external_power_limit(int current_lim, - int voltage_lim) -{ - int port; - - if (current_lim == EC_POWER_LIMIT_NONE) - current_lim = CHARGE_CEIL_NONE; - if (voltage_lim == EC_POWER_LIMIT_NONE) - voltage_lim = PD_MAX_VOLTAGE_MV; - - for (port = 0; port < board_get_usb_pd_port_count(); ++port) { - charge_manager_set_ceil(port, CEIL_REQUESTOR_HOST, current_lim); - pd_set_external_voltage_limit(port, voltage_lim); - } -} - -/* - * On transition out of S0, disable all external power limits, in case AP - * failed to clear them. - */ -static void charge_manager_external_power_limit_off(void) -{ - charge_manager_set_external_power_limit(EC_POWER_LIMIT_NONE, - EC_POWER_LIMIT_NONE); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, charge_manager_external_power_limit_off, - HOOK_PRIO_DEFAULT); - -static enum ec_status -hc_external_power_limit(struct host_cmd_handler_args *args) -{ - const struct ec_params_external_power_limit_v1 *p = args->params; - - charge_manager_set_external_power_limit(p->current_lim, - p->voltage_lim); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_EXTERNAL_POWER_LIMIT, - hc_external_power_limit, - EC_VER_MASK(1)); - -static int command_external_power_limit(int argc, char **argv) -{ - int max_current; - int max_voltage; - char *e; - - if (argc >= 2) { - max_current = strtoi(argv[1], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - } else - max_current = EC_POWER_LIMIT_NONE; - - if (argc >= 3) { - max_voltage = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - } else - max_voltage = EC_POWER_LIMIT_NONE; - - charge_manager_set_external_power_limit(max_current, max_voltage); - ccprintf("max req: %dmA %dmV\n", max_current, max_voltage); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(chglim, command_external_power_limit, - "[max_current (mA)] [max_voltage (mV)]", - "Set max charger current / voltage"); -#endif /* CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT */ - -#ifdef CONFIG_CMD_CHARGE_SUPPLIER_INFO -static int charge_supplier_info(int argc, char **argv) -{ - ccprintf("port=%d, type=%d, cur=%dmA, vtg=%dmV, lsm=%d\n", - charge_manager_get_active_charge_port(), - charge_supplier, - charge_current, - charge_voltage, - left_safe_mode); - - return 0; -} -DECLARE_CONSOLE_COMMAND(chgsup, charge_supplier_info, - NULL, "print chg supplier info"); -#endif - -__overridable -int board_charge_port_is_sink(int port) -{ - return 1; -} - -__overridable -int board_charge_port_is_connected(int port) -{ - return 1; -} - -__overridable -void board_fill_source_power_info(int port, - struct ec_response_usb_pd_power_info *r) -{ - r->meas.voltage_now = 0; - r->meas.voltage_max = 0; - r->meas.current_max = 0; - r->meas.current_lim = 0; - r->max_power = 0; -} |