summaryrefslogtreecommitdiff
path: root/common/usb_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_common.c')
-rw-r--r--common/usb_common.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/common/usb_common.c b/common/usb_common.c
index 86c85898dd..c060a5e006 100644
--- a/common/usb_common.c
+++ b/common/usb_common.c
@@ -12,6 +12,7 @@
#include "charge_state.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
+#include "util.h"
int usb_get_battery_soc(void)
{
@@ -81,3 +82,192 @@ enum pd_cc_polarity_type get_snk_polarity(enum tcpc_cc_voltage_status cc1,
*/
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