/* Copyright 2019 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. */ /* * Contains common USB functions shared between the old (i.e. usb_pd_protocol) * and the new (i.e. usb_sm_*) USB-C PD stacks. */ #include "common.h" #include "charge_state.h" #include "usb_pd.h" #include "usb_pd_tcpm.h" #include "util.h" int usb_get_battery_soc(void) { #if defined(CONFIG_CHARGER) return charge_get_percent(); #elif defined(CONFIG_BATTERY) return board_get_battery_soc(); #else return 0; #endif } /* * CC values for regular sources and Debug sources (aka DTS) * * Source type Mode of Operation CC1 CC2 * --------------------------------------------- * Regular Default USB Power RpUSB Open * Regular USB-C @ 1.5 A Rp1A5 Open * Regular USB-C @ 3 A Rp3A0 Open * DTS Default USB Power Rp3A0 Rp1A5 * DTS USB-C @ 1.5 A Rp1A5 RpUSB * DTS USB-C @ 3 A Rp3A0 RpUSB */ typec_current_t usb_get_typec_current_limit(enum pd_cc_polarity_type polarity, enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) { typec_current_t charge = 0; enum tcpc_cc_voltage_status cc = polarity ? cc2 : cc1; enum tcpc_cc_voltage_status cc_alt = polarity ? cc1 : cc2; switch (cc) { case TYPEC_CC_VOLT_RP_3_0: if (!cc_is_rp(cc_alt) || cc_alt == TYPEC_CC_VOLT_RP_DEF) charge = 3000; else if (cc_alt == TYPEC_CC_VOLT_RP_1_5) charge = 500; break; case TYPEC_CC_VOLT_RP_1_5: charge = 1500; break; case TYPEC_CC_VOLT_RP_DEF: charge = 500; break; default: break; } if (IS_ENABLED(CONFIG_USBC_DISABLE_CHARGE_FROM_RP_DEF) && charge == 500) charge = 0; if (cc_is_rp(cc_alt)) charge |= TYPEC_CURRENT_DTS_MASK; return charge; } enum pd_cc_polarity_type get_snk_polarity(enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) { /* The following assumes: * * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5 * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN */ return cc2 > cc1; } /* * 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. */ #if defined(PD_MAX_VOLTAGE_MV) && defined(PD_OPERATING_POWER_MW) 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 prefer_cur; int __attribute__((unused)) cur_mv = 0; /* 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++) { /* 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 case of 'tie' */ 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; } /* Prefer higher power, except for tiebreaker */ if (uw > cur_uw || prefer_cur) { 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 *mv) { int max_ma, uw; *mv = ((pdo >> 10) & 0x3FF) * 50; if (*mv == 0) { *ma = 0; return; } if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { uw = 250000 * (pdo & 0x3FF); max_ma = 1000 * MIN(1000 * uw, PD_MAX_POWER_MW) / *mv; } else { max_ma = 10 * (pdo & 0x3FF); max_ma = MIN(max_ma, PD_MAX_POWER_MW * 1000 / *mv); } *ma = MIN(max_ma, PD_MAX_CURRENT_MA); } void pd_build_request(uint32_t src_cap_cnt, const uint32_t * const src_caps, int32_t vpd_vdo, uint32_t *rdo, uint32_t *ma, uint32_t *mv, enum pd_request_type req_type, uint32_t max_request_mv) { 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; if (req_type == PD_REQUEST_VSAFE5V) { /* src cap 0 should be vSafe5V */ pdo_index = 0; pdo = src_caps[0]; } else { /* find pdo index for max voltage we can request */ pdo_index = pd_find_pdo_index(src_cap_cnt, src_caps, max_request_mv, &pdo); } pd_extract_pdo_power(pdo, ma, mv); /* * 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); if (max_vbus > VPD_MAX_VBUS_50V) max_vbus = VPD_MAX_VBUS_20V; /* * Valid max_vbus values: * 20000 mV * 30000 mV * 40000 mV * 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); } } #endif