diff options
author | Yuval Peress <peress@google.com> | 2022-02-15 13:20:51 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2022-02-19 19:22:33 +0000 |
commit | 8e1b318f771f938ee77ff4afdf90c85695cc30f2 (patch) | |
tree | 864af9b18dfac9fde76e70d922fb9f5d02a8206d | |
parent | 2d653eea9c0c70bd41cc8459885d9b0c13f2d27e (diff) | |
download | chrome-ec-8e1b318f771f938ee77ff4afdf90c85695cc30f2.tar.gz |
zephyr: test: Unplug high power charger
Test scenario where a high power charger is unplugged. The test verifies:
- The battery stops charging
- The charging state for the charger chip is reset
- The type-c status shows that everything is disconnected
- The power info command shows that no power is flowing.
BRANCH=none
BUG=b:209907620
TEST=zmake configure --test test-drivers
Signed-off-by: Yuval Peress <peress@google.com>
Change-Id: I8420af8df00045882e0295104aebff514927e523
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3465530
Reviewed-by: Sam Hurst <shurst@google.com>
Commit-Queue: Sam Hurst <shurst@google.com>
-rw-r--r-- | include/usb_pd.h | 2 | ||||
-rw-r--r-- | zephyr/test/drivers/include/utils.h | 96 | ||||
-rw-r--r-- | zephyr/test/drivers/src/integration/usbc/usb.c | 61 | ||||
-rw-r--r-- | zephyr/test/drivers/src/integration/usbc/usb_20v_3a_pd_charger.c | 233 | ||||
-rw-r--r-- | zephyr/test/drivers/src/power_common.c | 19 |
5 files changed, 336 insertions, 75 deletions
diff --git a/include/usb_pd.h b/include/usb_pd.h index cf8b1cb531..4228d12fd3 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -104,6 +104,8 @@ enum pd_rx_errors { #define PDO_FIXED_PEAK_CURR () /* [21..20] Peak current */ #define PDO_FIXED_VOLT(mv) (((mv)/50) << 10) /* Voltage in 50mV units */ #define PDO_FIXED_CURR(ma) (((ma)/10) << 0) /* Max current in 10mA units */ +#define PDO_FIXED_GET_VOLT(pdo) (((pdo >> 10) & 0x3FF) * 50) +#define PDO_FIXED_GET_CURR(pdo) ((pdo & 0x3FF) * 10) #define PDO_FIXED(mv, ma, flags) (PDO_FIXED_VOLT(mv) |\ PDO_FIXED_CURR(ma) | (flags)) diff --git a/zephyr/test/drivers/include/utils.h b/zephyr/test/drivers/include/utils.h index 3dc5a97ba2..68919ec79c 100644 --- a/zephyr/test/drivers/include/utils.h +++ b/zephyr/test/drivers/include/utils.h @@ -6,7 +6,11 @@ #ifndef ZEPHYR_TEST_DRIVERS_INCLUDE_UTILS_H_ #define ZEPHYR_TEST_DRIVERS_INCLUDE_UTILS_H_ +#include <drivers/gpio/gpio_emul.h> + #include "charger.h" +#include "extpower.h" +#include "host_command.h" /** @brief Set chipset to S0 state. Call all necessary hooks. */ void test_set_chipset_to_s0(void); @@ -119,4 +123,96 @@ void test_set_chipset_to_g3(void); */ #define zassume_mem_equal(...) zassert_mem_equal(##__VA_ARGS__) +/** + * Run the host command to get the charge state for a given charger number. + * + * This function assumes a successful host command processing and will make a + * call to the zassume_* API. A failure here will abort the calling test. + * + * @param chgnum The charger number to query. + * @return The result of the query. + */ +static inline struct ec_response_charge_state host_cmd_charge_state(int chgnum) +{ + struct ec_params_charge_state params = { + .chgnum = chgnum, + .cmd = CHARGE_STATE_CMD_GET_STATE, + }; + struct ec_response_charge_state response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_CHARGE_STATE, 0, response, params); + + zassume_ok(host_command_process(&args), + "Failed to get charge state for chgnum %d", chgnum); + return response; +} + +/** + * Run the host command to get the USB PD power info for a given port. + * + * This function assumes a successful host command processing and will make a + * call to the zassume_* API. A failure here will abort the calling test. + * + * @param port The USB port to get info from. + * @return The result of the query. + */ +static inline struct ec_response_usb_pd_power_info host_cmd_power_info(int port) +{ + struct ec_params_usb_pd_power_info params = { .port = port }; + struct ec_response_usb_pd_power_info response; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND( + EC_CMD_USB_PD_POWER_INFO, 0, response, params); + + zassume_ok(host_command_process(&args), + "Failed to get power info for port %d", port); + return response; +} + +/** + * Run the host command to get the Type-C status information for a given port. + * + * This function assumes a successful host command processing and will make a + * call to the zassume_* API. A failure here will abort the calling test. + * + * @param port The USB port to get info from. + * @return The result of the query. + */ +static inline struct ec_response_typec_status host_cmd_typec_status(int port) +{ + struct ec_params_typec_status params = { .port = port }; + struct ec_response_typec_status response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_TYPEC_STATUS, 0, response, params); + + zassume_ok(host_command_process(&args), + "Failed to get Type-C state for port %d", port); + return response; +} + +#define GPIO_ACOK_OD_NODE DT_NODELABEL(gpio_acok_od) +#define GPIO_ACOK_OD_PIN DT_GPIO_PIN(GPIO_ACOK_OD_NODE, gpios) + +/** + * Set whether or not AC is enabled. + * + * If enabled, the device _should_ begin charging. + * + * This function assumes a successful gpio emulator call and will make a call + * to the zassume_* API. A failure here will abort the calling test. + * + * This function sleeps to wait for the GPIO interrupt to take place. + * + * @param enabled Whether or not to enable AC. + */ +static inline void set_ac_enabled(bool enabled) +{ + const struct device *acok_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_ACOK_OD_NODE, gpios)); + + zassume_ok(gpio_emul_input_set(acok_dev, GPIO_ACOK_OD_PIN, enabled), + NULL); + k_sleep(K_MSEC(CONFIG_EXTPOWER_DEBOUNCE_MS + 1)); + zassume_equal(enabled, extpower_is_present(), NULL); +} + #endif /* ZEPHYR_TEST_DRIVERS_INCLUDE_UTILS_H_ */ diff --git a/zephyr/test/drivers/src/integration/usbc/usb.c b/zephyr/test/drivers/src/integration/usbc/usb.c index 0959eb1880..20232555e0 100644 --- a/zephyr/test/drivers/src/integration/usbc/usb.c +++ b/zephyr/test/drivers/src/integration/usbc/usb.c @@ -116,13 +116,8 @@ static void integration_usb_after(void *state) */ static void check_charge_state(int chgnum, bool attached) { - struct ec_params_charge_state charge_params = { - .chgnum = chgnum, .cmd = CHARGE_STATE_CMD_GET_STATE}; - struct ec_response_charge_state charge_response; - struct host_cmd_handler_args args = BUILD_HOST_COMMAND( - EC_CMD_CHARGE_STATE, 0, charge_response, charge_params); - - zassert_ok(host_command_process(&args), "Failed to get charge state"); + struct ec_response_charge_state charge_response = + host_cmd_charge_state(chgnum); zassert_equal(charge_response.get_state.ac, attached, "USB default but AC absent"); /* The charging voltage and current are not directly related to the PD @@ -263,58 +258,6 @@ ZTEST(integration_usb, test_attach_5v_pd_charger) 5000, 3000); } -ZTEST(integration_usb, test_attach_20v_pd_charger) -{ - const struct emul *tcpci_emul = - emul_get_binding(DT_LABEL(TCPCI_EMUL_LABEL)); - const struct emul *charger_emul = - emul_get_binding(DT_LABEL(DT_NODELABEL(isl923x_emul))); - struct i2c_emul *i2c_emul; - uint16_t battery_status; - struct tcpci_src_emul my_charger; - const struct device *gpio_dev = - DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_AC_OK_PATH, gpios)); - - /* Attach emulated charger. Send Source Capabilities that offer 20V. Set - * the charger input voltage to ~18V (the highest voltage it supports). - */ - zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_AC_OK_PIN, 1), NULL); - tcpci_src_emul_init(&my_charger); - my_charger.data.pdo[1] = - PDO_FIXED(20000, 3000, PDO_FIXED_UNCONSTRAINED); - zassert_ok(tcpci_src_emul_connect_to_tcpci(&my_charger.data, - &my_charger.common_data, - &my_charger.ops, tcpci_emul), - NULL); - isl923x_emul_set_adc_vbus(charger_emul, 20000); - - /* Wait for PD negotiation and current ramp. - * TODO(b/213906889): Check message timing and contents. - */ - k_sleep(K_SECONDS(10)); - - /* Verify battery charging. */ - i2c_emul = sbat_emul_get_ptr(BATTERY_ORD); - zassert_ok(sbat_emul_get_word_val(i2c_emul, SB_BATTERY_STATUS, - &battery_status), - NULL); - zassert_equal(battery_status & STATUS_DISCHARGING, 0, - "Battery is discharging: %d", battery_status); - - /* Check the charging voltage and current. Cross-check the PD state, - * the battery/charger state, and the active PDO as reported by the PD - * state. The charging voltage and current are not directly related to - * the PD charging and current, but they should be positive if the - * battery is charging. - */ - check_charge_state(0, true); - check_typec_status(0, PD_ROLE_SINK, USB_CHG_TYPE_PD, 2); - - /* TODO(b/217394181): Refactor to direct assert calls */ - check_usb_pd_power_info(0, USB_PD_PORT_POWER_SINK, USB_CHG_TYPE_PD, - 20000, 3000); -} - ZTEST(integration_usb, test_attach_sink) { const struct emul *tcpci_emul = diff --git a/zephyr/test/drivers/src/integration/usbc/usb_20v_3a_pd_charger.c b/zephyr/test/drivers/src/integration/usbc/usb_20v_3a_pd_charger.c new file mode 100644 index 0000000000..b1dc9031da --- /dev/null +++ b/zephyr/test/drivers/src/integration/usbc/usb_20v_3a_pd_charger.c @@ -0,0 +1,233 @@ +/* Copyright 2022 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. + */ + +#include <ztest.h> + +#include "battery_smart.h" +#include "emul/emul_isl923x.h" +#include "emul/emul_smart_battery.h" +#include "emul/tcpc/emul_tcpci_partner_src.h" +#include "test_state.h" +#include "utils.h" +#include "usb_pd.h" + +#define BATTERY_ORD DT_DEP_ORD(DT_NODELABEL(battery)) + +struct usb_attach_20v_3a_pd_charger_fixture { + struct tcpci_src_emul charger_20v; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +static inline void +connect_charger_to_port(struct usb_attach_20v_3a_pd_charger_fixture *fixture) +{ + set_ac_enabled(true); + zassume_ok(tcpci_src_emul_connect_to_tcpci( + &fixture->charger_20v.data, + &fixture->charger_20v.common_data, + &fixture->charger_20v.ops, fixture->tcpci_emul), + NULL); + + isl923x_emul_set_adc_vbus( + fixture->charger_emul, + PDO_FIXED_GET_VOLT(fixture->charger_20v.data.pdo[1])); + + /* Wait for PD negotiation and current ramp. + * TODO(b/213906889): Check message timing and contents. + */ + k_sleep(K_SECONDS(10)); +} + +static inline void disconnect_charger_from_port( + struct usb_attach_20v_3a_pd_charger_fixture *fixture) +{ + set_ac_enabled(false); + zassume_ok(tcpci_emul_disconnect_partner(fixture->tcpci_emul), NULL); + isl923x_emul_set_adc_vbus(fixture->charger_emul, 0); + k_sleep(K_SECONDS(1)); +} + +static void *usb_attach_20v_3a_pd_charger_setup(void) +{ + static struct usb_attach_20v_3a_pd_charger_fixture test_fixture; + + /* Get references for the emulators */ + test_fixture.tcpci_emul = + emul_get_binding(DT_LABEL(DT_NODELABEL(tcpci_emul))); + test_fixture.charger_emul = + emul_get_binding(DT_LABEL(DT_NODELABEL(isl923x_emul))); + + /* Initialized the charger to supply 20V and 3A */ + tcpci_src_emul_init(&test_fixture.charger_20v); + test_fixture.charger_20v.data.pdo[1] = + PDO_FIXED(20000, 3000, PDO_FIXED_UNCONSTRAINED); + + return &test_fixture; +} + +static void usb_attach_20v_3a_pd_charger_before(void *data) +{ + connect_charger_to_port( + (struct usb_attach_20v_3a_pd_charger_fixture *)data); +} + +static void usb_attach_20v_3a_pd_charger_after(void *data) +{ + disconnect_charger_from_port( + (struct usb_attach_20v_3a_pd_charger_fixture *)data); +} + +ZTEST_SUITE(usb_attach_20v_3a_pd_charger, drivers_predicate_post_main, + usb_attach_20v_3a_pd_charger_setup, + usb_attach_20v_3a_pd_charger_before, + usb_attach_20v_3a_pd_charger_after, NULL); + +ZTEST(usb_attach_20v_3a_pd_charger, test_battery_is_charging) +{ + struct i2c_emul *i2c_emul = sbat_emul_get_ptr(BATTERY_ORD); + uint16_t battery_status; + + zassume_ok(sbat_emul_get_word_val(i2c_emul, SB_BATTERY_STATUS, + &battery_status), + NULL); + zassert_equal(battery_status & STATUS_DISCHARGING, 0, + "Battery is discharging: %d", battery_status); +} + +ZTEST(usb_attach_20v_3a_pd_charger, test_charge_state) +{ + struct ec_response_charge_state state = host_cmd_charge_state(0); + + zassert_true(state.get_state.ac, "AC_OK not triggered"); + zassert_true(state.get_state.chg_voltage > 0, + "Expected a charge voltage, but got %dmV", + state.get_state.chg_voltage); + zassert_true(state.get_state.chg_current > 0, + "Expected a charge current, but got %dmA", + state.get_state.chg_current); +} + +ZTEST(usb_attach_20v_3a_pd_charger, test_typec_status) +{ + struct ec_response_typec_status status = host_cmd_typec_status(0); + + zassert_true(status.pd_enabled, "PD is disabled"); + zassert_true(status.dev_connected, "Device disconnected"); + zassert_true(status.sop_connected, "Charger is not SOP capable"); + zassert_equal(status.source_cap_count, 2, + "Expected 2 source PDOs, but got %d", + status.source_cap_count); + zassert_equal(status.power_role, PD_ROLE_SINK, + "Expected power role to be %d, but got %d", PD_ROLE_SINK, + status.power_role); +} + +ZTEST(usb_attach_20v_3a_pd_charger, test_power_info) +{ + struct ec_response_usb_pd_power_info info = host_cmd_power_info(0); + + zassert_equal(info.role, USB_PD_PORT_POWER_SINK, + "Expected role to be %d, but got %d", + USB_PD_PORT_POWER_SINK, info.role); + zassert_equal(info.type, USB_CHG_TYPE_PD, + "Expected type to be %d, but got %d", USB_CHG_TYPE_PD, + info.type); + zassert_equal(info.meas.voltage_max, 20000, + "Expected charge voltage max of 20000mV, but got %dmV", + info.meas.voltage_max); + zassert_within( + info.meas.voltage_now, 20000, 2000, + "Charging voltage expected to be near 20000mV, but was %dmV", + info.meas.voltage_now); + zassert_equal(info.meas.current_max, 3000, + "Current max expected to be 3000mV, but was %dmV", + info.meas.current_max); + zassert_true(info.meas.current_lim >= 3000, + "VBUS max is set to 3000mA, but PD is reporting %dmA", + info.meas.current_lim); + zassert_equal(info.max_power, 20000 * 3000, + "Charging expected to be at %duW, but PD max is %duW", + 20000 * 3000, info.max_power); +} + +ZTEST_F(usb_attach_20v_3a_pd_charger, test_disconnect_battery_not_charging) +{ + struct i2c_emul *i2c_emul = sbat_emul_get_ptr(BATTERY_ORD); + uint16_t battery_status; + + disconnect_charger_from_port(this); + zassert_ok(sbat_emul_get_word_val(i2c_emul, SB_BATTERY_STATUS, + &battery_status), + NULL); + zassert_equal(battery_status & STATUS_DISCHARGING, STATUS_DISCHARGING, + "Battery is not discharging: %d", battery_status); +} + +ZTEST_F(usb_attach_20v_3a_pd_charger, test_disconnect_charge_state) +{ + struct ec_response_charge_state charge_state; + + disconnect_charger_from_port(this); + charge_state = host_cmd_charge_state(0); + + zassert_false(charge_state.get_state.ac, "AC_OK not triggered"); + zassert_equal(charge_state.get_state.chg_current, 0, + "Max charge current expected 0mA, but was %dmA", + charge_state.get_state.chg_current); + zassert_equal(charge_state.get_state.chg_input_current, + CONFIG_PLATFORM_EC_CHARGER_INPUT_CURRENT, + "Charge input current limit expected %dmA, but was %dmA", + CONFIG_PLATFORM_EC_CHARGER_INPUT_CURRENT, + charge_state.get_state.chg_input_current); +} + +ZTEST_F(usb_attach_20v_3a_pd_charger, test_disconnect_typec_status) +{ + struct ec_response_typec_status typec_status; + + disconnect_charger_from_port(this); + typec_status = host_cmd_typec_status(0); + + zassert_false(typec_status.pd_enabled, NULL); + zassert_false(typec_status.dev_connected, NULL); + zassert_false(typec_status.sop_connected, NULL); + zassert_equal(typec_status.source_cap_count, 0, + "Expected 0 source caps, but got %d", + typec_status.source_cap_count); + zassert_equal(typec_status.power_role, USB_CHG_TYPE_NONE, + "Expected power role to be %d, but got %d", + USB_CHG_TYPE_NONE, typec_status.power_role); +} + +ZTEST_F(usb_attach_20v_3a_pd_charger, test_disconnect_power_info) +{ + struct ec_response_usb_pd_power_info power_info; + + disconnect_charger_from_port(this); + power_info = host_cmd_power_info(0); + + zassert_equal(power_info.role, USB_PD_PORT_POWER_DISCONNECTED, + "Expected power role to be %d, but got %d", + USB_PD_PORT_POWER_DISCONNECTED, power_info.role); + zassert_equal(power_info.type, USB_CHG_TYPE_NONE, + "Expected charger type to be %d, but got %s", + USB_CHG_TYPE_NONE, power_info.type); + zassert_equal(power_info.max_power, 0, + "Expected the maximum power to be 0uW, but got %duW", + power_info.max_power); + zassert_equal(power_info.meas.voltage_max, 0, + "Expected maximum voltage of 0mV, but got %dmV", + power_info.meas.voltage_max); + zassert_within(power_info.meas.voltage_now, 0, 10, + "Expected present voltage near 0mV, but got %dmV", + power_info.meas.voltage_now); + zassert_equal(power_info.meas.current_max, 0, + "Expected maximum current of 0mA, but got %dmA", + power_info.meas.current_max); + zassert_true(power_info.meas.current_lim >= 0, + "Expected the PD current limit to be >= 0, but got %dmA", + power_info.meas.current_lim); +} diff --git a/zephyr/test/drivers/src/power_common.c b/zephyr/test/drivers/src/power_common.c index d24537ba34..e5eace0155 100644 --- a/zephyr/test/drivers/src/power_common.c +++ b/zephyr/test/drivers/src/power_common.c @@ -29,9 +29,6 @@ #define BATTERY_ORD DT_DEP_ORD(DT_NODELABEL(battery)) -#define GPIO_ACOK_OD_NODE DT_PATH(named_gpios, acok_od) -#define GPIO_ACOK_OD_PIN DT_GPIO_PIN(GPIO_ACOK_OD_NODE, gpios) - /* Description of all power states with chipset state masks */ static struct { /* Power state */ @@ -460,8 +457,6 @@ static void setup_hibernation_delay(void *state) struct ec_params_smart_discharge params; struct host_cmd_handler_args args = BUILD_HOST_COMMAND(EC_CMD_SMART_DISCHARGE, 0, response, params); - const struct device *acok_dev = - DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_ACOK_OD_NODE, gpios)); struct sbat_emul_bat_data *bat; struct i2c_emul *emul; ARG_UNUSED(state); @@ -486,9 +481,7 @@ static void setup_hibernation_delay(void *state) test_set_chipset_to_g3(); /* Disable AC */ - zassert_ok(gpio_emul_input_set(acok_dev, GPIO_ACOK_OD_PIN, 0), NULL); - msleep(CONFIG_EXTPOWER_DEBOUNCE_MS + 1); - zassert_equal(0, extpower_is_present(), NULL); + set_ac_enabled(false); RESET_FAKE(system_hibernate); } @@ -500,8 +493,6 @@ ZTEST(power_common_hibernation, test_power_hc_hibernation_delay) struct ec_params_hibernation_delay params; struct host_cmd_handler_args args = BUILD_HOST_COMMAND( EC_CMD_HIBERNATION_DELAY, 0, response, params); - const struct device *acok_dev = - DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_ACOK_OD_NODE, gpios)); uint32_t h_delay; int sleep_time; @@ -577,9 +568,7 @@ ZTEST(power_common_hibernation, test_power_hc_hibernation_delay) response.time_remaining); /* Enable AC */ - zassert_ok(gpio_emul_input_set(acok_dev, GPIO_ACOK_OD_PIN, 1), NULL); - msleep(CONFIG_EXTPOWER_DEBOUNCE_MS + 1); - zassert_equal(1, extpower_is_present(), NULL); + set_ac_enabled(true); /* Reset system_hibernate fake to check that it is not called on AC */ RESET_FAKE(system_hibernate); @@ -598,9 +587,7 @@ ZTEST(power_common_hibernation, test_power_hc_hibernation_delay) "system_hibernate() shouldn't be called on AC"); /* Disable AC */ - zassert_ok(gpio_emul_input_set(acok_dev, GPIO_ACOK_OD_PIN, 0), NULL); - msleep(CONFIG_EXTPOWER_DEBOUNCE_MS + 1); - zassert_equal(0, extpower_is_present(), NULL); + set_ac_enabled(false); /* Go to different state */ power_set_state(POWER_G3S5); |