coreboot-libre-fam15h-rdimm/3rdparty/chromeec/common/usb_common.c

285 lines
6.8 KiB
C

/* 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 "task.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);
/*
* 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);
}
}
#endif
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
void notify_sysjump_ready(volatile const task_id_t * const sysjump_task_waiting)
{
/*
* If event was set from pd_prepare_sysjump, wake the
* task waiting on us to complete.
*/
if (*sysjump_task_waiting != TASK_ID_INVALID)
task_set_event(*sysjump_task_waiting,
TASK_EVENT_SYSJUMP_READY, 0);
}
#endif