diff options
Diffstat (limited to 'common/usb_pd_dual_role.c')
-rw-r--r-- | common/usb_pd_dual_role.c | 473 |
1 files changed, 0 insertions, 473 deletions
diff --git a/common/usb_pd_dual_role.c b/common/usb_pd_dual_role.c deleted file mode 100644 index 52042c5439..0000000000 --- a/common/usb_pd_dual_role.c +++ /dev/null @@ -1,473 +0,0 @@ -/* Copyright 2020 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. - * - * Dual Role (Source & Sink) USB-PD module. - */ - -#include "charge_manager.h" -#include "charge_state.h" -#include "dps.h" -#include "system.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* The macro is used to prevent a DBZ exception while decoding PDOs. */ -#define PROCESS_ZERO_DIVISOR(x) ((x) == 0 ? 1 : (x)) - -#if defined(PD_MAX_VOLTAGE_MV) && defined(PD_OPERATING_POWER_MW) -/* - * As a sink, this is the max voltage (in millivolts) we can request - * before getting source caps - */ -static unsigned int max_request_mv = PD_MAX_VOLTAGE_MV; - -/* TODO(b:169532537): deprecate CONFIG_USB_PD_PREFER_MV */ -STATIC_IF_NOT(CONFIG_USB_PD_PREFER_MV) -struct pd_pref_config_t __maybe_unused pd_pref_config; - -void pd_set_max_voltage(unsigned int mv) -{ - max_request_mv = mv; -} - -unsigned int pd_get_max_voltage(void) -{ - return max_request_mv; -} - -/* - * Zinger implements a board specific usb policy that does not define - * PD_MAX_VOLTAGE_MV and PD_OPERATING_POWER_MW. And in turn, does not - * use the following functions. - */ -int pd_find_pdo_index(uint32_t src_cap_cnt, const uint32_t * const src_caps, - int max_mv, uint32_t *selected_pdo) -{ - int i, uw, mv; - int ret = 0; - int cur_uw = 0; - int has_preferred_pdo; - int prefer_cur; - int desired_uw = 0; - const int prefer_mv = pd_pref_config.mv; - const int type = pd_pref_config.type; - - int __attribute__((unused)) cur_mv = 0; - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) - desired_uw = charge_get_plt_plus_bat_desired_mw() * 1000; - - /* max voltage is always limited by this boards max request */ - max_mv = MIN(max_mv, PD_MAX_VOLTAGE_MV); - - /* Get max power that is under our max voltage input */ - for (i = 0; i < src_cap_cnt; i++) { - if (IS_ENABLED(CONFIG_USB_PD_ONLY_FIXED_PDOS) && - (src_caps[i] & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - continue; - /* its an unsupported Augmented PDO (PD3.0) */ - if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) - continue; - - mv = ((src_caps[i] >> 10) & 0x3FF) * 50; - /* Skip invalid voltage */ - if (!mv) - continue; - /* Skip any voltage not supported by this board */ - if (!pd_is_valid_input_voltage(mv)) - continue; - - if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - uw = 250000 * (src_caps[i] & 0x3FF); - } else { - int ma = (src_caps[i] & 0x3FF) * 10; - - ma = MIN(ma, PD_MAX_CURRENT_MA); - uw = ma * mv; - } - - if (mv > max_mv) - continue; - uw = MIN(uw, PD_MAX_POWER_MW * 1000); - prefer_cur = 0; - - /* Apply special rules in favor of voltage */ - if (IS_ENABLED(PD_PREFER_LOW_VOLTAGE)) { - if (uw == cur_uw && mv < cur_mv) - prefer_cur = 1; - } else if (IS_ENABLED(PD_PREFER_HIGH_VOLTAGE)) { - if (uw == cur_uw && mv > cur_mv) - prefer_cur = 1; - } else if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) { - /* Pick if the PDO provides more than desired. */ - if (uw >= desired_uw) { - /* pick if cur_uw is less than desired watt */ - if (cur_uw < desired_uw) - prefer_cur = 1; - else if (type == PD_PREFER_BUCK) { - /* - * pick the smallest mV above prefer_mv - */ - if (mv >= prefer_mv && mv < cur_mv) - prefer_cur = 1; - /* - * pick if cur_mv is less than - * prefer_mv, and we have higher mV - */ - else if (cur_mv < prefer_mv && - mv > cur_mv) - prefer_cur = 1; - } else if (type == PD_PREFER_BOOST) { - /* - * pick the largest mV below prefer_mv - */ - if (mv <= prefer_mv && mv > cur_mv) - prefer_cur = 1; - /* - * pick if cur_mv is larger than - * prefer_mv, and we have lower mV - */ - else if (cur_mv > prefer_mv && - mv < cur_mv) - prefer_cur = 1; - } - /* - * pick the largest power if we don't see one staisfy - * desired power - */ - } else if (cur_uw == 0 || uw > cur_uw) { - prefer_cur = 1; - } - } - - /* Prefer higher power, except for tiebreaker */ - has_preferred_pdo = - prefer_cur || - (!IS_ENABLED(CONFIG_USB_PD_PREFER_MV) && uw > cur_uw); - - if (has_preferred_pdo) { - ret = i; - cur_uw = uw; - cur_mv = mv; - } - } - - if (selected_pdo) - *selected_pdo = src_caps[ret]; - - return ret; -} - -void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *max_mv, - uint32_t *min_mv) -{ - int max_ma, mw; - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) { - *max_mv = PDO_FIXED_VOLTAGE(pdo); - *min_mv = *max_mv; - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) { - *max_mv = PDO_AUG_MAX_VOLTAGE(pdo); - *min_mv = PDO_AUG_MIN_VOLTAGE(pdo); - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_VARIABLE) { - *max_mv = PDO_VAR_MAX_VOLTAGE(pdo); - *min_mv = PDO_VAR_MIN_VOLTAGE(pdo); - } else { - *max_mv = PDO_BATT_MAX_VOLTAGE(pdo); - *min_mv = PDO_BATT_MIN_VOLTAGE(pdo); - } - - if (*max_mv == 0) { - *ma = 0; - *min_mv = 0; - return; - } - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) { - max_ma = PDO_FIXED_CURRENT(pdo); - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) { - max_ma = PDO_AUG_MAX_CURRENT(pdo); - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_VARIABLE) { - max_ma = PDO_VAR_MAX_CURRENT(pdo); - } else { - mw = PDO_BATT_MAX_POWER(pdo); - max_ma = 1000 * mw / PROCESS_ZERO_DIVISOR(*min_mv); - } - max_ma = MIN(max_ma, - PD_MAX_POWER_MW * 1000 / PROCESS_ZERO_DIVISOR(*min_mv)); - *ma = MIN(max_ma, PD_MAX_CURRENT_MA); -} - -void pd_build_request(int32_t vpd_vdo, uint32_t *rdo, uint32_t *ma, - uint32_t *mv, int port) -{ - uint32_t pdo; - int pdo_index, flags = 0; - int uw; - int max_or_min_ma; - int max_or_min_mw; - int max_vbus; - int vpd_vbus_dcr; - int vpd_gnd_dcr; - uint32_t src_cap_cnt = pd_get_src_cap_cnt(port); - const uint32_t * const src_caps = pd_get_src_caps(port); - int charging_allowed; - int max_request_allowed; - uint32_t max_request_mv = pd_get_max_voltage(); - uint32_t unused; - - /* - * If this port is the current charge port, or if there isn't an active - * charge port, set this value to true. If CHARGE_PORT_NONE isn't - * considered, then there can be a race condition in PD negotiation and - * the charge manager which forces an incorrect request for - * vSafe5V. This can then lead to a brownout condition when the input - * current limit gets incorrectly set to 0.5A. - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - int chg_port = charge_manager_get_selected_charge_port(); - - charging_allowed = - (chg_port == port || chg_port == CHARGE_PORT_NONE); - } else { - charging_allowed = 1; - } - - if (IS_ENABLED(CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED)) - max_request_allowed = pd_is_max_request_allowed(); - else - max_request_allowed = 1; - - if (IS_ENABLED(CONFIG_USB_PD_DPS) && dps_is_enabled()) - max_request_mv = - MIN(max_request_mv, dps_get_dynamic_voltage()); - - /* - * If currently charging on a different port, or we are not allowed to - * request the max voltage, then select vSafe5V - */ - if (charging_allowed && max_request_allowed) { - /* find pdo index for max voltage we can request */ - pdo_index = pd_find_pdo_index(src_cap_cnt, src_caps, - max_request_mv, &pdo); - } else { - /* src cap 0 should be vSafe5V */ - pdo_index = 0; - pdo = src_caps[0]; - } - - pd_extract_pdo_power(pdo, ma, mv, &unused); - - /* - * Adjust VBUS current if CTVPD device was detected. - */ - if (vpd_vdo > 0) { - max_vbus = VPD_VDO_MAX_VBUS(vpd_vdo); - vpd_vbus_dcr = VPD_VDO_VBUS_IMP(vpd_vdo) << 1; - vpd_gnd_dcr = VPD_VDO_GND_IMP(vpd_vdo); - - /* - * Valid max_vbus values: - * 00b - 20000 mV - * 01b - 30000 mV - * 10b - 40000 mV - * 11b - 50000 mV - */ - max_vbus = 20000 + max_vbus * 10000; - if (*mv > max_vbus) - *mv = max_vbus; - - /* - * 5000 mA cable: 150 = 750000 / 50000 - * 3000 mA cable: 250 = 750000 / 30000 - */ - if (*ma > 3000) - *ma = 750000 / (150 + vpd_vbus_dcr + vpd_gnd_dcr); - else - *ma = 750000 / (250 + vpd_vbus_dcr + vpd_gnd_dcr); - } - - uw = *ma * *mv; - /* Mismatch bit set if less power offered than the operating power */ - if (uw < (1000 * PD_OPERATING_POWER_MW)) - flags |= RDO_CAP_MISMATCH; - -#ifdef CONFIG_USB_PD_GIVE_BACK - /* Tell source we are give back capable. */ - flags |= RDO_GIVE_BACK; - - /* - * BATTERY PDO: Inform the source that the sink will reduce - * power to this minimum level on receipt of a GotoMin Request. - */ - max_or_min_mw = PD_MIN_POWER_MW; - - /* - * FIXED or VARIABLE PDO: Inform the source that the sink will - * reduce current to this minimum level on receipt of a GotoMin - * Request. - */ - max_or_min_ma = PD_MIN_CURRENT_MA; -#else - /* - * Can't give back, so set maximum current and power to - * operating level. - */ - max_or_min_ma = *ma; - max_or_min_mw = uw / 1000; -#endif - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - int mw = uw / 1000; - *rdo = RDO_BATT(pdo_index + 1, mw, max_or_min_mw, flags); - } else { - *rdo = RDO_FIXED(pdo_index + 1, *ma, max_or_min_ma, flags); - } - - /* - * Ref: USB Power Delivery Specification - * (Revision 3.0, Version 2.0 / Revision 2.0, Version 1.3) - * 6.4.2.4 USB Communications Capable - * 6.4.2.5 No USB Suspend - * - * If the port partner is capable of USB communication set the - * USB Communications Capable flag. - * If the port partner is sink device do not suspend USB as the - * power can be used for charging. - */ - if (pd_get_partner_usb_comm_capable(port)) { - *rdo |= RDO_COMM_CAP; - if (pd_get_power_role(port) == PD_ROLE_SINK) - *rdo |= RDO_NO_SUSPEND; - } -} - -void pd_process_source_cap(int port, int cnt, uint32_t *src_caps) -{ - pd_set_src_caps(port, cnt, src_caps); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - uint32_t ma, mv, pdo, unused; - uint32_t max_mv = pd_get_max_voltage(); - - if (IS_ENABLED(CONFIG_USB_PD_DPS) && dps_is_enabled()) - max_mv = MIN(max_mv, dps_get_dynamic_voltage()); - - /* Get max power info that we could request */ - pd_find_pdo_index(pd_get_src_cap_cnt(port), - pd_get_src_caps(port), - max_mv, &pdo); - pd_extract_pdo_power(pdo, &ma, &mv, &unused); - - /* Set max. limit, but apply 500mA ceiling */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, PD_MIN_MA); - pd_set_input_current_limit(port, ma, mv); - } -} -#endif /* defined(PD_MAX_VOLTAGE_MV) && defined(PD_OPERATING_POWER_MW) */ - -bool pd_is_battery_capable(void) -{ - bool capable; - - /* Battery is present and at some minimum percentage. */ - capable = (usb_get_battery_soc() >= - CONFIG_USB_PD_TRY_SRC_MIN_BATT_SOC); - -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - /* - * Not capable if the battery is in the disconnect state. The discharge - * FET may not be enabled and so attempting being a SRC may cut off - * our only power source at the time. - */ - capable &= (battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED); -#elif defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * When battery is cutoff in ship mode it may not be reliable to - * check if battery is present with its state of charge. - * Also check if battery is initialized and ready to provide power. - */ - capable &= (battery_is_present() == BP_YES); -#endif /* CONFIG_BATTERY_PRESENT_[CUSTOM|GPIO] */ - - return capable; -} - -#ifdef CONFIG_USB_PD_TRY_SRC -bool pd_is_try_source_capable(void) -{ - int i; - uint8_t try_src = 0; - bool new_try_src; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - try_src |= (pd_get_dual_role(i) == PD_DRP_TOGGLE_ON); - - /* - * Enable try source when dual-role toggling AND battery is capable - * of powering the whole system. - */ - new_try_src = (try_src && pd_is_battery_capable()); - -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - /* - * If a dedicated supplier is present, power is not a concern and - * therefore allow Try.Src if we're toggling. - */ - new_try_src = try_src && (charge_manager_get_supplier() == - CHARGE_SUPPLIER_DEDICATED); -#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT */ - - return new_try_src; -} -#endif /* CONFIG_USB_PD_TRY_SRC */ - -static int get_bbram_idx(uint8_t port) -{ - if (port < MAX_SYSTEM_BBRAM_IDX_PD_PORTS) - return (port + SYSTEM_BBRAM_IDX_PD0); - - return -1; -} - -int pd_get_saved_port_flags(int port, uint8_t *flags) -{ - if (system_get_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { -#ifndef CHIP_HOST - ccprintf("PD NVRAM FAIL"); -#endif - return EC_ERROR_UNKNOWN; - } - - return EC_SUCCESS; -} - -static void pd_set_saved_port_flags(int port, uint8_t flags) -{ - if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { -#ifndef CHIP_HOST - ccprintf("PD NVRAM FAIL"); -#endif - } -} - -void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t do_set) -{ - uint8_t saved_flags; - - if (pd_get_saved_port_flags(port, &saved_flags) != EC_SUCCESS) - return; - - if (do_set) - saved_flags |= flag; - else - saved_flags &= ~flag; - - pd_set_saved_port_flags(port, saved_flags); -} |