diff options
Diffstat (limited to 'zephyr/test/drivers/default/src/integration')
8 files changed, 2605 insertions, 0 deletions
diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb.c b/zephyr/test/drivers/default/src/integration/usbc/usb.c new file mode 100644 index 0000000000..0436f55e93 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb.c @@ -0,0 +1,133 @@ +/* Copyright 2021 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include "battery_smart.h" +#include "ec_commands.h" +#include "ec_tasks.h" +#include "driver/tcpm/ps8xxx_public.h" +#include "emul/emul_isl923x.h" +#include "emul/emul_smart_battery.h" +#include "emul/tcpc/emul_ps8xxx.h" +#include "emul/tcpc/emul_tcpci.h" +#include "emul/tcpc/emul_tcpci_partner_drp.h" +#include "emul/tcpc/emul_tcpci_partner_snk.h" +#include "emul/tcpc/emul_tcpci_partner_src.h" +#include "host_command.h" +#include "test/drivers/stubs.h" +#include "tcpm/tcpci.h" +#include "test/usb_pe.h" +#include "test/drivers/utils.h" +#include "test/drivers/test_state.h" + +#define BATTERY_NODE DT_NODELABEL(battery) + +#define GPIO_AC_OK_PATH DT_PATH(named_gpios, acok_od) +#define GPIO_AC_OK_PIN DT_GPIO_PIN(GPIO_AC_OK_PATH, gpios) + +#define GPIO_BATT_PRES_ODL_PATH DT_PATH(named_gpios, ec_batt_pres_odl) +#define GPIO_BATT_PRES_ODL_PORT DT_GPIO_PIN(GPIO_BATT_PRES_ODL_PATH, gpios) + +static void integration_usb_before(void *state) +{ + const struct emul *tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + const struct emul *tcpci_emul2 = EMUL_GET_USBC_BINDING(1, tcpc); + const struct emul *charger_emul = EMUL_GET_USBC_BINDING(0, chg); + /* Reset vbus to 0mV */ + /* TODO(b/217610871): Remove redundant test state cleanup */ + isl923x_emul_set_adc_vbus(charger_emul, 0); + const struct emul *battery_emul = EMUL_DT_GET(BATTERY_NODE); + struct sbat_emul_bat_data *bat; + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_AC_OK_PATH, gpios)); + + ARG_UNUSED(state); + /* + * TODO(b/217755888): Refactor to using assume API + */ + zassert_ok(tcpc_config[0].drv->init(0), NULL); + zassert_ok(tcpc_config[1].drv->init(1), NULL); + tcpc_config[USBC_PORT_C0].flags &= ~TCPC_FLAGS_TCPCI_REV2_0; + tcpci_emul_set_rev(tcpci_emul, TCPCI_EMUL_REV1_0_VER1_0); + pd_set_suspend(0, 0); + pd_set_suspend(1, 0); + /* Reset to disconnected state. */ + /* + * TODO(b/217755888): Refactor to using assume API + */ + zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul), NULL); + zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul2), NULL); + + /* Battery defaults to charging, so reset to not charging. */ + bat = sbat_emul_get_bat_data(battery_emul); + bat->cur = -5; + + /* + * TODO(b/217755888): Refactor to using assume API + */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_AC_OK_PIN, 0), NULL); +} + +static void integration_usb_after(void *state) +{ + const struct emul *tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + const struct emul *tcpci_emul2 = EMUL_GET_USBC_BINDING(1, tcpc); + const struct emul *charger_emul = EMUL_GET_USBC_BINDING(0, chg); + ARG_UNUSED(state); + + /* TODO: This function should trigger gpios to signal there is nothing + * attached to the port. + */ + /* + * TODO(b/217755888): Refactor to using assume API + */ + zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul), NULL); + zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul2), NULL); + /* Give time to actually disconnect */ + k_sleep(K_SECONDS(1)); + + /* Reset vbus to 0mV */ + isl923x_emul_set_adc_vbus(charger_emul, 0); +} + +ZTEST(integration_usb, test_attach_drp) +{ + const struct emul *tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + struct tcpci_partner_data my_drp; + struct tcpci_drp_emul_data drp_ext; + struct tcpci_src_emul_data src_ext; + struct tcpci_snk_emul_data snk_ext; + + /* Set chipset to ON, this will set TCPM to DRP */ + test_set_chipset_to_s0(); + + /* TODO(b/214401892): Check why need to give time TCPM to spin */ + k_sleep(K_SECONDS(1)); + + /* Attach emulated sink */ + tcpci_partner_init(&my_drp, PD_REV20); + my_drp.extensions = tcpci_drp_emul_init( + &drp_ext, &my_drp, PD_ROLE_SINK, + tcpci_src_emul_init(&src_ext, &my_drp, NULL), + tcpci_snk_emul_init(&snk_ext, &my_drp, NULL)); + + zassert_ok(tcpci_partner_connect_to_tcpci(&my_drp, tcpci_emul), NULL); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); + + /* + * Test that SRC ready is achieved + * TODO: Change it to examining EC_CMD_TYPEC_STATUS + */ + zassert_equal(PE_SNK_READY, get_state_pe(USBC_PORT_C0), NULL); + zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul), NULL); +} + +ZTEST_SUITE(integration_usb, drivers_predicate_post_main, NULL, + integration_usb_before, integration_usb_after, NULL); diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb_20v_3a_pd_charger.c b/zephyr/test/drivers/default/src/integration/usbc/usb_20v_3a_pd_charger.c new file mode 100644 index 0000000000..3fc73337b9 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_20v_3a_pd_charger.c @@ -0,0 +1,231 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <zephyr/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/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "usb_pd.h" + +#define BATTERY_NODE DT_NODELABEL(battery) + +struct usb_attach_20v_3a_pd_charger_fixture { + struct tcpci_partner_data charger_20v; + struct tcpci_src_emul_data src_ext; + 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_partner_connect_to_tcpci(&fixture->charger_20v, + fixture->tcpci_emul), + NULL); + + isl923x_emul_set_adc_vbus(fixture->charger_emul, + PDO_FIXED_GET_VOLT(fixture->src_ext.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_USBC_BINDING(0, tcpc); + test_fixture.charger_emul = EMUL_GET_USBC_BINDING(0, chg); + + /* Initialized the charger to supply 20V and 3A */ + tcpci_partner_init(&test_fixture.charger_20v, PD_REV20); + test_fixture.charger_20v.extensions = tcpci_src_emul_init( + &test_fixture.src_ext, &test_fixture.charger_20v, NULL); + test_fixture.src_ext.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) +{ + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + uint16_t battery_status; + + zassume_ok(sbat_emul_get_word_val(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) +{ + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + uint16_t battery_status; + + disconnect_charger_from_port(fixture); + zassert_ok(sbat_emul_get_word_val(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(fixture); + 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(fixture); + 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(fixture); + 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, 5, 5, + "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/default/src/integration/usbc/usb_5v_3a_pd_sink.c b/zephyr/test/drivers/default/src/integration/usbc/usb_5v_3a_pd_sink.c new file mode 100644 index 0000000000..5654754838 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_5v_3a_pd_sink.c @@ -0,0 +1,255 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdint.h> +#include <zephyr/ztest.h> + +#include "battery_smart.h" +#include "emul/emul_isl923x.h" +#include "emul/emul_smart_battery.h" +#include "emul/tcpc/emul_tcpci_partner_snk.h" +#include "tcpm/tcpci.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "timer.h" +#include "usb_pd.h" + +struct usb_attach_5v_3a_pd_sink_fixture { + struct tcpci_partner_data sink_5v_3a; + struct tcpci_snk_emul_data snk_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +/* Chromebooks only charge PD partners at 5v */ +#define TEST_SRC_PORT_VBUS_MV 5000 +#define TEST_SRC_PORT_TARGET_MA 3000 + +#define TEST_INITIAL_SINK_CAP \ + PDO_FIXED(TEST_SRC_PORT_VBUS_MV, TEST_SRC_PORT_TARGET_MA, 0) +/* Only used to verify sink capabilities being received by SRC port */ +#define TEST_ADDITIONAL_SINK_CAP PDO_FIXED(TEST_SRC_PORT_VBUS_MV, 5000, 0) + +static void *usb_attach_5v_3a_pd_sink_setup(void) +{ + static struct usb_attach_5v_3a_pd_sink_fixture test_fixture; + + /* Get references for the emulators */ + test_fixture.tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + test_fixture.charger_emul = EMUL_GET_USBC_BINDING(0, chg); + + return &test_fixture; +} + +static void usb_attach_5v_3a_pd_sink_before(void *data) +{ + struct usb_attach_5v_3a_pd_sink_fixture *test_fixture = data; + + /* Set chipset to ON, this will set TCPM to DRP */ + test_set_chipset_to_s0(); + + /* TODO(b/214401892): Check why need to give time TCPM to spin */ + k_sleep(K_SECONDS(1)); + + /* Initialized the sink to request 5V and 3A */ + tcpci_partner_init(&test_fixture->sink_5v_3a, PD_REV20); + test_fixture->sink_5v_3a.extensions = tcpci_snk_emul_init( + &test_fixture->snk_ext, &test_fixture->sink_5v_3a, NULL); + test_fixture->snk_ext.pdo[0] = TEST_INITIAL_SINK_CAP; + test_fixture->snk_ext.pdo[1] = TEST_ADDITIONAL_SINK_CAP; + connect_sink_to_port(&test_fixture->sink_5v_3a, + test_fixture->tcpci_emul, + test_fixture->charger_emul); +} + +static void usb_attach_5v_3a_pd_sink_after(void *data) +{ + struct usb_attach_5v_3a_pd_sink_fixture *test_fixture = data; + + disconnect_sink_from_port(test_fixture->tcpci_emul); +} + +ZTEST_SUITE(usb_attach_5v_3a_pd_sink, drivers_predicate_post_main, + usb_attach_5v_3a_pd_sink_setup, usb_attach_5v_3a_pd_sink_before, + usb_attach_5v_3a_pd_sink_after, NULL); + +ZTEST_F(usb_attach_5v_3a_pd_sink, test_partner_pd_completed) +{ + zassert_true(fixture->snk_ext.pd_completed, NULL); +} + +ZTEST(usb_attach_5v_3a_pd_sink, test_battery_is_discharging) +{ + const struct emul *emul = EMUL_DT_GET(DT_NODELABEL(battery)); + uint16_t battery_status; + + zassume_ok(sbat_emul_get_word_val(emul, SB_BATTERY_STATUS, + &battery_status), + NULL); + zassert_equal(battery_status & STATUS_DISCHARGING, STATUS_DISCHARGING, + "Battery is not discharging: %d", battery_status); +} + +ZTEST(usb_attach_5v_3a_pd_sink, 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.sink_cap_count, 2, + "Expected 2 sink PDOs, but got %d", + status.sink_cap_count); + zassert_equal(status.power_role, PD_ROLE_SOURCE, + "Expected power role to be %d, but got %d", + PD_ROLE_SOURCE, status.power_role); +} + +ZTEST(usb_attach_5v_3a_pd_sink, test_power_info) +{ + struct ec_response_usb_pd_power_info info = host_cmd_power_info(0); + + zassert_equal(info.role, USB_PD_PORT_POWER_SOURCE, + "Expected role to be %d, but got %d", + USB_PD_PORT_POWER_SOURCE, info.role); + zassert_equal(info.type, USB_CHG_TYPE_NONE, + "Expected type to be %d, but got %d", USB_CHG_TYPE_NONE, + info.type); + zassert_equal(info.meas.voltage_max, 0, + "Expected charge voltage max of 0mV, but got %dmV", + info.meas.voltage_max); + zassert_within( + info.meas.voltage_now, TEST_SRC_PORT_VBUS_MV, 500, + "Charging voltage expected to be near 5000mV, but was %dmV", + info.meas.voltage_now); + zassert_equal(info.meas.current_max, TEST_SRC_PORT_TARGET_MA, + "Current max expected to be 1500mV, but was %dmV", + info.meas.current_max); + zassert_equal(info.meas.current_lim, 0, + "VBUS max is set to 0mA, but PD is reporting %dmA", + info.meas.current_lim); + zassert_equal(info.max_power, 0, + "Charging expected to be at %duW, but PD max is %duW", 0, + info.max_power); +} + +ZTEST_F(usb_attach_5v_3a_pd_sink, test_disconnect_battery_discharging) +{ + const struct emul *emul = EMUL_DT_GET(DT_NODELABEL(battery)); + uint16_t battery_status; + + disconnect_sink_from_port(fixture->tcpci_emul); + zassert_ok(sbat_emul_get_word_val(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_5v_3a_pd_sink, test_disconnect_charge_state) +{ + struct ec_response_charge_state charge_state; + + disconnect_sink_from_port(fixture->tcpci_emul); + 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_5v_3a_pd_sink, test_disconnect_typec_status) +{ + struct ec_response_typec_status typec_status; + + disconnect_sink_from_port(fixture->tcpci_emul); + 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_5v_3a_pd_sink, test_disconnect_power_info) +{ + struct ec_response_usb_pd_power_info power_info; + + disconnect_sink_from_port(fixture->tcpci_emul); + 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, 5, 5, + "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); +} + +/** + * @brief TestPurpose: Verify GotoMin message. + * + * @details + * - TCPM is configured initially as Source + * - Initiate Goto_Min request + * - Verify emulated sink PD negotiation is completed + * + * Expected Results + * - Sink completes Goto Min PD negotiation + */ +ZTEST_F(usb_attach_5v_3a_pd_sink, verify_goto_min) +{ + pd_dpm_request(0, DPM_REQUEST_GOTO_MIN); + k_sleep(K_SECONDS(1)); + + zassert_true(fixture->snk_ext.pd_completed, NULL); +} + +/** + * @brief TestPurpose: Verify Ping message. + * + * @details + * - TCPM is configured initially as Source + * - Initiate Ping request + * - Verify emulated sink received ping message + * + * Expected Results + * - Sink received ping message + */ +ZTEST_F(usb_attach_5v_3a_pd_sink, verify_ping_msg) +{ + tcpci_snk_emul_clear_ping_received(&fixture->snk_ext); + + pd_dpm_request(0, DPM_REQUEST_SEND_PING); + k_sleep(K_USEC(PD_T_SOURCE_ACTIVITY)); + + zassert_true(fixture->snk_ext.ping_received, NULL); +} diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb_5v_3a_pd_source.c b/zephyr/test/drivers/default/src/integration/usbc/usb_5v_3a_pd_source.c new file mode 100644 index 0000000000..4d89e8c0d3 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_5v_3a_pd_source.c @@ -0,0 +1,248 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <zephyr/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 "system.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "usb_pd.h" + +#define BATTERY_NODE DT_NODELABEL(battery) + +struct usb_attach_5v_3a_pd_source_fixture { + struct tcpci_partner_data source_5v_3a; + struct tcpci_src_emul_data src_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +static void *usb_attach_5v_3a_pd_source_setup(void) +{ + static struct usb_attach_5v_3a_pd_source_fixture test_fixture; + + /* Get references for the emulators */ + test_fixture.tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + test_fixture.charger_emul = EMUL_GET_USBC_BINDING(0, chg); + + /* Initialized the charger to supply 5V and 3A */ + tcpci_partner_init(&test_fixture.source_5v_3a, PD_REV20); + test_fixture.source_5v_3a.extensions = tcpci_src_emul_init( + &test_fixture.src_ext, &test_fixture.source_5v_3a, NULL); + test_fixture.src_ext.pdo[1] = + PDO_FIXED(5000, 3000, PDO_FIXED_UNCONSTRAINED); + + return &test_fixture; +} + +static void usb_attach_5v_3a_pd_source_before(void *data) +{ + struct usb_attach_5v_3a_pd_source_fixture *fixture = data; + + connect_source_to_port(&fixture->source_5v_3a, &fixture->src_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); +} + +static void usb_attach_5v_3a_pd_source_after(void *data) +{ + struct usb_attach_5v_3a_pd_source_fixture *fixture = data; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); +} + +ZTEST_SUITE(usb_attach_5v_3a_pd_source, drivers_predicate_post_main, + usb_attach_5v_3a_pd_source_setup, usb_attach_5v_3a_pd_source_before, + usb_attach_5v_3a_pd_source_after, NULL); + +ZTEST(usb_attach_5v_3a_pd_source, test_battery_is_charging) +{ + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + uint16_t battery_status; + + zassume_ok(sbat_emul_get_word_val(emul, SB_BATTERY_STATUS, + &battery_status), + NULL); + zassert_equal(battery_status & STATUS_DISCHARGING, 0, + "Battery is discharging: %d", battery_status); +} + +ZTEST(usb_attach_5v_3a_pd_source, 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_5v_3a_pd_source, 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_5v_3a_pd_source, 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, 5000, + "Expected charge voltage max of 5000mV, but got %dmV", + info.meas.voltage_max); + zassert_within( + info.meas.voltage_now, 5000, 500, + "Charging voltage expected to be near 5000mV, 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, 5000 * 3000, + "Charging expected to be at %duW, but PD max is %duW", + 5000 * 3000, info.max_power); +} + +ZTEST_F(usb_attach_5v_3a_pd_source, test_disconnect_battery_not_charging) +{ + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + uint16_t battery_status; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + zassert_ok(sbat_emul_get_word_val(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_5v_3a_pd_source, test_disconnect_charge_state) +{ + struct ec_response_charge_state charge_state; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + 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_5v_3a_pd_source, test_disconnect_typec_status) +{ + struct ec_response_typec_status typec_status; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + 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_5v_3a_pd_source, test_disconnect_power_info) +{ + struct ec_response_usb_pd_power_info power_info; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + 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, 5, 5, + "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); +} + +ZTEST(usb_attach_5v_3a_pd_source, + test_ap_can_boot_on_low_battery_while_charging) +{ + const struct emul *smart_batt_emul = EMUL_DT_GET(DT_NODELABEL(battery)); + struct sbat_emul_bat_data *batt_data = + sbat_emul_get_bat_data(smart_batt_emul); + + /* Set capacity to what gives a charge percentage less than required + * for booting the AP + * + * Capacaity is reset by emulator's ZTEST_RULE + */ + batt_data->cap = (CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON * + batt_data->design_cap / 100) - + 1; + + zassert_true(system_can_boot_ap(), NULL); +} + +ZTEST_F(usb_attach_5v_3a_pd_source, + test_ap_fails_to_boot_on_low_battery_while_not_charging) +{ + const struct emul *smart_batt_emul = EMUL_DT_GET(DT_NODELABEL(battery)); + struct sbat_emul_bat_data *batt_data = + sbat_emul_get_bat_data(smart_batt_emul); + + /* Set capacity to what gives a charge percentage less than required + * for booting the AP + * + * Capacaity is reset by emulator's ZTEST_RULE + */ + batt_data->cap = (CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON * + batt_data->design_cap / 100) - + 1; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + + zassert_false(system_can_boot_ap(), NULL); +} diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb_attach_src_snk.c b/zephyr/test/drivers/default/src/integration/usbc/usb_attach_src_snk.c new file mode 100644 index 0000000000..761bb9daf1 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_attach_src_snk.c @@ -0,0 +1,792 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include "ec_commands.h" +#include "ec_tasks.h" +#include "driver/tcpm/ps8xxx_public.h" +#include "emul/emul_isl923x.h" +#include "emul/tcpc/emul_ps8xxx.h" +#include "emul/tcpc/emul_tcpci.h" +#include "emul/tcpc/emul_tcpci_partner_snk.h" +#include "emul/tcpc/emul_tcpci_partner_src.h" +#include "host_command.h" +#include "test/drivers/stubs.h" +#include "tcpm/tcpci.h" +#include "test/usb_pe.h" +#include "test/drivers/utils.h" +#include "test/drivers/test_state.h" + +#define SNK_PORT USBC_PORT_C0 +#define SRC_PORT USBC_PORT_C1 + +#define DEFAULT_VBUS_MV 5000 + +/* Determined by CONFIG_PLATFORM_EC_USB_PD_PULLUP */ +#define DEFAULT_VBUS_SRC_PORT_MA 1500 + +/* SRC TCPCI Emulator attaches as TYPEC_CC_VOLT_RP_3_0 */ +#define DEFAULT_VBUS_SNK_PORT_MA 3000 + +#define DEFAULT_SINK_SENT_TO_SOURCE_CAP_COUNT 1 +#define DEFAULT_SOURCE_SENT_TO_SINK_CAP_COUNT 1 + +struct emul_state { + /* TODO(b/217737667): Remove driver specific code. */ + const struct emul *tcpci_generic_emul; + const struct emul *tcpci_ps8xxx_emul; + const struct emul *charger_isl923x_emul; + struct tcpci_partner_data my_src; + struct tcpci_src_emul_data src_ext; + struct tcpci_partner_data my_snk; + struct tcpci_snk_emul_data snk_ext; +}; + +struct integration_usb_attach_src_then_snk_fixture { + struct emul_state my_emulator_state; +}; + +struct integration_usb_attach_snk_then_src_fixture { + struct emul_state my_emulator_state; +}; + +static void integration_usb_setup(struct emul_state *fixture) +{ + const struct emul *tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + const struct emul *tcpci_emul2 = EMUL_GET_USBC_BINDING(1, tcpc); + const struct emul *charger_emul = EMUL_GET_USBC_BINDING(0, chg); + + /* Setting these are required because compiler believes these values are + * not compile time constants. + */ + /* + * TODO(b/217758708): emuls should be identified at compile-time. + */ + fixture->tcpci_generic_emul = tcpci_emul; + fixture->tcpci_ps8xxx_emul = tcpci_emul2; + fixture->charger_isl923x_emul = charger_emul; + + tcpci_partner_init(&fixture->my_snk, PD_REV20); + fixture->my_snk.extensions = + tcpci_snk_emul_init(&fixture->snk_ext, &fixture->my_snk, NULL); + + tcpci_partner_init(&fixture->my_src, PD_REV20); + fixture->my_src.extensions = + tcpci_src_emul_init(&fixture->src_ext, &fixture->my_src, NULL); + + /* + * TODO(b/221288815): TCPCI config flags should be compile-time + * constants + * TODO(b/209907615): Verify TCPCI rev1 on non-port partner. + */ + /* Turn TCPCI rev 2 ON */ + tcpc_config[SNK_PORT].flags |= TCPC_FLAGS_TCPCI_REV2_0; + tcpc_config[SRC_PORT].flags |= TCPC_FLAGS_TCPCI_REV2_0; +} + +static void *integration_usb_src_snk_setup(void) +{ + static struct integration_usb_attach_src_then_snk_fixture fixture_state; + + integration_usb_setup(&fixture_state.my_emulator_state); + + return &fixture_state; +} + +static void *integration_usb_snk_src_setup(void) +{ + static struct integration_usb_attach_snk_then_src_fixture fixture_state; + + integration_usb_setup(&fixture_state.my_emulator_state); + + return &fixture_state; +} + +static void attach_src_snk_common_before(struct emul_state *my_emul_state) +{ + const struct emul *tcpci_emul_src = my_emul_state->tcpci_generic_emul; + const struct emul *tcpci_emul_snk = my_emul_state->tcpci_ps8xxx_emul; + const struct emul *charger_emul = my_emul_state->charger_isl923x_emul; + + /* Reset vbus to 0mV */ + /* TODO(b/217737667): Remove driver specific code. */ + isl923x_emul_set_adc_vbus(charger_emul, 0); + + zassume_ok(tcpc_config[SNK_PORT].drv->init(SNK_PORT), NULL); + /* + * Arbitrary FW ver. The emulator should really be setting this + * during its init. + */ + tcpci_emul_set_reg(tcpci_emul_snk, PS8XXX_REG_FW_REV, 0x31); + + zassume_ok(tcpc_config[SRC_PORT].drv->init(SRC_PORT), NULL); + + pd_set_suspend(SNK_PORT, false); + pd_set_suspend(SRC_PORT, false); + + /* Reset to disconnected state. */ + zassume_ok(tcpci_emul_disconnect_partner(tcpci_emul_src), NULL); + zassume_ok(tcpci_emul_disconnect_partner(tcpci_emul_snk), NULL); + + /* Set chipset to ON, this will set TCPM to DRP */ + test_set_chipset_to_s0(); +} + +static void attach_src_snk_common_after(struct emul_state *my_emul_state) +{ + const struct emul *tcpci_generic_emul = + my_emul_state->tcpci_generic_emul; + const struct emul *tcpci_ps8xxx_emul = my_emul_state->tcpci_ps8xxx_emul; + const struct emul *charger_emul = my_emul_state->charger_isl923x_emul; + + tcpci_emul_disconnect_partner(tcpci_generic_emul); + tcpci_emul_disconnect_partner(tcpci_ps8xxx_emul); + + /* Give time to actually disconnect */ + k_sleep(K_SECONDS(1)); + + /* Reset vbus to 0mV */ + /* TODO(b/217737667): Remove driver specific code. */ + isl923x_emul_set_adc_vbus(charger_emul, 0); +} + +static void attach_emulated_snk(struct emul_state *my_emul_state) +{ + const struct emul *tcpci_emul_snk = my_emul_state->tcpci_ps8xxx_emul; + struct tcpci_partner_data *my_snk = &my_emul_state->my_snk; + uint16_t power_reg_val; + + /* Turn on VBUS detection */ + /* + * TODO(b/223901282): integration tests should not be setting vbus + * detection via registers, also this should be turned on by default. + */ + tcpci_emul_get_reg(tcpci_emul_snk, TCPC_REG_POWER_STATUS, + &power_reg_val); + tcpci_emul_set_reg(tcpci_emul_snk, TCPC_REG_POWER_STATUS, + power_reg_val | TCPC_REG_POWER_STATUS_VBUS_DET); + + /* Necessary for TCPCIv2 vSave0V detection on VBUS to reach attached + * state + */ + tcpci_emul_set_reg(tcpci_emul_snk, TCPC_REG_EXT_STATUS, + TCPC_REG_EXT_STATUS_SAFE0V); + + zassume_ok(tcpci_partner_connect_to_tcpci(my_snk, tcpci_emul_snk), + NULL); + + /* TODO(b/214401892): Check why need to give time TCPM to spin */ + k_sleep(K_SECONDS(1)); +} + +static void attach_emulated_src(struct emul_state *my_emul_state) +{ + const struct emul *tcpci_emul_src = my_emul_state->tcpci_generic_emul; + const struct emul *charger_emul = my_emul_state->charger_isl923x_emul; + struct tcpci_partner_data *my_src = &my_emul_state->my_src; + uint16_t power_reg_val; + + /* Turn on VBUS detection */ + /* + * TODO(b/223901282): integration tests should not be setting vbus + * detection via registers, also this should be turned on by default. + */ + tcpci_emul_get_reg(tcpci_emul_src, TCPC_REG_POWER_STATUS, + &power_reg_val); + tcpci_emul_set_reg(tcpci_emul_src, TCPC_REG_POWER_STATUS, + power_reg_val | TCPC_REG_POWER_STATUS_VBUS_DET); + + /* Necessary for TCPCIv2 vSave0V detection on VBUS to reach attached + * state + */ + tcpci_emul_set_reg(tcpci_emul_src, TCPC_REG_EXT_STATUS, + TCPC_REG_EXT_STATUS_SAFE0V); + + zassume_ok(tcpci_partner_connect_to_tcpci(my_src, tcpci_emul_src), + NULL); + isl923x_emul_set_adc_vbus(charger_emul, DEFAULT_VBUS_MV); +} + +static void integration_usb_attach_snk_then_src_before(void *state) +{ + struct integration_usb_attach_src_then_snk_fixture *fixture = state; + struct emul_state *my_state = &fixture->my_emulator_state; + + attach_src_snk_common_before(my_state); + + /* 1) Attach SINK */ + attach_emulated_snk(my_state); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); + + /* 2) Attach SOURCE */ + attach_emulated_src(my_state); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); +} + +static void integration_usb_attach_src_then_snk_before(void *state) +{ + struct integration_usb_attach_src_then_snk_fixture *fixture = state; + struct emul_state *my_state = &fixture->my_emulator_state; + + attach_src_snk_common_before(my_state); + + /* 1) Attach SOURCE */ + attach_emulated_src(my_state); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); + + /* 2) Attach SINK */ + attach_emulated_snk(my_state); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); +} + +static void integration_usb_attach_src_then_snk_after(void *state) +{ + struct integration_usb_attach_src_then_snk_fixture *fixture = state; + + attach_src_snk_common_after(&fixture->my_emulator_state); +} + +static void integration_usb_attach_snk_then_src_after(void *state) +{ + struct integration_usb_attach_snk_then_src_fixture *fixture = state; + + attach_src_snk_common_after(&fixture->my_emulator_state); +} + +ZTEST_F(integration_usb_attach_src_then_snk, verify_snk_port_pd_info) +{ + struct ec_response_usb_pd_power_info response; + + response = host_cmd_power_info(SNK_PORT); + + /* Assert */ + zassert_equal(response.role, USB_PD_PORT_POWER_SINK, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_SINK, response.role); + zassert_equal(response.type, USB_CHG_TYPE_PD, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_PD, response.type); + + zassert_equal(response.meas.voltage_max, DEFAULT_VBUS_MV, + "Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, response.meas.voltage_max); + + zassert_within(response.meas.voltage_now, DEFAULT_VBUS_MV, + DEFAULT_VBUS_MV / 10, + "Actually charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, response.meas.voltage_now); + + zassert_equal(response.meas.current_max, DEFAULT_VBUS_SNK_PORT_MA, + "Charging at VBUS max %dmA, but PD reports %dmA", + DEFAULT_VBUS_SNK_PORT_MA, response.meas.current_max); + + zassert_true(response.meas.current_lim >= DEFAULT_VBUS_SNK_PORT_MA, + "Charging at VBUS max %dmA, but PD current limit %dmA", + DEFAULT_VBUS_SNK_PORT_MA, response.meas.current_lim); + + zassert_equal(response.max_power, + DEFAULT_VBUS_MV * DEFAULT_VBUS_SNK_PORT_MA, + "Charging up to %duW, PD max power %duW", + DEFAULT_VBUS_MV * DEFAULT_VBUS_SNK_PORT_MA, + response.max_power); +} + +ZTEST_F(integration_usb_attach_src_then_snk, verify_src_port_pd_info) +{ + struct ec_response_usb_pd_power_info response; + + response = host_cmd_power_info(SRC_PORT); + + /* Assert */ + zassert_equal(response.role, USB_PD_PORT_POWER_SOURCE, + "Power role %d, but PD reports role %d", PD_ROLE_SOURCE, + response.role); + + zassert_equal(response.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, response.type); + + /* TODO(b/209907615): Confirm measure value requirements */ + zassert_within(response.meas.voltage_now, DEFAULT_VBUS_MV, + DEFAULT_VBUS_MV / 10, + "Expected Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, response.meas.voltage_now); + + zassert_equal(response.meas.current_max, DEFAULT_VBUS_SRC_PORT_MA, + "Charging at VBUS max %dmA, but PD reports %dmA", + DEFAULT_VBUS_SRC_PORT_MA, response.meas.current_max); + + /* Note: We are the source so we skip checking: */ + /* meas.voltage_max */ + /* max_power */ + /* current limit */ +} + +ZTEST_F(integration_usb_attach_snk_then_src, verify_snk_port_pd_info) +{ + struct ec_response_usb_pd_power_info response; + + response = host_cmd_power_info(SNK_PORT); + + /* Assert */ + zassert_equal(response.role, USB_PD_PORT_POWER_SINK, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_SINK, response.role); + zassert_equal(response.type, USB_CHG_TYPE_PD, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_PD, response.type); + + /* Verify Default 5V and 3A */ + zassert_equal(response.meas.voltage_max, DEFAULT_VBUS_MV, + "Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, response.meas.voltage_max); + + zassert_within(response.meas.voltage_now, DEFAULT_VBUS_MV, + DEFAULT_VBUS_MV / 10, + "Actually charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, response.meas.voltage_now); + + zassert_equal(response.meas.current_max, DEFAULT_VBUS_SNK_PORT_MA, + "Charging at VBUS max %dmA, but PD reports %dmA", + DEFAULT_VBUS_SNK_PORT_MA, response.meas.current_max); + + zassert_true(response.meas.current_lim >= DEFAULT_VBUS_SNK_PORT_MA, + "Charging at VBUS max %dmA, but PD current limit %dmA", + DEFAULT_VBUS_SNK_PORT_MA, response.meas.current_lim); + + zassert_equal(response.max_power, + DEFAULT_VBUS_MV * DEFAULT_VBUS_SNK_PORT_MA, + "Charging up to %duW, PD max power %duW", + DEFAULT_VBUS_MV * DEFAULT_VBUS_SNK_PORT_MA, + response.max_power); +} + +ZTEST_F(integration_usb_attach_snk_then_src, verify_src_port_pd_info) +{ + struct ec_response_usb_pd_power_info response; + + response = host_cmd_power_info(SRC_PORT); + + /* Assert */ + zassert_equal(response.role, USB_PD_PORT_POWER_SOURCE, + "Power role %d, but PD reports role %d", PD_ROLE_SOURCE, + response.role); + + zassert_equal(response.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, response.type); + + /* Verify Default 5V and 3A */ + /* TODO(b/209907615): Confirm measure value requirements */ + zassert_within(response.meas.voltage_now, DEFAULT_VBUS_MV, + DEFAULT_VBUS_MV / 10, + "Expected Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, response.meas.voltage_now); + + zassert_equal(response.meas.current_max, DEFAULT_VBUS_SRC_PORT_MA, + "Charging at VBUS max %dmA, but PD reports %dmA", + DEFAULT_VBUS_SRC_PORT_MA, response.meas.current_max); + + /* Note: We are the source so we skip checking: */ + /* meas.voltage_max */ + /* max_power */ + /* current limit */ +} + +ZTEST_F(integration_usb_attach_src_then_snk, verify_snk_port_typec_status) +{ + struct ec_response_typec_status response = + host_cmd_typec_status(SNK_PORT); + + zassert_true(response.pd_enabled, "Source attached but PD disabled"); + + zassert_true(response.dev_connected, + "Source attached but device disconnected"); + + zassert_true(response.sop_connected, + "Source attached but not SOP capable"); + + zassert_equal(response.source_cap_count, + DEFAULT_SOURCE_SENT_TO_SINK_CAP_COUNT, + "Source received %d source PDOs", + response.source_cap_count); + + /* The source emulator is being attached to a sink port (our policy + * engine) so it does not send any sink caps, so sink port received no + * sink caps. + */ + zassert_equal(response.sink_cap_count, 0, "Port received %d sink PDOs", + response.sink_cap_count); + + zassert_equal(response.power_role, PD_ROLE_SINK, + "Source attached, but TCPM power role is %d", + response.power_role); +} + +ZTEST_F(integration_usb_attach_src_then_snk, verify_src_port_typec_status) +{ + struct ec_response_typec_status response = + host_cmd_typec_status(SRC_PORT); + + zassert_true(response.pd_enabled, "Sink attached but PD disabled"); + + zassert_true(response.dev_connected, + "Sink attached but device disconnected"); + + zassert_true(response.sop_connected, + "Sink attached but not SOP capable"); + + /* The sink emulator is being attached to a source port (our policy + * engine) so it does not send any sink caps, so source port received no + * sink caps. + */ + zassert_equal(response.source_cap_count, 0, + "Port received %d source PDOs", + response.source_cap_count); + + zassert_equal(response.sink_cap_count, + DEFAULT_SINK_SENT_TO_SOURCE_CAP_COUNT, + "Port received %d sink PDOs", response.sink_cap_count); + + zassert_equal(response.power_role, PD_ROLE_SOURCE, + "Sink attached, but TCPM power role is %d", + response.power_role); +} + +ZTEST_F(integration_usb_attach_snk_then_src, verify_snk_port_typec_status) +{ + struct ec_response_typec_status response = + host_cmd_typec_status(SNK_PORT); + + zassert_true(response.pd_enabled, "Source attached but PD disabled"); + + zassert_true(response.dev_connected, + "Source attached but device disconnected"); + + zassert_true(response.sop_connected, + "Source attached but not SOP capable"); + + zassert_equal(response.source_cap_count, + DEFAULT_SOURCE_SENT_TO_SINK_CAP_COUNT, + "Source received %d source PDOs", + response.source_cap_count); + + /* The source emulator is being attached to a sink port (our policy + * engine) so it does not send any sink caps, so sink port received no + * sink caps. + */ + zassert_equal(response.sink_cap_count, 0, "Port received %d sink PDOs", + response.sink_cap_count); + + zassert_equal(response.power_role, PD_ROLE_SINK, + "Source attached, but TCPM power role is %d", + response.power_role); +} + +ZTEST_F(integration_usb_attach_snk_then_src, verify_src_port_typec_status) +{ + struct ec_response_typec_status response = + host_cmd_typec_status(SRC_PORT); + + zassert_true(response.pd_enabled, "Sink attached but PD disabled"); + + zassert_true(response.dev_connected, + "Sink attached but device disconnected"); + + zassert_true(response.sop_connected, + "Sink attached but not SOP capable"); + + /* The sink emulator is being attached to a source port (our policy + * engine) so it does not send any sink caps, so source port received no + * sink caps. + */ + zassert_equal(response.source_cap_count, 0, + "Port received %d source PDOs", + response.source_cap_count); + + zassert_equal(response.sink_cap_count, + DEFAULT_SINK_SENT_TO_SOURCE_CAP_COUNT, + "Port received %d sink PDOs", response.sink_cap_count); + + zassert_equal(response.power_role, PD_ROLE_SOURCE, + "Sink attached, but TCPM power role is %d", + response.power_role); +} + +ZTEST_SUITE(integration_usb_attach_src_then_snk, drivers_predicate_post_main, + integration_usb_src_snk_setup, + integration_usb_attach_src_then_snk_before, + integration_usb_attach_src_then_snk_after, NULL); + +ZTEST_SUITE(integration_usb_attach_snk_then_src, drivers_predicate_post_main, + integration_usb_snk_src_setup, + integration_usb_attach_snk_then_src_before, + integration_usb_attach_snk_then_src_after, NULL); + +struct usb_detach_test_fixture { + struct emul_state fixture; +}; + +static void integration_usb_test_detach(const struct emul *e) +{ + zassume_ok(tcpci_emul_disconnect_partner(e), NULL); +} + +static void integration_usb_test_sink_detach(struct emul_state *fixture) +{ + integration_usb_test_detach(fixture->tcpci_ps8xxx_emul); +} + +static void integration_usb_test_source_detach(struct emul_state *fixture) +{ + integration_usb_test_detach(fixture->tcpci_generic_emul); +} + +void *usb_detach_test_setup(void) +{ + static struct usb_detach_test_fixture usb_detach_fixture = { 0 }; + + integration_usb_setup(&usb_detach_fixture.fixture); + + return &usb_detach_fixture; +} + +static void usb_detach_test_before(void *state) +{ + struct usb_detach_test_fixture *fixture = state; + struct emul_state *my_state = &fixture->fixture; + + attach_src_snk_common_before(my_state); + + /* 1) Attach SINK */ + attach_emulated_snk(my_state); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); + + /* 2) Attach SOURCE */ + attach_emulated_src(my_state); + + /* Wait for PD negotiation */ + k_sleep(K_SECONDS(10)); +} + +static void usb_detach_test_after(void *state) +{ + struct usb_detach_test_fixture *fixture = state; + + attach_src_snk_common_after(&fixture->fixture); +} + +ZTEST_F(usb_detach_test, verify_detach_src_snk) +{ + struct emul_state *emul_state = &fixture->fixture; + struct ec_response_usb_pd_power_info src_power_info = { 0 }; + struct ec_response_usb_pd_power_info snk_power_info = { 0 }; + + integration_usb_test_source_detach(emul_state); + integration_usb_test_sink_detach(emul_state); + + k_sleep(K_SECONDS(10)); + isl923x_emul_set_adc_vbus(emul_state->charger_isl923x_emul, 0); + + snk_power_info = host_cmd_power_info(SNK_PORT); + src_power_info = host_cmd_power_info(SRC_PORT); + + /* Validate Sink power info */ + zassert_equal(snk_power_info.role, USB_PD_PORT_POWER_DISCONNECTED, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_DISCONNECTED, snk_power_info.role); + zassert_equal(snk_power_info.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, snk_power_info.type); + + zassert_equal(snk_power_info.meas.voltage_max, 0, + "Charging at VBUS %dmV, but PD reports %dmV", 0, + snk_power_info.meas.voltage_max); + + zassert_within(snk_power_info.meas.voltage_now, 0, 10, + "Actually charging at VBUS %dmV, but PD reports %dmV", 0, + snk_power_info.meas.voltage_now); + + zassert_equal(snk_power_info.meas.current_max, 0, + "Charging at VBUS max %dmA, but PD reports %dmA", 0, + snk_power_info.meas.current_max); + + zassert_true(snk_power_info.meas.current_lim >= 0, + "Charging at VBUS max %dmA, but PD current limit %dmA", 0, + snk_power_info.meas.current_lim); + + zassert_equal(snk_power_info.max_power, 0, + "Charging up to %duW, PD max power %duW", 0, + snk_power_info.max_power); + + /* Validate Source power info */ + zassert_equal(src_power_info.role, USB_PD_PORT_POWER_DISCONNECTED, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_DISCONNECTED, src_power_info.role); + + zassert_equal(src_power_info.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, src_power_info.type); + + /* TODO(b/209907615): Confirm measure value requirements */ + zassert_within(src_power_info.meas.voltage_now, 0, 10, + "Expected Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, src_power_info.meas.voltage_now); + + zassert_equal(src_power_info.meas.current_max, 0, + "Charging at VBUS max %dmA, but PD reports %dmA", 0, + src_power_info.meas.current_max); +} + +ZTEST_F(usb_detach_test, verify_detach_snk_src) +{ + struct emul_state *emul_state = &fixture->fixture; + struct ec_response_usb_pd_power_info src_power_info = { 0 }; + struct ec_response_usb_pd_power_info snk_power_info = { 0 }; + + integration_usb_test_sink_detach(emul_state); + integration_usb_test_source_detach(emul_state); + + k_sleep(K_SECONDS(10)); + isl923x_emul_set_adc_vbus(emul_state->charger_isl923x_emul, 0); + + snk_power_info = host_cmd_power_info(SNK_PORT); + src_power_info = host_cmd_power_info(SRC_PORT); + + /* Validate Sink power info */ + zassert_equal(snk_power_info.role, USB_PD_PORT_POWER_DISCONNECTED, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_DISCONNECTED, snk_power_info.role); + zassert_equal(snk_power_info.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, snk_power_info.type); + + zassert_equal(snk_power_info.meas.voltage_max, 0, + "Charging at VBUS %dmV, but PD reports %dmV", 0, + snk_power_info.meas.voltage_max); + + zassert_within(snk_power_info.meas.voltage_now, 0, 10, + "Actually charging at VBUS %dmV, but PD reports %dmV", 0, + snk_power_info.meas.voltage_now); + + zassert_equal(snk_power_info.meas.current_max, 0, + "Charging at VBUS max %dmA, but PD reports %dmA", 0, + snk_power_info.meas.current_max); + + zassert_true(snk_power_info.meas.current_lim >= 0, + "Charging at VBUS max %dmA, but PD current limit %dmA", 0, + snk_power_info.meas.current_lim); + + zassert_equal(snk_power_info.max_power, 0, + "Charging up to %duW, PD max power %duW", 0, + snk_power_info.max_power); + + /* Validate Source power info */ + zassert_equal(src_power_info.role, USB_PD_PORT_POWER_DISCONNECTED, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_DISCONNECTED, src_power_info.role); + + zassert_equal(src_power_info.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, src_power_info.type); + + /* TODO(b/209907615): Confirm measure value requirements */ + zassert_within(src_power_info.meas.voltage_now, 0, 10, + "Expected Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, src_power_info.meas.voltage_now); + + zassert_equal(src_power_info.meas.current_max, 0, + "Charging at VBUS max %dmA, but PD reports %dmA", 0, + src_power_info.meas.current_max); +} + +ZTEST_F(usb_detach_test, verify_detach_sink) +{ + struct emul_state *emul_state = &fixture->fixture; + struct ec_response_usb_pd_power_info pd_power_info = { 0 }; + + integration_usb_test_sink_detach(emul_state); + k_sleep(K_SECONDS(10)); + isl923x_emul_set_adc_vbus(emul_state->charger_isl923x_emul, 0); + + pd_power_info = host_cmd_power_info(SNK_PORT); + + /* Assert */ + zassert_equal(pd_power_info.role, USB_PD_PORT_POWER_SINK, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_SINK, pd_power_info.role); + zassert_equal(pd_power_info.type, USB_CHG_TYPE_PD, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_PD, pd_power_info.type); + + zassert_equal(pd_power_info.meas.voltage_max, DEFAULT_VBUS_MV, + "Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, pd_power_info.meas.voltage_max); + + zassert_within(pd_power_info.meas.voltage_now, 0, 10, + "Actually charging at VBUS %dmV, but PD reports %dmV", 0, + pd_power_info.meas.voltage_now); + + zassert_equal(pd_power_info.meas.current_max, DEFAULT_VBUS_SNK_PORT_MA, + "Charging at VBUS max %dmA, but PD reports %dmA", + DEFAULT_VBUS_SNK_PORT_MA, pd_power_info.meas.current_max); + + zassert_true(pd_power_info.meas.current_lim >= 500, + "Charging at VBUS max %dmA, but PD current limit %dmA", + 500, pd_power_info.meas.current_lim); + + zassert_equal(pd_power_info.max_power, + DEFAULT_VBUS_MV * DEFAULT_VBUS_SNK_PORT_MA, + "Charging up to %duW, PD max power %duW", + DEFAULT_VBUS_MV * DEFAULT_VBUS_SNK_PORT_MA, + pd_power_info.max_power); +} + +ZTEST_F(usb_detach_test, verify_detach_source) +{ + struct emul_state *emul_state = &fixture->fixture; + struct ec_response_usb_pd_power_info pd_power_info = { SNK_PORT }; + + integration_usb_test_source_detach(emul_state); + k_sleep(K_SECONDS(10)); + isl923x_emul_set_adc_vbus(emul_state->charger_isl923x_emul, 0); + + pd_power_info = host_cmd_power_info(SNK_PORT); + + /* Assert */ + zassert_equal(pd_power_info.role, USB_PD_PORT_POWER_DISCONNECTED, + "Power role %d, but PD reports role %d", + USB_PD_PORT_POWER_DISCONNECTED, pd_power_info.role); + + zassert_equal(pd_power_info.type, USB_CHG_TYPE_NONE, + "Charger type %d, but PD reports type %d", + USB_CHG_TYPE_NONE, pd_power_info.type); + + /* TODO(b/209907615): Confirm measure value requirements */ + zassert_within(pd_power_info.meas.voltage_now, 0, 10, + "Expected Charging at VBUS %dmV, but PD reports %dmV", + DEFAULT_VBUS_MV, pd_power_info.meas.voltage_now); + + zassert_equal(pd_power_info.meas.current_max, 0, + "Charging at VBUS max %dmA, but PD reports %dmA", 0, + pd_power_info.meas.current_max); +} + +ZTEST_SUITE(usb_detach_test, drivers_predicate_post_main, + integration_usb_src_snk_setup, usb_detach_test_before, + usb_detach_test_after, NULL); diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb_pd_bist_shared.c b/zephyr/test/drivers/default/src/integration/usbc/usb_pd_bist_shared.c new file mode 100644 index 0000000000..9c76f862f8 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_pd_bist_shared.c @@ -0,0 +1,193 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <zephyr/ztest.h> + +#include "emul/emul_isl923x.h" +#include "emul/emul_smart_battery.h" +#include "emul/tcpc/emul_tcpci_partner_snk.h" +#include "test/drivers/stubs.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "usb_common.h" +#include "usb_pd.h" +#include "util.h" + +struct usb_pd_bist_shared_fixture { + struct tcpci_partner_data sink_5v_500ma; + struct tcpci_snk_emul_data snk_ext_500ma; + struct tcpci_partner_data src; + struct tcpci_src_emul_data src_ext; + const struct emul *tcpci_emul; /* USBC_PORT_C0 in dts */ + const struct emul *tcpci_ps8xxx_emul; /* USBC_PORT_C1 in dts */ + const struct emul *charger_emul; +}; + +static void *usb_pd_bist_shared_setup(void) +{ + static struct usb_pd_bist_shared_fixture test_fixture; + + /* Get references for the emulators */ + test_fixture.tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + test_fixture.charger_emul = EMUL_GET_USBC_BINDING(0, chg); + test_fixture.tcpci_ps8xxx_emul = EMUL_GET_USBC_BINDING(1, tcpc); + + return &test_fixture; +} + +static void usb_pd_bist_shared_before(void *data) +{ + struct usb_pd_bist_shared_fixture *test_fixture = data; + + /* Set chipset to ON, this will set TCPM to DRP */ + test_set_chipset_to_s0(); + + /* TODO(b/214401892): Check why need to give time TCPM to spin */ + k_sleep(K_SECONDS(1)); + + /* Initialized the sink to request 5V and 500mA */ + tcpci_partner_init(&test_fixture->sink_5v_500ma, PD_REV30); + test_fixture->sink_5v_500ma.extensions = + tcpci_snk_emul_init(&test_fixture->snk_ext_500ma, + &test_fixture->sink_5v_500ma, NULL); + test_fixture->snk_ext_500ma.pdo[0] = PDO_FIXED(5000, 500, 0); + + /* Initialized the source */ + tcpci_partner_init(&test_fixture->src, PD_REV30); + test_fixture->src.extensions = tcpci_src_emul_init( + &test_fixture->src_ext, &test_fixture->src, NULL); + + /* Initially connect the 5V 500mA partner to C0 */ + connect_sink_to_port(&test_fixture->sink_5v_500ma, + test_fixture->tcpci_emul, + test_fixture->charger_emul); +} + +static void usb_pd_bist_shared_after(void *data) +{ + struct usb_pd_bist_shared_fixture *test_fixture = data; + + /* Disocnnect C0 as sink, C1 as source */ + disconnect_sink_from_port(test_fixture->tcpci_emul); + disconnect_source_from_port(test_fixture->tcpci_ps8xxx_emul, + test_fixture->charger_emul); +} + +ZTEST_SUITE(usb_pd_bist_shared, drivers_predicate_post_main, + usb_pd_bist_shared_setup, usb_pd_bist_shared_before, + usb_pd_bist_shared_after, NULL); + +ZTEST_F(usb_pd_bist_shared, verify_bist_shared_mode) +{ + uint32_t bist_data; + uint32_t f5v_cap; + + /* + * Verify we were offered the 1.5A source cap because of our low current + * needs initially + */ + f5v_cap = fixture->snk_ext_500ma.last_5v_source_cap; + /* Capability should be 5V fixed, 1.5 A */ + zassert_equal((f5v_cap & PDO_TYPE_MASK), PDO_TYPE_FIXED, + "PDO type wrong"); + zassert_equal(PDO_FIXED_VOLTAGE(f5v_cap), 5000, "PDO voltage wrong"); + zassert_equal(PDO_FIXED_CURRENT(f5v_cap), 1500, + "PDO initial current wrong"); + + /* Start up BIST shared test mode */ + bist_data = BDO(BDO_MODE_SHARED_ENTER, 0); + zassume_ok(tcpci_partner_send_data_msg(&fixture->sink_5v_500ma, + PD_DATA_BIST, &bist_data, 1, 0), + "Failed to send BIST enter message"); + + /* The DUT has tBISTSharedTestMode (1 second) to offer us 3A now */ + k_sleep(K_SECONDS(1)); + + f5v_cap = fixture->snk_ext_500ma.last_5v_source_cap; + /* Capability should be 5V fixed, 3.0 A */ + zassert_equal((f5v_cap & PDO_TYPE_MASK), PDO_TYPE_FIXED, + "PDO type wrong"); + zassert_equal(PDO_FIXED_VOLTAGE(f5v_cap), 5000, "PDO voltage wrong"); + zassert_equal(PDO_FIXED_CURRENT(f5v_cap), 3000, + "PDO current didn't increase in BIST mode"); + + /* Leave BIST shared test mode */ + bist_data = BDO(BDO_MODE_SHARED_EXIT, 0); + zassume_ok(tcpci_partner_send_data_msg(&fixture->sink_5v_500ma, + PD_DATA_BIST, &bist_data, 1, 0), + "Failed to send BIST exit message"); + + /* + * The DUT may now execute ErrorRecovery or simply send a new + * Source_Cap. Either way, we should go back to 1.5 A + */ + k_sleep(K_SECONDS(5)); + + f5v_cap = fixture->snk_ext_500ma.last_5v_source_cap; + /* Capability should be 5V fixed, 1.5 A */ + zassert_equal((f5v_cap & PDO_TYPE_MASK), PDO_TYPE_FIXED, + "PDO type wrong"); + zassert_equal(PDO_FIXED_VOLTAGE(f5v_cap), 5000, "PDO voltage wrong"); + zassert_equal(PDO_FIXED_CURRENT(f5v_cap), 1500, + "PDO current didn't decrease after BIST exit"); +} + +ZTEST_F(usb_pd_bist_shared, verify_bist_shared_no_snk_entry) +{ + uint32_t bist_data; + uint32_t f5v_cap; + + /* + * Ensure we only enter BIST shared mode when acting as a source. We + * must not enter shared mode from PE_SNK_Ready. + */ + + /* Attach a new source */ + connect_source_to_port(&fixture->src, &fixture->src_ext, 1, + fixture->tcpci_ps8xxx_emul, + fixture->charger_emul); + + /* Have the source send the BIST Enter Mode */ + bist_data = BDO(BDO_MODE_SHARED_ENTER, 0); + zassume_ok(tcpci_partner_send_data_msg(&fixture->src, PD_DATA_BIST, + &bist_data, 1, 0), + "Failed to send BIST enter message"); + + /* Wait tBISTSharedTestMode (1 second) */ + k_sleep(K_SECONDS(1)); + + /* Verify our low power sink on C0 still only has 1.5 A */ + f5v_cap = fixture->snk_ext_500ma.last_5v_source_cap; + /* Capability should be 5V fixed, 1.5 A */ + zassert_equal((f5v_cap & PDO_TYPE_MASK), PDO_TYPE_FIXED, + "PDO type wrong"); + zassert_equal(PDO_FIXED_VOLTAGE(f5v_cap), 5000, "PDO voltage wrong"); + zassert_equal(PDO_FIXED_CURRENT(f5v_cap), 1500, + "PDO current incorrect after bad BIST entry"); +} + +ZTEST_F(usb_pd_bist_shared, verify_bist_shared_exit_no_action) +{ + uint32_t bist_data; + uint32_t f5v_cap; + + /* + * Verify that if we receive a BIST shared mode exit with no entry, we + * take no action on the port. + */ + tcpci_snk_emul_clear_last_5v_cap(&fixture->snk_ext_500ma); + + bist_data = BDO(BDO_MODE_SHARED_EXIT, 0); + zassume_ok(tcpci_partner_send_data_msg(&fixture->sink_5v_500ma, + PD_DATA_BIST, &bist_data, 1, 0), + "Failed to send BIST exit message"); + + /* Wait for the time it would take to settle out exit */ + k_sleep(K_SECONDS(5)); + + /* Verify we didn't receive any new source caps due to the mode exit */ + f5v_cap = fixture->snk_ext_500ma.last_5v_source_cap; + zassert_equal(f5v_cap, 0, "Received unexpected source cap"); +} diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb_pd_ctrl_msg.c b/zephyr/test/drivers/default/src/integration/usbc/usb_pd_ctrl_msg.c new file mode 100644 index 0000000000..894deaed13 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_pd_ctrl_msg.c @@ -0,0 +1,395 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdint.h> +#include <zephyr/ztest.h> + +#include "common.h" +#include "ec_tasks.h" +#include "emul/emul_isl923x.h" +#include "emul/tcpc/emul_tcpci_partner_drp.h" +#include "tcpm/tcpci.h" +#include "test/drivers/stubs.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "test/usb_pe.h" +#include "usb_pd.h" + +#define TEST_USB_PORT 0 +BUILD_ASSERT(TEST_USB_PORT == USBC_PORT_C0); + +#define TEST_ADDED_PDO PDO_FIXED(10000, 3000, PDO_FIXED_UNCONSTRAINED) + +struct usb_pd_ctrl_msg_test_fixture { + struct tcpci_partner_data partner_emul; + struct tcpci_snk_emul_data snk_ext; + struct tcpci_src_emul_data src_ext; + struct tcpci_drp_emul_data drp_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; + enum pd_power_role drp_partner_pd_role; +}; + +struct usb_pd_ctrl_msg_test_sink_fixture { + struct usb_pd_ctrl_msg_test_fixture fixture; +}; + +struct usb_pd_ctrl_msg_test_source_fixture { + struct usb_pd_ctrl_msg_test_fixture fixture; +}; + +static void +tcpci_drp_emul_connect_partner(struct tcpci_partner_data *partner_emul, + const struct emul *tcpci_emul, + const struct emul *charger_emul) +{ + /* + * TODO(b/221439302) Updating the TCPCI emulator registers, updating the + * vbus, as well as alerting should all be a part of the connect + * function. + */ + isl923x_emul_set_adc_vbus(charger_emul, 0); + tcpci_emul_set_reg(tcpci_emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_VBUS_DET); + + tcpci_emul_set_reg(tcpci_emul, TCPC_REG_EXT_STATUS, + TCPC_REG_EXT_STATUS_SAFE0V); + + tcpci_tcpc_alert(TEST_USB_PORT); + + zassume_ok(tcpci_partner_connect_to_tcpci(partner_emul, tcpci_emul), + NULL); +} + +static void disconnect_partner(struct usb_pd_ctrl_msg_test_fixture *fixture) +{ + zassume_ok(tcpci_emul_disconnect_partner(fixture->tcpci_emul), NULL); + k_sleep(K_SECONDS(1)); +} + +static void *usb_pd_ctrl_msg_setup_emul(void) +{ + static struct usb_pd_ctrl_msg_test_fixture fixture; + + /* Get references for the emulators */ + fixture.tcpci_emul = EMUL_GET_USBC_BINDING(TEST_USB_PORT, tcpc); + fixture.charger_emul = EMUL_GET_USBC_BINDING(TEST_USB_PORT, chg); + + return &fixture; +} + +static void *usb_pd_ctrl_msg_sink_setup(void) +{ + struct usb_pd_ctrl_msg_test_fixture *fixture = + usb_pd_ctrl_msg_setup_emul(); + + fixture->drp_partner_pd_role = PD_ROLE_SINK; + + return fixture; +} + +static void *usb_pd_ctrl_msg_source_setup(void) +{ + struct usb_pd_ctrl_msg_test_fixture *fixture = + usb_pd_ctrl_msg_setup_emul(); + + fixture->drp_partner_pd_role = PD_ROLE_SOURCE; + + return fixture; +} + +static void usb_pd_ctrl_msg_before(void *data) +{ + struct usb_pd_ctrl_msg_test_fixture *fixture = data; + + set_test_runner_tid(); + + test_set_chipset_to_g3(); + k_sleep(K_SECONDS(1)); + + /* Set chipset to ON, this will set TCPM to DRP */ + test_set_chipset_to_s0(); + + /* TODO(b/214401892): Check why need to give time TCPM to spin */ + k_sleep(K_SECONDS(1)); + + /* Initialized DRP */ + tcpci_partner_init(&fixture->partner_emul, PD_REV20); + fixture->partner_emul.extensions = tcpci_drp_emul_init( + &fixture->drp_ext, &fixture->partner_emul, + fixture->drp_partner_pd_role, + tcpci_src_emul_init(&fixture->src_ext, &fixture->partner_emul, + NULL), + tcpci_snk_emul_init(&fixture->snk_ext, &fixture->partner_emul, + NULL)); + /* Add additional Sink PDO to partner to verify + * PE_DR_SNK_Get_Sink_Cap/PE_SRC_Get_Sink_Cap (these are shared PE + * states) state was reached + */ + fixture->snk_ext.pdo[1] = TEST_ADDED_PDO; + + /* Turn TCPCI rev 2 ON */ + tcpc_config[TEST_USB_PORT].flags |= TCPC_FLAGS_TCPCI_REV2_0; + + tcpci_drp_emul_connect_partner(&fixture->partner_emul, + fixture->tcpci_emul, + fixture->charger_emul); + + k_sleep(K_SECONDS(10)); +} + +static void usb_pd_ctrl_msg_after(void *data) +{ + struct usb_pd_ctrl_msg_test_fixture *fixture = data; + + disconnect_partner(fixture); +} + +/** ZTEST_SUITE to setup DRP partner_emul as SINK */ +ZTEST_SUITE(usb_pd_ctrl_msg_test_sink, drivers_predicate_post_main, + usb_pd_ctrl_msg_sink_setup, usb_pd_ctrl_msg_before, + usb_pd_ctrl_msg_after, NULL); + +/** ZTEST_SUITE to setup DRP partner_emul as SOURCE */ +ZTEST_SUITE(usb_pd_ctrl_msg_test_source, drivers_predicate_post_main, + usb_pd_ctrl_msg_source_setup, usb_pd_ctrl_msg_before, + usb_pd_ctrl_msg_after, NULL); + +ZTEST_F(usb_pd_ctrl_msg_test_sink, verify_vconn_swap) +{ + struct usb_pd_ctrl_msg_test_fixture *super_fixture = &fixture->fixture; + struct ec_response_typec_status snk_resp = { 0 }; + int rv = 0; + + snk_resp = host_cmd_typec_status(TEST_USB_PORT); + + zassert_equal(PD_ROLE_VCONN_SRC, snk_resp.vconn_role, + "SNK Returned vconn_role=%u", snk_resp.vconn_role); + + /* Send VCONN_SWAP request */ + rv = tcpci_partner_send_control_msg(&super_fixture->partner_emul, + PD_CTRL_VCONN_SWAP, 0); + zassert_ok(rv, "Failed to send VCONN_SWAP request, rv=%d", rv); + + k_sleep(K_SECONDS(1)); + + snk_resp = host_cmd_typec_status(TEST_USB_PORT); + zassert_equal(PD_ROLE_VCONN_OFF, snk_resp.vconn_role, + "SNK Returned vconn_role=%u", snk_resp.vconn_role); +} + +ZTEST_F(usb_pd_ctrl_msg_test_sink, verify_pr_swap) +{ + struct usb_pd_ctrl_msg_test_fixture *super_fixture = &fixture->fixture; + struct ec_response_typec_status snk_resp = { 0 }; + int rv = 0; + + snk_resp = host_cmd_typec_status(TEST_USB_PORT); + + zassert_equal(PD_ROLE_SINK, snk_resp.power_role, + "SNK Returned power_role=%u", snk_resp.power_role); + + /* Ignore ACCEPT in common handler for PR Swap request, + * causes soft reset + */ + tcpci_partner_common_handler_mask_msg(&super_fixture->partner_emul, + PD_CTRL_ACCEPT, true); + + /* Send PR_SWAP request */ + rv = tcpci_partner_send_control_msg(&super_fixture->partner_emul, + PD_CTRL_PR_SWAP, 0); + zassert_ok(rv, "Failed to send PR_SWAP request, rv=%d", rv); + + /* Send PS_RDY request */ + rv = tcpci_partner_send_control_msg(&super_fixture->partner_emul, + PD_CTRL_PS_RDY, 15); + zassert_ok(rv, "Failed to send PS_RDY request, rv=%d", rv); + + k_sleep(K_MSEC(20)); + + snk_resp = host_cmd_typec_status(TEST_USB_PORT); + zassert_equal(PD_ROLE_SOURCE, snk_resp.power_role, + "SNK Returned power_role=%u", snk_resp.power_role); +} + +/** + * @brief TestPurpose: Verify DR Swap when DRP partner is configured as sink. + * + * @details + * - TCPM is brought up as Sink/UFP + * - TCPM over time will evaluate and trigger DR Swap to Sink/DFP + * + * Expected Results + * - TypeC status query returns PD_ROLE_DFP + */ +ZTEST_F(usb_pd_ctrl_msg_test_sink, verify_dr_swap) +{ + struct ec_response_typec_status typec_status = + host_cmd_typec_status(TEST_USB_PORT); + + zassert_equal(PD_ROLE_DFP, typec_status.data_role, + "Returned data_role=%u", typec_status.data_role); +} + +/** + * @brief TestPurpose: Verify DR Swap is rejected when DRP partner is + * configured as source. + * + * @details + * - TCPM is configured initially as Sink/UFP. + * - TCPM initiates DR swap according to policy (Sink/DFP) + * - Partner requests DR Swap. + * - Verify Request is rejected due the TCPM not being UFP. + * + * Expected Results + * - Data role does not change on TEST_USB_PORT after DR Swap request. + */ +ZTEST_F(usb_pd_ctrl_msg_test_source, verify_dr_swap_rejected) +{ + struct usb_pd_ctrl_msg_test_fixture *super_fixture = &fixture->fixture; + struct ec_response_typec_status typec_status = { 0 }; + int rv = 0; + + typec_status = host_cmd_typec_status(TEST_USB_PORT); + zassert_equal(PD_ROLE_DFP, typec_status.data_role, + "Returned data_role=%u", typec_status.data_role); + + /* Send DR_SWAP request */ + rv = tcpci_partner_send_control_msg(&super_fixture->partner_emul, + PD_CTRL_DR_SWAP, 0); + zassert_ok(rv, "Failed to send DR_SWAP request, rv=%d", rv); + + k_sleep(K_MSEC(20)); + + /* Verify DR_Swap request is REJECTED */ + typec_status = host_cmd_typec_status(TEST_USB_PORT); + zassert_equal(PD_ROLE_DFP, typec_status.data_role, + "Returned data_role=%u", typec_status.data_role); +} + +/** + * @brief TestPurpose: Verify DR Swap via DPM request when DRP is configured + * as source + * + * @details + * - TCPM is configured initially as Sink/UFP. + * - TCPM initiates DR swap according to policy (Sink/DFP) + * - Test case initiates DPM DR Swap. + * - Verify DR Swap Request is processed. + * + * Expected Results + * - Data role changes after DPM DR Swap request + */ +ZTEST_F(usb_pd_ctrl_msg_test_source, verify_dpm_dr_swap) +{ + struct ec_response_typec_status typec_status = { 0 }; + + typec_status = host_cmd_typec_status(TEST_USB_PORT); + zassert_equal(PD_ROLE_DFP, typec_status.data_role, + "Returned data_role=%u", typec_status.data_role); + + pd_dpm_request(TEST_USB_PORT, DPM_REQUEST_DR_SWAP); + k_sleep(K_SECONDS(1)); + + typec_status = host_cmd_typec_status(TEST_USB_PORT); + zassert_equal(PD_ROLE_UFP, typec_status.data_role, + "Returned data_role=%u", typec_status.data_role); +} + +/** + * @brief TestPurpose: Verify TCPM initiates Get_Sink_Cap message during a typec + * status host command and receives sink_capabilities message. + * + * @details + * - TCPM is configured initially as Sink + * - TypeC Status Host Command is Invoked + * + * Expected Results + * - TypeC Status Host Command reveals sink capabilility PDOs. + */ +ZTEST(usb_pd_ctrl_msg_test_source, verify_dpm_get_sink_cap) +{ + struct ec_response_typec_status typec_status = { 0 }; + + typec_status = host_cmd_typec_status(TEST_USB_PORT); + + zassert_true(typec_status.sink_cap_count > 1, NULL); + zassert_equal(typec_status.sink_cap_pdos[1], TEST_ADDED_PDO, NULL); +} + +/** + * @brief TestPurpose: Verify TCPM initiates Get_Sink_Cap message during a typec + * status host command and receives sink_capabilities message. + * + * @details + * - TCPM is configured initially as Source + * - TypeC Status Host Command is Invoked + * + * Expected Results + * - TypeC Status Host Command reveals sink capabilility PDOs. + */ +ZTEST(usb_pd_ctrl_msg_test_sink, verify_get_sink_cap) +{ + struct ec_response_typec_status typec_status = { 0 }; + + typec_status = host_cmd_typec_status(TEST_USB_PORT); + + zassert_true(typec_status.sink_cap_count > 1, NULL); + zassert_equal(typec_status.sink_cap_pdos[1], TEST_ADDED_PDO, NULL); +} + +/** + * @brief TestPurpose: Verify BIST TX MODE 2. + * + * @details + * - TCPM is configured initially as Sink + * - Initiate BIST TX + * + * Expected Results + * - BIST occurs and we transition back to READY state + */ +ZTEST_F(usb_pd_ctrl_msg_test_source, verify_bist_tx_mode2) +{ + struct usb_pd_ctrl_msg_test_fixture *super_fixture = &fixture->fixture; + uint32_t bdo = BDO(BDO_MODE_CARRIER2, 0); + + tcpci_partner_send_data_msg(&super_fixture->partner_emul, PD_DATA_BIST, + &bdo, 1, 0); + + pd_dpm_request(TEST_USB_PORT, DPM_REQUEST_BIST_TX); + k_sleep(K_MSEC(10)); + zassert_equal(get_state_pe(TEST_USB_PORT), PE_BIST_TX, NULL); + + k_sleep(K_SECONDS(5)); + zassert_equal(get_state_pe(TEST_USB_PORT), PE_SNK_READY, NULL); +} + +/** + * @brief TestPurpose: Verify BIST TX TEST DATA. + * + * @details + * - TCPM is configured initially as Sink + * - Initiate BIST TX + * - End testing via signaling a Hard Reset + * + * Expected Results + * - Partner remains in BIST_TX state until hard reset is received. + */ +ZTEST_F(usb_pd_ctrl_msg_test_source, verify_bist_tx_test_data) +{ + struct usb_pd_ctrl_msg_test_fixture *super_fixture = &fixture->fixture; + uint32_t bdo = BDO(BDO_MODE_TEST_DATA, 0); + + tcpci_partner_send_data_msg(&super_fixture->partner_emul, PD_DATA_BIST, + &bdo, 1, 0); + + pd_dpm_request(TEST_USB_PORT, DPM_REQUEST_BIST_TX); + k_sleep(K_SECONDS(5)); + zassert_equal(get_state_pe(TEST_USB_PORT), PE_BIST_TX, NULL); + + tcpci_partner_common_send_hard_reset(&super_fixture->partner_emul); + k_sleep(K_SECONDS(1)); + zassert_equal(get_state_pe(TEST_USB_PORT), PE_SNK_READY, NULL); +} diff --git a/zephyr/test/drivers/default/src/integration/usbc/usb_pd_rev3.c b/zephyr/test/drivers/default/src/integration/usbc/usb_pd_rev3.c new file mode 100644 index 0000000000..fbe634a838 --- /dev/null +++ b/zephyr/test/drivers/default/src/integration/usbc/usb_pd_rev3.c @@ -0,0 +1,358 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <zephyr/ztest.h> + +#include "battery.h" +#include "battery_smart.h" +#include "chipset.h" +#include "emul/emul_isl923x.h" +#include "emul/emul_smart_battery.h" +#include "emul/tcpc/emul_tcpci_partner_src.h" +#include "hooks.h" +#include "test/drivers/stubs.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "usb_common.h" +#include "usb_pd.h" +#include "util.h" + +struct usb_attach_5v_3a_pd_source_rev3_fixture { + struct tcpci_partner_data source_5v_3a; + struct tcpci_src_emul_data src_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +static void *usb_attach_5v_3a_pd_source_setup(void) +{ + static struct usb_attach_5v_3a_pd_source_rev3_fixture test_fixture; + + /* Get references for the emulators */ + test_fixture.tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + test_fixture.charger_emul = EMUL_GET_USBC_BINDING(0, chg); + + /* Initialized the charger to supply 5V and 3A */ + tcpci_partner_init(&test_fixture.source_5v_3a, PD_REV30); + test_fixture.source_5v_3a.extensions = tcpci_src_emul_init( + &test_fixture.src_ext, &test_fixture.source_5v_3a, NULL); + test_fixture.src_ext.pdo[1] = + PDO_FIXED(5000, 3000, PDO_FIXED_UNCONSTRAINED); + + return &test_fixture; +} + +static void usb_attach_5v_3a_pd_source_before(void *data) +{ + struct usb_attach_5v_3a_pd_source_rev3_fixture *fixture = data; + + connect_source_to_port(&fixture->source_5v_3a, &fixture->src_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + + /* Clear Alert and Status receive checks */ + tcpci_src_emul_clear_alert_received(&fixture->src_ext); + tcpci_src_emul_clear_status_received(&fixture->src_ext); + zassume_false(fixture->src_ext.alert_received, NULL); + zassume_false(fixture->src_ext.status_received, NULL); + + /* Initial check on power state */ + zassume_true(chipset_in_state(CHIPSET_STATE_ON), NULL); +} + +static void usb_attach_5v_3a_pd_source_after(void *data) +{ + struct usb_attach_5v_3a_pd_source_rev3_fixture *fixture = data; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); +} + +ZTEST_SUITE(usb_attach_5v_3a_pd_source_rev3, drivers_predicate_post_main, + usb_attach_5v_3a_pd_source_setup, usb_attach_5v_3a_pd_source_before, + usb_attach_5v_3a_pd_source_after, NULL); + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, test_batt_cap) +{ + int battery_index = 0; + + tcpci_partner_common_send_get_battery_capabilities( + &fixture->source_5v_3a, battery_index); + + /* Allow some time for TCPC to process and respond */ + k_sleep(K_SECONDS(1)); + + zassert_true(fixture->source_5v_3a.battery_capabilities + .have_response[battery_index], + "No battery capabilities response stored."); + + /* The response */ + struct pd_bcdb *bcdb = + &fixture->source_5v_3a.battery_capabilities.bcdb[battery_index]; + + zassert_equal(USB_VID_GOOGLE, bcdb->vid, "Incorrect battery VID"); + zassert_equal(CONFIG_USB_PID, bcdb->pid, "Incorrect battery PID"); + zassert_false((bcdb->battery_type) & BIT(0), + "Invalid battery ref bit should not be set"); + + /* Verify the battery capacity and last full charge capacity. These + * fields require that the battery is present and that we can + * access information about the nominal voltage and capacity. + * + * TODO(b/237427945): Add test for case when battery is not present + */ + + /* See pe_give_battery_cap_entry() in common/usbc/usb_pe_drp_sm.c */ + + zassume_true(battery_is_present(), "Battery must be present"); + zassume_true(IS_ENABLED(HAS_TASK_HOSTCMD) && + *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0, + "Cannot access battery data"); + + /* Millivolts */ + int design_volt = *(int *)host_get_memmap(EC_MEMMAP_BATT_DVLT); + + /* Milliamphours */ + int design_cap = *(int *)host_get_memmap(EC_MEMMAP_BATT_DCAP); + int full_cap = *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC); + + /* Multiply millivolts by milliamphours and scale to deciwatthours + * (0.1 Wh), the unit of energy used in the PD messages. + */ + + int expected_design_cap = + DIV_ROUND_NEAREST((design_cap * design_volt), 1000 * 1000 / 10); + + int expected_last_charge_cap = + DIV_ROUND_NEAREST((design_cap * full_cap), 1000 * 1000 / 10); + + zassert_equal(expected_design_cap, bcdb->design_cap, + "Design capacity not correct. Expected %d but got %d", + expected_design_cap, bcdb->design_cap); + zassert_equal( + expected_last_charge_cap, bcdb->last_full_charge_cap, + "Last full charge capacity not correct. Expected %d but got %d", + expected_last_charge_cap, bcdb->last_full_charge_cap); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, test_batt_cap_invalid) +{ + /* Request data on a battery that does not exist. The PD stack only + * supports battery 0. + */ + + int battery_index = 5; + + tcpci_partner_common_send_get_battery_capabilities( + &fixture->source_5v_3a, battery_index); + + /* Allow some time for TCPC to process and respond */ + k_sleep(K_SECONDS(1)); + + /* Ensure we get a response that says our battery index was invalid */ + + zassert_true(fixture->source_5v_3a.battery_capabilities + .have_response[battery_index], + "No battery capabilities response stored."); + zassert_true( + (fixture->source_5v_3a.battery_capabilities.bcdb[battery_index] + .battery_type) & + BIT(0), + "Invalid battery ref bit should be set"); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, verify_alert_msg) +{ + zassume_equal(pd_broadcast_alert_msg(ADO_OTP_EVENT), EC_SUCCESS, NULL); + + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, verify_alert_on_power_state_change) +{ + /* Suspend and check partner received Alert and Status messages */ + hook_notify(HOOK_CHIPSET_SUSPEND); + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); + zassert_true(fixture->src_ext.status_received, NULL); + tcpci_src_emul_clear_alert_received(&fixture->src_ext); + tcpci_src_emul_clear_status_received(&fixture->src_ext); + zassume_false(fixture->src_ext.alert_received, NULL); + zassume_false(fixture->src_ext.status_received, NULL); + + /* Shutdown and check partner received Alert and Status messages */ + hook_notify(HOOK_CHIPSET_SHUTDOWN); + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); + zassert_true(fixture->src_ext.status_received, NULL); + tcpci_src_emul_clear_alert_received(&fixture->src_ext); + tcpci_src_emul_clear_status_received(&fixture->src_ext); + zassume_false(fixture->src_ext.alert_received, NULL); + zassume_false(fixture->src_ext.status_received, NULL); + + /* Startup and check partner received Alert and Status messages */ + hook_notify(HOOK_CHIPSET_STARTUP); + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); + zassert_true(fixture->src_ext.status_received, NULL); + tcpci_src_emul_clear_alert_received(&fixture->src_ext); + tcpci_src_emul_clear_status_received(&fixture->src_ext); + zassume_false(fixture->src_ext.alert_received, NULL); + zassume_false(fixture->src_ext.status_received, NULL); + + /* Resume and check partner received Alert and Status messages */ + hook_notify(HOOK_CHIPSET_RESUME); + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); + zassert_true(fixture->src_ext.status_received, NULL); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, + verify_inaction_on_pd_button_press_while_awake) +{ + uint32_t ado; + + /* While awake expect nothing on valid press */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_false(fixture->src_ext.alert_received, NULL); + zassert_false(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ON), NULL); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, + verify_inaction_on_invalid_pd_button_press) +{ + uint32_t ado; + + /* Shutdown device to test wake from USB PD power button */ + chipset_force_shutdown(CHIPSET_SHUTDOWN_BUTTON); + k_sleep(K_SECONDS(10)); + + /* Clear alert and status flags set during shutdown */ + tcpci_src_emul_clear_alert_received(&fixture->src_ext); + tcpci_src_emul_clear_status_received(&fixture->src_ext); + zassume_false(fixture->src_ext.alert_received, NULL); + zassume_false(fixture->src_ext.status_received, NULL); + zassume_true(chipset_in_state(CHIPSET_STATE_ANY_OFF), NULL); + + /* While in S5/G3 expect nothing on invalid (too long) press */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(10)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_false(fixture->src_ext.alert_received, NULL); + zassert_false(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ANY_OFF), NULL); + + /* Wake device to setup for subsequent tests */ + chipset_power_on(); + k_sleep(K_SECONDS(10)); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, verify_startup_on_pd_button_press) +{ + uint32_t ado; + + /* Shutdown device to test wake from USB PD power button */ + chipset_force_shutdown(CHIPSET_SHUTDOWN_BUTTON); + k_sleep(K_SECONDS(10)); + + /* Clear alert and status flags set during shutdown */ + tcpci_src_emul_clear_alert_received(&fixture->src_ext); + tcpci_src_emul_clear_status_received(&fixture->src_ext); + zassume_false(fixture->src_ext.alert_received, NULL); + zassume_false(fixture->src_ext.status_received, NULL); + zassume_true(chipset_in_state(CHIPSET_STATE_ANY_OFF), NULL); + + /* While in S5/G3 expect Alert->Get_Status->Status on valid press */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); + zassert_true(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ON), NULL); +} + +ZTEST_F(usb_attach_5v_3a_pd_source_rev3, verify_chipset_on_pd_button_behavior) +{ + uint32_t ado; + + /* Expect no power state change on short press */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_false(fixture->src_ext.alert_received, NULL); + zassert_false(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ON), NULL); + + /* Expect no change on invalid button press while chipset is on */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(10)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_false(fixture->src_ext.alert_received, NULL); + zassert_false(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ON), NULL); + + /* + * Expect no power state change on 6 second press->press->release due + * to the timers resetting on the second press. + */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(3)); + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(3)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_false(fixture->src_ext.alert_received, NULL); + zassert_false(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ON), NULL); + + /* Expect power state change on long press */ + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_PRESS; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(6)); + ado = ADO_EXTENDED_ALERT_EVENT | ADO_POWER_BUTTON_RELEASE; + tcpci_partner_send_data_msg(&fixture->source_5v_3a, PD_DATA_ALERT, &ado, + 1, 0); + k_sleep(K_SECONDS(2)); + zassert_true(fixture->src_ext.alert_received, NULL); + zassert_true(fixture->src_ext.status_received, NULL); + zassert_true(chipset_in_state(CHIPSET_STATE_ANY_OFF), NULL); + + /* Wake device to setup for subsequent tests */ + chipset_power_on(); + k_sleep(K_SECONDS(10)); +} |