summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Bettis <jbettis@google.com>2021-04-06 12:51:02 -0600
committerCommit Bot <commit-bot@chromium.org>2021-05-05 19:16:27 +0000
commitbb6943694a22efe71625cdaf1d7a809979b97528 (patch)
treee9a7f698df268762dab01854fa8d1bd46d3641b5
parent984c6e145b834a276e8508f54f2d5171554ed41b (diff)
downloadchrome-ec-bb6943694a22efe71625cdaf1d7a809979b97528.tar.gz
ec: Parse battery and augmented PDOs correctly.
Fixes pd_extract_pdo_power() to output the correct voltage and current for non-fixed PDOs. Add unit test. BRANCH=servo BUG=b:178484932 TEST=added Signed-off-by: Jeremy Bettis <jbettis@google.com> Change-Id: I791de41f00d75306a377e1ab9fb54409d5f537e7 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2827325 Commit-Queue: Jeremy Bettis <jbettis@chromium.org> Tested-by: Jeremy Bettis <jbettis@chromium.org> Reviewed-by: Brian Nemec <bnemec@chromium.org>
-rw-r--r--common/usb_common.c40
-rw-r--r--include/usb_common.h4
-rw-r--r--test/usb_common_test.c129
3 files changed, 155 insertions, 18 deletions
diff --git a/common/usb_common.c b/common/usb_common.c
index 970152d4f9..a00a85e469 100644
--- a/common/usb_common.c
+++ b/common/usb_common.c
@@ -256,32 +256,40 @@ int pd_find_pdo_index(uint32_t src_cap_cnt, const uint32_t * const src_caps,
void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *max_mv,
uint32_t *min_mv)
{
- uint32_t max_ma, uw;
-
- if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) {
- max_ma = 50 * (pdo & GENMASK(6, 0));
- *min_mv = 100 * ((pdo & GENMASK(15, 8)) >> 8);
- *max_mv = 100 * ((pdo & GENMASK(24, 17)) >> 17);
- max_ma = MIN(max_ma, PD_MAX_POWER_MW * 1000 / *min_mv);
- *ma = MIN(max_ma, PD_MAX_CURRENT_MA);
- return;
+ 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);
}
- *max_mv = ((pdo >> 10) & 0x3FF) * 50;
if (*max_mv == 0) {
*ma = 0;
*min_mv = 0;
return;
}
- if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
- uw = 250000 * (pdo & 0x3FF);
- max_ma = 1000 * MIN(1000 * uw, PD_MAX_POWER_MW) / *max_mv;
+
+ 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 {
- max_ma = 10 * (pdo & 0x3FF);
- max_ma = MIN(max_ma, PD_MAX_POWER_MW * 1000 / *max_mv);
+ mw = PDO_BATT_MAX_POWER(pdo);
+ max_ma = 1000 * mw / *min_mv;
}
+ max_ma = MIN(max_ma, PD_MAX_POWER_MW * 1000 / *min_mv);
*ma = MIN(max_ma, PD_MAX_CURRENT_MA);
- *min_mv = *max_mv;
}
void pd_build_request(uint32_t src_cap_cnt, const uint32_t * const src_caps,
diff --git a/include/usb_common.h b/include/usb_common.h
index 04495a17b0..27f48e579b 100644
--- a/include/usb_common.h
+++ b/include/usb_common.h
@@ -93,8 +93,8 @@ int pd_find_pdo_index(uint32_t src_cap_cnt, const uint32_t * const src_caps,
*
* @param pdo raw pdo to extract
* @param ma current of the PDO (output)
- * @param mv voltage of the PDO, or max_mv of the Augmented PDO (output)
- * @param mv voltage of the PDO, or min_mv of the Augmented PDO (output)
+ * @param max_mv maximum voltage of the PDO (output)
+ * @param min_mv minimum voltage of the PDO (output)
*/
void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *max_mv,
uint32_t *min_mv);
diff --git a/test/usb_common_test.c b/test/usb_common_test.c
index d054f4ead2..c657d67455 100644
--- a/test/usb_common_test.c
+++ b/test/usb_common_test.c
@@ -66,9 +66,138 @@ int test_pd_get_cc_state(void)
return EC_SUCCESS;
}
+/*
+ * From USB Power Delivery Specification Revision 3.0, Version 2.0
+ * Table 6-7 Power Data Object
+ */
+#define MAKE_FIXED(v, c) (0 << 30 | (v / 50) << 10 | (c / 10))
+#define MAKE_BATT(v_max, v_min, p) \
+ (1 << 30 | (v_max / 50) << 20 | (v_min / 50) << 10 | (p / 250))
+#define MAKE_VAR(v_max, v_min, c) \
+ (2 << 30 | (v_max / 50) << 20 | (v_min / 50) << 10 | (c / 10))
+#define MAKE_AUG(v_max, v_min, c) \
+ (3 << 30 | (v_max / 100) << 17 | (v_min / 100) << 8 | (c / 50))
+
+/*
+ * Tests various cases for pd_extract_pdo_power. It takes a very high voltage to
+ * exceed PD_MAX_POWER_MW without also exceeding PD_MAX_CURRENT_MA, so those
+ * tests are not particularly realistic.
+ */
+int test_pd_extract_pdo_power(void)
+{
+ uint32_t ma;
+ uint32_t max_mv;
+ uint32_t min_mv;
+
+ pd_extract_pdo_power(MAKE_FIXED(/*v=*/5000, /*c=*/3000), &ma, &max_mv,
+ &min_mv);
+ TEST_EQ(max_mv, 5000, "%d");
+ TEST_EQ(min_mv, 5000, "%d");
+ TEST_EQ(ma, 3000, "%d");
+ pd_extract_pdo_power(MAKE_FIXED(/*v=*/20000, /*c=*/2600), &ma, &max_mv,
+ &min_mv);
+ TEST_EQ(max_mv, 20000, "%d");
+ TEST_EQ(min_mv, 20000, "%d");
+ TEST_EQ(ma, 2600, "%d");
+ pd_extract_pdo_power(MAKE_FIXED(/*v=*/20000, /*c=*/4000), &ma, &max_mv,
+ &min_mv);
+ TEST_EQ(max_mv, 20000, "%d");
+ TEST_EQ(min_mv, 20000, "%d");
+ TEST_EQ(ma, 3000, "%d"); /* Capped at PD_MAX_CURRENT_MA */
+ pd_extract_pdo_power(MAKE_FIXED(/*v=*/10000, /*c=*/4000), &ma, &max_mv,
+ &min_mv);
+ TEST_EQ(max_mv, 10000, "%d");
+ TEST_EQ(min_mv, 10000, "%d");
+ TEST_EQ(ma, 3000, "%d"); /* Capped at PD_MAX_CURRENT_MA */
+ pd_extract_pdo_power(MAKE_FIXED(/*v=*/21000, /*c=*/4000), &ma, &max_mv,
+ &min_mv);
+ TEST_EQ(max_mv, 21000, "%d");
+ TEST_EQ(min_mv, 21000, "%d");
+ TEST_EQ(ma, 2857, "%d"); /* Capped at PD_MAX_POWER_MW */
+
+ pd_extract_pdo_power(MAKE_BATT(/*v_max=*/5700, /*v_min=*/3300,
+ /*p=*/7000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 5700, "%d");
+ TEST_EQ(min_mv, 3300, "%d");
+ TEST_EQ(ma, 2121, "%d"); /* 3300mV * 2121mA ~= 7000mW */
+ pd_extract_pdo_power(MAKE_BATT(/*v_max=*/3300, /*v_min=*/2700,
+ /*p=*/12000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 3300, "%d");
+ TEST_EQ(min_mv, 2700, "%d");
+ TEST_EQ(ma, 3000, "%d"); /* Capped at PD_MAX_CURRENT_MA */
+
+ pd_extract_pdo_power(MAKE_BATT(/*v_max=*/25000, /*v_min=*/21000,
+ /*p=*/61000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 25000, "%d");
+ TEST_EQ(min_mv, 21000, "%d");
+ TEST_EQ(ma, 2857, "%d"); /* Capped at PD_MAX_POWER_MW */
+
+ pd_extract_pdo_power(MAKE_VAR(/*v_max=*/5000, /*v_min=*/3300,
+ /*c=*/3000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 5000, "%d");
+ TEST_EQ(min_mv, 3300, "%d");
+ TEST_EQ(ma, 3000, "%d");
+ pd_extract_pdo_power(MAKE_VAR(/*v_max=*/20000, /*v_min=*/5000,
+ /*c=*/2600),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 20000, "%d");
+ TEST_EQ(min_mv, 5000, "%d");
+ TEST_EQ(ma, 2600, "%d");
+ pd_extract_pdo_power(MAKE_VAR(/*v_max=*/20000, /*v_min=*/5000,
+ /*c=*/4000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 20000, "%d");
+ TEST_EQ(min_mv, 5000, "%d");
+ TEST_EQ(ma, 3000, "%d"); /* Capped at PD_MAX_CURRENT_MA */
+ pd_extract_pdo_power(MAKE_VAR(/*v_max=*/10000, /*v_min=*/3300,
+ /*c=*/4000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 10000, "%d");
+ TEST_EQ(min_mv, 3300, "%d");
+ TEST_EQ(ma, 3000, "%d"); /* Capped at PD_MAX_CURRENT_MA */
+ pd_extract_pdo_power(MAKE_VAR(/*v_max=*/22000, /*v_min=*/21000,
+ /*c=*/4000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 22000, "%d");
+ TEST_EQ(min_mv, 21000, "%d");
+ TEST_EQ(ma, 2857, "%d"); /* Capped at PD_MAX_POWER_MW */
+
+ pd_extract_pdo_power(MAKE_AUG(/*v_max=*/5000, /*v_min=*/3300,
+ /*c=*/3000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 5000, "%d");
+ TEST_EQ(min_mv, 3300, "%d");
+ TEST_EQ(ma, 3000, "%d");
+ pd_extract_pdo_power(MAKE_AUG(/*v_max=*/20000, /*v_min=*/3300,
+ /*c=*/2600),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 20000, "%d");
+ TEST_EQ(min_mv, 3300, "%d");
+ TEST_EQ(ma, 2600, "%d");
+ pd_extract_pdo_power(MAKE_AUG(/*v_max=*/10000, /*v_min=*/3300,
+ /*c=*/4000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 10000, "%d");
+ TEST_EQ(min_mv, 3300, "%d");
+ TEST_EQ(ma, 3000, "%d"); /* Capped at PD_MAX_CURRENT_MA */
+ pd_extract_pdo_power(MAKE_AUG(/*v_max=*/22000, /*v_min=*/21000,
+ /*c=*/4000),
+ &ma, &max_mv, &min_mv);
+ TEST_EQ(max_mv, 22000, "%d");
+ TEST_EQ(min_mv, 21000, "%d");
+ TEST_EQ(ma, 2857, "%d"); /* Capped at PD_MAX_POWER_MW */
+
+ return EC_SUCCESS;
+}
+
void run_test(void)
{
RUN_TEST(test_pd_get_cc_state);
+ RUN_TEST(test_pd_extract_pdo_power);
test_print_result();
}