285 lines
6.8 KiB
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
|