diff options
Diffstat (limited to 'zephyr/test/drivers/default/src')
88 files changed, 26323 insertions, 0 deletions
diff --git a/zephyr/test/drivers/default/src/battery.c b/zephyr/test/drivers/default/src/battery.c new file mode 100644 index 0000000000..6b01a5ca39 --- /dev/null +++ b/zephyr/test/drivers/default/src/battery.c @@ -0,0 +1,41 @@ +/* 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.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include "battery.h" +#include "test/drivers/test_state.h" + +#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 battery_after(void *data) +{ + const struct device *dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_BATT_PRES_ODL_PATH, gpios)); + + /* Set default state (battery is present) */ + gpio_emul_input_set(dev, GPIO_BATT_PRES_ODL_PORT, 0); +} + +ZTEST_USER(battery, test_battery_is_present_gpio) +{ + const struct device *dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_BATT_PRES_ODL_PATH, gpios)); + + zassert_not_null(dev, NULL); + /* ec_batt_pres_odl = 0 means battery present. */ + zassert_ok(gpio_emul_input_set(dev, GPIO_BATT_PRES_ODL_PORT, 0), NULL); + zassert_equal(BP_YES, battery_is_present(), NULL); + /* ec_batt_pres_odl = 1 means battery missing. */ + zassert_ok(gpio_emul_input_set(dev, GPIO_BATT_PRES_ODL_PORT, 1), NULL); + zassert_equal(BP_NO, battery_is_present(), NULL); +} + +ZTEST_SUITE(battery, drivers_predicate_post_main, NULL, NULL, battery_after, + NULL); diff --git a/zephyr/test/drivers/default/src/bb_retimer.c b/zephyr/test/drivers/default/src/bb_retimer.c new file mode 100644 index 0000000000..74d8fa86a2 --- /dev/null +++ b/zephyr/test/drivers/default/src/bb_retimer.c @@ -0,0 +1,578 @@ +/* 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.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include "common.h" +#include "ec_tasks.h" +#include "emul/emul_bb_retimer.h" +#include "emul/emul_common_i2c.h" +#include "hooks.h" +#include "i2c.h" +#include "test/drivers/stubs.h" +#include "usb_prl_sm.h" +#include "usb_tc_sm.h" +#include "chipset.h" + +#include "driver/retimer/bb_retimer.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +#define GPIO_USB_C1_LS_EN_PATH DT_PATH(named_gpios, usb_c1_ls_en) +#define GPIO_USB_C1_LS_EN_PORT DT_GPIO_PIN(GPIO_USB_C1_LS_EN_PATH, gpios) +#define GPIO_USB_C1_RT_RST_ODL_PATH DT_PATH(named_gpios, usb_c1_rt_rst_odl) +#define GPIO_USB_C1_RT_RST_ODL_PORT \ + DT_GPIO_PIN(GPIO_USB_C1_RT_RST_ODL_PATH, gpios) +#define BB_RETIMER_NODE DT_NODELABEL(usb_c1_bb_retimer_emul) + +/** Test is retimer fw update capable function. */ +ZTEST_USER(bb_retimer, test_bb_is_fw_update_capable) +{ + /* BB retimer is fw update capable */ + zassert_true(bb_usb_retimer.is_retimer_fw_update_capable(), NULL); +} + +/** Test is retimer fw update capable function. */ +ZTEST_USER(bb_retimer_no_tasks, test_bb_set_state) +{ + struct pd_discovery *disc; + uint32_t conn, exp_conn; + const struct emul *emul = EMUL_DT_GET(BB_RETIMER_NODE); + struct i2c_common_emul_data *common_data = + emul_bb_retimer_get_i2c_common_data(emul); + bool ack_required; + + set_test_runner_tid(); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + BB_RETIMER_REG_CONNECTION_STATE); + + /* Test fail on reset register write */ + zassert_equal(EC_ERROR_INVAL, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_NONE, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set UFP role for whole test */ + tc_set_data_role(USBC_PORT_C1, PD_ROLE_UFP); + zassume_equal(PD_ROLE_UFP, pd_get_data_role(USBC_PORT_C1), NULL); + + /* Test none mode */ + bb_emul_set_reg(emul, BB_RETIMER_REG_CONNECTION_STATE, 0x12144678); + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_NONE, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + /* Only UFP mode is set */ + exp_conn = BB_RETIMER_USB_DATA_ROLE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test USB3 gen1 mode */ + prl_set_rev(USBC_PORT_C1, TCPCI_MSG_SOP_PRIME, PD_REV10); + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_USB_ENABLED, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_USB_3_CONNECTION; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test USB3 gen2 mode */ + disc = pd_get_am_discovery_and_notify_access(USBC_PORT_C1, + TCPCI_MSG_SOP_PRIME); + disc->identity.product_t1.p_rev30.ss = USB_R30_SS_U32_U40_GEN2; + prl_set_rev(USBC_PORT_C1, TCPCI_MSG_SOP_PRIME, PD_REV30); + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_USB_ENABLED, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_USB_3_CONNECTION | BB_RETIMER_USB_3_SPEED; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode */ + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test USB4 mode */ + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_USB4_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | BB_RETIMER_USB4_ENABLED; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test USB4 mode with polarity inverted */ + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_USB4_ENABLED | + USB_PD_MUX_POLARITY_INVERTED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_CONNECTION_ORIENTATION | BB_RETIMER_USB4_ENABLED; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test DP mode */ + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_DP_ENABLED, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_DP_ENABLED | + USB_PD_MUX_HPD_IRQ, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | BB_RETIMER_IRQ_HPD; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_DP_ENABLED | + USB_PD_MUX_HPD_LVL, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_USB_DATA_ROLE | + BB_RETIMER_DATA_CONNECTION_PRESENT | BB_RETIMER_HPD_LVL; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); +} + +/** Test setting different options for DFP role */ +ZTEST_USER(bb_retimer_no_tasks, test_bb_set_dfp_state) +{ + union tbt_mode_resp_device device_resp; + union tbt_mode_resp_cable cable_resp; + struct pd_discovery *disc, *dev_disc; + uint32_t conn, exp_conn; + const struct emul *emul = EMUL_DT_GET(BB_RETIMER_NODE); + bool ack_required; + + set_test_runner_tid(); + + tc_set_data_role(USBC_PORT_C1, PD_ROLE_DFP); + zassume_equal(PD_ROLE_DFP, pd_get_data_role(USBC_PORT_C1), NULL); + + /* Test PD mux none mode with DFP should clear all bits in state */ + bb_emul_set_reg(emul, BB_RETIMER_REG_CONNECTION_STATE, 0x12144678); + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_NONE, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = 0; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Set active cable type */ + disc = pd_get_am_discovery_and_notify_access(USBC_PORT_C1, + TCPCI_MSG_SOP_PRIME); + disc->identity.idh.product_type = IDH_PTYPE_ACABLE; + disc->identity.product_t2.a2_rev30.active_elem = ACTIVE_RETIMER; + disc->identity.product_t1.p_rev30.ss = USB_R30_SS_U32_U40_GEN2; + prl_set_rev(USBC_PORT_C1, TCPCI_MSG_SOP_PRIME, PD_REV30); + + /* Set cable VDO */ + disc->svid_cnt = 1; + disc->svids[0].svid = USB_VID_INTEL; + disc->svids[0].discovery = PD_DISC_COMPLETE; + disc->svids[0].mode_cnt = 1; + cable_resp.tbt_alt_mode = TBT_ALTERNATE_MODE; + cable_resp.tbt_cable_speed = TBT_SS_RES_0; + cable_resp.tbt_rounded = TBT_GEN3_NON_ROUNDED; + cable_resp.tbt_cable = TBT_CABLE_NON_OPTICAL; + cable_resp.retimer_type = USB_NOT_RETIMER; + cable_resp.lsrx_comm = BIDIR_LSRX_COMM; + cable_resp.tbt_active_passive = TBT_CABLE_PASSIVE; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + + /* Set device VDO */ + dev_disc = pd_get_am_discovery_and_notify_access(USBC_PORT_C1, + TCPCI_MSG_SOP); + dev_disc->svid_cnt = 1; + dev_disc->svids[0].svid = USB_VID_INTEL; + dev_disc->svids[0].discovery = PD_DISC_COMPLETE; + dev_disc->svids[0].mode_cnt = 1; + device_resp.tbt_alt_mode = TBT_ALTERNATE_MODE; + device_resp.tbt_adapter = TBT_ADAPTER_TBT3; + device_resp.intel_spec_b0 = VENDOR_SPECIFIC_NOT_SUPPORTED; + device_resp.vendor_spec_b0 = VENDOR_SPECIFIC_NOT_SUPPORTED; + device_resp.vendor_spec_b1 = VENDOR_SPECIFIC_NOT_SUPPORTED; + dev_disc->svids[0].mode_vdo[0] = device_resp.raw_value; + + /* Test USB mode with active cable */ + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_USB_ENABLED, &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_USB_3_CONNECTION | BB_RETIMER_USB_3_SPEED | + BB_RETIMER_RE_TIMER_DRIVER | BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with active cable */ + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with retimer */ + cable_resp.retimer_type = USB_RETIMER; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | BB_RETIMER_RE_TIMER_DRIVER | + BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with optical cable */ + cable_resp.retimer_type = USB_NOT_RETIMER; + cable_resp.tbt_cable = TBT_CABLE_OPTICAL; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | BB_RETIMER_TBT_CABLE_TYPE | + BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with active link training */ + cable_resp.tbt_cable = TBT_CABLE_NON_OPTICAL; + cable_resp.lsrx_comm = UNIDIR_LSRX_COMM; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = + BB_RETIMER_DATA_CONNECTION_PRESENT | BB_RETIMER_TBT_CONNECTION | + BB_RETIMER_TBT_ACTIVE_LINK_TRAINING | BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with different cable speeds */ + cable_resp.lsrx_comm = BIDIR_LSRX_COMM; + cable_resp.tbt_cable_speed = TBT_SS_U31_GEN1; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | + BB_RETIMER_USB4_TBT_CABLE_SPEED_SUPPORT(1) | + BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + cable_resp.tbt_cable_speed = TBT_SS_U32_GEN1_GEN2; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | + BB_RETIMER_USB4_TBT_CABLE_SPEED_SUPPORT(2) | + BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + cable_resp.tbt_cable_speed = TBT_SS_TBT_GEN3; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | + BB_RETIMER_USB4_TBT_CABLE_SPEED_SUPPORT(3) | + BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with TBT gen4 cable */ + cable_resp.tbt_cable_speed = TBT_SS_RES_0; + cable_resp.tbt_rounded = TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = + BB_RETIMER_DATA_CONNECTION_PRESENT | BB_RETIMER_TBT_CONNECTION | + BB_RETIMER_TBT_CABLE_GENERATION(1) | BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with legacy TBT adapter */ + cable_resp.tbt_rounded = TBT_GEN3_NON_ROUNDED; + disc->svids[0].mode_vdo[0] = cable_resp.raw_value; + device_resp.tbt_adapter = TBT_ADAPTER_TBT2_LEGACY; + dev_disc->svids[0].mode_vdo[0] = device_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | BB_RETIMER_TBT_TYPE | + BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with Intel specific b0 */ + device_resp.tbt_adapter = TBT_ADAPTER_TBT3; + device_resp.intel_spec_b0 = VENDOR_SPECIFIC_SUPPORTED; + dev_disc->svids[0].mode_vdo[0] = device_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = BB_RETIMER_DATA_CONNECTION_PRESENT | + BB_RETIMER_TBT_CONNECTION | BB_RETIMER_ACTIVE_PASSIVE; + + if (IS_ENABLED(CONFIG_USBC_RETIMER_INTEL_BB_VPRO_CAPABLE)) + exp_conn |= BB_RETIMER_VPRO_DOCK_DP_OVERDRIVE; + + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); + + /* Test TBT mode with vendor specific b1 */ + device_resp.intel_spec_b0 = VENDOR_SPECIFIC_NOT_SUPPORTED; + device_resp.vendor_spec_b1 = VENDOR_SPECIFIC_SUPPORTED; + dev_disc->svids[0].mode_vdo[0] = device_resp.raw_value; + zassert_equal(EC_SUCCESS, + bb_usb_retimer.set(usb_muxes[USBC_PORT_C1].mux, + USB_PD_MUX_TBT_COMPAT_ENABLED, + &ack_required), + NULL); + zassert_false(ack_required, "ACK is never required for BB retimer"); + conn = bb_emul_get_reg(emul, BB_RETIMER_REG_CONNECTION_STATE); + exp_conn = + BB_RETIMER_DATA_CONNECTION_PRESENT | BB_RETIMER_TBT_CONNECTION | + BB_RETIMER_VPRO_DOCK_DP_OVERDRIVE | BB_RETIMER_ACTIVE_PASSIVE; + zassert_equal(exp_conn, conn, "Expected state 0x%lx, got 0x%lx", + exp_conn, conn); +} + +/** Test BB retimer init */ +ZTEST_USER(bb_retimer, test_bb_init) +{ + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_USB_C1_LS_EN_PATH, gpios)); + const struct emul *emul = EMUL_DT_GET(BB_RETIMER_NODE); + struct i2c_common_emul_data *common_data = + emul_bb_retimer_get_i2c_common_data(emul); + + zassert_not_null(gpio_dev, "Cannot get GPIO device"); + + /* Set AP to normal state and wait for chipset task */ + test_set_chipset_to_s0(); + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + BB_RETIMER_REG_VENDOR_ID); + /* Test fail on vendor ID read */ + zassert_equal(EC_ERROR_INVAL, + bb_usb_retimer.init(usb_muxes[USBC_PORT_C1].mux), NULL); + /* Enable pins should be set always after init, when AP is on */ + zassert_equal(1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_LS_EN_PORT), + NULL); + zassert_equal( + 1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_RT_RST_ODL_PORT), + NULL); + + /* Setup wrong vendor ID */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + bb_emul_set_reg(emul, BB_RETIMER_REG_VENDOR_ID, 0x12144678); + /* Test fail on wrong vendor ID */ + zassert_equal(EC_ERROR_INVAL, + bb_usb_retimer.init(usb_muxes[USBC_PORT_C1].mux), NULL); + zassert_equal(1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_LS_EN_PORT), + NULL); + zassert_equal( + 1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_RT_RST_ODL_PORT), + NULL); + + /* Setup emulator fail on device ID read */ + i2c_common_emul_set_read_fail_reg(common_data, + BB_RETIMER_REG_DEVICE_ID); + bb_emul_set_reg(emul, BB_RETIMER_REG_VENDOR_ID, BB_RETIMER_VENDOR_ID_1); + /* Test fail on device ID read */ + zassert_equal(EC_ERROR_INVAL, + bb_usb_retimer.init(usb_muxes[USBC_PORT_C1].mux), NULL); + zassert_equal(1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_LS_EN_PORT), + NULL); + zassert_equal( + 1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_RT_RST_ODL_PORT), + NULL); + + /* Setup wrong device ID */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + bb_emul_set_reg(emul, BB_RETIMER_REG_DEVICE_ID, 0x12144678); + /* Test fail on wrong device ID */ + zassert_equal(EC_ERROR_INVAL, + bb_usb_retimer.init(usb_muxes[USBC_PORT_C1].mux), NULL); + zassert_equal(1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_LS_EN_PORT), + NULL); + zassert_equal( + 1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_RT_RST_ODL_PORT), + NULL); + + /* Test successful init */ + bb_emul_set_reg(emul, BB_RETIMER_REG_DEVICE_ID, BB_RETIMER_DEVICE_ID); + zassert_equal(EC_SUCCESS, + bb_usb_retimer.init(usb_muxes[USBC_PORT_C1].mux), NULL); + zassert_equal(1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_LS_EN_PORT), + NULL); + zassert_equal( + 1, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_RT_RST_ODL_PORT), + NULL); + + /* Set AP to off state and wait for chipset task */ + test_set_chipset_to_g3(); + + /* With AP off, init should fail and pins should be unset */ + zassert_equal(EC_ERROR_NOT_POWERED, + bb_usb_retimer.init(usb_muxes[USBC_PORT_C1].mux), NULL); + zassert_equal(0, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_LS_EN_PORT), + NULL); + + msleep(1); + zassert_equal( + 0, gpio_emul_output_get(gpio_dev, GPIO_USB_C1_RT_RST_ODL_PORT), + NULL); +} + +/** Test BB retimer console command */ +ZTEST_USER(bb_retimer, test_bb_console_cmd) +{ + int rv; + + /* Validate well formed shell commands */ + rv = shell_execute_cmd(get_ec_shell(), "bb 1 r 2"); + zassert_ok(rv, "rv=%d", rv); + rv = shell_execute_cmd(get_ec_shell(), "bb 1 w 2 0"); + zassert_ok(rv, "rv=%d", rv); + + /* Validate errors for malformed shell commands */ + rv = shell_execute_cmd(get_ec_shell(), "bb x"); + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "rv=%d", rv); + rv = shell_execute_cmd(get_ec_shell(), "bb x r 2"); + zassert_equal(EC_ERROR_PARAM1, rv, "rv=%d", rv); + rv = shell_execute_cmd(get_ec_shell(), "bb 0 r 2"); + zassert_equal(EC_ERROR_PARAM1, rv, "rv=%d", rv); + rv = shell_execute_cmd(get_ec_shell(), "bb 1 x 2"); + zassert_equal(EC_ERROR_PARAM2, rv, "rv=%d", rv); + rv = shell_execute_cmd(get_ec_shell(), "bb 1 r x"); + zassert_equal(EC_ERROR_PARAM3, rv, "rv=%d", rv); + rv = shell_execute_cmd(get_ec_shell(), "bb 1 w 2 x"); + zassert_equal(EC_ERROR_PARAM4, rv, "rv=%d", rv); +} + +ZTEST_SUITE(bb_retimer_no_tasks, drivers_predicate_pre_main, NULL, NULL, NULL, + NULL); + +ZTEST_SUITE(bb_retimer, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/bc12.c b/zephyr/test/drivers/default/src/bc12.c new file mode 100644 index 0000000000..a8d23e73ce --- /dev/null +++ b/zephyr/test/drivers/default/src/bc12.c @@ -0,0 +1,277 @@ +/* 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.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include "emul/emul_pi3usb9201.h" + +#include "timer.h" +#include "usb_charge.h" +#include "battery.h" +#include "extpower.h" +#include "test/drivers/stubs.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(test_drivers_bc12, LOG_LEVEL_DBG); + +#define EMUL_NODE DT_NODELABEL(pi3usb9201_emul) + +/* Control_1 register bit definitions */ +#define PI3USB9201_REG_CTRL_1_INT_MASK BIT(0) +#define PI3USB9201_REG_CTRL_1_MODE_SHIFT 1 +#define PI3USB9201_REG_CTRL_1_MODE_MASK \ + (0x7 << PI3USB9201_REG_CTRL_1_MODE_SHIFT) + +/* Control_2 register bit definitions */ +#define PI3USB9201_REG_CTRL_2_AUTO_SW BIT(1) +#define PI3USB9201_REG_CTRL_2_START_DET BIT(3) + +/* Host status register bit definitions */ +#define PI3USB9201_REG_HOST_STS_BC12_DET BIT(0) +#define PI3USB9201_REG_HOST_STS_DEV_PLUG BIT(1) +#define PI3USB9201_REG_HOST_STS_DEV_UNPLUG BIT(2) + +enum pi3usb9201_mode { + PI3USB9201_POWER_DOWN, + PI3USB9201_SDP_HOST_MODE, + PI3USB9201_DCP_HOST_MODE, + PI3USB9201_CDP_HOST_MODE, + PI3USB9201_CLIENT_MODE, + PI3USB9201_RESERVED_1, + PI3USB9201_RESERVED_2, + PI3USB9201_USB_PATH_ON, +}; + +enum pi3usb9201_client_sts { + CHG_OTHER = 0, + CHG_2_4A, + CHG_2_0A, + CHG_1_0A, + CHG_RESERVED, + CHG_CDP, + CHG_SDP, + CHG_DCP, +}; + +struct bc12_status { + enum charge_supplier supplier; + int current_limit; +}; + +static const struct bc12_status bc12_chg_limits[] = { + [CHG_OTHER] = { .supplier = CHARGE_SUPPLIER_OTHER, + .current_limit = 500 }, + [CHG_2_4A] = { .supplier = CHARGE_SUPPLIER_PROPRIETARY, + .current_limit = USB_CHARGER_MAX_CURR_MA }, + [CHG_2_0A] = { .supplier = CHARGE_SUPPLIER_PROPRIETARY, + .current_limit = USB_CHARGER_MAX_CURR_MA }, + [CHG_1_0A] = { .supplier = CHARGE_SUPPLIER_PROPRIETARY, + .current_limit = 1000 }, + [CHG_RESERVED] = { .supplier = CHARGE_SUPPLIER_NONE, + .current_limit = 0 }, + [CHG_CDP] = { .supplier = CHARGE_SUPPLIER_BC12_CDP, + .current_limit = USB_CHARGER_MAX_CURR_MA }, + [CHG_SDP] = { .supplier = CHARGE_SUPPLIER_BC12_SDP, + .current_limit = 500 }, +#if defined(CONFIG_CHARGE_RAMP_SW) || defined(CONFIG_CHARGE_RAMP_HW) + [CHG_DCP] = { .supplier = CHARGE_SUPPLIER_BC12_DCP, + .current_limit = USB_CHARGER_MAX_CURR_MA }, +#else + [CHG_DCP] = { .supplier = CHARGE_SUPPLIER_BC12_DCP, + .current_limit = 500 }, +#endif +}; + +#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 test_bc12_pi3usb9201_host_mode(void) +{ + const struct emul *emul = EMUL_DT_GET(EMUL_NODE); + uint8_t a, b; + + /* + * Pretend that the USB-C Port Manager (TCPMv2) has set the port data + * role to DFP. + */ + usb_charger_task_set_event(0, USB_CHG_EVENT_DR_DFP); + msleep(1); + /* + * Expect the pi3usb9201 driver to configure CDP host mode and unmask + * interrupts. + */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_1, &a); + b = PI3USB9201_CDP_HOST_MODE << PI3USB9201_REG_CTRL_1_MODE_SHIFT; + zassert_equal(a, b, NULL); + + /* Pretend that a device has been plugged in. */ + msleep(500); + pi3usb9201_emul_set_reg(emul, PI3USB9201_REG_HOST_STS, + PI3USB9201_REG_HOST_STS_DEV_PLUG); + usb_charger_task_set_event(0, USB_CHG_EVENT_BC12); + msleep(1); + /* Expect the pi3usb9201 driver to configure SDP host mode. */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_1, &a); + b = PI3USB9201_SDP_HOST_MODE << PI3USB9201_REG_CTRL_1_MODE_SHIFT; + zassert_equal(a, b, NULL); + pi3usb9201_emul_set_reg(emul, PI3USB9201_REG_HOST_STS, 0); + + /* Pretend that a device has been unplugged. */ + msleep(500); + pi3usb9201_emul_set_reg(emul, PI3USB9201_REG_HOST_STS, + PI3USB9201_REG_HOST_STS_DEV_UNPLUG); + usb_charger_task_set_event(0, USB_CHG_EVENT_BC12); + msleep(1); + /* Expect the pi3usb9201 driver to configure CDP host mode. */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_1, &a); + b = PI3USB9201_CDP_HOST_MODE << PI3USB9201_REG_CTRL_1_MODE_SHIFT; + zassert_equal(a, b, NULL); + pi3usb9201_emul_set_reg(emul, PI3USB9201_REG_HOST_STS, 0); +} + +static void +test_bc12_pi3usb9201_client_mode(enum pi3usb9201_client_sts detect_result, + enum charge_supplier supplier, + int current_limit) +{ + const struct emul *emul = EMUL_DT_GET(EMUL_NODE); + uint8_t a, b; + int port, voltage; + + /* + * Pretend that the USB-C Port Manager (TCPMv2) has set the port data + * role to UFP and decided charging from the port is allowed. + */ + msleep(500); + usb_charger_task_set_event(0, USB_CHG_EVENT_DR_UFP); + charge_manager_update_dualrole(USBC_PORT_C0, CAP_DEDICATED); + msleep(1); + /* + * Expect the pi3usb9201 driver to configure client mode and start + * detection. + */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_1, &a); + b = PI3USB9201_CLIENT_MODE << PI3USB9201_REG_CTRL_1_MODE_SHIFT; + zassert_equal(a, b, NULL); + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_2, &a); + b = PI3USB9201_REG_CTRL_2_START_DET; + zassert_equal(a, b, NULL); + + /* Pretend that detection completed. */ + msleep(500); + pi3usb9201_emul_set_reg(emul, PI3USB9201_REG_CLIENT_STS, + 1 << detect_result); + usb_charger_task_set_event(0, USB_CHG_EVENT_BC12); + msleep(1); + /* Expect the pi3usb9201 driver to clear the start bit. */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_2, &a); + zassert_equal(a, 0, NULL); + pi3usb9201_emul_set_reg(emul, PI3USB9201_REG_CLIENT_STS, 0); + /* + * Expect the charge manager to select the detected BC1.2 supplier. + */ + port = CHARGE_PORT_NONE; + voltage = 0; + if (supplier != CHARGE_SUPPLIER_NONE) { + port = USBC_PORT_C0; + voltage = USB_CHARGER_VOLTAGE_MV; + } + /* Wait for the charge port to update. */ + msleep(500); + zassert_equal(charge_manager_get_active_charge_port(), port, NULL); + zassert_equal(charge_manager_get_supplier(), supplier, NULL); + zassert_equal(charge_manager_get_charger_current(), current_limit, + NULL); + zassert_equal(charge_manager_get_charger_voltage(), voltage, NULL); + + /* + * Pretend that the USB-C Port Manager (TCPMv2) has set the port data + * role to disconnected. + */ + msleep(500); + usb_charger_task_set_event(0, USB_CHG_EVENT_CC_OPEN); + msleep(1); + /* + * Expect the pi3usb9201 driver to configure power down mode and mask + * interrupts. + */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_1, &a); + b = PI3USB9201_POWER_DOWN << PI3USB9201_REG_CTRL_1_MODE_SHIFT; + b |= PI3USB9201_REG_CTRL_1_INT_MASK; + zassert_equal(a, b, NULL); + /* Expect the charge manager to have no active supplier. */ + zassert_equal(charge_manager_get_active_charge_port(), CHARGE_PORT_NONE, + NULL); + zassert_equal(charge_manager_get_supplier(), CHARGE_SUPPLIER_NONE, + NULL); + zassert_equal(charge_manager_get_charger_current(), 0, NULL); + zassert_equal(charge_manager_get_charger_voltage(), 0, NULL); +} + +/* + * PI3USB9201 is a dual-role BC1.2 charger detector/advertiser used on USB + * ports. It can be programmed to operate in host mode or client mode through + * I2C. When operating as a host, PI3USB9201 enables BC1.2 SDP/CDP/DCP + * advertisement to the attached USB devices via the D+/- connection. When + * operating as a client, PI3USB9201 starts BC1.2 detection to detect the + * attached host type. In both host mode and client mode, the detection results + * are reported through I2C to the controller. + */ +ZTEST_USER(bc12, test_bc12_pi3usb9201) +{ + const struct device *batt_pres_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_BATT_PRES_ODL_PATH, gpios)); + const struct emul *emul = EMUL_DT_GET(EMUL_NODE); + uint8_t a, b; + + /* Pretend we have battery and AC so charging works normally. */ + zassert_ok(gpio_emul_input_set(batt_pres_dev, GPIO_BATT_PRES_ODL_PORT, + 0), + NULL); + zassert_equal(BP_YES, battery_is_present(), NULL); + set_ac_enabled(true); + + /* Wait long enough for TCPMv2 to be idle. */ + msleep(2000); + + /* + * Pretend that the USB-C Port Manager (TCPMv2) has set the port data + * role to disconnected. + */ + usb_charger_task_set_event(0, USB_CHG_EVENT_CC_OPEN); + usb_charger_task_set_event(1, USB_CHG_EVENT_CC_OPEN); + msleep(1); + /* + * Expect the pi3usb9201 driver to configure power down mode and mask + * interrupts. + */ + pi3usb9201_emul_get_reg(emul, PI3USB9201_REG_CTRL_1, &a); + b = PI3USB9201_POWER_DOWN << PI3USB9201_REG_CTRL_1_MODE_SHIFT; + b |= PI3USB9201_REG_CTRL_1_INT_MASK; + zassert_equal(a, b, NULL); + + test_bc12_pi3usb9201_host_mode(); + + for (int c = CHG_OTHER; c <= CHG_DCP; c++) { + test_bc12_pi3usb9201_client_mode( + c, bc12_chg_limits[c].supplier, + bc12_chg_limits[c].current_limit); + } +} + +/* + * TODO(b/216660795): Cleanup state using a teardown_fn + */ +static void bc12_after(void *unused) +{ + set_ac_enabled(false); +} + +ZTEST_SUITE(bc12, drivers_predicate_post_main, NULL, NULL, bc12_after, NULL); diff --git a/zephyr/test/drivers/default/src/bma2x2.c b/zephyr/test/drivers/default/src/bma2x2.c new file mode 100644 index 0000000000..e848a265fd --- /dev/null +++ b/zephyr/test/drivers/default/src/bma2x2.c @@ -0,0 +1,942 @@ +/* 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 "common.h" +#include "i2c.h" +#include "emul/emul_bma255.h" +#include "emul/emul_common_i2c.h" + +#include "accelgyro.h" +#include "motion_sense.h" +#include "driver/accel_bma2x2.h" +#include "test/drivers/test_state.h" + +/** How accurate comparision of vectors should be. */ +#define V_EPS 8 + +#define BMA_NODE DT_NODELABEL(bma_emul) + +/** Mutex for test motion sensor */ +static mutex_t sensor_mutex; + +/** Rotation used in some tests */ +static const mat33_fp_t test_rotation = { { 0, FLOAT_TO_FP(1), 0 }, + { FLOAT_TO_FP(-1), 0, 0 }, + { 0, 0, FLOAT_TO_FP(-1) } }; + +/** Rotate given vector by test rotation */ +void rotate_int3v_by_test_rotation(int16_t *v) +{ + int16_t t; + + t = v[0]; + v[0] = -v[1]; + v[1] = t; + v[2] = -v[2]; +} + +static struct accelgyro_saved_data_t acc_data; + +/** Mock minimal motion sensor setup required for bma2x2 driver test */ +static struct motion_sensor_t ms = { + .name = "bma_emul", + .type = MOTIONSENSE_TYPE_ACCEL, + .drv = &bma2x2_accel_drv, + .mutex = &sensor_mutex, + .drv_data = &acc_data, + .port = I2C_PORT_NODELABEL(i2c0), + .i2c_spi_addr_flags = DT_REG_ADDR(BMA_NODE), + .rot_standard_ref = NULL, + .current_range = 0, +}; + +/** Set emulator offset values to vector of three int16_t */ +static void set_emul_offset(const struct emul *emul, int16_t *offset) +{ + bma_emul_set_off(emul, BMA_EMUL_AXIS_X, offset[0]); + bma_emul_set_off(emul, BMA_EMUL_AXIS_Y, offset[1]); + bma_emul_set_off(emul, BMA_EMUL_AXIS_Z, offset[2]); +} + +/** Save emulator offset values to vector of three int16_t */ +static void get_emul_offset(const struct emul *emul, int16_t *offset) +{ + offset[0] = bma_emul_get_off(emul, BMA_EMUL_AXIS_X); + offset[1] = bma_emul_get_off(emul, BMA_EMUL_AXIS_Y); + offset[2] = bma_emul_get_off(emul, BMA_EMUL_AXIS_Z); +} + +/** Set emulator accelerometer values to vector of three int16_t */ +static void set_emul_acc(const struct emul *emul, int16_t *acc) +{ + bma_emul_set_acc(emul, BMA_EMUL_AXIS_X, acc[0]); + bma_emul_set_acc(emul, BMA_EMUL_AXIS_Y, acc[1]); + bma_emul_set_acc(emul, BMA_EMUL_AXIS_Z, acc[2]); +} + +/** Convert accelerometer read to units used by emulator */ +static void drv_acc_to_emul(intv3_t drv, int range, int16_t *out) +{ + const int scale = MOTION_SCALING_FACTOR / BMA_EMUL_1G; + + out[0] = drv[0] * range / scale; + out[1] = drv[1] * range / scale; + out[2] = drv[2] * range / scale; +} + +/** Compare two vectors of three int16_t */ +static void compare_int3v_f(int16_t *exp_v, int16_t *v, int line) +{ + int i; + + for (i = 0; i < 3; i++) { + zassert_within( + exp_v[i], v[i], V_EPS, + "Expected [%d; %d; %d], got [%d; %d; %d]; line: %d", + exp_v[0], exp_v[1], exp_v[2], v[0], v[1], v[2], line); + } +} +#define compare_int3v(exp_v, v) compare_int3v_f(exp_v, v, __LINE__) + +/** Data for reset fail function */ +struct reset_func_data { + /** Fail for given attempts */ + int fail_attempts; + /** Do not fail for given attempts */ + int ok_before_fail; + /** Reset register value after given attempts */ + int reset_value; +}; + +/** + * Custom emulator function used in init test. It returns cmd soft when reset + * register is accessed data.reset_value times. Error is returned after + * accessing register data.ok_before_fail times. Error is returned during next + * data.fail_attempts times. + */ +static int emul_read_reset(const struct emul *emul, int reg, uint8_t *buf, + int bytes, void *data) +{ + struct reset_func_data *d = data; + + reg = bma_emul_access_reg(emul, reg, bytes, true /* = read */); + if (reg != BMA2x2_RST_ADDR) { + return 1; + } + + if (d->reset_value > 0) { + d->reset_value--; + bma_emul_set_reg(emul, BMA2x2_RST_ADDR, BMA2x2_CMD_SOFT_RESET); + } else { + bma_emul_set_reg(emul, BMA2x2_RST_ADDR, 0); + } + + if (d->ok_before_fail > 0) { + d->ok_before_fail--; + return 1; + } + + if (d->fail_attempts > 0) { + d->fail_attempts--; + return -EIO; + } + + return 1; +} + +/** + * Test get offset with and without rotation. Also test behaviour on I2C error. + */ +ZTEST_USER(bma2x2, test_bma_get_offset) +{ + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + int16_t ret_offset[3]; + int16_t exp_offset[3]; + int16_t temp; + + /* Test fail on each axis */ + i2c_common_emul_set_read_fail_reg(common_data, + BMA2x2_OFFSET_X_AXIS_ADDR); + zassert_equal(EC_ERROR_INVAL, + ms.drv->get_offset(&ms, ret_offset, &temp), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + BMA2x2_OFFSET_Y_AXIS_ADDR); + zassert_equal(EC_ERROR_INVAL, + ms.drv->get_offset(&ms, ret_offset, &temp), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + BMA2x2_OFFSET_Z_AXIS_ADDR); + zassert_equal(EC_ERROR_INVAL, + ms.drv->get_offset(&ms, ret_offset, &temp), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set emulator offset */ + exp_offset[0] = BMA_EMUL_1G / 10; + exp_offset[1] = BMA_EMUL_1G / 20; + exp_offset[2] = -(int)BMA_EMUL_1G / 30; + set_emul_offset(emul, exp_offset); + /* Disable rotation */ + ms.rot_standard_ref = NULL; + + /* Test get offset without rotation */ + zassert_equal(EC_SUCCESS, ms.drv->get_offset(&ms, ret_offset, &temp), + NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + compare_int3v(exp_offset, ret_offset); + + /* Setup rotation and rotate expected offset */ + ms.rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_offset); + + /* Test get offset with rotation */ + zassert_equal(EC_SUCCESS, ms.drv->get_offset(&ms, ret_offset, &temp), + NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + compare_int3v(exp_offset, ret_offset); +} + +/** + * Test set offset with and without rotation. Also test behaviour on I2C error. + */ +ZTEST_USER(bma2x2, test_bma_set_offset) +{ + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + int16_t ret_offset[3]; + int16_t exp_offset[3] = { 0, 0, 0 }; + int16_t temp = 0; + + /* Test fail on each axis */ + i2c_common_emul_set_write_fail_reg(common_data, + BMA2x2_OFFSET_X_AXIS_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_offset(&ms, exp_offset, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMA2x2_OFFSET_Y_AXIS_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_offset(&ms, exp_offset, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMA2x2_OFFSET_Z_AXIS_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_offset(&ms, exp_offset, temp), + NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set input offset */ + exp_offset[0] = BMA_EMUL_1G / 10; + exp_offset[1] = BMA_EMUL_1G / 20; + exp_offset[2] = -(int)BMA_EMUL_1G / 30; + /* Disable rotation */ + ms.rot_standard_ref = NULL; + + /* Test set offset without rotation */ + zassert_equal(EC_SUCCESS, ms.drv->set_offset(&ms, exp_offset, temp), + NULL); + get_emul_offset(emul, ret_offset); + compare_int3v(exp_offset, ret_offset); + + /* Setup rotation and rotate input for set_offset function */ + ms.rot_standard_ref = &test_rotation; + ret_offset[0] = exp_offset[0]; + ret_offset[1] = exp_offset[1]; + ret_offset[2] = exp_offset[2]; + rotate_int3v_by_test_rotation(ret_offset); + + /* Test set offset with rotation */ + zassert_equal(EC_SUCCESS, ms.drv->set_offset(&ms, ret_offset, temp), + NULL); + get_emul_offset(emul, ret_offset); + compare_int3v(exp_offset, ret_offset); +} + +/* + * Try to set range and check if expected range was set in driver and in + * emulator. + */ +static void check_set_range_f(const struct emul *emul, int range, int rnd, + int exp_range, int line) +{ + uint8_t exp_range_reg; + uint8_t range_reg; + + zassert_equal(EC_SUCCESS, ms.drv->set_range(&ms, range, rnd), + "set_range failed; line: %d", line); + zassert_equal(exp_range, ms.current_range, + "Expected range %d, got %d; line %d", exp_range, + ms.current_range, line); + range_reg = bma_emul_get_reg(emul, BMA2x2_RANGE_SELECT_ADDR); + range_reg &= BMA2x2_RANGE_SELECT_MSK; + + switch (exp_range) { + case 2: + exp_range_reg = BMA2x2_RANGE_2G; + break; + case 4: + exp_range_reg = BMA2x2_RANGE_4G; + break; + case 8: + exp_range_reg = BMA2x2_RANGE_8G; + break; + case 16: + exp_range_reg = BMA2x2_RANGE_16G; + break; + default: + /* Unknown expected range */ + zassert_unreachable( + "Expected range %d not supported by device; line %d", + exp_range, line); + return; + } + + zassert_equal(exp_range_reg, range_reg, + "Expected range reg 0x%x, got 0x%x; line %d", + exp_range_reg, range_reg, line); +} +#define check_set_range(emul, range, rnd, exp_range) \ + check_set_range_f(emul, range, rnd, exp_range, __LINE__) + +/** Test set range with and without I2C errors. */ +ZTEST_USER(bma2x2, test_bma_set_range) +{ + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + int start_range; + + /* Setup starting range, shouldn't be changed on error */ + start_range = 2; + ms.current_range = start_range; + bma_emul_set_reg(emul, BMA2x2_RANGE_SELECT_ADDR, BMA2x2_RANGE_2G); + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + BMA2x2_RANGE_SELECT_ADDR); + + /* Test fail on read */ + zassert_equal(EC_ERROR_INVAL, ms.drv->set_range(&ms, 12, 0), NULL); + zassert_equal(start_range, ms.current_range, NULL); + zassert_equal(BMA2x2_RANGE_2G, + bma_emul_get_reg(emul, BMA2x2_RANGE_SELECT_ADDR), NULL); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_range(&ms, 12, 1), NULL); + zassert_equal(start_range, ms.current_range, NULL); + zassert_equal(BMA2x2_RANGE_2G, + bma_emul_get_reg(emul, BMA2x2_RANGE_SELECT_ADDR), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + BMA2x2_RANGE_SELECT_ADDR); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms.drv->set_range(&ms, 12, 0), NULL); + zassert_equal(start_range, ms.current_range, NULL); + zassert_equal(BMA2x2_RANGE_2G, + bma_emul_get_reg(emul, BMA2x2_RANGE_SELECT_ADDR), NULL); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_range(&ms, 12, 1), NULL); + zassert_equal(start_range, ms.current_range, NULL); + zassert_equal(BMA2x2_RANGE_2G, + bma_emul_get_reg(emul, BMA2x2_RANGE_SELECT_ADDR), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting range with rounding down */ + check_set_range(emul, 1, 0, 2); + check_set_range(emul, 2, 0, 2); + check_set_range(emul, 3, 0, 2); + check_set_range(emul, 4, 0, 4); + check_set_range(emul, 5, 0, 4); + check_set_range(emul, 6, 0, 4); + check_set_range(emul, 7, 0, 4); + check_set_range(emul, 8, 0, 8); + check_set_range(emul, 9, 0, 8); + check_set_range(emul, 15, 0, 8); + check_set_range(emul, 16, 0, 16); + check_set_range(emul, 17, 0, 16); + + /* Test setting range with rounding up */ + check_set_range(emul, 1, 1, 2); + check_set_range(emul, 2, 1, 2); + check_set_range(emul, 3, 1, 4); + check_set_range(emul, 4, 1, 4); + check_set_range(emul, 5, 1, 8); + check_set_range(emul, 6, 1, 8); + check_set_range(emul, 7, 1, 8); + check_set_range(emul, 8, 1, 8); + check_set_range(emul, 9, 1, 16); + check_set_range(emul, 15, 1, 16); + check_set_range(emul, 16, 1, 16); + check_set_range(emul, 17, 1, 16); +} + +/** Test init with and without I2C errors. */ +ZTEST_USER(bma2x2, test_bma_init) +{ + struct reset_func_data reset_func_data; + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + + /* Setup emulator fail read function */ + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_CHIP_ID_ADDR); + + /* Test fail on chip id read */ + zassert_equal(EC_ERROR_UNKNOWN, ms.drv->init(&ms), NULL); + + /* Disable failing on chip id read, but set wrong value */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + bma_emul_set_reg(emul, BMA2x2_CHIP_ID_ADDR, 23); + + /* Test wrong chip id */ + zassert_equal(EC_ERROR_ACCESS_DENIED, ms.drv->init(&ms), NULL); + + /* Set correct chip id, but fail on reset reg read */ + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_RST_ADDR); + bma_emul_set_reg(emul, BMA2x2_CHIP_ID_ADDR, BMA255_CHIP_ID_MAJOR); + + /* Test fail on reset register read */ + zassert_equal(EC_ERROR_INVAL, ms.drv->init(&ms), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMA2x2_RST_ADDR); + + /* Test fail on reset register write */ + zassert_equal(EC_ERROR_INVAL, ms.drv->init(&ms), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail reset read function */ + reset_func_data.ok_before_fail = 1; + reset_func_data.fail_attempts = 100; + reset_func_data.reset_value = 0; + i2c_common_emul_set_read_func(common_data, emul_read_reset, + &reset_func_data); + + /* Test fail on too many reset read errors */ + zassert_equal(EC_ERROR_TIMEOUT, ms.drv->init(&ms), NULL); + + /* Test success after reset read errors */ + reset_func_data.ok_before_fail = 1; + reset_func_data.fail_attempts = 3; + zassert_equal(EC_RES_SUCCESS, ms.drv->init(&ms), NULL); + + /* Test success without read errors */ + reset_func_data.fail_attempts = 0; + zassert_equal(EC_RES_SUCCESS, ms.drv->init(&ms), NULL); + + /* Test fail on too many reset read wrong value */ + reset_func_data.fail_attempts = 0; + reset_func_data.reset_value = 100; + zassert_equal(EC_ERROR_TIMEOUT, ms.drv->init(&ms), NULL); + + /* Test success on few reset read wrong value */ + reset_func_data.fail_attempts = 0; + reset_func_data.reset_value = 4; + zassert_equal(EC_RES_SUCCESS, ms.drv->init(&ms), NULL); + + /* Remove custom emulator read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +/* + * Try to set data rate and check if expected rate was set in driver and in + * emulator. + */ +static void check_set_rate_f(const struct emul *emul, int rate, int rnd, + int exp_rate, int line) +{ + uint8_t exp_rate_reg; + uint8_t rate_reg; + int drv_rate; + + zassert_equal(EC_SUCCESS, ms.drv->set_data_rate(&ms, rate, rnd), + "set_data_rate failed; line: %d", line); + drv_rate = ms.drv->get_data_rate(&ms); + zassert_equal(exp_rate, drv_rate, "Expected rate %d, got %d; line %d", + exp_rate, drv_rate, line); + rate_reg = bma_emul_get_reg(emul, BMA2x2_BW_SELECT_ADDR); + rate_reg &= BMA2x2_BW_MSK; + + switch (exp_rate) { + case 7812: + exp_rate_reg = BMA2x2_BW_7_81HZ; + break; + case 15625: + exp_rate_reg = BMA2x2_BW_15_63HZ; + break; + case 31250: + exp_rate_reg = BMA2x2_BW_31_25HZ; + break; + case 62500: + exp_rate_reg = BMA2x2_BW_62_50HZ; + break; + case 125000: + exp_rate_reg = BMA2x2_BW_125HZ; + break; + case 250000: + exp_rate_reg = BMA2x2_BW_250HZ; + break; + case 500000: + exp_rate_reg = BMA2x2_BW_500HZ; + break; + case 1000000: + exp_rate_reg = BMA2x2_BW_1000HZ; + break; + default: + /* Unknown expected rate */ + zassert_unreachable( + "Expected rate %d not supported by device; line %d", + exp_rate, line); + return; + } + + zassert_equal(exp_rate_reg, rate_reg, + "Expected rate reg 0x%x, got 0x%x; line %d", exp_rate_reg, + rate_reg, line); +} +#define check_set_rate(emul, rate, rnd, exp_rate) \ + check_set_rate_f(emul, rate, rnd, exp_rate, __LINE__) + +/** Test set and get rate with and without I2C errors. */ +ZTEST_USER(bma2x2, test_bma_rate) +{ + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + uint8_t reg_rate; + int drv_rate; + + /* Test setting rate with rounding down */ + check_set_rate(emul, 1, 0, 7812); + check_set_rate(emul, 1, 0, 7812); + check_set_rate(emul, 7811, 0, 7812); + check_set_rate(emul, 7812, 0, 7812); + check_set_rate(emul, 7813, 0, 7812); + check_set_rate(emul, 15624, 0, 7812); + check_set_rate(emul, 15625, 0, 15625); + check_set_rate(emul, 15626, 0, 15625); + check_set_rate(emul, 31249, 0, 15625); + check_set_rate(emul, 31250, 0, 31250); + check_set_rate(emul, 31251, 0, 31250); + check_set_rate(emul, 62499, 0, 31250); + check_set_rate(emul, 62500, 0, 62500); + check_set_rate(emul, 62501, 0, 62500); + check_set_rate(emul, 124999, 0, 62500); + check_set_rate(emul, 125000, 0, 125000); + check_set_rate(emul, 125001, 0, 125000); + check_set_rate(emul, 249999, 0, 125000); + check_set_rate(emul, 250000, 0, 250000); + check_set_rate(emul, 250001, 0, 250000); + check_set_rate(emul, 499999, 0, 250000); + check_set_rate(emul, 500000, 0, 500000); + check_set_rate(emul, 500001, 0, 500000); + check_set_rate(emul, 999999, 0, 500000); + check_set_rate(emul, 1000000, 0, 1000000); + check_set_rate(emul, 1000001, 0, 1000000); + check_set_rate(emul, 2000000, 0, 1000000); + + /* Test setting rate with rounding up */ + check_set_rate(emul, 1, 1, 7812); + check_set_rate(emul, 1, 1, 7812); + check_set_rate(emul, 7811, 1, 7812); + check_set_rate(emul, 7812, 1, 7812); + check_set_rate(emul, 7813, 1, 15625); + check_set_rate(emul, 15624, 1, 15625); + check_set_rate(emul, 15625, 1, 15625); + check_set_rate(emul, 15626, 1, 31250); + check_set_rate(emul, 31249, 1, 31250); + check_set_rate(emul, 31250, 1, 31250); + check_set_rate(emul, 31251, 1, 62500); + check_set_rate(emul, 62499, 1, 62500); + check_set_rate(emul, 62500, 1, 62500); + check_set_rate(emul, 62501, 1, 125000); + check_set_rate(emul, 124999, 1, 125000); + check_set_rate(emul, 125000, 1, 125000); + check_set_rate(emul, 125001, 1, 250000); + check_set_rate(emul, 249999, 1, 250000); + check_set_rate(emul, 250000, 1, 250000); + check_set_rate(emul, 250001, 1, 500000); + check_set_rate(emul, 499999, 1, 500000); + check_set_rate(emul, 500000, 1, 500000); + check_set_rate(emul, 500001, 1, 1000000); + check_set_rate(emul, 999999, 1, 1000000); + check_set_rate(emul, 1000000, 1, 1000000); + check_set_rate(emul, 1000001, 1, 1000000); + check_set_rate(emul, 2000000, 1, 1000000); + + /* Current rate shouldn't be changed on error */ + drv_rate = ms.drv->get_data_rate(&ms); + reg_rate = bma_emul_get_reg(emul, BMA2x2_BW_SELECT_ADDR); + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_BW_SELECT_ADDR); + + /* Test fail on read */ + zassert_equal(EC_ERROR_INVAL, ms.drv->set_data_rate(&ms, 15625, 0), + NULL); + zassert_equal(drv_rate, ms.drv->get_data_rate(&ms), NULL); + zassert_equal(reg_rate, bma_emul_get_reg(emul, BMA2x2_BW_SELECT_ADDR), + NULL); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_data_rate(&ms, 15625, 1), + NULL); + zassert_equal(drv_rate, ms.drv->get_data_rate(&ms), NULL); + zassert_equal(reg_rate, bma_emul_get_reg(emul, BMA2x2_BW_SELECT_ADDR), + NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMA2x2_BW_SELECT_ADDR); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms.drv->set_data_rate(&ms, 15625, 0), + NULL); + zassert_equal(drv_rate, ms.drv->get_data_rate(&ms), NULL); + zassert_equal(reg_rate, bma_emul_get_reg(emul, BMA2x2_BW_SELECT_ADDR), + NULL); + zassert_equal(EC_ERROR_INVAL, ms.drv->set_data_rate(&ms, 15625, 1), + NULL); + zassert_equal(drv_rate, ms.drv->get_data_rate(&ms), NULL); + zassert_equal(reg_rate, bma_emul_get_reg(emul, BMA2x2_BW_SELECT_ADDR), + NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +/** Test read with and without I2C errors. */ +ZTEST_USER(bma2x2, test_bma_read) +{ + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + int16_t ret_acc[3]; + int16_t exp_acc[3]; + intv3_t ret_acc_v; + + /* Set offset 0 to simplify test */ + bma_emul_set_off(emul, BMA_EMUL_AXIS_X, 0); + bma_emul_set_off(emul, BMA_EMUL_AXIS_Y, 0); + bma_emul_set_off(emul, BMA_EMUL_AXIS_Z, 0); + + /* Test fail on each axis */ + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_X_AXIS_LSB_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->read(&ms, ret_acc_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_X_AXIS_MSB_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->read(&ms, ret_acc_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_Y_AXIS_LSB_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->read(&ms, ret_acc_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_Y_AXIS_MSB_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->read(&ms, ret_acc_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_Z_AXIS_LSB_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->read(&ms, ret_acc_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMA2x2_Z_AXIS_MSB_ADDR); + zassert_equal(EC_ERROR_INVAL, ms.drv->read(&ms, ret_acc_v), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set input accelerometer values */ + exp_acc[0] = BMA_EMUL_1G / 10; + exp_acc[1] = BMA_EMUL_1G / 20; + exp_acc[2] = -(int)BMA_EMUL_1G / 30; + set_emul_acc(emul, exp_acc); + /* Disable rotation */ + ms.rot_standard_ref = NULL; + /* Set range to 2G */ + zassert_equal(EC_SUCCESS, ms.drv->set_range(&ms, 2, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms.drv->read(&ms, ret_acc_v), NULL); + drv_acc_to_emul(ret_acc_v, 2, ret_acc); + compare_int3v(exp_acc, ret_acc); + + /* Set range to 4G */ + zassert_equal(EC_SUCCESS, ms.drv->set_range(&ms, 4, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms.drv->read(&ms, ret_acc_v), NULL); + drv_acc_to_emul(ret_acc_v, 4, ret_acc); + compare_int3v(exp_acc, ret_acc); + + /* Setup rotation and rotate expected vector */ + ms.rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_acc); + /* Set range to 2G */ + zassert_equal(EC_SUCCESS, ms.drv->set_range(&ms, 2, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms.drv->read(&ms, ret_acc_v), NULL); + drv_acc_to_emul(ret_acc_v, 2, ret_acc); + compare_int3v(exp_acc, ret_acc); + + /* Set range to 4G */ + zassert_equal(EC_SUCCESS, ms.drv->set_range(&ms, 4, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms.drv->read(&ms, ret_acc_v), NULL); + drv_acc_to_emul(ret_acc_v, 4, ret_acc); + compare_int3v(exp_acc, ret_acc); +} + +/** Data for functions used in perform_calib test */ +struct calib_func_data { + /** Time when offset compensation where triggered */ + int calib_start; + /** Time how long offset cal ready should be unset */ + int time; + /** Flag indicate if read should fail after compensation is triggered */ + int read_fail; +}; + +/** + * Custom emulator read function used in perform_calib test. It controls if + * cal ready bit in offset control register should be set. It is set after + * data.time miliseconds passed from data.calib_start time. Function returns + * error when offset control register is accessed when cal ready bit is not set + * and data.read_fail is not zero. + */ +static int emul_read_calib_func(const struct emul *emul, int reg, uint8_t *val, + int bytes, void *data) +{ + struct calib_func_data *d = data; + uint8_t reg_val; + int cur_time; + + reg = bma_emul_access_reg(emul, reg, bytes, true /* = read */); + if (reg != BMA2x2_OFFSET_CTRL_ADDR) { + return 1; + } + + reg_val = bma_emul_get_reg(emul, BMA2x2_OFFSET_CTRL_ADDR); + cur_time = k_uptime_get_32(); + if (cur_time - d->calib_start < d->time) { + if (d->read_fail) { + return -EIO; + } + reg_val &= ~BMA2x2_OFFSET_CAL_READY; + } else { + reg_val |= BMA2x2_OFFSET_CAL_READY; + } + bma_emul_set_reg(emul, BMA2x2_OFFSET_CTRL_ADDR, reg_val); + + return 1; +} + +/** + * Custom emulator write function used in perform_calib test. It sets + * calib_start field in data with time when offset compensation process was + * triggerd. + */ +static int emul_write_calib_func(const struct emul *emul, int reg, uint8_t val, + int bytes, void *data) +{ + struct calib_func_data *d = data; + + reg = bma_emul_access_reg(emul, reg, bytes, false /* = read */); + if (reg != BMA2x2_OFFSET_CTRL_ADDR) { + return 1; + } + + if (val & BMA2x2_OFFSET_TRIGGER_MASK) { + d->calib_start = k_uptime_get_32(); + } + + return 1; +} + +/** Test offset compensation with and without I2C errors. */ +ZTEST_USER(bma2x2, test_bma_perform_calib) +{ + struct calib_func_data func_data; + const struct emul *emul = EMUL_DT_GET(BMA_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + int16_t start_off[3]; + int16_t exp_off[3]; + int16_t ret_off[3]; + int range; + int rate; + mat33_fp_t rot = { { FLOAT_TO_FP(1), 0, 0 }, + { 0, FLOAT_TO_FP(1), 0 }, + { 0, 0, FLOAT_TO_FP(-1) } }; + + /* Range and rate cannot change after calibration */ + range = 4; + rate = 125000; + zassert_equal(EC_SUCCESS, ms.drv->set_range(&ms, range, 0), NULL); + zassert_equal(EC_SUCCESS, ms.drv->set_data_rate(&ms, rate, 0), NULL); + + /* Set offset 0 */ + start_off[0] = 0; + start_off[1] = 0; + start_off[2] = 0; + set_emul_offset(emul, start_off); + + /* Set input accelerometer values */ + exp_off[0] = BMA_EMUL_1G / 10; + exp_off[1] = BMA_EMUL_1G / 20; + exp_off[2] = -(int)BMA_EMUL_1G / 30; + set_emul_acc(emul, exp_off); + + /* + * Expected offset is [-X, -Y, 1G - Z] for no rotation or positive + * rotation on Z axis + */ + exp_off[0] = -exp_off[0]; + exp_off[1] = -exp_off[1]; + exp_off[2] = BMA_EMUL_1G - exp_off[2]; + + /* Setup emulator calibration functions */ + i2c_common_emul_set_read_func(common_data, emul_read_calib_func, + &func_data); + i2c_common_emul_set_write_func(common_data, emul_write_calib_func, + &func_data); + + /* Setup emulator to fail on first access to offset control register */ + func_data.calib_start = k_uptime_get_32(); + func_data.read_fail = 1; + func_data.time = 1000000; + + /* Test success on disabling calibration */ + zassert_equal(EC_SUCCESS, ms.drv->perform_calib(&ms, 0), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + + /* Test fail on first access to offset control register */ + zassert_equal(EC_ERROR_INVAL, ms.drv->perform_calib(&ms, 1), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + + /* Setup emulator to return cal not ready */ + func_data.calib_start = k_uptime_get_32(); + func_data.read_fail = 0; + func_data.time = 1000000; + + /* Test fail on cal not ready */ + zassert_equal(EC_ERROR_ACCESS_DENIED, ms.drv->perform_calib(&ms, 1), + NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + + /* + * Setup emulator to fail on access to offset control register after + * triggering offset compensation + */ + func_data.calib_start = 0; + func_data.read_fail = 1; + func_data.time = 160; + + /* Test fail on read during offset compensation */ + zassert_equal(EC_ERROR_INVAL, ms.drv->perform_calib(&ms, 1), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + + /* + * Setup emulator to return cal not ready for 1s after triggering + * offset compensation + */ + func_data.calib_start = 0; + func_data.read_fail = 0; + func_data.time = 1000; + + zassert_equal(EC_RES_TIMEOUT, ms.drv->perform_calib(&ms, 1), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + + /* + * Setup emulator to return cal not ready for 160ms after triggering + * offset compensation + */ + func_data.calib_start = 0; + func_data.read_fail = 0; + func_data.time = 160; + /* Disable rotation */ + ms.rot_standard_ref = NULL; + + /* Test successful offset compenastion without rotation */ + zassert_equal(EC_SUCCESS, ms.drv->perform_calib(&ms, 1), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + get_emul_offset(emul, ret_off); + compare_int3v(exp_off, ret_off); + + func_data.calib_start = 0; + /* Enable rotation with negative value on Z axis */ + ms.rot_standard_ref = &rot; + /* Expected offset -1G - accelerometer[Z] */ + exp_off[2] = + -((int)BMA_EMUL_1G) - bma_emul_get_acc(emul, BMA_EMUL_AXIS_Z); + + /* Test successful offset compenastion with negative Z rotation */ + zassert_equal(EC_SUCCESS, ms.drv->perform_calib(&ms, 1), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + get_emul_offset(emul, ret_off); + compare_int3v(exp_off, ret_off); + + func_data.calib_start = 0; + /* Set positive rotation on Z axis */ + rot[2][2] = FLOAT_TO_FP(1); + /* Expected offset 1G - accelerometer[Z] */ + exp_off[2] = BMA_EMUL_1G - bma_emul_get_acc(emul, BMA_EMUL_AXIS_Z); + + /* Test successful offset compenastion with positive Z rotation */ + zassert_equal(EC_SUCCESS, ms.drv->perform_calib(&ms, 1), NULL); + zassert_equal(range, ms.current_range, NULL); + zassert_equal(rate, ms.drv->get_data_rate(&ms), NULL); + get_emul_offset(emul, ret_off); + compare_int3v(exp_off, ret_off); + + /* Remove custom emulator functions */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); + i2c_common_emul_set_write_func(common_data, NULL, NULL); +} + +/** Test get resolution. */ +ZTEST_USER(bma2x2, test_bma_get_resolution) +{ + /* Resolution should be always 12 bits */ + zassert_equal(12, ms.drv->get_resolution(&ms), NULL); +} + +static void *bma2x2_setup(void) +{ + k_mutex_init(&sensor_mutex); + + return NULL; +} + +static void bma2x2_after(void *data) +{ + ms.rot_standard_ref = NULL; +} + +ZTEST_SUITE(bma2x2, drivers_predicate_post_main, bma2x2_setup, NULL, + bma2x2_after, NULL); diff --git a/zephyr/test/drivers/default/src/bmi160.c b/zephyr/test/drivers/default/src/bmi160.c new file mode 100644 index 0000000000..3f06e7f0fd --- /dev/null +++ b/zephyr/test/drivers/default/src/bmi160.c @@ -0,0 +1,2188 @@ +/* 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 "common.h" +#include "i2c.h" +#include "emul/emul_bmi.h" +#include "emul/emul_common_i2c.h" +#include "test/drivers/test_mocks.h" + +#include "motion_sense_fifo.h" +#include "driver/accelgyro_bmi160.h" +#include "driver/accelgyro_bmi_common.h" +#include "test/drivers/test_state.h" + +#define BMI_NODE DT_NODELABEL(accel_bmi160) +#define BMI_ACC_SENSOR_ID SENSOR_ID(DT_NODELABEL(ms_bmi160_accel)) +#define BMI_GYR_SENSOR_ID SENSOR_ID(DT_NODELABEL(ms_bmi160_gyro)) +#define BMI_INT_EVENT \ + TASK_EVENT_MOTION_SENSOR_INTERRUPT(SENSOR_ID(DT_ALIAS(bmi160_int))) + +/** How accurate comparision of vectors should be */ +#define V_EPS 8 + +/** Convert from one type of vector to another */ +#define convert_int3v_int16(v, r) \ + do { \ + r[0] = v[0]; \ + r[1] = v[1]; \ + r[2] = v[2]; \ + } while (0) + +/** Rotation used in some tests */ +static const mat33_fp_t test_rotation = { { 0, FLOAT_TO_FP(1), 0 }, + { FLOAT_TO_FP(-1), 0, 0 }, + { 0, 0, FLOAT_TO_FP(-1) } }; +/** Rotate given vector by test rotation */ +static void rotate_int3v_by_test_rotation(intv3_t v) +{ + int16_t t; + + t = v[0]; + v[0] = -v[1]; + v[1] = t; + v[2] = -v[2]; +} + +/** Set emulator accelerometer offset values to intv3_t vector */ +static void set_emul_acc_offset(const struct emul *emul, intv3_t offset) +{ + bmi_emul_set_off(emul, BMI_EMUL_ACC_X, offset[0]); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Y, offset[1]); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Z, offset[2]); +} + +/** Save emulator accelerometer offset values to intv3_t vector */ +static void get_emul_acc_offset(const struct emul *emul, intv3_t offset) +{ + offset[0] = bmi_emul_get_off(emul, BMI_EMUL_ACC_X); + offset[1] = bmi_emul_get_off(emul, BMI_EMUL_ACC_Y); + offset[2] = bmi_emul_get_off(emul, BMI_EMUL_ACC_Z); +} + +/** Set emulator accelerometer values to intv3_t vector */ +static void set_emul_acc(const struct emul *emul, intv3_t acc) +{ + bmi_emul_set_value(emul, BMI_EMUL_ACC_X, acc[0]); + bmi_emul_set_value(emul, BMI_EMUL_ACC_Y, acc[1]); + bmi_emul_set_value(emul, BMI_EMUL_ACC_Z, acc[2]); +} + +/** Set emulator gyroscope offset values to intv3_t vector */ +static void set_emul_gyr_offset(const struct emul *emul, intv3_t offset) +{ + bmi_emul_set_off(emul, BMI_EMUL_GYR_X, offset[0]); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Y, offset[1]); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Z, offset[2]); +} + +/** Save emulator gyroscope offset values to intv3_t vector */ +static void get_emul_gyr_offset(const struct emul *emul, intv3_t offset) +{ + offset[0] = bmi_emul_get_off(emul, BMI_EMUL_GYR_X); + offset[1] = bmi_emul_get_off(emul, BMI_EMUL_GYR_Y); + offset[2] = bmi_emul_get_off(emul, BMI_EMUL_GYR_Z); +} + +/** Set emulator gyroscope values to vector of three int16_t */ +static void set_emul_gyr(const struct emul *emul, intv3_t gyr) +{ + bmi_emul_set_value(emul, BMI_EMUL_GYR_X, gyr[0]); + bmi_emul_set_value(emul, BMI_EMUL_GYR_Y, gyr[1]); + bmi_emul_set_value(emul, BMI_EMUL_GYR_Z, gyr[2]); +} + +/** Convert accelerometer read to units used by emulator */ +static void drv_acc_to_emul(intv3_t drv, int range, intv3_t out) +{ + const int scale = MOTION_SCALING_FACTOR / BMI_EMUL_1G; + + out[0] = drv[0] * range / scale; + out[1] = drv[1] * range / scale; + out[2] = drv[2] * range / scale; +} + +/** Convert gyroscope read to units used by emulator */ +static void drv_gyr_to_emul(intv3_t drv, int range, intv3_t out) +{ + const int scale = MOTION_SCALING_FACTOR / BMI_EMUL_125_DEG_S; + + range /= 125; + out[0] = drv[0] * range / scale; + out[1] = drv[1] * range / scale; + out[2] = drv[2] * range / scale; +} + +/** Compare two vectors of intv3_t type */ +static void compare_int3v_f(intv3_t exp_v, intv3_t v, int eps, int line) +{ + int i; + + for (i = 0; i < 3; i++) { + zassert_within( + exp_v[i], v[i], eps, + "Expected [%d; %d; %d], got [%d; %d; %d]; line: %d", + exp_v[0], exp_v[1], exp_v[2], v[0], v[1], v[2], line); + } +} +#define compare_int3v_eps(exp_v, v, e) compare_int3v_f(exp_v, v, e, __LINE__) +#define compare_int3v(exp_v, v) compare_int3v_eps(exp_v, v, V_EPS) + +/** Test get accelerometer offset with and without rotation */ +ZTEST_USER(bmi160, test_bmi_acc_get_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int16_t ret[3]; + intv3_t ret_v; + intv3_t exp_v; + int16_t temp; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Set emulator offset */ + exp_v[0] = BMI_EMUL_1G / 10; + exp_v[1] = BMI_EMUL_1G / 20; + exp_v[2] = -(int)BMI_EMUL_1G / 30; + set_emul_acc_offset(emul, exp_v); + /* BMI driver returns value in mg units */ + exp_v[0] = 1000 / 10; + exp_v[1] = 1000 / 20; + exp_v[2] = -1000 / 30; + + /* Test fail on offset read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_ACC70); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_ACC70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_ACC70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test get offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v(exp_v, ret_v); + + /* Setup rotation and rotate expected offset */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + + /* Test get offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v(exp_v, ret_v); +} + +/** Test get gyroscope offset with and without rotation */ +ZTEST_USER(bmi160, test_bmi_gyr_get_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int16_t ret[3]; + intv3_t ret_v; + intv3_t exp_v; + int16_t temp; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set emulator offset */ + exp_v[0] = BMI_EMUL_125_DEG_S / 100; + exp_v[1] = BMI_EMUL_125_DEG_S / 200; + exp_v[2] = -(int)BMI_EMUL_125_DEG_S / 300; + set_emul_gyr_offset(emul, exp_v); + /* BMI driver returns value in mdeg/s units */ + exp_v[0] = 125000 / 100; + exp_v[1] = 125000 / 200; + exp_v[2] = -125000 / 300; + + /* Test fail on offset read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_GYR70); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_GYR70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_GYR70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test get offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v_eps(exp_v, ret_v, 64); + + /* Setup rotation and rotate expected offset */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + + /* Test get offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v_eps(exp_v, ret_v, 64); +} + +/** + * Test set accelerometer offset with and without rotation. Also test behaviour + * on I2C error. + */ +ZTEST_USER(bmi160, test_bmi_acc_set_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int16_t input_v[3]; + int16_t temp = 0; + intv3_t ret_v; + intv3_t exp_v; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Test fail on OFFSET EN GYR98 register read and write */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, BMI160_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on offset write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_OFFSET_ACC70); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI160_OFFSET_ACC70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI160_OFFSET_ACC70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set input offset */ + exp_v[0] = BMI_EMUL_1G / 10; + exp_v[1] = BMI_EMUL_1G / 20; + exp_v[2] = -(int)BMI_EMUL_1G / 30; + /* BMI driver accept value in mg units */ + input_v[0] = 1000 / 10; + input_v[1] = 1000 / 20; + input_v[2] = -1000 / 30; + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test set offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_acc_offset(emul, ret_v); + /* + * Depending on used range, accelerometer values may be up to 6 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_v, ret_v, 64); + /* Accelerometer offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_ACC_EN, + NULL); + + /* Setup rotation and rotate input for set_offset function */ + ms->rot_standard_ref = &test_rotation; + convert_int3v_int16(input_v, ret_v); + rotate_int3v_by_test_rotation(ret_v); + convert_int3v_int16(ret_v, input_v); + + /* Test set offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_acc_offset(emul, ret_v); + compare_int3v_eps(exp_v, ret_v, 64); + /* Accelerometer offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_ACC_EN, + NULL); +} + +/** + * Test set gyroscope offset with and without rotation. Also test behaviour + * on I2C error. + */ +ZTEST_USER(bmi160, test_bmi_gyr_set_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int16_t input_v[3]; + int16_t temp = 0; + intv3_t ret_v; + intv3_t exp_v; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Test fail on OFFSET EN GYR98 register read and write */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, BMI160_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on offset write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_OFFSET_GYR70); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI160_OFFSET_GYR70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI160_OFFSET_GYR70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set input offset */ + exp_v[0] = BMI_EMUL_125_DEG_S / 100; + exp_v[1] = BMI_EMUL_125_DEG_S / 200; + exp_v[2] = -(int)BMI_EMUL_125_DEG_S / 300; + /* BMI driver accept value in mdeg/s units */ + input_v[0] = 125000 / 100; + input_v[1] = 125000 / 200; + input_v[2] = -125000 / 300; + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test set offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_gyr_offset(emul, ret_v); + compare_int3v(exp_v, ret_v); + /* Gyroscope offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_GYRO_EN, + NULL); + + /* Setup rotation and rotate input for set_offset function */ + ms->rot_standard_ref = &test_rotation; + convert_int3v_int16(input_v, ret_v); + rotate_int3v_by_test_rotation(ret_v); + convert_int3v_int16(ret_v, input_v); + + /* Test set offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_gyr_offset(emul, ret_v); + compare_int3v(exp_v, ret_v); + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_GYRO_EN, + NULL); +} + +/** + * Try to set accelerometer range and check if expected range was set + * in driver and in emulator. + */ +static void check_set_acc_range_f(const struct emul *emul, + struct motion_sensor_t *ms, int range, + int rnd, int exp_range, int line) +{ + uint8_t exp_range_reg; + uint8_t range_reg; + + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, rnd), + "set_range failed; line: %d", line); + zassert_equal(exp_range, ms->current_range, + "Expected range %d, got %d; line %d", exp_range, + ms->current_range, line); + range_reg = bmi_emul_get_reg(emul, BMI160_ACC_RANGE); + + switch (exp_range) { + case 2: + exp_range_reg = BMI160_GSEL_2G; + break; + case 4: + exp_range_reg = BMI160_GSEL_4G; + break; + case 8: + exp_range_reg = BMI160_GSEL_8G; + break; + case 16: + exp_range_reg = BMI160_GSEL_16G; + break; + default: + /* Unknown expected range */ + zassert_unreachable( + "Expected range %d not supported by device; line %d", + exp_range, line); + return; + } + + zassert_equal(exp_range_reg, range_reg, + "Expected range reg 0x%x, got 0x%x; line %d", + exp_range_reg, range_reg, line); +} +#define check_set_acc_range(emul, ms, range, rnd, exp_range) \ + check_set_acc_range_f(emul, ms, range, rnd, exp_range, __LINE__) + +/** Test set accelerometer range with and without I2C errors */ +ZTEST_USER(bmi160, test_bmi_acc_set_range) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int start_range; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Setup starting range, shouldn't be changed on error */ + start_range = 2; + ms->current_range = start_range; + bmi_emul_set_reg(emul, BMI160_ACC_RANGE, BMI160_GSEL_2G); + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_ACC_RANGE); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 12, 0), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI160_GSEL_2G, bmi_emul_get_reg(emul, BMI160_ACC_RANGE), + NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 12, 1), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI160_GSEL_2G, bmi_emul_get_reg(emul, BMI160_ACC_RANGE), + NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting range with rounding down */ + check_set_acc_range(emul, ms, 1, 0, 2); + check_set_acc_range(emul, ms, 2, 0, 2); + check_set_acc_range(emul, ms, 3, 0, 2); + check_set_acc_range(emul, ms, 4, 0, 4); + check_set_acc_range(emul, ms, 5, 0, 4); + check_set_acc_range(emul, ms, 6, 0, 4); + check_set_acc_range(emul, ms, 7, 0, 4); + check_set_acc_range(emul, ms, 8, 0, 8); + check_set_acc_range(emul, ms, 9, 0, 8); + check_set_acc_range(emul, ms, 15, 0, 8); + check_set_acc_range(emul, ms, 16, 0, 16); + check_set_acc_range(emul, ms, 17, 0, 16); + + /* Test setting range with rounding up */ + check_set_acc_range(emul, ms, 1, 1, 2); + check_set_acc_range(emul, ms, 2, 1, 2); + check_set_acc_range(emul, ms, 3, 1, 4); + check_set_acc_range(emul, ms, 4, 1, 4); + check_set_acc_range(emul, ms, 5, 1, 8); + check_set_acc_range(emul, ms, 6, 1, 8); + check_set_acc_range(emul, ms, 7, 1, 8); + check_set_acc_range(emul, ms, 8, 1, 8); + check_set_acc_range(emul, ms, 9, 1, 16); + check_set_acc_range(emul, ms, 15, 1, 16); + check_set_acc_range(emul, ms, 16, 1, 16); + check_set_acc_range(emul, ms, 17, 1, 16); +} + +/** + * Try to set gyroscope range and check if expected range was set in driver and + * in emulator. + */ +static void check_set_gyr_range_f(const struct emul *emul, + struct motion_sensor_t *ms, int range, + int rnd, int exp_range, int line) +{ + uint8_t exp_range_reg; + uint8_t range_reg; + + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, rnd), + "set_range failed; line: %d", line); + zassert_equal(exp_range, ms->current_range, + "Expected range %d, got %d; line %d", exp_range, + ms->current_range, line); + range_reg = bmi_emul_get_reg(emul, BMI160_GYR_RANGE); + + switch (exp_range) { + case 125: + exp_range_reg = BMI160_DPS_SEL_125; + break; + case 250: + exp_range_reg = BMI160_DPS_SEL_250; + break; + case 500: + exp_range_reg = BMI160_DPS_SEL_500; + break; + case 1000: + exp_range_reg = BMI160_DPS_SEL_1000; + break; + case 2000: + exp_range_reg = BMI160_DPS_SEL_2000; + break; + default: + /* Unknown expected range */ + zassert_unreachable( + "Expected range %d not supported by device; line %d", + exp_range, line); + return; + } + + zassert_equal(exp_range_reg, range_reg, + "Expected range reg 0x%x, got 0x%x; line %d", + exp_range_reg, range_reg, line); +} +#define check_set_gyr_range(emul, ms, range, rnd, exp_range) \ + check_set_gyr_range_f(emul, ms, range, rnd, exp_range, __LINE__) + +/** Test set gyroscope range with and without I2C errors */ +ZTEST_USER(bmi160, test_bmi_gyr_set_range) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int start_range; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Setup starting range, shouldn't be changed on error */ + start_range = 250; + ms->current_range = start_range; + bmi_emul_set_reg(emul, BMI160_GYR_RANGE, BMI160_DPS_SEL_250); + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_GYR_RANGE); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 125, 0), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI160_DPS_SEL_250, + bmi_emul_get_reg(emul, BMI160_GYR_RANGE), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 125, 1), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI160_DPS_SEL_250, + bmi_emul_get_reg(emul, BMI160_GYR_RANGE), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting range with rounding down */ + check_set_gyr_range(emul, ms, 1, 0, 125); + check_set_gyr_range(emul, ms, 124, 0, 125); + check_set_gyr_range(emul, ms, 125, 0, 125); + check_set_gyr_range(emul, ms, 126, 0, 125); + check_set_gyr_range(emul, ms, 249, 0, 125); + check_set_gyr_range(emul, ms, 250, 0, 250); + check_set_gyr_range(emul, ms, 251, 0, 250); + check_set_gyr_range(emul, ms, 499, 0, 250); + check_set_gyr_range(emul, ms, 500, 0, 500); + check_set_gyr_range(emul, ms, 501, 0, 500); + check_set_gyr_range(emul, ms, 999, 0, 500); + check_set_gyr_range(emul, ms, 1000, 0, 1000); + check_set_gyr_range(emul, ms, 1001, 0, 1000); + check_set_gyr_range(emul, ms, 1999, 0, 1000); + check_set_gyr_range(emul, ms, 2000, 0, 2000); + check_set_gyr_range(emul, ms, 2001, 0, 2000); + + /* Test setting range with rounding up */ + check_set_gyr_range(emul, ms, 1, 1, 125); + check_set_gyr_range(emul, ms, 124, 1, 125); + check_set_gyr_range(emul, ms, 125, 1, 125); + check_set_gyr_range(emul, ms, 126, 1, 250); + check_set_gyr_range(emul, ms, 249, 1, 250); + check_set_gyr_range(emul, ms, 250, 1, 250); + check_set_gyr_range(emul, ms, 251, 1, 500); + check_set_gyr_range(emul, ms, 499, 1, 500); + check_set_gyr_range(emul, ms, 500, 1, 500); + check_set_gyr_range(emul, ms, 501, 1, 1000); + check_set_gyr_range(emul, ms, 999, 1, 1000); + check_set_gyr_range(emul, ms, 1000, 1, 1000); + check_set_gyr_range(emul, ms, 1001, 1, 2000); + check_set_gyr_range(emul, ms, 1999, 1, 2000); + check_set_gyr_range(emul, ms, 2000, 1, 2000); + check_set_gyr_range(emul, ms, 2001, 1, 2000); +} + +/** Test get resolution of acclerometer and gyroscope sensor */ +ZTEST_USER(bmi160, test_bmi_get_resolution) +{ + struct motion_sensor_t *ms; + + /* Test accelerometer */ + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Resolution should be always 16 bits */ + zassert_equal(16, ms->drv->get_resolution(ms), NULL); + + /* Test gyroscope */ + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Resolution should be always 16 bits */ + zassert_equal(16, ms->drv->get_resolution(ms), NULL); +} + +/** + * Try to set accelerometer data rate and check if expected rate was set + * in driver and in emulator. + */ +static void check_set_acc_rate_f(const struct emul *emul, + struct motion_sensor_t *ms, int rate, int rnd, + int exp_rate, int line) +{ + uint8_t exp_rate_reg; + uint8_t rate_reg; + int drv_rate; + + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, rnd), + "set_data_rate failed; line: %d", line); + drv_rate = ms->drv->get_data_rate(ms); + zassert_equal(exp_rate, drv_rate, "Expected rate %d, got %d; line %d", + exp_rate, drv_rate, line); + rate_reg = bmi_emul_get_reg(emul, BMI160_ACC_CONF); + rate_reg &= BMI_ODR_MASK; + + switch (exp_rate) { + case 12500: + exp_rate_reg = 0x5; + break; + case 25000: + exp_rate_reg = 0x6; + break; + case 50000: + exp_rate_reg = 0x7; + break; + case 100000: + exp_rate_reg = 0x8; + break; + case 200000: + exp_rate_reg = 0x9; + break; + case 400000: + exp_rate_reg = 0xa; + break; + case 800000: + exp_rate_reg = 0xb; + break; + case 1600000: + exp_rate_reg = 0xc; + break; + default: + /* Unknown expected rate */ + zassert_unreachable( + "Expected rate %d not supported by device; line %d", + exp_rate, line); + return; + } + + zassert_equal(exp_rate_reg, rate_reg, + "Expected rate reg 0x%x, got 0x%x; line %d", exp_rate_reg, + rate_reg, line); +} +#define check_set_acc_rate(emul, ms, rate, rnd, exp_rate) \ + check_set_acc_rate_f(emul, ms, rate, rnd, exp_rate, __LINE__) + +/** Test set and get accelerometer rate with and without I2C errors */ +ZTEST_USER(bmi160, test_bmi_acc_rate) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + uint8_t reg_rate; + int pmu_status; + int drv_rate; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Test setting rate with rounding down */ + check_set_acc_rate(emul, ms, 12500, 0, 12500); + check_set_acc_rate(emul, ms, 12501, 0, 12500); + check_set_acc_rate(emul, ms, 24999, 0, 12500); + check_set_acc_rate(emul, ms, 25000, 0, 25000); + check_set_acc_rate(emul, ms, 25001, 0, 25000); + check_set_acc_rate(emul, ms, 49999, 0, 25000); + check_set_acc_rate(emul, ms, 50000, 0, 50000); + check_set_acc_rate(emul, ms, 50001, 0, 50000); + check_set_acc_rate(emul, ms, 99999, 0, 50000); + check_set_acc_rate(emul, ms, 100000, 0, 100000); + check_set_acc_rate(emul, ms, 100001, 0, 100000); + check_set_acc_rate(emul, ms, 199999, 0, 100000); + check_set_acc_rate(emul, ms, 200000, 0, 200000); + check_set_acc_rate(emul, ms, 200001, 0, 200000); + check_set_acc_rate(emul, ms, 399999, 0, 200000); + /* + * We cannot test frequencies from 400000 to 1600000 because + * CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ is set to 250000 + */ + + /* Test setting rate with rounding up */ + check_set_acc_rate(emul, ms, 6251, 1, 12500); + check_set_acc_rate(emul, ms, 12499, 1, 12500); + check_set_acc_rate(emul, ms, 12500, 1, 12500); + check_set_acc_rate(emul, ms, 12501, 1, 25000); + check_set_acc_rate(emul, ms, 24999, 1, 25000); + check_set_acc_rate(emul, ms, 25000, 1, 25000); + check_set_acc_rate(emul, ms, 25001, 1, 50000); + check_set_acc_rate(emul, ms, 49999, 1, 50000); + check_set_acc_rate(emul, ms, 50000, 1, 50000); + check_set_acc_rate(emul, ms, 50001, 1, 100000); + check_set_acc_rate(emul, ms, 99999, 1, 100000); + check_set_acc_rate(emul, ms, 100000, 1, 100000); + check_set_acc_rate(emul, ms, 100001, 1, 200000); + check_set_acc_rate(emul, ms, 199999, 1, 200000); + check_set_acc_rate(emul, ms, 200000, 1, 200000); + + /* Test out of range rate with rounding down */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 0), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 12499, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 2000000, 0), NULL); + + /* Test out of range rate with rounding up */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 1), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 6250, 1), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 200001, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 2000000, 1), NULL); + + /* Current rate shouldn't be changed on error */ + drv_rate = ms->drv->get_data_rate(ms); + reg_rate = bmi_emul_get_reg(emul, BMI160_ACC_CONF); + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_CONF); + + /* Test fail on read */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_ACC_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_ACC_CONF), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_ACC_CONF); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_ACC_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_ACC_CONF), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test disabling sensor */ + pmu_status = BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET; + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET; + bmi_emul_set_reg(emul, BMI160_PMU_STATUS, pmu_status); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 0, 0), NULL); + + bmi_read8(ms->port, ms->i2c_spi_addr_flags, BMI160_PMU_STATUS, + &pmu_status); + zassert_equal(BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET, pmu_status, + "Gyroscope should be still enabled"); + + /* Test enabling sensor */ + bmi_emul_set_reg(emul, BMI160_PMU_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 50000, 0), NULL); + + bmi_read8(ms->port, ms->i2c_spi_addr_flags, BMI160_PMU_STATUS, + &pmu_status); + zassert_equal(BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET, pmu_status, + "Accelerometer should be enabled"); +} + +/** + * Try to set gyroscope data rate and check if expected rate was set + * in driver and in emulator. + */ +static void check_set_gyr_rate_f(const struct emul *emul, + struct motion_sensor_t *ms, int rate, int rnd, + int exp_rate, int line) +{ + uint8_t exp_rate_reg; + uint8_t rate_reg; + int drv_rate; + + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, rnd), + "set_data_rate failed; line: %d", line); + drv_rate = ms->drv->get_data_rate(ms); + zassert_equal(exp_rate, drv_rate, "Expected rate %d, got %d; line %d", + exp_rate, drv_rate, line); + rate_reg = bmi_emul_get_reg(emul, BMI160_GYR_CONF); + rate_reg &= BMI_ODR_MASK; + + switch (exp_rate) { + case 25000: + exp_rate_reg = 0x6; + break; + case 50000: + exp_rate_reg = 0x7; + break; + case 100000: + exp_rate_reg = 0x8; + break; + case 200000: + exp_rate_reg = 0x9; + break; + case 400000: + exp_rate_reg = 0xa; + break; + case 800000: + exp_rate_reg = 0xb; + break; + case 1600000: + exp_rate_reg = 0xc; + break; + case 3200000: + exp_rate_reg = 0xc; + break; + default: + /* Unknown expected rate */ + zassert_unreachable( + "Expected rate %d not supported by device; line %d", + exp_rate, line); + return; + } + + zassert_equal(exp_rate_reg, rate_reg, + "Expected rate reg 0x%x, got 0x%x; line %d", exp_rate_reg, + rate_reg, line); +} +#define check_set_gyr_rate(emul, ms, rate, rnd, exp_rate) \ + check_set_gyr_rate_f(emul, ms, rate, rnd, exp_rate, __LINE__) + +/** Test set and get gyroscope rate with and without I2C errors */ +ZTEST_USER(bmi160, test_bmi_gyr_rate) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + uint8_t reg_rate; + int pmu_status; + int drv_rate; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Test setting rate with rounding down */ + check_set_gyr_rate(emul, ms, 25000, 0, 25000); + check_set_gyr_rate(emul, ms, 25001, 0, 25000); + check_set_gyr_rate(emul, ms, 49999, 0, 25000); + check_set_gyr_rate(emul, ms, 50000, 0, 50000); + check_set_gyr_rate(emul, ms, 50001, 0, 50000); + check_set_gyr_rate(emul, ms, 99999, 0, 50000); + check_set_gyr_rate(emul, ms, 100000, 0, 100000); + check_set_gyr_rate(emul, ms, 100001, 0, 100000); + check_set_gyr_rate(emul, ms, 199999, 0, 100000); + check_set_gyr_rate(emul, ms, 200000, 0, 200000); + check_set_gyr_rate(emul, ms, 200001, 0, 200000); + check_set_gyr_rate(emul, ms, 399999, 0, 200000); + /* + * We cannot test frequencies from 400000 to 3200000 because + * CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ is set to 250000 + */ + + /* Test setting rate with rounding up */ + check_set_gyr_rate(emul, ms, 12501, 1, 25000); + check_set_gyr_rate(emul, ms, 24999, 1, 25000); + check_set_gyr_rate(emul, ms, 25000, 1, 25000); + check_set_gyr_rate(emul, ms, 25001, 1, 50000); + check_set_gyr_rate(emul, ms, 49999, 1, 50000); + check_set_gyr_rate(emul, ms, 50000, 1, 50000); + check_set_gyr_rate(emul, ms, 50001, 1, 100000); + check_set_gyr_rate(emul, ms, 99999, 1, 100000); + check_set_gyr_rate(emul, ms, 100000, 1, 100000); + check_set_gyr_rate(emul, ms, 100001, 1, 200000); + check_set_gyr_rate(emul, ms, 199999, 1, 200000); + check_set_gyr_rate(emul, ms, 200000, 1, 200000); + + /* Test out of range rate with rounding down */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 0), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 24999, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 4000000, 0), NULL); + + /* Test out of range rate with rounding up */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 1), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 12499, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 200001, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 4000000, 1), NULL); + + /* Current rate shouldn't be changed on error */ + drv_rate = ms->drv->get_data_rate(ms); + reg_rate = bmi_emul_get_reg(emul, BMI160_GYR_CONF); + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_CONF); + + /* Test fail on read */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_GYR_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_GYR_CONF), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_GYR_CONF); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_GYR_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI160_GYR_CONF), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test disabling sensor */ + pmu_status = BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET; + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET; + bmi_emul_set_reg(emul, BMI160_PMU_STATUS, pmu_status); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 0, 0), NULL); + + bmi_read8(ms->port, ms->i2c_spi_addr_flags, BMI160_PMU_STATUS, + &pmu_status); + zassert_equal(BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET, pmu_status, + "Accelerometer should be still enabled"); + + /* Test enabling sensor */ + bmi_emul_set_reg(emul, BMI160_PMU_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 50000, 0), NULL); + + bmi_read8(ms->port, ms->i2c_spi_addr_flags, BMI160_PMU_STATUS, + &pmu_status); + zassert_equal(BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET, pmu_status, + "Gyroscope should be enabled"); +} + +/** + * Test setting and getting scale in accelerometer and gyroscope sensors. + * Correct appling scale to results is checked in "read" test. + */ +ZTEST_USER(bmi160, test_bmi_scale) +{ + struct motion_sensor_t *ms; + int16_t ret_scale[3]; + int16_t exp_scale[3] = { 100, 231, 421 }; + int16_t t; + + /* Test accelerometer */ + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, exp_scale, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->get_scale(ms, ret_scale, &t), NULL); + + zassert_equal(t, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + zassert_equal(exp_scale[0], ret_scale[0], NULL); + zassert_equal(exp_scale[1], ret_scale[1], NULL); + zassert_equal(exp_scale[2], ret_scale[2], NULL); + + /* Test gyroscope */ + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, exp_scale, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->get_scale(ms, ret_scale, &t), NULL); + + zassert_equal(t, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + zassert_equal(exp_scale[0], ret_scale[0], NULL); + zassert_equal(exp_scale[1], ret_scale[1], NULL); + zassert_equal(exp_scale[2], ret_scale[2], NULL); +} + +/** Test reading temperature using accelerometer and gyroscope sensors */ +ZTEST_USER(bmi160, test_bmi_read_temp) +{ + struct motion_sensor_t *ms_acc, *ms_gyr; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int ret_temp; + int exp_temp; + + ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_TEMPERATURE_0); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_acc->drv->read_temp(ms_acc, &ret_temp), NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_gyr->drv->read_temp(ms_gyr, &ret_temp), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_TEMPERATURE_1); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_acc->drv->read_temp(ms_acc, &ret_temp), NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_gyr->drv->read_temp(ms_gyr, &ret_temp), NULL); + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Fail on invalid temperature */ + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, 0x00); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, 0x80); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_acc->drv->read_temp(ms_acc, &ret_temp), NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_gyr->drv->read_temp(ms_gyr, &ret_temp), NULL); + + /* + * Test correct values. Both motion sensors should return the same + * temperature. + */ + exp_temp = C_TO_K(23); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, 0x00); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, 0x00); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + + exp_temp = C_TO_K(87); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, 0xff); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, 0x7f); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + + exp_temp = C_TO_K(-41); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, 0x01); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, 0x80); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + + exp_temp = C_TO_K(47); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, 0x00); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, 0x30); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); +} + +/** Test reading accelerometer sensor data */ +ZTEST_USER(bmi160, test_bmi_acc_read) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + intv3_t ret_v; + intv3_t exp_v; + int16_t scale[3] = { MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE }; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Set offset 0 to simplify test */ + bmi_emul_set_off(emul, BMI_EMUL_ACC_X, 0); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Y, 0); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Z, 0); + + /* Fail on read status */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* When not ready, driver should return saved raw value */ + exp_v[0] = 100; + exp_v[1] = 200; + exp_v[2] = 300; + ms->raw_xyz[0] = exp_v[0]; + ms->raw_xyz[1] = exp_v[1]; + ms->raw_xyz[2] = exp_v[2]; + + /* Status not ready */ + bmi_emul_set_reg(emul, BMI160_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status only GYR ready */ + bmi_emul_set_reg(emul, BMI160_STATUS, BMI160_DRDY_GYR); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status ACC ready */ + bmi_emul_set_reg(emul, BMI160_STATUS, BMI160_DRDY_ACC); + + /* Set input accelerometer values */ + exp_v[0] = BMI_EMUL_1G / 10; + exp_v[1] = BMI_EMUL_1G / 20; + exp_v[2] = -(int)BMI_EMUL_1G / 30; + set_emul_acc(emul, exp_v); + /* Disable rotation */ + ms->rot_standard_ref = NULL; + /* Set scale */ + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, scale, 0), NULL); + /* Set range to 2G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 2, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 2, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 4G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 4, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 4, ret_v); + compare_int3v(exp_v, ret_v); + + /* Setup rotation and rotate expected vector */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + /* Set range to 2G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 2, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 2, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 4G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 4, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 4, ret_v); + compare_int3v(exp_v, ret_v); + + /* Fail on read of data registers */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_X_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_X_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_Y_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_Y_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_Z_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_Z_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + ms->rot_standard_ref = NULL; +} + +/** Test reading gyroscope sensor data */ +ZTEST_USER(bmi160, test_bmi_gyr_read) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + intv3_t ret_v; + intv3_t exp_v; + int16_t scale[3] = { MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE }; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Set offset 0 to simplify test */ + bmi_emul_set_off(emul, BMI_EMUL_GYR_X, 0); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Y, 0); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Z, 0); + + /* Fail on read status */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* When not ready, driver should return saved raw value */ + exp_v[0] = 100; + exp_v[1] = 200; + exp_v[2] = 300; + ms->raw_xyz[0] = exp_v[0]; + ms->raw_xyz[1] = exp_v[1]; + ms->raw_xyz[2] = exp_v[2]; + + /* Status not ready */ + bmi_emul_set_reg(emul, BMI160_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status only ACC ready */ + bmi_emul_set_reg(emul, BMI160_STATUS, BMI160_DRDY_ACC); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status GYR ready */ + bmi_emul_set_reg(emul, BMI160_STATUS, BMI160_DRDY_GYR); + + /* Set input accelerometer values */ + exp_v[0] = BMI_EMUL_125_DEG_S / 10; + exp_v[1] = BMI_EMUL_125_DEG_S / 20; + exp_v[2] = -(int)BMI_EMUL_125_DEG_S / 30; + set_emul_gyr(emul, exp_v); + /* Disable rotation */ + ms->rot_standard_ref = NULL; + /* Set scale */ + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, scale, 0), NULL); + /* Set range to 125°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 125, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 125, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 1000°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 1000, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 1000, ret_v); + compare_int3v(exp_v, ret_v); + + /* Setup rotation and rotate expected vector */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + /* Set range to 125°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 125, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 125, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 1000°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 1000, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 1000, ret_v); + compare_int3v(exp_v, ret_v); + + /* Fail on read of data registers */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_X_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_X_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_Y_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_Y_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_Z_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_Z_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + ms->rot_standard_ref = NULL; +} + +/** + * Custom emulatro read function which always return not ready STATUS register. + * Used in calibration test. + */ +static int emul_nrdy(const struct emul *emul, int reg, uint8_t *val, int byte, + void *data) +{ + if (reg == BMI160_STATUS) { + bmi_emul_set_reg(emul, BMI160_STATUS, 0); + *val = 0; + + return 0; + } + + return 1; +} + +/** Test acceleromtere calibration */ +ZTEST_USER(bmi160, test_bmi_acc_perform_calib) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + uint8_t pmu_status; + intv3_t start_off; + intv3_t exp_off; + intv3_t ret_off; + int range; + int rate; + mat33_fp_t rot = { { FLOAT_TO_FP(1), 0, 0 }, + { 0, FLOAT_TO_FP(1), 0 }, + { 0, 0, FLOAT_TO_FP(-1) } }; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Enable sensors */ + pmu_status = BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET; + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET; + bmi_emul_set_reg(emul, BMI160_PMU_STATUS, pmu_status); + + /* Range and rate cannot change after calibration */ + range = 4; + rate = 50000; + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, 0), NULL); + + /* Set offset 0 */ + start_off[0] = 0; + start_off[1] = 0; + start_off[2] = 0; + set_emul_acc_offset(emul, start_off); + + /* Set input accelerometer values */ + exp_off[0] = BMI_EMUL_1G / 10; + exp_off[1] = BMI_EMUL_1G / 20; + exp_off[2] = BMI_EMUL_1G - (int)BMI_EMUL_1G / 30; + set_emul_acc(emul, exp_off); + + /* + * Expected offset is [-X, -Y, 1G - Z] for no rotation or positive + * rotation on Z axis + */ + exp_off[0] = -exp_off[0]; + exp_off[1] = -exp_off[1]; + exp_off[2] = BMI_EMUL_1G - exp_off[2]; + + /* Test fail on rate set */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_ACC_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on status read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + /* Stop fast offset compensation before next test */ + bmi_emul_set_reg(emul, BMI160_CMD_REG, BMI160_CMD_NOOP); + + /* Test fail on data not ready */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_read_func(common_data, emul_nrdy, NULL); + zassert_equal(EC_RES_TIMEOUT, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + /* Remove custom emulator read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); + /* Stop fast offset compensation before next test */ + bmi_emul_set_reg(emul, BMI160_CMD_REG, BMI160_CMD_NOOP); + + /* Disable rotation */ + ms->rot_standard_ref = NULL; + /* Test successful offset compenastion without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + get_emul_acc_offset(emul, ret_off); + /* + * Depending on used range, accelerometer values may be up to 6 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_off, ret_off, 64); + /* Acelerometer offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_ACC_EN, + NULL); + + /* Enable rotation with negative value on Z axis */ + ms->rot_standard_ref = &rot; + /* Expected offset -1G - accelerometer[Z] */ + bmi_emul_set_value(emul, BMI_EMUL_ACC_Z, -(int)BMI_EMUL_1G - 1234); + exp_off[2] = 1234; + + /* Test successful offset compenastion with negative Z rotation */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + get_emul_acc_offset(emul, ret_off); + compare_int3v_eps(exp_off, ret_off, 64); + /* Acelerometer offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_ACC_EN, + NULL); + + /* Set positive rotation on Z axis */ + rot[2][2] = FLOAT_TO_FP(1); + /* Expected offset 1G - accelerometer[Z] */ + bmi_emul_set_value(emul, BMI_EMUL_ACC_Z, BMI_EMUL_1G - 1234); + exp_off[2] = 1234; + + /* Test successful offset compenastion with positive Z rotation */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + get_emul_acc_offset(emul, ret_off); + compare_int3v_eps(exp_off, ret_off, 64); + /* Acelerometer offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_ACC_EN, + NULL); + /* Disable rotation */ + ms->rot_standard_ref = NULL; +} + +/** Test gyroscope calibration */ +ZTEST_USER(bmi160, test_bmi_gyr_perform_calib) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + uint8_t pmu_status; + intv3_t start_off; + intv3_t exp_off; + intv3_t ret_off; + int range; + int rate; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Enable sensors */ + pmu_status = BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET; + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET; + bmi_emul_set_reg(emul, BMI160_PMU_STATUS, pmu_status); + + /* Range and rate cannot change after calibration */ + range = 250; + rate = 50000; + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, 0), NULL); + + /* Set offset 0 */ + start_off[0] = 0; + start_off[1] = 0; + start_off[2] = 0; + set_emul_gyr_offset(emul, start_off); + + /* Set input accelerometer values */ + exp_off[0] = BMI_EMUL_125_DEG_S / 100; + exp_off[1] = BMI_EMUL_125_DEG_S / 200; + exp_off[2] = -(int)BMI_EMUL_125_DEG_S / 300; + set_emul_gyr(emul, exp_off); + + /* Expected offset is [-X, -Y, -Z] */ + exp_off[0] = -exp_off[0]; + exp_off[1] = -exp_off[1]; + exp_off[2] = -exp_off[2]; + + /* Test success on disabling calibration */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 0), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on rate set */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_GYR_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on status read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + /* Stop fast offset compensation before next test */ + bmi_emul_set_reg(emul, BMI160_CMD_REG, BMI160_CMD_NOOP); + + /* Test fail on data not ready */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_read_func(common_data, emul_nrdy, NULL); + zassert_equal(EC_RES_TIMEOUT, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + /* Remove custom emulator read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); + /* Stop fast offset compensation before next test */ + bmi_emul_set_reg(emul, BMI160_CMD_REG, BMI160_CMD_NOOP); + + /* Test successful offset compenastion */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + get_emul_gyr_offset(emul, ret_off); + /* + * Depending on used range, gyroscope values may be up to 4 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_off, ret_off, 32); + /* Gyroscope offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI160_OFFSET_EN_GYR98) & + BMI160_OFFSET_GYRO_EN, + NULL); +} + +/** Test init function of BMI160 accelerometer and gyroscope sensors */ +ZTEST_USER(bmi160, test_bmi_init) +{ + struct motion_sensor_t *ms_acc, *ms_gyr; + + ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Test successful init */ + zassert_equal(EC_RES_SUCCESS, ms_acc->drv->init(ms_acc), NULL); + + zassert_equal(EC_RES_SUCCESS, ms_gyr->drv->init(ms_gyr), NULL); +} + +/** Data for custom emulator read function used in FIFO test */ +struct fifo_func_data { + uint16_t interrupts; +}; + +/** + * Custom emulator read function used in FIFO test. It sets interrupt registers + * to value passed as additional data. It sets interrupt registers to 0 after + * access. + */ +static int emul_fifo_func(const struct emul *emul, int reg, uint8_t *val, + int byte, void *data) +{ + struct fifo_func_data *d = data; + + if (reg + byte == BMI160_INT_STATUS_0) { + bmi_emul_set_reg(emul, BMI160_INT_STATUS_0, + d->interrupts & 0xff); + d->interrupts &= 0xff00; + } else if (reg + byte == BMI160_INT_STATUS_1) { + bmi_emul_set_reg(emul, BMI160_INT_STATUS_1, + (d->interrupts >> 8) & 0xff); + d->interrupts &= 0xff; + } + + return 1; +} + +/** + * Run irq handler on accelerometer sensor and check if committed data in FIFO + * match what was set in FIFO frames in emulator. + */ +static void check_fifo_f(struct motion_sensor_t *ms_acc, + struct motion_sensor_t *ms_gyr, + struct bmi_emul_frame *frame, int acc_range, + int gyr_range, int line) +{ + struct ec_response_motion_sensor_data vector; + struct bmi_emul_frame *f_acc, *f_gyr; + uint32_t event = BMI_INT_EVENT; + uint16_t size; + intv3_t exp_v; + intv3_t ret_v; + + /* Find first frame of acc and gyr type */ + f_acc = frame; + while (f_acc != NULL && !(f_acc->type & BMI_EMUL_FRAME_ACC)) { + f_acc = f_acc->next; + } + + f_gyr = frame; + while (f_gyr != NULL && !(f_gyr->type & BMI_EMUL_FRAME_GYR)) { + f_gyr = f_gyr->next; + } + + /* Read FIFO in driver */ + zassert_equal(EC_SUCCESS, ms_acc->drv->irq_handler(ms_acc, &event), + "Failed to read FIFO in irq handler, line %d", line); + + /* Read all data committed to FIFO */ + while (motion_sense_fifo_read(sizeof(vector), 1, &vector, &size)) { + /* Ignore timestamp frames */ + if (vector.flags == MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) { + continue; + } + + /* Check acclerometer frames */ + if (ms_acc - motion_sensors == vector.sensor_num) { + if (f_acc == NULL) { + zassert_unreachable( + "Not expected acclerometer data in FIFO, line %d", + line); + } + + convert_int3v_int16(vector.data, ret_v); + drv_acc_to_emul(ret_v, acc_range, ret_v); + exp_v[0] = f_acc->acc_x; + exp_v[1] = f_acc->acc_y; + exp_v[2] = f_acc->acc_z; + compare_int3v_f(exp_v, ret_v, V_EPS, line); + f_acc = f_acc->next; + } + + /* Check gyroscope frames */ + if (ms_gyr - motion_sensors == vector.sensor_num) { + if (f_gyr == NULL) { + zassert_unreachable( + "Not expected gyroscope data in FIFO, line %d", + line); + } + + convert_int3v_int16(vector.data, ret_v); + drv_gyr_to_emul(ret_v, gyr_range, ret_v); + exp_v[0] = f_gyr->gyr_x; + exp_v[1] = f_gyr->gyr_y; + exp_v[2] = f_gyr->gyr_z; + compare_int3v_f(exp_v, ret_v, V_EPS, line); + f_gyr = f_gyr->next; + } + } + + /* Skip frames of different type at the end */ + while (f_acc != NULL && !(f_acc->type & BMI_EMUL_FRAME_ACC)) { + f_acc = f_acc->next; + } + + while (f_gyr != NULL && !(f_gyr->type & BMI_EMUL_FRAME_GYR)) { + f_gyr = f_gyr->next; + } + + /* All frames are readed */ + zassert_is_null(f_acc, "Not all accelerometer frames are read, line %d", + line); + zassert_is_null(f_gyr, "Not all gyroscope frames are read, line %d", + line); +} +#define check_fifo(ms_acc, ms_gyr, frame, acc_range, gyr_range) \ + check_fifo_f(ms_acc, ms_gyr, frame, acc_range, gyr_range, __LINE__) + +/** Test irq handler of accelerometer sensor */ +ZTEST_USER(bmi160, test_bmi_acc_fifo) +{ + struct motion_sensor_t *ms, *ms_gyr; + struct fifo_func_data func_data; + struct bmi_emul_frame f[3]; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int gyr_range = 125; + int acc_range = 2; + int event; + + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* init bmi before test */ + zassert_equal(EC_RES_SUCCESS, ms->drv->init(ms), NULL); + zassert_equal(EC_RES_SUCCESS, ms_gyr->drv->init(ms_gyr), NULL); + + /* Need to be set to collect all data in FIFO */ + ms->oversampling_ratio = 1; + ms_gyr->oversampling_ratio = 1; + /* Only BMI event should be handled */ + event = 0x1234 & ~BMI_INT_EVENT; + zassert_equal(EC_ERROR_NOT_HANDLED, ms->drv->irq_handler(ms, &event), + NULL); + + event = BMI_INT_EVENT; + + /* Test fail to read interrupt status registers */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_INT_STATUS_0); + zassert_equal(EC_ERROR_INVAL, ms->drv->irq_handler(ms, &event), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_INT_STATUS_1); + zassert_equal(EC_ERROR_INVAL, ms->drv->irq_handler(ms, &event), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test no interrupt */ + bmi_emul_set_reg(emul, BMI160_INT_STATUS_0, 0); + bmi_emul_set_reg(emul, BMI160_INT_STATUS_1, 0); + + /* Enable sensor FIFO */ + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 50000, 0), NULL); + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, NULL, acc_range, gyr_range); + + /* Set custom function for FIFO test */ + i2c_common_emul_set_read_func(common_data, emul_fifo_func, &func_data); + /* Set range */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, acc_range, 0), NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->set_range(ms_gyr, gyr_range, 0), + NULL); + /* Setup single accelerometer frame */ + f[0].type = BMI_EMUL_FRAME_ACC; + f[0].acc_x = BMI_EMUL_1G / 10; + f[0].acc_y = BMI_EMUL_1G / 20; + f[0].acc_z = -(int)BMI_EMUL_1G / 30; + f[0].next = NULL; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI160_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Setup second accelerometer frame */ + f[1].type = BMI_EMUL_FRAME_ACC; + f[1].acc_x = -(int)BMI_EMUL_1G / 40; + f[1].acc_y = BMI_EMUL_1G / 50; + f[1].acc_z = BMI_EMUL_1G / 60; + f[0].next = &(f[1]); + f[1].next = NULL; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI160_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Enable sensor FIFO */ + zassert_equal(EC_SUCCESS, ms_gyr->drv->set_data_rate(ms_gyr, 50000, 0), + NULL); + + /* Setup first gyroscope frame (after two accelerometer frames) */ + f[2].type = BMI_EMUL_FRAME_GYR; + f[2].gyr_x = -(int)BMI_EMUL_125_DEG_S / 100; + f[2].gyr_y = BMI_EMUL_125_DEG_S / 200; + f[2].gyr_z = BMI_EMUL_125_DEG_S / 300; + f[1].next = &(f[2]); + f[2].next = NULL; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI160_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Setup second accelerometer frame to by gyroscope frame too */ + f[1].type |= BMI_EMUL_FRAME_GYR; + f[1].gyr_x = -(int)BMI_EMUL_125_DEG_S / 300; + f[1].gyr_y = BMI_EMUL_125_DEG_S / 400; + f[1].gyr_z = BMI_EMUL_125_DEG_S / 500; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI160_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Skip frame should be ignored by driver */ + bmi_emul_set_skipped_frames(emul, 8); + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI160_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Setup second frame as an config frame */ + f[1].type = BMI_EMUL_FRAME_CONFIG; + /* Indicate that accelerometer range changed */ + f[1].config = 0x1; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI160_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Remove custom emulator read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +/** Test irq handler of gyroscope sensor */ +ZTEST_USER(bmi160, test_bmi_gyr_fifo) +{ + struct motion_sensor_t *ms; + uint32_t event; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Interrupt shuldn't be triggered for gyroscope motion sense */ + event = BMI_INT_EVENT; + zassert_equal(EC_ERROR_NOT_HANDLED, ms->drv->irq_handler(ms, &event), + NULL); +} + +/** Test reading from compass via `bmi160_sec_raw_read8()` */ +ZTEST_USER(bmi160, test_bmi_sec_raw_read8) +{ + struct motion_sensor_t *ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + uint8_t expected_read_value = 0xAA; + uint8_t requested_reg_addr = 0x55; + uint8_t actual_reg_addr; + int actual_read_value; + int ret; + + bmi_emul_set_reg(emul, BMI160_MAG_I2C_READ_DATA, expected_read_value); + + ret = bmi160_sec_raw_read8(ms->port, ms->i2c_spi_addr_flags, + requested_reg_addr, &actual_read_value); + + /* Verify return value */ + zassert_equal(ret, EC_RES_SUCCESS, "Expected return code %d but got %d", + EC_RES_SUCCESS, ret); + + /* Verify the correct value was read */ + zassert_equal(expected_read_value, actual_read_value, + "Read value $%02x but expected to read $%02x", + actual_read_value, expected_read_value); + + /* Verify the intended register address was read */ + actual_reg_addr = bmi_emul_get_reg(emul, BMI160_MAG_I2C_READ_ADDR); + zassert_equal(requested_reg_addr, actual_reg_addr, + "Read reg $%02x but expected to read $%02x", + actual_reg_addr, requested_reg_addr); +} + +/** Test writing to compass via `bmi160_sec_raw_write8()` */ +ZTEST_USER(bmi160, test_bmi_sec_raw_write8) +{ + struct motion_sensor_t *ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + uint8_t expected_write_value = 0xAB; + uint8_t requested_reg_addr = 0x56; + uint8_t actual_reg_addr; + int actual_written_value; + int ret; + + ret = bmi160_sec_raw_write8(ms->port, ms->i2c_spi_addr_flags, + requested_reg_addr, expected_write_value); + + /* Verify return value */ + zassert_equal(ret, EC_RES_SUCCESS, "Expected return code %d but got %d", + EC_RES_SUCCESS, ret); + + /* Verify the correct value was written */ + actual_written_value = + bmi_emul_get_reg(emul, BMI160_MAG_I2C_WRITE_DATA); + zassert_equal(expected_write_value, actual_written_value, + "Wrote value $%02x but expected to write $%02x", + actual_written_value, expected_write_value); + + /* Verify the intended register address was used */ + actual_reg_addr = bmi_emul_get_reg(emul, BMI160_MAG_I2C_WRITE_ADDR); + zassert_equal(requested_reg_addr, actual_reg_addr, + "Wrote reg $%02x but expected to write $%02x", + actual_reg_addr, requested_reg_addr); +} + +/** Test setting an offset on an invalid sensor type */ +ZTEST_USER(bmi160, test_bmi_set_offset_invalid_type) +{ + struct motion_sensor_t ms_fake; + int ret; + int16_t unused_offset = 0; + int16_t temp = 0; + + /* make a copy of the accel motion sensor so we modify its type */ + memcpy(&ms_fake, &motion_sensors[BMI_ACC_SENSOR_ID], sizeof(ms_fake)); + ms_fake.type = MOTIONSENSE_TYPE_MAX; + + ret = ms_fake.drv->set_offset(&ms_fake, &unused_offset, temp); + + zassert_equal(ret, EC_RES_INVALID_PARAM, + "Expected return code of %d but got %d", + EC_RES_INVALID_PARAM, ret); +} + +/** Test performing a calibration on a magnetometer, which is not supported */ +ZTEST_USER(bmi160, test_bmi_perform_calib_invalid_type) +{ + struct motion_sensor_t ms_fake; + int ret; + + /* make a copy of the accel motion sensor so we modify its type */ + memcpy(&ms_fake, &motion_sensors[BMI_ACC_SENSOR_ID], sizeof(ms_fake)); + ms_fake.type = MOTIONSENSE_TYPE_MAG; + + ret = ms_fake.drv->perform_calib(&ms_fake, 1); + + zassert_equal(ret, EC_RES_INVALID_PARAM, + "Expected return code of %d but got %d", + EC_RES_INVALID_PARAM, ret); +} + +/** Test reading the onboard temperature sensor */ +ZTEST_USER(bmi160, test_bmi_temp_sensor) +{ + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + int ret; + + /* Part 1: + * Set up the register so we read 300 Kelvin. 0x0000 is 23 deg C, and + * each LSB is 0.5^9 deg C. See BMI160 datasheet for more details. + */ + int actual_read_temp_k, expected_temp_k = 300; + uint16_t temp_reg_value = (K_TO_C(expected_temp_k) - 23) << 9; + + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, temp_reg_value & 0xFF); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, temp_reg_value >> 8); + + /* The output will be in Kelvin */ + ret = bmi160_get_sensor_temp(BMI_ACC_SENSOR_ID, &actual_read_temp_k); + + zassert_equal(ret, EC_RES_SUCCESS, "Expected %d but got %d", + EC_RES_SUCCESS, ret); + zassert_equal(expected_temp_k, actual_read_temp_k, + "Expected %dK but got %dK", expected_temp_k, + actual_read_temp_k); + + /* Part 2: + * Have the chip return an invalid reading. + */ + temp_reg_value = BMI_INVALID_TEMP; + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_0, temp_reg_value & 0xFF); + bmi_emul_set_reg(emul, BMI160_TEMPERATURE_1, temp_reg_value >> 8); + + ret = bmi160_get_sensor_temp(BMI_ACC_SENSOR_ID, &actual_read_temp_k); + + zassert_equal(ret, EC_ERROR_NOT_POWERED, "Expected %d but got %d", + EC_ERROR_NOT_POWERED, ret); +} + +ZTEST_USER(bmi160, test_bmi_interrupt_handler) +{ + /* The accelerometer interrupt handler simply sets an event flag for the + * motion sensing task. Make sure that flag starts cleared, fire the + * interrupt, and ensure the flag is set. + */ + + atomic_t *mask; + + mask = task_get_event_bitmap(TASK_ID_MOTIONSENSE); + zassert_true(mask != NULL, + "Got a null pointer when getting event bitmap."); + zassert_true((*mask & CONFIG_ACCELGYRO_BMI160_INT_EVENT) == 0, + "Event flag is set before firing interrupt"); + + bmi160_interrupt(0); + + mask = task_get_event_bitmap(TASK_ID_MOTIONSENSE); + zassert_true(mask != NULL, + "Got a null pointer when getting event bitmap."); + zassert_true(*mask & CONFIG_ACCELGYRO_BMI160_INT_EVENT, + "Event flag is not set after firing interrupt"); +} + +/* Make an I2C emulator mock wrapped in FFF for use with test_bmi_init_chip_id() + */ +FAKE_VALUE_FUNC(int, bmi_init_chip_id_mock_write_fn, const struct emul *, int, + uint8_t, int, void *); + +/** Test handling of invalid or unreadable chip IDs in init() */ +ZTEST_USER(bmi160, test_bmi_init_chip_id) +{ + struct motion_sensor_t *ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + int ret; + + /* Part 1: Cannot read the Chip ID register */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_CHIP_ID); + ret = ms->drv->init(ms); + + zassert_equal(ret, EC_ERROR_UNKNOWN, "Expected %d but got %d", + EC_ERROR_UNKNOWN, ret); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Part 2: Incorrect chip ID - this triggers a series of writes in an + * attempt to 'unlock' the chip. + */ + + /* Have the mocked write function return 1 so everything is passed + * through. We only care about using FFF to capture the argument + * history. + */ + + RESET_FAKE(bmi_init_chip_id_mock_write_fn); + bmi_init_chip_id_mock_write_fn_fake.return_val = 1; + i2c_common_emul_set_write_func(common_data, + bmi_init_chip_id_mock_write_fn, NULL); + + /* Return a phony chip ID */ + bmi_emul_set_reg(emul, BMI160_CHIP_ID, 0xFF); + + ret = ms->drv->init(ms); + + /* Verify return value */ + zassert_equal(ret, EC_ERROR_ACCESS_DENIED, "Expected %d but got %d", + EC_ERROR_ACCESS_DENIED, ret); + + /* Verify that all expected I2C writes were completed, in order */ + MOCK_ASSERT_I2C_WRITE(bmi_init_chip_id_mock_write_fn, 0, BMI160_CMD_REG, + BMI160_CMD_EXT_MODE_EN_B0); + MOCK_ASSERT_I2C_WRITE(bmi_init_chip_id_mock_write_fn, 1, BMI160_CMD_REG, + BMI160_CMD_EXT_MODE_EN_B1); + MOCK_ASSERT_I2C_WRITE(bmi_init_chip_id_mock_write_fn, 2, BMI160_CMD_REG, + BMI160_CMD_EXT_MODE_EN_B2); + MOCK_ASSERT_I2C_WRITE(bmi_init_chip_id_mock_write_fn, 3, + BMI160_CMD_EXT_MODE_ADDR, BMI160_CMD_PAGING_EN); + MOCK_ASSERT_I2C_WRITE(bmi_init_chip_id_mock_write_fn, 4, + BMI160_CMD_EXT_MODE_ADDR, 0); + + i2c_common_emul_set_write_func(common_data, NULL, NULL); +} + +static void bmi160_before(void *fixture) +{ + ARG_UNUSED(fixture); + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + struct motion_sensor_t *acc_ms; + struct motion_sensor_t *gyr_ms; + + acc_ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + gyr_ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + bmi_emul_set_reg(emul, BMI160_CHIP_ID, 0xd1); + + /* Disable rotation */ + gyr_ms->rot_standard_ref = NULL; + acc_ms->rot_standard_ref = NULL; + + zassume_equal(EC_SUCCESS, acc_ms->drv->set_data_rate(acc_ms, 50000, 0), + NULL); + zassume_equal(EC_SUCCESS, gyr_ms->drv->set_data_rate(gyr_ms, 50000, 0), + NULL); +} + +static void bmi160_after(void *state) +{ + ARG_UNUSED(state); + struct motion_sensor_t *acc_ms, *gyr_ms; + + acc_ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + gyr_ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + acc_ms->drv->set_data_rate(acc_ms, 0, 0); + gyr_ms->drv->set_data_rate(gyr_ms, 0, 0); +} + +ZTEST_SUITE(bmi160, drivers_predicate_pre_main, NULL, bmi160_before, + bmi160_after, NULL); + +/** Cause an interrupt and verify the motion_sense task handled it. */ +ZTEST_USER(bmi160_tasks, test_irq_handling) +{ + struct bmi_emul_frame f[3]; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + + f[0].type = BMI_EMUL_FRAME_ACC; + f[0].acc_x = BMI_EMUL_1G / 10; + f[0].acc_y = BMI_EMUL_1G / 20; + f[0].acc_z = -(int)BMI_EMUL_1G / 30; + f[0].next = NULL; + bmi_emul_append_frame(emul, f); + bmi_emul_set_reg(emul, BMI160_INT_STATUS_0, BMI160_FWM_INT & 0xff); + bmi_emul_set_reg(emul, BMI160_INT_STATUS_1, + (BMI160_FWM_INT >> 8) & 0xff); + + bmi160_interrupt(0); + k_sleep(K_SECONDS(10)); + + /* Verify that the motion_sense_task read it. */ + zassert_equal(bmi_emul_get_reg(emul, BMI160_INT_STATUS_0), 0, NULL); + zassert_equal(bmi_emul_get_reg(emul, BMI160_INT_STATUS_1), 0, NULL); +} + +ZTEST_SUITE(bmi160_tasks, drivers_predicate_post_main, NULL, bmi160_before, + bmi160_after, NULL); diff --git a/zephyr/test/drivers/default/src/bmi260.c b/zephyr/test/drivers/default/src/bmi260.c new file mode 100644 index 0000000000..9295d631ca --- /dev/null +++ b/zephyr/test/drivers/default/src/bmi260.c @@ -0,0 +1,2305 @@ +/* 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/fff.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "common.h" +#include "i2c.h" +#include "emul/emul_bmi.h" +#include "emul/emul_common_i2c.h" + +#include "motion_sense_fifo.h" +#include "driver/accelgyro_bmi260.h" +#include "driver/accelgyro_bmi_common.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" + +#define BMI_NODE DT_NODELABEL(accel_bmi260) +#define BMI_ACC_SENSOR_ID SENSOR_ID(DT_NODELABEL(ms_bmi260_accel)) +#define BMI_GYR_SENSOR_ID SENSOR_ID(DT_NODELABEL(ms_bmi260_gyro)) +#define BMI_INT_EVENT \ + TASK_EVENT_MOTION_SENSOR_INTERRUPT(SENSOR_ID(DT_ALIAS(bmi260_int))) + +/** How accurate comparision of vectors should be */ +#define V_EPS 8 + +/** Convert from one type of vector to another */ +#define convert_int3v_int16(v, r) \ + do { \ + r[0] = v[0]; \ + r[1] = v[1]; \ + r[2] = v[2]; \ + } while (0) + +/** Rotation used in some tests */ +static const mat33_fp_t test_rotation = { { 0, FLOAT_TO_FP(1), 0 }, + { FLOAT_TO_FP(-1), 0, 0 }, + { 0, 0, FLOAT_TO_FP(-1) } }; +/** Rotate given vector by test rotation */ +static void rotate_int3v_by_test_rotation(intv3_t v) +{ + int16_t t; + + t = v[0]; + v[0] = -v[1]; + v[1] = t; + v[2] = -v[2]; +} + +/** Set emulator accelerometer offset values to intv3_t vector */ +static void set_emul_acc_offset(const struct emul *emul, intv3_t offset) +{ + bmi_emul_set_off(emul, BMI_EMUL_ACC_X, offset[0]); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Y, offset[1]); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Z, offset[2]); +} + +/** Save emulator accelerometer offset values to intv3_t vector */ +static void get_emul_acc_offset(const struct emul *emul, intv3_t offset) +{ + offset[0] = bmi_emul_get_off(emul, BMI_EMUL_ACC_X); + offset[1] = bmi_emul_get_off(emul, BMI_EMUL_ACC_Y); + offset[2] = bmi_emul_get_off(emul, BMI_EMUL_ACC_Z); +} + +/** Set emulator accelerometer values to intv3_t vector */ +static void set_emul_acc(const struct emul *emul, intv3_t acc) +{ + bmi_emul_set_value(emul, BMI_EMUL_ACC_X, acc[0]); + bmi_emul_set_value(emul, BMI_EMUL_ACC_Y, acc[1]); + bmi_emul_set_value(emul, BMI_EMUL_ACC_Z, acc[2]); +} + +/** Set emulator gyroscope offset values to intv3_t vector */ +static void set_emul_gyr_offset(const struct emul *emul, intv3_t offset) +{ + bmi_emul_set_off(emul, BMI_EMUL_GYR_X, offset[0]); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Y, offset[1]); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Z, offset[2]); +} + +/** Save emulator gyroscope offset values to intv3_t vector */ +static void get_emul_gyr_offset(const struct emul *emul, intv3_t offset) +{ + offset[0] = bmi_emul_get_off(emul, BMI_EMUL_GYR_X); + offset[1] = bmi_emul_get_off(emul, BMI_EMUL_GYR_Y); + offset[2] = bmi_emul_get_off(emul, BMI_EMUL_GYR_Z); +} + +/** Set emulator gyroscope values to vector of three int16_t */ +static void set_emul_gyr(const struct emul *emul, intv3_t gyr) +{ + bmi_emul_set_value(emul, BMI_EMUL_GYR_X, gyr[0]); + bmi_emul_set_value(emul, BMI_EMUL_GYR_Y, gyr[1]); + bmi_emul_set_value(emul, BMI_EMUL_GYR_Z, gyr[2]); +} + +/** Convert accelerometer read to units used by emulator */ +static void drv_acc_to_emul(intv3_t drv, int range, intv3_t out) +{ + const int scale = MOTION_SCALING_FACTOR / BMI_EMUL_1G; + + out[0] = drv[0] * range / scale; + out[1] = drv[1] * range / scale; + out[2] = drv[2] * range / scale; +} + +/** Convert gyroscope read to units used by emulator */ +static void drv_gyr_to_emul(intv3_t drv, int range, intv3_t out) +{ + const int scale = MOTION_SCALING_FACTOR / BMI_EMUL_125_DEG_S; + + range /= 125; + out[0] = drv[0] * range / scale; + out[1] = drv[1] * range / scale; + out[2] = drv[2] * range / scale; +} + +/** Compare two vectors of intv3_t type */ +static void compare_int3v_f(intv3_t exp_v, intv3_t v, int eps, int line) +{ + int i; + + for (i = 0; i < 3; i++) { + zassert_within( + exp_v[i], v[i], eps, + "Expected [%d; %d; %d], got [%d; %d; %d]; line: %d", + exp_v[0], exp_v[1], exp_v[2], v[0], v[1], v[2], line); + } +} +#define compare_int3v_eps(exp_v, v, e) compare_int3v_f(exp_v, v, e, __LINE__) +#define compare_int3v(exp_v, v) compare_int3v_eps(exp_v, v, V_EPS) + +/** + * Custom emulator read function which always return INIT OK status in + * INTERNAL STATUS register. Used in init test. + */ +static int emul_init_ok(const struct emul *emul, int reg, uint8_t *val, + int byte, void *data) +{ + bmi_emul_set_reg(emul, BMI260_INTERNAL_STATUS, BMI260_INIT_OK); + + return 1; +} + +/** Init BMI260 before test */ +static void bmi_init_emul(void) +{ + struct motion_sensor_t *ms_acc; + struct motion_sensor_t *ms_gyr; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int ret; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* + * Init BMI before test. It is needed custom function to set value of + * BMI260_INTERNAL_STATUS register, because init function triggers reset + * which clears value set in this register before test. + */ + i2c_common_emul_set_read_func(common_data, emul_init_ok, NULL); + + ret = ms_acc->drv->init(ms_acc); + zassert_equal(EC_RES_SUCCESS, ret, "Got accel init error %d", ret); + + ret = ms_gyr->drv->init(ms_gyr); + zassert_equal(EC_RES_SUCCESS, ret, "Got gyro init error %d", ret); + + /* Remove custom emulator read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +/** Test get accelerometer offset with and without rotation */ +ZTEST_USER(bmi260, test_bmi_acc_get_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int16_t ret[3]; + intv3_t ret_v; + intv3_t exp_v; + int16_t temp; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Set emulator offset */ + exp_v[0] = BMI_EMUL_1G / 10; + exp_v[1] = BMI_EMUL_1G / 20; + exp_v[2] = -(int)BMI_EMUL_1G / 30; + set_emul_acc_offset(emul, exp_v); + /* BMI driver returns value in mg units */ + exp_v[0] = 1000 / 10; + exp_v[1] = 1000 / 20; + exp_v[2] = -1000 / 30; + + /* Test fail on offset read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_ACC70); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_ACC70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_ACC70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test get offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v(exp_v, ret_v); + + /* Setup rotation and rotate expected offset */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + + /* Test get offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v(exp_v, ret_v); +} + +/** Test get gyroscope offset with and without rotation */ +ZTEST_USER(bmi260, test_bmi_gyr_get_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int16_t ret[3]; + intv3_t ret_v; + intv3_t exp_v; + int16_t temp; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set emulator offset */ + exp_v[0] = BMI_EMUL_125_DEG_S / 100; + exp_v[1] = BMI_EMUL_125_DEG_S / 200; + exp_v[2] = -(int)BMI_EMUL_125_DEG_S / 300; + set_emul_gyr_offset(emul, exp_v); + /* BMI driver returns value in mdeg/s units */ + exp_v[0] = 125000 / 100; + exp_v[1] = 125000 / 200; + exp_v[2] = -125000 / 300; + + /* Test fail on offset read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_GYR70); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_GYR70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_GYR70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI160_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->get_offset(ms, ret, &temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test get offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v_eps(exp_v, ret_v, 64); + + /* Setup rotation and rotate expected offset */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + + /* Test get offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->get_offset(ms, ret, &temp), NULL); + zassert_equal(temp, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + convert_int3v_int16(ret, ret_v); + compare_int3v_eps(exp_v, ret_v, 64); +} + +/** + * Test set accelerometer offset with and without rotation. Also test behaviour + * on I2C error. + */ +ZTEST_USER(bmi260, test_bmi_acc_set_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int16_t input_v[3] = { 0, 0, 0 }; + int16_t temp = 0; + intv3_t ret_v; + intv3_t exp_v; + uint8_t nv_c; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Test fail on NV CONF register read and write */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_NV_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, BMI260_NV_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on offset write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI160_OFFSET_ACC70); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI160_OFFSET_ACC70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI160_OFFSET_ACC70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup NV_CONF register value */ + bmi_emul_set_reg(emul, BMI260_NV_CONF, 0x7); + /* Set input offset */ + exp_v[0] = BMI_EMUL_1G / 10; + exp_v[1] = BMI_EMUL_1G / 20; + exp_v[2] = -(int)BMI_EMUL_1G / 30; + /* BMI driver accept value in mg units */ + input_v[0] = 1000 / 10; + input_v[1] = 1000 / 20; + input_v[2] = -1000 / 30; + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test set offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_acc_offset(emul, ret_v); + /* + * Depending on used range, accelerometer values may be up to 6 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_v, ret_v, 64); + nv_c = bmi_emul_get_reg(emul, BMI260_NV_CONF); + /* Only ACC_OFFSET_EN bit should be changed */ + zassert_equal(0x7 | BMI260_ACC_OFFSET_EN, nv_c, + "Expected 0x%x, got 0x%x", 0x7 | BMI260_ACC_OFFSET_EN, + nv_c); + + /* Setup NV_CONF register value */ + bmi_emul_set_reg(emul, BMI260_NV_CONF, 0); + /* Setup rotation and rotate input for set_offset function */ + ms->rot_standard_ref = &test_rotation; + convert_int3v_int16(input_v, ret_v); + rotate_int3v_by_test_rotation(ret_v); + convert_int3v_int16(ret_v, input_v); + + /* Test set offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_acc_offset(emul, ret_v); + compare_int3v_eps(exp_v, ret_v, 64); + nv_c = bmi_emul_get_reg(emul, BMI260_NV_CONF); + /* Only ACC_OFFSET_EN bit should be changed */ + zassert_equal(BMI260_ACC_OFFSET_EN, nv_c, "Expected 0x%x, got 0x%x", + BMI260_ACC_OFFSET_EN, nv_c); +} + +/** + * Test set gyroscope offset with and without rotation. Also test behaviour + * on I2C error. + */ +ZTEST_USER(bmi260, test_bmi_gyr_set_offset) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int16_t input_v[3]; + int16_t temp = 0; + intv3_t ret_v; + intv3_t exp_v; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Test fail on OFFSET EN GYR98 register read and write */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, BMI260_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on offset write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_OFFSET_GYR70); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI260_OFFSET_GYR70 + 1); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + BMI260_OFFSET_GYR70 + 2); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_offset(ms, input_v, temp), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set input offset */ + exp_v[0] = BMI_EMUL_125_DEG_S / 100; + exp_v[1] = BMI_EMUL_125_DEG_S / 200; + exp_v[2] = -(int)BMI_EMUL_125_DEG_S / 300; + /* BMI driver accept value in mdeg/s units */ + input_v[0] = 125000 / 100; + input_v[1] = 125000 / 200; + input_v[2] = -125000 / 300; + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Test set offset without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_gyr_offset(emul, ret_v); + /* + * Depending on used range, gyroscope values may be up to 4 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_v, ret_v, 32); + /* Gyroscope offset should be enabled */ + zassert_true(bmi_emul_get_reg(emul, BMI260_OFFSET_EN_GYR98) & + BMI260_OFFSET_GYRO_EN, + NULL); + + /* Setup rotation and rotate input for set_offset function */ + ms->rot_standard_ref = &test_rotation; + convert_int3v_int16(input_v, ret_v); + rotate_int3v_by_test_rotation(ret_v); + convert_int3v_int16(ret_v, input_v); + + /* Test set offset with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->set_offset(ms, input_v, temp), NULL); + get_emul_gyr_offset(emul, ret_v); + compare_int3v_eps(exp_v, ret_v, 32); + zassert_true(bmi_emul_get_reg(emul, BMI260_OFFSET_EN_GYR98) & + BMI260_OFFSET_GYRO_EN, + NULL); +} + +/** + * Try to set accelerometer range and check if expected range was set + * in driver and in emulator. + */ +static void check_set_acc_range_f(const struct emul *emul, + struct motion_sensor_t *ms, int range, + int rnd, int exp_range, int line) +{ + uint8_t exp_range_reg; + uint8_t range_reg; + + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, rnd), + "set_range failed; line: %d", line); + zassert_equal(exp_range, ms->current_range, + "Expected range %d, got %d; line %d", exp_range, + ms->current_range, line); + range_reg = bmi_emul_get_reg(emul, BMI260_ACC_RANGE); + + switch (exp_range) { + case 2: + exp_range_reg = BMI260_GSEL_2G; + break; + case 4: + exp_range_reg = BMI260_GSEL_4G; + break; + case 8: + exp_range_reg = BMI260_GSEL_8G; + break; + case 16: + exp_range_reg = BMI260_GSEL_16G; + break; + default: + /* Unknown expected range */ + zassert_unreachable( + "Expected range %d not supported by device; line %d", + exp_range, line); + return; + } + + zassert_equal(exp_range_reg, range_reg, + "Expected range reg 0x%x, got 0x%x; line %d", + exp_range_reg, range_reg, line); +} +#define check_set_acc_range(emul, ms, range, rnd, exp_range) \ + check_set_acc_range_f(emul, ms, range, rnd, exp_range, __LINE__) + +/** Test set accelerometer range with and without I2C errors */ +ZTEST_USER(bmi260, test_bmi_acc_set_range) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int start_range; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Setup starting range, shouldn't be changed on error */ + start_range = 2; + ms->current_range = start_range; + bmi_emul_set_reg(emul, BMI260_ACC_RANGE, BMI260_GSEL_2G); + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_ACC_RANGE); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 12, 0), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI260_GSEL_2G, bmi_emul_get_reg(emul, BMI260_ACC_RANGE), + NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 12, 1), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI260_GSEL_2G, bmi_emul_get_reg(emul, BMI260_ACC_RANGE), + NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting range with rounding down */ + check_set_acc_range(emul, ms, 1, 0, 2); + check_set_acc_range(emul, ms, 2, 0, 2); + check_set_acc_range(emul, ms, 3, 0, 2); + check_set_acc_range(emul, ms, 4, 0, 4); + check_set_acc_range(emul, ms, 5, 0, 4); + check_set_acc_range(emul, ms, 6, 0, 4); + check_set_acc_range(emul, ms, 7, 0, 4); + check_set_acc_range(emul, ms, 8, 0, 8); + check_set_acc_range(emul, ms, 9, 0, 8); + check_set_acc_range(emul, ms, 15, 0, 8); + check_set_acc_range(emul, ms, 16, 0, 16); + check_set_acc_range(emul, ms, 17, 0, 16); + + /* Test setting range with rounding up */ + check_set_acc_range(emul, ms, 1, 1, 2); + check_set_acc_range(emul, ms, 2, 1, 2); + check_set_acc_range(emul, ms, 3, 1, 4); + check_set_acc_range(emul, ms, 4, 1, 4); + check_set_acc_range(emul, ms, 5, 1, 8); + check_set_acc_range(emul, ms, 6, 1, 8); + check_set_acc_range(emul, ms, 7, 1, 8); + check_set_acc_range(emul, ms, 8, 1, 8); + check_set_acc_range(emul, ms, 9, 1, 16); + check_set_acc_range(emul, ms, 15, 1, 16); + check_set_acc_range(emul, ms, 16, 1, 16); + check_set_acc_range(emul, ms, 17, 1, 16); +} + +/** + * Try to set gyroscope range and check if expected range was set in driver and + * in emulator. + */ +static void check_set_gyr_range_f(const struct emul *emul, + struct motion_sensor_t *ms, int range, + int rnd, int exp_range, int line) +{ + uint8_t exp_range_reg; + uint8_t range_reg; + + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, rnd), + "set_range failed; line: %d", line); + zassert_equal(exp_range, ms->current_range, + "Expected range %d, got %d; line %d", exp_range, + ms->current_range, line); + range_reg = bmi_emul_get_reg(emul, BMI260_GYR_RANGE); + + switch (exp_range) { + case 125: + exp_range_reg = BMI260_DPS_SEL_125; + break; + case 250: + exp_range_reg = BMI260_DPS_SEL_250; + break; + case 500: + exp_range_reg = BMI260_DPS_SEL_500; + break; + case 1000: + exp_range_reg = BMI260_DPS_SEL_1000; + break; + case 2000: + exp_range_reg = BMI260_DPS_SEL_2000; + break; + default: + /* Unknown expected range */ + zassert_unreachable( + "Expected range %d not supported by device; line %d", + exp_range, line); + return; + } + + zassert_equal(exp_range_reg, range_reg, + "Expected range reg 0x%x, got 0x%x; line %d", + exp_range_reg, range_reg, line); +} +#define check_set_gyr_range(emul, ms, range, rnd, exp_range) \ + check_set_gyr_range_f(emul, ms, range, rnd, exp_range, __LINE__) + +/** Test set gyroscope range with and without I2C errors */ +ZTEST_USER(bmi260, test_bmi_gyr_set_range) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int start_range; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Setup starting range, shouldn't be changed on error */ + start_range = 250; + ms->current_range = start_range; + bmi_emul_set_reg(emul, BMI260_GYR_RANGE, BMI260_DPS_SEL_250); + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_GYR_RANGE); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 125, 0), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI260_DPS_SEL_250, + bmi_emul_get_reg(emul, BMI260_GYR_RANGE), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_range(ms, 125, 1), NULL); + zassert_equal(start_range, ms->current_range, NULL); + zassert_equal(BMI260_DPS_SEL_250, + bmi_emul_get_reg(emul, BMI260_GYR_RANGE), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting range with rounding down */ + check_set_gyr_range(emul, ms, 1, 0, 125); + check_set_gyr_range(emul, ms, 124, 0, 125); + check_set_gyr_range(emul, ms, 125, 0, 125); + check_set_gyr_range(emul, ms, 126, 0, 125); + check_set_gyr_range(emul, ms, 249, 0, 125); + check_set_gyr_range(emul, ms, 250, 0, 250); + check_set_gyr_range(emul, ms, 251, 0, 250); + check_set_gyr_range(emul, ms, 499, 0, 250); + check_set_gyr_range(emul, ms, 500, 0, 500); + check_set_gyr_range(emul, ms, 501, 0, 500); + check_set_gyr_range(emul, ms, 999, 0, 500); + check_set_gyr_range(emul, ms, 1000, 0, 1000); + check_set_gyr_range(emul, ms, 1001, 0, 1000); + check_set_gyr_range(emul, ms, 1999, 0, 1000); + check_set_gyr_range(emul, ms, 2000, 0, 2000); + check_set_gyr_range(emul, ms, 2001, 0, 2000); + + /* Test setting range with rounding up */ + check_set_gyr_range(emul, ms, 1, 1, 125); + check_set_gyr_range(emul, ms, 124, 1, 125); + check_set_gyr_range(emul, ms, 125, 1, 125); + check_set_gyr_range(emul, ms, 126, 1, 250); + check_set_gyr_range(emul, ms, 249, 1, 250); + check_set_gyr_range(emul, ms, 250, 1, 250); + check_set_gyr_range(emul, ms, 251, 1, 500); + check_set_gyr_range(emul, ms, 499, 1, 500); + check_set_gyr_range(emul, ms, 500, 1, 500); + check_set_gyr_range(emul, ms, 501, 1, 1000); + check_set_gyr_range(emul, ms, 999, 1, 1000); + check_set_gyr_range(emul, ms, 1000, 1, 1000); + check_set_gyr_range(emul, ms, 1001, 1, 2000); + check_set_gyr_range(emul, ms, 1999, 1, 2000); + check_set_gyr_range(emul, ms, 2000, 1, 2000); + check_set_gyr_range(emul, ms, 2001, 1, 2000); +} + +/** Test get resolution of acclerometer and gyroscope sensor */ +ZTEST_USER(bmi260, test_bmi_get_resolution) +{ + struct motion_sensor_t *ms; + + /* Test accelerometer */ + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Resolution should be always 16 bits */ + zassert_equal(16, ms->drv->get_resolution(ms), NULL); + + /* Test gyroscope */ + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Resolution should be always 16 bits */ + zassert_equal(16, ms->drv->get_resolution(ms), NULL); +} + +/** + * Try to set accelerometer data rate and check if expected rate was set + * in driver and in emulator. + */ +static void check_set_acc_rate_f(const struct emul *emul, + struct motion_sensor_t *ms, int rate, int rnd, + int exp_rate, int line) +{ + uint8_t exp_rate_reg; + uint8_t rate_reg; + int drv_rate; + + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, rnd), + "set_data_rate failed; line: %d", line); + drv_rate = ms->drv->get_data_rate(ms); + zassert_equal(exp_rate, drv_rate, "Expected rate %d, got %d; line %d", + exp_rate, drv_rate, line); + rate_reg = bmi_emul_get_reg(emul, BMI260_ACC_CONF); + rate_reg &= BMI_ODR_MASK; + + switch (exp_rate) { + case 12500: + exp_rate_reg = 0x5; + break; + case 25000: + exp_rate_reg = 0x6; + break; + case 50000: + exp_rate_reg = 0x7; + break; + case 100000: + exp_rate_reg = 0x8; + break; + case 200000: + exp_rate_reg = 0x9; + break; + case 400000: + exp_rate_reg = 0xa; + break; + case 800000: + exp_rate_reg = 0xb; + break; + case 1600000: + exp_rate_reg = 0xc; + break; + default: + /* Unknown expected rate */ + zassert_unreachable( + "Expected rate %d not supported by device; line %d", + exp_rate, line); + return; + } + + zassert_equal(exp_rate_reg, rate_reg, + "Expected rate reg 0x%x, got 0x%x; line %d", exp_rate_reg, + rate_reg, line); +} +#define check_set_acc_rate(emul, ms, rate, rnd, exp_rate) \ + check_set_acc_rate_f(emul, ms, rate, rnd, exp_rate, __LINE__) + +/** Test set and get accelerometer rate with and without I2C errors */ +ZTEST_USER(bmi260, test_bmi_acc_rate) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + uint8_t reg_rate; + uint8_t pwr_ctrl; + int drv_rate; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Test setting rate with rounding down */ + check_set_acc_rate(emul, ms, 12500, 0, 12500); + check_set_acc_rate(emul, ms, 12501, 0, 12500); + check_set_acc_rate(emul, ms, 24999, 0, 12500); + check_set_acc_rate(emul, ms, 25000, 0, 25000); + check_set_acc_rate(emul, ms, 25001, 0, 25000); + check_set_acc_rate(emul, ms, 49999, 0, 25000); + check_set_acc_rate(emul, ms, 50000, 0, 50000); + check_set_acc_rate(emul, ms, 50001, 0, 50000); + check_set_acc_rate(emul, ms, 99999, 0, 50000); + check_set_acc_rate(emul, ms, 100000, 0, 100000); + check_set_acc_rate(emul, ms, 100001, 0, 100000); + check_set_acc_rate(emul, ms, 199999, 0, 100000); + check_set_acc_rate(emul, ms, 200000, 0, 200000); + check_set_acc_rate(emul, ms, 200001, 0, 200000); + check_set_acc_rate(emul, ms, 399999, 0, 200000); + /* + * We cannot test frequencies from 400000 to 1600000 because + * CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ is set to 250000 + */ + + /* Test setting rate with rounding up */ + check_set_acc_rate(emul, ms, 6251, 1, 12500); + check_set_acc_rate(emul, ms, 12499, 1, 12500); + check_set_acc_rate(emul, ms, 12500, 1, 12500); + check_set_acc_rate(emul, ms, 12501, 1, 25000); + check_set_acc_rate(emul, ms, 24999, 1, 25000); + check_set_acc_rate(emul, ms, 25000, 1, 25000); + check_set_acc_rate(emul, ms, 25001, 1, 50000); + check_set_acc_rate(emul, ms, 49999, 1, 50000); + check_set_acc_rate(emul, ms, 50000, 1, 50000); + check_set_acc_rate(emul, ms, 50001, 1, 100000); + check_set_acc_rate(emul, ms, 99999, 1, 100000); + check_set_acc_rate(emul, ms, 100000, 1, 100000); + check_set_acc_rate(emul, ms, 100001, 1, 200000); + check_set_acc_rate(emul, ms, 199999, 1, 200000); + check_set_acc_rate(emul, ms, 200000, 1, 200000); + + /* Test out of range rate with rounding down */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 0), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 12499, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 2000000, 0), NULL); + + /* Test out of range rate with rounding up */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 1), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 6250, 1), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 200001, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 2000000, 1), NULL); + + /* Current rate shouldn't be changed on error */ + drv_rate = ms->drv->get_data_rate(ms); + reg_rate = bmi_emul_get_reg(emul, BMI260_ACC_CONF); + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_CONF); + + /* Test fail on read */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_ACC_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_ACC_CONF), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_ACC_CONF); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_ACC_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_ACC_CONF), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test disabling sensor */ + bmi_emul_set_reg(emul, BMI260_PWR_CTRL, + BMI260_AUX_EN | BMI260_GYR_EN | BMI260_ACC_EN); + bmi_emul_set_reg(emul, BMI260_ACC_CONF, BMI260_FILTER_PERF); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 0, 0), NULL); + + pwr_ctrl = bmi_emul_get_reg(emul, BMI260_PWR_CTRL); + reg_rate = bmi_emul_get_reg(emul, BMI260_ACC_CONF); + zassert_equal(BMI260_AUX_EN | BMI260_GYR_EN, pwr_ctrl, NULL); + zassert_true(!(reg_rate & BMI260_FILTER_PERF), NULL); + + /* Test enabling sensor */ + bmi_emul_set_reg(emul, BMI260_PWR_CTRL, 0); + bmi_emul_set_reg(emul, BMI260_ACC_CONF, 0); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 50000, 0), NULL); + + pwr_ctrl = bmi_emul_get_reg(emul, BMI260_PWR_CTRL); + reg_rate = bmi_emul_get_reg(emul, BMI260_ACC_CONF); + zassert_equal(BMI260_ACC_EN, pwr_ctrl, NULL); + zassert_true(reg_rate & BMI260_FILTER_PERF, NULL); + + /* Test disabling sensor (by setting rate to 0) but failing. */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_PWR_CTRL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 0, 0), + "Did not properly handle failed power down."); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test enabling sensor but failing. (after first disabling it) */ + ms->drv->set_data_rate(ms, 0, 0); + + i2c_common_emul_set_write_fail_reg(common_data, BMI260_PWR_CTRL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + "Did not properly handle failed power up."); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +/** + * Try to set gyroscope data rate and check if expected rate was set + * in driver and in emulator. + */ +static void check_set_gyr_rate_f(const struct emul *emul, + struct motion_sensor_t *ms, int rate, int rnd, + int exp_rate, int line) +{ + uint8_t exp_rate_reg; + uint8_t rate_reg; + int drv_rate; + + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, rnd), + "set_data_rate failed; line: %d", line); + drv_rate = ms->drv->get_data_rate(ms); + zassert_equal(exp_rate, drv_rate, "Expected rate %d, got %d; line %d", + exp_rate, drv_rate, line); + rate_reg = bmi_emul_get_reg(emul, BMI260_GYR_CONF); + rate_reg &= BMI_ODR_MASK; + + switch (exp_rate) { + case 25000: + exp_rate_reg = 0x6; + break; + case 50000: + exp_rate_reg = 0x7; + break; + case 100000: + exp_rate_reg = 0x8; + break; + case 200000: + exp_rate_reg = 0x9; + break; + case 400000: + exp_rate_reg = 0xa; + break; + case 800000: + exp_rate_reg = 0xb; + break; + case 1600000: + exp_rate_reg = 0xc; + break; + case 3200000: + exp_rate_reg = 0xc; + break; + default: + /* Unknown expected rate */ + zassert_unreachable( + "Expected rate %d not supported by device; line %d", + exp_rate, line); + return; + } + + zassert_equal(exp_rate_reg, rate_reg, + "Expected rate reg 0x%x, got 0x%x; line %d", exp_rate_reg, + rate_reg, line); +} +#define check_set_gyr_rate(emul, ms, rate, rnd, exp_rate) \ + check_set_gyr_rate_f(emul, ms, rate, rnd, exp_rate, __LINE__) + +/** Test set and get gyroscope rate with and without I2C errors */ +ZTEST_USER(bmi260, test_bmi_gyr_rate) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + uint8_t reg_rate; + uint8_t pwr_ctrl; + int drv_rate; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Test setting rate with rounding down */ + check_set_gyr_rate(emul, ms, 25000, 0, 25000); + check_set_gyr_rate(emul, ms, 25001, 0, 25000); + check_set_gyr_rate(emul, ms, 49999, 0, 25000); + check_set_gyr_rate(emul, ms, 50000, 0, 50000); + check_set_gyr_rate(emul, ms, 50001, 0, 50000); + check_set_gyr_rate(emul, ms, 99999, 0, 50000); + check_set_gyr_rate(emul, ms, 100000, 0, 100000); + check_set_gyr_rate(emul, ms, 100001, 0, 100000); + check_set_gyr_rate(emul, ms, 199999, 0, 100000); + check_set_gyr_rate(emul, ms, 200000, 0, 200000); + check_set_gyr_rate(emul, ms, 200001, 0, 200000); + check_set_gyr_rate(emul, ms, 399999, 0, 200000); + /* + * We cannot test frequencies from 400000 to 3200000 because + * CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ is set to 250000 + */ + + /* Test setting rate with rounding up */ + check_set_gyr_rate(emul, ms, 12501, 1, 25000); + check_set_gyr_rate(emul, ms, 24999, 1, 25000); + check_set_gyr_rate(emul, ms, 25000, 1, 25000); + check_set_gyr_rate(emul, ms, 25001, 1, 50000); + check_set_gyr_rate(emul, ms, 49999, 1, 50000); + check_set_gyr_rate(emul, ms, 50000, 1, 50000); + check_set_gyr_rate(emul, ms, 50001, 1, 100000); + check_set_gyr_rate(emul, ms, 99999, 1, 100000); + check_set_gyr_rate(emul, ms, 100000, 1, 100000); + check_set_gyr_rate(emul, ms, 100001, 1, 200000); + check_set_gyr_rate(emul, ms, 199999, 1, 200000); + check_set_gyr_rate(emul, ms, 200000, 1, 200000); + + /* Test out of range rate with rounding down */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 0), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 24999, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 0), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 4000000, 0), NULL); + + /* Test out of range rate with rounding up */ + zassert_equal(EC_RES_INVALID_PARAM, ms->drv->set_data_rate(ms, 1, 1), + NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 12499, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 200001, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 400000, 1), NULL); + zassert_equal(EC_RES_INVALID_PARAM, + ms->drv->set_data_rate(ms, 4000000, 1), NULL); + + /* Current rate shouldn't be changed on error */ + drv_rate = ms->drv->get_data_rate(ms); + reg_rate = bmi_emul_get_reg(emul, BMI260_GYR_CONF); + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_CONF); + + /* Test fail on read */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_GYR_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_GYR_CONF), NULL); + + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup emulator fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_GYR_CONF); + + /* Test fail on write */ + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 0), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_GYR_CONF), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 50000, 1), + NULL); + zassert_equal(drv_rate, ms->drv->get_data_rate(ms), NULL); + zassert_equal(reg_rate, bmi_emul_get_reg(emul, BMI260_GYR_CONF), NULL); + + /* Do not fail on write */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test disabling sensor */ + bmi_emul_set_reg(emul, BMI260_PWR_CTRL, + BMI260_AUX_EN | BMI260_GYR_EN | BMI260_ACC_EN); + bmi_emul_set_reg(emul, BMI260_GYR_CONF, + BMI260_FILTER_PERF | BMI260_GYR_NOISE_PERF); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 0, 0), NULL); + + pwr_ctrl = bmi_emul_get_reg(emul, BMI260_PWR_CTRL); + reg_rate = bmi_emul_get_reg(emul, BMI260_GYR_CONF); + zassert_equal(BMI260_AUX_EN | BMI260_ACC_EN, pwr_ctrl, NULL); + zassert_true(!(reg_rate & (BMI260_FILTER_PERF | BMI260_GYR_NOISE_PERF)), + NULL); + + /* Test enabling sensor */ + bmi_emul_set_reg(emul, BMI260_PWR_CTRL, 0); + bmi_emul_set_reg(emul, BMI260_GYR_CONF, 0); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 50000, 0), NULL); + + pwr_ctrl = bmi_emul_get_reg(emul, BMI260_PWR_CTRL); + reg_rate = bmi_emul_get_reg(emul, BMI260_GYR_CONF); + zassert_equal(BMI260_GYR_EN, pwr_ctrl, NULL); + zassert_true(reg_rate & (BMI260_FILTER_PERF | BMI260_GYR_NOISE_PERF), + NULL); +} + +/** + * Test setting and getting scale in accelerometer and gyroscope sensors. + * Correct appling scale to results is checked in "read" test. + */ +ZTEST_USER(bmi260, test_bmi_scale) +{ + struct motion_sensor_t *ms; + int16_t ret_scale[3]; + int16_t exp_scale[3] = { 100, 231, 421 }; + int16_t t; + + /* Test accelerometer */ + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, exp_scale, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->get_scale(ms, ret_scale, &t), NULL); + + zassert_equal(t, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + zassert_equal(exp_scale[0], ret_scale[0], NULL); + zassert_equal(exp_scale[1], ret_scale[1], NULL); + zassert_equal(exp_scale[2], ret_scale[2], NULL); + + /* Test gyroscope */ + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, exp_scale, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->get_scale(ms, ret_scale, &t), NULL); + + zassert_equal(t, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, NULL); + zassert_equal(exp_scale[0], ret_scale[0], NULL); + zassert_equal(exp_scale[1], ret_scale[1], NULL); + zassert_equal(exp_scale[2], ret_scale[2], NULL); +} + +/** Test reading temperature using accelerometer and gyroscope sensors */ +ZTEST_USER(bmi260, test_bmi_read_temp) +{ + struct motion_sensor_t *ms_acc, *ms_gyr; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int ret_temp; + int exp_temp; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Setup emulator fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_TEMPERATURE_0); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_acc->drv->read_temp(ms_acc, &ret_temp), NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_gyr->drv->read_temp(ms_gyr, &ret_temp), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_TEMPERATURE_1); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_acc->drv->read_temp(ms_acc, &ret_temp), NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_gyr->drv->read_temp(ms_gyr, &ret_temp), NULL); + /* Do not fail on read */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Fail on invalid temperature */ + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_0, 0x00); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_1, 0x80); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_acc->drv->read_temp(ms_acc, &ret_temp), NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + ms_gyr->drv->read_temp(ms_gyr, &ret_temp), NULL); + + /* + * Test correct values. Both motion sensors should return the same + * temperature. + */ + exp_temp = C_TO_K(23); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_0, 0x00); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_1, 0x00); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + + exp_temp = C_TO_K(87); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_0, 0xff); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_1, 0x7f); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + + exp_temp = C_TO_K(-41); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_0, 0x01); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_1, 0x80); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + + exp_temp = C_TO_K(47); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_0, 0x00); + bmi_emul_set_reg(emul, BMI260_TEMPERATURE_1, 0x30); + zassert_equal(EC_SUCCESS, ms_acc->drv->read_temp(ms_acc, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->read_temp(ms_gyr, &ret_temp), + NULL); + zassert_equal(exp_temp, ret_temp, NULL); +} + +/** Test reading accelerometer sensor data */ +ZTEST_USER(bmi260, test_bmi_acc_read) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + intv3_t ret_v; + intv3_t exp_v; + int16_t scale[3] = { MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE }; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Set offset 0 to simplify test */ + bmi_emul_set_off(emul, BMI_EMUL_ACC_X, 0); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Y, 0); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Z, 0); + + /* Fail on read status */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* When not ready, driver should return saved raw value */ + exp_v[0] = 100; + exp_v[1] = 200; + exp_v[2] = 300; + ms->raw_xyz[0] = exp_v[0]; + ms->raw_xyz[1] = exp_v[1]; + ms->raw_xyz[2] = exp_v[2]; + + /* Status not ready */ + bmi_emul_set_reg(emul, BMI260_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status only GYR ready */ + bmi_emul_set_reg(emul, BMI260_STATUS, BMI260_DRDY_GYR); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status ACC ready */ + bmi_emul_set_reg(emul, BMI260_STATUS, BMI260_DRDY_ACC); + + /* Set input accelerometer values */ + exp_v[0] = BMI_EMUL_1G / 10; + exp_v[1] = BMI_EMUL_1G / 20; + exp_v[2] = -(int)BMI_EMUL_1G / 30; + set_emul_acc(emul, exp_v); + /* Disable rotation */ + ms->rot_standard_ref = NULL; + /* Set scale */ + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, scale, 0), NULL); + /* Set range to 2G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 2, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 2, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 4G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 4, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 4, ret_v); + compare_int3v(exp_v, ret_v); + + /* Setup rotation and rotate expected vector */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + /* Set range to 2G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 2, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 2, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 4G */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 4, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_acc_to_emul(ret_v, 4, ret_v); + compare_int3v(exp_v, ret_v); + + /* Fail on read of data registers */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_X_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_X_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_Y_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_Y_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_Z_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_Z_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + ms->rot_standard_ref = NULL; +} + +/** Test reading gyroscope sensor data */ +ZTEST_USER(bmi260, test_bmi_gyr_read) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + intv3_t ret_v; + intv3_t exp_v; + int16_t scale[3] = { MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE }; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Set offset 0 to simplify test */ + bmi_emul_set_off(emul, BMI_EMUL_GYR_X, 0); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Y, 0); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Z, 0); + + /* Fail on read status */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* When not ready, driver should return saved raw value */ + exp_v[0] = 100; + exp_v[1] = 200; + exp_v[2] = 300; + ms->raw_xyz[0] = exp_v[0]; + ms->raw_xyz[1] = exp_v[1]; + ms->raw_xyz[2] = exp_v[2]; + + /* Status not ready */ + bmi_emul_set_reg(emul, BMI260_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status only ACC ready */ + bmi_emul_set_reg(emul, BMI260_STATUS, BMI260_DRDY_ACC); + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + compare_int3v(exp_v, ret_v); + + /* Status GYR ready */ + bmi_emul_set_reg(emul, BMI260_STATUS, BMI260_DRDY_GYR); + + /* Set input accelerometer values */ + exp_v[0] = BMI_EMUL_125_DEG_S / 10; + exp_v[1] = BMI_EMUL_125_DEG_S / 20; + exp_v[2] = -(int)BMI_EMUL_125_DEG_S / 30; + set_emul_gyr(emul, exp_v); + /* Disable rotation */ + ms->rot_standard_ref = NULL; + /* Set scale */ + zassert_equal(EC_SUCCESS, ms->drv->set_scale(ms, scale, 0), NULL); + /* Set range to 125°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 125, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 125, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 1000°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 1000, 0), NULL); + + /* Test read without rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 1000, ret_v); + compare_int3v(exp_v, ret_v); + + /* Setup rotation and rotate expected vector */ + ms->rot_standard_ref = &test_rotation; + rotate_int3v_by_test_rotation(exp_v); + /* Set range to 125°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 125, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 125, ret_v); + compare_int3v(exp_v, ret_v); + + /* Set range to 1000°/s */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 1000, 0), NULL); + + /* Test read with rotation */ + zassert_equal(EC_SUCCESS, ms->drv->read(ms, ret_v), NULL); + drv_gyr_to_emul(ret_v, 1000, ret_v); + compare_int3v(exp_v, ret_v); + + /* Fail on read of data registers */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_X_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_X_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_Y_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_Y_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_Z_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_Z_H_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, ret_v), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + ms->rot_standard_ref = NULL; +} + +/** Test accelerometer calibration */ +ZTEST_USER(bmi260, test_bmi_acc_perform_calib) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + intv3_t start_off; + intv3_t exp_off; + intv3_t ret_off; + int range; + int rate; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + + bmi_init_emul(); + + /* Disable rotation */ + ms->rot_standard_ref = NULL; + + /* Range and rate cannot change after calibration */ + range = 4; + rate = 50000; + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, 0), NULL); + + /* Set offset 0 */ + start_off[0] = 0; + start_off[1] = 0; + start_off[2] = 0; + set_emul_acc_offset(emul, start_off); + + /* Set input accelerometer values */ + exp_off[0] = BMI_EMUL_1G / 10; + exp_off[1] = BMI_EMUL_1G / 20; + exp_off[2] = BMI_EMUL_1G - (int)BMI_EMUL_1G / 30; + set_emul_acc(emul, exp_off); + + /* Expected offset is [-X, -Y, 1G - Z] */ + exp_off[0] = -exp_off[0]; + exp_off[1] = -exp_off[1]; + exp_off[2] = BMI_EMUL_1G - exp_off[2]; + + /* Test success on disabling calibration */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 0), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on rate read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on status read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on data not ready */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + bmi_emul_set_reg(emul, BMI260_STATUS, 0); + zassert_equal(EC_ERROR_TIMEOUT, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Setup data status ready for rest of the test */ + bmi_emul_set_reg(emul, BMI260_STATUS, BMI260_DRDY_ACC); + + /* Test fail on data read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_ACC_X_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on setting offset */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_NV_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test successful offset compenastion */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + get_emul_acc_offset(emul, ret_off); + /* + * Depending on used range, accelerometer values may be up to 6 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_off, ret_off, 64); +} + +/** Test gyroscope calibration */ +ZTEST_USER(bmi260, test_bmi_gyr_perform_calib) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + intv3_t start_off; + intv3_t exp_off; + intv3_t ret_off; + int range; + int rate; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + bmi_init_emul(); + + /* Range and rate cannot change after calibration */ + range = 125; + rate = 50000; + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, range, 0), NULL); + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, rate, 0), NULL); + + /* Set offset 0 */ + start_off[0] = 0; + start_off[1] = 0; + start_off[2] = 0; + set_emul_gyr_offset(emul, start_off); + + /* Set input accelerometer values */ + exp_off[0] = BMI_EMUL_125_DEG_S / 100; + exp_off[1] = BMI_EMUL_125_DEG_S / 200; + exp_off[2] = -(int)BMI_EMUL_125_DEG_S / 300; + set_emul_gyr(emul, exp_off); + + /* Expected offset is [-X, -Y, -Z] */ + exp_off[0] = -exp_off[0]; + exp_off[1] = -exp_off[1]; + exp_off[2] = -exp_off[2]; + + /* Test success on disabling calibration */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 0), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on rate read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_CONF); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on status read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on data not ready */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + bmi_emul_set_reg(emul, BMI260_STATUS, 0); + zassert_equal(EC_ERROR_TIMEOUT, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* + * Setup data status ready for rest of the test. Gyroscope calibration + * should check DRDY_GYR bit, but current driver check only for ACC. + */ + bmi_emul_set_reg(emul, BMI260_STATUS, + BMI260_DRDY_ACC | BMI260_DRDY_GYR); + + /* Test fail on data read */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_GYR_X_L_G); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + /* Test fail on setting offset */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_OFFSET_EN_GYR98); + zassert_equal(EC_ERROR_INVAL, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test successful offset compenastion */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(range, ms->current_range, NULL); + zassert_equal(rate, ms->drv->get_data_rate(ms), NULL); + get_emul_gyr_offset(emul, ret_off); + /* + * Depending on used range, gyroscope values may be up to 4 bits + * more accurate then offset value resolution. + */ + compare_int3v_eps(exp_off, ret_off, 32); +} + +/** + * A custom fake to use with the `init_rom_map` mock that returns the + * value of `addr` + */ +static const void *init_rom_map_addr_passthru(const void *addr, int size) +{ + return addr; +} + +/** Test init function of BMI260 accelerometer and gyroscope sensors */ +ZTEST_USER(bmi260, test_bmi_init) +{ + struct motion_sensor_t *ms_acc, *ms_gyr; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* The mock should return whatever is passed in to its addr param */ + RESET_FAKE(init_rom_map); + init_rom_map_fake.custom_fake = init_rom_map_addr_passthru; + + bmi_init_emul(); +} + +/** Data for custom emulator read function used in FIFO test */ +struct fifo_func_data { + uint16_t interrupts; +}; + +/** + * Custom emulator read function used in FIFO test. It sets interrupt registers + * to value passed as additional data. It sets interrupt registers to 0 after + * access. + */ +static int emul_fifo_func(const struct emul *emul, int reg, uint8_t *val, + int byte, void *data) +{ + struct fifo_func_data *d = data; + + if (reg + byte == BMI260_INT_STATUS_0) { + bmi_emul_set_reg(emul, BMI260_INT_STATUS_0, + d->interrupts & 0xff); + d->interrupts &= 0xff00; + } else if (reg + byte == BMI260_INT_STATUS_1) { + bmi_emul_set_reg(emul, BMI260_INT_STATUS_1, + (d->interrupts >> 8) & 0xff); + d->interrupts &= 0xff; + } + + return 1; +} + +/** + * Run irq handler on accelerometer sensor and check if committed data in FIFO + * match what was set in FIFO frames in emulator. + */ +static void check_fifo_f(struct motion_sensor_t *ms_acc, + struct motion_sensor_t *ms_gyr, + struct bmi_emul_frame *frame, int acc_range, + int gyr_range, int line) +{ + struct ec_response_motion_sensor_data vector; + struct bmi_emul_frame *f_acc, *f_gyr; + uint32_t event = BMI_INT_EVENT; + uint16_t size; + intv3_t exp_v; + intv3_t ret_v; + + /* Find first frame of acc and gyr type */ + f_acc = frame; + while (f_acc != NULL && !(f_acc->type & BMI_EMUL_FRAME_ACC)) { + f_acc = f_acc->next; + } + + f_gyr = frame; + while (f_gyr != NULL && !(f_gyr->type & BMI_EMUL_FRAME_GYR)) { + f_gyr = f_gyr->next; + } + + /* Read FIFO in driver */ + zassert_equal(EC_SUCCESS, ms_acc->drv->irq_handler(ms_acc, &event), + "Failed to read FIFO in irq handler, line %d", line); + + /* Read all data committed to FIFO */ + while (motion_sense_fifo_read(sizeof(vector), 1, &vector, &size)) { + /* Ignore timestamp frames */ + if (vector.flags == MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) { + continue; + } + + /* Check acclerometer frames */ + if (ms_acc - motion_sensors == vector.sensor_num) { + if (f_acc == NULL) { + zassert_unreachable( + "Not expected acclerometer data in FIFO, line %d", + line); + } + + convert_int3v_int16(vector.data, ret_v); + drv_acc_to_emul(ret_v, acc_range, ret_v); + exp_v[0] = f_acc->acc_x; + exp_v[1] = f_acc->acc_y; + exp_v[2] = f_acc->acc_z; + compare_int3v_f(exp_v, ret_v, V_EPS, line); + f_acc = f_acc->next; + } + + /* Check gyroscope frames */ + if (ms_gyr - motion_sensors == vector.sensor_num) { + if (f_gyr == NULL) { + zassert_unreachable( + "Not expected gyroscope data in FIFO, line %d", + line); + } + + convert_int3v_int16(vector.data, ret_v); + drv_gyr_to_emul(ret_v, gyr_range, ret_v); + exp_v[0] = f_gyr->gyr_x; + exp_v[1] = f_gyr->gyr_y; + exp_v[2] = f_gyr->gyr_z; + compare_int3v_f(exp_v, ret_v, V_EPS, line); + f_gyr = f_gyr->next; + } + } + + /* Skip frames of different type at the end */ + while (f_acc != NULL && !(f_acc->type & BMI_EMUL_FRAME_ACC)) { + f_acc = f_acc->next; + } + + while (f_gyr != NULL && !(f_gyr->type & BMI_EMUL_FRAME_GYR)) { + f_gyr = f_gyr->next; + } + + /* All frames are readed */ + zassert_is_null(f_acc, "Not all accelerometer frames are read, line %d", + line); + zassert_is_null(f_gyr, "Not all gyroscope frames are read, line %d", + line); +} +#define check_fifo(ms_acc, ms_gyr, frame, acc_range, gyr_range) \ + check_fifo_f(ms_acc, ms_gyr, frame, acc_range, gyr_range, __LINE__) + +/** Test irq handler of accelerometer sensor */ +ZTEST_USER(bmi260, test_bmi_acc_fifo) +{ + struct motion_sensor_t *ms, *ms_gyr; + struct fifo_func_data func_data; + struct bmi_emul_frame f[3]; + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + int gyr_range = 125; + int acc_range = 2; + int event; + + common_data = emul_bmi_get_i2c_common_data(emul); + ms = &motion_sensors[BMI_ACC_SENSOR_ID]; + ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + bmi_init_emul(); + + /* Need to be set to collect all data in FIFO */ + ms->oversampling_ratio = 1; + ms_gyr->oversampling_ratio = 1; + /* Only BMI event should be handled */ + event = 0x1234 & ~BMI_INT_EVENT; + zassert_equal(EC_ERROR_NOT_HANDLED, ms->drv->irq_handler(ms, &event), + NULL); + + event = BMI_INT_EVENT; + + /* Test fail to read interrupt status registers */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_INT_STATUS_0); + zassert_equal(EC_ERROR_INVAL, ms->drv->irq_handler(ms, &event), NULL); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_INT_STATUS_1); + zassert_equal(EC_ERROR_INVAL, ms->drv->irq_handler(ms, &event), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test no interrupt */ + bmi_emul_set_reg(emul, BMI260_INT_STATUS_0, 0); + bmi_emul_set_reg(emul, BMI260_INT_STATUS_1, 0); + + /* Enable sensor FIFO */ + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 50000, 0), NULL); + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, NULL, acc_range, gyr_range); + + /* Set custom function for FIFO test */ + i2c_common_emul_set_read_func(common_data, emul_fifo_func, &func_data); + /* Set range */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, acc_range, 0), NULL); + zassert_equal(EC_SUCCESS, ms_gyr->drv->set_range(ms_gyr, gyr_range, 0), + NULL); + /* Setup single accelerometer frame */ + f[0].type = BMI_EMUL_FRAME_ACC; + f[0].acc_x = BMI_EMUL_1G / 10; + f[0].acc_y = BMI_EMUL_1G / 20; + f[0].acc_z = -(int)BMI_EMUL_1G / 30; + f[0].next = NULL; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI260_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Setup second accelerometer frame */ + f[1].type = BMI_EMUL_FRAME_ACC; + f[1].acc_x = -(int)BMI_EMUL_1G / 40; + f[1].acc_y = BMI_EMUL_1G / 50; + f[1].acc_z = BMI_EMUL_1G / 60; + f[0].next = &(f[1]); + f[1].next = NULL; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI260_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Enable sensor FIFO */ + zassert_equal(EC_SUCCESS, ms_gyr->drv->set_data_rate(ms_gyr, 50000, 0), + NULL); + + /* Setup first gyroscope frame (after two accelerometer frames) */ + f[2].type = BMI_EMUL_FRAME_GYR; + f[2].gyr_x = -(int)BMI_EMUL_125_DEG_S / 100; + f[2].gyr_y = BMI_EMUL_125_DEG_S / 200; + f[2].gyr_z = BMI_EMUL_125_DEG_S / 300; + f[1].next = &(f[2]); + f[2].next = NULL; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI260_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Setup second accelerometer frame to by gyroscope frame too */ + f[1].type |= BMI_EMUL_FRAME_GYR; + f[1].gyr_x = -(int)BMI_EMUL_125_DEG_S / 300; + f[1].gyr_y = BMI_EMUL_125_DEG_S / 400; + f[1].gyr_z = BMI_EMUL_125_DEG_S / 500; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI260_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Skip frame should be ignored by driver */ + bmi_emul_set_skipped_frames(emul, 8); + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI260_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Setup second frame as an config frame */ + f[1].type = BMI_EMUL_FRAME_CONFIG; + /* Indicate that accelerometer range changed */ + f[1].config = 0x1; + bmi_emul_append_frame(emul, f); + /* Setup interrupts register */ + func_data.interrupts = BMI260_FWM_INT; + + /* Trigger irq handler and check results */ + check_fifo(ms, ms_gyr, f, acc_range, gyr_range); + + /* Remove custom emulator read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +/** Test irq handler of gyroscope sensor */ +ZTEST_USER(bmi260, test_bmi_gyr_fifo) +{ + struct motion_sensor_t *ms; + uint32_t event; + + ms = &motion_sensors[BMI_GYR_SENSOR_ID]; + + /* Interrupt shuldn't be triggered for gyroscope motion sense */ + event = BMI_INT_EVENT; + zassert_equal(EC_ERROR_NOT_HANDLED, ms->drv->irq_handler(ms, &event), + NULL); +} + +ZTEST_USER(bmi260, test_unsupported_configs) +{ + /* + * This test checks that we properly handle passing in invalid sensor + * types or attempting unsupported operations on certain sensor types. + */ + + struct motion_sensor_t ms_fake; + + /* Part 1: + * Setting offset on anything that is not an accel or gyro is an error. + * Make a copy of the accelerometer motion sensor struct and modify its + * type to magnetometer for this test. + */ + memcpy(&ms_fake, &motion_sensors[BMI_ACC_SENSOR_ID], sizeof(ms_fake)); + ms_fake.type = MOTIONSENSE_TYPE_MAG; + + int16_t offset[3] = { 0 }; + int ret = + ms_fake.drv->set_offset(&ms_fake, (const int16_t *)&offset, 0); + zassert_equal( + ret, EC_RES_INVALID_PARAM, + "Expected a return code of %d (EC_RES_INVALID_PARAM) but got %d", + EC_RES_INVALID_PARAM, ret); + + /* Part 2: + * Running a calibration on a magnetometer is also not supported. + */ + memcpy(&ms_fake, &motion_sensors[BMI_ACC_SENSOR_ID], sizeof(ms_fake)); + ms_fake.type = MOTIONSENSE_TYPE_MAG; + + ret = ms_fake.drv->perform_calib(&ms_fake, 1); + zassert_equal( + ret, EC_RES_INVALID_PARAM, + "Expected a return code of %d (EC_RES_INVALID_PARAM) but got %d", + EC_RES_INVALID_PARAM, ret); +} + +ZTEST_USER(bmi260, test_interrupt_handler) +{ + /* The accelerometer interrupt handler simply sets an event flag for the + * motion sensing task. Make sure that flag starts cleared, fire the + * interrupt, and ensure the flag is set. + */ + + atomic_t *mask; + + mask = task_get_event_bitmap(TASK_ID_MOTIONSENSE); + zassert_true(mask != NULL, + "Got a null pointer when getting event bitmap."); + zassert_true((*mask & CONFIG_ACCELGYRO_BMI260_INT_EVENT) == 0, + "Event flag is set before firing interrupt"); + + bmi260_interrupt(0); + + mask = task_get_event_bitmap(TASK_ID_MOTIONSENSE); + zassert_true(mask != NULL, + "Got a null pointer when getting event bitmap."); + zassert_true(*mask & CONFIG_ACCELGYRO_BMI260_INT_EVENT, + "Event flag is not set after firing interrupt"); +} + +ZTEST_USER(bmi260, test_bmi_init_chip_id) +{ + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data = + emul_bmi_get_i2c_common_data(emul); + struct motion_sensor_t *ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + + /* Part 1: + * Error occurs while reading the chip ID + */ + i2c_common_emul_set_read_fail_reg(common_data, BMI260_CHIP_ID); + int ret = ms_acc->drv->init(ms_acc); + + zassert_equal(ret, EC_ERROR_UNKNOWN, + "Expected %d (EC_ERROR_UNKNOWN) but got %d", + EC_ERROR_UNKNOWN, ret); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Part 2: + * Test cases where the returned chip ID does not match what is + * expected. This involves overriding values in the motion_sensor + * struct, so make a copy first. + */ + struct motion_sensor_t ms_fake; + + memcpy(&ms_fake, ms_acc, sizeof(ms_fake)); + + /* Part 2a: expecting MOTIONSENSE_CHIP_BMI220 but get BMI260's chip ID! + */ + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI260_CHIP_ID_MAJOR); + ms_fake.chip = MOTIONSENSE_CHIP_BMI220; + + ret = ms_fake.drv->init(&ms_fake); + zassert_equal(ret, EC_ERROR_ACCESS_DENIED, + "Expected %d (EC_ERROR_ACCESS_DENIED) but got %d", + EC_ERROR_ACCESS_DENIED, ret); + + /* Part 2b: expecting MOTIONSENSE_CHIP_BMI260 but get BMI220's chip ID! + */ + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI220_CHIP_ID_MAJOR); + ms_fake.chip = MOTIONSENSE_CHIP_BMI260; + + ret = ms_fake.drv->init(&ms_fake); + zassert_equal(ret, EC_ERROR_ACCESS_DENIED, + "Expected %d (EC_ERROR_ACCESS_DENIED) but got %d", + EC_ERROR_ACCESS_DENIED, ret); + + /* Part 2c: use an invalid expected chip */ + ms_fake.chip = MOTIONSENSE_CHIP_MAX; + + ret = ms_fake.drv->init(&ms_fake); + zassert_equal(ret, EC_ERROR_ACCESS_DENIED, + "Expected %d (EC_ERROR_ACCESS_DENIED) but got %d", + EC_ERROR_ACCESS_DENIED, ret); +} + +/* Make an I2C emulator mock wrapped in FFF */ +FAKE_VALUE_FUNC(int, bmi_config_load_no_mapped_flash_mock_read_fn, + const struct emul *, int, uint8_t *, int, void *); +struct i2c_common_emul_data *common_data; +static int bmi_config_load_no_mapped_flash_mock_read_fn_helper( + const struct emul *emul, int reg, uint8_t *val, int bytes, void *data) +{ + if (reg == BMI260_INTERNAL_STATUS && val) { + /* We want to force-return a status of 'initialized' when this + * is read. + */ + *val = BMI260_INIT_OK; + return 0; + } + /* For other registers, go through the normal emulator route */ + return 1; +} + +ZTEST_USER(bmi260, test_bmi_config_load_no_mapped_flash) +{ + /* Tests the situation where we load BMI config data when flash memory + * is not mapped (basically what occurs when `init_rom_map()` in + * `bmi_config_load()` returns NULL) + */ + + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + struct motion_sensor_t *ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + int ret, num_status_reg_reads; + + common_data = emul_bmi_get_i2c_common_data(emul); + + /* Force bmi_config_load() to have to manually copy from memory */ + RESET_FAKE(init_rom_map); + init_rom_map_fake.return_val = NULL; + + /* Force init_rom_copy() to succeed */ + RESET_FAKE(init_rom_copy); + init_rom_copy_fake.return_val = 0; + + /* Set proper chip ID and raise the INIT_OK flag to signal that config + * succeeded. + */ + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI260_CHIP_ID_MAJOR); + i2c_common_emul_set_read_func( + common_data, bmi_config_load_no_mapped_flash_mock_read_fn, + NULL); + RESET_FAKE(bmi_config_load_no_mapped_flash_mock_read_fn); + bmi_config_load_no_mapped_flash_mock_read_fn_fake.custom_fake = + bmi_config_load_no_mapped_flash_mock_read_fn_helper; + + /* Part 1: successful path */ + ret = ms_acc->drv->init(ms_acc); + + zassert_equal(ret, EC_RES_SUCCESS, "Got %d but expected %d", ret, + EC_RES_SUCCESS); + + /* Check the number of times we accessed BMI260_INTERNAL_STATUS */ + num_status_reg_reads = MOCK_COUNT_CALLS_WITH_ARG_VALUE( + bmi_config_load_no_mapped_flash_mock_read_fn_fake, 1, + BMI260_INTERNAL_STATUS); + zassert_equal(1, num_status_reg_reads, + "Accessed status reg %d times but expected %d.", + num_status_reg_reads, 1); + + /* Part 2: write to `BMI260_INIT_ADDR_0` fails */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_INIT_ADDR_0); + + ret = ms_acc->drv->init(ms_acc); + zassert_equal(ret, EC_ERROR_INVALID_CONFIG, "Got %d but expected %d", + ret, EC_ERROR_INVALID_CONFIG); + + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Part 3: init_rom_copy() fails w/ a non-zero return code of 255. */ + init_rom_copy_fake.return_val = 255; + + ret = ms_acc->drv->init(ms_acc); + zassert_equal(ret, EC_ERROR_INVALID_CONFIG, "Got %d but expected %d", + ret, EC_ERROR_INVALID_CONFIG); + + init_rom_copy_fake.return_val = 0; + + /* Part 4: write to `BMI260_INIT_DATA` fails */ + i2c_common_emul_set_write_fail_reg(common_data, BMI260_INIT_DATA); + + ret = ms_acc->drv->init(ms_acc); + zassert_equal(ret, EC_ERROR_INVALID_CONFIG, "Got %d but expected %d", + ret, EC_ERROR_INVALID_CONFIG); + + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Cleanup */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +ZTEST_USER(bmi260, test_bmi_config_unsupported_chip) +{ + /* Test what occurs when we try to configure a chip that is + * turned off in Kconfig (BMI220). This test assumes that + * CONFIG_ACCELGYRO_BMI220 is NOT defined. + */ + +#if defined(CONFIG_ACCELGYRO_BMI220) +#error "Test test_bmi_config_unsupported_chip will not work properly with " \ + "CONFIG_ACCELGYRO_BMI220 defined." +#endif + + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + struct motion_sensor_t ms_fake; + + common_data = emul_bmi_get_i2c_common_data(emul); + + /* Set up struct and emaulator to be a BMI220 chip, which + * `bmi_config_load()` does not support in the current configuration + */ + + memcpy(&ms_fake, &motion_sensors[BMI_ACC_SENSOR_ID], sizeof(ms_fake)); + ms_fake.chip = MOTIONSENSE_CHIP_BMI220; + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI220_CHIP_ID_MAJOR); + + int ret = ms_fake.drv->init(&ms_fake); + + zassert_equal(ret, EC_ERROR_INVALID_CONFIG, "Expected %d but got %d", + EC_ERROR_INVALID_CONFIG, ret); +} + +ZTEST_USER(bmi260, test_init_config_read_failure) +{ + /* Test proper response to a failed read from the register + * BMI260_INTERNAL_STATUS. + */ + + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + struct motion_sensor_t *ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + int ret; + + common_data = emul_bmi_get_i2c_common_data(emul); + + /* Set up i2c emulator and mocks */ + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI260_CHIP_ID_MAJOR); + i2c_common_emul_set_read_fail_reg(common_data, BMI260_INTERNAL_STATUS); + RESET_FAKE(init_rom_map); + init_rom_map_fake.custom_fake = init_rom_map_addr_passthru; + + ret = ms_acc->drv->init(ms_acc); + + zassert_equal(ret, EC_ERROR_INVALID_CONFIG, "Expected %d but got %d", + EC_ERROR_INVALID_CONFIG, ret); +} + +/* Mock read function and counter used to test the timeout when + * waiting for the chip to initialize + */ +static int timeout_test_status_reg_access_count; +static int status_timeout_mock_read_fn(const struct emul *emul, int reg, + uint8_t *val, int bytes, void *data) +{ + if (reg == BMI260_INTERNAL_STATUS && val) { + /* We want to force-return a non-OK status each time */ + timeout_test_status_reg_access_count++; + *val = BMI260_INIT_ERR; + return 0; + } else { + return 1; + } +} + +ZTEST_USER(bmi260, test_init_config_status_timeout) +{ + /* We allow up to 15 tries to get a successful BMI260_INIT_OK + * value from the BMI260_INTERNAL_STATUS register. Make sure + * we properly handle the case where the chip is not initialized + * before the timeout. + */ + + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + struct motion_sensor_t *ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + int ret; + + common_data = emul_bmi_get_i2c_common_data(emul); + + /* Set up i2c emulator and mocks */ + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI260_CHIP_ID_MAJOR); + timeout_test_status_reg_access_count = 0; + i2c_common_emul_set_read_func(common_data, status_timeout_mock_read_fn, + NULL); + RESET_FAKE(init_rom_map); + init_rom_map_fake.custom_fake = init_rom_map_addr_passthru; + + ret = ms_acc->drv->init(ms_acc); + + zassert_equal(timeout_test_status_reg_access_count, 15, + "Expected %d attempts but counted %d", 15, + timeout_test_status_reg_access_count); + zassert_equal(ret, EC_ERROR_INVALID_CONFIG, "Expected %d but got %d", + EC_ERROR_INVALID_CONFIG, ret); +} + +/** + * @brief Put the driver and emulator in to a consistent state before each test. + * + * @param arg Test fixture (unused) + */ +static void bmi260_test_before(void *arg) +{ + ARG_UNUSED(arg); + + const struct emul *emul = EMUL_DT_GET(BMI_NODE); + struct i2c_common_emul_data *common_data; + struct motion_sensor_t *ms_acc = &motion_sensors[BMI_ACC_SENSOR_ID]; + struct motion_sensor_t *ms_gyr = &motion_sensors[BMI_GYR_SENSOR_ID]; + + common_data = emul_bmi_get_i2c_common_data(emul); + + /* Reset I2C */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_read_func(common_data, NULL, NULL); + i2c_common_emul_set_write_func(common_data, NULL, NULL); + + /* Reset local fakes(s) */ + RESET_FAKE(bmi_config_load_no_mapped_flash_mock_read_fn); + + /* Clear rotation matrices */ + ms_acc->rot_standard_ref = NULL; + ms_gyr->rot_standard_ref = NULL; + + /* Set Chip ID register to BMI260 (required for init() to succeed) */ + bmi_emul_set_reg(emul, BMI260_CHIP_ID, BMI260_CHIP_ID_MAJOR); +} + +ZTEST_SUITE(bmi260, drivers_predicate_pre_main, NULL, bmi260_test_before, NULL, + NULL); diff --git a/zephyr/test/drivers/default/src/charge_manager.c b/zephyr/test/drivers/default/src/charge_manager.c new file mode 100644 index 0000000000..85048178ae --- /dev/null +++ b/zephyr/test/drivers/default/src/charge_manager.c @@ -0,0 +1,58 @@ +/* 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 "charge_manager.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(charge_manager, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); + +/** + * Test the default implementation of board_fill_source_power_info(). The fill + * function should reset all the power info values. If the test binary overrides + * board_fill_source_power_info(), then this test can be removed. + */ +ZTEST_USER(charge_manager, test_default_fill_power_info) +{ + struct ec_response_usb_pd_power_info info = { + .meas = { + .voltage_now = 10, + .voltage_max = 10, + .current_max = 10, + .current_lim = 10, + }, + .max_power = 10, + }; + + board_fill_source_power_info(0, &info); + zassert_equal(info.meas.voltage_now, 0, NULL); + zassert_equal(info.meas.voltage_max, 0, NULL); + zassert_equal(info.meas.current_max, 0, NULL); + zassert_equal(info.meas.current_lim, 0, NULL); + zassert_equal(info.max_power, 0, NULL); +} + +/** + * Test the default implementation of board_charge_port_is_connected(). This + * function should always return 1 regardless of input. + */ +ZTEST_USER(charge_manager, test_default_charge_port_is_connected) +{ + zassert_true(board_charge_port_is_connected(-1), NULL); + zassert_true(board_charge_port_is_connected(0), NULL); + zassert_true(board_charge_port_is_connected(1), NULL); + zassert_true(board_charge_port_is_connected(500), NULL); +} + +ZTEST_USER(charge_manager, test_default_charge_port_is_sink) +{ + zassert_true(board_charge_port_is_sink(-1), NULL); + zassert_true(board_charge_port_is_sink(0), NULL); + zassert_true(board_charge_port_is_sink(1), NULL); + zassert_true(board_charge_port_is_sink(500), NULL); +} diff --git a/zephyr/test/drivers/default/src/console.c b/zephyr/test/drivers/default/src/console.c new file mode 100644 index 0000000000..c74fd3ea1c --- /dev/null +++ b/zephyr/test/drivers/default/src/console.c @@ -0,0 +1,88 @@ +/* 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/shell/shell_dummy.h> + +#include "builtin/stdio.h" +#include "test/drivers/test_state.h" +#include "console.h" +#include "uart.h" +#include "ec_commands.h" + +ZTEST_USER(console, printf_overflow) +{ + char buffer[10]; + + zassert_equal(-EC_ERROR_OVERFLOW, + crec_snprintf(buffer, 4, "1234567890"), NULL); + zassert_equal(0, strcmp(buffer, "123"), "got '%s'", buffer); + zassert_equal(-EC_ERROR_OVERFLOW, + crec_snprintf(buffer, 4, "%%%%%%%%%%"), NULL); + zassert_equal(0, strcmp(buffer, "%%%"), "got '%s'", buffer); +} + +/* This test is identical to test_buf_notify_null in + * test/console_edit.c. Please keep them in sync to verify that + * uart_console_read_buffer works identically in legacy EC and zephyr. + */ +ZTEST_USER(console, buf_notify_null) +{ + char buffer[100]; + uint16_t write_count; + size_t consumed_count; + + /* Flush the console buffer before we start. */ + zassert_ok(uart_console_read_buffer_init(), NULL); + + /* Write a nul char to the buffer. */ + consumed_count = console_buf_notify_chars("ab\0c", 4); + + /* Check if all bytes were consumed by console buffer */ + zassert_equal(consumed_count, 4, "got %d", consumed_count); + + /* Check if the nul is present in the buffer. */ + zassert_ok(uart_console_read_buffer_init(), NULL); + zassert_ok(uart_console_read_buffer(CONSOLE_READ_RECENT, buffer, + sizeof(buffer), &write_count), + NULL); + zassert_equal(0, strncmp(buffer, "abc", 4), "got '%s'", buffer); + zassert_equal(write_count, 4, "got %d", write_count); +} + +static const char *large_string = + "This is a very long string, it will cause a buffer flush at " + "some point while printing to the shell. Long long text. Blah " + "blah. Long long text. Blah blah. Long long text. Blah blah."; +ZTEST_USER(console, shell_fprintf_full) +{ + const struct shell *shell_zephyr = get_ec_shell(); + const char *outbuffer; + size_t buffer_size; + + zassert_true(strlen(large_string) >= + shell_zephyr->fprintf_ctx->buffer_size, + "large_string is too short, fix test."); + + shell_backend_dummy_clear_output(shell_zephyr); + shell_fprintf(shell_zephyr, SHELL_NORMAL, "%s", large_string); + + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + zassert_true(strncmp(outbuffer, large_string, strlen(large_string)) == + 0, + "Invalid console output %s", outbuffer); +} + +ZTEST_USER(console, cprint_too_big) +{ + zassert_true(strlen(large_string) >= CONFIG_SHELL_PRINTF_BUFF_SIZE, + "buffer is too short, fix test."); + + zassert_equal(cprintf(CC_COMMAND, "%s", large_string), + -EC_ERROR_OVERFLOW, NULL); +} + +ZTEST_SUITE(console, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/accelinfo.c b/zephyr/test/drivers/default/src/console_cmd/accelinfo.c new file mode 100644 index 0000000000..11638fcc70 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelinfo.c @@ -0,0 +1,55 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "config.h" +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "timer.h" + +static void console_cmd_accelinfo_after(void *fixture) +{ + ARG_UNUSED(fixture); + shell_execute_cmd(get_ec_shell(), "accelinfo off"); +} + +ZTEST_SUITE(console_cmd_accelinfo, drivers_predicate_post_main, NULL, NULL, + console_cmd_accelinfo_after, NULL); + +ZTEST_USER(console_cmd_accelinfo, test_too_many_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelinfo arg1 arg2"); + + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelinfo, test_print_once) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelinfo"), NULL); +} + +ZTEST_USER(console_cmd_accelinfo, test_invalid_arg) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelinfo bar"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_accelinfo, test_enable_disable) +{ + /* + * There's no way to verify what is being printed to the console yet, so + * just assert that the command executed and returned 0. + */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelinfo on"), NULL); + k_msleep(CONFIG_MOTION_MIN_SENSE_WAIT_TIME * MSEC * 2); + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelinfo off"), NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/accelinit.c b/zephyr/test/drivers/default/src/console_cmd/accelinit.c new file mode 100644 index 0000000000..c440faebba --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelinit.c @@ -0,0 +1,93 @@ +/* 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/fff.h> +#include <zephyr/shell/shell.h> +#include <zephyr/ztest.h> + +#include "accelgyro.h" +#include "console.h" +#include "motion_sense.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +FAKE_VALUE_FUNC(int, mock_init, struct motion_sensor_t *); + +struct console_cmd_accelinit_fixture { + const struct accelgyro_drv *sensor_0_drv; + struct accelgyro_drv mock_drv; +}; + +static void *console_cmd_accelinit_setup(void) +{ + static struct console_cmd_accelinit_fixture fixture = { + .mock_drv = { + .init = mock_init, + }, + }; + fixture.sensor_0_drv = motion_sensors[0].drv; + + return &fixture; +} + +static void console_cmd_accelinit_before(void *fixture) +{ + ARG_UNUSED(fixture); + RESET_FAKE(mock_init); + FFF_RESET_HISTORY(); +} +static void console_cmd_accelinit_after(void *fixture) +{ + struct console_cmd_accelinit_fixture *this = fixture; + + motion_sensors[0].drv = this->sensor_0_drv; + motion_sensors[0].drv->init(&motion_sensors[0]); +} + +ZTEST_SUITE(console_cmd_accelinit, drivers_predicate_post_main, + console_cmd_accelinit_setup, console_cmd_accelinit_before, + console_cmd_accelinit_after, NULL); + +ZTEST_USER(console_cmd_accelinit, test_invalid_sensor_num) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelinit f"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelinit -1"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelinit 100"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_accelinit, test_state_was_set) +{ + motion_sensors[0].state = SENSOR_INIT_ERROR; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelinit 0"), NULL); + zassert_equal(SENSOR_INITIALIZED, motion_sensors[0].state, + "Expected %d, but got %d", SENSOR_INITIALIZED, + motion_sensors[0].state); +} + +ZTEST_USER_F(console_cmd_accelinit, test_fail_3_times) +{ + mock_init_fake.return_val = 1; + motion_sensors[0].drv = &fixture->mock_drv; + motion_sensors[0].state = SENSOR_INITIALIZED; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelinit 0"), NULL); + zassert_equal(3, mock_init_fake.call_count, + "Expected 3 calls, but got %d", + mock_init_fake.call_count); + zassert_equal(SENSOR_INIT_ERROR, motion_sensors[0].state, + "Expected %d, but got %d", SENSOR_INIT_ERROR, + motion_sensors[0].state); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/accelrange.c b/zephyr/test/drivers/default/src/console_cmd/accelrange.c new file mode 100644 index 0000000000..ff9d03bfe2 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelrange.c @@ -0,0 +1,118 @@ +/* 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/shell/shell.h> +#include <zephyr/devicetree.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "driver/accel_bma2x2.h" +#include "ec_commands.h" +#include "emul/emul_bma255.h" +#include "emul/emul_common_i2c.h" +#include "i2c.h" +#include "motion_sense.h" +#include "test/drivers/test_state.h" + +#define EMUL_NODE DT_NODELABEL(bma_emul) + +#define BMA_ORD DT_DEP_ORD(EMUL_LABEL) + +static void console_cmd_accelrange_after(void *fixture) +{ + const struct emul *emul = EMUL_DT_GET(EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + + ARG_UNUSED(fixture); + shell_execute_cmd(get_ec_shell(), "accelrange 0 2"); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST_SUITE(console_cmd_accelrange, drivers_predicate_post_main, NULL, NULL, + console_cmd_accelrange_after, NULL); + +ZTEST_USER(console_cmd_accelrange, test_num_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelrange"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelrange 0 1 2 3"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelrange, test_bad_sensor_num) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelrange t"); + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelrange -1"); + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelrange 100"); + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_accelrange, test_print_range) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrange 0"), NULL); +} + +ZTEST_USER(console_cmd_accelrange, test_set_invalid_range) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelrange 0 t"); + + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_accelrange, test_set_range_round_up_implicit) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrange 0 3"), NULL); + zassert_equal(motion_sensors[0].current_range, 4, + "Expected 4, but got %d", + motion_sensors[0].current_range); +} + +ZTEST_USER(console_cmd_accelrange, test_set_range_round_up_explicit) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrange 0 3 1"), NULL); + zassert_equal(motion_sensors[0].current_range, 4, + "Expected 4, but got %d", + motion_sensors[0].current_range); +} + +ZTEST_USER(console_cmd_accelrange, test_set_range_round_down) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrange 0 5 0"), NULL); + zassert_equal(motion_sensors[0].current_range, 4, + "Expected 4, but got %d", + motion_sensors[0].current_range); +} + +ZTEST_USER(console_cmd_accelrange, test_i2c_error) +{ + const struct emul *emul = EMUL_DT_GET(EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_bma_get_i2c_common_data(emul); + int rv; + + i2c_common_emul_set_read_fail_reg(common_data, + BMA2x2_RANGE_SELECT_ADDR); + + rv = shell_execute_cmd(get_ec_shell(), "accelrange 0 3"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/accelrate.c b/zephyr/test/drivers/default/src/console_cmd/accelrate.c new file mode 100644 index 0000000000..59482ed866 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelrate.c @@ -0,0 +1,104 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "motion_sense.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +static int original_sensor_0_s0_config_odr; + +static void *console_cmd_accelrate_setup(void) +{ + original_sensor_0_s0_config_odr = + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr; + return NULL; +} + +static void console_cmd_accelrate_after(void *state) +{ + ARG_UNUSED(state); + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr = + original_sensor_0_s0_config_odr; +} + +ZTEST_SUITE(console_cmd_accelrate, drivers_predicate_post_main, + console_cmd_accelrate_setup, NULL, console_cmd_accelrate_after, + NULL); + +ZTEST_USER(console_cmd_accelrate, test_bad_arg_count) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelrate"); + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelrate 1 2 3 4"); + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelrate, test_invalid_sensor_num) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelrate f"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelrate -1"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelrate 100"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_accelrate, test_print_rate) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrate 0"), NULL); +} + +ZTEST_USER(console_cmd_accelrate, test_bad_rate_value) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelrate 0 f"); + + zassert_equal(EC_ERROR_PARAM2, rv, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_accelrate, test_set_ap_rate) +{ + test_set_chipset_to_s0(); + + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr = 0; + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrate 0 100"), NULL); + zassert_equal(100 | ROUND_UP_FLAG, + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr, + "Expected %d, but got %d", 100 | ROUND_UP_FLAG, + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr); + + /* Try explicit round up value: 1 */ + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr = 0; + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrate 0 100 1"), + NULL); + zassert_equal(100 | ROUND_UP_FLAG, + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr, + "Expected %d, but got %d", 100 | ROUND_UP_FLAG, + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr); + + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr = 0; + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelrate 0 100 0"), + NULL); + zassert_equal(100, motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr, + "Expected %d, but got %d", 100 | ROUND_UP_FLAG, + motion_sensors[0].config[SENSOR_CONFIG_EC_S0].odr); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/accelread.c b/zephyr/test/drivers/default/src/console_cmd/accelread.c new file mode 100644 index 0000000000..81ebf87e55 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelread.c @@ -0,0 +1,124 @@ +/* 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/fff.h> +#include <zephyr/shell/shell.h> +#include <zephyr/ztest.h> + +#include "accelgyro.h" +#include "console.h" +#include "ec_commands.h" +#include "motion_sense.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +FAKE_VALUE_FUNC(int, mock_read, const struct motion_sensor_t *, int *); +FAKE_VALUE_FUNC(int, mock_set_data_rate, const struct motion_sensor_t *, int, + int); +FAKE_VALUE_FUNC(int, mock_get_data_rate, const struct motion_sensor_t *); + +struct console_cmd_accelread_fixture { + const struct accelgyro_drv *sensor_0_drv; + struct accelgyro_drv mock_drv; +}; + +static void *console_cmd_accelread_setup(void) +{ + static struct console_cmd_accelread_fixture fixture = { + .mock_drv = { + .read = mock_read, + /* + * Data rate functions are required so that motion_sense + * task doesn't segfault. + */ + .set_data_rate = mock_set_data_rate, + .get_data_rate = mock_get_data_rate, + }, + }; + fixture.sensor_0_drv = motion_sensors[0].drv; + + return &fixture; +} + +static void console_cmd_accelread_before(void *fixture) +{ + ARG_UNUSED(fixture); + RESET_FAKE(mock_read); + RESET_FAKE(mock_set_data_rate); + RESET_FAKE(mock_get_data_rate); + FFF_RESET_HISTORY(); +} + +static void console_cmd_accelread_after(void *fixture) +{ + struct console_cmd_accelread_fixture *this = fixture; + + motion_sensors[0].drv = this->sensor_0_drv; +} + +ZTEST_SUITE(console_cmd_accelread, drivers_predicate_pre_main, + console_cmd_accelread_setup, console_cmd_accelread_before, + console_cmd_accelread_after, NULL); + +ZTEST_USER(console_cmd_accelread, test_too_few_arguments) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelread"); + + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelread, test_invalid_sensor_num) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelread f"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelread -1"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelread 100"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +static struct console_cmd_accelread_fixture *current_fixture; + +int mock_read_call_super(const struct motion_sensor_t *s, int *v) +{ + return current_fixture->sensor_0_drv->read(s, v); +} + +ZTEST_USER_F(console_cmd_accelread, test_read) +{ + current_fixture = fixture; + mock_read_fake.custom_fake = mock_read_call_super; + mock_get_data_rate_fake.return_val = 100; + motion_sensors[0].drv = &fixture->mock_drv; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelread 0"), NULL); + zassert_equal(1, mock_read_fake.call_count, + "Expected only 1 call to read, but got %d", + mock_read_fake.call_count); + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelread 0 2"), NULL); + zassert_equal(3, mock_read_fake.call_count, + "Expected only 3 call to read, but got %d", + mock_read_fake.call_count); +} + +ZTEST_USER_F(console_cmd_accelread, test_read_fail) +{ + mock_read_fake.return_val = 1; + motion_sensors[0].drv = &fixture->mock_drv; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelread 0"), NULL); + zassert_equal(1, mock_read_fake.call_count, + "Expected only 1 call to read, but got %d", + mock_read_fake.call_count); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/accelres.c b/zephyr/test/drivers/default/src/console_cmd/accelres.c new file mode 100644 index 0000000000..5e29a0572d --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelres.c @@ -0,0 +1,127 @@ +/* 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/fff.h> +#include <zephyr/shell/shell.h> +#include <zephyr/ztest.h> + +#include "accelgyro.h" +#include "console.h" +#include "driver/accel_bma2x2.h" +#include "ec_commands.h" +#include "motion_sense.h" +#include "test/drivers/test_state.h" + +FAKE_VALUE_FUNC(int, set_resolution, const struct motion_sensor_t *, int, int); + +struct console_cmd_accelres_fixture { + const struct accelgyro_drv *sensor_0_drv; + struct accelgyro_drv mock_drv; +}; + +void *console_cmd_accelres_setup(void) +{ + static struct console_cmd_accelres_fixture fixture = { + .mock_drv = { + .set_resolution = set_resolution, + }, + }; + + fixture.sensor_0_drv = motion_sensors[0].drv; + + return &fixture; +} + +void console_cmd_accelres_before(void *fixture) +{ + ARG_UNUSED(fixture); + RESET_FAKE(set_resolution); + FFF_RESET_HISTORY(); +} + +void console_cmd_accelres_after(void *fixture) +{ + struct console_cmd_accelres_fixture *this = fixture; + + motion_sensors[0].drv = this->sensor_0_drv; +} + +ZTEST_SUITE(console_cmd_accelres, drivers_predicate_post_main, + console_cmd_accelres_setup, NULL, console_cmd_accelres_after, NULL); + +ZTEST_USER(console_cmd_accelres, test_too_few_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelres"); + + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelres, test_too_many_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelres 1 2 3 4"); + + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelres, test_invalid_sensor_num) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelres f"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelres -1"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelres 100"); + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_accelres, test_print_res) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelres 0"), NULL); +} + +ZTEST_USER(console_cmd_accelres, test_set_res__invalid_data) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelres 0 f"); + + zassert_equal(EC_ERROR_PARAM2, rv, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_accelres, test_set_res__no_setter) +{ + int resolution; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelres 0 4"), NULL); + resolution = motion_sensors[0].drv->get_resolution(&motion_sensors[0]); + zassert_equal(BMA2x2_RESOLUTION, resolution, "Expected %d, but got %d", + BMA2x2_RESOLUTION, resolution); +} + +ZTEST_USER_F(console_cmd_accelres, test_set_res__bad_res_value) +{ + int rv; + + set_resolution_fake.return_val = EC_ERROR_INVAL; + motion_sensors[0].drv = &fixture->mock_drv; + rv = shell_execute_cmd(get_ec_shell(), "accelres 0 0"); + zassert_equal(EC_ERROR_PARAM2, rv, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_accelres, test_invalid_rounding_arg) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelres 0 12 f"); + + zassert_equal(EC_ERROR_PARAM3, rv, "Expected %d, but got %d", + EC_ERROR_PARAM3, rv); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/accelspoof.c b/zephyr/test/drivers/default/src/console_cmd/accelspoof.c new file mode 100644 index 0000000000..3e183ca296 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/accelspoof.c @@ -0,0 +1,97 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "motion_sense.h" +#include "test/drivers/test_state.h" + +static void console_cmd_accelspoof_after(void *fixture) +{ + ARG_UNUSED(fixture); + shell_execute_cmd(get_ec_shell(), "accelspoof 0 off"); + motion_sensors[0].spoof_xyz[0] = 0; + motion_sensors[0].spoof_xyz[1] = 0; + motion_sensors[0].spoof_xyz[2] = 0; +} + +ZTEST_SUITE(console_cmd_accelspoof, drivers_predicate_post_main, NULL, NULL, + console_cmd_accelspoof_after, NULL); + +ZTEST_USER(console_cmd_accelspoof, test_too_few_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelspoof"); + + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelspoof, test_invalid_sensor_id) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "accelspoof -1"); + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), "accelspoof 100"); + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_accelspoof, test_print_mode) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelspoof 0"), NULL); +} + +ZTEST_USER(console_cmd_accelspoof, test_invalid_boolean) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelspoof 0 bar"); + + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_accelspoof, test_enable_disable) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelspoof 0 on"), NULL); + zassert_true(motion_sensors[0].flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE, + NULL); + + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelspoof 0 off"), NULL); + zassert_false(motion_sensors[0].flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE, + NULL); +} + +ZTEST_USER(console_cmd_accelspoof, test_wrong_num_axis_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "accelspoof 0 on 1"); + + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_accelspoof, test_enable_explicit_values) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelspoof 0 on 1 2 3"), + NULL); + zassert_equal(1, motion_sensors[0].spoof_xyz[0], NULL); + zassert_equal(2, motion_sensors[0].spoof_xyz[1], NULL); + zassert_equal(3, motion_sensors[0].spoof_xyz[2], NULL); +} + +ZTEST_USER(console_cmd_accelspoof, test_enable_implicit_values) +{ + motion_sensors[0].raw_xyz[0] = 4; + motion_sensors[0].raw_xyz[1] = 5; + motion_sensors[0].raw_xyz[2] = 6; + zassert_ok(shell_execute_cmd(get_ec_shell(), "accelspoof 0 on"), NULL); + zassert_equal(4, motion_sensors[0].spoof_xyz[0], NULL); + zassert_equal(5, motion_sensors[0].spoof_xyz[1], NULL); + zassert_equal(6, motion_sensors[0].spoof_xyz[2], NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/adc.c b/zephyr/test/drivers/default/src/console_cmd/adc.c new file mode 100644 index 0000000000..85dfda939a --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/adc.c @@ -0,0 +1,43 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +/* Default adc command, lists out channels */ +ZTEST_USER(console_cmd_adc, test_adc_noname) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "adc"), + "Failed default print"); +} + +/* adc with named channels */ +ZTEST_USER(console_cmd_adc, test_adc_named_channels) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "adc charger"), + "Failed to get charger adc channel."); + zassert_ok(shell_execute_cmd(get_ec_shell(), "adc ddr-soc"), + "Failed to get ddr-soc adc channel."); + zassert_ok(shell_execute_cmd(get_ec_shell(), "adc fan"), + "Failed to get fan adc channel."); + zassert_ok(shell_execute_cmd(get_ec_shell(), "adc psys"), + "Failed to get psys adc channel."); +} + +/* adc with unknown channel */ +ZTEST_USER(console_cmd_adc, test_adc_wrong_name) +{ + int rv = shell_execute_cmd(get_ec_shell(), "adc fish"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_SUITE(console_cmd_adc, NULL, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/battery.c b/zephyr/test/drivers/default/src/console_cmd/battery.c new file mode 100644 index 0000000000..9c3e21fcf1 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/battery.c @@ -0,0 +1,90 @@ +/* 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/drivers/emul.h> +#include <zephyr/shell/shell.h> +#include <zephyr/ztest.h> + +#include "battery_smart.h" +#include "console.h" +#include "ec_commands.h" +#include "emul/emul_common_i2c.h" +#include "emul/emul_smart_battery.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +struct console_cmd_battery_fixture { + const struct emul *emul; + struct i2c_common_emul_data *i2c_emul; +}; + +static void *console_cmd_battery_setup(void) +{ + static struct console_cmd_battery_fixture fixture = { + .emul = EMUL_DT_GET(DT_NODELABEL(battery)), + }; + + fixture.i2c_emul = emul_smart_battery_get_i2c_common_data(fixture.emul); + + return &fixture; +} + +static void console_cmd_battery_after(void *f) +{ + struct console_cmd_battery_fixture *fixture = f; + + i2c_common_emul_set_read_fail_reg(fixture->i2c_emul, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +/* Default battery command */ +ZTEST_USER(console_cmd_battery, test_battery_default) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "battery"), + "Failed default print"); +} + +ZTEST_USER_F(console_cmd_battery, test_battery_status_i2c_error) +{ + /* Force a failure on the battery i2c write to SB_BATTERY_STATUS */ + i2c_common_emul_set_read_fail_reg(fixture->i2c_emul, SB_BATTERY_STATUS); + zassert_ok(shell_execute_cmd(get_ec_shell(), "battery"), + "Failed default print"); +} + +/* Battery command with repeat */ +ZTEST_USER(console_cmd_battery, test_battery_repeat) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "battery 2"), + "Failed default print"); + zassert_ok(shell_execute_cmd(get_ec_shell(), "battery 8"), + "Failed default print"); +} + +/* Battery command with repeat and sleep */ +ZTEST_USER(console_cmd_battery, test_battery_repeat_sleep) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "battery 2 400"), + "Failed default print"); + zassert_ok(shell_execute_cmd(get_ec_shell(), "battery 8 200"), + "Failed default print"); +} + +/* Battery command with invalid repeat and sleep */ +ZTEST_USER(console_cmd_battery, test_battery_bad_repeat_sleep) +{ + int rv = shell_execute_cmd(get_ec_shell(), "battery fish 400"); + + zassert_equal(rv, EC_ERROR_INVAL, "Expected %d, but got %d", + EC_ERROR_INVAL, rv); + + rv = shell_execute_cmd(get_ec_shell(), "battery 2 fish"); + + zassert_equal(rv, EC_ERROR_INVAL, "Expected %d, but got %d", + EC_ERROR_INVAL, rv); +} + +ZTEST_SUITE(console_cmd_battery, drivers_predicate_post_main, + console_cmd_battery_setup, NULL, console_cmd_battery_after, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/button.c b/zephyr/test/drivers/default/src/console_cmd/button.c new file mode 100644 index 0000000000..9272b2ce2d --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/button.c @@ -0,0 +1,67 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_button, test_button_no_arg) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "button"); + + zassert_equal(EC_ERROR_PARAM_COUNT, rv, "Expected %d, returned %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_button, test_button_vup) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "button vup a"); + + zassert_equal(EC_ERROR_PARAM2, rv, "Expected %d, returned %d", + EC_ERROR_PARAM2, rv); + + rv = shell_execute_cmd(get_ec_shell(), "button vup 50"); + + zassert_ok(rv, "Expected %d, returned %d", EC_SUCCESS, rv); +} + +ZTEST_USER(console_cmd_button, test_button_vdown) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "button vdown a"); + + zassert_equal(EC_ERROR_PARAM2, rv, "Expected %d, returned %d", + EC_ERROR_PARAM2, rv); + + rv = shell_execute_cmd(get_ec_shell(), "button vdown 50"); + + zassert_ok(rv, "Expected %d, returned %d", EC_SUCCESS, rv); +} + +ZTEST_USER(console_cmd_button, test_button_rec) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "button rec 50"); + + if (IS_ENABLED(CONFIG_DEDICATED_RECOVERY_BUTTON)) { + zassert_ok(rv, "Expected %d, returned %d", EC_SUCCESS, rv); + } else { + /* Recovery button does not exist */ + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, returned %d", + EC_ERROR_PARAM1, rv); + } +} + +ZTEST_SUITE(console_cmd_button, NULL, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/cbi.c b/zephyr/test/drivers/default/src/console_cmd/cbi.c new file mode 100644 index 0000000000..495ffd7e4c --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/cbi.c @@ -0,0 +1,81 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "test/drivers/test_state.h" + +static void set_wp(bool value) +{ + const struct gpio_dt_spec *wp = GPIO_DT_FROM_NODELABEL(gpio_wp_l); + + gpio_pin_set_dt(wp, value); +} + +static void before(void *unused) +{ + /* Ensure eeprom is ready */ + set_wp(false); + zassert_ok(shell_execute_cmd(get_ec_shell(), "cbi remove 42 init"), + NULL); +} + +static void after(void *unused) +{ + /* re-enable WP */ + set_wp(true); +} + +ZTEST_SUITE(console_cmd_cbi, drivers_predicate_post_main, NULL, before, after, + NULL); + +ZTEST_USER(console_cmd_cbi, test_base) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "cbi"), NULL); +} + +ZTEST_USER(console_cmd_cbi, test_wp) +{ + set_wp(true); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi remove 42"), NULL); +} + +ZTEST_USER(console_cmd_cbi, test_remove) +{ + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi remove"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "cbi remove 42"), NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi remove abc"), NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi remove 42 1"), NULL); +} + +ZTEST_USER(console_cmd_cbi, test_set) +{ + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi set"), NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi set 10"), NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi set 11 1"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "cbi set 12 1 4"), NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi set 13 1 4 4"), + NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi set 14 1 10"), NULL); +} + +ZTEST_USER(console_cmd_cbi, test_extra) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), + "cbi remove 42 skip_write"), + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "cbi remove 42 init"), + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), + "cbi remove 42 init skip_write"), + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), + "cbi remove 42 skip_write init"), + NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "cbi remove 42 extra"), + NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/charge_manager.c b/zephyr/test/drivers/default/src/console_cmd/charge_manager.c new file mode 100644 index 0000000000..f6ee049ea1 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/charge_manager.c @@ -0,0 +1,118 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "charge_manager.h" +#include "console.h" +#include "emul/emul_isl923x.h" +#include "emul/tcpc/emul_tcpci.h" +#include "emul/tcpc/emul_tcpci_partner_snk.h" +#include "tcpm/tcpci.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +struct console_cmd_charge_manager_fixture { + struct tcpci_partner_data sink_5v_3a; + struct tcpci_snk_emul_data sink_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +static void *console_cmd_charge_manager_setup(void) +{ + static struct console_cmd_charge_manager_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 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.sink_ext, &test_fixture.sink_5v_3a, NULL); + test_fixture.sink_ext.pdo[1] = + PDO_FIXED(5000, 3000, PDO_FIXED_UNCONSTRAINED); + + return &test_fixture; +} + +static void console_cmd_charge_manager_after(void *state) +{ + struct console_cmd_charge_manager_fixture *fixture = state; + + shell_execute_cmd(get_ec_shell(), "chgoverride -1"); + disconnect_sink_from_port(fixture->tcpci_emul); +} + +ZTEST_SUITE(console_cmd_charge_manager, drivers_predicate_post_main, + console_cmd_charge_manager_setup, NULL, + console_cmd_charge_manager_after, NULL); + +/** + * Test the chgsup (charge supplier info) command. This command only prints to + * console some information which is not yet possible to verify. So just check + * that the console command ran successfully. + */ +ZTEST_USER(console_cmd_charge_manager, test_chgsup) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgsup"), NULL); +} + +/** + * Test chgoverride command with no arguments. This should just print the + * current override port. + */ +ZTEST_USER(console_cmd_charge_manager, test_chgoverride_missing_port) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgoverride"), NULL); +} + +ZTEST_USER(console_cmd_charge_manager, test_chgoverride_off_from_off) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgoverride -1"), NULL); + zassert_equal(charge_manager_get_override(), OVERRIDE_OFF, NULL); +} + +ZTEST_USER(console_cmd_charge_manager, test_chgoverride_disable_from_off) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgoverride -2"), NULL); + zassert_equal(charge_manager_get_override(), OVERRIDE_DONT_CHARGE, + NULL); +} + +ZTEST_USER(console_cmd_charge_manager, test_chgoverride_0_from_off) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgoverride 0"), NULL); + zassert_equal(charge_manager_get_override(), 0, NULL); +} + +ZTEST_USER_F(console_cmd_charge_manager, test_chgoverride_0_from_sink) +{ + 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)); + + connect_sink_to_port(&fixture->sink_5v_3a, fixture->tcpci_emul, + fixture->charger_emul); + zassert_equal(shell_execute_cmd(get_ec_shell(), "chgoverride 0"), + EC_ERROR_INVAL, NULL); +} + +ZTEST_USER(console_cmd_charge_manager, test_chgoverride_invalid_port) +{ + char cmd[256]; + + zassume_true(sprintf(cmd, "chgoverride %d", CHARGE_PORT_COUNT) > 0, + NULL); + zassert_equal(shell_execute_cmd(get_ec_shell(), cmd), EC_ERROR_PARAM1, + NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/charge_state.c b/zephyr/test/drivers/default/src/console_cmd/charge_state.c new file mode 100644 index 0000000000..d5dc9fe415 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/charge_state.c @@ -0,0 +1,282 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "charge_state.h" +#include "charge_state_v2.h" +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_charge_state, test_idle_too_few_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate idle"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_idle_arg_not_a_bool) +{ + int rv; + + /* + * There are many strings that will fail parse_bool(), just test one to + * test the code path in the command, other tests for parse_bool are + * done in the respective unit test. + */ + rv = shell_execute_cmd(get_ec_shell(), "chgstate idle g"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_idle_on__no_ac) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate idle on"); + zassert_equal(rv, EC_ERROR_NOT_POWERED, "Expected %d, but got %d", + EC_ERROR_NOT_POWERED, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_discharge_on__no_ac) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate discharge on"); + zassert_equal(rv, EC_ERROR_NOT_POWERED, "Expected %d, but got %d", + EC_ERROR_NOT_POWERED, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_discharge_too_few_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate discharge"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_discharge_arg_not_a_bool) +{ + int rv; + + /* + * There are many strings that will fail parse_bool(), just test one to + * test the code path in the command, other tests for parse_bool are + * done in the respective unit test. + */ + rv = shell_execute_cmd(get_ec_shell(), "chgstate discharge g"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_debug_too_few_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate debug"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_debug_arg_not_bool) +{ + int rv; + + /* + * There are many strings that will fail parse_bool(), just test one to + * test the code path in the command, other tests for parse_bool are + * done in the respective unit test. + */ + rv = shell_execute_cmd(get_ec_shell(), "chgstate debug g"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_debug_on) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgstate debug on"), + NULL); +} + +ZTEST_USER(console_cmd_charge_state, test_debug_on_show_charging_progress) +{ + /* + * Force reset the previous display charge so the charge state task + * prints on the next iteration. + */ + reset_prev_disp_charge(); + charging_progress_displayed(); + + /* Enable debug printing */ + zassume_ok(shell_execute_cmd(get_ec_shell(), "chgstate debug on"), + NULL); + + /* Sleep at least 1 full iteration of the charge state loop */ + k_sleep(K_USEC(CHARGE_MAX_SLEEP_USEC + 1)); + + zassert_true(charging_progress_displayed(), NULL); +} + +ZTEST_USER(console_cmd_charge_state, test_sustain_too_few_args__2_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate sustain"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_sustain_too_few_args__3_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "chgstate sustain 5"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_charge_state, test_sustain_invalid_params) +{ + /* Verify that lower bound is less than upper bound */ + zassert_equal(shell_execute_cmd(get_ec_shell(), + "chgstate sustain 50 30"), + EC_ERROR_INVAL, NULL); + + /* Verify that lower bound is at least 0 (when upper bound is given) */ + zassert_equal(shell_execute_cmd(get_ec_shell(), + "chgstate sustain -5 30"), + EC_ERROR_INVAL, NULL); + + /* Verify that upper bound is at most 100 */ + zassert_equal(shell_execute_cmd(get_ec_shell(), + "chgstate sustain 50 101"), + EC_ERROR_INVAL, NULL); +} + +struct console_cmd_charge_state_fixture { + struct tcpci_partner_data source_5v_3a; + struct tcpci_src_emul_data source_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +static void *console_cmd_charge_state_setup(void) +{ + static struct console_cmd_charge_state_fixture fixture; + + /* Get references for the emulators */ + fixture.tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc); + fixture.charger_emul = EMUL_GET_USBC_BINDING(0, chg); + + /* Initialized the source to supply 5V and 3A */ + tcpci_partner_init(&fixture.source_5v_3a, PD_REV20); + fixture.source_5v_3a.extensions = tcpci_src_emul_init( + &fixture.source_ext, &fixture.source_5v_3a, NULL); + fixture.source_ext.pdo[1] = + PDO_FIXED(5000, 3000, PDO_FIXED_UNCONSTRAINED); + + return &fixture; +} + +static void console_cmd_charge_state_after(void *data) +{ + struct console_cmd_charge_state_fixture *fixture = data; + + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + shell_execute_cmd(get_ec_shell(), "chgstate debug off"); + shell_execute_cmd(get_ec_shell(), "chgstate sustain -1 -1"); +} + +ZTEST_SUITE(console_cmd_charge_state, drivers_predicate_post_main, + console_cmd_charge_state_setup, NULL, + console_cmd_charge_state_after, NULL); + +ZTEST_USER_F(console_cmd_charge_state, test_idle_on_from_normal) +{ + /* Connect a source so we start charging */ + connect_source_to_port(&fixture->source_5v_3a, &fixture->source_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + + /* Verify that we're in "normal" mode */ + zassume_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_NORMAL, NULL); + + /* Move to idle */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgstate idle on"), NULL); + zassert_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_IDLE, NULL); +} + +ZTEST_USER_F(console_cmd_charge_state, test_normal_from_idle) +{ + /* Connect a source so we start charging */ + connect_source_to_port(&fixture->source_5v_3a, &fixture->source_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + + /* Verify that we're in "normal" mode */ + zassume_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_NORMAL, NULL); + + /* Move to idle */ + zassume_ok(shell_execute_cmd(get_ec_shell(), "chgstate idle on"), NULL); + zassume_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_IDLE, NULL); + + /* Move back to normal */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgstate idle off"), + NULL); + zassert_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_NORMAL, NULL); +} + +ZTEST_USER_F(console_cmd_charge_state, test_discharge_on) +{ + /* Connect a source so we start charging */ + connect_source_to_port(&fixture->source_5v_3a, &fixture->source_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + + /* Verify that we're in "normal" mode */ + zassume_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_NORMAL, NULL); + + /* Enable discharge */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgstate discharge on"), + NULL); + zassert_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_DISCHARGE, NULL); +} + +ZTEST_USER_F(console_cmd_charge_state, test_discharge_off) +{ + /* Connect a source so we start charging */ + connect_source_to_port(&fixture->source_5v_3a, &fixture->source_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + + /* Verify that we're in "normal" mode */ + zassume_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_NORMAL, NULL); + + /* Enable discharge */ + zassume_ok(shell_execute_cmd(get_ec_shell(), "chgstate discharge on"), + NULL); + zassume_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_DISCHARGE, NULL); + + /* Disable discharge */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgstate discharge off"), + NULL); + zassert_equal(get_chg_ctrl_mode(), CHARGE_CONTROL_NORMAL, NULL); +} + +ZTEST_USER(console_cmd_charge_state, test_sustain) +{ + struct ec_response_charge_control charge_control_values; + + /* Verify that lower bound is less than upper bound */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "chgstate sustain 30 50"), + NULL); + + charge_control_values = host_cmd_charge_control( + CHARGE_CONTROL_NORMAL, EC_CHARGE_CONTROL_CMD_GET); + zassert_equal(charge_control_values.sustain_soc.lower, 30, NULL); + zassert_equal(charge_control_values.sustain_soc.upper, 50, NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/charger.c b/zephyr/test/drivers/default/src/console_cmd/charger.c new file mode 100644 index 0000000000..9adda29a8d --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/charger.c @@ -0,0 +1,184 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "dptf.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +/* Tests which need no fixture */ +ZTEST_USER(console_cmd_charger, test_default_dump) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "charger"), + "Failed default print"); +} + +ZTEST_USER(console_cmd_charger, test_good_index) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "charger 0"), + "Failed index 0 print"); +} + +/* Bad parameter tests */ +ZTEST_USER(console_cmd_charger, test_bad_index) +{ + int rv = shell_execute_cmd(get_ec_shell(), "charger 55"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_charger, test_bad_command) +{ + int rv = shell_execute_cmd(get_ec_shell(), "charger fish"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_charger, test_bad_input_current) +{ + int rv = shell_execute_cmd(get_ec_shell(), "charger input fish"); + + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_charger, test_bad_current) +{ + int rv = shell_execute_cmd(get_ec_shell(), "charger current fish"); + + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_charger, test_bad_voltage) +{ + int rv = shell_execute_cmd(get_ec_shell(), "charger voltage fish"); + + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_charger, test_bad_dptf_current) +{ + int rv = shell_execute_cmd(get_ec_shell(), "charger dptf fish"); + + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +/* Good parameter sub-command tests */ +ZTEST_USER(console_cmd_charger, test_good_input_current) +{ + int input_current; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "charger input 1000"), + "Failed to set input current"); + zassume_ok(charger_get_input_current_limit(0, &input_current), + "Failed to get input current"); + zassert_equal(input_current, 1000, + "Input current not set in charger: %d", input_current); +} + +ZTEST_USER(console_cmd_charger, test_good_dptf) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "charger dptf 1000"), + "Failed to set dptf current"); + zassert_equal(dptf_get_charging_current_limit(), 1000, + "Unexpected dptf current"); +} + +ZTEST_USER(console_cmd_charger, test_unsupported_dump) +{ + /* Must define CONFIG_CMD_CHARGER_DUMP for this sub-command */ + int rv = shell_execute_cmd(get_ec_shell(), "charger dump"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +/* Fixture needed to supply AC for manual current/voltage set */ +struct console_cmd_charger_fixture { + struct tcpci_partner_data source_5v_3a; + struct tcpci_src_emul_data source_ext; + const struct emul *tcpci_emul; + const struct emul *charger_emul; +}; + +static void *console_cmd_charger_setup(void) +{ + static struct console_cmd_charger_fixture fixture; + + /* Assume we have one charger at index 0 */ + zassume_true(board_get_charger_chip_count() > 0, + "Insufficient chargers found"); + + /* Get references for the emulators */ + fixture.tcpci_emul = EMUL_DT_GET(DT_NODELABEL(tcpci_emul)); + fixture.charger_emul = EMUL_DT_GET(DT_NODELABEL(isl923x_emul)); + + /* Initialized the source to supply 5V and 3A */ + tcpci_partner_init(&fixture.source_5v_3a, PD_REV20); + fixture.source_5v_3a.extensions = tcpci_src_emul_init( + &fixture.source_ext, &fixture.source_5v_3a, NULL); + fixture.source_ext.pdo[1] = + PDO_FIXED(5000, 3000, PDO_FIXED_UNCONSTRAINED); + + return &fixture; +} + +static void console_cmd_charger_after(void *data) +{ + struct console_cmd_charger_fixture *fixture = data; + + /* Disconnect the source, and ensure we reset charge params */ + disconnect_source_from_port(fixture->tcpci_emul, fixture->charger_emul); + host_cmd_charge_control(CHARGE_CONTROL_NORMAL, + EC_CHARGE_CONTROL_CMD_SET); +} + +/* Tests that need the fixture */ +ZTEST_USER_F(console_cmd_charger, test_good_current) +{ + int current; + + /* Connect a source so we start charging */ + connect_source_to_port(&fixture->source_5v_3a, &fixture->source_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + zassert_ok(shell_execute_cmd(get_ec_shell(), "charger current 1000"), + "Failed to set current"); + + /* Give the charger task time to pick up the manual current */ + k_sleep(K_SECONDS(1)); + + zassume_ok(charger_get_current(0, ¤t), "Failed to get current"); + zassert_equal(current, 1000, "Current not set in charger: %d", current); +} + +ZTEST_USER_F(console_cmd_charger, test_good_voltage) +{ + int voltage; + + /* Connect a source so we start charging */ + connect_source_to_port(&fixture->source_5v_3a, &fixture->source_ext, 1, + fixture->tcpci_emul, fixture->charger_emul); + /* Note: select a fake voltage larger than the charger's minimum */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "charger voltage 3000"), + "Failed to set voltage"); + + /* Give the charger task time to pick up the manual voltage */ + k_sleep(K_SECONDS(1)); + + zassume_ok(charger_get_voltage(0, &voltage), "Failed to get voltage"); + zassert_equal(voltage, 3000, "Voltage not set in charger: %d", voltage); +} + +ZTEST_SUITE(console_cmd_charger, drivers_predicate_post_main, + console_cmd_charger_setup, NULL, console_cmd_charger_after, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/crash.c b/zephyr/test/drivers/default/src/console_cmd/crash.c new file mode 100644 index 0000000000..bc0b5d0254 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/crash.c @@ -0,0 +1,34 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "builtin/assert.h" +#include "console.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(console_cmd_crash, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); + +ZTEST_USER(console_cmd_crash, test_wrong_num_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "crash"); + + zassert_equal(EC_ERROR_PARAM1, rv, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_crash, test_assert) +{ + int rv; + + RESET_FAKE(assert_post_action); + rv = shell_execute_cmd(get_ec_shell(), "crash assert"); + + zassert_equal(EC_ERROR_UNKNOWN, rv, NULL); + zassert_equal(1, assert_post_action_fake.call_count, NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/cutoff.c b/zephyr/test/drivers/default/src/console_cmd/cutoff.c new file mode 100644 index 0000000000..00ce40660f --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/cutoff.c @@ -0,0 +1,86 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "battery.h" +#include "console.h" +#include "ec_commands.h" +#include "hooks.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +static void console_cmd_cutoff_after(void *unused) +{ + ARG_UNUSED(unused); + set_ac_enabled(true); + hook_notify(HOOK_AC_CHANGE); + k_msleep(500); +} + +ZTEST_SUITE(console_cmd_cutoff, drivers_predicate_post_main, NULL, NULL, + console_cmd_cutoff_after, NULL); + +ZTEST_USER(console_cmd_cutoff, test_sb_cutoff) +{ + int rv = shell_execute_cmd(get_ec_shell(), "cutoff"); + + zassert_equal(EC_RES_SUCCESS, rv, "Expected %d, but got %d", + EC_RES_SUCCESS, rv); + zassert_true(battery_is_cut_off(), NULL); +} + +ZTEST_USER(console_cmd_cutoff, test_invalid_arg1) +{ + int rv = shell_execute_cmd(get_ec_shell(), "cutoff bad_arg"); + + zassert_equal(EC_ERROR_INVAL, rv, "Expected %d, but got %d", + EC_ERROR_INVAL, rv); + zassert_false(battery_is_cut_off(), NULL); +} + +ZTEST_USER(console_cmd_cutoff, test_at_shutdown) +{ + int rv = shell_execute_cmd(get_ec_shell(), "cutoff at-shutdown"); + + zassert_equal(EC_RES_SUCCESS, rv, "Expected %d, but got %d", + EC_RES_SUCCESS, rv); + zassert_false(battery_is_cut_off(), NULL); + hook_notify(HOOK_CHIPSET_SHUTDOWN); + zassert_true(WAIT_FOR(battery_is_cut_off(), 1500000, k_msleep(250)), + NULL); +} + +ZTEST_USER(console_cmd_cutoff, test_clear_pending_shutdown) +{ + int rv = shell_execute_cmd(get_ec_shell(), "cutoff at-shutdown"); + + zassume_true(extpower_is_present(), NULL); + zassert_equal(EC_RES_SUCCESS, rv, "Expected %d, but got %d", + EC_RES_SUCCESS, rv); + + /* Triggering the AC_CHANGE hook will cancel the pending cutoff */ + hook_notify(HOOK_AC_CHANGE); + + /* The shutdown will no longer cutoff the battery */ + hook_notify(HOOK_CHIPSET_SHUTDOWN); + zassert_false(WAIT_FOR(battery_is_cut_off(), 1500000, k_msleep(250)), + NULL); +} + +ZTEST_USER(console_cmd_cutoff, test_ac_change_exits_cutoff) +{ + int rv; + + set_ac_enabled(false); + + rv = shell_execute_cmd(get_ec_shell(), "cutoff"); + zassert_equal(EC_RES_SUCCESS, rv, "Expected %d, but got %d", + EC_RES_SUCCESS, rv); + + set_ac_enabled(true); + zassert_false(battery_is_cut_off(), NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/gpio.c b/zephyr/test/drivers/default/src/console_cmd/gpio.c new file mode 100644 index 0000000000..164f272e27 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/gpio.c @@ -0,0 +1,37 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_gpio, test_read_invoke_success) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "gpioget test"), NULL); +} + +ZTEST_USER(console_cmd_gpio, test_read_invoke_fail) +{ + zassert_ok(!shell_execute_cmd(get_ec_shell(), "gpioget DOES_NOT_EXIST"), + NULL); +} + +ZTEST_USER(console_cmd_gpio, test_set_gpio) +{ + const struct gpio_dt_spec *gp = GPIO_DT_FROM_NODELABEL(gpio_test); + + zassert_ok(gpio_pin_set_dt(gp, 0), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "gpioset test 1"), NULL); + zassert_equal(gpio_pin_get_dt(gp), 1, NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "gpioset test 0"), NULL); + zassert_equal(gpio_pin_get_dt(gp), 0, NULL); +} + +ZTEST_SUITE(console_cmd_gpio, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/hcdebug.c b/zephyr/test/drivers/default/src/console_cmd/hcdebug.c new file mode 100644 index 0000000000..71adb02690 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/hcdebug.c @@ -0,0 +1,49 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" + +static void console_cmd_hcdebug_after(void *fixture) +{ + ARG_UNUSED(fixture); + shell_execute_cmd(get_ec_shell(), "hcdebug off"); +} + +ZTEST_SUITE(console_cmd_hcdebug, drivers_predicate_post_main, NULL, NULL, + console_cmd_hcdebug_after, NULL); + +ZTEST_USER(console_cmd_hcdebug, test_too_many_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "hcdebug arg1 arg2"); + + zassert_not_equal(rv, EC_SUCCESS, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_hcdebug, test_no_args) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "hcdebug"), NULL); +} + +ZTEST_USER(console_cmd_hcdebug, test_invalid_arg) +{ + int rv = shell_execute_cmd(get_ec_shell(), "hcdebug bar"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_hcdebug, test_valid_args) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "hcdebug off"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "hcdebug normal"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "hcdebug every"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "hcdebug params"), NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/hibdelay.c b/zephyr/test/drivers/default/src/console_cmd/hibdelay.c new file mode 100644 index 0000000000..c72a2bf66a --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/hibdelay.c @@ -0,0 +1,37 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(console_cmd_hibdelay, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); + +ZTEST_USER(console_cmd_hibdelay, test_too_many_args) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "hibdelay 1 2"), NULL); +} + +ZTEST_USER(console_cmd_hibdelay, test_no_args) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "hibdelay"), NULL); +} + +ZTEST_USER(console_cmd_hibdelay, test_invalid_arg) +{ + int rv = shell_execute_cmd(get_ec_shell(), "hibdelay 3.4"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_USER(console_cmd_hibdelay, test_valid_args) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "hibdelay 5"), NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/hostevent.c b/zephyr/test/drivers/default/src/console_cmd/hostevent.c new file mode 100644 index 0000000000..af9b37edd1 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/hostevent.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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "include/lpc.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +#ifdef CONFIG_HOST_EVENT64 +#define HOSTEVENT_PRINT_FORMAT "016" PRIx64 +#else +#define HOSTEVENT_PRINT_FORMAT "08" PRIx32 +#endif + +struct console_cmd_hostevent_fixture { + struct host_events_ctx ctx; +}; + +static void *console_cmd_hostevent_setup(void) +{ + static struct console_cmd_hostevent_fixture fixture = { 0 }; + + return &fixture; +} + +static void console_cmd_hostevent_before(void *fixture) +{ + struct console_cmd_hostevent_fixture *f = fixture; + + host_events_save(&f->ctx); +} + +static void console_cmd_hostevent_after(void *fixture) +{ + struct console_cmd_hostevent_fixture *f = fixture; + + host_events_restore(&f->ctx); +} + +static int console_cmd_hostevent(const char *subcommand, host_event_t mask) +{ + int rv; + char cmd_buf[CONFIG_SHELL_CMD_BUFF_SIZE]; + + rv = snprintf(cmd_buf, CONFIG_SHELL_CMD_BUFF_SIZE, + "hostevent %s 0x%" HOSTEVENT_PRINT_FORMAT, subcommand, + mask); + + zassume_between_inclusive(rv, 0, CONFIG_SHELL_CMD_BUFF_SIZE, + "hostevent console command too long"); + + return shell_execute_cmd(get_ec_shell(), cmd_buf); +} + +/* hostevent with no arguments */ +ZTEST_USER(console_cmd_hostevent, test_hostevent) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "hostevent"), + "Failed default print"); +} + +/* hostevent with invalid arguments */ +ZTEST_USER(console_cmd_hostevent, test_hostevent_invalid) +{ + int rv; + host_event_t mask = 0; + + /* Test invalid sub-command */ + rv = console_cmd_hostevent("invalid", mask); + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + /* Test invalid mask */ + rv = shell_execute_cmd(get_ec_shell(), "hostevent set invalid-mask"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +/* hostevent with sub-commands and verification */ +ZTEST_USER(console_cmd_hostevent, test_hostevent_sub_commands) +{ + int rv; + enum ec_status ret_val; + host_event_t event_mask; + host_event_t all_events = 0; + host_event_t set_events; + struct ec_response_host_event result = { 0 }; + struct { + enum lpc_host_event_type type; + const char *name; + host_event_t mask; + } subcommand[] = { + { + .type = LPC_HOST_EVENT_SMI, + .name = "SMI", + .mask = EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED), + }, + { + .type = LPC_HOST_EVENT_SCI, + .name = "SCI", + .mask = EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN), + }, + { + .type = LPC_HOST_EVENT_WAKE, + .name = "WAKE", + .mask = EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON), + }, + { + .type = LPC_HOST_EVENT_ALWAYS_REPORT, + .name = "ALWAYS_REPORT", + .mask = EC_HOST_EVENT_MASK( + EC_HOST_EVENT_AC_DISCONNECTED), + }, + }; + + for (int i = 0; i < ARRAY_SIZE(subcommand); i++) { + event_mask = lpc_get_host_event_mask(subcommand[i].type); + zassert_false(event_mask & subcommand[i].mask, + "%s mask is set before test started", + subcommand[i].name); + /* + * Setting mask value overwrites existing setting, so OR in + * the test bit. + */ + event_mask |= subcommand[i].mask; + rv = console_cmd_hostevent(subcommand[i].name, event_mask); + zassert_ok(rv, "Subcommand %s failed", subcommand[i].name); + zassert_true(lpc_get_host_event_mask(subcommand[i].type) & + subcommand[i].mask, + "Failed to set %s event mask", subcommand[i].name); + + /* + * It is only valid to set host events, once at least one mask + * value includes the event. Setting host events preserves + * existing events. + */ + zassert_false(host_get_events() & subcommand[i].mask, + "Host event is set before test started"); + rv = console_cmd_hostevent("set", subcommand[i].mask); + zassert_ok(rv, "Subcommand SET failed"); + + all_events |= subcommand[i].mask; + } + + /* Verify all host events were set, and none were lost */ + zassert_true((host_get_events() & all_events) == all_events, + "Failed to set host events"); + + /* Test clearing of host events */ + set_events = all_events; + for (int i = 0; i < ARRAY_SIZE(subcommand); i++) { + set_events &= ~subcommand[i].mask; + rv = console_cmd_hostevent("clear", subcommand[i].mask); + zassert_ok(rv, "Subcommand CLEAR failed"); + + zassert_true((host_get_events() & set_events) == set_events, + "Failed to clear host event"); + } + + /* Verify the backup host events were set, and none were cleared */ + ret_val = host_cmd_host_event(EC_HOST_EVENT_GET, EC_HOST_EVENT_B, + &result); + zassert_equal(ret_val, EC_RES_SUCCESS, "Expected=%d, returned=%d", + EC_RES_SUCCESS, ret_val); + zassert_true((result.value & all_events) == all_events, + "Failed to set host events backup"); + + /* Test clearing of backup host events */ + set_events = all_events; + for (int i = 0; i < ARRAY_SIZE(subcommand); i++) { + set_events &= ~subcommand[i].mask; + rv = console_cmd_hostevent("clearb", subcommand[i].mask); + zassert_ok(rv, "Subcommand CLEAR failed"); + + ret_val = host_cmd_host_event(EC_HOST_EVENT_GET, + EC_HOST_EVENT_B, &result); + zassert_equal(ret_val, EC_RES_SUCCESS, + "Expected=%d, returned=%d", EC_RES_SUCCESS, + ret_val); + zassert_true((result.value & set_events) == set_events, + "Failed to clear host events backup"); + } +} + +ZTEST_SUITE(console_cmd_hostevent, drivers_predicate_post_main, + console_cmd_hostevent_setup, console_cmd_hostevent_before, + console_cmd_hostevent_after, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/i2c_portmap.c b/zephyr/test/drivers/default/src/console_cmd/i2c_portmap.c new file mode 100644 index 0000000000..4b2ec548a2 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/i2c_portmap.c @@ -0,0 +1,27 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(console_cmd_i2c_portmap, drivers_predicate_post_main, NULL, NULL, + NULL, NULL); + +ZTEST_USER(console_cmd_i2c_portmap, test_too_many_args) +{ + int rv = shell_execute_cmd(get_ec_shell(), "i2c_portmap arg1"); + + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_i2c_portmap, test_get_i2c_portmap) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "i2c_portmap"), NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/md.c b/zephyr/test/drivers/default/src/console_cmd/md.c new file mode 100644 index 0000000000..c8c3e2c130 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/md.c @@ -0,0 +1,83 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(console_cmd_md, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); + +ZTEST_USER(console_cmd_md, test_too_few_args) +{ + zassert_equal(EC_ERROR_PARAM_COUNT, + shell_execute_cmd(get_ec_shell(), "md"), NULL); +} + +ZTEST_USER(console_cmd_md, test_error_param1) +{ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "md .j"), NULL); +} + +ZTEST_USER(console_cmd_md, test_error_bad_address) +{ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "md not_an_address"), + NULL); +} + +ZTEST_USER(console_cmd_md, test_default_count) +{ + uint8_t memory[] = { 0x01, 0x02, 0x03, 0x04 }; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "md %" PRIuPTR, (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); +} + +ZTEST_USER(console_cmd_md, test_count_arg) +{ + uint8_t memory[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "md %" PRIuPTR " 2", (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); +} + +ZTEST_USER(console_cmd_md, test_byte_format) +{ + uint8_t memory[] = { 0x01, 0x02, 0x03, 0x04 }; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "md .b %" PRIuPTR, (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); +} + +ZTEST_USER(console_cmd_md, test_half_format) +{ + uint8_t memory[] = { 0x01, 0x02, 0x03, 0x04 }; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "md .h %" PRIuPTR, (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); +} + +ZTEST_USER(console_cmd_md, test_string_format) +{ + char memory[] = "hello world"; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "md .s %" PRIuPTR " 12", (uintptr_t)memory) != + 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/panic_output.c b/zephyr/test/drivers/default/src/console_cmd/panic_output.c new file mode 100644 index 0000000000..7cc809e835 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/panic_output.c @@ -0,0 +1,71 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "panic.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +/* Test panicinfo when a panic hasn't occurred */ +ZTEST_USER(console_cmd_panic_output, test_panicinfo) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "panicinfo"), + "Failed default print"); +} + +/* Test panicinfo when a panic hasn't occurred with an extra arg. */ +/* Should return successfully. */ +ZTEST_USER(console_cmd_panic_output, test_panicinfo_bad_arg) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "panicinfo fish"), + "Failed default print with a bad argument"); +} + +/* Fixture needed to save panic data state */ +struct console_cmd_panic_output_fixture { + struct panic_data *p_data; + struct panic_data cpy_data; +}; + +static void *console_cmd_panic_setup(void) +{ + static struct console_cmd_panic_output_fixture fixture; + + return &fixture; +} + +static void console_cmd_panic_before(void *data) +{ + struct console_cmd_panic_output_fixture *fixture = data; + + fixture->p_data = get_panic_data_write(); + fixture->cpy_data = *fixture->p_data; +} + +static void console_cmd_panic_after(void *data) +{ + struct console_cmd_panic_output_fixture *fixture = data; + + struct panic_data *p_data = fixture->p_data; + + *p_data = fixture->cpy_data; +} + +/* Tests that need the fixture */ +ZTEST_USER_F(console_cmd_panic_output, test_panicinfo_with_panic) +{ + fixture->p_data->flags = 0x1; + fixture->p_data->struct_size = CONFIG_PANIC_DATA_SIZE; + fixture->p_data->magic = PANIC_DATA_MAGIC; + zassert_ok(shell_execute_cmd(get_ec_shell(), "panicinfo"), + "Failed to print details about panic."); +} + +ZTEST_SUITE(console_cmd_panic_output, NULL, console_cmd_panic_setup, + console_cmd_panic_before, console_cmd_panic_after, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/port80.c b/zephyr/test/drivers/default/src/console_cmd/port80.c new file mode 100644 index 0000000000..792895eb27 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/port80.c @@ -0,0 +1,46 @@ +/* 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. + */ + +/** + * @file + * @brief Unit Tests for ESPI port 80 console command + */ + +#include <zephyr/logging/log.h> +#include <zephyr/shell/shell.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "port80.h" + +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +/** + * @brief TestPurpose: Verify port 80 console commands + * + * @details + * Validate that the port 80 console commands work. + * + * Expected Results + * - The port 80 console commands return the appropriate result + */ +ZTEST(port80, test_port80_console) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "port80"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "port80 flush"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "port80 scroll"), NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), "port80 intprint"), NULL); + zassert_ok(!shell_execute_cmd(get_ec_shell(), "port80 unknown_param"), + NULL); +} + +/** + * @brief Test Suite: Verifies port 80 console commands. + */ +ZTEST_SUITE(console_cmd_port80, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/power_button.c b/zephyr/test/drivers/default/src/console_cmd/power_button.c new file mode 100644 index 0000000000..92d0aeaf78 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/power_button.c @@ -0,0 +1,34 @@ +/* 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 <console.h> + +ZTEST_SUITE(console_cmd_power_button, NULL, NULL, NULL, NULL, NULL); + +ZTEST_USER(console_cmd_power_button, test_return_ok) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "powerbtn"), NULL); +} + +ZTEST_USER(console_cmd_power_button, test_negative_delay) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "powerbtn -1"); + + zassert_not_equal(rv, EC_SUCCESS, + "Command should error on negative delay"); +} + +ZTEST_USER(console_cmd_power_button, test_invalid_arg) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "powerbtn foo"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/powerindebug.c b/zephyr/test/drivers/default/src/console_cmd/powerindebug.c new file mode 100644 index 0000000000..9f52a9b569 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/powerindebug.c @@ -0,0 +1,36 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_powerindebug, test_no_params) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "powerindebug"), + "Failed to get debug mask"); +} + +ZTEST_USER(console_cmd_powerindebug, test_good_params) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "powerindebug 0x10"), + "Failed to set debug mask"); + zassert_ok(shell_execute_cmd(get_ec_shell(), "powerindebug 0"), + "Failed to set debug mask"); +} + +ZTEST_USER(console_cmd_powerindebug, test_bad_params) +{ + int rv = shell_execute_cmd(get_ec_shell(), "powerindebug fish"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_SUITE(console_cmd_powerindebug, NULL, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/rtc.c b/zephyr/test/drivers/default/src/console_cmd/rtc.c new file mode 100644 index 0000000000..80530129af --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/rtc.c @@ -0,0 +1,73 @@ +/* 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 "console.h" +#include "ec_commands.h" +#include "system.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_rtc, test_rtc_no_arg) +{ + char expected_buffer[32]; + uint32_t sec = 7; + + snprintf(expected_buffer, sizeof(expected_buffer), + "RTC: 0x%08x (%d.00 s)", sec, sec); + + system_set_rtc(sec); + + CHECK_CONSOLE_CMD("rtc", expected_buffer, EC_SUCCESS); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_invalid) +{ + CHECK_CONSOLE_CMD("rtc set", NULL, EC_ERROR_INVAL); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_set) +{ + char command[32]; + char expected_buffer[32]; + uint32_t sec = 48879; + + snprintf(expected_buffer, sizeof(expected_buffer), + "RTC: 0x%08x (%d.00 s)", sec, sec); + snprintf(command, sizeof(command), "rtc set %d", sec); + + CHECK_CONSOLE_CMD(command, expected_buffer, EC_SUCCESS); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_set_bad) +{ + CHECK_CONSOLE_CMD("rtc set t", NULL, EC_ERROR_PARAM2); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_alarm_no_args) +{ + CHECK_CONSOLE_CMD("rtc_alarm", "Setting RTC alarm", EC_SUCCESS); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_alarm_good_args) +{ + CHECK_CONSOLE_CMD("rtc_alarm 1", "Setting RTC alarm", EC_SUCCESS); + CHECK_CONSOLE_CMD("rtc_alarm 1 5", "Setting RTC alarm", EC_SUCCESS); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_alarm_bad_args) +{ + CHECK_CONSOLE_CMD("rtc_alarm t", NULL, EC_ERROR_PARAM1); + CHECK_CONSOLE_CMD("rtc_alarm 1 t", NULL, EC_ERROR_PARAM2); +} + +ZTEST_USER(console_cmd_rtc, test_rtc_alarm_reset) +{ + CHECK_CONSOLE_CMD("rtc_alarm 0", "Setting RTC alarm", EC_SUCCESS); + CHECK_CONSOLE_CMD("rtc_alarm 0 0", "Setting RTC alarm", EC_SUCCESS); +} + +ZTEST_SUITE(console_cmd_rtc, NULL, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/rw.c b/zephyr/test/drivers/default/src/console_cmd/rw.c new file mode 100644 index 0000000000..2bf59b30d5 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/rw.c @@ -0,0 +1,98 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(console_cmd_rw, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); + +ZTEST_USER(console_cmd_rw, test_too_few_args) +{ + zassert_equal(EC_ERROR_PARAM_COUNT, + shell_execute_cmd(get_ec_shell(), "rw"), NULL); +} + +ZTEST_USER(console_cmd_rw, test_error_param1) +{ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "rw .j"), NULL); +} + +ZTEST_USER(console_cmd_rw, test_error_bad_address) +{ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "rw not_an_address"), + NULL); + zassert_equal(EC_ERROR_PARAM2, + shell_execute_cmd(get_ec_shell(), "rw .b not_an_address"), + NULL); +} + +ZTEST_USER(console_cmd_rw, test_read) +{ + uint8_t memory[] = { 0x01, 0x02, 0x03, 0x04 }; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "rw .b %" PRIuPTR, (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); + + zassume_true(sprintf(cmd, "rw .h %" PRIuPTR, (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); + + zassume_true(sprintf(cmd, "rw %" PRIuPTR, (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); +} + +ZTEST_USER(console_cmd_rw, test_write_invalid_value) +{ + zassert_equal(EC_ERROR_PARAM2, + shell_execute_cmd(get_ec_shell(), "rw 0 not-a-value"), + NULL); + zassert_equal(EC_ERROR_PARAM3, + shell_execute_cmd(get_ec_shell(), "rw .b 0 not-a-value"), + NULL); +} + +ZTEST_USER(console_cmd_rw, test_write) +{ + uint8_t memory[4] = { 0 }; + char cmd[128] = { 0 }; + + zassume_true(sprintf(cmd, "rw .b %" PRIuPTR " 1", (uintptr_t)memory) != + 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); + zassert_equal(1, memory[0], "memory[0] was %u", memory[0]); + zassert_equal(0, memory[1], "memory[1] was %u", memory[1]); + zassert_equal(0, memory[2], "memory[2] was %u", memory[2]); + zassert_equal(0, memory[3], "memory[3] was %u", memory[3]); + + memset(memory, 0, 4); + zassume_true(sprintf(cmd, "rw .h %" PRIuPTR " 258", + (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); + zassert_equal(2, memory[0], "memory[0] was %u", memory[0]); + zassert_equal(1, memory[1], "memory[1] was %u", memory[1]); + zassert_equal(0, memory[2], "memory[2] was %u", memory[2]); + zassert_equal(0, memory[3], "memory[3] was %u", memory[3]); + + memset(memory, 0, 4); + zassume_true(sprintf(cmd, "rw %" PRIuPTR " 16909060", + (uintptr_t)memory) != 0, + NULL); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), NULL); + zassert_equal(4, memory[0], "memory[0] was %u", memory[0]); + zassert_equal(3, memory[1], "memory[1] was %u", memory[1]); + zassert_equal(2, memory[2], "memory[2] was %u", memory[2]); + zassert_equal(1, memory[3], "memory[3] was %u", memory[3]); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/sleepmask.c b/zephyr/test/drivers/default/src/console_cmd/sleepmask.c new file mode 100644 index 0000000000..6ae017dc66 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/sleepmask.c @@ -0,0 +1,100 @@ +/* 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/drivers/emul.h> +#include <zephyr/shell/shell_dummy.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "system.h" + +ZTEST_USER(console_cmd_sleepmask, test_no_args) +{ + const struct shell *shell_zephyr = get_ec_shell(); + const char *outbuffer; + size_t buffer_size; + + shell_backend_dummy_clear_output(shell_zephyr); + + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask"), NULL); + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + + zassert_true(buffer_size > 0, NULL); + zassert_not_null(strstr(outbuffer, "sleep mask"), NULL); +} + +ZTEST_USER(console_cmd_sleepmask, test_bad_args) +{ + const struct shell *shell_zephyr = get_ec_shell(); + + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(shell_zephyr, "sleepmask whoopsie"), + NULL); +} + +ZTEST_USER(console_cmd_sleepmask, test_set_sleep_mask_directly) +{ + const struct shell *shell_zephyr = get_ec_shell(); + const char *outbuffer; + size_t buffer_size; + + shell_backend_dummy_clear_output(shell_zephyr); + + /* Set mask as 0 */ + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask 0"), NULL); + shell_backend_dummy_clear_output(shell_zephyr); + + /* Get mask and weakly verify mask is 0 */ + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask"), NULL); + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + + zassert_not_null(strstr(outbuffer, "0"), NULL); + zassert_is_null(strstr(outbuffer, "1"), NULL); + + /* Set mask as 1 */ + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask 1"), NULL); + shell_backend_dummy_clear_output(shell_zephyr); + + /* Get mask and weakly verify mask is 1 */ + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask"), NULL); + zassert_not_null(strstr(outbuffer, "1"), NULL); +} + +ZTEST_USER(console_cmd_sleepmask, test_enable_disable_force_sleepmask) +{ + const struct shell *shell_zephyr = get_ec_shell(); + + /* Verifying enabled to disabled */ + + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask on"), NULL); + + int enabled_bits = sleep_mask & SLEEP_MASK_FORCE_NO_DSLEEP; + + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask off"), NULL); + + int disabled_bits = sleep_mask & SLEEP_MASK_FORCE_NO_DSLEEP; + + zassert_false(enabled_bits & disabled_bits, NULL); + + /* Verifying disabled to enabled */ + + zassert_ok(shell_execute_cmd(shell_zephyr, "sleepmask on"), NULL); + + enabled_bits = sleep_mask & SLEEP_MASK_FORCE_NO_DSLEEP; + zassert_false(enabled_bits & disabled_bits, NULL); +} + +static void console_cmd_sleepmask_before_after(void *test_data) +{ + ARG_UNUSED(test_data); + + enable_sleep(-1); +} + +ZTEST_SUITE(console_cmd_sleepmask, drivers_predicate_post_main, NULL, + console_cmd_sleepmask_before_after, + console_cmd_sleepmask_before_after, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/sleeptimeout.c b/zephyr/test/drivers/default/src/console_cmd/sleeptimeout.c new file mode 100644 index 0000000000..d802eb5948 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/sleeptimeout.c @@ -0,0 +1,44 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_sleeptimeout, test_no_params) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "sleeptimeout"), + "Failed default print"); +} + +ZTEST_USER(console_cmd_sleeptimeout, test_good_params) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "sleeptimeout default"), + "Failed to set default sleep timeout"); + zassert_ok(shell_execute_cmd(get_ec_shell(), "sleeptimeout infinite"), + "Failed to disable sleep timeout"); + zassert_ok(shell_execute_cmd(get_ec_shell(), "sleeptimeout 1500"), + "Failed to set sleep timeout to a custom value"); +} + +ZTEST_USER(console_cmd_sleeptimeout, test_bad_params) +{ + int rv = shell_execute_cmd(get_ec_shell(), "sleeptimeout 0"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); + + rv = shell_execute_cmd(get_ec_shell(), + "sleeptimeout EC_HOST_SLEEP_TIMEOUT_INFINITE"); + + zassert_equal(rv, EC_ERROR_PARAM1, "Expected %d, but got %d", + EC_ERROR_PARAM1, rv); +} + +ZTEST_SUITE(console_cmd_sleeptimeout, NULL, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/sysinfo.c b/zephyr/test/drivers/default/src/console_cmd/sysinfo.c new file mode 100644 index 0000000000..3aeef6510c --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/sysinfo.c @@ -0,0 +1,84 @@ +/* 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/drivers/emul.h> +#include <zephyr/fff.h> +#include <zephyr/shell/shell_dummy.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "system.h" + +ZTEST_USER(console_cmd_sysinfo, test_no_args) +{ + const struct shell *shell_zephyr = get_ec_shell(); + const char *outbuffer; + size_t buffer_size; + + shell_backend_dummy_clear_output(shell_zephyr); + + zassert_ok(shell_execute_cmd(shell_zephyr, "sysinfo"), NULL); + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + + zassert_true(buffer_size > 0, NULL); + + /* Weakly verify some contents */ + zassert_not_null(strstr(outbuffer, "Reset flags:"), NULL); + zassert_not_null(strstr(outbuffer, "Copy:"), NULL); + zassert_not_null(strstr(outbuffer, "Jumped:"), NULL); + zassert_not_null(strstr(outbuffer, "Recovery:"), NULL); + zassert_not_null(strstr(outbuffer, "Flags:"), NULL); +} + +ZTEST_USER(console_cmd_sysinfo, test_no_args__sys_locked) +{ + const struct shell *shell_zephyr = get_ec_shell(); + const char *outbuffer; + size_t buffer_size; + + /* System unlocked */ + shell_backend_dummy_clear_output(shell_zephyr); + system_is_locked_fake.return_val = false; + + zassert_ok(shell_execute_cmd(shell_zephyr, "sysinfo"), NULL); + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + + zassert_true(buffer_size > 0, NULL); + zassert_not_null(strstr(outbuffer, "unlocked"), NULL); + + /* System locked */ + shell_backend_dummy_clear_output(shell_zephyr); + system_is_locked_fake.return_val = true; + + zassert_true(buffer_size > 0, NULL); + zassert_ok(shell_execute_cmd(shell_zephyr, "sysinfo"), NULL); + + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + zassert_not_null(strstr(outbuffer, "locked"), NULL); + + /* Verify system_is_locked in sysinfo cmd response remains */ + shell_backend_dummy_clear_output(shell_zephyr); + system_is_locked_fake.return_val = false; + + zassert_ok(shell_execute_cmd(shell_zephyr, "sysinfo"), NULL); + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + + zassert_true(buffer_size > 0, NULL); + zassert_not_null(strstr(outbuffer, "locked"), NULL); +} + +static void console_cmd_sysinfo_before_after(void *test_data) +{ + ARG_UNUSED(test_data); + + system_common_reset_state(); +} + +ZTEST_SUITE(console_cmd_sysinfo, drivers_predicate_post_main, NULL, + console_cmd_sysinfo_before_after, console_cmd_sysinfo_before_after, + NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/tcpci_dump.c b/zephyr/test/drivers/default/src/console_cmd/tcpci_dump.c new file mode 100644 index 0000000000..9652519cab --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/tcpci_dump.c @@ -0,0 +1,46 @@ +/* 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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_USER(console_cmd_tcpci_dump, test_no_params) +{ + int rv = shell_execute_cmd(get_ec_shell(), "tcpci_dump"); + + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_tcpci_dump, test_good_index) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "tcpci_dump 0"), + "Failed index 0 print"); +} + +ZTEST_USER(console_cmd_tcpci_dump, test_bad_index) +{ + int rv = shell_execute_cmd(get_ec_shell(), "tcpci_dump 84"); + + zassert_equal(rv, EC_ERROR_INVAL, "Expected %d, but got %d", + EC_ERROR_INVAL, rv); +} + +static void console_cmd_tcpci_dump_begin(void *data) +{ + ARG_UNUSED(data); + + /* Assume we have at least one TCPC */ + zassume_true(board_get_charger_chip_count() > 0, + "Insufficient TCPCs found"); +} + +ZTEST_SUITE(console_cmd_tcpci_dump, drivers_predicate_post_main, NULL, + console_cmd_tcpci_dump_begin, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/usb_pd_console.c b/zephyr/test/drivers/default/src/console_cmd/usb_pd_console.c new file mode 100644 index 0000000000..21056056d4 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/usb_pd_console.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/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "usb_prl_sm.h" + +static void console_cmd_usb_pd_after(void *fixture) +{ + ARG_UNUSED(fixture); + + /* TODO (b/230059737) */ + test_set_chipset_to_g3(); + k_sleep(K_SECONDS(1)); + test_set_chipset_to_s0(); + k_sleep(K_SECONDS(10)); + + /* Keep port used by testsuite enabled (default state) */ + pd_comm_enable(0, 1); + pd_set_suspend(0, 0); +} + +ZTEST_SUITE(console_cmd_usb_pd, drivers_predicate_post_main, NULL, NULL, + console_cmd_usb_pd_after, NULL); + +ZTEST_USER(console_cmd_usb_pd, test_too_few_args) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_dump) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd dump 0"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd dump 4"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd dump -4"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd dump x"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_trysrc) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd trysrc 0"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd trysrc 2"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd trysrc 5"); + zassert_equal(rv, EC_ERROR_PARAM3, "Expected %d, but got %d", + EC_ERROR_PARAM3, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_version) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd version"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_bad_port) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 5"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 5 tx"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_tx) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 tx"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_charger) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 charger"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_dev) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dev"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dev 20"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dev x"); + zassert_equal(rv, EC_ERROR_PARAM3, "Expected %d, but got %d", + EC_ERROR_PARAM3, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_disable) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 disable"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_enable) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 enable"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_suspend) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 suspend"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_resume) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 resume"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_hard) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 hard"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_soft) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 soft"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_swap) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 swap"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 swap power"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 swap data"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 swap vconn"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 swap x"); + zassert_equal(rv, EC_ERROR_PARAM3, "Expected %d, but got %d", + EC_ERROR_PARAM3, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_dualrole) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole on"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole off"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole freeze"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole sink"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole source"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 dualrole x"); + zassert_equal(rv, EC_ERROR_PARAM4, "Expected %d, but got %d", + EC_ERROR_PARAM4, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_state) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 state"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_srccaps) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 srccaps"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_timer) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pd 0 timer"); + zassert_equal(rv, EC_SUCCESS, "Expected %d, but got %d", EC_SUCCESS, + rv); +} + +static void set_device_vdo(int port, enum tcpci_msg_type type) +{ + union tbt_mode_resp_device device_resp; + struct pd_discovery *dev_disc; + + dev_disc = pd_get_am_discovery_and_notify_access(port, type); + dev_disc->svid_cnt = 1; + dev_disc->svids[0].svid = USB_VID_INTEL; + dev_disc->svids[0].discovery = PD_DISC_COMPLETE; + dev_disc->svids[0].mode_cnt = 1; + device_resp.tbt_alt_mode = TBT_ALTERNATE_MODE; + device_resp.tbt_adapter = TBT_ADAPTER_TBT3; + device_resp.intel_spec_b0 = VENDOR_SPECIFIC_NOT_SUPPORTED; + device_resp.vendor_spec_b0 = VENDOR_SPECIFIC_NOT_SUPPORTED; + device_resp.vendor_spec_b1 = VENDOR_SPECIFIC_NOT_SUPPORTED; + dev_disc->svids[0].mode_vdo[0] = device_resp.raw_value; +} + +static void set_active_cable_type(int port, enum tcpci_msg_type type, + enum idh_ptype ptype) +{ + struct pd_discovery *dev_disc; + + dev_disc = pd_get_am_discovery_and_notify_access(port, type); + dev_disc->identity.idh.product_type = ptype; + prl_set_rev(port, type, PD_REV30); +} + +ZTEST_USER(console_cmd_usb_pd, test_pe) +{ + int rv; + + pd_set_identity_discovery(0, TCPCI_MSG_SOP, PD_DISC_COMPLETE); + + rv = shell_execute_cmd(get_ec_shell(), "pe 0 dump"); + zassert_ok(rv, "Expected %d, but got %d", EC_SUCCESS, rv); + + set_device_vdo(0, TCPCI_MSG_SOP); + rv = shell_execute_cmd(get_ec_shell(), "pe 0 dump"); + zassert_ok(rv, "Expected %d, but got %d", EC_SUCCESS, rv); + + /* Handle error scenarios */ + rv = shell_execute_cmd(get_ec_shell(), "pe 0"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "pe x dump"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} + +ZTEST_USER(console_cmd_usb_pd, test_pdcable) +{ + int rv; + + rv = shell_execute_cmd(get_ec_shell(), "pdcable 0"); + zassert_ok(rv, "Expected %d, but got %d", EC_SUCCESS, rv); + + set_device_vdo(0, TCPCI_MSG_SOP_PRIME); + + /* Set active cable type IDH_PTYPE_ACABLE */ + set_active_cable_type(0, TCPCI_MSG_SOP_PRIME, IDH_PTYPE_ACABLE); + rv = shell_execute_cmd(get_ec_shell(), "pdcable 0"); + zassert_ok(rv, "Expected %d, but got %d", EC_SUCCESS, rv); + + /* Set active cable type IDH_PTYPE_PCABLE */ + set_active_cable_type(0, TCPCI_MSG_SOP_PRIME, IDH_PTYPE_PCABLE); + rv = shell_execute_cmd(get_ec_shell(), "pdcable 0"); + zassert_ok(rv, "Expected %d, but got %d", EC_SUCCESS, rv); + + /* Handle error scenarios */ + rv = shell_execute_cmd(get_ec_shell(), "pdcable"); + zassert_equal(rv, EC_ERROR_PARAM_COUNT, "Expected %d, but got %d", + EC_ERROR_PARAM_COUNT, rv); + + rv = shell_execute_cmd(get_ec_shell(), "pdcable t"); + zassert_equal(rv, EC_ERROR_PARAM2, "Expected %d, but got %d", + EC_ERROR_PARAM2, rv); +} diff --git a/zephyr/test/drivers/default/src/console_cmd/version.c b/zephyr/test/drivers/default/src/console_cmd/version.c new file mode 100644 index 0000000000..932cc51449 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/version.c @@ -0,0 +1,37 @@ +/* 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/drivers/emul.h> +#include <zephyr/shell/shell_dummy.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "system.h" + +ZTEST_USER(console_cmd_version, test_no_args) +{ + const struct shell *shell_zephyr = get_ec_shell(); + const char *outbuffer; + size_t buffer_size; + + shell_backend_dummy_clear_output(shell_zephyr); + + zassert_ok(shell_execute_cmd(shell_zephyr, "version"), NULL); + outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size); + + zassert_true(buffer_size > 0, NULL); + + /* Weakly verify some contents */ + zassert_not_null(strstr(outbuffer, "Chip:"), NULL); + zassert_not_null(strstr(outbuffer, "Board:"), NULL); + zassert_not_null(strstr(outbuffer, "RO:"), NULL); + zassert_not_null(strstr(outbuffer, "RW:"), NULL); + zassert_not_null(strstr(outbuffer, "Build:"), NULL); +} + +ZTEST_SUITE(console_cmd_version, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); diff --git a/zephyr/test/drivers/default/src/console_cmd/waitms.c b/zephyr/test/drivers/default/src/console_cmd/waitms.c new file mode 100644 index 0000000000..0d03ee7414 --- /dev/null +++ b/zephyr/test/drivers/default/src/console_cmd/waitms.c @@ -0,0 +1,51 @@ +/* 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 <stdio.h> +#include <zephyr/shell/shell.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "timer.h" + +static void test_int(int ms) +{ + char cmd[32]; + unsigned long measured; + timestamp_t start; + timestamp_t end; + + sprintf(cmd, "waitms %d", ms); + start = get_time(); + zassert_ok(shell_execute_cmd(get_ec_shell(), cmd), + "Failed to execute 'waitms' command"); + end = get_time(); + measured = (end.val - start.val) / 1000; + zassert_equal(measured, ms, "'waitms %d' failed, took %ld ms", ms, + measured); +} + +ZTEST_SUITE(console_cmd_waitms, NULL, NULL, NULL, NULL, NULL); + +ZTEST_USER(console_cmd_waitms, test_waitms) +{ + /* + * Test across three orders of magnitude. Beyond ~3s the watchdog will + * trigger so don't need to bother testing 10s of seconds or greater. + */ + test_int(0); + test_int(5); + test_int(75); + test_int(250); + test_int(1000); + + /* A plain string should fail. */ + zassert_true(shell_execute_cmd(get_ec_shell(), "waitms string"), NULL); + + /* Floats and negative ints should fail. */ + zassert_true(shell_execute_cmd(get_ec_shell(), "waitms 123.456"), NULL); + zassert_true(shell_execute_cmd(get_ec_shell(), "waitms -67.3"), NULL); + zassert_true(shell_execute_cmd(get_ec_shell(), "waitms -7"), NULL); +} diff --git a/zephyr/test/drivers/default/src/cros_cbi.c b/zephyr/test/drivers/default/src/cros_cbi.c new file mode 100644 index 0000000000..e92765cb52 --- /dev/null +++ b/zephyr/test/drivers/default/src/cros_cbi.c @@ -0,0 +1,55 @@ +/* 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/device.h> +#include <zephyr/ztest.h> + +#include "cros_cbi.h" +#include "test/drivers/test_state.h" + +ZTEST(cros_cbi, test_check_match) +{ + int value; + + value = cros_cbi_ssfc_check_match( + CBI_SSFC_VALUE_ID(DT_NODELABEL(base_sensor_0))); + zassert_true(value, "Expected cbi ssfc to match base_sensor_0"); + + value = cros_cbi_ssfc_check_match( + CBI_SSFC_VALUE_ID(DT_NODELABEL(base_sensor_1))); + zassert_false(value, "Expected cbi ssfc not to match base_sensor_1"); + + value = cros_cbi_ssfc_check_match(CBI_SSFC_VALUE_COUNT); + zassert_false(value, "Expected cbi ssfc to fail on invalid enum"); +} + +ZTEST(cros_cbi, test_fail_check_match) +{ + int value; + + value = cros_cbi_ssfc_check_match(CBI_SSFC_VALUE_COUNT); + zassert_false(value, + "Expected cbi ssfc to never match CBI_SSFC_VALUE_COUNT"); +} + +ZTEST(cros_cbi, test_fw_config) +{ + uint32_t value; + int ret; + + ret = cros_cbi_get_fw_config(FW_CONFIG_FIELD_1, &value); + zassert_true(ret == 0, + "Expected no error return from cros_cbi_get_fw_config"); + zassert_true(value == FW_FIELD_1_B, + "Expected field value to match FW_FIELD_1_B as default"); + + ret = cros_cbi_get_fw_config(FW_CONFIG_FIELD_2, &value); + zassert_true(ret == 0, + "Expected no error return from cros_cbi_get_fw_config"); + zassert_false(value == FW_FIELD_2_X, + "Expected field value to not match FW_FIELD_2_X"); +} + +ZTEST_SUITE(cros_cbi, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/espi.c b/zephyr/test/drivers/default/src/espi.c new file mode 100644 index 0000000000..9843471ae7 --- /dev/null +++ b/zephyr/test/drivers/default/src/espi.c @@ -0,0 +1,326 @@ +/* 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 <string.h> +#include <zephyr/fff.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "ec_commands.h" +#include "gpio.h" +#include "host_command.h" +#include "system.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +#define PORT 0 + +#define AC_OK_OD_GPIO_NAME "acok_od" + +static void espi_before(void *state) +{ + ARG_UNUSED(state); + RESET_FAKE(system_is_locked); +} + +static void espi_after(void *state) +{ + ARG_UNUSED(state); + RESET_FAKE(system_is_locked); +} + +ZTEST_USER(espi, test_host_command_get_protocol_info) +{ + struct ec_response_get_protocol_info response; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND_RESPONSE( + EC_CMD_GET_PROTOCOL_INFO, 0, response); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(response.protocol_versions, BIT(3), NULL); + zassert_equal(response.max_request_packet_size, EC_LPC_HOST_PACKET_SIZE, + NULL); + zassert_equal(response.max_response_packet_size, + EC_LPC_HOST_PACKET_SIZE, NULL); + zassert_equal(response.flags, 0, NULL); +} + +ZTEST_USER(espi, test_host_command_usb_pd_power_info) +{ + /* Only test we've enabled the command */ + struct ec_response_usb_pd_power_info response; + struct ec_params_usb_pd_power_info params = { .port = PORT }; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND( + EC_CMD_USB_PD_POWER_INFO, 0, response, params); + + args.params = ¶ms; + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); +} + +ZTEST_USER(espi, test_host_command_typec_status) +{ + /* Only test we've enabled the command */ + 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); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); +} + +ZTEST_USER(espi, test_host_command_usb_pd_get_amode) +{ + /* Only test we've enabled the command */ + struct ec_params_usb_pd_get_mode_request params = { + .port = PORT, + .svid_idx = 0, + }; + struct ec_params_usb_pd_get_mode_response response; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND( + EC_CMD_USB_PD_GET_AMODE, 0, response, params); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + /* Note: with no SVIDs the response size is the size of the svid field. + * See the usb alt mode test for verifying larger struct sizes + */ + zassert_equal(args.response_size, sizeof(response.svid), NULL); +} + +ZTEST_USER(espi, test_host_command_gpio_get_v0) +{ + struct ec_params_gpio_get p = { + /* Checking for AC enabled */ + .name = AC_OK_OD_GPIO_NAME, + }; + struct ec_response_gpio_get response; + + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 0, response, p); + + set_ac_enabled(true); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_true(response.val, NULL); + + set_ac_enabled(false); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_false(response.val, NULL); +} + +ZTEST_USER(espi, test_host_command_gpio_get_v1_get_by_name) +{ + struct ec_params_gpio_get_v1 p = { + .subcmd = EC_GPIO_GET_BY_NAME, + /* Checking for AC enabled */ + .get_value_by_name = { + AC_OK_OD_GPIO_NAME, + }, + }; + struct ec_response_gpio_get_v1 response; + + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 1, response, p); + + set_ac_enabled(true); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response.get_value_by_name), + NULL); + zassert_true(response.get_info.val, NULL); + + set_ac_enabled(false); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response.get_value_by_name), + NULL); + zassert_false(response.get_info.val, NULL); +} + +ZTEST_USER(espi, test_host_command_gpio_get_v1_get_count) +{ + struct ec_params_gpio_get_v1 p = { + .subcmd = EC_GPIO_GET_COUNT, + }; + struct ec_response_gpio_get_v1 response; + + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 1, response, p); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response.get_count), NULL); + zassert_equal(response.get_count.val, GPIO_COUNT, NULL); +} + +ZTEST_USER(espi, test_host_command_gpio_get_v1_get_info) +{ + const enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_acok_od)); + struct ec_params_gpio_get_v1 p = { + .subcmd = EC_GPIO_GET_INFO, + .get_info = { + .index = signal, + }, + }; + struct ec_response_gpio_get_v1 response; + + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 1, response, p); + + set_ac_enabled(true); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_ok(strcmp(response.get_info.name, AC_OK_OD_GPIO_NAME), NULL); + zassert_true(response.get_info.val, NULL); + + set_ac_enabled(false); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_ok(strcmp(response.get_info.name, AC_OK_OD_GPIO_NAME), NULL); + zassert_false(response.get_info.val, NULL); +} + +ZTEST_USER(espi, test_host_command_gpio_set) +{ + struct nothing { + int place_holder; + }; + const struct gpio_dt_spec *gp = GPIO_DT_FROM_NODELABEL(gpio_test); + struct ec_params_gpio_set p = { + .name = "test", + .val = 0, + }; + + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_GPIO_SET, 0, p); + + /* Force value to 1 to see change */ + zassume_ok(gpio_pin_set_dt(gp, 1), NULL); + + zassert_ok(host_command_process(&args), NULL); + zassert_equal(gpio_pin_get_dt(gp), p.val, NULL); + + p.val = 1; + + zassert_ok(host_command_process(&args), NULL); + zassert_equal(gpio_pin_get_dt(gp), p.val, NULL); +} + +ZTEST(espi, test_hc_gpio_get_v0_invalid_name) +{ + struct ec_response_gpio_get response; + struct ec_params_gpio_get params = { .name = "INVALID_GPIO_NAME" }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 0, response, params); + + zassert_equal(EC_RES_ERROR, host_command_process(&args), NULL); +} + +ZTEST(espi, test_hc_gpio_get_v1_get_by_name_invalid_name) +{ + struct ec_response_gpio_get_v1 response; + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_BY_NAME, + .get_value_by_name.name = "INVALID_GPIO_NAME", + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 1, response, params); + + zassert_equal(EC_RES_ERROR, host_command_process(&args), NULL); +} + +ZTEST(espi, test_hc_gpio_get_v1_get_info_invalid_index) +{ + struct ec_response_gpio_get_v1 response; + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + .get_info.index = GPIO_COUNT, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 1, response, params); + + zassert_equal(EC_RES_ERROR, host_command_process(&args), NULL); +} + +ZTEST(espi, test_hc_gpio_get_v1_invalid_subcmd) +{ + struct ec_response_gpio_get_v1 response; + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_CMD_GPIO_GET, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_GPIO_GET, 1, response, params); + + zassert_equal(EC_RES_INVALID_PARAM, host_command_process(&args), NULL); +} + +/* EC_CMD_GET_FEATURES */ +ZTEST_USER(espi, test_host_command_ec_cmd_get_features) +{ + struct ec_response_get_features response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_GET_FEATURES, 0, response); + + int rv = host_command_process(&args); + + zassert_equal(rv, EC_RES_SUCCESS, "Expected %d, but got %d", + EC_RES_SUCCESS, rv); + + /* Check features returned */ + uint32_t feature_mask; + + feature_mask = EC_FEATURE_MASK_0(EC_FEATURE_FLASH); + feature_mask |= EC_FEATURE_MASK_0(EC_FEATURE_MOTION_SENSE); + feature_mask |= EC_FEATURE_MASK_0(EC_FEATURE_KEYB); + zassert_true((response.flags[0] & feature_mask), + "Known features were not returned."); + feature_mask = EC_FEATURE_MASK_1(EC_FEATURE_UNIFIED_WAKE_MASKS); + feature_mask |= EC_FEATURE_MASK_1(EC_FEATURE_HOST_EVENT64); + feature_mask |= EC_FEATURE_MASK_1(EC_FEATURE_EXEC_IN_RAM); + zassert_true((response.flags[1] & feature_mask), + "Known features were not returned."); +} + +ZTEST(espi, test_hc_gpio_set_system_is_locked) +{ + struct ec_params_gpio_set params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_GPIO_SET, 0, params); + + system_is_locked_fake.return_val = 1; + zassert_equal(EC_RES_ACCESS_DENIED, host_command_process(&args), NULL); +} + +ZTEST(espi, test_hc_gpio_set_invalid_gpio_name) +{ + struct ec_params_gpio_set params = { + .name = "", + .val = 0, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_GPIO_SET, 0, params); + + zassert_equal(EC_RES_ERROR, host_command_process(&args), NULL); +} + +ZTEST_SUITE(espi, drivers_predicate_post_main, NULL, espi_before, espi_after, + NULL); diff --git a/zephyr/test/drivers/default/src/flash.c b/zephyr/test/drivers/default/src/flash.c new file mode 100644 index 0000000000..b49d21b997 --- /dev/null +++ b/zephyr/test/drivers/default/src/flash.c @@ -0,0 +1,444 @@ +/* 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/drivers/emul.h> +#include <zephyr/drivers/gpio.h> +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "ec_commands.h" +#include "emul/emul_flash.h" +#include "flash.h" +#include "host_command.h" +#include "system.h" +#include "test/drivers/test_state.h" + +#define WP_L_GPIO_PATH DT_PATH(named_gpios, wp_l) + +static int gpio_wp_l_set(int value) +{ + const struct device *wp_l_gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(WP_L_GPIO_PATH, gpios)); + + return gpio_emul_input_set(wp_l_gpio_dev, + DT_GPIO_PIN(WP_L_GPIO_PATH, gpios), value); +} + +ZTEST_USER(flash, test_hostcmd_flash_protect_wp_asserted) +{ + struct ec_response_flash_protect response; + struct ec_params_flash_protect params = { + .mask = 0, + .flags = 0, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_FLASH_PROTECT, 0, response, params); + /* The original flags not 0 as GPIO WP_L asserted */ + uint32_t expected_flags = EC_FLASH_PROTECT_GPIO_ASSERTED; + + /* Get the flash protect */ + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Enable RO_AT_BOOT */ + params.mask = EC_FLASH_PROTECT_RO_AT_BOOT; + params.flags = EC_FLASH_PROTECT_RO_AT_BOOT; + expected_flags |= EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Disable RO_AT_BOOT; should change nothing as GPIO WP_L is asserted */ + params.mask = EC_FLASH_PROTECT_RO_AT_BOOT; + params.flags = 0; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Enable ALL_NOW */ + params.mask = EC_FLASH_PROTECT_ALL_NOW; + params.flags = EC_FLASH_PROTECT_ALL_NOW; + expected_flags |= EC_FLASH_PROTECT_ALL_NOW; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Disable ALL_NOW; should change nothing as GPIO WP_L is asserted */ + params.mask = EC_FLASH_PROTECT_ALL_NOW; + params.flags = 0; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Disable RO_AT_BOOT; should change nothing as GPIO WP_L is asserted */ + params.mask = EC_FLASH_PROTECT_RO_AT_BOOT; + params.flags = 0; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); +} + +ZTEST_USER(flash, test_hostcmd_flash_protect_wp_deasserted) +{ + struct ec_response_flash_protect response; + struct ec_params_flash_protect params = { + .mask = 0, + .flags = 0, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_FLASH_PROTECT, 0, response, params); + /* The original flags 0 as GPIO WP_L deasserted */ + uint32_t expected_flags = 0; + + zassert_ok(gpio_wp_l_set(1), NULL); + + /* Get the flash protect */ + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Enable RO_AT_BOOT */ + params.mask = EC_FLASH_PROTECT_RO_AT_BOOT; + params.flags = EC_FLASH_PROTECT_RO_AT_BOOT; + expected_flags |= EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Disable RO_AT_BOOT */ + params.mask = EC_FLASH_PROTECT_RO_AT_BOOT; + params.flags = 0; + expected_flags &= + ~(EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW); + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Enable RO_AT_BOOT */ + params.mask = EC_FLASH_PROTECT_RO_AT_BOOT; + params.flags = EC_FLASH_PROTECT_RO_AT_BOOT; + expected_flags |= EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); + + /* Enable ALL_NOW; should change nothing as GPIO WP_L is deasserted */ + params.mask = EC_FLASH_PROTECT_ALL_NOW; + params.flags = EC_FLASH_PROTECT_ALL_NOW; + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flags, expected_flags, "response.flags = %d", + response.flags); +} + +#define TEST_BUF_SIZE 0x100 + +ZTEST_USER(flash, test_hostcmd_flash_write_and_erase) +{ + uint8_t in_buf[TEST_BUF_SIZE]; + uint8_t out_buf[sizeof(struct ec_params_flash_write) + TEST_BUF_SIZE]; + + struct ec_params_flash_read read_params = { + .offset = 0x10000, + .size = TEST_BUF_SIZE, + }; + struct host_cmd_handler_args read_args = + BUILD_HOST_COMMAND(EC_CMD_FLASH_READ, 0, in_buf, read_params); + + struct ec_params_flash_erase erase_params = { + .offset = 0x10000, + .size = 0x10000, + }; + struct host_cmd_handler_args erase_args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_FLASH_ERASE, 0, erase_params); + + /* The write host command structs need to be filled run-time */ + struct ec_params_flash_write *write_params = + (struct ec_params_flash_write *)out_buf; + struct host_cmd_handler_args write_args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_FLASH_WRITE, 0); + + write_params->offset = 0x10000; + write_params->size = TEST_BUF_SIZE; + write_args.params = write_params; + write_args.params_size = sizeof(*write_params) + TEST_BUF_SIZE; + + /* Flash write to all 0xec */ + memset(write_params + 1, 0xec, TEST_BUF_SIZE); + zassert_ok(host_command_process(&write_args), NULL); + + /* Flash read and compare the readback data */ + zassert_ok(host_command_process(&read_args), NULL); + zassert_equal(read_args.response_size, TEST_BUF_SIZE, NULL); + zassert_equal(in_buf[0], 0xec, "readback data not expected: 0x%x", + in_buf[0]); + zassert_equal(in_buf[TEST_BUF_SIZE - 1], 0xec, + "readback data not expected: 0x%x", in_buf[0]); + + /* Flash erase */ + zassert_ok(host_command_process(&erase_args), NULL); + + /* Flash read and compare the readback data */ + zassert_ok(host_command_process(&read_args), NULL); + zassert_equal(in_buf[0], 0xff, "readback data not expected: 0x%x", + in_buf[0]); + zassert_equal(in_buf[TEST_BUF_SIZE - 1], 0xff, + "readback data not expected: 0x%x", in_buf[0]); +} + +#define EC_FLASH_REGION_START \ + MIN(CONFIG_EC_PROTECTED_STORAGE_OFF, CONFIG_EC_WRITABLE_STORAGE_OFF) + +static void test_region_info(uint32_t region, uint32_t expected_offset, + uint32_t expected_size) +{ + struct ec_response_flash_region_info response; + struct ec_params_flash_region_info params = { + .region = region, + }; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND( + EC_CMD_FLASH_REGION_INFO, 1, response, params); + + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.offset, expected_offset, NULL); + zassert_equal(response.size, expected_size, NULL); +} + +ZTEST_USER(flash, test_hostcmd_flash_region_info_ro) +{ + test_region_info(EC_FLASH_REGION_RO, + CONFIG_EC_PROTECTED_STORAGE_OFF + + CONFIG_RO_STORAGE_OFF - EC_FLASH_REGION_START, + EC_FLASH_REGION_RO_SIZE); +} + +ZTEST_USER(flash, test_hostcmd_flash_region_info_active) +{ + test_region_info(EC_FLASH_REGION_ACTIVE, + flash_get_rw_offset(system_get_active_copy()) - + EC_FLASH_REGION_START, + CONFIG_EC_WRITABLE_STORAGE_SIZE); +} + +ZTEST_USER(flash, test_hostcmd_flash_region_info_active_wp_ro) +{ + test_region_info(EC_FLASH_REGION_WP_RO, + CONFIG_WP_STORAGE_OFF - EC_FLASH_REGION_START, + CONFIG_WP_STORAGE_SIZE); +} + +ZTEST_USER(flash, test_hostcmd_flash_region_info_active_update) +{ + test_region_info(EC_FLASH_REGION_UPDATE, + flash_get_rw_offset(system_get_update_copy()) - + EC_FLASH_REGION_START, + CONFIG_EC_WRITABLE_STORAGE_SIZE); +} + +ZTEST_USER(flash, test_hostcmd_flash_region_info_active_invalid) +{ + struct ec_response_flash_region_info response; + struct ec_params_flash_region_info params = { + /* Get an invalid region */ + .region = 10, + }; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND( + EC_CMD_FLASH_REGION_INFO, 1, response, params); + + zassert_equal(host_command_process(&args), EC_RES_INVALID_PARAM, NULL); +} + +ZTEST_USER(flash, test_hostcmd_flash_info) +{ + struct ec_response_flash_info_1 response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_FLASH_INFO, 1, response); + + /* Get the flash info. */ + zassert_ok(host_command_process(&args), NULL); + zassert_equal(response.flash_size, + CONFIG_FLASH_SIZE_BYTES - EC_FLASH_REGION_START, + "response.flash_size = %d", response.flash_size); + zassert_equal(response.flags, 0, "response.flags = %d", response.flags); + zassert_equal(response.write_block_size, CONFIG_FLASH_WRITE_SIZE, + "response.write_block_size = %d", + response.write_block_size); + zassert_equal(response.erase_block_size, CONFIG_FLASH_ERASE_SIZE, + "response.erase_block_size = %d", + response.erase_block_size); + zassert_equal(response.protect_block_size, CONFIG_FLASH_BANK_SIZE, + "response.protect_block_size = %d", + response.protect_block_size); + zassert_equal( + response.write_ideal_size, + (args.response_max - sizeof(struct ec_params_flash_write)) & + ~(CONFIG_FLASH_WRITE_SIZE - 1), + "response.write_ideal_size = %d", response.write_ideal_size); +} + +ZTEST_USER(flash, test_console_cmd_flashwp__invalid) +{ + /* Command requires a 2nd CLI arg */ + zassert_ok(!shell_execute_cmd(get_ec_shell(), "flashwp"), NULL); +} + +ZTEST_USER(flash, test_console_cmd_flashwp__now) +{ + uint32_t current; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp true"), NULL); + + current = crec_flash_get_protect(); + zassert_true(EC_FLASH_PROTECT_GPIO_ASSERTED & current, "current = %08x", + current); + zassert_true(EC_FLASH_PROTECT_RO_AT_BOOT & current, "current = %08x", + current); + + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp now"), NULL); + + current = crec_flash_get_protect(); + zassert_true(current & EC_FLASH_PROTECT_ALL_NOW, "current = %08x", + current); +} + +ZTEST_USER(flash, test_console_cmd_flashwp__all) +{ + uint32_t current; + + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp true"), NULL); + + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp all"), NULL); + + current = crec_flash_get_protect(); + zassert_true(EC_FLASH_PROTECT_ALL_NOW & current, "current = %08x", + current); +} + +ZTEST_USER(flash, test_console_cmd_flashwp__bool_false) +{ + uint32_t current; + + /* Set RO_AT_BOOT and verify */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp true"), NULL); + + current = crec_flash_get_protect(); + zassert_true(current & EC_FLASH_PROTECT_RO_AT_BOOT, "current = %08x", + current); + + gpio_wp_l_set(1); + + /* Now clear it */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp false"), NULL); + + current = crec_flash_get_protect(); + zassert_false(current & EC_FLASH_PROTECT_RO_AT_BOOT, "current = %08x", + current); +} + +ZTEST_USER(flash, test_console_cmd_flashwp__bool_true) +{ + uint32_t current; + + gpio_wp_l_set(1); + + zassert_ok(shell_execute_cmd(get_ec_shell(), "flashwp true"), NULL); + + current = crec_flash_get_protect(); + zassert_equal(EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW, + current, "current = %08x", current); +} + +ZTEST_USER(flash, test_console_cmd_flashwp__bad_param) +{ + zassert_ok(!shell_execute_cmd(get_ec_shell(), "flashwp xyz"), NULL); +} + +/** + * @brief Prepare a region of flash for the test_crec_flash_is_erased* tests + * + * @param offset Offset to write bytes at. + * @param size Number of bytes to erase. + * @param make_write If true, write an arbitrary byte after erase so the region + * is no longer fully erased. + */ +static void setup_flash_region_helper(uint32_t offset, uint32_t size, + bool make_write) +{ + struct ec_params_flash_erase erase_params = { + .offset = offset, + .size = size, + }; + struct host_cmd_handler_args erase_args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_FLASH_ERASE, 0, erase_params); + + zassume_ok(host_command_process(&erase_args), NULL); + + if (make_write) { + /* Sized for flash_write header plus one byte of data */ + uint8_t out_buf[sizeof(struct ec_params_flash_write) + + sizeof(uint8_t)]; + + struct ec_params_flash_write *write_params = + (struct ec_params_flash_write *)out_buf; + struct host_cmd_handler_args write_args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_FLASH_WRITE, 0); + + write_params->offset = offset; + write_params->size = 1; + write_args.params = write_params; + write_args.params_size = sizeof(out_buf); + + /* Write one byte at start of region */ + out_buf[sizeof(*write_params)] = 0xec; + + zassume_ok(host_command_process(&write_args), NULL); + } +} + +ZTEST_USER(flash, test_crec_flash_is_erased__happy) +{ + uint32_t offset = 0x10000; + + setup_flash_region_helper(offset, TEST_BUF_SIZE, false); + + zassert_true(crec_flash_is_erased(offset, TEST_BUF_SIZE), NULL); +} + +ZTEST_USER(flash, test_crec_flash_is_erased__not_erased) +{ + uint32_t offset = 0x10000; + + setup_flash_region_helper(offset, TEST_BUF_SIZE, true); + + zassert_true(!crec_flash_is_erased(offset, TEST_BUF_SIZE), NULL); +} + +static void flash_reset(void) +{ + /* Set the GPIO WP_L to default */ + gpio_wp_l_set(0); + + /* Reset the protection flags */ + cros_flash_emul_protect_reset(); +} + +static void flash_before(void *data) +{ + ARG_UNUSED(data); + flash_reset(); +} + +static void flash_after(void *data) +{ + ARG_UNUSED(data); + flash_reset(); + + /* The test modifies this bank. Erase it in case of failure. */ + crec_flash_erase(0x10000, 0x10000); +} + +ZTEST_SUITE(flash, drivers_predicate_post_main, NULL, flash_before, flash_after, + NULL); diff --git a/zephyr/test/drivers/default/src/gpio.c b/zephyr/test/drivers/default/src/gpio.c new file mode 100644 index 0000000000..acfa0de26e --- /dev/null +++ b/zephyr/test/drivers/default/src/gpio.c @@ -0,0 +1,421 @@ +/* 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. + */ + +/** + * @file + * @brief Unit Tests for GPIO. + */ + +#include <zephyr/device.h> + +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <zephyr/logging/log.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "common.h" +#include "ec_tasks.h" +#include "gpio.h" +#include "gpio/gpio.h" +#include "gpio/gpio_int.h" +#include "test/drivers/stubs.h" +#include "util.h" +#include "test/drivers/test_state.h" + +extern bool gpio_test_interrupt_triggered; +/** + * @brief TestPurpose: Verify Zephyr to EC GPIO bitmask conversion. + * + * @details + * Validate Zephyr to EC GPIO bitmask conversion. + * + * Expected Results + * - GPIO bitmask conversions are successful + */ +ZTEST(gpio, test_convert_from_zephyr_flags) +{ + int retval; + struct { + int zephyr_bmask; + gpio_flags_t expected_ec_bmask; + } validate[] = { + { GPIO_DISCONNECTED, GPIO_FLAG_NONE }, + { GPIO_OUTPUT_INIT_LOW, GPIO_LOW }, + { GPIO_OUTPUT_INIT_HIGH, GPIO_HIGH }, + { GPIO_VOLTAGE_1P8, GPIO_SEL_1P8V }, + { GPIO_INT_ENABLE, GPIO_FLAG_NONE }, + { GPIO_INT_ENABLE | GPIO_INT_EDGE, GPIO_FLAG_NONE }, + { GPIO_INT_ENABLE | GPIO_INT_EDGE | GPIO_INT_HIGH_1, + GPIO_INT_F_RISING }, + { GPIO_INT_ENABLE | GPIO_INT_EDGE | GPIO_INT_LOW_0, + GPIO_INT_F_FALLING }, + { GPIO_INT_ENABLE | GPIO_INT_HIGH_1, GPIO_INT_F_HIGH }, + { GPIO_INT_ENABLE | GPIO_INT_LOW_0, GPIO_INT_F_LOW }, + { GPIO_OUTPUT_INIT_LOGICAL, 0 }, + { GPIO_OPEN_DRAIN | GPIO_PULL_UP, + GPIO_OPEN_DRAIN | GPIO_PULL_UP }, + }; + int num_tests = ARRAY_SIZE(validate); + + for (int i = 0; i < num_tests; i++) { + retval = convert_from_zephyr_flags(validate[i].zephyr_bmask); + zassert_equal(validate[i].expected_ec_bmask, retval, + "[%d] Expected 0x%08X, returned 0x%08X.", i, + validate[i].expected_ec_bmask, retval); + } +} + +/** + * @brief TestPurpose: Verify EC to Zephyr GPIO bitmask conversion. + * + * @details + * Validate EC to Zephyr GPIO bitmask conversion. + * + * Expected Results + * - GPIO bitmask conversions are successful + */ +ZTEST(gpio, test_convert_to_zephyr_flags) +{ + gpio_flags_t retval; + + struct { + gpio_flags_t ec_bmask; + int expected_zephyr_bmask; + } validate[] = { + { GPIO_FLAG_NONE, GPIO_DISCONNECTED }, + { GPIO_LOW, GPIO_OUTPUT_INIT_LOW }, + { GPIO_HIGH, GPIO_OUTPUT_INIT_HIGH }, + { GPIO_INT_F_RISING, + GPIO_INT_ENABLE | GPIO_INT_EDGE | GPIO_INT_HIGH_1 }, + { GPIO_INT_F_FALLING, + GPIO_INT_ENABLE | GPIO_INT_EDGE | GPIO_INT_LOW_0 }, + { GPIO_INT_F_LOW, GPIO_INT_ENABLE | GPIO_INT_LOW_0 }, + { GPIO_INT_F_HIGH, GPIO_INT_ENABLE | GPIO_INT_HIGH_1 }, + { GPIO_SEL_1P8V, GPIO_VOLTAGE_1P8 }, + }; + int num_tests = ARRAY_SIZE(validate); + + for (int i = 0; i < num_tests; i++) { + retval = convert_to_zephyr_flags(validate[i].ec_bmask); + zassert_equal(validate[i].expected_zephyr_bmask, retval, + "[%d] Expected 0x%08X, returned 0x%08X.", i, + validate[i].expected_zephyr_bmask, retval); + } +} + +/** + * @brief TestPurpose: Verify GPIO signal_is_gpio. + * + * @details + * Validate signal_is_gpio + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_signal_is_gpio) +{ + zassert_true(signal_is_gpio(GPIO_SIGNAL(DT_NODELABEL(gpio_test))), + "Expected true"); +} + +/** + * @brief TestPurpose: Verify legacy API GPIO set/get level. + * + * @details + * Validate set/get level for legacy API + * This tests the legacy API, though no Zepyhr + * based code should use it. + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_legacy_gpio_get_set_level) +{ + enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_test)); + int level; + /* Test invalid signal */ + gpio_set_level(GPIO_COUNT, 0); + zassert_equal(0, gpio_get_level(GPIO_COUNT), "Expected level==0"); + /* Test valid signal */ + gpio_set_level(signal, 0); + zassert_equal(0, gpio_get_level(signal), "Expected level==0"); + gpio_set_level(signal, 1); + zassert_equal(1, gpio_get_level(signal), "Expected level==1"); + level = gpio_get_ternary(signal); + gpio_set_level_verbose(CC_CHIPSET, signal, 0); + zassert_equal(0, gpio_get_level(signal), "Expected level==0"); +} + +/** + * @brief TestPurpose: Verify legacy GPIO enable/disable interrupt. + * + * @details + * Validate gpio_enable_interrupt/gpio_disable_interrupt + * Uses the legacy API. No Zephyr code should use this API. + * + * Expected Results + * - Success + */ + +ZTEST(gpio, test_legacy_gpio_enable_interrupt) +{ + enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_test)); + + gpio_test_interrupt_triggered = false; + + /* Test invalid signal */ + zassert_not_equal(EC_SUCCESS, gpio_disable_interrupt(GPIO_COUNT), NULL); + zassert_not_equal(EC_SUCCESS, gpio_enable_interrupt(GPIO_COUNT), NULL); + zassert_false(gpio_test_interrupt_triggered, NULL); + + /* Test valid signal */ + zassert_ok(gpio_disable_interrupt(signal), NULL); + gpio_set_level(signal, 0); + zassert_false(gpio_test_interrupt_triggered, NULL); + gpio_set_level(signal, 1); + zassert_false(gpio_test_interrupt_triggered, NULL); + + zassert_ok(gpio_enable_interrupt(signal), NULL); + gpio_set_level(signal, 0); + zassert_true(gpio_test_interrupt_triggered, NULL); + gpio_test_interrupt_triggered = false; + gpio_set_level(signal, 1); + zassert_true(gpio_test_interrupt_triggered, NULL); +} + +/** + * @brief TestPurpose: Verify GPIO set/get level. + * + * @details + * Validate set/get level + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_gpio_pin_get_set_level) +{ + const struct gpio_dt_spec *gp = GPIO_DT_FROM_NODELABEL(gpio_test); + + /* Test invalid signal */ + zassert_equal(NULL, gpio_get_dt_spec(-1), "Expected NULL ptr"); + + zassert_false(gp == NULL, "Unexpected NULL ptr"); + /* Test valid signal */ + gpio_pin_set_dt(gp, 0); + zassert_equal(0, gpio_pin_get_dt(gp), "Expected level==0"); + + gpio_pin_set_dt(gp, 1); + zassert_equal(1, gpio_pin_get_dt(gp), "Expected level==1"); +} + +/** + * @brief TestPurpose: Verify GPIO get name. + * + * @details + * Validate gpio_get_name + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_gpio_get_name) +{ + enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_test)); + const char *signal_name; + + /* Test invalid signal */ + signal_name = gpio_get_name(GPIO_COUNT); + zassert_mem_equal("UNIMPLEMENTED", signal_name, strlen(signal_name), + "gpio_get_name returned a valid signal \'%s\'", + signal_name); + + /* Test valid signal */ + signal_name = gpio_get_name(signal); + zassert_mem_equal("test", signal_name, strlen(signal_name), + "gpio_get_name returned signal \'%s\'", signal_name); +} + +/** + * @brief Helper function to get GPIO flags + * + * @param signal + * @return gpio_flags_t + */ +gpio_flags_t gpio_helper_get_flags(enum gpio_signal signal) +{ + const struct gpio_dt_spec *spec; + gpio_flags_t flags; + + spec = gpio_get_dt_spec(signal); + gpio_emul_flags_get(spec->port, spec->pin, &flags); + + return flags; +} + +/** + * @brief TestPurpose: Verify GPIO get default flags. + * + * @details + * Validate gpio_get_default_flags + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_gpio_get_default_flags) +{ + enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_test)); + gpio_flags_t flags; + gpio_flags_t flags_at_start[GPIO_COUNT]; + int def_flags; + + /* Snapshot of GPIO flags before testing */ + for (int i = 0; i < GPIO_COUNT; i++) + flags_at_start[i] = gpio_helper_get_flags(i); + + /* Test invalid signal */ + def_flags = gpio_get_default_flags(GPIO_COUNT); + zassert_equal(0, def_flags, "Expected 0x0, returned 0x%08X", def_flags); + gpio_set_flags(GPIO_COUNT, GPIO_INPUT); + + /* Verify flags didn't change */ + for (int i = 0; i < GPIO_COUNT; i++) { + flags = gpio_helper_get_flags(i); + zassert_equal(flags_at_start[i], flags, + "%s[%d] flags_at_start=0x%x, flags=0x%x", + gpio_get_name(i), i, flags_at_start[i], flags); + } + + /* Test valid signal */ + def_flags = gpio_get_default_flags(signal); + zassert_equal(GPIO_INPUT | GPIO_OUTPUT, def_flags, + "Expected 0x%08x, returned 0x%08X", + GPIO_INPUT | GPIO_OUTPUT, def_flags); + + gpio_set_flags(signal, GPIO_INPUT); + flags = gpio_helper_get_flags(signal); + zassert_equal(flags, GPIO_INPUT, "Flags set 0x%x", flags); + + gpio_set_flags(signal, GPIO_OUTPUT); + flags = gpio_helper_get_flags(signal); + zassert_equal(flags, GPIO_OUTPUT, "Flags set 0x%x", flags); +} + +/** + * @brief TestPurpose: Verify GPIO no-auto-init. + * + * @details + * Validate no-auto-init device tree property, + * which will not initialise the GPIO at startup. + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_gpio_no_auto_init) +{ + const struct gpio_dt_spec *gp = GPIO_DT_FROM_NODELABEL(gpio_no_init); + enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_no_init)); + gpio_flags_t flags; + + flags = gpio_helper_get_flags(signal); + zassert_equal(0, flags, "Expected 0x%08x, returned 0x%08X", 0, flags); + + /* Configure pin. */ + gpio_pin_configure_dt(gp, GPIO_INPUT | GPIO_OUTPUT); + flags = gpio_helper_get_flags(signal); + zassert_equal(flags, (GPIO_ACTIVE_LOW | GPIO_OUTPUT | GPIO_INPUT), + "Flags set 0x%x", flags); +} + +/** + * @brief TestPurpose: Verify GPIO reset. + * + * @details + * Validate gpio_reset + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_gpio_reset) +{ + enum gpio_signal signal = GPIO_SIGNAL(DT_NODELABEL(gpio_test)); + gpio_flags_t flags; + gpio_flags_t flags_at_start[GPIO_COUNT]; + + /* Snapshot of GPIO flags before testing */ + for (int i = 0; i < GPIO_COUNT; i++) + flags_at_start[i] = gpio_helper_get_flags(i); + + /* Test reset on invalid signal */ + gpio_reset(GPIO_COUNT); + + /* Verify flags didn't change */ + for (int i = 0; i < GPIO_COUNT; i++) { + flags = gpio_helper_get_flags(i); + zassert_equal(flags_at_start[i], flags, + "%s[%d] flags_at_start=0x%x, flags=0x%x", + gpio_get_name(i), i, flags_at_start[i], flags); + } + + /* Test reset on valid signal */ + gpio_set_flags(signal, GPIO_OUTPUT); + flags = gpio_helper_get_flags(signal); + zassert_equal(flags, GPIO_OUTPUT, "Flags set 0x%x", flags); + + gpio_reset(signal); + + flags = gpio_helper_get_flags(signal); + zassert_equal(flags, gpio_get_default_flags(signal), "Flags set 0x%x", + flags); +} + +/** + * @brief TestPurpose: Verify GPIO enable/disable interrupt. + * + * @details + * Validate gpio_enable_dt_interrupt + * + * Expected Results + * - Success + */ +ZTEST(gpio, test_gpio_enable_dt_interrupt) +{ + const struct gpio_dt_spec *gp = GPIO_DT_FROM_NODELABEL(gpio_test); + const struct gpio_int_config *intr = + GPIO_INT_FROM_NODELABEL(int_gpio_test); + + gpio_test_interrupt_triggered = false; + + /* Test valid signal */ + zassert_ok(gpio_disable_dt_interrupt(intr), NULL); + gpio_pin_set_dt(gp, 0); + zassert_false(gpio_test_interrupt_triggered, NULL); + gpio_pin_set_dt(gp, 1); + zassert_false(gpio_test_interrupt_triggered, NULL); + + zassert_ok(gpio_enable_dt_interrupt(intr), NULL); + gpio_pin_set_dt(gp, 0); + zassert_true(gpio_test_interrupt_triggered, NULL); + gpio_test_interrupt_triggered = false; + gpio_pin_set_dt(gp, 1); + zassert_true(gpio_test_interrupt_triggered, NULL); +} + +/** + * @brief GPIO test setup handler. + */ +static void gpio_before(void *state) +{ + ARG_UNUSED(state); + /** TODO: Reset all signals here. Currently other tests fail when reset + * for(int i = 0; i < GPIO_COUNT; i++) { + * gpio_reset(i); + * } + */ + gpio_reset(GPIO_SIGNAL(DT_NODELABEL(gpio_test))); +} + +/** + * @brief Test Suite: Verifies GPIO functionality. + */ +ZTEST_SUITE(gpio, drivers_predicate_post_main, NULL, gpio_before, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/i2c.c b/zephyr/test/drivers/default/src/i2c.c new file mode 100644 index 0000000000..caced4aedf --- /dev/null +++ b/zephyr/test/drivers/default/src/i2c.c @@ -0,0 +1,145 @@ +/* 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 "ec_commands.h" +#include "host_command.h" +#include "i2c.h" +#include "test/drivers/test_state.h" + +ZTEST_USER(i2c, test_i2c_set_speed_success) +{ + struct ec_response_i2c_control response; + struct ec_params_i2c_control get_params = { + .port = I2C_PORT_USB_C0, + .cmd = EC_I2C_CONTROL_GET_SPEED, + }; + struct host_cmd_handler_args get_args = + BUILD_HOST_COMMAND(EC_CMD_I2C_CONTROL, 0, response, get_params); + struct ec_params_i2c_control set_params = { + .port = I2C_PORT_USB_C0, + .cmd = EC_I2C_CONTROL_SET_SPEED, + }; + struct host_cmd_handler_args set_args = + BUILD_HOST_COMMAND(EC_CMD_I2C_CONTROL, 0, response, set_params); + + /* Get the speed: 100. */ + zassert_ok(host_command_process(&get_args), NULL); + zassert_ok(get_args.result, NULL); + zassert_equal(get_args.response_size, sizeof(response), NULL); + zassert_equal(response.cmd_response.speed_khz, 100, + "response.cmd_response.speed_khz = %d", + response.cmd_response.speed_khz); + + /* Set the speed to 400. */ + set_params.cmd_params.speed_khz = 400; + zassert_ok(host_command_process(&set_args), NULL); + zassert_ok(set_args.result, NULL); + zassert_equal(set_args.response_size, sizeof(response), NULL); + zassert_equal(response.cmd_response.speed_khz, 100, + "response.cmd_response.speed_khz = %d", + response.cmd_response.speed_khz); + + /* Get the speed to verify. */ + zassert_ok(host_command_process(&get_args), NULL); + zassert_ok(get_args.result, NULL); + zassert_equal(get_args.response_size, sizeof(response), NULL); + zassert_equal(response.cmd_response.speed_khz, 400, + "response.cmd_response.speed_khz = %d", + response.cmd_response.speed_khz); + + /* Set the speed back to 100. */ + set_params.cmd_params.speed_khz = 100; + zassert_ok(host_command_process(&set_args), NULL); + zassert_ok(set_args.result, NULL); + zassert_equal(set_args.response_size, sizeof(response), NULL); + zassert_equal(response.cmd_response.speed_khz, 400, + "response.cmd_response.speed_khz = %d", + response.cmd_response.speed_khz); +} + +ZTEST_USER(i2c, test_i2c_set_speed_not_dynamic) +{ + struct ec_response_i2c_control response; + struct ec_params_i2c_control set_params = { + .port = I2C_PORT_POWER, + .cmd = EC_I2C_CONTROL_SET_SPEED, + .cmd_params.speed_khz = 400, + }; + struct host_cmd_handler_args set_args = + BUILD_HOST_COMMAND(EC_CMD_I2C_CONTROL, 0, response, set_params); + + /* Set the speed to 400 on a bus which doesn't support dynamic-speed. */ + zassert_equal(EC_RES_ERROR, host_command_process(&set_args), NULL); +} + +ZTEST_USER(i2c, test_i2c_control_wrong_port) +{ + struct ec_response_i2c_control response; + struct ec_params_i2c_control get_params = { + .port = 10, + .cmd = EC_I2C_CONTROL_GET_SPEED, + }; + struct host_cmd_handler_args get_args = + BUILD_HOST_COMMAND(EC_CMD_I2C_CONTROL, 0, response, get_params); + + /* Set the .port=10, which is not defined. */ + zassert_equal(EC_RES_INVALID_PARAM, host_command_process(&get_args), + NULL); +} + +ZTEST_USER(i2c, test_i2c_control_wrong_cmd) +{ + struct ec_response_i2c_control response; + struct ec_params_i2c_control params = { + .port = I2C_PORT_USB_C0, + .cmd = 10, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_I2C_CONTROL, 0, response, params); + + /* Call the .cmd=10, which is not defined. */ + zassert_equal(EC_RES_INVALID_COMMAND, host_command_process(&args), + NULL); +} + +ZTEST_USER(i2c, test_i2c_set_speed_wrong_freq) +{ + struct ec_response_i2c_control response; + struct ec_params_i2c_control set_params = { + .port = I2C_PORT_USB_C0, + .cmd = EC_I2C_CONTROL_SET_SPEED, + .cmd_params.speed_khz = 123, + }; + struct host_cmd_handler_args set_args = + BUILD_HOST_COMMAND(EC_CMD_I2C_CONTROL, 0, response, set_params); + + /* Set the speed to 123 KHz (an invalid speed). */ + zassert_equal(EC_RES_INVALID_PARAM, host_command_process(&set_args), + NULL); +} + +static void i2c_freq_reset(void) +{ + /* The test modifies this port. Reset it to the DTS defined. */ + zassert_ok(i2c_set_freq(I2C_PORT_USB_C0, I2C_FREQ_100KHZ), NULL); +} + +static void *i2c_setup(void) +{ + i2c_freq_reset(); + return NULL; +} + +static void i2c_teardown(void *state) +{ + ARG_UNUSED(state); + i2c_freq_reset(); +} + +ZTEST_SUITE(i2c, drivers_predicate_post_main, i2c_setup, NULL, NULL, + i2c_teardown); diff --git a/zephyr/test/drivers/default/src/i2c_passthru.c b/zephyr/test/drivers/default/src/i2c_passthru.c new file mode 100644 index 0000000000..aea81fc198 --- /dev/null +++ b/zephyr/test/drivers/default/src/i2c_passthru.c @@ -0,0 +1,123 @@ +/* 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 "driver/ln9310.h" +#include "ec_commands.h" +#include "host_command.h" +#include "i2c.h" +#include "test/drivers/test_state.h" + +ZTEST_USER(i2c_passthru, test_read_without_write) +{ + uint8_t param_buf[sizeof(struct ec_params_i2c_passthru) + + sizeof(struct ec_params_i2c_passthru_msg)]; + uint8_t response_buf[sizeof(struct ec_response_i2c_passthru) + 2]; + struct ec_params_i2c_passthru *params = + (struct ec_params_i2c_passthru *)¶m_buf; + struct ec_response_i2c_passthru *response = + (struct ec_response_i2c_passthru *)&response_buf; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_I2C_PASSTHRU, 0); + + params->port = I2C_PORT_POWER; + params->num_msgs = 1; + params->msg[0].addr_flags = LN9310_I2C_ADDR_0_FLAGS | EC_I2C_FLAG_READ; + params->msg[0].len = 1; + args.params = ¶m_buf; + args.params_size = sizeof(param_buf); + args.response = &response_buf; + args.response_max = sizeof(response_buf); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(response->i2c_status, EC_I2C_STATUS_NAK, NULL); + zassert_equal(args.response_size, + sizeof(struct ec_response_i2c_passthru), NULL); +} + +ZTEST_USER(i2c_passthru, test_passthru_protect) +{ + struct ec_response_i2c_passthru_protect response; + struct ec_params_i2c_passthru_protect status_params = { + .port = I2C_PORT_SENSOR, + .subcmd = EC_CMD_I2C_PASSTHRU_PROTECT_STATUS, + }; + struct host_cmd_handler_args status_args = BUILD_HOST_COMMAND( + EC_CMD_I2C_PASSTHRU_PROTECT, 0, response, status_params); + struct ec_params_i2c_passthru_protect enable_params = { + .port = I2C_PORT_SENSOR, + .subcmd = EC_CMD_I2C_PASSTHRU_PROTECT_ENABLE, + }; + struct host_cmd_handler_args enable_args = BUILD_HOST_COMMAND_PARAMS( + EC_CMD_I2C_PASSTHRU_PROTECT, 0, enable_params); + + /* Check the protect status: 0 (unprotected) */ + zassert_ok(host_command_process(&status_args), NULL); + zassert_ok(status_args.result, NULL); + zassert_equal(status_args.response_size, sizeof(response), NULL); + zassert_equal(response.status, 0, "response.status = %d", + response.status); + + /* Protect the bus */ + zassert_ok(host_command_process(&enable_args), NULL); + zassert_ok(enable_args.result, NULL); + + /* Check the protect status: 1 (protected) */ + zassert_ok(host_command_process(&status_args), NULL); + zassert_ok(status_args.result, NULL); + zassert_equal(status_args.response_size, sizeof(response), NULL); + zassert_equal(response.status, 1, "response.status = %d", + response.status); + + /* Error case: wrong subcmd */ + status_params.subcmd = 10; + zassert_equal(host_command_process(&status_args), + EC_RES_INVALID_COMMAND, NULL); + status_params.subcmd = EC_CMD_I2C_PASSTHRU_PROTECT_STATUS; + + /* Error case: wrong port */ + status_params.port = 10; + zassert_equal(host_command_process(&status_args), EC_RES_INVALID_PARAM, + NULL); + status_params.port = I2C_PORT_SENSOR; + + /* Error case: response size not enough */ + status_args.response_max = 0; + zassert_equal(host_command_process(&status_args), EC_RES_INVALID_PARAM, + NULL); + status_args.response_max = sizeof(response); + + /* Error case: params size not enough */ + status_args.params_size = 0; + zassert_equal(host_command_process(&status_args), EC_RES_INVALID_PARAM, + NULL); + status_args.params_size = sizeof(status_params); +} + +ZTEST_USER(i2c_passthru, test_passthru_protect_tcpcs) +{ + struct ec_params_i2c_passthru_protect enable_params = { + .port = I2C_PORT_SENSOR, + .subcmd = EC_CMD_I2C_PASSTHRU_PROTECT_ENABLE_TCPCS, + }; + struct host_cmd_handler_args enable_args = BUILD_HOST_COMMAND_PARAMS( + EC_CMD_I2C_PASSTHRU_PROTECT, 0, enable_params); + + /* Protect the all TCPC buses */ + zassert_ok(host_command_process(&enable_args), NULL); + zassert_ok(enable_args.result, NULL); +} + +static void i2c_passthru_after(void *state) +{ + ARG_UNUSED(state); + i2c_passthru_protect_reset(); +} + +ZTEST_SUITE(i2c_passthru, drivers_predicate_post_main, NULL, NULL, + i2c_passthru_after, NULL); 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)); +} diff --git a/zephyr/test/drivers/default/src/isl923x.c b/zephyr/test/drivers/default/src/isl923x.c new file mode 100644 index 0000000000..9144730887 --- /dev/null +++ b/zephyr/test/drivers/default/src/isl923x.c @@ -0,0 +1,1076 @@ +/* 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/ztest.h> +#include <zephyr/drivers/emul.h> +#include <zephyr/fff.h> + +#include "battery.h" +#include "battery_smart.h" +#include "test/drivers/charger_utils.h" +#include "driver/charger/isl923x.h" +#include "driver/charger/isl923x_public.h" +#include "emul/emul_common_i2c.h" +#include "emul/emul_isl923x.h" +#include "system.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" + +BUILD_ASSERT(CONFIG_CHARGER_SENSE_RESISTOR == 10 || + CONFIG_CHARGER_SENSE_RESISTOR == 5); + +BUILD_ASSERT(CONFIG_CHARGER_SENSE_RESISTOR_AC == 20 || + CONFIG_CHARGER_SENSE_RESISTOR_AC == 10); + +BUILD_ASSERT(IS_ENABLED(CONFIG_CHARGER_ISL9238), + "Must test on ISL9238; ISL9237, ISL9238c, and RAA489000 are not" + " yet supported"); + +#if CONFIG_CHARGER_SENSE_RESISTOR == 10 +#define EXPECTED_CURRENT_MA(n) (n) +#define EXPECTED_CURRENT_REG(n) (n) +#else +#define EXPECTED_CURRENT_MA(n) (n * 2) +#define EXPECTED_CURRENT_REG(n) (n / 2) +#endif + +#if CONFIG_CHARGER_SENSE_RESISTOR_AC == 20 +#define EXPECTED_INPUT_CURRENT_MA(n) (n) +#define EXPECTED_INPUT_CURRENT_REG(n) (n) +#else +#define EXPECTED_INPUT_CURRENT_MA(n) (n * 2) +#define EXPECTED_INPUT_CURRENT_REG(n) (n / 2) +#endif + +#define CHARGER_NUM get_charger_num(&isl923x_drv) +#define ISL923X_EMUL EMUL_DT_GET(DT_NODELABEL(isl923x_emul)) +#define COMMON_DATA emul_isl923x_get_i2c_common_data(ISL923X_EMUL) + +static int mock_write_fn_always_fail(const struct emul *emul, int reg, + uint8_t val, int bytes, void *data) +{ + ztest_test_fail(); + return 0; +} + +ZTEST(isl923x, test_isl923x_set_current) +{ + int expected_current_milli_amps[] = { + EXPECTED_CURRENT_MA(0), EXPECTED_CURRENT_MA(4), + EXPECTED_CURRENT_MA(8), EXPECTED_CURRENT_MA(16), + EXPECTED_CURRENT_MA(32), EXPECTED_CURRENT_MA(64), + EXPECTED_CURRENT_MA(128), EXPECTED_CURRENT_MA(256), + EXPECTED_CURRENT_MA(512), EXPECTED_CURRENT_MA(1024), + EXPECTED_CURRENT_MA(2048), EXPECTED_CURRENT_MA(4096) + }; + int current_milli_amps; + + /* Test I2C failure when reading charge current */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CHG_CURRENT); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.get_current(CHARGER_NUM, ¤t_milli_amps), + NULL); + + /* Reset fail register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + for (int i = 0; i < ARRAY_SIZE(expected_current_milli_amps); ++i) { + zassert_ok(isl923x_drv.set_current( + CHARGER_NUM, expected_current_milli_amps[i]), + "Failed to set the current to %dmA", + expected_current_milli_amps[i]); + zassert_ok(isl923x_drv.get_current(CHARGER_NUM, + ¤t_milli_amps), + "Failed to get current"); + zassert_equal(expected_current_milli_amps[i], + current_milli_amps, + "Expected current %dmA but got %dmA", + expected_current_milli_amps[i], + current_milli_amps); + } +} + +ZTEST(isl923x, test_isl923x_set_voltage) +{ + int expected_voltage_milli_volts[] = { 8, 16, 32, 64, + 128, 256, 512, 1024, + 2048, 4096, 8192, 16384 }; + int voltage_milli_volts; + + /* Test 0mV first, it's a special case because of voltage_min */ + zassert_ok(isl923x_drv.set_voltage(CHARGER_NUM, 0), + "Failed to set the voltage to 0mV"); + zassert_ok(isl923x_drv.get_voltage(CHARGER_NUM, &voltage_milli_volts), + "Failed to get voltage"); + zassert_equal(battery_get_info()->voltage_min, voltage_milli_volts, + "Expected voltage %dmV but got %dmV", + battery_get_info()->voltage_min, voltage_milli_volts); + + for (int i = 0; i < ARRAY_SIZE(expected_voltage_milli_volts); ++i) { + zassert_ok(isl923x_drv.set_voltage( + CHARGER_NUM, + expected_voltage_milli_volts[i]), + "Failed to set the voltage to %dmV", + expected_voltage_milli_volts[i]); + zassert_ok(isl923x_drv.get_voltage(CHARGER_NUM, + &voltage_milli_volts), + "Failed to get voltage"); + zassert_equal(expected_voltage_milli_volts[i], + voltage_milli_volts, + "Expected voltage %dmV but got %dmV", + expected_voltage_milli_volts[i], + voltage_milli_volts); + } +} + +ZTEST(isl923x, test_isl923x_set_input_current_limit) +{ + int expected_current_milli_amps[] = { EXPECTED_INPUT_CURRENT_MA(0), + EXPECTED_INPUT_CURRENT_MA(4), + EXPECTED_INPUT_CURRENT_MA(8), + EXPECTED_INPUT_CURRENT_MA(16), + EXPECTED_INPUT_CURRENT_MA(32), + EXPECTED_INPUT_CURRENT_MA(64), + EXPECTED_INPUT_CURRENT_MA(128), + EXPECTED_INPUT_CURRENT_MA(256), + EXPECTED_INPUT_CURRENT_MA(512), + EXPECTED_INPUT_CURRENT_MA(1024), + EXPECTED_INPUT_CURRENT_MA(2048), + EXPECTED_INPUT_CURRENT_MA(4096) }; + int current_milli_amps; + + /* Test failing to write to current limit 1 reg */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + ISL923X_REG_ADAPTER_CURRENT_LIMIT1); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.set_input_current_limit(CHARGER_NUM, 0), + NULL); + + /* Test failing to write to current limit 2 reg */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + ISL923X_REG_ADAPTER_CURRENT_LIMIT2); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.set_input_current_limit(CHARGER_NUM, 0), + NULL); + + /* Reset fail register */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test failing to read current limit 1 reg */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + ISL923X_REG_ADAPTER_CURRENT_LIMIT1); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.get_input_current_limit(CHARGER_NUM, + ¤t_milli_amps), + NULL); + + /* Reset fail register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test normal code path */ + for (int i = 0; i < ARRAY_SIZE(expected_current_milli_amps); ++i) { + zassert_ok(isl923x_drv.set_input_current_limit( + CHARGER_NUM, expected_current_milli_amps[i]), + "Failed to set input current limit to %dmV", + expected_current_milli_amps[i]); + zassert_ok(isl923x_drv.get_input_current_limit( + CHARGER_NUM, ¤t_milli_amps), + "Failed to get input current limit"); + zassert_equal(expected_current_milli_amps[i], + current_milli_amps, + "Expected input current %dmA but got %dmA", + expected_current_milli_amps[i], + current_milli_amps); + } +} + +ZTEST(isl923x, test_isl923x_psys) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "psys"), NULL); +} + +ZTEST(isl923x, test_manufacturer_id) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + int id; + + isl923x_emul_set_manufacturer_id(isl923x_emul, 0x1234); + zassert_ok(isl923x_drv.manufacturer_id(CHARGER_NUM, &id), NULL); + zassert_equal(0x1234, id, NULL); + + /* Test read error */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + ISL923X_REG_MANUFACTURER_ID); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.manufacturer_id(CHARGER_NUM, &id), NULL); + + /* Reset fail register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST(isl923x, test_device_id) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + int id; + + isl923x_emul_set_device_id(isl923x_emul, 0x5678); + zassert_ok(isl923x_drv.device_id(CHARGER_NUM, &id), NULL); + zassert_equal(0x5678, id, NULL); + + /* Test read error */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_DEVICE_ID); + zassert_equal(EC_ERROR_INVAL, isl923x_drv.device_id(CHARGER_NUM, &id), + NULL); + + /* Reset fail register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST(isl923x, test_options) +{ + uint32_t option; + + /* Test failed control 0 read */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL0); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.get_option(CHARGER_NUM, &option), NULL); + + /* Test failed control 1 read */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL1); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.get_option(CHARGER_NUM, &option), NULL); + + /* Reset failed read */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test failed control 0 write */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL0); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.set_option(CHARGER_NUM, option), NULL); + + /* Test failed control 1 write */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL1); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.set_option(CHARGER_NUM, option), NULL); + + /* Reset failed write */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test normal write/read, note that bits 23 and 0 are always 0 */ + zassert_ok(isl923x_drv.set_option(CHARGER_NUM, 0xffffffff), NULL); + zassert_ok(isl923x_drv.get_option(CHARGER_NUM, &option), NULL); + zassert_equal(0xff7ffffe, option, + "Expected options 0xff7ffffe but got 0x%x", option); +} + +ZTEST(isl923x, test_get_info) +{ + const struct charger_info *info = isl923x_drv.get_info(CHARGER_NUM); + + zassert_ok(strcmp("isl9238", info->name), NULL); + zassert_equal(ISL9238_SYS_VOLTAGE_REG_MAX, info->voltage_max, NULL); + zassert_equal(ISL923X_SYS_VOLTAGE_REG_MIN, info->voltage_min, NULL); + zassert_equal(8, info->voltage_step, NULL); + zassert_equal(EXPECTED_CURRENT_MA(6080), info->current_max, NULL); + zassert_equal(EXPECTED_CURRENT_MA(4), info->current_min, NULL); + zassert_equal(EXPECTED_CURRENT_MA(4), info->current_step, NULL); + zassert_equal(EXPECTED_INPUT_CURRENT_MA(6080), info->input_current_max, + NULL); + zassert_equal(EXPECTED_INPUT_CURRENT_MA(4), info->input_current_min, + NULL); + zassert_equal(EXPECTED_INPUT_CURRENT_MA(4), info->input_current_step, + NULL); +} + +ZTEST(isl923x, test_status) +{ + int status; + + zassert_ok(isl923x_drv.get_status(CHARGER_NUM, &status), NULL); + zassert_equal(CHARGER_LEVEL_2, status, NULL); +} + +ZTEST(isl923x, test_set_mode) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + + /* Enable learn mode and set mode (actual value doesn't matter) */ + zassert_ok(isl923x_drv.discharge_on_ac(CHARGER_NUM, true), NULL); + zassert_ok(isl923x_drv.set_mode(CHARGER_NUM, 0), NULL); + /* Learn mode should still be set */ + zassert_true(isl923x_emul_is_learn_mode_enabled(isl923x_emul), NULL); + + /* Disable learn mode, but keep the bits */ + zassert_ok(isl923x_drv.discharge_on_ac(CHARGER_NUM, false), NULL); + isl923x_emul_set_learn_mode_enabled(isl923x_emul, true); + zassert_ok(isl923x_drv.set_mode(CHARGER_NUM, 0), NULL); + /* Learn mode should still be off */ + zassert_true(!isl923x_emul_is_learn_mode_enabled(isl923x_emul), NULL); +} + +ZTEST(isl923x, test_post_init) +{ + zassert_ok(isl923x_drv.post_init(CHARGER_NUM), NULL); +} + +ZTEST(isl923x, test_set_ac_prochot) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + const struct device *i2c_dev = isl923x_emul_get_parent(isl923x_emul); + uint16_t expected_current_milli_amps[] = { + EXPECTED_INPUT_CURRENT_MA(0), + EXPECTED_INPUT_CURRENT_MA(128), + EXPECTED_INPUT_CURRENT_MA(256), + EXPECTED_INPUT_CURRENT_MA(512), + EXPECTED_INPUT_CURRENT_MA(1024), + EXPECTED_INPUT_CURRENT_MA(2048), + EXPECTED_INPUT_CURRENT_MA(4096) + }; + uint16_t current_milli_amps; + + /* Test can't set current above max */ + zassert_equal(EC_ERROR_INVAL, + isl923x_set_ac_prochot( + CHARGER_NUM, ISL923X_AC_PROCHOT_CURRENT_MAX + 1), + NULL); + + /* Test failed I2C write to prochot register */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_PROCHOT_AC); + zassert_equal(EC_ERROR_INVAL, isl923x_set_ac_prochot(CHARGER_NUM, 0), + NULL); + + /* Clear write fail reg */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + for (int i = 0; i < ARRAY_SIZE(expected_current_milli_amps); ++i) { + uint8_t reg_addr = ISL923X_REG_PROCHOT_AC; + + /* + * Due to resistor multiplying the current, the upper end of the + * test data might be out of bounds (which is already tested + * above). Skip the test. + */ + if (expected_current_milli_amps[i] > + ISL923X_AC_PROCHOT_CURRENT_MAX) { + continue; + } + + zassert_ok(isl923x_set_ac_prochot( + CHARGER_NUM, expected_current_milli_amps[i]), + "Failed to set AC prochot to %dmA", + expected_current_milli_amps[i]); + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), + ¤t_milli_amps, + sizeof(current_milli_amps)), + "Failed to read AC prochot register"); + zassert_equal(EXPECTED_INPUT_CURRENT_REG( + expected_current_milli_amps[i]), + current_milli_amps, + "AC prochot expected %dmA but got %dmA", + EXPECTED_INPUT_CURRENT_REG( + expected_current_milli_amps[i]), + current_milli_amps); + } +} +ZTEST(isl923x, test_set_dc_prochot) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + const struct device *i2c_dev = isl923x_emul_get_parent(isl923x_emul); + uint16_t expected_current_milli_amps[] = { + EXPECTED_CURRENT_MA(256), EXPECTED_CURRENT_MA(512), + EXPECTED_CURRENT_MA(1024), EXPECTED_CURRENT_MA(2048), + EXPECTED_CURRENT_MA(4096), EXPECTED_CURRENT_MA(8192) + }; + uint16_t current_milli_amps; + + /* Test can't set current above max */ + zassert_equal(EC_ERROR_INVAL, + isl923x_set_dc_prochot( + CHARGER_NUM, ISL923X_DC_PROCHOT_CURRENT_MAX + 1), + NULL); + + /* Test failed I2C write to prochot register */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_PROCHOT_DC); + zassert_equal(EC_ERROR_INVAL, isl923x_set_dc_prochot(CHARGER_NUM, 0), + NULL); + + /* Clear write fail reg */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + for (int i = 0; i < ARRAY_SIZE(expected_current_milli_amps); ++i) { + uint8_t reg_addr = ISL923X_REG_PROCHOT_DC; + + /* + * Due to resistor multiplying the current, the upper end of the + * test data might be out of bounds (which is already tested + * above). Skip the test. + */ + if (expected_current_milli_amps[i] > + ISL923X_DC_PROCHOT_CURRENT_MAX) { + continue; + } + zassert_ok(isl923x_set_dc_prochot( + CHARGER_NUM, expected_current_milli_amps[i]), + "Failed to set DC prochot to %dmA", + expected_current_milli_amps[i]); + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), + ¤t_milli_amps, + sizeof(current_milli_amps)), + "Failed to read DC prochot register"); + zassert_equal( + EXPECTED_CURRENT_REG(expected_current_milli_amps[i]), + current_milli_amps, + "AC prochot expected %dmA but got %dmA", + EXPECTED_CURRENT_REG(expected_current_milli_amps[i]), + current_milli_amps); + } +} + +ZTEST(isl923x, test_comparator_inversion) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + const struct device *i2c_dev = isl923x_emul_get_parent(isl923x_emul); + uint8_t reg_addr = ISL923X_REG_CONTROL2; + uint16_t reg_value; + uint8_t tx_buf[] = { reg_addr, 0, 0 }; + + /* Test failed read, should not write */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL2); + i2c_common_emul_set_write_func(COMMON_DATA, mock_write_fn_always_fail, + NULL); + zassert_equal(EC_ERROR_INVAL, + isl923x_set_comparator_inversion(CHARGER_NUM, false), + NULL); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_func(COMMON_DATA, NULL, NULL); + + /* Test failed write */ + zassert_ok(i2c_write(i2c_dev, tx_buf, sizeof(tx_buf), + isl923x_emul->bus.i2c->addr), + "Failed to clear CTRL2 register"); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL2); + zassert_equal(EC_ERROR_INVAL, + isl923x_set_comparator_inversion(CHARGER_NUM, true), + NULL); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test enable comparator inversion */ + zassert_ok(isl923x_set_comparator_inversion(CHARGER_NUM, true), NULL); + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), ®_value, + sizeof(reg_value)), + "Failed to read CTRL 2 register"); + zassert_true((reg_value & ISL923X_C2_INVERT_CMOUT) != 0, NULL); + + /* Test disable comparator inversion */ + zassert_ok(isl923x_set_comparator_inversion(CHARGER_NUM, false), NULL); + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), ®_value, + sizeof(reg_value)), + "Failed to read CTRL 2 register"); + zassert_true((reg_value & ISL923X_C2_INVERT_CMOUT) == 0, NULL); +} + +ZTEST(isl923x, test_discharge_on_ac) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + const struct device *i2c_dev = isl923x_emul_get_parent(isl923x_emul); + const struct i2c_common_emul_cfg *cfg = + isl923x_emul_get_cfg(isl923x_emul); + uint8_t reg_addr = ISL923X_REG_CONTROL1; + uint8_t tx_buf[] = { reg_addr, 0, 0 }; + uint16_t reg_value; + + /* Test failure to read CTRL1 register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL1); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.discharge_on_ac(CHARGER_NUM, true), NULL); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set CTRL1 register to 0 */ + zassert_ok(i2c_write(i2c_dev, tx_buf, sizeof(tx_buf), cfg->addr), NULL); + + /* Test failure to write CTRL1 register */ + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL1); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.discharge_on_ac(CHARGER_NUM, true), NULL); + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), ®_value, + sizeof(reg_value)), + NULL); + zassert_equal(0, reg_value, NULL); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test enabling discharge on AC */ + zassert_ok(isl923x_drv.discharge_on_ac(CHARGER_NUM, true), NULL); + + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), ®_value, + sizeof(reg_value)), + NULL); + zassert_true((reg_value & ISL923X_C1_LEARN_MODE_ENABLE) != 0, NULL); + + /* Test disabling discharge on AC */ + zassert_ok(isl923x_drv.discharge_on_ac(CHARGER_NUM, false), NULL); + + zassert_ok(i2c_write_read(i2c_dev, isl923x_emul->bus.i2c->addr, + ®_addr, sizeof(reg_addr), ®_value, + sizeof(reg_value)), + NULL); + zassert_true((reg_value & ISL923X_C1_LEARN_MODE_ENABLE) == 0, NULL); +} + +ZTEST(isl923x, test_get_vbus_voltage) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + /* Standard fixed-power PD source voltages. */ + int test_voltage_mv[] = { 5000, 9000, 15000, 20000 }; + int voltage; + + /* Test fail to read the ADC vbus register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, RAA489000_REG_ADC_VBUS); + zassert_equal(EC_ERROR_INVAL, + isl923x_drv.get_vbus_voltage(CHARGER_NUM, 0, &voltage), + NULL); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + for (int i = 0; i < ARRAY_SIZE(test_voltage_mv); ++i) { + int expected_voltage_mv = test_voltage_mv[i]; + + isl923x_emul_set_adc_vbus(isl923x_emul, expected_voltage_mv); + zassert_ok(isl923x_drv.get_vbus_voltage(CHARGER_NUM, 0, + &voltage), + NULL); + /* isl923x_get_vbus_voltage treats the measured voltage as + * having an effective step size of 96 mV. This is slightly + * different than the scheme described in the ISL9238 datasheet. + * Reported VBUS should therefore be within 100 mV of nominal + * VBUS. + */ + zassert_within(expected_voltage_mv, voltage, 100, + "Expected %dmV but got %dmV", + expected_voltage_mv, voltage); + } +} + +ZTEST(isl923x, test_init) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + int input_current; + + /* Test failed CTRL2 register read (prochot debounce) */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL2); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); + + /* Test failed CTRL2 register write */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL2); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); + + /* Test failed CTRL 0 read */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL0); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); + + /* Test failed CTRL 0 write */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL0); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); + + /* Test failed CTRL 3 read */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL9238_REG_CONTROL3); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); + + /* Test failed CTRL 3 write */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, ISL9238_REG_CONTROL3); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); + + /* Test failed write adapter current limit */ + isl923x_emul_reset_registers(isl923x_emul); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + ISL923X_REG_ADAPTER_CURRENT_LIMIT1); + isl923x_drv.init(CHARGER_NUM); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_ok(isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + NULL); + zassert_equal(0, input_current, + "Expected input current 0mA but got %dmA", input_current); +} + +ZTEST(isl923x, test_init_late_jump) +{ + int input_current; + + isl923x_drv.init(CHARGER_NUM); + + /* Init again with system_jumped_late() returning true and make sure + * the input current limit is still correct. + */ + + system_jumped_late_fake.return_val = 1; + isl923x_drv.init(CHARGER_NUM); + + zassert_equal(EC_SUCCESS, + isl923x_drv.get_input_current_limit(CHARGER_NUM, + &input_current), + "Could not read input current limit."); + zassert_equal(CONFIG_CHARGER_INPUT_CURRENT, input_current, + "Input current (%d) not at (%d)", input_current, + CONFIG_CHARGER_INPUT_CURRENT); +} + +ZTEST(isl923x, test_isl923x_is_acok) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + enum ec_error_list rv; + bool acok; + + /* Part 1: invalid charger number */ + rv = raa489000_is_acok(board_get_charger_chip_count() + 1, &acok); + zassert_equal(EC_ERROR_INVAL, rv, + "Invalid charger num, but AC OK check succeeded"); + + /* Part 2: error accessing register */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL9238_REG_INFO2); + + rv = raa489000_is_acok(CHARGER_NUM, &acok); + zassert_equal(EC_ERROR_INVAL, rv, + "Register read failure, but AC OK check succeeded"); + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Part 3: successful path - ACOK is true */ + raa489000_emul_set_acok_pin(isl923x_emul, 1); + + rv = raa489000_is_acok(CHARGER_NUM, &acok); + zassert_equal(EC_SUCCESS, rv, "AC OK check did not return success"); + zassert_true(acok, "AC OK is false"); + + /* Part 3: successful path - ACOK is false */ + raa489000_emul_set_acok_pin(isl923x_emul, 0); + + rv = raa489000_is_acok(CHARGER_NUM, &acok); + zassert_equal(EC_SUCCESS, rv, "AC OK check did not return success"); + zassert_false(acok, "AC OK is true"); +} + +ZTEST(isl923x, test_isl923x_enable_asgate) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + int rv; + + /* Part 1: Try enabling the ASGATE */ + rv = raa489000_enable_asgate(CHARGER_NUM, true); + + zassert_equal(EC_SUCCESS, rv, "Expected return code of %d but got %d", + EC_SUCCESS, rv); + zassert_true( + isl923x_emul_peek_reg(isl923x_emul, RAA489000_REG_CONTROL8) & + RAA489000_C8_ASGATE_ON_READY, + "RAA489000_C8_ASGATE_ON_READY bit not set in Control Reg 8"); + + /* Part 2: Turn it back off */ + rv = raa489000_enable_asgate(CHARGER_NUM, false); + + zassert_equal(EC_SUCCESS, rv, "Expected return code of %d but got %d", + EC_SUCCESS, rv); + zassert_false(isl923x_emul_peek_reg(isl923x_emul, + RAA489000_REG_CONTROL8) & + RAA489000_C8_ASGATE_ON_READY, + "RAA489000_C8_ASGATE_ON_READY bit set in Control Reg 8"); +} + +/* Mock read and write functions to use in the hibernation test */ +FAKE_VALUE_FUNC(int, hibernate_mock_read_fn, const struct emul *, int, + uint8_t *, int, void *); +FAKE_VALUE_FUNC(int, hibernate_mock_write_fn, const struct emul *, int, uint8_t, + int, void *); + +/** + * @brief Setup function for the hibernate tests. + */ +static void isl923x_hibernate_before(void *state) +{ + ARG_UNUSED(state); + + /* Reset mocks and make the read/write mocks pass all data through */ + RESET_FAKE(hibernate_mock_read_fn); + RESET_FAKE(hibernate_mock_write_fn); + hibernate_mock_read_fn_fake.return_val = 1; + hibernate_mock_write_fn_fake.return_val = 1; + + i2c_common_emul_set_read_func(COMMON_DATA, hibernate_mock_read_fn, + NULL); + i2c_common_emul_set_write_func(COMMON_DATA, hibernate_mock_write_fn, + NULL); + + /* Don't fail on any register access */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +/** + * @brief Teardown function for the hibernate tests. + */ +static void isl923x_hibernate_after(void *state) +{ + ARG_UNUSED(state); + + /* Clear the mock read/write functions */ + i2c_common_emul_set_read_func(COMMON_DATA, NULL, NULL); + i2c_common_emul_set_write_func(COMMON_DATA, NULL, NULL); + + /* Don't fail on any register access */ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__happy_path) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + uint16_t actual; + + raa489000_hibernate(CHARGER_NUM, false); + + /* Check ISL923X_REG_CONTROL0 */ + actual = isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL0); + + zassert_false(actual & RAA489000_C0_EN_CHG_PUMPS_TO_100PCT, + "RAA489000_C0_EN_CHG_PUMPS_TO_100PCT should not be set"); + zassert_false(actual & RAA489000_C0_BGATE_FORCE_ON, + "RAA489000_C0_BGATE_FORCE_ON should not be set"); + + /* Check ISL923X_REG_CONTROL1 */ + actual = isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1); + + zassert_false( + actual & RAA489000_C1_ENABLE_SUPP_SUPPORT_MODE, + "RAA489000_C1_ENABLE_SUPP_SUPPORT_MODE should not be set"); + zassert_false(actual & ISL923X_C1_ENABLE_PSYS, + "ISL923X_C1_ENABLE_PSYS should not be set"); + zassert_true(actual & RAA489000_C1_BGATE_FORCE_OFF, + "RAA489000_C1_BGATE_FORCE_OFF should be set"); + zassert_true(actual & ISL923X_C1_DISABLE_MON, + "ISL923X_C1_DISABLE_MON should be set"); + + /* Check ISL9238_REG_CONTROL3 (disable_adc = false) */ + actual = isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3); + + zassert_true(actual & RAA489000_ENABLE_ADC, + "RAA489000_ENABLE_ADC should be set"); + + /* Check ISL9238_REG_CONTROL4 */ + actual = isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL4); + + zassert_true(actual & RAA489000_C4_DISABLE_GP_CMP, + "RAA489000_C4_DISABLE_GP_CMP should be set"); + + /* Ensure all expected register reads and writes happened */ + int registers[] = { ISL923X_REG_CONTROL0, ISL923X_REG_CONTROL1, + ISL9238_REG_CONTROL3, ISL9238_REG_CONTROL4 }; + + for (int i = 0; i < ARRAY_SIZE(registers); i++) { + /* Each reg has 2 reads and 2 writes because they are 16-bit */ + MOCK_ASSERT_I2C_READ(hibernate_mock_read_fn, i * 2, + registers[i]); + MOCK_ASSERT_I2C_READ(hibernate_mock_read_fn, (i * 2) + 1, + registers[i]); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, i * 2, + registers[i], MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, (i * 2) + 1, + registers[i], MOCK_IGNORE_VALUE); + } +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__invalid_charger_number) +{ + /* Mocks should just be pass-through */ + RESET_FAKE(hibernate_mock_read_fn); + RESET_FAKE(hibernate_mock_write_fn); + hibernate_mock_read_fn_fake.return_val = 1; + hibernate_mock_write_fn_fake.return_val = 1; + + raa489000_hibernate(board_get_charger_chip_count() + 1, false); + + /* Make sure no I2C activity happened */ + zassert_equal(hibernate_mock_read_fn_fake.call_count, 0, + "No I2C reads should have happened"); + zassert_equal(hibernate_mock_write_fn_fake.call_count, 0, + "No I2C writes should have happened"); +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__fail_at_ISL923X_REG_CONTROL0) +{ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL0); + + raa489000_hibernate(CHARGER_NUM, false); + + /* + * We have no return codes to check, so instead verify that the first + * successful I2C write is to CONTROL1 and not CONTROL0. + */ + + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 0, ISL923X_REG_CONTROL1, + MOCK_IGNORE_VALUE); +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__fail_at_ISL923X_REG_CONTROL1) +{ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL923X_REG_CONTROL1); + + raa489000_hibernate(CHARGER_NUM, false); + + /* + * Ensure we skipped CONTROL1. (NB: due to 16-bit regs, each write takes + * two calls to the mock_write_fn) + */ + + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 0, ISL923X_REG_CONTROL0, + MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 1, ISL923X_REG_CONTROL0, + MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 2, ISL9238_REG_CONTROL3, + MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 3, ISL9238_REG_CONTROL3, + MOCK_IGNORE_VALUE); +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__fail_at_ISL9238_REG_CONTROL3) +{ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL9238_REG_CONTROL3); + + raa489000_hibernate(CHARGER_NUM, false); + + /* + * Ensure we skipped CONTROL3. (NB: due to 16-bit regs, each write takes + * two calls to the mock_write_fn) + */ + + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 2, ISL923X_REG_CONTROL1, + MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 3, ISL923X_REG_CONTROL1, + MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 4, ISL9238_REG_CONTROL4, + MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 5, ISL9238_REG_CONTROL4, + MOCK_IGNORE_VALUE); +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__fail_at_ISL9238_REG_CONTROL4) +{ + i2c_common_emul_set_read_fail_reg(COMMON_DATA, ISL9238_REG_CONTROL4); + + raa489000_hibernate(CHARGER_NUM, false); + + /* + * Ensure we skipped CONTROL4. (i.e. the last calls should be to write + * to CONTROL3) + */ + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, + hibernate_mock_write_fn_fake.call_count - 2, + ISL9238_REG_CONTROL3, MOCK_IGNORE_VALUE); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, + hibernate_mock_write_fn_fake.call_count - 1, + ISL9238_REG_CONTROL3, MOCK_IGNORE_VALUE); +} + +ZTEST(isl923x_hibernate, test_isl923x_hibernate__adc_disable) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + uint16_t expected; + + raa489000_hibernate(CHARGER_NUM, true); + + /* Check ISL9238_REG_CONTROL3 (disable_adc = true) */ + expected = isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3); + expected &= ~RAA489000_ENABLE_ADC; + + MOCK_ASSERT_I2C_READ(hibernate_mock_read_fn, 4, ISL9238_REG_CONTROL3); + MOCK_ASSERT_I2C_READ(hibernate_mock_read_fn, 5, ISL9238_REG_CONTROL3); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 4, ISL9238_REG_CONTROL3, + expected & 0xff); + MOCK_ASSERT_I2C_WRITE(hibernate_mock_write_fn, 5, ISL9238_REG_CONTROL3, + expected >> 8); +} + +ZTEST(isl923x_hibernate, test_isl9238c_hibernate) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + uint16_t control1_expected, control2_expected, control3_expected; + int rv; + + /* Part 1: Happy path */ + control1_expected = + (isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1) & + ~ISL923X_C1_ENABLE_PSYS) | + ISL923X_C1_DISABLE_MON; + control2_expected = + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL2) | + ISL923X_C2_COMPARATOR; + control3_expected = + isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3) | + ISL9238_C3_BGATE_OFF; + + rv = isl9238c_hibernate(CHARGER_NUM); + + zassert_equal(EC_SUCCESS, rv, "Expected return code %d but got %d", + EC_SUCCESS, rv); + zassert_equal(isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1), + control1_expected, + "Unexpected register value 0x%02x. Should be 0x%02x", + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1), + control1_expected); + zassert_equal(isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL2), + control2_expected, + "Unexpected register value 0x%02x. Should be 0x%02x", + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL2), + control2_expected); + zassert_equal(isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3), + control3_expected, + "Unexpected register value 0x%02x. Should be 0x%02x", + isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3), + control3_expected); + + /* Part 2: Fail reading each register and check for error code */ + int registers[] = { ISL923X_REG_CONTROL1, ISL923X_REG_CONTROL2, + ISL9238_REG_CONTROL3 }; + + for (int i = 0; i < ARRAY_SIZE(registers); i++) { + i2c_common_emul_set_read_fail_reg(COMMON_DATA, registers[i]); + + rv = isl9238c_hibernate(CHARGER_NUM); + + zassert_equal(EC_ERROR_INVAL, rv, + "Wrong return code. Expected %d but got %d", + EC_ERROR_INVAL, rv); + } +} + +ZTEST(isl923x_hibernate, test_isl9238c_resume) +{ + const struct emul *isl923x_emul = ISL923X_EMUL; + uint16_t control1_expected, control2_expected, control3_expected; + int rv; + + /* Part 1: Happy path */ + control1_expected = + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1) | + ISL923X_C1_ENABLE_PSYS; + control2_expected = + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL2) & + ~ISL923X_C2_COMPARATOR; + control3_expected = + isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3) & + ~ISL9238_C3_BGATE_OFF; + + rv = isl9238c_resume(CHARGER_NUM); + + zassert_equal(EC_SUCCESS, rv, "Expected return code %d but got %d", + EC_SUCCESS, rv); + zassert_equal(isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1), + control1_expected, + "Unexpected register value 0x%02x. Should be 0x%02x", + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL1), + control1_expected); + zassert_equal(isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL2), + control2_expected, + "Unexpected register value 0x%02x. Should be 0x%02x", + isl923x_emul_peek_reg(isl923x_emul, ISL923X_REG_CONTROL2), + control2_expected); + zassert_equal(isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3), + control3_expected, + "Unexpected register value 0x%02x. Should be 0x%02x", + isl923x_emul_peek_reg(isl923x_emul, ISL9238_REG_CONTROL3), + control3_expected); + + /* Part 2: Fail reading each register and check for error code */ + int registers[] = { ISL923X_REG_CONTROL1, ISL923X_REG_CONTROL2, + ISL9238_REG_CONTROL3 }; + + for (int i = 0; i < ARRAY_SIZE(registers); i++) { + i2c_common_emul_set_read_fail_reg(COMMON_DATA, registers[i]); + + rv = isl9238c_resume(CHARGER_NUM); + + zassert_equal(EC_ERROR_INVAL, rv, + "Wrong return code. Expected %d but got %d", + EC_ERROR_INVAL, rv); + } +} + +ZTEST_SUITE(isl923x, drivers_predicate_pre_main, NULL, NULL, NULL, NULL); + +ZTEST_SUITE(isl923x_hibernate, drivers_predicate_post_main, NULL, + isl923x_hibernate_before, isl923x_hibernate_after, NULL); diff --git a/zephyr/test/drivers/default/src/led.c b/zephyr/test/drivers/default/src/led.c new file mode 100644 index 0000000000..e89a3d8b66 --- /dev/null +++ b/zephyr/test/drivers/default/src/led.c @@ -0,0 +1,92 @@ +/* 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 <zephyr/ztest_assert.h> + +#include <zephyr/device.h> +#include <zephyr/drivers/pwm.h> + +#include "ec_commands.h" +#include "led.h" +#include "led_common.h" +#include "pwm_mock.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +ZTEST_SUITE(pwm_led_driver, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); + +ZTEST(pwm_led_driver, test_led_set_brightness) +{ + const uint8_t brightness_off[EC_LED_COLOR_COUNT] = {}; + const uint8_t brightness_white[EC_LED_COLOR_COUNT] = { + [EC_LED_COLOR_WHITE] = 1 + }; + const uint8_t brightness_amber[EC_LED_COLOR_COUNT] = { + [EC_LED_COLOR_AMBER] = 1 + }; + const struct device *pwm_blue_left = + DEVICE_DT_GET(DT_NODELABEL(pwm_blue_left)); + const struct device *pwm_white_left = + DEVICE_DT_GET(DT_NODELABEL(pwm_white_left)); + const struct device *pwm_amber_right = + DEVICE_DT_GET(DT_NODELABEL(pwm_amber_right)); + const struct device *pwm_white_right = + DEVICE_DT_GET(DT_NODELABEL(pwm_white_right)); + + /* Turn off all LEDs */ + led_set_brightness(EC_LED_ID_LEFT_LED, brightness_off); + led_set_brightness(EC_LED_ID_RIGHT_LED, brightness_off); + zassert_equal(pwm_mock_get_duty(pwm_blue_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_amber_right, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_right, 0), 0, NULL); + + /* Call led_set_color(LED_WHITE, LEFT_LED) */ + led_set_brightness(EC_LED_ID_LEFT_LED, brightness_white); + zassert_equal(pwm_mock_get_duty(pwm_blue_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_left, 0), 100, NULL); + zassert_equal(pwm_mock_get_duty(pwm_amber_right, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_right, 0), 0, NULL); + + /* Unsupporte, call led_set_color(LED_OFF, LEFT_LED) */ + led_set_brightness(EC_LED_ID_LEFT_LED, brightness_amber); + zassert_equal(pwm_mock_get_duty(pwm_blue_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_amber_right, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_right, 0), 0, NULL); + + /* Call led_set_color(AMBER, RIGHT_LED) */ + led_set_brightness(EC_LED_ID_RIGHT_LED, brightness_amber); + zassert_equal(pwm_mock_get_duty(pwm_blue_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_left, 0), 0, NULL); + zassert_equal(pwm_mock_get_duty(pwm_amber_right, 0), 100, NULL); + zassert_equal(pwm_mock_get_duty(pwm_white_right, 0), 0, NULL); +} + +ZTEST(pwm_led_driver, test_led_get_brightness) +{ + uint8_t brightness[EC_LED_COLOR_COUNT]; + const uint8_t expected_left[EC_LED_COLOR_COUNT] = { + [EC_LED_COLOR_BLUE] = 100, + [EC_LED_COLOR_WHITE] = 100, + }; + const uint8_t expected_right[EC_LED_COLOR_COUNT] = { + [EC_LED_COLOR_WHITE] = 100, + [EC_LED_COLOR_AMBER] = 100, + }; + + /* Verify LED colors defined in device tree are reflected in the + * brightness array. + */ + memset(brightness, 255, sizeof(brightness)); + led_get_brightness_range(EC_LED_ID_LEFT_LED, brightness); + zassert_mem_equal(brightness, expected_left, sizeof(brightness), NULL); + + memset(brightness, 255, sizeof(brightness)); + led_get_brightness_range(EC_LED_ID_RIGHT_LED, brightness); + zassert_mem_equal(brightness, expected_right, sizeof(brightness), NULL); +} diff --git a/zephyr/test/drivers/default/src/lid_angle.c b/zephyr/test/drivers/default/src/lid_angle.c new file mode 100644 index 0000000000..568057d95a --- /dev/null +++ b/zephyr/test/drivers/default/src/lid_angle.c @@ -0,0 +1,70 @@ +/* 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 "ec_commands.h" +#include "lid_angle.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" + +#define LID_ANGLE_MIN_LARGE_ANGLE 0 +#define LID_ANGLE_MAX_LARGE_ANGLE 360 + +static void lid_angle_after(void *f) +{ + ARG_UNUSED(f); + /* Reset the wake angle */ + lid_angle_set_wake_angle(180); + /* Flush the buffer */ + lid_angle_update(LID_ANGLE_UNRELIABLE); + lid_angle_update(LID_ANGLE_UNRELIABLE); + lid_angle_update(LID_ANGLE_UNRELIABLE); + lid_angle_update(LID_ANGLE_UNRELIABLE); +} + +ZTEST_SUITE(lid_angle, drivers_predicate_post_main, NULL, NULL, lid_angle_after, + NULL); + +ZTEST(lid_angle, test_get_set_wake_angle) +{ + lid_angle_set_wake_angle(LID_ANGLE_MIN_LARGE_ANGLE - 1); + zassert_equal(LID_ANGLE_MIN_LARGE_ANGLE, lid_angle_get_wake_angle(), + NULL); + + lid_angle_set_wake_angle(LID_ANGLE_MAX_LARGE_ANGLE + 1); + zassert_equal(LID_ANGLE_MAX_LARGE_ANGLE, lid_angle_get_wake_angle(), + NULL); + + lid_angle_set_wake_angle( + (LID_ANGLE_MIN_LARGE_ANGLE + LID_ANGLE_MAX_LARGE_ANGLE) / 2); + zassert_equal((LID_ANGLE_MIN_LARGE_ANGLE + LID_ANGLE_MAX_LARGE_ANGLE) / + 2, + lid_angle_get_wake_angle(), NULL); +} + +ZTEST(lid_angle, test_no_wake_min_large_angle) +{ + lid_angle_set_wake_angle(LID_ANGLE_MIN_LARGE_ANGLE); + lid_angle_update(45); + lid_angle_update(45); + lid_angle_update(45); + lid_angle_update(45); + + zassert_equal(1, lid_angle_peripheral_enable_fake.call_count, NULL); + zassert_equal(0, lid_angle_peripheral_enable_fake.arg0_val, NULL); +} + +ZTEST(lid_angle, test_wake_max_large_angle) +{ + lid_angle_set_wake_angle(LID_ANGLE_MAX_LARGE_ANGLE); + lid_angle_update(45); + lid_angle_update(45); + lid_angle_update(45); + lid_angle_update(45); + + zassert_equal(1, lid_angle_peripheral_enable_fake.call_count, NULL); + zassert_equal(1, lid_angle_peripheral_enable_fake.arg0_val, NULL); +} diff --git a/zephyr/test/drivers/default/src/lid_switch.c b/zephyr/test/drivers/default/src/lid_switch.c new file mode 100644 index 0000000000..1647f73e00 --- /dev/null +++ b/zephyr/test/drivers/default/src/lid_switch.c @@ -0,0 +1,277 @@ +/* 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 <zephyr/drivers/emul.h> +#include <zephyr/drivers/gpio.h> +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <lid_switch.h> +#include <zephyr/shell/shell_dummy.h> +#include <console.h> + +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "ec_commands.h" +#include "host_command.h" + +#define LID_GPIO_PATH DT_PATH(named_gpios, lid_open_ec) +#define LID_GPIO_PIN DT_GPIO_PIN(LID_GPIO_PATH, gpios) + +int emul_lid_open(void) +{ + const struct device *lid_gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(LID_GPIO_PATH, gpios)); + + return gpio_emul_input_set(lid_gpio_dev, LID_GPIO_PIN, 1); +} + +int emul_lid_close(void) +{ + const struct device *lid_gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(LID_GPIO_PATH, gpios)); + + return gpio_emul_input_set(lid_gpio_dev, LID_GPIO_PIN, 0); +} + +static void *lid_switch_setup(void) +{ + /** + * Set chipset to S0 as chipset power on after opening lid may disturb + * test + */ + test_set_chipset_to_s0(); + + return NULL; +} + +static void lid_switch_before(void *unused) +{ + /* Make sure that interrupt fire at the next lid open/close */ + zassume_ok(emul_lid_close(), NULL); + zassume_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); +} + +static void lid_switch_after(void *unused) +{ + struct ec_params_force_lid_open params = { + .enabled = 0, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_FORCE_LID_OPEN, 0, params); + int res; + + res = host_command_process(&args); + if (res) + TC_ERROR("host_command_process() failed (%d)\n", res); + + if (args.result) + TC_ERROR("args.result != 0 (%d != 0)\n", args.result); + + res = emul_lid_open(); + if (res) + TC_ERROR("emul_lid_open() failed (%d)\n", res); + k_sleep(K_MSEC(100)); +} + +ZTEST(lid_switch, test_lid_open) +{ + /* Start closed. */ + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 0, NULL); + + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 1, NULL); +} + +ZTEST(lid_switch, test_lid_debounce) +{ + /* Start closed. */ + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 0, NULL); + + /* Create interrupts quickly before they can be handled. */ + zassert_ok(emul_lid_open(), NULL); + zassert_ok(emul_lid_close(), NULL); + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 1, NULL); +} + +ZTEST(lid_switch, test_lid_close) +{ + /* Start open. */ + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); + + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(200)); + zassert_equal(lid_is_open(), 0, NULL); +} + +ZTEST(lid_switch, test_cmd_lidopen) +{ + /* Start closed. */ + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 0, NULL); + + /* Forced override lid open. */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidopen"), + NULL); + zassert_equal(lid_is_open(), 1, NULL); + k_sleep(K_MSEC(100)); + + printk("GPIO lid open/close\n"); + /* Open & close with gpio. */ + zassert_ok(emul_lid_open(), NULL); + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(500)); + zassert_equal(lid_is_open(), 0, NULL); +} + +ZTEST(lid_switch, test_cmd_lidopen_bounce) +{ + /* Start closed. */ + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 0, NULL); + + printk("Console lid open\n"); + /* Forced override lid open. */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidopen"), + NULL); + zassert_equal(lid_is_open(), 1, NULL); + k_sleep(K_MSEC(100)); + + printk("Console lid open\n"); + /* Forced override lid open. */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidopen"), + NULL); + zassert_equal(lid_is_open(), 1, NULL); + k_sleep(K_MSEC(100)); + + printk("GPIO lid open/close\n"); + /* Open & close with gpio. */ + zassert_ok(emul_lid_open(), NULL); + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(500)); + zassert_equal(lid_is_open(), 0, NULL); +} + +ZTEST(lid_switch, test_cmd_lidclose) +{ + /* Start open. */ + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 1, NULL); + + /* Forced override lid close. */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidclose"), + NULL); + zassert_equal(lid_is_open(), 0, NULL); + k_sleep(K_MSEC(100)); + + printk("GPIO lid close/open\n"); + /* Close & open with gpio. */ + zassert_ok(emul_lid_close(), NULL); + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(500)); + zassert_equal(lid_is_open(), 1, NULL); +} + +ZTEST(lid_switch, test_cmd_lidclose_bounce) +{ + /* Start open. */ + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 1, NULL); + + /* Forced override lid close. */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidclose"), + NULL); + zassert_equal(lid_is_open(), 0, NULL); + k_sleep(K_MSEC(100)); + + /* Forced override lid close. */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidclose"), + NULL); + zassert_equal(lid_is_open(), 0, NULL); + k_sleep(K_MSEC(100)); + + printk("GPIO lid close/open\n"); + /* Close & open with gpio. */ + zassert_ok(emul_lid_close(), NULL); + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(500)); + zassert_equal(lid_is_open(), 1, NULL); +} + +#if defined(CONFIG_SHELL_BACKEND_DUMMY) +ZTEST(lid_switch, test_cmd_lidstate_open) +{ + const char *buffer; + size_t buffer_size; + + /* Start open. */ + zassert_ok(emul_lid_open(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 1, NULL); + + /* Read the state with console. */ + shell_backend_dummy_clear_output(get_ec_shell()); + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidstate"), + NULL); + buffer = shell_backend_dummy_get_output(get_ec_shell(), &buffer_size); + zassert_true(strcmp(buffer, "\r\nlid state: open\r\n") == 0, + "Invalid console output %s", buffer); +} + +ZTEST(lid_switch, test_cmd_lidstate_close) +{ + const char *buffer; + size_t buffer_size; + + /* Start closed. */ + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 0, NULL); + + /* Read the state with console. */ + shell_backend_dummy_clear_output(get_ec_shell()); + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "lidstate"), + NULL); + buffer = shell_backend_dummy_get_output(get_ec_shell(), &buffer_size); + zassert_true(strcmp(buffer, "\r\nlid state: closed\r\n") == 0, + "Invalid console output %s", buffer); +} +#else +#error This test requires CONFIG_SHELL_BACKEND_DUMMY +#endif + +ZTEST(lid_switch, test_hc_force_lid_open) +{ + struct ec_params_force_lid_open params = { + .enabled = 1, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_FORCE_LID_OPEN, 0, params); + + /* Start closed. */ + zassert_ok(emul_lid_close(), NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 0, NULL); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + k_sleep(K_MSEC(100)); + zassert_equal(lid_is_open(), 1, NULL); +} + +ZTEST_SUITE(lid_switch, drivers_predicate_post_main, lid_switch_setup, + lid_switch_before, lid_switch_after, NULL); diff --git a/zephyr/test/drivers/default/src/lis2dw12.c b/zephyr/test/drivers/default/src/lis2dw12.c new file mode 100644 index 0000000000..4cb29796f8 --- /dev/null +++ b/zephyr/test/drivers/default/src/lis2dw12.c @@ -0,0 +1,455 @@ +/* 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 <zephyr/drivers/emul.h> +#include "driver/accel_lis2dw12.h" +#include "emul/emul_common_i2c.h" +#include "emul/emul_lis2dw12.h" +#include "test/drivers/test_state.h" + +#define LIS2DW12_NODELABEL DT_NODELABEL(ms_lis2dw12_accel) +#define LIS2DW12_SENSOR_ID SENSOR_ID(LIS2DW12_NODELABEL) +#define LIS2DW12_EMUL_NODE DT_NODELABEL(lis2dw12_emul) + +#include <stdio.h> + +#define CHECK_XYZ_EQUALS(VEC1, VEC2) \ + do { \ + zassert_equal((VEC1)[X], (VEC2)[X], \ + "Got %d for X, expected %d", (VEC1)[X], \ + (VEC2)[X]); \ + zassert_equal((VEC1)[Y], (VEC2)[Y], \ + "Got %d for Y, expected %d", (VEC1)[Y], \ + (VEC2)[Y]); \ + zassert_equal((VEC1)[Z], (VEC2)[Z], \ + "Got %d for Z, expected %d", (VEC1)[Z], \ + (VEC2)[Z]); \ + } while (0) + +/** Used with the LIS2DW12 set rate function to control rounding behavior */ +enum lis2dw12_round_mode { + ROUND_DOWN, + ROUND_UP, +}; + +static inline void lis2dw12_setup(void) +{ + lis2dw12_emul_reset(EMUL_DT_GET(LIS2DW12_EMUL_NODE)); + + /* Reset certain sensor struct values */ + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + + ms->current_range = 0; +} + +static void lis2dw12_before(void *state) +{ + ARG_UNUSED(state); + lis2dw12_setup(); +} + +static void lis2dw12_after(void *state) +{ + ARG_UNUSED(state); + lis2dw12_setup(); +} + +ZTEST(lis2dw12, test_lis2dw12_init__fail_read_who_am_i) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + i2c_common_emul_set_read_fail_reg(common_data, LIS2DW12_WHO_AM_I_REG); + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_INVAL, rv, NULL); +} + +ZTEST(lis2dw12, test_lis2dw12_init__fail_who_am_i) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + lis2dw12_emul_set_who_am_i(emul, ~LIS2DW12_WHO_AM_I); + + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_ACCESS_DENIED, rv, + "init returned %d but was expecting %d", rv, + EC_ERROR_ACCESS_DENIED); +} + +ZTEST(lis2dw12, test_lis2dw12_init__fail_write_soft_reset) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + i2c_common_emul_set_write_fail_reg(common_data, + LIS2DW12_SOFT_RESET_ADDR); + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_INVAL, rv, NULL); +} + +ZTEST(lis2dw12, test_lis2dw12_init__timeout_read_soft_reset) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + i2c_common_emul_set_read_fail_reg(common_data, + LIS2DW12_SOFT_RESET_ADDR); + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_TIMEOUT, rv, "init returned %d but expected %d", + rv, EC_ERROR_TIMEOUT); +} + +static int lis2dw12_test_mock_write_fail_set_bdu(const struct emul *emul, + int reg, uint8_t val, + int bytes, void *data) +{ + if (reg == LIS2DW12_BDU_ADDR && bytes == 1 && + (val & LIS2DW12_BDU_MASK) != 0) { + return -EIO; + } + return 1; +} + +ZTEST(lis2dw12, test_lis2dw12_init__fail_set_bdu) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + i2c_common_emul_set_write_func( + common_data, lis2dw12_test_mock_write_fail_set_bdu, NULL); + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_INVAL, rv, "init returned %d but expected %d", + rv, EC_ERROR_INVAL); + zassert_true(lis2dw12_emul_get_soft_reset_count(emul) > 0, + "expected at least one soft reset"); +} + +ZTEST(lis2dw12, test_lis2dw12_init__fail_set_lir) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + i2c_common_emul_set_read_fail_reg(common_data, LIS2DW12_LIR_ADDR); + + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_INVAL, rv, "init returned %d but expected %d", + rv, EC_ERROR_INVAL); + zassert_true(lis2dw12_emul_get_soft_reset_count(emul) > 0, + "expected at least one soft reset"); +} + +static int lis2dw12_test_mock_write_fail_set_power_mode(const struct emul *emul, + int reg, uint8_t val, + int bytes, void *data) +{ + if (reg == LIS2DW12_ACC_LPMODE_ADDR && bytes == 1 && + (val & LIS2DW12_ACC_LPMODE_MASK) != 0) { + /* Cause an error when trying to set the LPMODE */ + return -EIO; + } + return 1; +} + +ZTEST(lis2dw12, test_lis2dw12_init__fail_set_power_mode) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + i2c_common_emul_set_write_func( + common_data, lis2dw12_test_mock_write_fail_set_power_mode, + NULL); + + rv = ms->drv->init(ms); + zassert_equal(EC_ERROR_INVAL, rv, "init returned %d but expected %d", + rv, EC_ERROR_INVAL); + zassert_true(lis2dw12_emul_get_soft_reset_count(emul) > 0, + "expected at least one soft reset"); +} + +ZTEST(lis2dw12, test_lis2dw12_init__success) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + struct stprivate_data *drvdata = ms->drv_data; + + int rv; + + rv = ms->drv->init(ms); + zassert_equal(EC_SUCCESS, rv, "init returned %d but expected %d", rv, + EC_SUCCESS); + zassert_true(lis2dw12_emul_get_soft_reset_count(emul) > 0, + "expected at least one soft reset"); + zassert_equal(LIS2DW12_RESOLUTION, drvdata->resol, + "Expected resolution of %d but got %d", + LIS2DW12_RESOLUTION, drvdata->resol); +} + +ZTEST(lis2dw12, test_lis2dw12_set_power_mode) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + /* Part 1: happy path */ + rv = lis2dw12_set_power_mode(ms, LIS2DW12_LOW_POWER, + LIS2DW12_LOW_POWER_MODE_2); + zassert_equal(rv, EC_SUCCESS, "Expected %d but got %d", EC_SUCCESS, rv); + + /* Part 2: unimplemented modes */ + rv = lis2dw12_set_power_mode(ms, LIS2DW12_LOW_POWER, + LIS2DW12_LOW_POWER_MODE_1); + zassert_equal(rv, EC_ERROR_UNIMPLEMENTED, "Expected %d but got %d", + EC_ERROR_UNIMPLEMENTED, rv); + + /* Part 3: attempt to set mode but cannot modify reg. */ + i2c_common_emul_set_read_fail_reg(common_data, LIS2DW12_ACC_MODE_ADDR); + rv = lis2dw12_set_power_mode(ms, LIS2DW12_LOW_POWER, + LIS2DW12_LOW_POWER_MODE_2); + zassert_equal(rv, EC_ERROR_INVAL, "Expected %d but got %d", + EC_ERROR_INVAL, rv); +} + +ZTEST(lis2dw12, test_lis2dw12_set_range) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + int rv; + + /* Part 1: Happy path. Go above the max range; it will be automatically + * clamped. + */ + + rv = ms->drv->set_range(ms, LIS2DW12_ACCEL_FS_MAX_VAL + 1, 0); + zassert_equal(rv, EC_SUCCESS, "Expected %d but got %d", EC_SUCCESS, rv); + zassert_equal(ms->current_range, LIS2DW12_ACCEL_FS_MAX_VAL, + "Expected %d but got %d", LIS2DW12_ACCEL_FS_MAX_VAL, + ms->current_range); + + /* Part 2: Error accessing register */ + i2c_common_emul_set_read_fail_reg(common_data, LIS2DW12_FS_ADDR); + rv = ms->drv->set_range(ms, LIS2DW12_ACCEL_FS_MAX_VAL, 0); + zassert_equal(rv, EC_ERROR_INVAL, "Expected %d but got %d", + EC_ERROR_INVAL, rv); +} + +ZTEST(lis2dw12, test_lis2dw12_set_rate) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + struct stprivate_data *drv_data = ms->drv_data; + int rv; + + /* Part 1: Turn off sensor with rate=0 */ + rv = ms->drv->set_data_rate(ms, 0, 0); + + zassert_equal(lis2dw12_emul_peek_odr(emul), LIS2DW12_ODR_POWER_OFF_VAL, + "Output data rate should be %d but got %d", + LIS2DW12_ODR_POWER_OFF_VAL, lis2dw12_emul_peek_odr(emul)); + zassert_equal(drv_data->base.odr, LIS2DW12_ODR_POWER_OFF_VAL, + "Output data rate should be %d but got %d", + LIS2DW12_ODR_POWER_OFF_VAL, drv_data->base.odr); + zassert_equal(rv, EC_SUCCESS, "Returned %d but expected %d", rv, + EC_SUCCESS); + + /* Part 2: Set some output data rates. We will request a certain rate + * and make sure the closest supported rate is used. + */ + + static const struct { + int requested_rate; /* millihertz */ + enum lis2dw12_round_mode round; + int expected_norm_rate; /* millihertz */ + uint8_t expected_reg_val; + } test_params[] = { + { 1000, ROUND_DOWN, LIS2DW12_ODR_MIN_VAL, + LIS2DW12_ODR_12HZ_VAL }, + { 12501, ROUND_DOWN, 12500, LIS2DW12_ODR_12HZ_VAL }, + { 25001, ROUND_DOWN, 25000, LIS2DW12_ODR_25HZ_VAL }, + { 50001, ROUND_DOWN, 50000, LIS2DW12_ODR_50HZ_VAL }, + { 100001, ROUND_DOWN, 100000, LIS2DW12_ODR_100HZ_VAL }, + { 200001, ROUND_DOWN, 200000, LIS2DW12_ODR_200HZ_VAL }, + { 400001, ROUND_DOWN, 400000, LIS2DW12_ODR_400HZ_VAL }, + { 800001, ROUND_DOWN, 800000, LIS2DW12_ODR_800HZ_VAL }, + { 1600001, ROUND_DOWN, 1600000, LIS2DW12_ODR_1_6kHZ_VAL }, + { 3200001, ROUND_DOWN, LIS2DW12_ODR_MAX_VAL, + LIS2DW12_ODR_1_6kHZ_VAL }, + + { 1000, ROUND_UP, LIS2DW12_ODR_MIN_VAL, LIS2DW12_ODR_12HZ_VAL }, + { 12501, ROUND_UP, 25000, LIS2DW12_ODR_25HZ_VAL }, + { 25001, ROUND_UP, 50000, LIS2DW12_ODR_50HZ_VAL }, + { 50001, ROUND_UP, 100000, LIS2DW12_ODR_100HZ_VAL }, + { 100001, ROUND_UP, 200000, LIS2DW12_ODR_200HZ_VAL }, + { 200001, ROUND_UP, 400000, LIS2DW12_ODR_400HZ_VAL }, + { 400001, ROUND_UP, 800000, LIS2DW12_ODR_800HZ_VAL }, + { 800001, ROUND_UP, 1600000, LIS2DW12_ODR_1_6kHZ_VAL }, + { 1600001, ROUND_UP, LIS2DW12_ODR_MAX_VAL, + LIS2DW12_ODR_1_6kHZ_VAL }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(test_params); i++) { + /* For each test vector in the above array */ + drv_data->base.odr = -1; + rv = ms->drv->set_data_rate(ms, test_params[i].requested_rate, + test_params[i].round); + + /* Check the normalized rate the driver chose */ + zassert_equal( + drv_data->base.odr, test_params[i].expected_norm_rate, + "For requested rate %d, output data rate should be %d but got %d", + test_params[i].requested_rate, + test_params[i].expected_norm_rate, drv_data->base.odr); + + /* Read ODR and mode bits back from CTRL1 register */ + uint8_t odr_bits = lis2dw12_emul_peek_odr(emul); + + zassert_equal( + odr_bits, test_params[i].expected_reg_val, + "For requested rate %d, ODR bits should be 0x%x but got 0x%x - %d", + test_params[i].requested_rate, + test_params[i].expected_reg_val, odr_bits, + LIS2DW12_ODR_MAX_VAL); + + /* Check if high performance mode was enabled if rate > + * 200,000mHz + */ + + uint8_t mode_bits = lis2dw12_emul_peek_mode(emul); + uint8_t lpmode_bits = lis2dw12_emul_peek_lpmode(emul); + + if (odr_bits > LIS2DW12_ODR_200HZ_VAL) { + /* High performance mode, LP mode immaterial */ + zassert_equal(mode_bits, LIS2DW12_HIGH_PERF, + "MODE[1:0] should be 0x%x, but got 0x%x", + LIS2DW12_HIGH_PERF, mode_bits); + + } else { + /* Low power mode, LP mode 2 */ + zassert_equal(mode_bits, LIS2DW12_LOW_POWER, + "MODE[1:0] should be 0x%x, but got 0x%x", + LIS2DW12_LOW_POWER, mode_bits); + + zassert_equal( + lpmode_bits, LIS2DW12_LOW_POWER_MODE_2, + "LPMODE[1:0] should be 0x%x, but got 0x%x", + LIS2DW12_LOW_POWER_MODE_2, lpmode_bits); + } + } +} + +ZTEST(lis2dw12, test_lis2dw12_read) +{ + const struct emul *emul = EMUL_DT_GET(LIS2DW12_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_lis2dw12_get_i2c_common_data(emul); + + struct motion_sensor_t *ms = &motion_sensors[LIS2DW12_SENSOR_ID]; + struct stprivate_data *drvdata = ms->drv_data; + intv3_t sample = { 0, 0, 0 }; + int rv; + + /* Reading requires a range to be set. Use 1 so it has no effect + * when scaling samples. Also need to set the sensor resolution + * manually. + */ + + ms->drv->set_range(ms, 1, 0); + drvdata->resol = LIS2DW12_RESOLUTION; + + /* Part 1: Try to read from sensor, but cannot check status register for + * ready bit + */ + + i2c_common_emul_set_read_fail_reg(common_data, LIS2DW12_STATUS_REG); + + rv = ms->drv->read(ms, sample); + + zassert_equal(rv, EC_ERROR_INVAL, + "Expected return val of %d but got %d", EC_ERROR_INVAL, + rv); + + /* Part 2: Try to read sensor, but no new data is available. In this + * case, the driver should return the reading in from `ms->raw_xyz` + */ + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + lis2dw12_emul_clear_accel_reading(emul); + ms->raw_xyz[X] = 123; + ms->raw_xyz[Y] = 456; + ms->raw_xyz[Z] = 789; + + rv = ms->drv->read(ms, sample); + + zassert_equal(rv, EC_SUCCESS, "Expected return val of %d but got %d", + EC_SUCCESS, rv); + CHECK_XYZ_EQUALS(sample, ms->raw_xyz); + + /* Part 3: Read from sensor w/ data ready, but an error occurs during + * read. + */ + intv3_t fake_sample = { 100, 200, 300 }; + + i2c_common_emul_set_read_fail_reg(common_data, LIS2DW12_OUT_X_L_ADDR); + lis2dw12_emul_set_accel_reading(emul, fake_sample); + + rv = ms->drv->read(ms, sample); + + zassert_equal(rv, EC_ERROR_INVAL, + "Expected return val of %d but got %d", EC_ERROR_INVAL, + rv); + + /* Part 4: Success */ + + intv3_t expected_sample; + + for (int i = 0; i < ARRAY_SIZE(expected_sample); i++) { + /* The read routine will normalize `fake_sample` to use the full + * range of INT16, so we need to compensate in our expected + * output + */ + + expected_sample[i] = + fake_sample[i] * (1 << (16 - LIS2DW12_RESOLUTION)); + } + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + lis2dw12_emul_set_accel_reading(emul, fake_sample); + + rv = ms->drv->read(ms, sample); + + zassert_equal(rv, EC_SUCCESS, "Expected return val of %d but got %d", + EC_SUCCESS, rv); + CHECK_XYZ_EQUALS(sample, expected_sample); +} + +ZTEST_SUITE(lis2dw12, drivers_predicate_post_main, NULL, lis2dw12_before, + lis2dw12_after, NULL); diff --git a/zephyr/test/drivers/default/src/ln9310.c b/zephyr/test/drivers/default/src/ln9310.c new file mode 100644 index 0000000000..326e8480ef --- /dev/null +++ b/zephyr/test/drivers/default/src/ln9310.c @@ -0,0 +1,685 @@ +/* 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/ztest.h> +#include <zephyr/drivers/emul.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest_assert.h> +#include <zephyr/drivers/i2c_emul.h> + +#include "driver/ln9310.h" +#include "emul/emul_ln9310.h" +#include "emul/emul_common_i2c.h" +#include "timer.h" +#include "test/drivers/test_state.h" + +/* + * TODO(b/201420132): Implement approach for tests to immediately schedule work + * to avoid any sleeping + */ +#define TEST_DELAY_MS 50 + +#define EMUL_LN9310_NODE DT_NODELABEL(ln9310) + +/* + * Chip revisions below LN9310_BC_STS_C_CHIP_REV_FIXED require an alternative + * software startup to properly initialize and power up. + */ +#define REQUIRES_CFLY_PRECHARGE_STARTUP_CHIP_REV \ + (LN9310_BC_STS_C_CHIP_REV_FIXED - 1) + +ZTEST(ln9310, test_ln9310_read_chip_fails) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery and chip rev won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + i2c_common_emul_set_read_fail_reg(common_data, LN9310_REG_BC_STS_C); + + zassert_true(ln9310_init() != 0, NULL); + zassert_false(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST(ln9310, test_ln9310_2s_powers_up) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + ln9310_software_enable(true); + + k_msleep(TEST_DELAY_MS); + zassert_true(ln9310_power_good(), NULL); +} + +ZTEST(ln9310, test_ln9310_3s_powers_up) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_3S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + ln9310_software_enable(true); + + k_msleep(TEST_DELAY_MS); + zassert_true(ln9310_power_good(), NULL); +} + +struct startup_workaround_data { + bool startup_workaround_attempted; + bool startup_workaround_should_fail; +}; + +static int mock_write_fn_intercept_startup_workaround(const struct emul *emul, + int reg, uint8_t val, + int bytes, void *data) +{ + struct startup_workaround_data *test_data = data; + + uint8_t startup_workaround_val = + (LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_ON | + LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_ON); + + test_data->startup_workaround_attempted = + test_data->startup_workaround_attempted || + ((reg == LN9310_REG_TEST_MODE_CTRL) && + (val == startup_workaround_val)); + + if (test_data->startup_workaround_should_fail) + return -1; + + return 1; +} + +ZTEST(ln9310, test_ln9310_2s_cfly_precharge_startup) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + + struct startup_workaround_data test_data = { + .startup_workaround_attempted = false, + .startup_workaround_should_fail = false, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, + REQUIRES_CFLY_PRECHARGE_STARTUP_CHIP_REV); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func( + common_data, mock_write_fn_intercept_startup_workaround, + &test_data); + + ln9310_software_enable(true); + zassert_true(test_data.startup_workaround_attempted, NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_true(ln9310_power_good(), NULL); + + ln9310_software_enable(false); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func(common_data, NULL, NULL); +} + +ZTEST(ln9310, test_ln9310_3s_cfly_precharge_startup) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + + struct startup_workaround_data test_data = { + .startup_workaround_attempted = false, + .startup_workaround_should_fail = false, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_3S); + ln9310_emul_set_version(emulator, + REQUIRES_CFLY_PRECHARGE_STARTUP_CHIP_REV); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func( + common_data, mock_write_fn_intercept_startup_workaround, + &test_data); + + ln9310_software_enable(true); + zassert_true(test_data.startup_workaround_attempted, NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_true(ln9310_power_good(), NULL); + + ln9310_software_enable(false); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func(common_data, NULL, NULL); +} + +ZTEST(ln9310, test_ln9310_cfly_precharge_exceeds_retries) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + + struct startup_workaround_data test_data = { + .startup_workaround_attempted = false, + .startup_workaround_should_fail = true, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* + * Battery and chip rev won't matter for statement + * coverage here so only testing one pair. + */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, + REQUIRES_CFLY_PRECHARGE_STARTUP_CHIP_REV); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func( + common_data, mock_write_fn_intercept_startup_workaround, + &test_data); + + ln9310_software_enable(true); + zassert_true(test_data.startup_workaround_attempted, NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func(common_data, NULL, NULL); +} + +ZTEST(ln9310, test_ln9310_battery_unknown) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* + * Chip rev won't matter for statement + * cov so only testing one version. + */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_UNKNOWN); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_true(ln9310_init() != 0, NULL); + zassert_false(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + ln9310_software_enable(true); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); +} + +ZTEST(ln9310, test_ln9310_2s_battery_read_fails) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + i2c_common_emul_set_read_fail_reg(common_data, LN9310_REG_BC_STS_B); + + zassert_true(ln9310_init() != 0, NULL); + zassert_false(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + /* For Battery 2S Versions: Test Read Battery Voltage Failure Too */ + ln9310_emul_reset(emulator); + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + i2c_common_emul_set_read_fail_reg(common_data, LN9310_REG_TRACK_CTRL); + + zassert_false(ln9310_init() == 0, NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST(ln9310, test_ln9310_lion_ctrl_reg_fails) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery won't matter here so only testing one version */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + i2c_common_emul_set_read_fail_reg(common_data, LN9310_REG_LION_CTRL); + + zassert_true(ln9310_init() != 0, NULL); + zassert_false(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + ln9310_software_enable(true); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +struct precharge_timeout_data { + timestamp_t time_to_mock; + bool handled_clearing_standby_en_bit_timeout; +}; + +static int mock_intercept_startup_ctrl_reg(const struct emul *emulator, int reg, + uint8_t val, int bytes, void *data) +{ + struct precharge_timeout_data *test_data = data; + + if (reg == LN9310_REG_STARTUP_CTRL && + test_data->handled_clearing_standby_en_bit_timeout == false) { + if (val == 0) { + timestamp_t time = get_time(); + + time.val += 1 + LN9310_CFLY_PRECHARGE_TIMEOUT; + test_data->time_to_mock = time; + get_time_mock = &test_data->time_to_mock; + } else { + /* ln9310 aborts a startup attempt */ + test_data->handled_clearing_standby_en_bit_timeout = + true; + get_time_mock = NULL; + } + } + return 1; +} + +ZTEST(ln9310, test_ln9310_cfly_precharge_timesout) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + struct precharge_timeout_data test_data = { + .time_to_mock = { + .val = -1, + .le = { + .lo = -1, + .hi = -1, + }, + }, + .handled_clearing_standby_en_bit_timeout = false, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery and chip rev won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, + REQUIRES_CFLY_PRECHARGE_STARTUP_CHIP_REV); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func( + common_data, mock_intercept_startup_ctrl_reg, &test_data); + + ln9310_software_enable(true); + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_true(test_data.handled_clearing_standby_en_bit_timeout, NULL); + /* It only times out on one attempt, it should subsequently startup */ + zassert_true(ln9310_power_good(), NULL); + + i2c_common_emul_set_write_func(common_data, NULL, NULL); +} + +struct reg_to_fail_data { + int reg_access_to_fail; + int reg_access_fail_countdown; +}; + +static int mock_read_intercept_reg_to_fail(const struct emul *emul, int reg, + uint8_t *val, int bytes, void *data) +{ + struct reg_to_fail_data *test_data = data; + + if (reg == test_data->reg_access_to_fail) { + test_data->reg_access_fail_countdown--; + if (test_data->reg_access_fail_countdown <= 0) + return -1; + } + return 1; +} + +ZTEST(ln9310, test_ln9310_interrupt_reg_fail) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + struct reg_to_fail_data test_data = { + .reg_access_to_fail = 0, + .reg_access_fail_countdown = 0, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery and chip rev won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + i2c_common_emul_set_read_func( + common_data, mock_read_intercept_reg_to_fail, &test_data); + + /* Fail in beginning of software enable */ + test_data.reg_access_to_fail = LN9310_REG_INT1; + test_data.reg_access_fail_countdown = 1; + + ln9310_software_enable(true); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + zassert_true(test_data.reg_access_fail_countdown <= 0, NULL); + + /* Fail in irq interrupt handler */ + test_data.reg_access_fail_countdown = 2; + + ln9310_software_enable(true); + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + zassert_false(ln9310_power_good(), NULL); + zassert_true(test_data.reg_access_fail_countdown <= 0, NULL); + + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +ZTEST(ln9310, test_ln9310_sys_sts_reg_fail) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + struct reg_to_fail_data test_data = { + .reg_access_to_fail = 0, + .reg_access_fail_countdown = 0, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery and chip rev won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + i2c_common_emul_set_read_func( + common_data, &mock_read_intercept_reg_to_fail, &test_data); + + /* Register only read once and in the interrupt handler */ + test_data.reg_access_to_fail = LN9310_REG_SYS_STS; + test_data.reg_access_fail_countdown = 1; + + ln9310_software_enable(1); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + + zassert_false(ln9310_power_good(), NULL); + zassert_true(test_data.reg_access_fail_countdown <= 0, NULL); + + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +struct reg_to_intercept { + int reg; + uint8_t replace_val; +}; + +static int mock_read_interceptor(const struct emul *emul, int reg, uint8_t *val, + int bytes, void *data) +{ + struct reg_to_intercept *test_data = data; + + if (test_data->reg == reg) { + *val = test_data->replace_val; + return 0; + } + + return 1; +} + +ZTEST(ln9310, test_ln9310_reset_explicit_detected_startup) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + struct reg_to_intercept test_data = { + .reg = LN9310_REG_LION_CTRL, + .replace_val = 0, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery and chip rev won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + i2c_common_emul_set_read_func(common_data, &mock_read_interceptor, + &test_data); + + ln9310_software_enable(true); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + + zassert_true(ln9310_power_good(), NULL); + + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +ZTEST(ln9310, test_ln9310_update_startup_seq_fails) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + struct reg_to_fail_data test_data = { + .reg_access_to_fail = LN9310_REG_CFG_4, + .reg_access_fail_countdown = 1, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + /* Requires older version of chip */ + ln9310_emul_set_version(emulator, + REQUIRES_CFLY_PRECHARGE_STARTUP_CHIP_REV); + + i2c_common_emul_set_read_func( + common_data, &mock_read_intercept_reg_to_fail, &test_data); + + zassert_false(ln9310_init() == 0, NULL); + zassert_false(ln9310_emul_is_init(emulator), NULL); + + ln9310_software_enable(true); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + + zassert_false(ln9310_power_good(), NULL); + zassert_true(test_data.reg_access_fail_countdown <= 0, NULL); + + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +ZTEST(ln9310, test_ln9310_state_change_only_on_mode_change_interrupt) +{ + const struct emul *emulator = EMUL_DT_GET(EMUL_LN9310_NODE); + struct i2c_common_emul_data *common_data = + emul_ln9310_get_i2c_common_data(emulator); + struct reg_to_intercept test_data = { + .reg = LN9310_REG_INT1, + .replace_val = 0, + }; + + zassert_not_null(emulator, NULL); + + ln9310_emul_set_context(emulator); + ln9310_emul_reset(emulator); + /* Battery and chip rev won't matter here so only testing one pair */ + ln9310_emul_set_battery_cell_type(emulator, BATTERY_CELL_TYPE_2S); + ln9310_emul_set_version(emulator, LN9310_BC_STS_C_CHIP_REV_FIXED); + + zassert_ok(ln9310_init(), NULL); + zassert_true(ln9310_emul_is_init(emulator), NULL); + + i2c_common_emul_set_read_func(common_data, &mock_read_interceptor, + &test_data); + + ln9310_software_enable(true); + + /* TODO(b/201420132) */ + k_msleep(TEST_DELAY_MS); + + zassert_false(ln9310_power_good(), NULL); + + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +static inline void reset_ln9310_state(void) +{ + ln9310_reset_to_initial_state(); + get_time_mock = NULL; +} + +static void ln9310_before(void *state) +{ + ARG_UNUSED(state); + reset_ln9310_state(); +} + +static void ln9310_after(void *state) +{ + ARG_UNUSED(state); + reset_ln9310_state(); +} + +ZTEST_SUITE(ln9310, drivers_predicate_post_main, NULL, ln9310_before, + ln9310_after, NULL); diff --git a/zephyr/test/drivers/default/src/locate_chip.c b/zephyr/test/drivers/default/src/locate_chip.c new file mode 100644 index 0000000000..6842543971 --- /dev/null +++ b/zephyr/test/drivers/default/src/locate_chip.c @@ -0,0 +1,134 @@ +/* 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/fff.h> +#include <zephyr/shell/shell.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "host_command.h" + +/** + * @brief TestPurpose: test the TCPC locate valid case. + */ +ZTEST_USER(locate_chip, test_hc_locate_chip_tcpc) +{ + int ret; + struct ec_params_locate_chip p; + struct ec_response_locate_chip r; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_LOCATE_CHIP, 0, r, p); + + p.type = EC_CHIP_TYPE_TCPC; + p.index = 0; + + ret = host_command_process(&args); + + zassert_equal(ret, EC_RES_SUCCESS, "Unexpected return value: %d", ret); + zassert_equal(r.bus_type, EC_BUS_TYPE_I2C, "Unexpected bus_type: %d", + r.bus_type); + zassert_equal(r.i2c_info.port, 2, "Unexpected port: %d", + r.i2c_info.port); + zassert_equal(r.i2c_info.addr_flags, 0x82, "Unexpected addr_flags: %d", + r.i2c_info.addr_flags); + + p.type = EC_CHIP_TYPE_TCPC; + p.index = 1; + + ret = host_command_process(&args); + + zassert_equal(ret, EC_RES_SUCCESS, "Unexpected return value: %d", ret); + zassert_equal(r.bus_type, EC_BUS_TYPE_I2C, "Unexpected bus_type: %d", + r.bus_type); + zassert_equal(r.i2c_info.port, 3, "Unexpected port: %d", + r.i2c_info.port); + zassert_equal(r.i2c_info.addr_flags, 0x0b, "Unexpected addr_flags: %d", + r.i2c_info.addr_flags); +} + +/** + * @brief TestPurpose: test the TCPC index overflow case. + */ +ZTEST_USER(locate_chip, test_hc_locate_chip_tcpc_overflow) +{ + int ret; + struct ec_params_locate_chip p; + struct ec_response_locate_chip r; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_LOCATE_CHIP, 0, r, p); + + p.type = EC_CHIP_TYPE_TCPC; + p.index = 10; + + ret = host_command_process(&args); + + zassert_equal(ret, EC_RES_OVERFLOW, "Unexpected return value: %d", ret); +} + +/** + * @brief TestPurpose: test the EEPROM locate valid case. + */ +ZTEST_USER(locate_chip, test_hc_locate_chip_eeprom) +{ + int ret; + struct ec_params_locate_chip p; + struct ec_response_locate_chip r; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_LOCATE_CHIP, 0, r, p); + + p.type = EC_CHIP_TYPE_CBI_EEPROM; + p.index = 0; + + ret = host_command_process(&args); + + zassert_equal(ret, EC_RES_SUCCESS, "Unexpected return value: %d", ret); + zassert_equal(r.bus_type, EC_BUS_TYPE_I2C, "Unexpected bus_type: %d", + r.bus_type); + zassert_equal(r.i2c_info.port, I2C_PORT_EEPROM, "Unexpected port: %d", + r.i2c_info.port); + zassert_equal(r.i2c_info.addr_flags, I2C_ADDR_EEPROM_FLAGS, + "Unexpected addr_flags: %d", r.i2c_info.addr_flags); +} + +/** + * @brief TestPurpose: test the EEPROM index overflow case. + */ +ZTEST_USER(locate_chip, test_hc_locate_chip_eeprom_overflow) +{ + int ret; + struct ec_params_locate_chip p; + struct ec_response_locate_chip r; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_LOCATE_CHIP, 0, r, p); + + p.type = EC_CHIP_TYPE_CBI_EEPROM; + p.index = 1; + + ret = host_command_process(&args); + + zassert_equal(ret, EC_RES_OVERFLOW, "Unexpected return value: %d", ret); +} + +/** + * @brief TestPurpose: test the invalid parameter case. + */ +ZTEST_USER(locate_chip, test_hc_locate_chip_invalid) +{ + int ret; + struct ec_params_locate_chip p; + struct ec_response_locate_chip r; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_LOCATE_CHIP, 0, r, p); + + p.type = EC_CHIP_TYPE_COUNT; + ret = host_command_process(&args); + + zassert_equal(ret, EC_RES_INVALID_PARAM, "Unexpected return value: %d", + ret); +} + +ZTEST_SUITE(locate_chip, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/motion_sense/motion_sense.c b/zephyr/test/drivers/default/src/motion_sense/motion_sense.c new file mode 100644 index 0000000000..d39c3ce335 --- /dev/null +++ b/zephyr/test/drivers/default/src/motion_sense/motion_sense.c @@ -0,0 +1,35 @@ +/* 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 "motion_sense.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(motion_sense, drivers_predicate_post_main, NULL, NULL, NULL, NULL); + +ZTEST_USER(motion_sense, ec_motion_sensor_fill_values) +{ + struct ec_response_motion_sensor_data dst = { + .data = { 1, 2, 3 }, + }; + const int32_t v[] = { 4, 5, 6 }; + + ec_motion_sensor_fill_values(&dst, v); + zassert_equal(dst.data[0], v[0], NULL); + zassert_equal(dst.data[1], v[1], NULL); + zassert_equal(dst.data[2], v[2], NULL); +} + +ZTEST_USER(motion_sense, ec_motion_sensor_clamp_i16) +{ + zassert_equal(ec_motion_sensor_clamp_i16(0), 0, NULL); + zassert_equal(ec_motion_sensor_clamp_i16(200), 200, NULL); + zassert_equal(ec_motion_sensor_clamp_i16(-512), -512, NULL); + zassert_equal(ec_motion_sensor_clamp_i16(INT16_MAX + 1), INT16_MAX, + NULL); + zassert_equal(ec_motion_sensor_clamp_i16(INT16_MIN - 1), INT16_MIN, + NULL); +} diff --git a/zephyr/test/drivers/default/src/panic.c b/zephyr/test/drivers/default/src/panic.c new file mode 100644 index 0000000000..4b97ed63d9 --- /dev/null +++ b/zephyr/test/drivers/default/src/panic.c @@ -0,0 +1,108 @@ +/* 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. + */ + +/** + * @file + * @brief Unit Tests for panic. + */ + +#include <zephyr/device.h> + +#include <zephyr/logging/log.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "common.h" +#include "ec_tasks.h" +#include "panic.h" +#include "test/drivers/stubs.h" +#include "test/drivers/test_state.h" + +struct panic_test_fixture { + struct panic_data saved_pdata; +}; + +static void *panic_test_setup(void) +{ + static struct panic_test_fixture panic_fixture = { 0 }; + + return &panic_fixture; +} + +static void panic_before(void *state) +{ + struct panic_test_fixture *fixture = state; + struct panic_data *pdata = get_panic_data_write(); + + ARG_UNUSED(state); + + fixture->saved_pdata = *pdata; +} + +static void panic_after(void *state) +{ + struct panic_test_fixture *fixture = state; + struct panic_data *pdata = get_panic_data_write(); + + ARG_UNUSED(state); + + *pdata = fixture->saved_pdata; +} + +/** + * @brief Test Suite: Verifies panic functionality. + */ +ZTEST_SUITE(panic, drivers_predicate_post_main, panic_test_setup, panic_before, + panic_after, NULL); + +/** + * @brief TestPurpose: Verify panic set/get reason. + * + * @details + * Validate panic set/get reason. + * + * Expected Results + * - Success + */ +ZTEST(panic, test_panic_reason) +{ + uint32_t reason; + uint32_t info; + uint8_t exception; + struct panic_data *pdata = panic_get_data(); + + zassert_is_null(pdata, NULL); + panic_set_reason(PANIC_SW_WATCHDOG, 0, 0); + + panic_get_reason(&reason, &info, &exception); + + zassert_equal(PANIC_SW_WATCHDOG, reason, NULL); + zassert_equal(0, info, NULL); + zassert_equal(0, exception, NULL); + + pdata = panic_get_data(); + zassert_not_null(pdata, NULL); + zassert_equal(pdata->struct_version, 2, NULL); + zassert_equal(pdata->magic, PANIC_DATA_MAGIC, NULL); + zassert_equal(pdata->struct_size, CONFIG_PANIC_DATA_SIZE, NULL); + + panic_data_print(pdata); +} + +ZTEST(panic, test_panic_data_start_bad_magic) +{ + struct panic_data *pdata = get_panic_data_write(); + + pdata->magic = PANIC_DATA_MAGIC + 1; + zassert_equal(0, get_panic_data_start(), NULL); +} + +ZTEST(panic, test_get_panic_data_start) +{ + struct panic_data *pdata = get_panic_data_write(); + + pdata->magic = PANIC_DATA_MAGIC; + zassert_equal((uintptr_t)pdata, get_panic_data_start(), NULL); +} diff --git a/zephyr/test/drivers/default/src/panic_output.c b/zephyr/test/drivers/default/src/panic_output.c new file mode 100644 index 0000000000..210c862901 --- /dev/null +++ b/zephyr/test/drivers/default/src/panic_output.c @@ -0,0 +1,74 @@ +/* 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 "panic.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(panic_output, drivers_predicate_post_main, NULL, NULL, NULL, NULL); + +ZTEST(panic_output, test_panic_printf) +{ + panic_printf("test output string from %s\n", __func__); +} + +ZTEST(panic_output, test_panic_puts) +{ + panic_puts("test output string\n"); +} + +ZTEST(panic_output, test_panic_sw_reason_is_valid) +{ + zassert_false(panic_sw_reason_is_valid(PANIC_SW_BASE - 1), NULL); + /* PANIC_SW_DIV_ZERO */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE), NULL); + /* PANIC_SW_STACK_OVERFLOW */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE + 1), NULL); + /* PANIC_SW_PD_CRASH */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE + 2), NULL); + /* PANIC_SW_ASSERT */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE + 3), NULL); + /* PANIC_SW_WATCHDOG */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE + 4), NULL); + /* PANIC_SW_RNG */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE + 5), NULL); + /* PANIC_SW_PMIC_FAULT */ + zassert_true(panic_sw_reason_is_valid(PANIC_SW_BASE + 6), NULL); + zassert_false(panic_sw_reason_is_valid(PANIC_SW_BASE + 7), NULL); +} + +ZTEST(panic_output, test_panic) +{ + panic(__func__); + zassert_equal(1, system_reset_fake.call_count, + "Expected system_reset() to be called once, but was " + "called %d times", + system_reset_fake.call_count); + zassert_equal(0, system_reset_fake.arg0_val, + "Expected system_reset() to be called with flags=0, but " + "got flags=%d", + system_reset_fake.arg0_val); +} + +ZTEST(panic_output, test_panic_assert_fail) +{ + int line_num = __LINE__; + + panic_assert_fail("Test panic message", __func__, __FILE__, line_num); + zassert_equal(1, software_panic_fake.call_count, + "Expected sofware_panic() to be called once, but was " + "called %d times", + software_panic_fake.call_count); + zassert_equal(PANIC_SW_ASSERT, software_panic_fake.arg0_val, + "Expected software_panic() to be called with " + "reason=%d (PANIC_SW_ASSERT) but got %d", + PANIC_SW_ASSERT, software_panic_fake.arg0_val); + zassert_equal(line_num, software_panic_fake.arg1_val, + "Expected software_panic() to be called with " + "line=%d but got %d", + line_num, software_panic_fake.arg1_val); +} diff --git a/zephyr/test/drivers/default/src/port80.c b/zephyr/test/drivers/default/src/port80.c new file mode 100644 index 0000000000..8563c2e478 --- /dev/null +++ b/zephyr/test/drivers/default/src/port80.c @@ -0,0 +1,191 @@ +/* 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. + */ + +/** + * @file + * @brief Unit Tests for ESPI port 80 writes + */ + +#include <zephyr/logging/log.h> +#include <zephyr/shell/shell.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "ec_commands.h" +#include "host_command.h" +#include "port80.h" + +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +/* + * Flush any existing writes. + */ +static void port80_flush(void) +{ + zassert_ok(shell_execute_cmd(get_ec_shell(), "port80 flush"), NULL); +} + +/** + * @brief TestPurpose: Verify port 80 writes + * + * @details + * Validate that the port 80 writes are processed correctly. + * + * Expected Results + * - The port 80 writes are received + */ +ZTEST(port80, test_port80_write) +{ + struct ec_response_port80_read response; + struct ec_params_port80_read params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_PORT80_READ, 1, response, params); + + port80_flush(); + port_80_write(0x12); + port_80_write(0x34); + /* Check the buffer using the host cmd */ + + /* Get the buffer info */ + params.subcmd = EC_PORT80_GET_INFO; + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response.get_info), NULL); + zassert_equal(response.get_info.writes, 2, NULL); + /* Read the buffer */ + params.subcmd = EC_PORT80_READ_BUFFER; + params.read_buffer.offset = 0; + params.read_buffer.num_entries = 2; + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(uint16_t) * 2, NULL); + zassert_equal(response.data.codes[0], 0x12, NULL); + zassert_equal(response.data.codes[1], 0x34, NULL); +} + +/** + * @brief TestPurpose: Verify port 80 read parameters + * + * @details + * Validate that the port 80 read parameters are checked + * + * Expected Results + * - The port 80 parameters are verified + */ +ZTEST(port80, test_port80_offset) +{ + struct ec_response_port80_read response; + struct ec_params_port80_read params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_PORT80_READ, 1, response, params); + + port80_flush(); + + params.subcmd = EC_PORT80_READ_BUFFER; + params.read_buffer.offset = 0; + params.read_buffer.num_entries = 0; + zassert_equal(host_command_process(&args), EC_RES_INVALID_PARAM, NULL); + params.read_buffer.offset = 0xFFFF; + params.read_buffer.num_entries = 2; + zassert_equal(host_command_process(&args), EC_RES_INVALID_PARAM, NULL); + params.read_buffer.offset = 0; + params.read_buffer.num_entries = 0xFFFF; + zassert_equal(host_command_process(&args), EC_RES_INVALID_PARAM, NULL); +} + +/** + * @brief TestPurpose: Verify port 80 reset event + * + * @details + * Validate that the port 80 handling works for the reset event + * + * Expected Results + * - The port 80 handling detects the reset event. + */ +ZTEST(port80, test_port80_special) +{ + struct ec_params_port80_read params; + struct ec_response_port80_last_boot response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_PORT80_READ, 0, response, params); + + port80_flush(); + port_80_write(0xDEAD); + port_80_write(0xAA); /* must be < 0x100 */ + port_80_write(PORT_80_EVENT_RESET); + /* Check the buffer using the host cmd version 0*/ + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(response.code, 0xAA, NULL); +} + +/** + * @brief TestPurpose: Verify port 80 subcommand + * + * @details + * Validate that the port 80 host subcommand is checked. + * + * Expected Results + * - The port 80 handling detects an invalid subcommand. + */ +ZTEST(port80, test_port80_subcmd) +{ + struct ec_params_port80_read params; + struct ec_response_port80_last_boot response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_PORT80_READ, 1, response, params); + + params.subcmd = 0xFFFF; + zassert_ok(!host_command_process(&args), NULL); +} + +/** + * @brief TestPurpose: Verify port 80 write wrap + * + * @details + * Validate that the port 80 host writes wrap around. + * + * Expected Results + * - The port 80 writes overwrites the history array. + */ +ZTEST(port80, test_port80_wrap) +{ + struct ec_params_port80_read params; + struct ec_response_port80_read response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_PORT80_READ, 1, response, params); + uint32_t size, count; + + port80_flush(); + /* Get the history array size */ + params.subcmd = EC_PORT80_GET_INFO; + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response.get_info), NULL); + size = response.get_info.history_size; + count = size + size / 2; /* Ensure write will wrap the history */ + for (uint32_t i = 0; i < count; i++) { + port_80_write(i); + } + /* + * Retrieve the first entry in the history array. + * It should equal the size of the array. + */ + params.subcmd = EC_PORT80_READ_BUFFER; + params.read_buffer.offset = 0; + params.read_buffer.num_entries = 1; + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(uint16_t), NULL); + zassert_equal(response.data.codes[0], size, NULL); +} + +/** + * @brief Test Suite: Verifies port 80 writes. + */ +ZTEST_SUITE(port80, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/power_common.c b/zephyr/test/drivers/default/src/power_common.c new file mode 100644 index 0000000000..3579cb7b3c --- /dev/null +++ b/zephyr/test/drivers/default/src/power_common.c @@ -0,0 +1,696 @@ +/* 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 <string.h> +#include <zephyr/ztest.h> +#include <zephyr/drivers/gpio.h> +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <zephyr/shell/shell.h> +#include <zephyr/shell/shell_dummy.h> +#include <zephyr/shell/shell_uart.h> + +#include "chipset.h" +#include "common.h" +#include "extpower.h" +#include "hooks.h" +#include "host_command.h" +#include "power.h" +#include "test/drivers/stubs.h" +#include "task.h" +#include "ec_tasks.h" +#include "test/drivers/test_state.h" + +#include "emul/emul_common_i2c.h" +#include "emul/emul_smart_battery.h" + +#include "battery.h" +#include "battery_smart.h" +#include "test/drivers/utils.h" + +#define BATTERY_NODE DT_NODELABEL(battery) + +/* Description of all power states with chipset state masks */ +static struct { + /* Power state */ + enum power_state p_state; + /* + * CHIPSET_STATE_* to which this state transition (the same as + * transition_from for static states) + */ + int transition_to; + /* CHIPSET_STATE_* from which this state transition */ + int transition_from; +} test_power_state_desc[] = { + { + .p_state = POWER_G3, + .transition_to = CHIPSET_STATE_HARD_OFF, + .transition_from = CHIPSET_STATE_HARD_OFF, + }, + { + .p_state = POWER_G3S5, + .transition_to = CHIPSET_STATE_SOFT_OFF, + .transition_from = CHIPSET_STATE_HARD_OFF, + }, + { + .p_state = POWER_S5G3, + .transition_to = CHIPSET_STATE_HARD_OFF, + .transition_from = CHIPSET_STATE_SOFT_OFF, + }, + { + .p_state = POWER_S5, + .transition_to = CHIPSET_STATE_SOFT_OFF, + .transition_from = CHIPSET_STATE_SOFT_OFF, + }, + { + .p_state = POWER_S5S3, + .transition_to = CHIPSET_STATE_SUSPEND, + .transition_from = CHIPSET_STATE_SOFT_OFF, + }, + { + .p_state = POWER_S3S5, + .transition_to = CHIPSET_STATE_SOFT_OFF, + .transition_from = CHIPSET_STATE_SUSPEND, + }, + { + .p_state = POWER_S3, + .transition_to = CHIPSET_STATE_SUSPEND, + .transition_from = CHIPSET_STATE_SUSPEND, + }, + { + .p_state = POWER_S3S0, + .transition_to = CHIPSET_STATE_ON, + .transition_from = CHIPSET_STATE_SUSPEND, + }, + { + .p_state = POWER_S0S3, + .transition_to = CHIPSET_STATE_SUSPEND, + .transition_from = CHIPSET_STATE_ON, + }, + { + .p_state = POWER_S0, + .transition_to = CHIPSET_STATE_ON, + .transition_from = CHIPSET_STATE_ON, + }, +}; + +/* + * Chipset state masks used by chipset_in_state and + * chipset_in_or_transitioning_to_state tests + */ +static int in_state_test_masks[] = { + CHIPSET_STATE_HARD_OFF, + CHIPSET_STATE_SOFT_OFF, + CHIPSET_STATE_SUSPEND, + CHIPSET_STATE_ON, + CHIPSET_STATE_STANDBY, + CHIPSET_STATE_ANY_OFF, + CHIPSET_STATE_ANY_SUSPEND, + CHIPSET_STATE_ANY_SUSPEND | CHIPSET_STATE_SOFT_OFF, +}; + +/** Test chipset_in_state() for each state */ +ZTEST(power_common_no_tasks, test_power_chipset_in_state) +{ + bool expected_in_state; + bool transition_from; + bool transition_to; + bool in_state; + int mask; + + for (int i = 0; i < ARRAY_SIZE(test_power_state_desc); i++) { + /* Set given power state */ + power_set_state(test_power_state_desc[i].p_state); + /* Test with selected state masks */ + for (int j = 0; j < ARRAY_SIZE(in_state_test_masks); j++) { + mask = in_state_test_masks[j]; + /* + * Currently tested mask match with state if it match + * with transition_to and from chipset states + */ + transition_to = mask & + test_power_state_desc[i].transition_to; + transition_from = + mask & test_power_state_desc[i].transition_from; + expected_in_state = transition_to && transition_from; + in_state = chipset_in_state(mask); + zassert_equal(expected_in_state, in_state, + "Wrong chipset_in_state() == %d, " + "should be %d; mask 0x%x; power state %d " + "in test case %d", + in_state, expected_in_state, mask, + test_power_state_desc[i].p_state, i); + } + } +} + +/** Test chipset_in_or_transitioning_to_state() for each state */ +ZTEST(power_common_no_tasks, test_power_chipset_in_or_transitioning_to_state) +{ + bool expected_in_state; + bool in_state; + int mask; + + for (int i = 0; i < ARRAY_SIZE(test_power_state_desc); i++) { + /* Set given power state */ + power_set_state(test_power_state_desc[i].p_state); + /* Test with selected state masks */ + for (int j = 0; j < ARRAY_SIZE(in_state_test_masks); j++) { + mask = in_state_test_masks[j]; + /* + * Currently tested mask match with state if it match + * with transition_to chipset state + */ + expected_in_state = + mask & test_power_state_desc[i].transition_to; + in_state = chipset_in_or_transitioning_to_state(mask); + zassert_equal(expected_in_state, in_state, + "Wrong " + "chipset_in_or_transitioning_to_state() " + "== %d, should be %d; mask 0x%x; " + "power state %d in test case %d", + in_state, expected_in_state, mask, + test_power_state_desc[i].p_state, i); + } + } +} + +/* Test using chipset_exit_hard_off() in different power states. The only + * way to test the value of want_g3_exit is to set the power state to G3 + * and then to see if test_power_common_state() transitions to G3S5 or not. + */ +ZTEST(power_common_no_tasks, test_power_exit_hard_off) +{ + /* + * Every test runs in a new thread, we need to add this thread to the + * dynamic shimmed tasks or this test will fail. + */ + set_test_runner_tid(); + + /* Force initial state */ + power_set_state(POWER_G3); + test_power_common_state(); + zassert_equal(POWER_G3, power_get_state(), NULL); + + /* Test after exit hard off, we reach G3S5 */ + chipset_exit_hard_off(); + test_power_common_state(); + zassert_equal(POWER_G3S5, power_get_state(), NULL); + + /* Go back to G3 and check we stay there */ + power_set_state(POWER_G3); + test_power_common_state(); + zassert_equal(POWER_G3, power_get_state(), NULL); + + /* Exit G3 again */ + chipset_exit_hard_off(); + test_power_common_state(); + zassert_equal(POWER_G3S5, power_get_state(), NULL); + + /* Go to S5G3 */ + power_set_state(POWER_S5G3); + test_power_common_state(); + zassert_equal(POWER_S5G3, power_get_state(), NULL); + + /* Test exit hard off in S5G3 -- should set want_g3_exit */ + chipset_exit_hard_off(); + /* Go back to G3 and check we exit it to G3S5 */ + power_set_state(POWER_G3); + test_power_common_state(); + zassert_equal(POWER_G3S5, power_get_state(), NULL); + + /* Test exit hard off is cleared on entering S5 */ + chipset_exit_hard_off(); + power_set_state(POWER_S5); + test_power_common_state(); + zassert_equal(POWER_S5, power_get_state(), NULL); + + /* Go back to G3 and check we stay in G3 */ + power_set_state(POWER_G3); + test_power_common_state(); + zassert_equal(POWER_G3, power_get_state(), NULL); + + /* Test exit hard off doesn't work on other states */ + power_set_state(POWER_S5S3); + test_power_common_state(); + zassert_equal(POWER_S5S3, power_get_state(), NULL); + chipset_exit_hard_off(); + test_power_common_state(); + + /* Go back to G3 and check we stay in G3 */ + power_set_state(POWER_G3); + test_power_common_state(); + zassert_equal(POWER_G3, power_get_state(), NULL); +} + +/* Test reboot ap on g3 host command is triggering reboot */ +ZTEST(power_common_no_tasks, test_power_reboot_ap_at_g3) +{ + struct ec_params_reboot_ap_on_g3_v1 params; + struct host_cmd_handler_args args = { + .command = EC_CMD_REBOOT_AP_ON_G3, + .version = 0, + .send_response = stub_send_response_callback, + .params = ¶ms, + .params_size = sizeof(params), + }; + int delay_ms; + int64_t before_time; + + /* + * Every test runs in a new thread, we need to add this thread to the + * dynamic shimmed tasks or this test will fail. + */ + set_test_runner_tid(); + + /* Force initial state S0 */ + power_set_state(POWER_S0); + test_power_common_state(); + zassert_equal(POWER_S0, power_get_state(), NULL); + + /* Test version 0 (no delay argument) */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + /* Go to G3 and check if reboot is triggered */ + power_set_state(POWER_G3); + test_power_common_state(); + zassert_equal(POWER_G3S5, power_get_state(), NULL); + + /* Test version 1 (with delay argument) */ + args.version = 1; + delay_ms = 3000; + params.reboot_ap_at_g3_delay = delay_ms / 1000; /* in seconds */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + /* Go to G3 and check if reboot is triggered after delay */ + power_set_state(POWER_G3); + before_time = k_uptime_get(); + test_power_common_state(); + zassert_true(k_uptime_delta(&before_time) >= 3000, NULL); + zassert_equal(POWER_G3S5, power_get_state(), NULL); +} + +/** Test setting cutoff and stay-up battery levels through host command */ +ZTEST(power_common, test_power_hc_smart_discharge) +{ + struct ec_response_smart_discharge response; + struct ec_params_smart_discharge params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_SMART_DISCHARGE, 0, response, params); + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + int hours_to_zero; + int hibern_drate; + int cutoff_drate; + int stayup_cap; + int cutoff_cap; + + /* Set up host command parameters */ + params.flags = EC_SMART_DISCHARGE_FLAGS_SET; + + /* Test fail when battery capacity is not available */ + i2c_common_emul_set_read_fail_reg(common_data, SB_FULL_CHARGE_CAPACITY); + zassert_equal(EC_RES_UNAVAILABLE, host_command_process(&args), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup discharge rates */ + params.drate.hibern = 10; + params.drate.cutoff = 100; + /* Test fail on higher discahrge in hibernation than cutoff */ + zassert_equal(EC_RES_INVALID_PARAM, host_command_process(&args), NULL); + + /* Setup discharge rates */ + params.drate.hibern = 10; + params.drate.cutoff = 0; + /* Test fail on only one discharge rate set to 0 */ + zassert_equal(EC_RES_INVALID_PARAM, host_command_process(&args), NULL); + + /* Setup correct parameters */ + hours_to_zero = 1000; + hibern_drate = 100; /* uA */ + cutoff_drate = 10; /* uA */ + /* Need at least 100 mA capacity to stay 1000h using 0.1mAh */ + stayup_cap = hibern_drate * hours_to_zero / 1000; + /* Need at least 10 mA capacity to stay 1000h using 0.01mAh */ + cutoff_cap = cutoff_drate * hours_to_zero / 1000; + + params.drate.hibern = hibern_drate; + params.drate.cutoff = cutoff_drate; + params.hours_to_zero = hours_to_zero; + + /* Test if correct values are set */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + zassert_equal(hibern_drate, response.drate.hibern, NULL); + zassert_equal(cutoff_drate, response.drate.cutoff, NULL); + zassert_equal(hours_to_zero, response.hours_to_zero, NULL); + zassert_equal(stayup_cap, response.dzone.stayup, NULL); + zassert_equal(cutoff_cap, response.dzone.cutoff, NULL); + + /* Setup discharge rate to 0 */ + params.drate.hibern = 0; + params.drate.cutoff = 0; + /* Update hours to zero */ + hours_to_zero = 2000; + params.hours_to_zero = hours_to_zero; + /* Need at least 200 mA capacity to stay 2000h using 0.1mAh */ + stayup_cap = hibern_drate * hours_to_zero / 1000; + /* Need at least 20 mA capacity to stay 2000h using 0.01mAh */ + cutoff_cap = cutoff_drate * hours_to_zero / 1000; + + /* Test that command doesn't change drate but apply new hours to zero */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + zassert_equal(hibern_drate, response.drate.hibern, NULL); + zassert_equal(cutoff_drate, response.drate.cutoff, NULL); + zassert_equal(hours_to_zero, response.hours_to_zero, NULL); + zassert_equal(stayup_cap, response.dzone.stayup, NULL); + zassert_equal(cutoff_cap, response.dzone.cutoff, NULL); + + /* Setup any parameters != 0 */ + params.drate.hibern = 1000; + params.drate.cutoff = 1000; + /* Clear set flag */ + params.flags = 0; + + /* Test that command doesn't change drate and dzone */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + zassert_equal(hibern_drate, response.drate.hibern, NULL); + zassert_equal(cutoff_drate, response.drate.cutoff, NULL); + zassert_equal(hours_to_zero, response.hours_to_zero, NULL); + zassert_equal(stayup_cap, response.dzone.stayup, NULL); + zassert_equal(cutoff_cap, response.dzone.cutoff, NULL); +} + +/** + * Test if default board_system_is_idle() recognize cutoff and stay-up + * levels correctly. + */ +ZTEST(power_common, test_power_board_system_is_idle) +{ + struct ec_response_smart_discharge response; + struct ec_params_smart_discharge params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_SMART_DISCHARGE, 0, response, params); + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + uint64_t last_shutdown_time = 0; + uint64_t target; + uint64_t now; + + bat = sbat_emul_get_bat_data(emul); + + /* Set up host command parameters */ + params.drate.hibern = 100; /* uA */ + params.drate.cutoff = 10; /* uA */ + params.hours_to_zero = 1000; /* h */ + params.flags = EC_SMART_DISCHARGE_FLAGS_SET; + /* Set stay-up and cutoff zones */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + /* Test shutdown ignore is send when target time is in future */ + target = 1125; + now = 1000; + zassert_equal(CRITICAL_SHUTDOWN_IGNORE, + board_system_is_idle(last_shutdown_time, &target, now), + NULL); + + /* Set "now" time after target time */ + now = target + 30; + + /* + * Test hibernation is requested when battery remaining capacity + * is not available + */ + i2c_common_emul_set_read_fail_reg(common_data, SB_REMAINING_CAPACITY); + zassert_equal(CRITICAL_SHUTDOWN_HIBERNATE, + board_system_is_idle(last_shutdown_time, &target, now), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Setup remaining capacity to trigger cutoff */ + bat->cap = response.dzone.cutoff - 5; + zassert_equal(CRITICAL_SHUTDOWN_CUTOFF, + board_system_is_idle(last_shutdown_time, &target, now), + NULL); + + /* Setup remaining capacity to trigger stay-up and ignore shutdown */ + bat->cap = response.dzone.stayup - 5; + zassert_equal(CRITICAL_SHUTDOWN_IGNORE, + board_system_is_idle(last_shutdown_time, &target, now), + NULL); + + /* Setup remaining capacity to be in safe zone to hibernate */ + bat->cap = response.dzone.stayup + 5; + zassert_equal(CRITICAL_SHUTDOWN_HIBERNATE, + board_system_is_idle(last_shutdown_time, &target, now), + NULL); +} + +/** + * Test power console command + */ +ZTEST(power_common, power_console_cmd) +{ + const char *buffer; + size_t buffer_size; + + test_set_chipset_to_g3(); + shell_backend_dummy_clear_output(get_ec_shell()); + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "power"), + NULL); + buffer = shell_backend_dummy_get_output(get_ec_shell(), &buffer_size); + zassert_true(strcmp(buffer, "\r\noff\r\n") == 0 || + strcmp(buffer, "\r\nOFF\r\n") == 0, + "Invalid console output %s", buffer); + + test_set_chipset_to_s0(); + shell_backend_dummy_clear_output(get_ec_shell()); + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "power"), + NULL); + buffer = shell_backend_dummy_get_output(get_ec_shell(), &buffer_size); + zassert_true(strcmp(buffer, "\r\non\r\n") == 0 || + strcmp(buffer, "\r\nON\r\n") == 0, + "Invalid console output %s", buffer); + + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "power x"), NULL); + + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "power on"), + NULL); + + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "power off"), NULL); +} + +/** + * Common setup for hibernation delay tests. Smart discharge zone is setup, + * battery is set in safe zone (which trigger hibernation), power state is + * set to G3 and AC is disabled. system_hibernate mock is reset. + */ +static void setup_hibernation_delay(void *state) +{ + struct ec_response_smart_discharge response; + struct ec_params_smart_discharge params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_SMART_DISCHARGE, 0, response, params); + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + ARG_UNUSED(state); + + bat = sbat_emul_get_bat_data(emul); + + /* Setup smart discharge zone and set capacity to safe zone */ + params.drate.hibern = 100; /* uA */ + params.drate.cutoff = 10; /* uA */ + params.hours_to_zero = 10000; /* h */ + params.flags = EC_SMART_DISCHARGE_FLAGS_SET; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + /* + * Make sure that battery is in safe zone in good condition to + * not trigger hibernate in charge_state_v2.c + */ + bat->cap = response.dzone.stayup + 5; + bat->volt = battery_get_info()->voltage_normal; + + /* Force initial state */ + test_set_chipset_to_g3(); + + /* Disable AC */ + set_ac_enabled(false); + + RESET_FAKE(system_hibernate); +} + +/** Test setting hibernation delay through host command */ +ZTEST(power_common_hibernation, test_power_hc_hibernation_delay) +{ + struct ec_response_hibernation_delay response; + struct ec_params_hibernation_delay params; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND( + EC_CMD_HIBERNATION_DELAY, 0, response, params); + uint32_t h_delay; + int sleep_time; + + /* Ensure the lid is closed so AC connect does not boot system */ + zassert_ok(shell_execute_cmd(get_ec_shell(), "lidclose"), NULL); + + zassert_equal(power_get_state(), POWER_G3, + "Power state is %d, expected G3", power_get_state()); + /* This is a no-op, but it will reset the last_shutdown_time. */ + power_set_state(POWER_G3); + + /* Set hibernate delay */ + h_delay = 9; + params.seconds = h_delay; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + zassert_equal(0, response.time_g3, "Time from last G3 enter %d != 0", + response.time_g3); + zassert_equal(h_delay, response.time_remaining, + "Time to hibernation %d != %d", response.time_remaining, + h_delay); + zassert_equal(h_delay, response.hibernate_delay, + "Hibernation delay %d != %d", h_delay, + response.hibernate_delay); + + /* Kick chipset task to process new hibernation delay */ + task_wake(TASK_ID_CHIPSET); + /* Wait some arbitrary time less than hibernate delay */ + sleep_time = 6; + k_msleep(sleep_time * 1000); + + /* Get hibernate delay */ + params.seconds = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + zassert_equal(sleep_time, response.time_g3, + "Time from last G3 enter %d != %d", response.time_g3, + sleep_time); + zassert_equal(h_delay - sleep_time, response.time_remaining, + "Time to hibernation %d != %d", response.time_remaining, + h_delay - sleep_time); + zassert_equal(h_delay, response.hibernate_delay, + "Hibernation delay %d != %d", h_delay, + response.hibernate_delay); + zassert_equal(0, system_hibernate_fake.call_count, + "system_hibernate() shouldn't be called before delay"); + + /* Wait to end of the hibenate delay */ + k_msleep((h_delay - sleep_time) * 1000); + + /* Get hibernate delay */ + params.seconds = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + zassert_equal(h_delay, response.time_g3, + "Time from last G3 enter %d != %d", response.time_g3, + h_delay); + zassert_equal(0, response.time_remaining, "Time to hibernation %d != 0", + response.time_remaining); + zassert_equal(h_delay, response.hibernate_delay, + "Hibernation delay %d != %d", h_delay, + response.hibernate_delay); + zassert_equal(1, system_hibernate_fake.call_count, + "system_hibernate() should be called after delay %d", + system_hibernate_fake.call_count); + + /* Wait some more time */ + k_msleep(2000); + + /* Get hibernate delay */ + params.seconds = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + /* After hibernation, remaining time shouldn't be negative */ + zassert_equal(0, response.time_remaining, "Time to hibernation %d != 0", + response.time_remaining); + + /* Enable AC */ + set_ac_enabled(true); + + /* Reset system_hibernate fake to check that it is not called on AC */ + RESET_FAKE(system_hibernate); + /* Allow chipset task to spin with enabled AC */ + task_wake(TASK_ID_CHIPSET); + k_msleep(1); + + /* Get hibernate delay */ + params.seconds = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + zassert_equal(0, response.time_g3, + "Time from last G3 enter %d should be 0 on AC", + response.time_g3); + zassert_equal(0, system_hibernate_fake.call_count, + "system_hibernate() shouldn't be called on AC"); + + /* Disable AC */ + set_ac_enabled(false); + + /* Go to different state */ + power_set_state(POWER_G3S5); + zassert_equal(POWER_G3S5, power_get_state(), NULL); + + /* Get hibernate delay */ + params.seconds = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + + zassert_equal(0, response.time_g3, + "Time from last G3 enter %d should be 0 on state != G3", + response.time_g3); +} + +/** Test setting hibernation delay through UART command */ +ZTEST(power_common_hibernation, test_power_cmd_hibernation_delay) +{ + uint32_t h_delay; + int sleep_time; + + zassert_equal(power_get_state(), POWER_G3, + "Power state is %d, expected G3", power_get_state()); + /* This is a no-op, but it will reset the last_shutdown_time. */ + power_set_state(POWER_G3); + + /* Test success on call without argument */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "hibdelay"), + NULL); + + /* Test error on hibernation delay argument that is not a number */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "hibdelay test1"), + NULL); + + /* Set hibernate delay */ + h_delay = 3; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "hibdelay 3"), NULL); + + /* Kick chipset task to process new hibernation delay */ + task_wake(TASK_ID_CHIPSET); + /* Wait some arbitrary time less than hibernate delay */ + sleep_time = 2; + k_msleep(sleep_time * 1000); + + zassert_equal(0, system_hibernate_fake.call_count, + "system_hibernate() shouldn't be called before delay"); + + /* Wait to end of the hibenate delay */ + k_msleep((h_delay - sleep_time) * 1000); + + zassert_equal(1, system_hibernate_fake.call_count, + "system_hibernate() should be called after delay %d", + system_hibernate_fake.call_count); +} + +ZTEST_SUITE(power_common_no_tasks, drivers_predicate_pre_main, NULL, NULL, NULL, + NULL); + +ZTEST_SUITE(power_common, drivers_predicate_post_main, NULL, NULL, NULL, NULL); + +ZTEST_SUITE(power_common_hibernation, drivers_predicate_post_main, NULL, + setup_hibernation_delay, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/ppc_sn5s330.c b/zephyr/test/drivers/default/src/ppc_sn5s330.c new file mode 100644 index 0000000000..c9ba62cf20 --- /dev/null +++ b/zephyr/test/drivers/default/src/ppc_sn5s330.c @@ -0,0 +1,689 @@ +/* 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/device.h> +#include <zephyr/devicetree.h> +#include <zephyr/drivers/emul.h> +#include <zephyr/ztest.h> +#include <zephyr/fff.h> + +#include "driver/ppc/sn5s330.h" +#include "driver/ppc/sn5s330_public.h" +#include "emul/emul_common_i2c.h" +#include "emul/emul_sn5s330.h" +#include "usbc_ppc.h" +#include "test/drivers/test_mocks.h" +#include "test/drivers/test_state.h" + +/** This must match the index of the sn5s330 in ppc_chips[] */ +#define SN5S330_PORT 0 +#define EMUL EMUL_DT_GET(DT_NODELABEL(sn5s330_emul)) +#define COMMON_DATA emul_sn5s330_get_i2c_common_data(EMUL) +#define FUNC_SET1_ILIMPP1_MSK 0x1F +#define SN5S330_INTERRUPT_DELAYMS 15 + +FAKE_VOID_FUNC(sn5s330_emul_interrupt_set_stub); + +/* + * TODO(b/203364783): Exclude other threads from interacting with the emulator + * to avoid test flakiness + */ + +struct intercept_write_data { + int reg_to_intercept; + uint8_t val_intercepted; +}; + +struct intercept_read_data { + int reg_to_intercept; + bool replace_reg_val; + uint8_t replacement_val; +}; + +static int intercept_read_func(const struct emul *emul, int reg, uint8_t *val, + int bytes, void *data) +{ + struct intercept_read_data *test_data = data; + + if (test_data->reg_to_intercept && test_data->replace_reg_val) + *val = test_data->replacement_val; + + return EC_SUCCESS; +} + +static int intercept_write_func(const struct emul *emul, int reg, uint8_t val, + int bytes, void *data) +{ + struct intercept_write_data *test_data = data; + + if (test_data->reg_to_intercept == reg) + test_data->val_intercepted = val; + + return 1; +} + +static int fail_until_write_func(const struct emul *emul, int reg, uint8_t val, + int bytes, void *data) +{ + uint32_t *count = data; + + if (*count != 0) { + *count -= 1; + return -EIO; + } + return 1; +} + +ZTEST(ppc_sn5s330, test_fail_once_func_set1) +{ + const struct emul *emul = EMUL; + uint32_t count = 1; + uint8_t func_set1_value; + + i2c_common_emul_set_write_func(COMMON_DATA, fail_until_write_func, + &count); + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + zassert_equal(count, 0, NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET1, &func_set1_value); + zassert_true((func_set1_value & SN5S330_ILIM_1_62) != 0, NULL); + i2c_common_emul_set_write_func(COMMON_DATA, NULL, NULL); +} + +ZTEST(ppc_sn5s330, test_dead_battery_boot_force_pp2_fets_set) +{ + struct intercept_write_data test_write_data = { + .reg_to_intercept = SN5S330_FUNC_SET3, + .val_intercepted = 0, + }; + struct intercept_read_data test_read_data = { + .reg_to_intercept = SN5S330_INT_STATUS_REG4, + .replace_reg_val = true, + .replacement_val = SN5S330_DB_BOOT, + }; + + i2c_common_emul_set_write_func(COMMON_DATA, intercept_write_func, + &test_write_data); + i2c_common_emul_set_read_func(COMMON_DATA, intercept_read_func, + &test_read_data); + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + /* + * Although the device enables PP2_FET on dead battery boot by setting + * the PP2_EN bit, the driver also force sets this bit during dead + * battery boot by writing that bit to the FUNC_SET3 reg. + * + * TODO(b/207034759): Verify need or remove redundant PP2 set. + */ + zassert_true(test_write_data.val_intercepted & SN5S330_PP2_EN, NULL); + zassert_false(sn5s330_drv.is_sourcing_vbus(SN5S330_PORT), NULL); +} + +ZTEST(ppc_sn5s330, test_enter_low_power_mode) +{ + const struct emul *emul = EMUL; + + uint8_t func_set2_reg; + uint8_t func_set3_reg; + uint8_t func_set4_reg; + uint8_t func_set9_reg; + + /* + * Requirements were extracted from TI's recommended changes for octopus + * to lower power use during hibernate as well as the follow up changes + * we made to allow the device to wake up from hibernate. + * + * For Reference: b/111006203#comment35 + */ + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + zassert_ok(sn5s330_drv.enter_low_power_mode(SN5S330_PORT), NULL); + + /* 1) Verify VBUS power paths are off */ + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_equal(func_set3_reg & SN5S330_PP1_EN, 0, NULL); + zassert_equal(func_set3_reg & SN5S330_PP2_EN, 0, NULL); + + /* 2) Verify VCONN power path is off */ + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET4, &func_set4_reg); + zassert_not_equal(func_set4_reg & SN5S330_CC_EN, 0, NULL); + zassert_equal(func_set4_reg & SN5S330_VCONN_EN, 0, NULL); + + /* 3) Verify SBU FET is off */ + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET2, &func_set2_reg); + zassert_equal(func_set2_reg & SN5S330_SBU_EN, 0, NULL); + + /* 4) Verify VBUS and SBU OVP comparators are off */ + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET9, &func_set9_reg); + zassert_equal(func_set9_reg & SN5S330_FORCE_OVP_EN_SBU, 0, NULL); + zassert_equal(func_set9_reg & SN5S330_PWR_OVR_VBUS, 0, NULL); + zassert_not_equal(func_set9_reg & SN5S330_OVP_EN_CC, 0, NULL); + zassert_equal(func_set9_reg & SN5S330_FORCE_ON_VBUS_OVP, 0, NULL); + zassert_equal(func_set9_reg & SN5S330_FORCE_ON_VBUS_UVP, 0, NULL); +} + +ZTEST(ppc_sn5s330, test_vbus_source_sink_enable) +{ + const struct emul *emul = EMUL; + uint8_t func_set3_reg; + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + /* Test enable/disable VBUS source FET */ + zassert_ok(sn5s330_drv.vbus_source_enable(SN5S330_PORT, true), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_not_equal(func_set3_reg & SN5S330_PP1_EN, 0, NULL); + + zassert_ok(sn5s330_drv.vbus_source_enable(SN5S330_PORT, false), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_equal(func_set3_reg & SN5S330_PP1_EN, 0, NULL); + + /* Test enable/disable VBUS sink FET */ + zassert_ok(sn5s330_drv.vbus_sink_enable(SN5S330_PORT, true), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_not_equal(func_set3_reg & SN5S330_PP2_EN, 0, NULL); + + zassert_ok(sn5s330_drv.vbus_sink_enable(SN5S330_PORT, false), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_equal(func_set3_reg & SN5S330_PP2_EN, 0, NULL); +} + +/* This test depends on EC GIPO initialization happening before I2C */ +BUILD_ASSERT(CONFIG_PLATFORM_EC_GPIO_INIT_PRIORITY < CONFIG_I2C_INIT_PRIORITY, + "GPIO initialization must happen before I2C"); +ZTEST(ppc_sn5s330, test_vbus_discharge) +{ + const struct emul *emul = EMUL; + uint8_t func_set3_reg; + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + /* Test enable/disable VBUS discharging */ + zassert_ok(sn5s330_drv.discharge_vbus(SN5S330_PORT, true), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_not_equal(func_set3_reg & SN5S330_VBUS_DISCH_EN, 0, NULL); + + zassert_ok(sn5s330_drv.discharge_vbus(SN5S330_PORT, false), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET3, &func_set3_reg); + zassert_equal(func_set3_reg & SN5S330_VBUS_DISCH_EN, 0, NULL); +} + +ZTEST(ppc_sn5s330, test_set_vbus_source_current_limit) +{ + const struct emul *emul = EMUL; + uint8_t func_set1_reg; + + /* Test every TCPC Pull Resistance Value */ + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + /* USB */ + zassert_ok(sn5s330_drv.set_vbus_source_current_limit(SN5S330_PORT, + TYPEC_RP_USB), + NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET1, &func_set1_reg); + zassert_equal(func_set1_reg & FUNC_SET1_ILIMPP1_MSK, SN5S330_ILIM_0_63, + NULL); + + /* 1.5A */ + zassert_ok(sn5s330_drv.set_vbus_source_current_limit(SN5S330_PORT, + TYPEC_RP_1A5), + NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET1, &func_set1_reg); + zassert_equal(func_set1_reg & FUNC_SET1_ILIMPP1_MSK, SN5S330_ILIM_1_62, + NULL); + + /* 3.0A */ + zassert_ok(sn5s330_drv.set_vbus_source_current_limit(SN5S330_PORT, + TYPEC_RP_3A0), + NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET1, &func_set1_reg); + zassert_equal(func_set1_reg & FUNC_SET1_ILIMPP1_MSK, SN5S330_ILIM_3_06, + NULL); + + /* Unknown/Reserved - We set result as USB */ + zassert_ok(sn5s330_drv.set_vbus_source_current_limit(SN5S330_PORT, + TYPEC_RP_RESERVED), + NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET1, &func_set1_reg); + zassert_equal(func_set1_reg & FUNC_SET1_ILIMPP1_MSK, SN5S330_ILIM_0_63, + NULL); +} + +ZTEST(ppc_sn5s330, test_sn5s330_set_sbu) +#ifdef CONFIG_USBC_PPC_SBU +{ + const struct emul *emul = EMUL; + uint8_t func_set2_reg; + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + /* Verify driver enables SBU FET */ + zassert_ok(sn5s330_drv.set_sbu(SN5S330_PORT, true), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET2, &func_set2_reg); + zassert_not_equal(func_set2_reg & SN5S330_SBU_EN, 0, NULL); + + /* Verify driver disables SBU FET */ + zassert_ok(sn5s330_drv.set_sbu(SN5S330_PORT, false), NULL); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET2, &func_set2_reg); + zassert_equal(func_set2_reg & SN5S330_SBU_EN, 0, NULL); +} +#else +{ + ztest_test_skip(); +} +#endif /* CONFIG_USBC_PPC_SBU */ + +ZTEST(ppc_sn5s330, test_sn5s330_vbus_overcurrent) +{ + const struct emul *emul = EMUL; + uint8_t int_trip_rise_reg1; + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + sn5s330_emul_make_vbus_overcurrent(emul); + /* + * TODO(b/201420132): Replace arbitrary sleeps. + */ + /* Make sure interrupt happens first. */ + k_msleep(SN5S330_INTERRUPT_DELAYMS); + zassert_true(sn5s330_emul_interrupt_set_stub_fake.call_count > 0, NULL); + + /* + * Verify we cleared vbus overcurrent interrupt trip rise bit so the + * driver can detect future overcurrent clamping interrupts. + */ + sn5s330_emul_peek_reg(emul, SN5S330_INT_TRIP_RISE_REG1, + &int_trip_rise_reg1); + zassert_equal(int_trip_rise_reg1 & SN5S330_ILIM_PP1_MASK, 0, NULL); +} + +ZTEST(ppc_sn5s330, test_sn5s330_vbus_overcurrent_late_jump) +{ + const struct emul *emul = EMUL; + uint8_t int_trip_rise_reg1; + + /* Simulate the system jumping late. The second call to init() will + * skip certain interrupt setup work. Make sure the interrupt continues + * to function. + */ + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + system_jumped_late_fake.return_val = 1; + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + sn5s330_emul_make_vbus_overcurrent(emul); + /* + * TODO(b/201420132): Replace arbitrary sleeps. + */ + /* Make sure interrupt happens first. */ + k_msleep(SN5S330_INTERRUPT_DELAYMS); + zassert_true(sn5s330_emul_interrupt_set_stub_fake.call_count > 0, NULL); + + /* + * Verify we cleared vbus overcurrent interrupt trip rise bit so the + * driver can detect future overcurrent clamping interrupts. + */ + sn5s330_emul_peek_reg(emul, SN5S330_INT_TRIP_RISE_REG1, + &int_trip_rise_reg1); + zassert_equal(int_trip_rise_reg1 & SN5S330_ILIM_PP1_MASK, 0, NULL); +} + +ZTEST(ppc_sn5s330, test_sn5s330_disable_vbus_low_interrupt) +{ + const struct emul *emul = EMUL; + + /* Interrupt disabled here */ + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + /* Would normally cause a vbus low interrupt */ + sn5s330_emul_lower_vbus_below_minv(emul); + zassert_equal(sn5s330_emul_interrupt_set_stub_fake.call_count, 0, NULL); +} + +ZTEST(ppc_sn5s330, test_sn5s330_disable_vbus_low_interrupt_late_jump) +{ + const struct emul *emul = EMUL; + + /* Simulate the system jumping late. The second call to init() will + * skip certain interrupt setup work. Make sure the interrupt continues + * to function. + */ + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + system_jumped_late_fake.return_val = 1; + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + /* Would normally cause a vbus low interrupt */ + sn5s330_emul_lower_vbus_below_minv(emul); + zassert_equal(sn5s330_emul_interrupt_set_stub_fake.call_count, 0, NULL); +} + +ZTEST(ppc_sn5s330, test_sn5s330_set_vconn_fet) +{ + if (!IS_ENABLED(CONFIG_USBC_PPC_VCONN)) { + ztest_test_skip(); + return; + } + + const struct emul *emul = EMUL; + uint8_t func_set4_reg; + + zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL); + + sn5s330_drv.set_vconn(SN5S330_PORT, false); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET4, &func_set4_reg); + zassert_equal(func_set4_reg & SN5S330_VCONN_EN, 0, NULL); + + sn5s330_drv.set_vconn(SN5S330_PORT, true); + sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET4, &func_set4_reg); + zassert_not_equal(func_set4_reg & SN5S330_VCONN_EN, 0, NULL); +} + +/* Make an I2C emulator mock read func wrapped in FFF */ +FAKE_VALUE_FUNC(int, dump_read_fn, const struct emul *, int, uint8_t *, int, + void *); + +ZTEST(ppc_sn5s330, test_dump) +{ + int ret; + + /* Set up our fake read function to pass through to the real emul */ + RESET_FAKE(dump_read_fn); + dump_read_fn_fake.return_val = 1; + i2c_common_emul_set_read_func(COMMON_DATA, dump_read_fn, NULL); + + ret = sn5s330_drv.reg_dump(SN5S330_PORT); + + zassert_equal(EC_SUCCESS, ret, "Expected EC_SUCCESS, got %d", ret); + + /* Check that all the expected I2C reads were performed. */ + MOCK_ASSERT_I2C_READ(dump_read_fn, 0, SN5S330_FUNC_SET1); + MOCK_ASSERT_I2C_READ(dump_read_fn, 1, SN5S330_FUNC_SET2); + MOCK_ASSERT_I2C_READ(dump_read_fn, 2, SN5S330_FUNC_SET3); + MOCK_ASSERT_I2C_READ(dump_read_fn, 3, SN5S330_FUNC_SET4); + MOCK_ASSERT_I2C_READ(dump_read_fn, 4, SN5S330_FUNC_SET5); + MOCK_ASSERT_I2C_READ(dump_read_fn, 5, SN5S330_FUNC_SET6); + MOCK_ASSERT_I2C_READ(dump_read_fn, 6, SN5S330_FUNC_SET7); + MOCK_ASSERT_I2C_READ(dump_read_fn, 7, SN5S330_FUNC_SET8); + MOCK_ASSERT_I2C_READ(dump_read_fn, 8, SN5S330_FUNC_SET9); + MOCK_ASSERT_I2C_READ(dump_read_fn, 9, SN5S330_FUNC_SET10); + MOCK_ASSERT_I2C_READ(dump_read_fn, 10, SN5S330_FUNC_SET11); + MOCK_ASSERT_I2C_READ(dump_read_fn, 11, SN5S330_FUNC_SET12); + MOCK_ASSERT_I2C_READ(dump_read_fn, 12, SN5S330_INT_STATUS_REG1); + MOCK_ASSERT_I2C_READ(dump_read_fn, 13, SN5S330_INT_STATUS_REG2); + MOCK_ASSERT_I2C_READ(dump_read_fn, 14, SN5S330_INT_STATUS_REG3); + MOCK_ASSERT_I2C_READ(dump_read_fn, 15, SN5S330_INT_STATUS_REG4); + MOCK_ASSERT_I2C_READ(dump_read_fn, 16, SN5S330_INT_TRIP_RISE_REG1); + MOCK_ASSERT_I2C_READ(dump_read_fn, 17, SN5S330_INT_TRIP_RISE_REG2); + MOCK_ASSERT_I2C_READ(dump_read_fn, 18, SN5S330_INT_TRIP_RISE_REG3); + MOCK_ASSERT_I2C_READ(dump_read_fn, 19, SN5S330_INT_TRIP_FALL_REG1); + MOCK_ASSERT_I2C_READ(dump_read_fn, 20, SN5S330_INT_TRIP_FALL_REG2); + MOCK_ASSERT_I2C_READ(dump_read_fn, 21, SN5S330_INT_TRIP_FALL_REG3); + MOCK_ASSERT_I2C_READ(dump_read_fn, 22, SN5S330_INT_MASK_RISE_REG1); + MOCK_ASSERT_I2C_READ(dump_read_fn, 23, SN5S330_INT_MASK_RISE_REG2); + MOCK_ASSERT_I2C_READ(dump_read_fn, 24, SN5S330_INT_MASK_RISE_REG3); + MOCK_ASSERT_I2C_READ(dump_read_fn, 25, SN5S330_INT_MASK_FALL_REG1); + MOCK_ASSERT_I2C_READ(dump_read_fn, 26, SN5S330_INT_MASK_FALL_REG2); + MOCK_ASSERT_I2C_READ(dump_read_fn, 27, SN5S330_INT_MASK_FALL_REG3); +} + +enum i2c_operation { + I2C_WRITE, + I2C_READ, +}; + +#define INIT_I2C_FAIL_HELPER(COMMON_DATA, RW, REG) \ + do { \ + if ((RW) == I2C_READ) { \ + i2c_common_emul_set_read_fail_reg((COMMON_DATA), \ + (REG)); \ + i2c_common_emul_set_write_fail_reg( \ + (COMMON_DATA), I2C_COMMON_EMUL_NO_FAIL_REG); \ + } else if ((RW) == I2C_WRITE) { \ + i2c_common_emul_set_read_fail_reg( \ + (COMMON_DATA), I2C_COMMON_EMUL_NO_FAIL_REG); \ + i2c_common_emul_set_write_fail_reg((COMMON_DATA), \ + (REG)); \ + } else { \ + zassert_true(false, "Invalid I2C operation"); \ + } \ + zassert_equal( \ + EC_ERROR_INVAL, sn5s330_drv.init(SN5S330_PORT), \ + "Did not get EC_ERROR_INVAL when reg %s (0x%02x)" \ + "could not be %s", \ + #REG, (REG), ((RW) == I2C_READ) ? "read" : "written"); \ + } while (0) + +ZTEST(ppc_sn5s330, test_init_reg_fails) +{ + /* Fail on each of the I2C operations the init function does to ensure + * we get the correct return value. This includes operations made by + * clr_flags() and set_flags(). + */ + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET5); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_READ, SN5S330_FUNC_SET6); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET6); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET2); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET9); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET11); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_READ, SN5S330_FUNC_SET8); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET8); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_READ, SN5S330_FUNC_SET4); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET4); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_READ, SN5S330_FUNC_SET3); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET3); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_READ, SN5S330_FUNC_SET10); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_FUNC_SET10); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, SN5S330_INT_STATUS_REG4); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_MASK_RISE_REG1); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_MASK_FALL_REG1); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_MASK_RISE_REG2); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_MASK_FALL_REG2); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_MASK_RISE_REG3); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_MASK_FALL_REG3); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_READ, SN5S330_INT_STATUS_REG4); + + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_TRIP_RISE_REG1); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_TRIP_RISE_REG2); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_TRIP_RISE_REG3); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_TRIP_FALL_REG1); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_TRIP_FALL_REG2); + INIT_I2C_FAIL_HELPER(COMMON_DATA, I2C_WRITE, + SN5S330_INT_TRIP_FALL_REG3); +} + +static int pp_fet_test_mock_read_fn(const struct emul *emul, int reg, + uint8_t *val, int bytes, void *data) +{ + int *counter = data; + + zassert_true(counter, "data cannot be a NULL pointer"); + + /* Pretend to be in dead battery mode (needed for part 2 of the test) */ + if (reg == SN5S330_INT_STATUS_REG4) { + *val = SN5S330_DB_BOOT; + return 0; + } + + /* Fail if we try to read SN5S330_FUNC_SET3 after the counter hits 0 */ + if (reg == SN5S330_FUNC_SET3 && (*counter)-- <= 0) { + printf("Failing\n"); + return -EIO; + } + + return 1; +} + +ZTEST(ppc_sn5s330, test_pp_fet_enable_fail) +{ + /* We attempt to enable the PP (power path) FET at two points during + * the init function, constituting the second and third accesses to the + * FUNC_SET3 register. We need to allow the first N reads/writes to + * succeed to test failure handling of each call to + * sn5s330_pp_fet_enable(). The second call requires us to be in dead + * battery mode, which we take care of in the mock read function. + */ + + int counter; + int ret; + + i2c_common_emul_set_read_func(COMMON_DATA, pp_fet_test_mock_read_fn, + &counter); + + /* Allow only the first access to the reg to succeed. This tests the + * error handling of the first call to sn5s330_pp_fet_enable(). + */ + + counter = 1; + ret = sn5s330_drv.init(SN5S330_PORT); + + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); + + /* Allow only the first two accesses to succeed. This tests the error + * handling of the second call to sn5s330_pp_fet_enable(). + */ + + counter = 2; + ret = sn5s330_drv.init(SN5S330_PORT); + + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); +} + +ZTEST(ppc_sn5s330, test_set_polarity) +{ + int ret; + uint8_t reg_val; + + /* Ensure flags start cleared */ + sn5s330_emul_peek_reg(EMUL, SN5S330_FUNC_SET4, ®_val); + zassert_false(reg_val & SN5S330_CC_POLARITY, + "Polarity flags should not be set after reset."); + + /* Set polarity flags */ + ret = sn5s330_drv.set_polarity(SN5S330_PORT, 1); + zassert_equal(EC_SUCCESS, ret, "Expected EC_SUCCESS but got %d", ret); + + sn5s330_emul_peek_reg(EMUL, SN5S330_FUNC_SET4, ®_val); + zassert_true(reg_val & SN5S330_CC_POLARITY, + "Polarity flags should be set."); + + /* Clear polarity flags */ + ret = sn5s330_drv.set_polarity(SN5S330_PORT, 0); + zassert_equal(EC_SUCCESS, ret, "Expected EC_SUCCESS but got %d", ret); + + sn5s330_emul_peek_reg(EMUL, SN5S330_FUNC_SET4, ®_val); + zassert_false(reg_val & SN5S330_CC_POLARITY, + "Polarity flags should be cleared."); +} + +ZTEST(ppc_sn5s330, test_set_vbus_source_current_limit_fail) +{ + int ret; + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET1); + + ret = sn5s330_drv.set_vbus_source_current_limit(SN5S330_PORT, + TYPEC_RP_3A0); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); +} + +ZTEST(ppc_sn5s330, test_sn5s330_discharge_vbus_fail) +{ + int ret; + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET3); + + ret = sn5s330_drv.discharge_vbus(SN5S330_PORT, false); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); +} + +ZTEST(ppc_sn5s330, test_low_power_mode_fail) +{ + /* Test failed I2C operations in the enter low power mode function */ + + int ret; + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET3); + ret = sn5s330_drv.enter_low_power_mode(SN5S330_PORT); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET4); + ret = sn5s330_drv.enter_low_power_mode(SN5S330_PORT); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET2); + ret = sn5s330_drv.enter_low_power_mode(SN5S330_PORT); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET9); + ret = sn5s330_drv.enter_low_power_mode(SN5S330_PORT); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); +} + +ZTEST(ppc_sn5s330, test_sn5s330_set_vconn_fail) +{ + /* Test failed I2C operations in the set Vconn function */ + + int ret; + + i2c_common_emul_set_read_fail_reg(COMMON_DATA, SN5S330_FUNC_SET4); + ret = sn5s330_drv.set_vconn(SN5S330_PORT, 0); + zassert_equal(EC_ERROR_INVAL, ret, "Expected EC_ERROR_INVAL but got %d", + ret); +} + +static inline void reset_sn5s330_state(void) +{ + i2c_common_emul_set_write_func(COMMON_DATA, NULL, NULL); + i2c_common_emul_set_read_func(COMMON_DATA, NULL, NULL); + i2c_common_emul_set_write_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_read_fail_reg(COMMON_DATA, + I2C_COMMON_EMUL_NO_FAIL_REG); + sn5s330_emul_reset(EMUL); + RESET_FAKE(sn5s330_emul_interrupt_set_stub); +} + +static void ppc_sn5s330_before(void *state) +{ + ARG_UNUSED(state); + reset_sn5s330_state(); +} + +static void ppc_sn5s330_after(void *state) +{ + ARG_UNUSED(state); + reset_sn5s330_state(); +} + +ZTEST_SUITE(ppc_sn5s330, drivers_predicate_post_main, NULL, ppc_sn5s330_before, + ppc_sn5s330_after, NULL); diff --git a/zephyr/test/drivers/default/src/ppc_syv682x.c b/zephyr/test/drivers/default/src/ppc_syv682x.c new file mode 100644 index 0000000000..edfbd45171 --- /dev/null +++ b/zephyr/test/drivers/default/src/ppc_syv682x.c @@ -0,0 +1,815 @@ +/* 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/device.h> +#include <zephyr/devicetree/gpio.h> +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <zephyr/drivers/gpio.h> +#include <zephyr/fff.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> +#include <zephyr/ztest_assert.h> + +#include "emul/emul_common_i2c.h" +#include "emul/emul_syv682x.h" +#include "test/drivers/stubs.h" +#include "syv682x.h" +#include "timer.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" +#include "usbc_ppc.h" + +#define SYV682X_NODE DT_NODELABEL(syv682x_emul) +#define GPIO_USB_C1_FRS_EN_PATH DT_PATH(named_gpios, usb_c1_frs_en) + +struct ppc_syv682x_fixture { + const struct emul *ppc_emul; + struct i2c_common_emul_data *common_data; + const struct device *frs_en_gpio_port; + int frs_en_gpio_pin; +}; + +/* Configuration for a mock I2C access function that sometimes fails. */ +struct reg_to_fail_data { + int reg_access_to_fail; + int reg_access_fail_countdown; +}; + +static const int syv682x_port = 1; + +static void *syv682x_test_setup(void) +{ + static struct ppc_syv682x_fixture fixture; + + fixture.ppc_emul = EMUL_DT_GET(SYV682X_NODE); + fixture.common_data = + emul_syv682x_get_i2c_common_data(fixture.ppc_emul); + zassume_not_null(fixture.ppc_emul, NULL); + fixture.frs_en_gpio_port = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_USB_C1_FRS_EN_PATH, gpios)); + zassume_not_null(fixture.frs_en_gpio_port, NULL); + fixture.frs_en_gpio_pin = DT_GPIO_PIN(GPIO_USB_C1_FRS_EN_PATH, gpios); + + return &fixture; +} + +static void syv682x_test_after(void *data) +{ + struct ppc_syv682x_fixture *fixture = data; + const struct emul *emul = fixture->ppc_emul; + struct i2c_common_emul_data *common_data = fixture->common_data; + + /* Disable the power path and clear interrupt conditions. */ + zassume_ok(syv682x_emul_set_reg(emul, SYV682X_CONTROL_1_REG, + SYV682X_CONTROL_1_PWR_ENB), + NULL); + syv682x_emul_set_condition(emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_NONE); + + /* Clear the mock read/write functions */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); + i2c_common_emul_set_write_func(common_data, NULL, NULL); + + /* Don't fail on any register access */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST_SUITE(ppc_syv682x, drivers_predicate_post_main, syv682x_test_setup, NULL, + syv682x_test_after, NULL); + +ZTEST_F(ppc_syv682x, test_syv682x_board_is_syv682c) +{ + /* + * The SYV682x driver should assume a version-C part in the absence of a + * board override. + */ + zassert_true(syv682x_board_is_syv682c(syv682x_port), NULL); +} + +static void check_control_1_default_init(uint8_t control_1) +{ + /* + * During init, when not in dead battery mode, the driver should + * configure the high-voltage channel as sink but leave the power path + * disabled. The driver should set the current limits according to + * configuration. + */ + int ilim; + + zassert_true(control_1 & SYV682X_CONTROL_1_PWR_ENB, + "Default init, but power path enabled"); + ilim = (control_1 & SYV682X_HV_ILIM_MASK) >> SYV682X_HV_ILIM_BIT_SHIFT; + zassert_equal(ilim, CONFIG_PLATFORM_EC_USBC_PPC_SYV682X_HV_ILIM, + "Default init, but HV current limit set to %d", ilim); + zassert_false(control_1 & SYV682X_CONTROL_1_HV_DR, + "Default init, but source mode selected"); + zassert_true(control_1 & SYV682X_CONTROL_1_CH_SEL, + "Default init, but 5V power path selected"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_init_dead_battery) +{ + uint8_t reg; + + /* + * With a dead battery, the device powers up sinking VBUS, and the + * driver should keep that going. + */ + zassume_ok(syv682x_emul_set_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, + SYV682X_CONTROL_1_CH_SEL), + NULL); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_VSAFE_5V, + SYV682X_CONTROL_4_NONE); + zassert_ok(ppc_init(syv682x_port), "PPC init failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + NULL); + zassert_true(reg & SYV682X_CONTROL_1_CH_SEL, + "Dead battery init, but CH_SEL set to 5V power path"); + zassert_false(reg & (SYV682X_CONTROL_1_PWR_ENB | + SYV682X_CONTROL_1_HV_DR), + "Dead battery init, but CONTROL_1 is 0x%x", reg); + zassert_false(ppc_is_sourcing_vbus(syv682x_port), + "Dead battery init, but VBUS source enabled"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_init_vsafe0v) +{ + uint8_t reg; + + /* With VBUS at vSafe0V, init should set the default configuration. */ + zassume_ok(syv682x_emul_set_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, + SYV682X_CONTROL_1_PWR_ENB), + NULL); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_VSAFE_0V, + SYV682X_CONTROL_4_NONE); + zassert_ok(ppc_init(syv682x_port), "PPC init failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + NULL); + check_control_1_default_init(reg); +} + +ZTEST_F(ppc_syv682x, test_syv682x_init_sink_disabled) +{ + uint8_t reg; + + /* With sink disabled, init should do the same thing. */ + zassume_ok(syv682x_emul_set_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, + SYV682X_CONTROL_1_CH_SEL), + NULL); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_VSAFE_0V, + SYV682X_CONTROL_4_NONE); + zassert_ok(ppc_init(syv682x_port), "PPC init failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + NULL); + check_control_1_default_init(reg); +} + +ZTEST_F(ppc_syv682x, test_syv682x_init_common) +{ + uint8_t reg; + int ilim; + + zassert_ok(ppc_init(syv682x_port), "PPC init failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + NULL); + + /* + * Any init sequence should also disable the FRS GPIO, set the 5V + * current limit according to configuration, set over-current, over- + * voltage, and discharge parameters appropriately, and enable CC lines. + */ + zassert_equal(gpio_emul_output_get(fixture->frs_en_gpio_port, + fixture->frs_en_gpio_pin), + 0, "FRS enabled, but FRS GPIO not asserted"); + ilim = (reg & SYV682X_5V_ILIM_MASK) >> SYV682X_5V_ILIM_BIT_SHIFT; + zassert_equal(ilim, CONFIG_PLATFORM_EC_USB_PD_PULLUP, + "Default init, but 5V current limit set to %d", ilim); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_2_REG, ®), + NULL); + zassert_equal(reg, + (SYV682X_OC_DELAY_10MS << SYV682X_OC_DELAY_SHIFT) | + (SYV682X_DSG_RON_200_OHM + << SYV682X_DSG_RON_SHIFT) | + (SYV682X_DSG_TIME_50MS << SYV682X_DSG_TIME_SHIFT), + "Default init, but CONTROL_2 is 0x%x", reg); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_3_REG, ®), + NULL); + zassert_equal(reg, + (SYV682X_OVP_23_7 << SYV682X_OVP_BIT_SHIFT) | + SYV682X_RVS_MASK, + "Default init, but CONTROL_3 is 0x%x", reg); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + NULL); + zassert_equal(reg & ~SYV682X_CONTROL_4_INT_MASK, + SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS, + "Default init, but CONTROL_4 is 0x%x", reg); +} + +ZTEST_F(ppc_syv682x, test_syv682x_vbus_source_enable) +{ + uint8_t reg; + + zassert_ok(ppc_vbus_source_enable(syv682x_port, true), + "VBUS enable failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + zassert_equal(reg & SYV682X_CONTROL_1_PWR_ENB, 0, + "VBUS sourcing enabled but power path disabled"); + zassert_true(ppc_is_sourcing_vbus(syv682x_port), + "PPC is not sourcing VBUS after VBUS enabled"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_vbus_source_disable) +{ + zassert_ok(ppc_vbus_source_enable(syv682x_port, false), + "VBUS disable failed"); + zassert_false(ppc_is_sourcing_vbus(syv682x_port), + "PPC sourcing VBUS after disable"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_interrupt_source_oc) +{ + zassume_ok(ppc_vbus_source_enable(syv682x_port, true), + "VBUS enable failed"); + /* An OC event less than 100 ms should not cause VBUS to turn off. */ + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_OC_5V, + SYV682X_CONTROL_4_NONE); + msleep(50); + zassert_true(ppc_is_sourcing_vbus(syv682x_port), + "PPC is not sourcing VBUS after 50 ms OC"); + /* But one greater than 100 ms should. */ + msleep(60); + zassert_false(ppc_is_sourcing_vbus(syv682x_port), + "PPC is sourcing VBUS after 100 ms OC"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_interrupt_tsd) +{ + /* + * A TSD event should cause the driver to disable source and sink paths. + * (The device will have already physically disabled them.) The state of + * the sink path is not part of the driver's API. + */ + zassume_ok(ppc_vbus_source_enable(syv682x_port, true), + "Source enable failed"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_TSD, + SYV682X_CONTROL_4_NONE); + msleep(1); + zassert_false(ppc_is_sourcing_vbus(syv682x_port), + "PPC is sourcing power after TSD"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_interrupt_vbus_ovp) +{ + /* An OVP event should cause the driver to disable the source path. */ + zassume_ok(ppc_vbus_source_enable(syv682x_port, true), + "Source enable failed"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_OVP, + SYV682X_CONTROL_4_NONE); + msleep(1); + zassert_false(ppc_is_sourcing_vbus(syv682x_port), + "PPC is sourcing power after OVP"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_interrupt_vbus_hv_oc) +{ + uint8_t reg; + + /* + * A high-voltage OC while sinking should cause the driver to try to + * re-enable the sink path until the OC count limit is reached, at which + * point the driver should leave it disabled. + */ + zassume_ok(ppc_vbus_sink_enable(syv682x_port, true), + "Sink enable failed"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_OC_HV, + SYV682X_CONTROL_4_NONE); + msleep(1); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + zassert_equal(reg & SYV682X_CONTROL_1_PWR_ENB, 0, + "Power path disabled after HV_OC handled"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_OC_HV, + SYV682X_CONTROL_4_NONE); + /* Alert GPIO doesn't change so wait for delayed syv682x interrupt */ + msleep(15); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + zassert_equal(reg & SYV682X_CONTROL_1_PWR_ENB, 0, + "Power path disabled after HV_OC handled"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_OC_HV, + SYV682X_CONTROL_4_NONE); + /* Alert GPIO doesn't change so wait for delayed syv682x interrupt */ + msleep(15); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + zassert_equal(reg & SYV682X_CONTROL_1_PWR_ENB, + SYV682X_CONTROL_1_PWR_ENB, + "Power path enabled after HV_OC handled 3 times"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_interrupt_vconn_oc) +{ + uint8_t reg; + + /* + * A VCONN OC event less than 100 ms should not cause the driver to turn + * VCONN off. + */ + ppc_set_vconn(syv682x_port, true); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_VCONN_OCP); + msleep(1); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + "Reading CONTROL_4 failed"); + zassert_true(reg & (SYV682X_CONTROL_4_VCONN1 | + SYV682X_CONTROL_4_VCONN2), + "VCONN disabled after initial VCONN OC"); + msleep(50); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + "Reading CONTROL_4 failed"); + zassert_true(reg & (SYV682X_CONTROL_4_VCONN1 | + SYV682X_CONTROL_4_VCONN2), + "VCONN disabled after short VCONN OC"); + /* + * But if the event keeps going for over 100 ms continuously, the driver + * should turn VCONN off. + */ + msleep(60); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + "Reading CONTROL_4 failed"); + zassert_false(reg & (SYV682X_CONTROL_4_VCONN1 | + SYV682X_CONTROL_4_VCONN2), + "VCONN enabled after long VCONN OC"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_interrupt_vconn_ov) +{ + uint8_t reg; + + /* + * A VCONN over-voltage (VBAT_OVP) event will cause the device to + * disconnect CC and VCONN. The driver should then reinitialize the + * device, which will enable both CC lines but leave VCONN disabled. The + * driver should then run generic CC over-voltage handling. + */ + ppc_set_vconn(syv682x_port, true); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_VBAT_OVP); + msleep(1); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + "Reading CONTROL_4 failed"); + zassert_true(reg & SYV682X_CONTROL_4_CC1_BPS, + "CC1 disabled after handling VBAT_OVP"); + zassert_true(reg & SYV682X_CONTROL_4_CC2_BPS, + "CC2 disabled after handling VBAT_OVP"); + zassert_false(reg & (SYV682X_CONTROL_4_VCONN1 | + SYV682X_CONTROL_4_VCONN2), + "VCONN enabled after handling VBAT_OVP"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_frs_enable) +{ + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_USB_C1_FRS_EN_PATH, gpios)); + uint8_t reg; + + /* + * Enabling FRS should enable only the appropriate CC line based on + * polarity. Disabling FRS should enable both CC lines. + */ + ppc_vbus_sink_enable(syv682x_port, true); + zassume_false(ppc_is_sourcing_vbus(syv682x_port), + "PPC is sourcing VBUS after sink enabled"); + ppc_set_polarity(syv682x_port, 0 /* CC1 */); + ppc_set_frs_enable(syv682x_port, true); + zassert_equal(gpio_emul_output_get(gpio_dev, fixture->frs_en_gpio_pin), + 1, "FRS enabled, but FRS GPIO not asserted"); + zassert_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + "Reading CONTROL_4 failed"); + zassert_equal( + reg & (SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS), + SYV682X_CONTROL_4_CC1_BPS, + "FRS enabled with CC1 polarity, but CONTROL_4 is 0x%x", reg); +} + +ZTEST_F(ppc_syv682x, test_syv682x_frs_disable) +{ + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_USB_C1_FRS_EN_PATH, gpios)); + uint8_t reg; + + ppc_vbus_sink_enable(syv682x_port, true); + zassume_false(ppc_is_sourcing_vbus(syv682x_port), + "PPC is sourcing VBUS after sink enabled"); + ppc_set_polarity(syv682x_port, 0 /* CC1 */); + + ppc_set_frs_enable(syv682x_port, false); + zassert_equal(gpio_emul_output_get(gpio_dev, fixture->frs_en_gpio_pin), + 0, "FRS disabled, but FRS GPIO not deasserted"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_4_REG, ®), + "Reading CONTROL_4 failed"); + zassert_equal( + reg & (SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS), + SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS, + "FRS disabled with CC1 polarity, but CONTROL_4 is 0x%x", reg); +} + +ZTEST_F(ppc_syv682x, test_syv682x_frs_trigger) +{ + /* + * An FRS event when the PPC is Sink should cause the PPC to switch from + * Sink to Source. + */ + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_FRS, + SYV682X_CONTROL_4_NONE); + msleep(1); + zassert_true(ppc_is_sourcing_vbus(syv682x_port), + "PPC is not sourcing VBUS after FRS signal handled"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_NONE); +} + +ZTEST_F(ppc_syv682x, test_syv682x_source_current_limit_usb_default) +{ + uint8_t reg; + int ilim_val; + + zassert_ok(ppc_set_vbus_source_current_limit(syv682x_port, + TYPEC_RP_USB), + "Could not set source current limit"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + ilim_val = (reg & SYV682X_5V_ILIM_MASK) >> SYV682X_5V_ILIM_BIT_SHIFT; + zassert_equal(reg & SYV682X_5V_ILIM_MASK, SYV682X_5V_ILIM_1_25, + "Set USB Rp value, but 5V_ILIM is %d", ilim_val); +} + +ZTEST_F(ppc_syv682x, test_syv682x_source_current_limit_1500ma) +{ + uint8_t reg; + int ilim_val; + + zassert_ok(ppc_set_vbus_source_current_limit(syv682x_port, + TYPEC_RP_1A5), + "Could not set source current limit"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + ilim_val = (reg & SYV682X_5V_ILIM_MASK) >> SYV682X_5V_ILIM_BIT_SHIFT; + zassert_equal(ilim_val, SYV682X_5V_ILIM_1_75, + "Set 1.5A Rp value, but 5V_ILIM is %d", ilim_val); +} + +ZTEST_F(ppc_syv682x, test_syv682x_source_current_limit_3000ma) +{ + uint8_t reg; + int ilim_val; + + zassert_ok(ppc_set_vbus_source_current_limit(syv682x_port, + TYPEC_RP_3A0), + "Could not set source current limit"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + "Reading CONTROL_1 failed"); + ilim_val = (reg & SYV682X_5V_ILIM_MASK) >> SYV682X_5V_ILIM_BIT_SHIFT; + zassert_equal(ilim_val, SYV682X_5V_ILIM_3_30, + "Set 3.0A Rp value, but 5V_ILIM is %d", ilim_val); +} + +ZTEST_F(ppc_syv682x, test_syv682x_write_busy) +{ + /* + * Writes should fail while the BUSY bit is set, except that writes to + * CONTROL_4 should succeed on the SYV682C. 1000 reads is intentionally + * many more than the driver is expected to make before reaching its + * timeout. It is not a goal of this test to verify the frequency of + * polling or the exact value of the timeout. + */ + syv682x_emul_set_busy_reads(fixture->ppc_emul, 1000); + zassert_equal(ppc_set_vbus_source_current_limit(syv682x_port, + TYPEC_RP_USB), + EC_ERROR_TIMEOUT, "SYV682 busy, but write completed"); + zassert_ok(ppc_set_frs_enable(syv682x_port, false), + "Could not set CONTROL_4 while busy on SYV682C"); + + /* + * If the busy bit clears before the driver reaches its timeout, the + * write should succeed. + */ + syv682x_emul_set_busy_reads(fixture->ppc_emul, 1); + zassert_equal(ppc_set_vbus_source_current_limit(syv682x_port, + TYPEC_RP_USB), + 0, "SYV682 not busy, but write failed"); + + syv682x_emul_set_busy_reads(fixture->ppc_emul, 0); +} + +ZTEST_F(ppc_syv682x, test_syv682x_dev_is_connected) +{ + uint8_t reg; + + zassert_ok(ppc_dev_is_connected(syv682x_port, PPC_DEV_SRC), + "Could not connect device as source"); + zassert_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_2_REG, ®), + "Reading CONTROL_2 failed"); + zassert_false(reg & SYV682X_CONTROL_2_FDSG, + "Connected as source, but force discharge enabled"); + + zassert_ok(ppc_dev_is_connected(syv682x_port, PPC_DEV_DISCONNECTED), + "Could not disconnect device"); + zassert_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_2_REG, ®), + "Reading CONTROL_2 failed"); + zassert_true(reg & SYV682X_CONTROL_2_FDSG, + "Disconnected, but force discharge disabled"); + + zassert_ok(ppc_dev_is_connected(syv682x_port, PPC_DEV_SNK), + "Could not connect device as source"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_vbus_sink_enable_trivial) +{ + /* + * If VBUS source is already enabled, disabling VBUS sink should + * trivially succeed. + */ + zassume_ok(ppc_vbus_source_enable(syv682x_port, true), + "VBUS enable failed"); + zassert_ok(ppc_vbus_sink_enable(syv682x_port, false), + "Sink disable failed"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_vbus_sink_enable_power_path) +{ + uint8_t reg; + int ilim; + + /* + * After enabling VBUS sink, the HV power path should be enabled in sink + * mode with the configured current limit. + */ + zassume_ok(ppc_vbus_source_enable(syv682x_port, false), + "VBUS enable failed"); + zassert_ok(ppc_vbus_sink_enable(syv682x_port, true), + "Sink disable failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + NULL); + zassert_true(reg & SYV682X_CONTROL_1_CH_SEL, + "Sink enabled, but CH_SEL set to 5V power path"); + zassert_false(reg & SYV682X_CONTROL_1_PWR_ENB, + "Sink enabled, but power path disabled"); + zassert_false(reg & SYV682X_CONTROL_1_HV_DR, + "Sink enabled, but high-voltage path in source mode"); + ilim = (reg & SYV682X_HV_ILIM_MASK) >> SYV682X_HV_ILIM_BIT_SHIFT; + zassert_equal(ilim, CONFIG_PLATFORM_EC_USBC_PPC_SYV682X_HV_ILIM, + "Sink enabled, but HV current limit set to %d", ilim); +} + +ZTEST_F(ppc_syv682x, test_syv682x_vbus_sink_disable) +{ + uint8_t reg; + + zassume_ok(ppc_vbus_source_enable(syv682x_port, false), + "VBUS enable failed"); + zassert_ok(ppc_vbus_sink_enable(syv682x_port, true), + "Sink disable failed"); + + zassert_ok(ppc_vbus_sink_enable(syv682x_port, false), + "Sink disable failed"); + zassume_ok(syv682x_emul_get_reg(fixture->ppc_emul, + SYV682X_CONTROL_1_REG, ®), + NULL); + zassert_true(reg & SYV682X_CONTROL_1_PWR_ENB, + "Sink disabled, but power path enabled"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_vbus_sink_oc_limit) +{ + zassert_ok(ppc_vbus_sink_enable(syv682x_port, true), + "Sink enable failed"); + + /* Generate 4 consecutive sink over-current interrupts. After reaching + * this count, the driver should prevent sink enable until the count is + * cleared by sink disable. + */ + for (int i = 0; i < 4; ++i) { + syv682x_emul_set_condition(fixture->ppc_emul, + SYV682X_STATUS_OC_HV, + SYV682X_CONTROL_4_NONE); + msleep(15); + } + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_NONE); + + zassert_not_equal(ppc_vbus_sink_enable(syv682x_port, true), EC_SUCCESS, + "VBUS sink enable succeeded after 4 OC events"); + + zassert_ok(ppc_vbus_sink_enable(syv682x_port, false), + "Sink disable failed"); + zassert_ok(ppc_vbus_sink_enable(syv682x_port, true), + "Sink enable failed"); + zassert_ok(ppc_vbus_sink_enable(syv682x_port, false), + "Sink disable failed"); +} + +ZTEST_F(ppc_syv682x, test_syv682x_set_vconn) +{ + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_VBAT_OVP); + zassert_not_equal(ppc_set_vconn(syv682x_port, true), EC_SUCCESS, + "VBAT OVP, but ppc_set_vconn succeeded"); +} + +ZTEST(ppc_syv682x, test_syv682x_ppc_dump) +{ + /* + * The ppc_dump command should succeed for this port. Don't check the + * output, since there are no standard requirements for that. + */ + const struct ppc_drv *drv = ppc_chips[syv682x_port].drv; + + zassert_ok(drv->reg_dump(syv682x_port), "ppc_dump command failed"); +} + +/* Intercepts I2C reads as a mock. Fails to read for the register at offset + * reg_access_to_fail on read number N, where N is the initial value of + * reg_access_fail_countdown. + */ +static int mock_read_intercept_reg_fail(const struct emul *emul, int reg, + uint8_t *val, int bytes, void *data) +{ + struct reg_to_fail_data *test_data = data; + + if (reg == test_data->reg_access_to_fail) { + test_data->reg_access_fail_countdown--; + if (test_data->reg_access_fail_countdown <= 0) + return -1; + } + return 1; +} + +ZTEST_F(ppc_syv682x, test_syv682x_i2c_error_status) +{ + /* Failed STATUS read should cause init to fail. */ + i2c_common_emul_set_read_fail_reg(fixture->common_data, + SYV682X_STATUS_REG); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "STATUS read error, but init succeeded"); + i2c_common_emul_set_read_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST_F(ppc_syv682x, test_syv682x_i2c_error_control_1) +{ + const struct ppc_drv *drv = ppc_chips[syv682x_port].drv; + struct reg_to_fail_data reg_fail = { + .reg_access_to_fail = 0, + .reg_access_fail_countdown = 0, + }; + + /* Failed CONTROL_1 read */ + i2c_common_emul_set_read_fail_reg(fixture->common_data, + SYV682X_CONTROL_1_REG); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_1 read error, but init succeeded"); + zassert_not_equal(ppc_vbus_source_enable(syv682x_port, true), + EC_SUCCESS, + "CONTROL_1 read error, but VBUS source enable " + "succeeded"); + zassert_not_equal(ppc_vbus_sink_enable(syv682x_port, true), EC_SUCCESS, + "CONTROL_1 read error, but VBUS sink enable " + "succeeded"); + zassert_not_equal(ppc_set_vbus_source_current_limit(syv682x_port, + TYPEC_RP_USB), + EC_SUCCESS, + "CONTROL_1 read error, but set current limit " + "succeeded"); + zassert_ok(drv->reg_dump(syv682x_port), + "CONTROL_1 read error, and ppc_dump failed"); + i2c_common_emul_set_read_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Init reads CONTROL_1 several times. The 3rd read happens while + * setting the source current limit. Check that init fails when that + * read fails. + */ + i2c_common_emul_set_read_func(fixture->common_data, + &mock_read_intercept_reg_fail, ®_fail); + reg_fail.reg_access_to_fail = SYV682X_CONTROL_1_REG; + reg_fail.reg_access_fail_countdown = 3; + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_1 read error, but init succeeded"); + i2c_common_emul_set_read_func(fixture->common_data, NULL, NULL); + + /* Failed CONTROL_1 write */ + i2c_common_emul_set_write_fail_reg(fixture->common_data, + SYV682X_CONTROL_1_REG); + + /* During init, the driver will write CONTROL_1 either to disable all + * power paths (normal case) or to enable the sink path (dead battery + * case). vSafe0V in STATUS is one indication of the normal case. + */ + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_VSAFE_0V, + SYV682X_CONTROL_4_NONE); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_1 write error, but init succeeded"); + syv682x_emul_set_condition(fixture->ppc_emul, SYV682X_STATUS_NONE, + SYV682X_CONTROL_4_NONE); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_1 write error, but init succeeded"); + + zassert_not_equal(ppc_vbus_source_enable(syv682x_port, true), + EC_SUCCESS, + "CONTROL_1 write error, but VBUS source " + "enable succeeded"); + i2c_common_emul_set_write_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST_F(ppc_syv682x, test_syv682x_i2c_error_control_2) +{ + /* Failed CONTROL_2 read */ + i2c_common_emul_set_read_fail_reg(fixture->common_data, + SYV682X_CONTROL_2_REG); + zassert_not_equal(ppc_discharge_vbus(syv682x_port, true), EC_SUCCESS, + "CONTROL_2 read error, but VBUS discharge succeeded"); + i2c_common_emul_set_read_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Failed CONTROL_2 write */ + i2c_common_emul_set_write_fail_reg(fixture->common_data, + SYV682X_CONTROL_2_REG); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_2 write error, but init succeeded"); + i2c_common_emul_set_write_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST_F(ppc_syv682x, test_syv682x_i2c_error_control_3) +{ + /* Failed CONTROL_3 read */ + i2c_common_emul_set_read_fail_reg(fixture->common_data, + SYV682X_CONTROL_3_REG); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_3 read error, but VBUS discharge succeeded"); + i2c_common_emul_set_read_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Failed CONTROL_3 write */ + i2c_common_emul_set_write_fail_reg(fixture->common_data, + SYV682X_CONTROL_3_REG); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_3 write error, but init succeeded"); + i2c_common_emul_set_write_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} + +ZTEST_F(ppc_syv682x, test_syv682x_i2c_error_control_4) +{ + /* Failed CONTROL_4 read */ + i2c_common_emul_set_read_fail_reg(fixture->common_data, + SYV682X_CONTROL_4_REG); + zassert_not_equal(ppc_set_vconn(syv682x_port, true), EC_SUCCESS, + "CONTROL_2 read error, but VCONN set succeeded"); + i2c_common_emul_set_read_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Failed CONTROL_4 write */ + i2c_common_emul_set_write_fail_reg(fixture->common_data, + SYV682X_CONTROL_4_REG); + zassert_not_equal(ppc_init(syv682x_port), EC_SUCCESS, + "CONTROL_4 write error, but init succeeded"); + zassert_not_equal(ppc_set_vconn(syv682x_port, true), EC_SUCCESS, + "CONTROL_4 write error, but VCONN set succeeded"); + i2c_common_emul_set_write_fail_reg(fixture->common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); +} diff --git a/zephyr/test/drivers/default/src/ps8xxx.c b/zephyr/test/drivers/default/src/ps8xxx.c new file mode 100644 index 0000000000..29d720a639 --- /dev/null +++ b/zephyr/test/drivers/default/src/ps8xxx.c @@ -0,0 +1,1311 @@ +/* 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 "common.h" +#include "emul/emul_common_i2c.h" +#include "emul/tcpc/emul_tcpci.h" +#include "emul/tcpc/emul_ps8xxx.h" +#include "timer.h" +#include "i2c.h" +#include "test/drivers/stubs.h" +#include "test/drivers/tcpci_test_common.h" + +#include "tcpm/tcpci.h" +#include "driver/tcpm/ps8xxx.h" +#include "driver/tcpm/ps8xxx_public.h" +#include "test/drivers/test_state.h" + +#define PS8XXX_EMUL_NODE DT_NODELABEL(ps8xxx_emul) + +/** Test PS8xxx init fail conditions common for all PS8xxx devices */ +static void test_ps8xxx_init_fail(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + /* Test fail on FW reg read */ + i2c_common_emul_set_read_fail_reg(common_data, PS8XXX_REG_FW_REV); + zassert_equal(EC_ERROR_TIMEOUT, ps8xxx_tcpm_drv.init(USBC_PORT_C1), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on FW reg set to 0 */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x0); + zassert_equal(EC_ERROR_TIMEOUT, ps8xxx_tcpm_drv.init(USBC_PORT_C1), + NULL); + + /* Set arbitrary FW reg value != 0 for rest of the test */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x31); + + /* Test fail on TCPCI init */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_UNINIT); + zassert_equal(EC_ERROR_TIMEOUT, ps8xxx_tcpm_drv.init(USBC_PORT_C1), + NULL); +} + +ZTEST(ps8805, test_init_fail) +{ + test_ps8xxx_init_fail(); +} + +ZTEST(ps8815, test_init_fail) +{ + test_ps8xxx_init_fail(); +} + +/** + * Test PS8805 init and indirectly ps8705_dci_disable which is + * used by PS8805 + */ +ZTEST(ps8805, test_ps8805_init) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + struct i2c_common_emul_data *p1_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_1); + + /* Set arbitrary FW reg value != 0 for this test */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x31); + /* Set correct power status for this test */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_POWER_STATUS, 0x0); + + /* Test fail on read I2C debug reg */ + i2c_common_emul_set_read_fail_reg(common_data, + PS8XXX_REG_I2C_DEBUGGING_ENABLE); + zassert_equal(EC_ERROR_INVAL, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on read DCI reg */ + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + PS8XXX_P1_REG_MUX_USB_DCI_CFG); + zassert_equal(EC_ERROR_INVAL, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test successful init */ + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + check_tcpci_reg(ps8xxx_emul, PS8XXX_REG_I2C_DEBUGGING_ENABLE, + PS8XXX_REG_I2C_DEBUGGING_ENABLE_ON); + zassert_equal(PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF, + ps8xxx_emul_get_dci_cfg(ps8xxx_emul) & + PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK, + NULL); +} + +/** Test PS8815 init */ +ZTEST(ps8815, test_ps8815_init) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *p1_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_1); + + /* Set arbitrary FW reg value != 0 for this test */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x31); + /* Set correct power status for rest of the test */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_POWER_STATUS, 0x0); + + /* Test fail on reading HW revision register */ + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + PS8815_P1_REG_HW_REVISION); + zassert_equal(EC_ERROR_INVAL, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test successful init */ + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); +} + +/** Test PS8xxx release */ +static void test_ps8xxx_release(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + uint64_t start_ms; + + /* Test successful release with correct FW reg read */ + start_ms = k_uptime_get(); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.release(USBC_PORT_C1), NULL); + zassert_true(k_uptime_get() - start_ms < 10, + "release on correct FW reg read shouldn't wait for chip"); + + /* Test delay on FW reg read fail */ + i2c_common_emul_set_read_fail_reg(common_data, PS8XXX_REG_FW_REV); + start_ms = k_uptime_get(); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.release(USBC_PORT_C1), NULL); + zassert_true(k_uptime_get() - start_ms >= 10, + "release on FW reg read fail should wait for chip"); +} + +ZTEST(ps8805, test_release) +{ + test_ps8xxx_release(); +} + +ZTEST(ps8815, test_release) +{ + test_ps8xxx_release(); +} + +/** + * Check if PS8815 set_cc write correct value to ROLE_CTRL register and if + * PS8815 specific workaround is applied to RP_DETECT_CONTROL. + */ +static void check_ps8815_set_cc(enum tcpc_rp_value rp, enum tcpc_cc_pull cc, + uint16_t rp_detect_ctrl, const char *test_case) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + uint16_t reg_val, exp_role_ctrl; + + /* Clear RP detect register to see if it is set after test */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_RP_DETECT_CONTROL, 0); + + exp_role_ctrl = TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, rp, cc, cc); + + zassert_equal(EC_SUCCESS, + ps8xxx_tcpm_drv.select_rp_value(USBC_PORT_C1, rp), + "Failed to set RP for case: %s", test_case); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.set_cc(USBC_PORT_C1, cc), + "Failed to set CC for case: %s", test_case); + + zassert_ok(tcpci_emul_get_reg(ps8xxx_emul, TCPC_REG_ROLE_CTRL, + ®_val), + "Failed tcpci_emul_get_reg() for case: %s", test_case); + zassert_equal(exp_role_ctrl, reg_val, + "0x%x != (role_ctrl = 0x%x) for case: %s", exp_role_ctrl, + reg_val, test_case); + zassert_ok(tcpci_emul_get_reg(ps8xxx_emul, PS8XXX_REG_RP_DETECT_CONTROL, + ®_val), + "Failed tcpci_emul_get_reg() for case: %s", test_case); + zassert_equal(rp_detect_ctrl, reg_val, + "0x%x != (rp detect = 0x%x) for case: %s", rp_detect_ctrl, + reg_val, test_case); +} + +/** Test PS8815 set cc and device specific workarounds */ +ZTEST(ps8815, test_ps8815_set_cc) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + int64_t start_time; + int64_t delay; + + /* + * Set other hw revision to disable workaround for b/171430855 (delay + * 1 ms on role control reg update). Delay could introduce thread switch + * which may disturb this test. + */ + ps8xxx_emul_set_hw_rev(ps8xxx_emul, 0x0a02); + + /* Set firmware version <= 0x10 to set "disable rp detect" workaround */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x8); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, RP_DETECT_DISABLE, + "fw rev 0x8 \"disable rp detect\" workaround"); + + /* First call to set_cc should disarm workaround */ + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, 0, + "second call without workaround"); + + /* drp_toggle should rearm "disable rp detect" workaround */ + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.drp_toggle(USBC_PORT_C1), + NULL); + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, RP_DETECT_DISABLE, + "drp_toggle rearm workaround"); + + /* + * Set firmware version <= 0x10 to set "disable rp detect" workaround + * again + */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0xa); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + + /* CC RD shouldn't trigger "disable rp detect" workaround */ + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RD, 0, + "CC RD not trigger workaround"); + + /* + * Set firmware version > 0x10 to unset "disable rp detect" + * workaround + */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x12); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + + /* Firmware > 0x10 shouldn't trigger "disable rp detect" workaround */ + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, 0, + "fw rev > 0x10 not trigger workaround"); + + /* + * Set hw revision 0x0a00 to enable workaround for b/171430855 (delay + * 1 ms on role control reg update) + */ + ps8xxx_emul_set_hw_rev(ps8xxx_emul, 0x0a00); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + + start_time = k_uptime_get(); + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, 0, + "delay on HW rev 0x0a00"); + delay = k_uptime_delta(&start_time); + zassert_true(delay >= 1, "expected delay on HW rev 0x0a00 (delay %lld)", + delay); + + /* + * Set hw revision 0x0a01 to enable workaround for b/171430855 (delay + * 1 ms on role control reg update) + */ + ps8xxx_emul_set_hw_rev(ps8xxx_emul, 0x0a01); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + start_time = k_uptime_get(); + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, 0, + "delay on HW rev 0x0a01"); + delay = k_uptime_delta(&start_time); + zassert_true(delay >= 1, "expected delay on HW rev 0x0a01 (delay %lld)", + delay); + + /* + * Set other hw revision to disable workaround for b/171430855 (delay + * 1 ms on role control reg update) + */ + ps8xxx_emul_set_hw_rev(ps8xxx_emul, 0x0a02); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + start_time = k_uptime_get(); + check_ps8815_set_cc(TYPEC_RP_1A5, TYPEC_CC_RP, 0, + "no delay on other HW rev"); + delay = k_uptime_delta(&start_time); + zassert_true(delay == 0, + "unexpected delay on HW rev 0x0a02 (delay %lld)", delay); +} + +/** Test PS8xxx set vconn */ +static void test_ps8xxx_set_vconn(void) +{ + uint64_t start_ms; + + /* Test vconn enable */ + start_ms = k_uptime_get(); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.set_vconn(USBC_PORT_C1, 1), + NULL); + zassert_true(k_uptime_get() - start_ms < 10, + "VCONN enable should be without delay"); + + /* Test vconn disable */ + start_ms = k_uptime_get(); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.set_vconn(USBC_PORT_C1, 0), + NULL); + /* Delay for VCONN disable is required because of issue b/185202064 */ + zassert_true(k_uptime_get() - start_ms >= 10, + "VCONN disable require minimum 10ms delay"); +} + +ZTEST(ps8805, test_set_vconn) +{ + test_ps8xxx_set_vconn(); +} + +ZTEST(ps8815, test_set_vconn) +{ + test_ps8xxx_set_vconn(); +} + +/** Test PS8xxx transmitting message from TCPC */ +static void test_ps8xxx_transmit(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + struct tcpci_emul_msg *msg; + uint64_t exp_cnt, cnt; + uint16_t reg_val; + + msg = tcpci_emul_get_tx_msg(ps8xxx_emul); + + /* Test fail on transmitting BIST MODE 2 message */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_TRANSMIT); + zassert_equal(EC_ERROR_INVAL, + ps8xxx_tcpm_drv.transmit( + USBC_PORT_C1, TCPCI_MSG_TX_BIST_MODE_2, 0, NULL), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test sending BIST MODE 2 message */ + exp_cnt = PS8751_BIST_COUNTER; + zassert_equal(EC_SUCCESS, + ps8xxx_tcpm_drv.transmit( + USBC_PORT_C1, TCPCI_MSG_TX_BIST_MODE_2, 0, NULL), + NULL); + check_tcpci_reg(ps8xxx_emul, PS8XXX_REG_BIST_CONT_MODE_CTR, 0); + zassert_equal(TCPCI_MSG_TX_BIST_MODE_2, msg->sop_type, NULL); + + /* Check BIST counter value */ + zassert_ok(tcpci_emul_get_reg(ps8xxx_emul, + PS8XXX_REG_BIST_CONT_MODE_BYTE2, + ®_val), + NULL); + cnt = reg_val; + cnt <<= 8; + zassert_ok(tcpci_emul_get_reg(ps8xxx_emul, + PS8XXX_REG_BIST_CONT_MODE_BYTE1, + ®_val), + NULL); + cnt |= reg_val; + cnt <<= 8; + zassert_ok(tcpci_emul_get_reg(ps8xxx_emul, + PS8XXX_REG_BIST_CONT_MODE_BYTE0, + ®_val), + NULL); + cnt |= reg_val; + zassert_equal(exp_cnt, cnt, "0x%llx != 0x%llx", exp_cnt, cnt); +} + +ZTEST(ps8805, test_transmit) +{ + test_ps8xxx_transmit(); +} + +ZTEST(ps8815, test_transmit) +{ + test_ps8xxx_transmit(); +} + +/** Test PS8805 and PS8815 drp toggle */ +static void test_ps88x5_drp_toggle(bool delay_expected) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + uint16_t exp_role_ctrl; + int64_t start_time; + int64_t delay; + + /* Test fail on command write */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_COMMAND); + zassert_equal(EC_ERROR_INVAL, ps8xxx_tcpm_drv.drp_toggle(USBC_PORT_C1), + NULL); + + /* Test fail on role control write */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_ROLE_CTRL); + zassert_equal(EC_ERROR_INVAL, ps8xxx_tcpm_drv.drp_toggle(USBC_PORT_C1), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on CC status read */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_CC_STATUS); + zassert_equal(EC_ERROR_INVAL, ps8xxx_tcpm_drv.drp_toggle(USBC_PORT_C1), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set CC status as snk, CC lines set arbitrary */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_CC_STATUS, + TCPC_REG_CC_STATUS_SET(1, TYPEC_CC_VOLT_OPEN, + TYPEC_CC_VOLT_RA)); + + /* + * TODO(b/203858808): PS8815 sleep here if specific FW rev. + * Find way to test 1 ms delay + */ + /* Test drp toggle when CC is snk. Role control CC lines should be RP */ + exp_role_ctrl = TCPC_REG_ROLE_CTRL_SET(TYPEC_DRP, TYPEC_RP_USB, + TYPEC_CC_RP, TYPEC_CC_RP); + start_time = k_uptime_get(); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.drp_toggle(USBC_PORT_C1), + NULL); + delay = k_uptime_delta(&start_time); + if (delay_expected) { + zassert_true(delay >= 1, "expected delay (%lld ms)", delay); + } else { + zassert_true(delay == 0, "unexpected delay (%lld ms)", delay); + } + check_tcpci_reg(ps8xxx_emul, TCPC_REG_ROLE_CTRL, exp_role_ctrl); + check_tcpci_reg(ps8xxx_emul, TCPC_REG_COMMAND, + TCPC_REG_COMMAND_LOOK4CONNECTION); + + /* Set CC status as src, CC lines set arbitrary */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_CC_STATUS, + TCPC_REG_CC_STATUS_SET(0, TYPEC_CC_VOLT_OPEN, + TYPEC_CC_VOLT_RA)); + + /* Test drp toggle when CC is src. Role control CC lines should be RD */ + exp_role_ctrl = TCPC_REG_ROLE_CTRL_SET(TYPEC_DRP, TYPEC_RP_USB, + TYPEC_CC_RD, TYPEC_CC_RD); + start_time = k_uptime_get(); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.drp_toggle(USBC_PORT_C1), + NULL); + delay = k_uptime_delta(&start_time); + if (delay_expected) { + zassert_true(delay >= 1, "expected delay (%lld ms)", delay); + } else { + zassert_true(delay == 0, "unexpected delay (%lld ms)", delay); + } + check_tcpci_reg(ps8xxx_emul, TCPC_REG_ROLE_CTRL, exp_role_ctrl); + check_tcpci_reg(ps8xxx_emul, TCPC_REG_COMMAND, + TCPC_REG_COMMAND_LOOK4CONNECTION); +} + +/** Test PS8815 drp toggle */ +ZTEST(ps8815, test_ps8815_drp_toggle) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + + /* + * Set hw revision 0x0a00 to enable workaround for b/171430855 (delay + * 1 ms on role control reg update) + */ + ps8xxx_emul_set_hw_rev(ps8xxx_emul, 0x0a00); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + test_ps88x5_drp_toggle(true); + + /* + * Set other hw revision to disable workaround for b/171430855 (delay + * 1 ms on role control reg update) + */ + ps8xxx_emul_set_hw_rev(ps8xxx_emul, 0x0a02); + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + test_ps88x5_drp_toggle(false); +} + +/** Test PS8805 drp toggle */ +ZTEST(ps8805, test_drp_toggle) +{ + test_ps88x5_drp_toggle(false); +} + +/** Test PS8xxx get chip info code used by all PS8xxx devices */ +static void test_ps8xxx_get_chip_info(uint16_t current_product_id) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + struct ec_response_pd_chip_info_v1 info; + uint16_t vendor, product, device_id, fw_rev; + + /* Setup chip info */ + vendor = PS8XXX_VENDOR_ID; + /* Get currently used product ID */ + product = current_product_id; + /* Arbitrary choose device ID that doesn't require fixing */ + device_id = 0x2; + /* Arbitrary revision */ + fw_rev = 0x32; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_PRODUCT_ID, product); + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_BCD_DEV, device_id); + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, fw_rev); + + /* Test fail on reading FW revision */ + i2c_common_emul_set_read_fail_reg(common_data, PS8XXX_REG_FW_REV); + zassert_equal(EC_ERROR_INVAL, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test reading chip info */ + zassert_equal(EC_SUCCESS, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + zassert_equal(vendor, info.vendor_id, NULL); + zassert_equal(product, info.product_id, NULL); + zassert_equal(device_id, info.device_id, NULL); + zassert_equal(fw_rev, info.fw_version_number, NULL); + + /* Test fail on wrong vendor id */ + vendor = 0x0; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + zassert_equal(EC_ERROR_UNKNOWN, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + + /* Set correct vendor id */ + vendor = PS8XXX_VENDOR_ID; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + + /* Set firmware revision to 0 */ + fw_rev = 0x0; + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, fw_rev); + + /* + * Test fail on firmware revision equals to 0 when getting chip info + * from live device + */ + zassert_equal(EC_ERROR_UNKNOWN, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + + /* + * Test if firmware revision 0 is accepted when getting chip info from + * not live device + */ + zassert_equal(EC_SUCCESS, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 0, &info), + NULL); + zassert_equal(vendor, info.vendor_id, NULL); + zassert_equal(product, info.product_id, NULL); + zassert_equal(device_id, info.device_id, NULL); + zassert_equal(fw_rev, info.fw_version_number, NULL); + + /* Set wrong vendor id */ + vendor = 0; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + + /* Test fail on vendor id mismatch on live device */ + zassert_equal(EC_ERROR_UNKNOWN, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + + /* Test that vendor id is fixed on not live device */ + zassert_equal(EC_SUCCESS, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 0, &info), + NULL); + zassert_equal(PS8XXX_VENDOR_ID, info.vendor_id, NULL); + zassert_equal(product, info.product_id, NULL); + zassert_equal(device_id, info.device_id, NULL); + zassert_equal(fw_rev, info.fw_version_number, NULL); + + /* Set correct vendor id */ + vendor = PS8XXX_VENDOR_ID; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + + /* Set wrong product id */ + product = 0; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_PRODUCT_ID, product); + + /* Test fail on product id mismatch on live device */ + zassert_equal(EC_ERROR_UNKNOWN, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + + /* Test that product id is fixed on not live device */ + zassert_equal(EC_SUCCESS, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 0, &info), + NULL); + zassert_equal(vendor, info.vendor_id, NULL); + zassert_equal(board_get_ps8xxx_product_id(USBC_PORT_C1), + info.product_id, NULL); + zassert_equal(device_id, info.device_id, NULL); + zassert_equal(fw_rev, info.fw_version_number, NULL); + + zassert_equal(false, check_ps8755_chip(USBC_PORT_C1), NULL); +} + +ZTEST(ps8805, test_ps8805_get_chip_info) +{ + test_ps8xxx_get_chip_info(PS8805_PRODUCT_ID); +} + +ZTEST(ps8815, test_ps8815_get_chip_info) +{ + test_ps8xxx_get_chip_info(PS8815_PRODUCT_ID); +} + +/** Test PS8805 get chip info and indirectly ps8805_make_device_id */ +ZTEST(ps8805, test_ps8805_get_chip_info_fix_dev_id) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *p0_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_0); + struct ec_response_pd_chip_info_v1 info; + uint16_t vendor, product, device_id, fw_rev; + uint16_t chip_rev; + + struct { + uint16_t exp_dev_id; + uint16_t chip_rev; + } test_param[] = { + /* Test A3 chip revision */ + { + .exp_dev_id = 0x2, + .chip_rev = 0xa0, + }, + /* Test A2 chip revision */ + { + .exp_dev_id = 0x1, + .chip_rev = 0x0, + }, + }; + + /* Setup chip info */ + vendor = PS8XXX_VENDOR_ID; + product = PS8805_PRODUCT_ID; + /* Arbitrary revision */ + fw_rev = 0x32; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_PRODUCT_ID, product); + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, fw_rev); + + /* Set correct power status for this test */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_POWER_STATUS, 0x0); + /* Init to allow access to "hidden" I2C ports */ + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + + /* Set device id which requires fixing */ + device_id = 0x1; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_BCD_DEV, device_id); + + /* Test error on fixing device id because of fail chip revision read */ + i2c_common_emul_set_read_fail_reg(p0_i2c_common_data, + PS8805_P0_REG_CHIP_REVISION); + zassert_equal(EC_ERROR_INVAL, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + i2c_common_emul_set_read_fail_reg(p0_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set wrong chip revision */ + chip_rev = 0x32; + ps8xxx_emul_set_chip_rev(ps8xxx_emul, chip_rev); + + /* Test error on fixing device id */ + zassert_equal(EC_ERROR_UNKNOWN, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + + /* Test fixing device id for specific chip revisions */ + for (int i = 0; i < ARRAY_SIZE(test_param); i++) { + ps8xxx_emul_set_chip_rev(ps8xxx_emul, test_param[i].chip_rev); + + /* Test correct device id after fixing */ + zassert_equal( + EC_SUCCESS, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + "Failed to get chip info in test case %d (chip_rev 0x%x)", + i, test_param[i].chip_rev); + zassert_equal( + vendor, info.vendor_id, + "0x%x != (vendor = 0x%x) in test case %d (chip_rev 0x%x)", + vendor, info.vendor_id, i, test_param[i].chip_rev); + zassert_equal( + product, info.product_id, + "0x%x != (product = 0x%x) in test case %d (chip_rev 0x%x)", + product, info.product_id, i, test_param[i].chip_rev); + zassert_equal( + test_param[i].exp_dev_id, info.device_id, + "0x%x != (device = 0x%x) in test case %d (chip_rev 0x%x)", + test_param[i].exp_dev_id, info.device_id, i, + test_param[i].chip_rev); + zassert_equal( + fw_rev, info.fw_version_number, + "0x%x != (FW rev = 0x%x) in test case %d (chip_rev 0x%x)", + fw_rev, info.fw_version_number, i, + test_param[i].chip_rev); + } +} + +/** Test PS8815 get chip info and indirectly ps8815_make_device_id */ +ZTEST(ps8815, test_ps8815_get_chip_info_fix_dev_id) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *p1_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_1); + struct ec_response_pd_chip_info_v1 info; + uint16_t vendor, product, device_id, fw_rev; + uint16_t hw_rev; + + struct { + uint16_t exp_dev_id; + uint16_t hw_rev; + } test_param[] = { + /* Test A0 HW revision */ + { + .exp_dev_id = 0x1, + .hw_rev = 0x0a00, + }, + /* Test A1 HW revision */ + { + .exp_dev_id = 0x2, + .hw_rev = 0x0a01, + }, + /* Test A2 HW revision */ + { + .exp_dev_id = 0x3, + .hw_rev = 0x0a02, + }, + }; + + /* Setup chip info */ + vendor = PS8XXX_VENDOR_ID; + product = PS8815_PRODUCT_ID; + /* Arbitrary revision */ + fw_rev = 0x32; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_VENDOR_ID, vendor); + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_PRODUCT_ID, product); + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, fw_rev); + + /* Set device id which requires fixing */ + device_id = 0x1; + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_BCD_DEV, device_id); + + /* Test error on fixing device id because of fail hw revision read */ + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + PS8815_P1_REG_HW_REVISION); + zassert_equal(EC_ERROR_INVAL, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set wrong hw revision */ + hw_rev = 0x32; + ps8xxx_emul_set_hw_rev(ps8xxx_emul, hw_rev); + + /* Test error on fixing device id */ + zassert_equal(EC_ERROR_UNKNOWN, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + NULL); + + /* Test fixing device id for specific HW revisions */ + for (int i = 0; i < ARRAY_SIZE(test_param); i++) { + ps8xxx_emul_set_hw_rev(ps8xxx_emul, test_param[i].hw_rev); + + /* Test correct device id after fixing */ + zassert_equal( + EC_SUCCESS, + ps8xxx_tcpm_drv.get_chip_info(USBC_PORT_C1, 1, &info), + "Failed to get chip info in test case %d (hw_rev 0x%x)", + i, test_param[i].hw_rev); + zassert_equal( + vendor, info.vendor_id, + "0x%x != (vendor = 0x%x) in test case %d (hw_rev 0x%x)", + vendor, info.vendor_id, i, test_param[i].hw_rev); + zassert_equal( + product, info.product_id, + "0x%x != (product = 0x%x) in test case %d (hw_rev 0x%x)", + product, info.product_id, i, test_param[i].hw_rev); + zassert_equal( + test_param[i].exp_dev_id, info.device_id, + "0x%x != (device = 0x%x) in test case %d (hw_rev 0x%x)", + test_param[i].exp_dev_id, info.device_id, i, + test_param[i].hw_rev); + zassert_equal( + fw_rev, info.fw_version_number, + "0x%x != (FW rev = 0x%x) in test case %d (hw_rev 0x%x)", + fw_rev, info.fw_version_number, i, + test_param[i].hw_rev); + } +} + +/** Test PS8805 get/set gpio */ +ZTEST(ps8805, test_ps8805_gpio) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *gpio_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_GPIO); + uint8_t exp_ctrl, gpio_ctrl; + int level; + + struct { + enum ps8805_gpio signal; + uint16_t gpio_reg; + int level; + } test_param[] = { + /* Chain of set and unset GPIO to test */ + { + .gpio_reg = PS8805_REG_GPIO_0, + .signal = PS8805_GPIO_0, + .level = 1, + }, + { + .gpio_reg = PS8805_REG_GPIO_1, + .signal = PS8805_GPIO_1, + .level = 1, + }, + { + .gpio_reg = PS8805_REG_GPIO_2, + .signal = PS8805_GPIO_2, + .level = 1, + }, + /* Test setting GPIO 0 which is already set */ + { + .gpio_reg = PS8805_REG_GPIO_0, + .signal = PS8805_GPIO_0, + .level = 1, + }, + /* Test clearing GPIOs */ + { + .gpio_reg = PS8805_REG_GPIO_0, + .signal = PS8805_GPIO_0, + .level = 0, + }, + { + .gpio_reg = PS8805_REG_GPIO_1, + .signal = PS8805_GPIO_1, + .level = 0, + }, + { + .gpio_reg = PS8805_REG_GPIO_2, + .signal = PS8805_GPIO_2, + .level = 0, + }, + /* Test clearing GPIO 0 which is already unset */ + { + .gpio_reg = PS8805_REG_GPIO_0, + .signal = PS8805_GPIO_0, + .level = 1, + }, + }; + + /* Set arbitrary FW reg value != 0 for this test */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x31); + /* Set correct power status for this test */ + tcpci_emul_set_reg(ps8xxx_emul, TCPC_REG_POWER_STATUS, 0x0); + /* Init to allow access to "hidden" I2C ports */ + zassert_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); + + /* Test fail on invalid signal for gpio control reg */ + zassert_equal(EC_ERROR_INVAL, + ps8805_gpio_set_level(USBC_PORT_C1, PS8805_GPIO_NUM, 1), + NULL); + zassert_equal(EC_ERROR_INVAL, + ps8805_gpio_get_level(USBC_PORT_C1, PS8805_GPIO_NUM, + &level), + NULL); + + /* Setup fail on gpio control reg read */ + i2c_common_emul_set_read_fail_reg(gpio_i2c_common_data, + PS8805_REG_GPIO_CONTROL); + + /* Test fail on reading gpio control reg */ + zassert_equal(EC_ERROR_INVAL, + ps8805_gpio_set_level(USBC_PORT_C1, PS8805_GPIO_0, 1), + NULL); + zassert_equal(EC_ERROR_INVAL, + ps8805_gpio_get_level(USBC_PORT_C1, PS8805_GPIO_0, + &level), + NULL); + + /* Do not fail on gpio control reg read */ + i2c_common_emul_set_read_fail_reg(gpio_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on writing gpio control reg */ + i2c_common_emul_set_write_fail_reg(gpio_i2c_common_data, + PS8805_REG_GPIO_CONTROL); + zassert_equal(EC_ERROR_INVAL, + ps8805_gpio_set_level(USBC_PORT_C1, PS8805_GPIO_0, 1), + NULL); + i2c_common_emul_set_write_fail_reg(gpio_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Clear gpio control reg */ + ps8xxx_emul_set_gpio_ctrl(ps8xxx_emul, 0x0); + exp_ctrl = 0; + + /* Test set and unset GPIO */ + for (int i = 0; i < ARRAY_SIZE(test_param); i++) { + if (test_param[i].level) { + exp_ctrl |= test_param[i].gpio_reg; + } else { + exp_ctrl &= ~test_param[i].gpio_reg; + } + zassert_equal( + EC_SUCCESS, + ps8805_gpio_set_level(USBC_PORT_C1, + test_param[i].signal, + test_param[i].level), + "Failed gpio_set in test case %d (gpio %d, level %d)", + i, test_param[i].signal, test_param[i].level); + zassert_equal( + EC_SUCCESS, + ps8805_gpio_get_level(USBC_PORT_C1, + test_param[i].signal, &level), + "Failed gpio_get in test case %d (gpio %d, level %d)", + i, test_param[i].signal, test_param[i].level); + zassert_equal( + test_param[i].level, level, + "%d != (gpio_get_level = %d) in test case %d (gpio %d, level %d)", + test_param[i].level, level, i, test_param[i].signal, + test_param[i].level); + gpio_ctrl = ps8xxx_emul_get_gpio_ctrl(ps8xxx_emul); + zassert_equal( + exp_ctrl, gpio_ctrl, + "0x%x != (gpio_ctrl = 0x%x) in test case %d (gpio %d, level %d)", + exp_ctrl, gpio_ctrl, i, test_param[i].signal, + test_param[i].level); + } +} + +/** Test TCPCI init and vbus level */ +static void test_ps8xxx_tcpci_init(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_init(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_init) +{ + test_ps8xxx_tcpci_init(); +} + +ZTEST(ps8815, test_tcpci_init) +{ + test_ps8xxx_tcpci_init(); +} + +/** Test TCPCI release */ +static void test_ps8xxx_tcpci_release(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_release(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_release) +{ + test_ps8xxx_tcpci_release(); +} + +ZTEST(ps8815, test_tcpci_release) +{ + test_ps8xxx_tcpci_release(); +} + +/** Test TCPCI get cc */ +static void test_ps8xxx_tcpci_get_cc(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_get_cc(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_get_cc) +{ + test_ps8xxx_tcpci_get_cc(); +} + +ZTEST(ps8815, test_tcpci_get_cc) +{ + test_ps8xxx_tcpci_get_cc(); +} + +/** Test TCPCI set cc */ +static void test_ps8xxx_tcpci_set_cc(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_set_cc(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_set_cc) +{ + test_ps8xxx_tcpci_set_cc(); +} + +ZTEST(ps8815, test_tcpci_set_cc) +{ + test_ps8xxx_tcpci_set_cc(); +} + +/** Test TCPCI set polarity */ +static void test_ps8xxx_tcpci_set_polarity(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_set_polarity(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_set_polarity) +{ + test_ps8xxx_tcpci_set_polarity(); +} + +ZTEST(ps8815, test_tcpci_set_polarity) +{ + test_ps8xxx_tcpci_set_polarity(); +} + +/** Test TCPCI set vconn */ +static void test_ps8xxx_tcpci_set_vconn(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_set_vconn(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_set_vconn) +{ + test_ps8xxx_tcpci_set_vconn(); +} + +ZTEST(ps8815, test_tcpci_set_vconn) +{ + test_ps8xxx_tcpci_set_vconn(); +} + +/** Test TCPCI set msg header */ +static void test_ps8xxx_tcpci_set_msg_header(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_set_msg_header(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_set_msg_header) +{ + test_ps8xxx_tcpci_set_msg_header(); +} + +ZTEST(ps8815, test_tcpci_set_msg_header) +{ + test_ps8xxx_tcpci_set_msg_header(); +} + +/** Test TCPCI get raw message */ +static void test_ps8xxx_tcpci_get_rx_message_raw(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_get_rx_message_raw(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_get_rx_message_raw) +{ + test_ps8xxx_tcpci_get_rx_message_raw(); +} + +ZTEST(ps8815, test_tcpci_get_rx_message_raw) +{ + test_ps8xxx_tcpci_get_rx_message_raw(); +} + +/** Test TCPCI transmitting message */ +static void test_ps8xxx_tcpci_transmit(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_transmit(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_transmit) +{ + test_ps8xxx_tcpci_transmit(); +} + +ZTEST(ps8815, test_tcpci_transmit) +{ + test_ps8xxx_tcpci_transmit(); +} + +/** Test TCPCI alert */ +static void test_ps8xxx_tcpci_alert(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_alert(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_alert) +{ + test_ps8xxx_tcpci_alert(); +} + +ZTEST(ps8815, test_tcpci_alert) +{ + test_ps8xxx_tcpci_alert(); +} + +/** Test TCPCI alert RX message */ +static void test_ps8xxx_tcpci_alert_rx_message(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_alert_rx_message(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_alert_rx_message) +{ + test_ps8xxx_tcpci_alert_rx_message(); +} + +ZTEST(ps8815, test_tcpci_alert_rx_message) +{ + test_ps8xxx_tcpci_alert_rx_message(); +} + +/** Test TCPCI enter low power mode */ +static void test_ps8xxx_tcpci_low_power_mode(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + /* + * PS8751/PS8815 has the auto sleep function that enters + * low power mode on its own in ~2 seconds. Other chips + * don't have it. Stub it out for PS8751/PS8815. + */ + if (board_get_ps8xxx_product_id(USBC_PORT_C1) == PS8751_PRODUCT_ID || + board_get_ps8xxx_product_id(USBC_PORT_C1) == PS8815_PRODUCT_ID) + return; + test_tcpci_low_power_mode(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_low_power_mode) +{ + test_ps8xxx_tcpci_low_power_mode(); +} + +ZTEST(ps8815, test_tcpci_low_power_mode) +{ + test_ps8xxx_tcpci_low_power_mode(); +} + +/** Test TCPCI set bist test mode */ +static void test_ps8xxx_tcpci_set_bist_mode(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + test_tcpci_set_bist_mode(ps8xxx_emul, common_data, USBC_PORT_C1); +} + +ZTEST(ps8805, test_tcpci_set_bist_mode) +{ + test_ps8xxx_tcpci_set_bist_mode(); +} + +ZTEST(ps8815, test_tcpci_set_bist_mode) +{ + test_ps8xxx_tcpci_set_bist_mode(); +} + +/* Setup no fail for all I2C devices associated with PS8xxx emulator */ +static void setup_no_fail_all(void) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(ps8xxx_emul); + + struct i2c_common_emul_data *p0_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_0); + struct i2c_common_emul_data *p1_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_1); + struct i2c_common_emul_data *gpio_i2c_common_data = + ps8xxx_emul_get_i2c_common_data(ps8xxx_emul, + PS8XXX_EMUL_PORT_GPIO); + + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + if (p0_i2c_common_data != NULL) { + i2c_common_emul_set_read_fail_reg(p0_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(p0_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + } + + if (p1_i2c_common_data != NULL) { + i2c_common_emul_set_read_fail_reg(p1_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(p1_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + } + + if (gpio_i2c_common_data != NULL) { + i2c_common_emul_set_read_fail_reg(gpio_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + i2c_common_emul_set_write_fail_reg(gpio_i2c_common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + } +} + +/** + * Setup PS8xxx emulator to mimic PS8805 and setup no fail for all I2C devices + * associated with PS8xxx emulator + */ +static void ps8805_before(void *state) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + ARG_UNUSED(state); + + board_set_ps8xxx_product_id(PS8805_PRODUCT_ID); + ps8xxx_emul_set_product_id(ps8xxx_emul, PS8805_PRODUCT_ID); + setup_no_fail_all(); + zassume_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); +} + +static void ps8805_after(void *state) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + ARG_UNUSED(state); + + /* Set correct firmware revision */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x31); +} + +/** + * Setup PS8xxx emulator to mimic PS8815 and setup no fail for all I2C devices + * associated with PS8xxx emulator + */ +static void ps8815_before(void *state) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + ARG_UNUSED(state); + + board_set_ps8xxx_product_id(PS8815_PRODUCT_ID); + ps8xxx_emul_set_product_id(ps8xxx_emul, PS8815_PRODUCT_ID); + setup_no_fail_all(); + zassume_equal(EC_SUCCESS, ps8xxx_tcpm_drv.init(USBC_PORT_C1), NULL); +} + +static void ps8815_after(void *state) +{ + const struct emul *ps8xxx_emul = EMUL_DT_GET(PS8XXX_EMUL_NODE); + ARG_UNUSED(state); + + /* Set correct firmware revision */ + tcpci_emul_set_reg(ps8xxx_emul, PS8XXX_REG_FW_REV, 0x31); +} + +ZTEST_SUITE(ps8805, drivers_predicate_pre_main, NULL, ps8805_before, + ps8805_after, NULL); + +ZTEST_SUITE(ps8815, drivers_predicate_pre_main, NULL, ps8815_before, + ps8815_after, NULL); diff --git a/zephyr/test/drivers/default/src/smart.c b/zephyr/test/drivers/default/src/smart.c new file mode 100644 index 0000000000..96200f1b91 --- /dev/null +++ b/zephyr/test/drivers/default/src/smart.c @@ -0,0 +1,568 @@ +/* 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/shell/shell.h> +#include <zephyr/shell/shell_uart.h> + +#include "common.h" +#include "console.h" +#include "i2c.h" +#include "emul/emul_common_i2c.h" +#include "emul/emul_smart_battery.h" + +#include "battery.h" +#include "battery_smart.h" +#include "test/drivers/test_state.h" + +#define BATTERY_NODE DT_NODELABEL(battery) + +/** Test all simple getters */ +ZTEST_USER(smart_battery, test_battery_getters) +{ + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + char block[32]; + int expected; + int word; + + bat = sbat_emul_get_bat_data(emul); + + zassert_equal(EC_SUCCESS, battery_get_mode(&word), NULL); + zassert_equal(bat->mode, word, "%d != %d", bat->mode, word); + + expected = 100 * bat->cap / bat->design_cap; + zassert_equal(EC_SUCCESS, battery_state_of_charge_abs(&word), NULL); + zassert_equal(expected, word, "%d != %d", expected, word); + + zassert_equal(EC_SUCCESS, battery_cycle_count(&word), NULL); + zassert_equal(bat->cycle_count, word, "%d != %d", bat->cycle_count, + word); + zassert_equal(EC_SUCCESS, battery_design_voltage(&word), NULL); + zassert_equal(bat->design_mv, word, "%d != %d", bat->design_mv, word); + zassert_equal(EC_SUCCESS, battery_serial_number(&word), NULL); + zassert_equal(bat->sn, word, "%d != %d", bat->sn, word); + zassert_equal(EC_SUCCESS, get_battery_manufacturer_name(block, 32), + NULL); + zassert_mem_equal(block, bat->mf_name, bat->mf_name_len, "%s != %s", + block, bat->mf_name); + zassert_equal(EC_SUCCESS, battery_device_name(block, 32), NULL); + zassert_mem_equal(block, bat->dev_name, bat->dev_name_len, "%s != %s", + block, bat->dev_name); + zassert_equal(EC_SUCCESS, battery_device_chemistry(block, 32), NULL); + zassert_mem_equal(block, bat->dev_chem, bat->dev_chem_len, "%s != %s", + block, bat->dev_chem); + word = battery_get_avg_current(); + zassert_equal(bat->avg_cur, word, "%d != %d", bat->avg_cur, word); + word = battery_get_avg_voltage(); + zassert_equal(bat->volt, word, "%d != %d", bat->volt, word); + + bat->avg_cur = 200; + expected = (bat->full_cap - bat->cap) * 60 / bat->avg_cur; + zassert_equal(EC_SUCCESS, battery_time_to_full(&word), NULL); + zassert_equal(expected, word, "%d != %d", expected, word); + + bat->cur = -200; + expected = bat->cap * 60 / (-bat->cur); + zassert_equal(EC_SUCCESS, battery_run_time_to_empty(&word), NULL); + zassert_equal(expected, word, "%d != %d", expected, word); + + bat->avg_cur = -200; + expected = bat->cap * 60 / (-bat->avg_cur); + zassert_equal(EC_SUCCESS, battery_time_to_empty(&word), NULL); + zassert_equal(expected, word, "%d != %d", expected, word); +} + +/** Test getting capacity. These functions should force mAh mode */ +ZTEST_USER(smart_battery, test_battery_get_capacity) +{ + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + int word; + + bat = sbat_emul_get_bat_data(emul); + + /* Test fail when checking battery mode */ + i2c_common_emul_set_read_fail_reg(common_data, SB_BATTERY_MODE); + zassert_equal(EC_ERROR_INVAL, battery_remaining_capacity(&word), NULL); + zassert_equal(EC_ERROR_INVAL, battery_full_charge_capacity(&word), + NULL); + zassert_equal(EC_ERROR_INVAL, battery_design_capacity(&word), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test getting remaining capacity and if mAh mode is forced */ + bat->mode |= MODE_CAPACITY; + zassert_equal(EC_SUCCESS, battery_remaining_capacity(&word), NULL); + zassert_equal(bat->cap, word, "%d != %d", bat->cap, word); + zassert_false(bat->mode & MODE_CAPACITY, "mAh mode not forced"); + + /* Test getting full charge capacity and if mAh mode is forced */ + bat->mode |= MODE_CAPACITY; + zassert_equal(EC_SUCCESS, battery_full_charge_capacity(&word), NULL); + zassert_equal(bat->full_cap, word, "%d != %d", bat->full_cap, word); + zassert_false(bat->mode & MODE_CAPACITY, "mAh mode not forced"); + + /* Test getting design capacity and if mAh mode is forced */ + bat->mode |= MODE_CAPACITY; + zassert_equal(EC_SUCCESS, battery_design_capacity(&word), NULL); + zassert_equal(bat->design_cap, word, "%d != %d", bat->design_cap, word); + zassert_false(bat->mode & MODE_CAPACITY, "mAh mode not forced"); +} + +/** Test battery status */ +ZTEST_USER(smart_battery, test_battery_status) +{ + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + int expected; + int status; + + bat = sbat_emul_get_bat_data(emul); + + bat->status = 0; + bat->cur = -200; + bat->cap_alarm = 0; + bat->time_alarm = 0; + bat->cap = bat->full_cap / 2; + bat->error_code = STATUS_CODE_OVERUNDERFLOW; + + expected = 0; + expected |= STATUS_DISCHARGING; + expected |= STATUS_CODE_OVERUNDERFLOW; + + zassert_equal(EC_SUCCESS, battery_status(&status), NULL); + zassert_equal(expected, status, "%d != %d", expected, status); +} + +/** Test wait for stable function */ +ZTEST_USER(smart_battery, test_battery_wait_for_stable) +{ + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + + /* Should fail when read function always fail */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_FAIL_ALL_REG); + zassert_equal(EC_ERROR_NOT_POWERED, battery_wait_for_stable(), NULL); + + /* Should be ok with default handler */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_equal(EC_SUCCESS, battery_wait_for_stable(), NULL); +} + +/** Test manufacture date */ +ZTEST_USER(smart_battery, test_battery_manufacture_date) +{ + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + int day, month, year; + int exp_month = 5; + int exp_year = 2018; + int exp_day = 19; + uint16_t date; + + bat = sbat_emul_get_bat_data(emul); + + date = sbat_emul_date_to_word(exp_day, exp_month, exp_year); + bat->mf_date = date; + + zassert_equal(EC_SUCCESS, battery_manufacture_date(&year, &month, &day), + NULL); + zassert_equal(exp_day, day, "%d != %d", exp_day, day); + zassert_equal(exp_month, month, "%d != %d", exp_month, month); + zassert_equal(exp_year, year, "%d != %d", exp_year, year); +} + +/** Test time at rate */ +ZTEST_USER(smart_battery, test_battery_time_at_rate) +{ + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + int expect_time; + int minutes; + int rate; + + bat = sbat_emul_get_bat_data(emul); + + /* Test fail on rate 0 */ + rate = 0; + zassert_equal(EC_ERROR_INVAL, battery_time_at_rate(rate, &minutes), + NULL); + + /* 10mAh at rate 6000mA will be discharged in 6s */ + bat->cap = 10; + rate = -6000; + + /* Test fail on writing at rate register */ + i2c_common_emul_set_write_fail_reg(common_data, SB_AT_RATE); + zassert_equal(EC_ERROR_INVAL, battery_time_at_rate(rate, &minutes), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on reading at rate ok register */ + i2c_common_emul_set_read_fail_reg(common_data, SB_AT_RATE_OK); + zassert_equal(EC_ERROR_INVAL, battery_time_at_rate(rate, &minutes), + NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* + * Expected discharging rate is less then 10s, + * so AtRateOk() register should return 0 + */ + zassert_equal(EC_ERROR_TIMEOUT, battery_time_at_rate(rate, &minutes), + NULL); + + /* 3000mAh at rate 300mA will be discharged in 10h */ + bat->cap = 3000; + rate = -300; + expect_time = 600; + + zassert_equal(EC_SUCCESS, battery_time_at_rate(rate, &minutes), NULL); + zassert_equal(expect_time, minutes, "%d != %d", expect_time, minutes); + + /* 1000mAh at rate 1000mA will be charged in 1h */ + bat->cap = bat->full_cap - 1000; + rate = 1000; + /* battery_time_at_rate report time to full as negative number */ + expect_time = -60; + + zassert_equal(EC_SUCCESS, battery_time_at_rate(rate, &minutes), NULL); + zassert_equal(expect_time, minutes, "%d != %d", expect_time, minutes); +} + +/** Test battery get params */ +ZTEST_USER(smart_battery, test_battery_get_params) +{ + struct sbat_emul_bat_data *bat; + struct batt_params batt; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + int flags; + + bat = sbat_emul_get_bat_data(emul); + + /* Fail temperature read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_TEMPERATURE); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_TEMPERATURE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail state of charge read; want charge cannot be set */ + i2c_common_emul_set_read_fail_reg(common_data, + SB_RELATIVE_STATE_OF_CHARGE); + flags = BATT_FLAG_RESPONSIVE | BATT_FLAG_BAD_STATE_OF_CHARGE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail voltage read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_VOLTAGE); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_VOLTAGE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail current read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_CURRENT); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_CURRENT; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail average current read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_AVERAGE_CURRENT); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_AVERAGE_CURRENT; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail charging voltage read; want charge cannot be set */ + i2c_common_emul_set_read_fail_reg(common_data, SB_CHARGING_VOLTAGE); + flags = BATT_FLAG_RESPONSIVE | BATT_FLAG_BAD_DESIRED_VOLTAGE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail charging voltage read; want charge cannot be set */ + i2c_common_emul_set_read_fail_reg(common_data, SB_CHARGING_CURRENT); + flags = BATT_FLAG_RESPONSIVE | BATT_FLAG_BAD_DESIRED_CURRENT; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail remaining capacity read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_REMAINING_CAPACITY); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_REMAINING_CAPACITY; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail full capacity read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_FULL_CHARGE_CAPACITY); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_FULL_CAPACITY; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail status read */ + i2c_common_emul_set_read_fail_reg(common_data, SB_BATTERY_STATUS); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_STATUS; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Fail all */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_FAIL_ALL_REG); + flags = BATT_FLAG_BAD_ANY; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + + /* Use default handler, everything should be ok */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); +} + +struct mfgacc_data { + int reg; + uint8_t *buf; + int len; +}; + +static int mfgacc_read_func(const struct emul *emul, int reg, uint8_t *val, + int bytes, void *data) +{ + struct mfgacc_data *conf = data; + + if (bytes == 0 && conf->reg == reg) { + sbat_emul_set_response(emul, reg, conf->buf, conf->len, false); + } + + return 1; +} + +/** Test battery manufacturer access */ +ZTEST_USER(smart_battery, test_battery_mfacc) +{ + struct sbat_emul_bat_data *bat; + struct mfgacc_data mfacc_conf; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + uint8_t recv_buf[10]; + uint8_t mf_data[10]; + uint16_t cmd; + int len; + + bat = sbat_emul_get_bat_data(emul); + + /* Select arbitrary command number for the test */ + cmd = 0x1234; + + /* Test fail on to short receive buffer */ + len = 2; + zassert_equal(EC_ERROR_INVAL, + sb_read_mfgacc(cmd, SB_ALT_MANUFACTURER_ACCESS, recv_buf, + len), + NULL); + + /* Set correct length for rest of the test */ + len = 10; + + /* Test fail on writing SB_MANUFACTURER_ACCESS register */ + i2c_common_emul_set_write_fail_reg(common_data, SB_MANUFACTURER_ACCESS); + zassert_equal(EC_ERROR_INVAL, + sb_read_mfgacc(cmd, SB_ALT_MANUFACTURER_ACCESS, recv_buf, + len), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on reading manufacturer data (custom handler is not set) */ + zassert_equal(EC_ERROR_INVAL, + sb_read_mfgacc(cmd, SB_ALT_MANUFACTURER_ACCESS, recv_buf, + len), + NULL); + + /* Set arbitrary manufacturer data */ + for (int i = 1; i < len; i++) { + mf_data[i] = i; + } + /* Set first byte of message as length */ + mf_data[0] = len; + + /* Setup custom handler */ + mfacc_conf.reg = SB_ALT_MANUFACTURER_ACCESS; + mfacc_conf.len = len; + mfacc_conf.buf = mf_data; + i2c_common_emul_set_read_func(common_data, mfgacc_read_func, + &mfacc_conf); + + /* Test error when mf_data doesn't start with command */ + zassert_equal(EC_ERROR_UNKNOWN, + sb_read_mfgacc(cmd, SB_ALT_MANUFACTURER_ACCESS, recv_buf, + len), + NULL); + + /* Set beginning of the manufacturer data */ + mf_data[1] = cmd & 0xff; + mf_data[2] = (cmd >> 8) & 0xff; + + /* Test successful manufacturer data read */ + zassert_equal(EC_SUCCESS, + sb_read_mfgacc(cmd, SB_ALT_MANUFACTURER_ACCESS, recv_buf, + len), + NULL); + /* Compare received data ignoring length byte */ + zassert_mem_equal(mf_data + 1, recv_buf, len - 1, NULL); + + /* Disable custom read function */ + i2c_common_emul_set_read_func(common_data, NULL, NULL); +} + +/** Test battery fake charge level set and read */ +ZTEST_USER(smart_battery, test_battery_fake_charge) +{ + struct sbat_emul_bat_data *bat; + struct batt_params batt; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct i2c_common_emul_data *common_data = + emul_smart_battery_get_i2c_common_data(emul); + int remaining_cap; + int fake_charge; + int charge; + int flags; + + bat = sbat_emul_get_bat_data(emul); + + /* Success on command with no argument */ + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "battfake"), + NULL); + + /* Fail on command with argument which is not a number */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "battfake test"), NULL); + + /* Fail on command with charge level above 100% */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "battfake 123"), NULL); + + /* Fail on command with charge level below 0% */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "battfake -23"), NULL); + + /* Set fake charge level */ + fake_charge = 65; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "battfake 65"), NULL); + + /* Test that fake charge level is applied */ + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + zassert_equal(fake_charge, batt.state_of_charge, "%d%% != %d%%", + fake_charge, batt.state_of_charge); + remaining_cap = bat->full_cap * fake_charge / 100; + zassert_equal(remaining_cap, batt.remaining_capacity, "%d != %d", + remaining_cap, batt.remaining_capacity); + + /* Test fake remaining capacity when full capacity is not available */ + i2c_common_emul_set_read_fail_reg(common_data, SB_FULL_CHARGE_CAPACITY); + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE | + BATT_FLAG_BAD_FULL_CAPACITY; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + zassert_equal(fake_charge, batt.state_of_charge, "%d%% != %d%%", + fake_charge, batt.state_of_charge); + remaining_cap = bat->design_cap * fake_charge / 100; + zassert_equal(remaining_cap, batt.remaining_capacity, "%d != %d", + remaining_cap, batt.remaining_capacity); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Disable fake charge level */ + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "battfake -1"), NULL); + + /* Test that fake charge level is not applied */ + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + charge = 100 * bat->cap / bat->full_cap; + zassert_equal(charge, batt.state_of_charge, "%d%% != %d%%", charge, + batt.state_of_charge); + zassert_equal(bat->cap, batt.remaining_capacity, "%d != %d", bat->cap, + batt.remaining_capacity); +} + +/** Test battery fake temperature set and read */ +ZTEST_USER(smart_battery, test_battery_fake_temperature) +{ + struct sbat_emul_bat_data *bat; + struct batt_params batt; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + int fake_temp; + int flags; + + bat = sbat_emul_get_bat_data(emul); + + /* Success on command with no argument */ + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "batttempfake"), NULL); + + /* Fail on command with argument which is not a number */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "batttempfake test"), + NULL); + + /* Fail on command with too high temperature (above 500.0 K) */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "batttempfake 5001"), + NULL); + + /* Fail on command with too low temperature (below 0 K) */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "batttempfake -23"), + NULL); + + /* Set fake temperature */ + fake_temp = 2840; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "batttempfake 2840"), + NULL); + + /* Test that fake temperature is applied */ + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + zassert_equal(fake_temp, batt.temperature, "%d != %d", fake_temp, + batt.temperature); + + /* Disable fake temperature */ + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "batttempfake -1"), + NULL); + + /* Test that fake temperature is not applied */ + flags = BATT_FLAG_WANT_CHARGE | BATT_FLAG_RESPONSIVE; + battery_get_params(&batt); + zassert_equal(flags, batt.flags, "0x%x != 0x%x", flags, batt.flags); + zassert_equal(bat->temp, batt.temperature, "%d != %d", bat->temp, + batt.temperature); +} + +ZTEST_SUITE(smart_battery, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/stm_mems_common.c b/zephyr/test/drivers/default/src/stm_mems_common.c new file mode 100644 index 0000000000..f7c59105b0 --- /dev/null +++ b/zephyr/test/drivers/default/src/stm_mems_common.c @@ -0,0 +1,338 @@ +/* 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/ztest.h> +#include <zephyr/device.h> +#include <zephyr/devicetree.h> +#include <errno.h> + +#include "common.h" +#include "driver/stm_mems_common.h" +#include "emul/emul_common_i2c.h" +#include "emul/i2c_mock.h" +#include "i2c/i2c.h" +#include "test/drivers/test_state.h" + +#define MOCK_EMUL EMUL_DT_GET(DT_NODELABEL(i2c_mock)) +#define COMMON_DATA emul_i2c_mock_get_i2c_common_data(MOCK_EMUL) + +struct mock_properties { + /* Incremented by the mock function every time it is called */ + int call_count; +}; + +static int mock_read_fn(const struct emul *emul, int reg, uint8_t *val, + int bytes, void *data) +{ + ztest_check_expected_value(reg); + ztest_check_expected_value(bytes); + if (val != NULL) { + /* Allow passing a mocked read byte through the output param */ + ztest_copy_return_data(val, sizeof(*val)); + } + return ztest_get_return_value(); +} + +static int mock_write_fn(const struct emul *emul, int reg, uint8_t val, + int bytes, void *data) +{ + struct mock_properties *props = (struct mock_properties *)data; + + if (props) + props->call_count++; + + ztest_check_expected_value(reg); + ztest_check_expected_value(val); + ztest_check_expected_value(bytes); + return ztest_get_return_value(); +} + +ZTEST(stm_mems_common, test_st_raw_read_n) +{ + const struct emul *emul = MOCK_EMUL; + + int rv; + + i2c_common_emul_set_read_func(COMMON_DATA, mock_read_fn, NULL); + /* + * Ensure the MSb (auto-increment bit) in the register address gets + * set, but also return an error condition + */ + ztest_expect_value(mock_read_fn, reg, 0x80); + ztest_expect_value(mock_read_fn, bytes, 0); + ztest_returns_value(mock_read_fn, -EIO); + + rv = st_raw_read_n(I2C_PORT_POWER, i2c_mock_get_addr(emul), 0, NULL, 2); + /* The shim layer translates -EIO to EC_ERROR_INVAL. */ + zassert_equal(rv, EC_ERROR_INVAL, "rv was %d but expected %d", rv, + EC_ERROR_INVAL); +} + +ZTEST(stm_mems_common, test_st_raw_read_n_noinc) +{ + const struct emul *emul = MOCK_EMUL; + + int rv; + + i2c_common_emul_set_read_func(COMMON_DATA, mock_read_fn, NULL); + /* + * Unlike `st_raw_read_n`, the MSb (auto-increment bit) in the register + * address should NOT be automatically set. Also return an error. + */ + ztest_expect_value(mock_read_fn, reg, 0x00); + ztest_expect_value(mock_read_fn, bytes, 0); + ztest_returns_value(mock_read_fn, -EIO); + + rv = st_raw_read_n_noinc(I2C_PORT_POWER, i2c_mock_get_addr(emul), 0, + NULL, 2); + /* The shim layer translates -EIO to EC_ERROR_INVAL. */ + zassert_equal(rv, EC_ERROR_INVAL, "rv was %d but expected %d", rv, + EC_ERROR_INVAL); +} + +ZTEST(stm_mems_common, test_st_write_data_with_mask) +{ + const struct emul *emul = MOCK_EMUL; + + int rv; + + const struct motion_sensor_t sensor = { + .port = I2C_PORT_POWER, + .i2c_spi_addr_flags = i2c_mock_get_addr(emul), + }; + + /* Arbitrary named test parameters */ + uint8_t test_addr = 0xAA; + uint8_t initial_value = 0x55; + uint8_t test_mask = 0xF0; + uint8_t test_data = 0xFF; + uint8_t expected_new_value = (initial_value & ~test_mask) | + (test_data & test_mask); + + /* Part 1: error occurs when reading initial value from sensor */ + i2c_common_emul_set_read_func(COMMON_DATA, mock_read_fn, NULL); + ztest_expect_value(mock_read_fn, reg, test_addr); + ztest_expect_value(mock_read_fn, bytes, 0); + /* Value is immaterial but ztest has no way to explicitly ignore it */ + ztest_return_data(mock_read_fn, val, &initial_value); + ztest_returns_value(mock_read_fn, -EIO); + + rv = st_write_data_with_mask(&sensor, test_addr, test_mask, test_data); + /* The shim layer translates -EIO to EC_ERROR_INVAL. */ + zassert_equal(rv, EC_ERROR_INVAL, "rv was %d but expected %d", rv, + EC_ERROR_INVAL); + + /* + * Part 2: initial read succeeds, but the initial value already + * matches the new value, so no write happens. + */ + ztest_expect_value(mock_read_fn, reg, test_addr); + ztest_expect_value(mock_read_fn, bytes, 0); + ztest_return_data(mock_read_fn, val, &expected_new_value); + ztest_returns_value(mock_read_fn, 0); + + struct mock_properties write_fn_props = { + .call_count = 0, + }; + + i2c_common_emul_set_write_func(COMMON_DATA, mock_write_fn, + &write_fn_props); + + rv = st_write_data_with_mask(&sensor, test_addr, test_mask, test_data); + zassert_equal(rv, EC_SUCCESS, "rv was %d but expected %d", rv, + EC_SUCCESS); + zassert_equal(write_fn_props.call_count, 0, + "mock_write_fn was called."); + + /* + * Part 3: this time a write is required, but it fails. This also + * tests the masking logic. + */ + ztest_expect_value(mock_read_fn, reg, test_addr); + ztest_expect_value(mock_read_fn, bytes, 0); + ztest_return_data(mock_read_fn, val, &initial_value); + ztest_returns_value(mock_read_fn, 0); + + write_fn_props.call_count = 0; /* Reset call count */ + ztest_expect_value(mock_write_fn, reg, test_addr); + ztest_expect_value(mock_write_fn, bytes, 1); + ztest_expect_value(mock_write_fn, val, expected_new_value); + ztest_returns_value(mock_write_fn, -EIO); + + rv = st_write_data_with_mask(&sensor, test_addr, test_mask, test_data); + /* The shim layer translates -EIO to EC_ERROR_INVAL. */ + zassert_equal(rv, EC_ERROR_INVAL, "rv was %d but expected %d", rv, + EC_ERROR_INVAL); + zassert_equal(write_fn_props.call_count, 1, + "mock_write_fn was not called."); +} + +ZTEST(stm_mems_common, test_st_get_resolution) +{ + int expected_resolution = 123; + int rv; + + struct stprivate_data driver_data = { + .resol = expected_resolution, + }; + + const struct motion_sensor_t sensor = { + .drv_data = &driver_data, + }; + + rv = st_get_resolution(&sensor); + zassert_equal(rv, expected_resolution, "rv is %d but expected %d", rv, + expected_resolution); +} + +ZTEST(stm_mems_common, test_st_set_offset) +{ + int16_t expected_offset[3] = { 123, 456, 789 }; + + struct stprivate_data driver_data; + const struct motion_sensor_t sensor = { + .drv_data = &driver_data, + }; + + int rv = st_set_offset(&sensor, expected_offset, 0); + + zassert_equal(rv, EC_SUCCESS, "rv as %d but expected %d", rv, + EC_SUCCESS); + zassert_equal(driver_data.offset[X], expected_offset[X], + "X offset is %d but expected %d", driver_data.offset[X], + expected_offset[X]); + zassert_equal(driver_data.offset[Y], expected_offset[Y], + "Y offset is %d but expected %d", driver_data.offset[Y], + expected_offset[Y]); + zassert_equal(driver_data.offset[Z], expected_offset[Z], + "Z offset is %d but expected %d", driver_data.offset[Z], + expected_offset[Z]); +} + +ZTEST(stm_mems_common, test_st_get_offset) +{ + struct stprivate_data driver_data = { + .offset = { [X] = 123, [Y] = 456, [Z] = 789 }, + }; + const struct motion_sensor_t sensor = { + .drv_data = &driver_data, + }; + + int16_t temp_out = 0; + int16_t actual_offset[3] = { 0, 0, 0 }; + + int rv = st_get_offset(&sensor, actual_offset, &temp_out); + + zassert_equal(rv, EC_SUCCESS, "rv as %d but expected %d", rv, + EC_SUCCESS); + zassert_equal( + temp_out, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, + "temp is %d but should be %d (EC_MOTION_SENSE_INVALID_CALIB_TEMP)", + temp_out, (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP); + + zassert_equal(actual_offset[X], driver_data.offset[X], + "X offset is %d but expected %d", actual_offset[X], + driver_data.offset[X]); + zassert_equal(actual_offset[Y], driver_data.offset[Y], + "Y offset is %d but expected %d", actual_offset[Y], + driver_data.offset[Y]); + zassert_equal(actual_offset[Z], driver_data.offset[Z], + "Z offset is %d but expected %d", actual_offset[Z], + driver_data.offset[Z]); +} + +ZTEST(stm_mems_common, test_st_get_data_rate) +{ + int expected_data_rate = 456; + int rv; + + struct stprivate_data driver_data = { + .base = { + .odr = expected_data_rate, + }, + }; + + const struct motion_sensor_t sensor = { + .drv_data = &driver_data, + }; + + rv = st_get_data_rate(&sensor); + zassert_equal(rv, expected_data_rate, "rv is %d but expected %d", rv, + expected_data_rate); +} + +ZTEST(stm_mems_common, test_st_normalize) +{ + struct stprivate_data driver_data = { + .resol = 12, /* 12 bits of useful data (arbitrary) */ + .offset = { /* Arbitrary offsets */ + [X] = -100, + [Y] = 200, + [Z] = 100, + }, + }; + /* Fixed-point identity matrix that performs no rotation. */ + const mat33_fp_t identity_rot_matrix = { + { INT_TO_FP(1), INT_TO_FP(0), INT_TO_FP(0) }, + { INT_TO_FP(0), INT_TO_FP(1), INT_TO_FP(0) }, + { INT_TO_FP(0), INT_TO_FP(0), INT_TO_FP(1) }, + }; + const struct motion_sensor_t sensor = { + .drv_data = &driver_data, + .rot_standard_ref = &identity_rot_matrix, + .current_range = 32, /* used to scale offsets (arbitrary) */ + }; + + /* Accelerometer data is passed in with the format: + * (lower address) (higher address) + * [X LSB] [X MSB] [Y LSB] [Y MSB] [Z LSB] [Z MSB] + * + * The LSB are left-aligned and contain noise/junk data + * in their least-significant bit positions. When interpreted + * as int16 samples, the `driver_data.resol`-count most + * significant bits are what we actually use. For this test, we + * set `resol` to 12, so there will be 12 useful bits and 4 noise + * bits. This test (and the EC code) assume we are compiling on + * a little-endian machine. The samples themselvesare unsigned and + * biased at 2^12/2 = 2^11. + */ + uint16_t input_reading[] = { + ((BIT(11) - 100) << 4) | 0x000a, + ((BIT(11) + 0) << 4) | 0x000b, + ((BIT(11) + 100) << 4) | 0x000c, + }; + + /* Expected outputs w/ noise bits suppressed and offsets applied. + * Note that the data stays left-aligned. + */ + intv3_t expected_output = { + ((BIT(11) - 100) << 4) + driver_data.offset[X], + ((BIT(11) + 0) << 4) + driver_data.offset[Y], + ((BIT(11) + 100) << 4) + driver_data.offset[Z], + }; + + intv3_t actual_output = { 0 }; + + st_normalize(&sensor, (int *)&actual_output, (uint8_t *)input_reading); + + zassert_within(actual_output[X], expected_output[X], 0.5f, + "X output is %d but expected %d", actual_output[X], + expected_output[X]); + zassert_within(actual_output[Y], expected_output[Y], 0.5f, + "Y output is %d but expected %d", actual_output[Y], + expected_output[Y]); + zassert_within(actual_output[Z], expected_output[Z], 0.5f, + "Z output is %d but expected %d", actual_output[Z], + expected_output[Z]); +} + +static void stm_mems_common_before(void *state) +{ + ARG_UNUSED(state); + i2c_mock_reset(MOCK_EMUL); +} + +ZTEST_SUITE(stm_mems_common, drivers_predicate_post_main, NULL, + stm_mems_common_before, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/system.c b/zephyr/test/drivers/default/src/system.c new file mode 100644 index 0000000000..01956d8721 --- /dev/null +++ b/zephyr/test/drivers/default/src/system.c @@ -0,0 +1,113 @@ +/* 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 "ec_commands.h" +#include "host_command.h" +#include "system.h" +#include "test/drivers/test_state.h" + +/* System Host Commands */ + +ZTEST_USER(system, test_hostcmd_sysinfo) +{ + struct ec_response_sysinfo response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_SYSINFO, 0, response); + + /* Simply issue the command and get the results */ + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(response.reset_flags, 0, "response.reset_flags = %d", + response.reset_flags); + zassert_equal(response.current_image, EC_IMAGE_RO, + "response.current_image = %d", response.current_image); + zassert_equal(response.flags, 0, "response.flags = %d", response.flags); +} + +ZTEST_USER(system, test_hostcmd_board_version) +{ + struct ec_response_board_version response; + struct host_cmd_handler_args args = BUILD_HOST_COMMAND_RESPONSE( + EC_CMD_GET_BOARD_VERSION, 0, response); + + /* Get the board version, which is default 0. */ + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(response.board_version, 0, "response.board_version = %d", + response.board_version); +} + +/* System Function Testing */ + +static void system_flags_before_after(void *data) +{ + ARG_UNUSED(data); + system_clear_reset_flags(-1); +} + +ZTEST(system_save_flags, test_system_encode_save_flags) +{ + int flags_to_save = 0; + uint32_t saved_flags = 0; + int arbitrary_reset_flags = 1; + + /* Save all possible flags */ + flags_to_save = -1; + + /* Clear all reset flags and set them arbitrarily */ + system_set_reset_flags(arbitrary_reset_flags); + + system_encode_save_flags(flags_to_save, &saved_flags); + + /* Verify all non-mutually exclusive flags */ + zassert_equal(1, saved_flags & system_get_reset_flags(), NULL); + zassert_not_equal(0, saved_flags & EC_RESET_FLAG_AP_OFF, NULL); + zassert_not_equal(0, saved_flags & EC_RESET_FLAG_STAY_IN_RO, NULL); + zassert_not_equal(0, saved_flags & EC_RESET_FLAG_AP_WATCHDOG, NULL); +} + +ZTEST(system_save_flags, + test_system_encode_save_flags_mutually_exclusive_reset_flags) +{ + int flags_to_save = 0; + uint32_t saved_flags = 0; + + /* Verify reset hard takes precedence over hibernate/soft */ + flags_to_save = SYSTEM_RESET_HARD | SYSTEM_RESET_HIBERNATE; + + system_encode_save_flags(flags_to_save, &saved_flags); + + zassert_not_equal(0, saved_flags & EC_RESET_FLAG_HARD, NULL); + zassert_equal(0, saved_flags & EC_RESET_FLAG_HIBERNATE, NULL); + zassert_equal(0, saved_flags & EC_RESET_FLAG_SOFT, NULL); + + /* Verify reset hibernate takes precedence over soft */ + flags_to_save = SYSTEM_RESET_HIBERNATE; + + system_encode_save_flags(flags_to_save, &saved_flags); + + zassert_equal(0, saved_flags & EC_RESET_FLAG_HARD, NULL); + zassert_not_equal(0, saved_flags & EC_RESET_FLAG_HIBERNATE, NULL); + zassert_equal(0, saved_flags & EC_RESET_FLAG_SOFT, NULL); + + /* Verify reset soft is always saved given no other flags */ + flags_to_save = 0; + + system_encode_save_flags(flags_to_save, &saved_flags); + + zassert_equal(0, saved_flags & EC_RESET_FLAG_HARD, NULL); + zassert_equal(0, saved_flags & EC_RESET_FLAG_HIBERNATE, NULL); + zassert_not_equal(0, saved_flags & EC_RESET_FLAG_SOFT, NULL); +} + +ZTEST_SUITE(system, drivers_predicate_post_main, NULL, NULL, NULL, NULL); + +ZTEST_SUITE(system_save_flags, drivers_predicate_post_main, NULL, + system_flags_before_after, system_flags_before_after, NULL); diff --git a/zephyr/test/drivers/default/src/tablet_mode.c b/zephyr/test/drivers/default/src/tablet_mode.c new file mode 100644 index 0000000000..d600d26072 --- /dev/null +++ b/zephyr/test/drivers/default/src/tablet_mode.c @@ -0,0 +1,168 @@ +/* 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/fff.h> +#include <zephyr/shell/shell.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "tablet_mode.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +static void tabletmode_before(void *state) +{ + ARG_UNUSED(state); + tablet_reset(); +} + +static void tabletmode_after(void *state) +{ + ARG_UNUSED(state); + tablet_reset(); +} + +/** + * @brief TestPurpose: various tablet_set_mode operations, make sure lid and + * base works independently. + */ +ZTEST_USER(tabletmode, test_tablet_set_mode) +{ + int ret; + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet initial mode: %d", ret); + + tablet_set_mode(1, TABLET_TRIGGER_LID); + + ret = tablet_get_mode(); + zassert_equal(ret, 1, "unexepcted tablet mode: %d", ret); + + tablet_set_mode(1, TABLET_TRIGGER_BASE); + + ret = tablet_get_mode(); + zassert_equal(ret, 1, "unexepcted tablet mode: %d", ret); + + tablet_set_mode(0, TABLET_TRIGGER_LID); + + ret = tablet_get_mode(); + zassert_equal(ret, 1, "unexepcted tablet mode: %d", ret); + + tablet_set_mode(0, TABLET_TRIGGER_BASE); + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet mode: %d", ret); +} + +/** + * @brief TestPurpose: test the tablet_disable functionality. + */ +ZTEST_USER(tabletmode, test_tablet_disable) +{ + int ret; + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet initial mode: %d", ret); + + tablet_disable(); + tablet_set_mode(1, TABLET_TRIGGER_LID); + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet mode: %d", ret); +} + +/** + * @brief TestPurpose: check that tabletmode on and off changes the mode. + */ +ZTEST_USER(tabletmode, test_settabletmode_on_off) +{ + int ret; + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet initial mode: %d", ret); + + ret = shell_execute_cmd(get_ec_shell(), "tabletmode"); + zassert_equal(ret, EC_SUCCESS, "unexepcted command return status: %d", + ret); + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet mode: %d", ret); + + ret = shell_execute_cmd(get_ec_shell(), "tabletmode on"); + zassert_equal(ret, EC_SUCCESS, "unexepcted command return status: %d", + ret); + + ret = tablet_get_mode(); + zassert_equal(ret, 1, "unexepcted tablet mode: %d", ret); + + ret = shell_execute_cmd(get_ec_shell(), "tabletmode off"); + zassert_equal(ret, EC_SUCCESS, "unexepcted command return status: %d", + ret); + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet mode: %d", ret); +} + +/** + * @brief TestPurpose: ensure that console tabletmode forces the status, + * inhibiting tablet_set_mode, and then unforce it with reset. + */ +ZTEST_USER(tabletmode, test_settabletmode_forced) +{ + int ret; + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet initial mode: %d", ret); + + ret = shell_execute_cmd(get_ec_shell(), "tabletmode on"); + zassert_equal(ret, EC_SUCCESS, "unexepcted command return status: %d", + ret); + + ret = tablet_get_mode(); + zassert_equal(ret, 1, "unexepcted tablet mode: %d", ret); + + tablet_set_mode(0, TABLET_TRIGGER_LID); + + ret = tablet_get_mode(); + zassert_equal(ret, 1, "unexepcted tablet mode: %d", ret); + + ret = shell_execute_cmd(get_ec_shell(), "tabletmode reset"); + zassert_equal(ret, EC_SUCCESS, "unexepcted command return status: %d", + ret); + + tablet_set_mode(0, TABLET_TRIGGER_LID); + + ret = tablet_get_mode(); + zassert_equal(ret, 0, "unexepcted tablet mode: %d", ret); +} + +/** + * @brief TestPurpose: check the "too many arguments" case. + */ +ZTEST_USER(tabletmode, test_settabletmode_too_many_args) +{ + int ret; + + ret = shell_execute_cmd(get_ec_shell(), + "tabletmode too many arguments"); + zassert_equal(ret, EC_ERROR_PARAM_COUNT, + "unexepcted command return status: %d", ret); +} + +/** + * @brief TestPurpose: check the "unknown argument" case. + */ +ZTEST_USER(tabletmode, test_settabletmode_unknown_arg) +{ + int ret; + + ret = shell_execute_cmd(get_ec_shell(), "tabletmode X"); + zassert_equal(ret, EC_ERROR_PARAM1, + "unexepcted command return status: %d", ret); +} + +ZTEST_SUITE(tabletmode, drivers_predicate_post_main, NULL, tabletmode_before, + tabletmode_after, NULL); diff --git a/zephyr/test/drivers/default/src/tcpci.c b/zephyr/test/drivers/default/src/tcpci.c new file mode 100644 index 0000000000..e549e5056a --- /dev/null +++ b/zephyr/test/drivers/default/src/tcpci.c @@ -0,0 +1,524 @@ +/* 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.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include "common.h" +#include "ec_tasks.h" +#include "emul/emul_common_i2c.h" +#include "emul/tcpc/emul_tcpci.h" +#include "hooks.h" +#include "i2c.h" +#include "test/drivers/stubs.h" +#include "test/drivers/tcpci_test_common.h" + +#include "tcpm/tcpci.h" +#include "test/drivers/test_state.h" + +#define TCPCI_EMUL_NODE DT_NODELABEL(tcpci_emul) + +/** Test TCPCI init and vbus level */ +ZTEST(tcpci, test_generic_tcpci_init) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_init(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI release */ +ZTEST(tcpci, test_generic_tcpci_release) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_release(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI get cc */ +ZTEST(tcpci, test_generic_tcpci_get_cc) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_get_cc(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI set cc */ +ZTEST(tcpci, test_generic_tcpci_set_cc) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_set_cc(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI set polarity */ +ZTEST(tcpci, test_generic_tcpci_set_polarity) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_set_polarity(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI set vconn */ +ZTEST(tcpci, test_generic_tcpci_set_vconn) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_set_vconn(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI set msg header */ +ZTEST(tcpci, test_generic_tcpci_set_msg_header) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_set_msg_header(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI rx and sop prime enable */ +ZTEST(tcpci, test_generic_tcpci_set_rx_detect) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_set_rx_detect(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI get raw message from TCPC revision 2.0 */ +ZTEST(tcpci, test_generic_tcpci_get_rx_message_raw_rev2) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + /* Revision 2.0 is set by default in test_rules */ + test_tcpci_get_rx_message_raw(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI get raw message from TCPC revision 1.0 */ +ZTEST(tcpci, test_generic_tcpci_get_rx_message_raw_rev1) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + tcpc_config[USBC_PORT_C0].flags = 0; + tcpci_emul_set_rev(emul, TCPCI_EMUL_REV1_0_VER1_0); + + test_tcpci_get_rx_message_raw(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI transmitting message from TCPC revision 2.0 */ +ZTEST(tcpci, test_generic_tcpci_transmit_rev2) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + /* Revision 2.0 is set by default in test_rules */ + test_tcpci_transmit(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI transmitting message from TCPC revision 1.0 */ +ZTEST(tcpci, test_generic_tcpci_transmit_rev1) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + tcpc_config[USBC_PORT_C0].flags = 0; + tcpci_emul_set_rev(emul, TCPCI_EMUL_REV1_0_VER1_0); + + test_tcpci_transmit(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI alert */ +ZTEST(tcpci, test_generic_tcpci_alert) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_alert(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI alert RX message */ +ZTEST(tcpci, test_generic_tcpci_alert_rx_message) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_alert_rx_message(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI auto discharge on disconnect */ +ZTEST(tcpci, test_generic_tcpci_auto_discharge) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_auto_discharge(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI drp toggle */ +ZTEST(tcpci, test_generic_tcpci_drp_toggle) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_drp_toggle(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI get chip info */ +ZTEST(tcpci, test_generic_tcpci_get_chip_info) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_get_chip_info(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI enter low power mode */ +ZTEST(tcpci, test_generic_tcpci_low_power_mode) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_low_power_mode(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI set bist test mode */ +ZTEST(tcpci, test_generic_tcpci_set_bist_mode) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_set_bist_mode(emul, common_data, USBC_PORT_C0); +} + +/** Test TCPCI discharge vbus */ +ZTEST(tcpci, test_generic_tcpci_discharge_vbus) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + uint8_t exp_ctrl, initial_ctrl; + + /* Set initial value for POWER ctrl register. Chosen arbitrary. */ + initial_ctrl = TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS | + TCPC_REG_POWER_CTRL_VOLT_ALARM_DIS; + tcpci_emul_set_reg(emul, TCPC_REG_POWER_CTRL, initial_ctrl); + + /* Test discharge enable */ + exp_ctrl = initial_ctrl | TCPC_REG_POWER_CTRL_FORCE_DISCHARGE; + tcpci_tcpc_discharge_vbus(USBC_PORT_C0, 1); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); + + /* Test discharge disable */ + exp_ctrl = initial_ctrl & ~TCPC_REG_POWER_CTRL_FORCE_DISCHARGE; + tcpci_tcpc_discharge_vbus(USBC_PORT_C0, 0); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); +} + +/** Test TCPC xfer */ +ZTEST(tcpci, test_tcpc_xfer) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + uint16_t val, exp_val; + uint8_t reg; + + /* Set value to register (value and register chosen arbitrary) */ + exp_val = 0x7fff; + reg = TCPC_REG_ALERT_MASK; + tcpci_emul_set_reg(emul, reg, exp_val); + + /* Test reading value using tcpc_xfer() function */ + zassert_equal(EC_SUCCESS, + tcpc_xfer(USBC_PORT_C0, ®, 1, (uint8_t *)&val, 2), + NULL); + zassert_equal(exp_val, val, "0x%x != 0x%x", exp_val, val); +} + +/** Test TCPCI debug accessory enable/disable */ +ZTEST(tcpci, test_generic_tcpci_debug_accessory) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + uint8_t exp_val, initial_val; + + /* Set initial value for STD output register. Chosen arbitrary. */ + initial_val = TCPC_REG_CONFIG_STD_OUTPUT_AUDIO_CONN_N | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB | + TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED | + TCPC_REG_CONFIG_STD_OUTPUT_DBG_ACC_CONN_N; + tcpci_emul_set_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, initial_val); + + /* Test debug accessory connect */ + exp_val = initial_val & ~TCPC_REG_CONFIG_STD_OUTPUT_DBG_ACC_CONN_N; + tcpci_tcpc_debug_accessory(USBC_PORT_C0, 1); + check_tcpci_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, exp_val); + + /* Test debug accessory disconnect */ + exp_val = initial_val | TCPC_REG_CONFIG_STD_OUTPUT_DBG_ACC_CONN_N; + tcpci_tcpc_debug_accessory(USBC_PORT_C0, 0); + check_tcpci_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, exp_val); +} + +/* Setup TCPCI usb mux to behave as it is used only for usb mux */ +static void set_usb_mux_not_tcpc(void) +{ + usbc0_mux0.flags = USB_MUX_FLAG_NOT_TCPC; +} + +/* Setup TCPCI usb mux to behave as it is used for usb mux and TCPC */ +static void set_usb_mux_tcpc(void) +{ + usbc0_mux0.flags = 0; +} + +/** Test TCPCI mux init */ +ZTEST(tcpci, test_generic_tcpci_mux_init) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + const struct usb_mux *tcpci_usb_mux = usb_muxes[USBC_PORT_C0].mux; + + /* Set as usb mux with TCPC for first init call */ + set_usb_mux_tcpc(); + + /* Make sure that TCPC is not accessed */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_FAIL_ALL_REG); + zassert_equal(EC_SUCCESS, tcpci_tcpm_mux_init(tcpci_usb_mux), NULL); + + /* Set as only usb mux without TCPC for rest of the test */ + set_usb_mux_not_tcpc(); + + /* Test fail on power status read */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_POWER_STATUS); + zassert_equal(EC_ERROR_INVAL, tcpci_tcpm_mux_init(tcpci_usb_mux), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on uninitialised bit set */ + tcpci_emul_set_reg(emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_UNINIT); + zassert_equal(EC_ERROR_TIMEOUT, tcpci_tcpm_mux_init(tcpci_usb_mux), + NULL); + + /* Set default power status for rest of the test */ + tcpci_emul_set_reg(emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_VBUS_DET); + + /* Test fail on alert mask write fail */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_ALERT_MASK); + zassert_equal(EC_ERROR_UNKNOWN, tcpci_tcpm_mux_init(tcpci_usb_mux), + NULL); + + /* Test fail on alert write fail */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_ALERT); + zassert_equal(EC_ERROR_UNKNOWN, tcpci_tcpm_mux_init(tcpci_usb_mux), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set arbitrary value to alert and alert mask registers */ + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, 0xffff); + tcpci_emul_set_reg(emul, TCPC_REG_ALERT_MASK, 0xffff); + + /* Test success init */ + zassert_equal(EC_SUCCESS, tcpci_tcpm_mux_init(tcpci_usb_mux), NULL); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, 0); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0); +} + +/** Test TCPCI mux enter low power mode */ +ZTEST(tcpci, test_generic_tcpci_mux_enter_low_power) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + const struct usb_mux *tcpci_usb_mux = usb_muxes[USBC_PORT_C0].mux; + + /* Set as usb mux with TCPC for first enter_low_power call */ + set_usb_mux_tcpc(); + + /* Make sure that TCPC is not accessed */ + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_FAIL_ALL_REG); + zassert_equal(EC_SUCCESS, tcpci_tcpm_mux_enter_low_power(tcpci_usb_mux), + NULL); + + /* Set as only usb mux without TCPC for rest of the test */ + set_usb_mux_not_tcpc(); + + /* Test error on failed command set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_COMMAND); + zassert_equal(EC_ERROR_INVAL, + tcpci_tcpm_mux_enter_low_power(tcpci_usb_mux), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test correct command is issued */ + zassert_equal(EC_SUCCESS, tcpci_tcpm_mux_enter_low_power(tcpci_usb_mux), + NULL); + check_tcpci_reg(emul, TCPC_REG_COMMAND, TCPC_REG_COMMAND_I2CIDLE); +} + +/** Test TCPCI mux set and get */ +static void test_generic_tcpci_mux_set_get(void) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + const struct usb_mux *tcpci_usb_mux = usb_muxes[USBC_PORT_C0].mux; + mux_state_t mux_state, mux_state_get; + uint16_t exp_val, initial_val; + bool ack; + + mux_state = USB_PD_MUX_NONE; + + /* Test fail on standard output config register read */ + i2c_common_emul_set_read_fail_reg(common_data, + TCPC_REG_CONFIG_STD_OUTPUT); + zassert_equal(EC_ERROR_INVAL, + tcpci_tcpm_mux_set(tcpci_usb_mux, mux_state, &ack), NULL); + zassert_equal(EC_ERROR_INVAL, + tcpci_tcpm_mux_get(tcpci_usb_mux, &mux_state_get), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on standard output config register write */ + i2c_common_emul_set_write_fail_reg(common_data, + TCPC_REG_CONFIG_STD_OUTPUT); + zassert_equal(EC_ERROR_INVAL, + tcpci_tcpm_mux_set(tcpci_usb_mux, mux_state, &ack), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set initial value for STD output register. Chosen arbitrary. */ + initial_val = TCPC_REG_CONFIG_STD_OUTPUT_AUDIO_CONN_N | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB | + TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED | + TCPC_REG_CONFIG_STD_OUTPUT_DBG_ACC_CONN_N; + tcpci_emul_set_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, initial_val); + + /* Test setting/getting no MUX connection without polarity inverted */ + exp_val = (initial_val & ~TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK) | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_NONE; + exp_val &= ~TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED; + mux_state = USB_PD_MUX_NONE; + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_set(tcpci_usb_mux, mux_state, &ack), NULL); + check_tcpci_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, exp_val); + zassert_false(ack, "Ack from host shouldn't be required"); + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_get(tcpci_usb_mux, &mux_state_get), NULL); + zassert_equal(mux_state, mux_state_get, "Expected state 0x%x, got 0x%x", + mux_state, mux_state_get); + + /* Test setting/getting MUX DP with polarity inverted */ + exp_val = (initial_val & ~TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK) | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP | + TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED; + mux_state = USB_PD_MUX_DP_ENABLED | USB_PD_MUX_POLARITY_INVERTED; + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_set(tcpci_usb_mux, mux_state, &ack), NULL); + check_tcpci_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, exp_val); + zassert_false(ack, "Ack from host shouldn't be required"); + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_get(tcpci_usb_mux, &mux_state_get), NULL); + zassert_equal(mux_state, mux_state_get, "Expected state 0x%x, got 0x%x", + mux_state, mux_state_get); + + /* Test setting/getting MUX USB without polarity inverted */ + exp_val = (initial_val & ~TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK) | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB; + exp_val &= ~TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED; + mux_state = USB_PD_MUX_USB_ENABLED; + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_set(tcpci_usb_mux, mux_state, &ack), NULL); + check_tcpci_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, exp_val); + zassert_false(ack, "Ack from host shouldn't be required"); + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_get(tcpci_usb_mux, &mux_state_get), NULL); + zassert_equal(mux_state, mux_state_get, "Expected state 0x%x, got 0x%x", + mux_state, mux_state_get); + + /* Test setting/getting MUX USB and DP with polarity inverted */ + exp_val = (initial_val & ~TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK) | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP | + TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB | + TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED; + mux_state = USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED | + USB_PD_MUX_POLARITY_INVERTED; + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_set(tcpci_usb_mux, mux_state, &ack), NULL); + check_tcpci_reg(emul, TCPC_REG_CONFIG_STD_OUTPUT, exp_val); + zassert_false(ack, "Ack from host shouldn't be required"); + zassert_equal(EC_SUCCESS, + tcpci_tcpm_mux_get(tcpci_usb_mux, &mux_state_get), NULL); + zassert_equal(mux_state, mux_state_get, "Expected state 0x%x, got 0x%x", + mux_state, mux_state_get); +} + +ZTEST(tcpci, test_generic_tcpci_mux_set_get) +{ + test_generic_tcpci_mux_set_get(); +} + +ZTEST(tcpci, test_generic_tcpci_mux_set_get__not_tcpc) +{ + set_usb_mux_not_tcpc(); + test_generic_tcpci_mux_set_get(); + set_usb_mux_tcpc(); +} + +ZTEST(tcpci, test_generic_tcpci_hard_reset_reinit) +{ + const struct emul *emul = EMUL_DT_GET(TCPCI_EMUL_NODE); + struct i2c_common_emul_data *common_data = + emul_tcpci_generic_get_i2c_common_data(emul); + + test_tcpci_hard_reset_reinit(emul, common_data, USBC_PORT_C0); +} + +static void *tcpci_setup(void) +{ + /* This test suite assumes that first usb mux for port C0 is TCPCI */ + __ASSERT(usb_muxes[USBC_PORT_C0].mux->driver == + &tcpci_tcpm_usb_mux_driver, + "Invalid config of usb_muxes in test/drivers/src/stubs.c"); + + return NULL; +} + +static void tcpci_after(void *state) +{ + set_usb_mux_tcpc(); +} + +ZTEST_SUITE(tcpci, drivers_predicate_pre_main, tcpci_setup, NULL, tcpci_after, + NULL); diff --git a/zephyr/test/drivers/default/src/tcpci_test_common.c b/zephyr/test/drivers/default/src/tcpci_test_common.c new file mode 100644 index 0000000000..f2c0c58bf9 --- /dev/null +++ b/zephyr/test/drivers/default/src/tcpci_test_common.c @@ -0,0 +1,1030 @@ +/* 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 "common.h" +#include "emul/emul_common_i2c.h" +#include "emul/tcpc/emul_tcpci.h" +#include "test/drivers/tcpci_test_common.h" + +#include "tcpm/tcpci.h" + +/** Check TCPC register value */ +void check_tcpci_reg_f(const struct emul *emul, int reg, uint16_t exp_val, + int line) +{ + uint16_t reg_val; + + zassert_ok(tcpci_emul_get_reg(emul, reg, ®_val), + "Failed tcpci_emul_get_reg(); line: %d", line); + zassert_equal(exp_val, reg_val, "Expected 0x%x, got 0x%x; line: %d", + exp_val, reg_val, line); +} + +/** Check TCPC register value with mask */ +void check_tcpci_reg_with_mask_f(const struct emul *emul, int reg, + uint16_t exp_val, uint16_t mask, int line) +{ + uint16_t reg_val; + + zassert_ok(tcpci_emul_get_reg(emul, reg, ®_val), + "Failed tcpci_emul_get_reg(); line: %d", line); + zassert_equal(exp_val & mask, reg_val & mask, + "Expected 0x%x, got 0x%x, mask 0x%x; line: %d", exp_val, + reg_val, mask, line); +} + +/** Test TCPCI init and vbus level */ +void test_tcpci_init(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint16_t exp_mask; + + tcpc_config[port].flags |= TCPC_FLAGS_TCPCI_REV2_0_NO_VSAFE0V; + + /* Test fail on power status read */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_POWER_STATUS); + zassert_equal(EC_ERROR_INVAL, drv->init(port), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test fail on uninitialised bit set */ + tcpci_emul_set_reg(emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_UNINIT); + zassert_equal(EC_ERROR_TIMEOUT, drv->init(port), NULL); + + /* + * Set expected alert mask. It is used in test until vSafe0V tcpc + * config flag is revmoved. + */ + exp_mask = TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED | + TCPC_REG_ALERT_TX_DISCARDED | TCPC_REG_ALERT_RX_STATUS | + TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS | + TCPC_REG_ALERT_FAULT | TCPC_REG_ALERT_POWER_STATUS; + + /* Set TCPCI emulator VBUS to safe0v (disconnected) */ + tcpci_emul_set_reg(emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_VBUS_DET); + + /* Test init with VBUS safe0v without vSafe0V tcpc config flag */ + zassert_equal(EC_SUCCESS, drv->init(port), NULL); + zassert_true(drv->check_vbus_level(port, VBUS_SAFE0V), NULL); + zassert_false(drv->check_vbus_level(port, VBUS_PRESENT), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, + TCPC_REG_POWER_STATUS_VBUS_PRES); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); + + /* Set TCPCI emulator VBUS to present (connected, above 4V) */ + tcpci_emul_set_reg(emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_VBUS_PRES | + TCPC_REG_POWER_STATUS_VBUS_DET); + + /* Test init with VBUS present without vSafe0V tcpc config flag */ + zassert_equal(EC_SUCCESS, drv->init(port), NULL); + zassert_false(drv->check_vbus_level(port, VBUS_SAFE0V), NULL); + zassert_true(drv->check_vbus_level(port, VBUS_PRESENT), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, + TCPC_REG_POWER_STATUS_VBUS_PRES); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); + + /* Disable vSafe0V tcpc config flag and update expected alert mask */ + exp_mask |= TCPC_REG_ALERT_EXT_STATUS; + tcpc_config[port].flags = TCPC_FLAGS_TCPCI_REV2_0; + + /* Test init with VBUS present with vSafe0V tcpc config flag */ + zassert_equal(EC_SUCCESS, drv->init(port), NULL); + zassert_false(drv->check_vbus_level(port, VBUS_SAFE0V), NULL); + zassert_true(drv->check_vbus_level(port, VBUS_PRESENT), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, + TCPC_REG_POWER_STATUS_VBUS_PRES); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); + + /* Set TCPCI emulator VBUS to safe0v (disconnected) */ + tcpci_emul_set_reg(emul, TCPC_REG_POWER_STATUS, + TCPC_REG_POWER_STATUS_VBUS_DET); + tcpci_emul_set_reg(emul, TCPC_REG_EXT_STATUS, + TCPC_REG_EXT_STATUS_SAFE0V); + + /* Test init with VBUS safe0v with vSafe0V tcpc config flag */ + zassert_equal(EC_SUCCESS, drv->init(port), NULL); + zassert_true(drv->check_vbus_level(port, VBUS_SAFE0V), NULL); + zassert_false(drv->check_vbus_level(port, VBUS_PRESENT), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, + TCPC_REG_POWER_STATUS_VBUS_PRES); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); + + /* + * Set TCPCI emulator VBUS to disconnected but not at vSafe0V + * (VBUS in 0.8V - 3.5V range) + */ + tcpci_emul_set_reg(emul, TCPC_REG_EXT_STATUS, 0); + + /* Test init with VBUS not safe0v with vSafe0V tcpc config flag */ + zassert_equal(EC_SUCCESS, drv->init(port), NULL); + zassert_false(drv->check_vbus_level(port, VBUS_SAFE0V), NULL); + zassert_false(drv->check_vbus_level(port, VBUS_PRESENT), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, + TCPC_REG_POWER_STATUS_VBUS_PRES); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); +} + +/** Test TCPCI release */ +void test_tcpci_release(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, 0xffff); + + zassert_equal(EC_SUCCESS, drv->release(port), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, 0); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, 0); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0); +} + +/** Test TCPCI get cc */ +void test_tcpci_get_cc(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + enum tcpc_cc_voltage_status cc1, cc2; + uint16_t cc_status, role_ctrl; + + struct { + /* TCPCI CC status register */ + enum tcpc_cc_voltage_status cc[2]; + bool connect_result; + /* TCPCI ROLE ctrl register */ + enum tcpc_cc_pull role_cc[2]; + enum tcpc_drp drp; + } test_param[] = { + /* Test DRP with open state */ + { + .cc = { TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_OPEN }, + .connect_result = false, + .drp = TYPEC_DRP, + }, + /* Test DRP with cc1 open state, cc2 src RA */ + { + .cc = { TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_RA }, + .connect_result = false, + .drp = TYPEC_DRP, + }, + /* Test DRP with cc1 src RA, cc2 src RD */ + { + .cc = { TYPEC_CC_VOLT_RA, TYPEC_CC_VOLT_RD }, + .connect_result = false, + .drp = TYPEC_DRP, + }, + /* Test DRP with cc1 snk open, cc2 snk default */ + { + .cc = { TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_RP_DEF }, + .connect_result = true, + .drp = TYPEC_DRP, + }, + /* Test DRP with cc1 snk 1.5, cc2 snk 3.0 */ + { + .cc = { TYPEC_CC_VOLT_RP_1_5, TYPEC_CC_VOLT_RP_3_0 }, + .connect_result = true, + .drp = TYPEC_DRP, + }, + /* Test no DRP with cc1 src open, cc2 src RA */ + { + .cc = { TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_RA }, + .connect_result = false, + .drp = TYPEC_NO_DRP, + .role_cc = { TYPEC_CC_RP, TYPEC_CC_RP }, + }, + /* Test no DRP with cc1 src RD, cc2 snk default */ + { + .cc = { TYPEC_CC_VOLT_RD, TYPEC_CC_VOLT_RP_DEF }, + .connect_result = false, + .drp = TYPEC_NO_DRP, + .role_cc = { TYPEC_CC_RP, TYPEC_CC_RD }, + }, + /* Test no DRP with cc1 snk default, cc2 snk open */ + { + .cc = { TYPEC_CC_VOLT_RP_DEF, TYPEC_CC_VOLT_OPEN }, + .connect_result = false, + .drp = TYPEC_NO_DRP, + .role_cc = { TYPEC_CC_RD, TYPEC_CC_RD }, + }, + /* Test no DRP with cc1 snk 3.0, cc2 snk 1.5 */ + { + .cc = { TYPEC_CC_VOLT_RP_3_0, TYPEC_CC_VOLT_RP_1_5 }, + .connect_result = false, + .drp = TYPEC_NO_DRP, + .role_cc = { TYPEC_CC_RD, TYPEC_CC_RD }, + }, + }; + + for (int i = 0; i < ARRAY_SIZE(test_param); i++) { + role_ctrl = TCPC_REG_ROLE_CTRL_SET(test_param[i].drp, 0, + test_param[i].role_cc[0], + test_param[i].role_cc[1]); + /* If CC status is TYPEC_CC_VOLT_RP_*, then BIT(2) is ignored */ + cc_status = TCPC_REG_CC_STATUS_SET(test_param[i].connect_result, + test_param[i].cc[0], + test_param[i].cc[1]); + tcpci_emul_set_reg(emul, TCPC_REG_ROLE_CTRL, role_ctrl); + tcpci_emul_set_reg(emul, TCPC_REG_CC_STATUS, cc_status); + zassert_equal( + EC_SUCCESS, drv->get_cc(port, &cc1, &cc2), + "Failed to get CC in test case %d (CC 0x%x, role 0x%x)", + i, cc_status, role_ctrl); + zassert_equal( + test_param[i].cc[0], cc1, + "0x%x != (cc1 = 0x%x) in test case %d (CC 0x%x, role 0x%x)", + test_param[i].cc[0], cc1, i, cc_status, role_ctrl); + zassert_equal( + test_param[i].cc[1], cc2, + "0x%x != (cc2 = 0x%x) in test case %d (CC 0x%x, role 0x%x)", + test_param[i].cc[0], cc1, i, cc_status, role_ctrl); + } +} + +/** Test TCPCI set cc */ +void test_tcpci_set_cc(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + enum tcpc_rp_value rp; + enum tcpc_cc_pull cc; + + /* Test setting default RP and cc open */ + rp = TYPEC_RP_USB; + cc = TYPEC_CC_OPEN; + zassert_equal(EC_SUCCESS, drv->select_rp_value(port, rp), NULL); + zassert_equal(EC_SUCCESS, drv->set_cc(port, cc), NULL); + check_tcpci_reg(emul, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, rp, cc, cc)); + + /* Test error on failed role ctrl set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_ROLE_CTRL); + zassert_equal(EC_ERROR_INVAL, drv->set_cc(port, TYPEC_CC_OPEN), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting 1.5 RP and cc RD */ + rp = TYPEC_RP_1A5; + cc = TYPEC_CC_RD; + zassert_equal(EC_SUCCESS, drv->select_rp_value(port, rp), NULL); + zassert_equal(EC_SUCCESS, drv->set_cc(port, cc), NULL); + check_tcpci_reg(emul, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, rp, cc, cc)); + + /* Test setting 3.0 RP and cc RP */ + rp = TYPEC_RP_3A0; + cc = TYPEC_CC_RP; + zassert_equal(EC_SUCCESS, drv->select_rp_value(port, rp), NULL); + zassert_equal(EC_SUCCESS, drv->set_cc(port, cc), NULL); + check_tcpci_reg(emul, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, rp, cc, cc)); + + /* + * Test setting 3.0 RP and cc RA. drv->select_rp_value() is + * intentionally not called to check if selected rp is persistent. + */ + cc = TYPEC_CC_RA; + zassert_equal(EC_SUCCESS, drv->set_cc(port, cc), NULL); + check_tcpci_reg(emul, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, rp, cc, cc)); +} + +/** Test TCPCI set polarity */ +void test_tcpci_set_polarity(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint8_t initial_ctrl; + uint8_t exp_ctrl; + + /* Set initial value for TCPC ctrl register. Chosen arbitrary. */ + initial_ctrl = TCPC_REG_TCPC_CTRL_DEBUG_ACC_CONTROL | + TCPC_REG_TCPC_CTRL_BIST_TEST_MODE; + tcpci_emul_set_reg(emul, TCPC_REG_TCPC_CTRL, initial_ctrl); + + /* Test error on failed polarity set */ + exp_ctrl = initial_ctrl; + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_TCPC_CTRL); + zassert_equal(EC_ERROR_INVAL, drv->set_polarity(port, POLARITY_CC2), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); + + /* Test setting polarity CC2 */ + exp_ctrl = initial_ctrl | TCPC_REG_TCPC_CTRL_SET(1); + zassert_equal(EC_SUCCESS, drv->set_polarity(port, POLARITY_CC2), NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); + + /* Test setting polarity CC1 */ + exp_ctrl = initial_ctrl & ~TCPC_REG_TCPC_CTRL_SET(1); + zassert_equal(EC_SUCCESS, drv->set_polarity(port, POLARITY_CC1), NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); + + /* Test setting polarity CC2 DTS */ + exp_ctrl = initial_ctrl | TCPC_REG_TCPC_CTRL_SET(1); + zassert_equal(EC_SUCCESS, drv->set_polarity(port, POLARITY_CC2_DTS), + NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); + + /* Test setting polarity CC1 DTS */ + exp_ctrl = initial_ctrl & ~TCPC_REG_TCPC_CTRL_SET(1); + zassert_equal(EC_SUCCESS, drv->set_polarity(port, POLARITY_CC1_DTS), + NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); +} + +/** Test TCPCI set vconn */ +void test_tcpci_set_vconn(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint8_t initial_ctrl; + uint8_t exp_ctrl; + + /* Set initial value for POWER ctrl register. Chosen arbitrary. */ + initial_ctrl = TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS | + TCPC_REG_POWER_CTRL_FORCE_DISCHARGE; + tcpci_emul_set_reg(emul, TCPC_REG_POWER_CTRL, initial_ctrl); + + /* Test error on failed vconn set */ + exp_ctrl = initial_ctrl; + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_POWER_CTRL); + zassert_equal(EC_ERROR_INVAL, drv->set_vconn(port, 1), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); + + /* Test vconn enable */ + exp_ctrl = initial_ctrl | TCPC_REG_POWER_CTRL_SET(1); + zassert_equal(EC_SUCCESS, drv->set_vconn(port, 1), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); + + /* Test vconn disable */ + exp_ctrl = initial_ctrl & ~TCPC_REG_POWER_CTRL_SET(1); + zassert_equal(EC_SUCCESS, drv->set_vconn(port, 0), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); +} + +/** Test TCPCI set msg header */ +void test_tcpci_set_msg_header(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + + /* Test error on failed header set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_MSG_HDR_INFO); + zassert_equal(EC_ERROR_INVAL, + drv->set_msg_header(port, PD_ROLE_SINK, PD_ROLE_UFP), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting sink UFP */ + zassert_equal(EC_SUCCESS, + drv->set_msg_header(port, PD_ROLE_SINK, PD_ROLE_UFP), + NULL); + check_tcpci_reg(emul, TCPC_REG_MSG_HDR_INFO, + TCPC_REG_MSG_HDR_INFO_SET(PD_ROLE_UFP, PD_ROLE_SINK)); + + /* Test setting sink DFP */ + zassert_equal(EC_SUCCESS, + drv->set_msg_header(port, PD_ROLE_SINK, PD_ROLE_DFP), + NULL); + check_tcpci_reg(emul, TCPC_REG_MSG_HDR_INFO, + TCPC_REG_MSG_HDR_INFO_SET(PD_ROLE_DFP, PD_ROLE_SINK)); + + /* Test setting source UFP */ + zassert_equal(EC_SUCCESS, + drv->set_msg_header(port, PD_ROLE_SOURCE, PD_ROLE_UFP), + NULL); + check_tcpci_reg(emul, TCPC_REG_MSG_HDR_INFO, + TCPC_REG_MSG_HDR_INFO_SET(PD_ROLE_UFP, PD_ROLE_SOURCE)); + + /* Test setting source DFP */ + zassert_equal(EC_SUCCESS, + drv->set_msg_header(port, PD_ROLE_SOURCE, PD_ROLE_DFP), + NULL); + check_tcpci_reg(emul, TCPC_REG_MSG_HDR_INFO, + TCPC_REG_MSG_HDR_INFO_SET(PD_ROLE_DFP, PD_ROLE_SOURCE)); +} + +/** Test TCPCI rx and sop prime enable */ +void test_tcpci_set_rx_detect(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + + /* Test error from rx_enable on rx detect set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_RX_DETECT); + zassert_equal(EC_ERROR_INVAL, drv->set_rx_enable(port, 1), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test rx disable */ + zassert_equal(EC_SUCCESS, drv->set_rx_enable(port, 0), NULL); + check_tcpci_reg(emul, TCPC_REG_RX_DETECT, 0x0); + + /* Test setting sop prime with rx disable doesn't change RX_DETECT */ + zassert_equal(EC_SUCCESS, drv->sop_prime_enable(port, 1), NULL); + check_tcpci_reg(emul, TCPC_REG_RX_DETECT, 0x0); + + /* Test that enabling rx after sop prime will set RX_DETECT properly */ + zassert_equal(EC_SUCCESS, drv->set_rx_enable(port, 1), NULL); + check_tcpci_reg(emul, TCPC_REG_RX_DETECT, + TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK); + + /* Test error from sop_prime on rx detect set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_RX_DETECT); + zassert_equal(EC_ERROR_INVAL, drv->sop_prime_enable(port, 0), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test disabling sop prime with rx enabled does change RX_DETECT */ + zassert_equal(EC_SUCCESS, drv->sop_prime_enable(port, 0), NULL); + check_tcpci_reg(emul, TCPC_REG_RX_DETECT, + TCPC_REG_RX_DETECT_SOP_HRST_MASK); + + /* Test that enabling rx after disabling sop prime set RX_DETECT */ + zassert_equal(EC_SUCCESS, drv->set_rx_enable(port, 0), NULL); + check_tcpci_reg(emul, TCPC_REG_RX_DETECT, 0x0); + zassert_equal(EC_SUCCESS, drv->set_rx_enable(port, 1), NULL); + check_tcpci_reg(emul, TCPC_REG_RX_DETECT, + TCPC_REG_RX_DETECT_SOP_HRST_MASK); +} + +/** Test TCPCI get raw message from TCPC */ +void test_tcpci_get_rx_message_raw(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + struct tcpci_emul_msg msg; + uint32_t payload[7]; + uint16_t rx_mask; + uint8_t buf[32]; + int exp_head; + int i, head; + int size; + + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, 0x0); + tcpci_emul_set_reg(emul, TCPC_REG_DEV_CAP_2, + TCPC_REG_DEV_CAP_2_LONG_MSG); + tcpci_emul_set_reg(emul, TCPC_REG_RX_DETECT, + TCPC_REG_RX_DETECT_SOP | TCPC_REG_RX_DETECT_SOPP); + + for (i = 0; i < 32; i++) { + buf[i] = i + 1; + } + msg.buf = buf; + msg.cnt = 31; + msg.sop_type = TCPCI_MSG_SOP; + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg, true), + "Failed to setup emulator message"); + + /* Test fail on reading byte count */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_RX_BUFFER); + zassert_equal(EC_ERROR_UNKNOWN, + drv->get_message_raw(port, payload, &head), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + /* Get raw message should always clean RX alerts */ + rx_mask = TCPC_REG_ALERT_RX_BUF_OVF | TCPC_REG_ALERT_RX_STATUS; + check_tcpci_reg_with_mask(emul, TCPC_REG_ALERT, 0x0, rx_mask); + + /* Test too short message */ + msg.cnt = 1; + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg, true), + "Failed to setup emulator message"); + zassert_equal(EC_ERROR_UNKNOWN, + drv->get_message_raw(port, payload, &head), NULL); + check_tcpci_reg_with_mask(emul, TCPC_REG_ALERT, 0x0, rx_mask); + + /* Test too long message */ + msg.cnt = 31; + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg, true), + "Failed to setup emulator message"); + zassert_equal(EC_ERROR_UNKNOWN, + drv->get_message_raw(port, payload, &head), NULL); + check_tcpci_reg_with_mask(emul, TCPC_REG_ALERT, 0x0, rx_mask); + + /* Test alert register and message payload on success */ + size = 28; + msg.cnt = size + 2; + msg.sop_type = TCPCI_MSG_SOP_PRIME; + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg, true), + "Failed to setup emulator message"); + zassert_equal(EC_SUCCESS, drv->get_message_raw(port, payload, &head), + NULL); + check_tcpci_reg_with_mask(emul, TCPC_REG_ALERT, 0x0, rx_mask); + /* + * Type is in bits 31-28 of header, buf[0] is in bits 7-0, + * buf[1] is in bits 15-8 + */ + exp_head = (TCPCI_MSG_SOP_PRIME << 28) | (buf[1] << 8) | buf[0]; + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf + 2, size, NULL); +} + +/** Test TCPCI transmitting message from TCPC */ +void test_tcpci_transmit(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + struct tcpci_emul_msg *msg; + uint32_t data[6]; + uint16_t header; + int i; + + msg = tcpci_emul_get_tx_msg(emul); + + /* Fill transmit data with pattern */ + for (i = 0; i < 6 * sizeof(uint32_t); i++) { + ((uint8_t *)data)[i] = i; + } + + /* Test transmit hard reset fail */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_TRANSMIT); + zassert_equal(EC_ERROR_INVAL, + drv->transmit(port, TCPCI_MSG_TX_HARD_RESET, 0, NULL), + NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test transmit cabel reset */ + zassert_equal(EC_SUCCESS, + drv->transmit(port, TCPCI_MSG_CABLE_RESET, 0, NULL), + NULL); + zassert_equal(TCPCI_MSG_CABLE_RESET, msg->sop_type, NULL); + + /* Test transmit hard reset */ + zassert_equal(EC_SUCCESS, + drv->transmit(port, TCPCI_MSG_TX_HARD_RESET, 0, NULL), + NULL); + zassert_equal(TCPCI_MSG_TX_HARD_RESET, msg->sop_type, NULL); + + /* Test transmit fail on rx buffer */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_TX_BUFFER); + zassert_equal(EC_ERROR_INVAL, + drv->transmit(port, TCPCI_MSG_SOP_PRIME, 0, data), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test transmit only header */ + /* Build random header with count 0 */ + header = PD_HEADER(PD_CTRL_PING, PD_ROLE_SOURCE, PD_ROLE_UFP, 0, 0, + PD_REV20, 0); + zassert_equal(EC_SUCCESS, + drv->transmit(port, TCPCI_MSG_SOP_PRIME, header, data), + NULL); + zassert_equal(TCPCI_MSG_SOP_PRIME, msg->sop_type, NULL); + zassert_mem_equal(msg->buf, &header, 2, NULL); + zassert_equal(2, msg->cnt, NULL); + + /* Test transmit message */ + /* Build random header with count 6 */ + header = PD_HEADER(PD_CTRL_PING, PD_ROLE_SOURCE, PD_ROLE_UFP, 0, 6, + PD_REV20, 0); + zassert_equal(EC_SUCCESS, + drv->transmit(port, TCPCI_MSG_SOP_PRIME, header, data), + NULL); + zassert_equal(TCPCI_MSG_SOP_PRIME, msg->sop_type, NULL); + zassert_mem_equal(msg->buf, &header, 2, NULL); + zassert_mem_equal(msg->buf + 2, data, 6 * sizeof(uint32_t), NULL); + zassert_equal(2 + 6 * sizeof(uint32_t), msg->cnt, NULL); +} + +/** Test TCPCI alert */ +void test_tcpci_alert(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + + /* Test alert read fail */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_ALERT); + drv->tcpc_alert(port); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Handle overcurrent */ + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, TCPC_REG_ALERT_FAULT); + tcpci_emul_set_reg(emul, TCPC_REG_FAULT_STATUS, + TCPC_REG_FAULT_STATUS_VCONN_OVER_CURRENT); + drv->tcpc_alert(port); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0x0); + check_tcpci_reg(emul, TCPC_REG_FAULT_STATUS, 0x0); + + /* Test TX complete */ + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, TCPC_REG_ALERT_TX_COMPLETE); + drv->tcpc_alert(port); + + /* Test clear alert and ext_alert */ + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, TCPC_REG_ALERT_ALERT_EXT); + tcpci_emul_set_reg(emul, TCPC_REG_ALERT_EXT, + TCPC_REG_ALERT_EXT_TIMER_EXPIRED); + drv->tcpc_alert(port); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0x0); + check_tcpci_reg(emul, TCPC_REG_FAULT_STATUS, 0x0); + + /* Test CC changed, CC status chosen arbitrary */ + tcpci_emul_set_reg(emul, TCPC_REG_CC_STATUS, + TCPC_REG_CC_STATUS_SET(1, TYPEC_CC_VOLT_RP_1_5, + TYPEC_CC_VOLT_RP_3_0)); + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, TCPC_REG_ALERT_CC_STATUS); + drv->tcpc_alert(port); + + /* Test Hard reset */ + tcpci_emul_set_reg(emul, TCPC_REG_ALERT, TCPC_REG_ALERT_RX_HARD_RST); + drv->tcpc_alert(port); +} + +/** Test TCPCI alert RX message */ +void test_tcpci_alert_rx_message(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + struct tcpci_emul_msg msg1, msg2; + uint8_t buf1[32], buf2[32]; + uint32_t payload[7]; + int exp_head; + int i, head; + int size; + + tcpci_emul_set_reg(emul, TCPC_REG_DEV_CAP_2, + TCPC_REG_DEV_CAP_2_LONG_MSG); + tcpci_emul_set_reg(emul, TCPC_REG_RX_DETECT, + TCPC_REG_RX_DETECT_SOP | TCPC_REG_RX_DETECT_SOPP); + + for (i = 0; i < 32; i++) { + buf1[i] = i + 1; + buf2[i] = i + 33; + } + size = 23; + msg1.buf = buf1; + msg1.cnt = size + 3; + msg1.sop_type = TCPCI_MSG_SOP; + + msg2.buf = buf2; + msg2.cnt = size + 3; + msg2.sop_type = TCPCI_MSG_SOP_PRIME; + + /* Test receiving one message */ + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg1, true), + "Failed to setup emulator message"); + drv->tcpc_alert(port); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0x0); + + /* Check if msg1 is in queue */ + zassert_true(tcpm_has_pending_message(port), NULL); + zassert_equal(EC_SUCCESS, tcpm_dequeue_message(port, payload, &head), + NULL); + exp_head = (TCPCI_MSG_SOP << 28) | (buf1[1] << 8) | buf1[0]; + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf1 + 2, size, NULL); + zassert_false(tcpm_has_pending_message(port), NULL); + + /* Test receiving two messages */ + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg1, true), + "Failed to setup emulator message"); + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg2, true), + "Failed to setup emulator message"); + drv->tcpc_alert(port); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0x0); + + /* Check if msg1 is in queue */ + zassert_true(tcpm_has_pending_message(port), NULL); + zassert_equal(EC_SUCCESS, tcpm_dequeue_message(port, payload, &head), + NULL); + exp_head = (TCPCI_MSG_SOP << 28) | (buf1[1] << 8) | buf1[0]; + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf1 + 2, size, NULL); + /* Check if msg2 is in queue */ + zassert_true(tcpm_has_pending_message(port), NULL); + zassert_equal(EC_SUCCESS, tcpm_dequeue_message(port, payload, &head), + NULL); + exp_head = (TCPCI_MSG_SOP_PRIME << 28) | (buf2[1] << 8) | buf2[0]; + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf2 + 2, size, NULL); + zassert_false(tcpm_has_pending_message(port), NULL); + + /* Test with too long first message */ + msg1.cnt = 32; + tcpci_emul_set_reg(emul, TCPC_REG_DEV_CAP_2, + TCPC_REG_DEV_CAP_2_LONG_MSG); + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg1, true), + "Failed to setup emulator message"); + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg2, true), + "Failed to setup emulator message"); + drv->tcpc_alert(port); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0x0); + + /* Check if msg2 is in queue */ + zassert_true(tcpm_has_pending_message(port), NULL); + zassert_equal(EC_SUCCESS, tcpm_dequeue_message(port, payload, &head), + NULL); + exp_head = (TCPCI_MSG_SOP_PRIME << 28) | (buf2[1] << 8) | buf2[0]; + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf2 + 2, size, NULL); + zassert_false(tcpm_has_pending_message(port), NULL); + + /* Test constant read message failure */ + zassert_equal(TCPCI_EMUL_TX_SUCCESS, + tcpci_emul_add_rx_msg(emul, &msg1, true), + "Failed to setup emulator message"); + /* Create loop with one message with wrong size */ + msg1.next = &msg1; + drv->tcpc_alert(port); + /* Nothing should be in queue */ + zassert_false(tcpm_has_pending_message(port), NULL); + + /* Test constant correct messages stream */ + msg1.cnt = size + 3; + drv->tcpc_alert(port); + msg1.next = NULL; + + /* msg1 should be at least twice in queue */ + exp_head = (TCPCI_MSG_SOP << 28) | (buf1[1] << 8) | buf1[0]; + for (i = 0; i < 2; i++) { + zassert_true(tcpm_has_pending_message(port), NULL); + zassert_equal(EC_SUCCESS, + tcpm_dequeue_message(port, payload, &head), NULL); + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf1 + 2, size, NULL); + } + tcpm_clear_pending_messages(port); + zassert_false(tcpm_has_pending_message(port), NULL); + + /* Read message that is left in TCPC buffer */ + drv->tcpc_alert(port); + check_tcpci_reg(emul, TCPC_REG_ALERT, 0x0); + + /* Check if msg1 is in queue */ + zassert_true(tcpm_has_pending_message(port), NULL); + zassert_equal(EC_SUCCESS, tcpm_dequeue_message(port, payload, &head), + NULL); + exp_head = (TCPCI_MSG_SOP << 28) | (buf1[1] << 8) | buf1[0]; + zassert_equal(exp_head, head, + "Received header 0x%08lx, expected 0x%08lx", head, + exp_head); + zassert_mem_equal(payload, buf1 + 2, size, NULL); + zassert_false(tcpm_has_pending_message(port), NULL); +} + +/** Test TCPCI auto discharge on disconnect */ +void test_tcpci_auto_discharge(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint8_t initial_ctrl; + uint8_t exp_ctrl; + + /* Set initial value for POWER ctrl register. Chosen arbitrary. */ + initial_ctrl = TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS | + TCPC_REG_POWER_CTRL_FORCE_DISCHARGE; + tcpci_emul_set_reg(emul, TCPC_REG_POWER_CTRL, initial_ctrl); + + /* Test discharge enable */ + exp_ctrl = initial_ctrl | TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT; + drv->tcpc_enable_auto_discharge_disconnect(port, 1); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); + + /* Test discharge disable */ + exp_ctrl = initial_ctrl & + ~TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT; + drv->tcpc_enable_auto_discharge_disconnect(port, 0); + check_tcpci_reg(emul, TCPC_REG_POWER_CTRL, exp_ctrl); +} + +/** Test TCPCI drp toggle */ +void test_tcpci_drp_toggle(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint8_t exp_tcpc_ctrl, exp_role_ctrl, initial_tcpc_ctrl; + + /* Test error on failed role CTRL set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_ROLE_CTRL); + zassert_equal(EC_ERROR_INVAL, drv->drp_toggle(port), NULL); + + /* Test error on failed TCPC CTRL set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_TCPC_CTRL); + zassert_equal(EC_ERROR_INVAL, drv->drp_toggle(port), NULL); + + /* Test error on failed command set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_COMMAND); + zassert_equal(EC_ERROR_INVAL, drv->drp_toggle(port), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set initial value for TCPC ctrl register. Chosen arbitrary. */ + initial_tcpc_ctrl = TCPC_REG_TCPC_CTRL_DEBUG_ACC_CONTROL | + TCPC_REG_TCPC_CTRL_BIST_TEST_MODE; + tcpci_emul_set_reg(emul, TCPC_REG_TCPC_CTRL, initial_tcpc_ctrl); + + /* + * Test correct registers values for rev 2.0. Role control CC lines + * have to be set to RP with DRP enabled and smallest RP value. + */ + exp_tcpc_ctrl = initial_tcpc_ctrl | + TCPC_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT; + exp_role_ctrl = TCPC_REG_ROLE_CTRL_SET(TYPEC_DRP, TYPEC_RP_USB, + TYPEC_CC_RP, TYPEC_CC_RP); + zassert_equal(EC_SUCCESS, drv->drp_toggle(port), NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_tcpc_ctrl); + check_tcpci_reg(emul, TCPC_REG_ROLE_CTRL, exp_role_ctrl); + check_tcpci_reg(emul, TCPC_REG_COMMAND, + TCPC_REG_COMMAND_LOOK4CONNECTION); + + /* Set TCPCI to revision 1 */ + tcpc_config[port].flags = 0; + tcpci_emul_set_rev(emul, TCPCI_EMUL_REV1_0_VER1_0); + + /* Set initial value for TCPC ctrl register. Chosen arbitrary. */ + initial_tcpc_ctrl = TCPC_REG_TCPC_CTRL_DEBUG_ACC_CONTROL | + TCPC_REG_TCPC_CTRL_BIST_TEST_MODE; + tcpci_emul_set_reg(emul, TCPC_REG_TCPC_CTRL, initial_tcpc_ctrl); + + /* + * Test correct registers values for rev 1.0. Role control CC lines + * have to be set to RD with DRP enabled and smallest RP value. + * Only CC lines setting is different from rev 2.0 + */ + exp_tcpc_ctrl = initial_tcpc_ctrl | + TCPC_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT; + exp_role_ctrl = TCPC_REG_ROLE_CTRL_SET(TYPEC_DRP, TYPEC_RP_USB, + TYPEC_CC_RD, TYPEC_CC_RD); + zassert_equal(EC_SUCCESS, drv->drp_toggle(port), NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_tcpc_ctrl); + check_tcpci_reg(emul, TCPC_REG_ROLE_CTRL, exp_role_ctrl); + check_tcpci_reg(emul, TCPC_REG_COMMAND, + TCPC_REG_COMMAND_LOOK4CONNECTION); +} + +/** Test TCPCI get chip info */ +void test_tcpci_get_chip_info(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + struct ec_response_pd_chip_info_v1 info; + uint16_t vendor, product, bcd; + + /* Test error on failed vendor id get */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_VENDOR_ID); + zassert_equal(EC_ERROR_INVAL, drv->get_chip_info(port, 1, &info), NULL); + + /* Test error on failed product id get */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_PRODUCT_ID); + zassert_equal(EC_ERROR_INVAL, drv->get_chip_info(port, 1, &info), NULL); + + /* Test error on failed BCD get */ + i2c_common_emul_set_read_fail_reg(common_data, TCPC_REG_VENDOR_ID); + zassert_equal(EC_ERROR_INVAL, drv->get_chip_info(port, 1, &info), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test reading chip info. Values chosen arbitrary. */ + vendor = 0x1234; + product = 0x5678; + bcd = 0x9876; + tcpci_emul_set_reg(emul, TCPC_REG_VENDOR_ID, vendor); + tcpci_emul_set_reg(emul, TCPC_REG_PRODUCT_ID, product); + tcpci_emul_set_reg(emul, TCPC_REG_BCD_DEV, bcd); + zassert_equal(EC_SUCCESS, drv->get_chip_info(port, 1, &info), NULL); + zassert_equal(vendor, info.vendor_id, NULL); + zassert_equal(product, info.product_id, NULL); + zassert_equal(bcd, info.device_id, NULL); + + /* Test reading cached chip info */ + info.vendor_id = 0; + info.product_id = 0; + info.device_id = 0; + /* Make sure, that TCPC is not accessed */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_FAIL_ALL_REG); + zassert_equal(EC_SUCCESS, drv->get_chip_info(port, 0, &info), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + zassert_equal(vendor, info.vendor_id, NULL); + zassert_equal(product, info.product_id, NULL); + zassert_equal(bcd, info.device_id, NULL); +} + +/** Test TCPCI enter low power mode */ +void test_tcpci_low_power_mode(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + + /* Test error on failed command set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_COMMAND); + zassert_equal(EC_ERROR_INVAL, drv->enter_low_power_mode(port), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test correct command is issued */ + zassert_equal(EC_SUCCESS, drv->enter_low_power_mode(port), NULL); + check_tcpci_reg(emul, TCPC_REG_COMMAND, TCPC_REG_COMMAND_I2CIDLE); +} + +/** Test TCPCI set bist test mode */ +void test_tcpci_set_bist_mode(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint16_t exp_mask, initial_mask; + uint8_t exp_ctrl, initial_ctrl; + + /* Test error on TCPC CTRL set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_TCPC_CTRL); + zassert_equal(EC_ERROR_INVAL, drv->set_bist_test_mode(port, 1), NULL); + + /* Test error on alert mask set */ + i2c_common_emul_set_write_fail_reg(common_data, TCPC_REG_ALERT_MASK); + zassert_equal(EC_ERROR_INVAL, drv->set_bist_test_mode(port, 1), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Set initial value for alert mask register. Chosen arbitrary. */ + initial_mask = TCPC_REG_ALERT_MASK_ALL; + tcpci_emul_set_reg(emul, TCPC_REG_ALERT_MASK, initial_mask); + + /* Set initial value for TCPC ctrl register. Chosen arbitrary. */ + initial_ctrl = TCPC_REG_TCPC_CTRL_DEBUG_ACC_CONTROL | + TCPC_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT; + tcpci_emul_set_reg(emul, TCPC_REG_TCPC_CTRL, initial_ctrl); + + /* Test enabling bist test mode */ + exp_mask = initial_mask & ~TCPC_REG_ALERT_RX_STATUS; + exp_ctrl = initial_ctrl | TCPC_REG_TCPC_CTRL_BIST_TEST_MODE; + zassert_equal(EC_SUCCESS, drv->set_bist_test_mode(port, 1), NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); + + /* Test disabling bist test mode */ + exp_mask = initial_mask | TCPC_REG_ALERT_RX_STATUS; + exp_ctrl = initial_ctrl & ~TCPC_REG_TCPC_CTRL_BIST_TEST_MODE; + zassert_equal(EC_SUCCESS, drv->set_bist_test_mode(port, 0), NULL); + check_tcpci_reg(emul, TCPC_REG_TCPC_CTRL, exp_ctrl); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, exp_mask); +} + +void test_tcpci_hard_reset_reinit(const struct emul *emul, + struct i2c_common_emul_data *common_data, + enum usbc_port port) +{ + const struct tcpm_drv *drv = tcpc_config[port].drv; + uint16_t power_status_mask; + uint16_t alert_mask; + + zassume_equal(EC_SUCCESS, drv->init(port), NULL); + tcpci_emul_get_reg(emul, TCPC_REG_POWER_STATUS_MASK, + &power_status_mask); + tcpci_emul_get_reg(emul, TCPC_REG_ALERT_MASK, &alert_mask); + zassert_ok(tcpci_hard_reset_reinit(USBC_PORT_C0), NULL); + check_tcpci_reg(emul, TCPC_REG_POWER_STATUS_MASK, power_status_mask); + check_tcpci_reg(emul, TCPC_REG_ALERT_MASK, alert_mask); +} diff --git a/zephyr/test/drivers/default/src/tcs3400.c b/zephyr/test/drivers/default/src/tcs3400.c new file mode 100644 index 0000000000..860b069532 --- /dev/null +++ b/zephyr/test/drivers/default/src/tcs3400.c @@ -0,0 +1,641 @@ +/* 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 "common.h" +#include "i2c.h" +#include "emul/emul_tcs3400.h" +#include "emul/emul_common_i2c.h" + +#include "motion_sense.h" +#include "motion_sense_fifo.h" +#include "driver/als_tcs3400.h" +#include "test/drivers/test_state.h" + +#define TCS_NODE DT_NODELABEL(tcs_emul) +#define TCS_CLR_SENSOR_ID SENSOR_ID(DT_NODELABEL(tcs3400_clear)) +#define TCS_RGB_SENSOR_ID SENSOR_ID(DT_NODELABEL(tcs3400_rgb)) +#define TCS_INT_EVENT \ + TASK_EVENT_MOTION_SENSOR_INTERRUPT(SENSOR_ID(DT_ALIAS(tcs3400_int))) + +/** How accurate comparision of rgb sensors should be */ +#define V_EPS 8 + +/** Test initialization of light sensor driver and device */ +ZTEST_USER(tcs3400, test_tcs_init) +{ + struct motion_sensor_t *ms, *ms_rgb; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + struct i2c_common_emul_data *common_data = + emul_tcs3400_get_i2c_common_data(emul); + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* RGB sensor initialization is always successful */ + zassert_equal(EC_SUCCESS, ms_rgb->drv->init(ms_rgb), NULL); + + /* Fail init on communication errors */ + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_FAIL_ALL_REG); + zassert_equal(EC_ERROR_INVAL, ms->drv->init(ms), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Fail on bad ID */ + tcs_emul_set_reg(emul, TCS_I2C_ID, 0); + zassert_equal(EC_ERROR_ACCESS_DENIED, ms->drv->init(ms), NULL); + /* Restore ID */ + tcs_emul_set_reg(emul, TCS_I2C_ID, + DT_STRING_TOKEN(DT_NODELABEL(tcs_emul), device_id)); + + /* Test successful init. ATIME and AGAIN should be changed on init */ + zassert_equal(EC_SUCCESS, ms->drv->init(ms), NULL); + zassert_equal(TCS_DEFAULT_ATIME, tcs_emul_get_reg(emul, TCS_I2C_ATIME), + NULL); + zassert_equal(TCS_DEFAULT_AGAIN, + tcs_emul_get_reg(emul, TCS_I2C_CONTROL), NULL); +} + +/** Test if read function leaves device in correct mode to accuire data */ +ZTEST_USER(tcs3400, test_tcs_read) +{ + struct motion_sensor_t *ms; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + struct i2c_common_emul_data *common_data = + emul_tcs3400_get_i2c_common_data(emul); + uint8_t enable; + intv3_t v; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + + /* Test error on writing registers */ + i2c_common_emul_set_write_fail_reg(common_data, TCS_I2C_ATIME); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, v), NULL); + i2c_common_emul_set_write_fail_reg(common_data, TCS_I2C_CONTROL); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, v), NULL); + i2c_common_emul_set_write_fail_reg(common_data, TCS_I2C_ENABLE); + zassert_equal(EC_ERROR_INVAL, ms->drv->read(ms, v), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test starting read with calibration */ + tcs_emul_set_reg(emul, TCS_I2C_ATIME, 0); + tcs_emul_set_reg(emul, TCS_I2C_CONTROL, 0); + tcs_emul_set_reg(emul, TCS_I2C_ENABLE, 0); + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + zassert_equal(EC_RES_IN_PROGRESS, ms->drv->read(ms, v), NULL); + zassert_equal(TCS_CALIBRATION_ATIME, + tcs_emul_get_reg(emul, TCS_I2C_ATIME), NULL); + zassert_equal(TCS_CALIBRATION_AGAIN, + tcs_emul_get_reg(emul, TCS_I2C_CONTROL), NULL); + enable = tcs_emul_get_reg(emul, TCS_I2C_ENABLE); + zassert_true(enable & TCS_I2C_ENABLE_POWER_ON, NULL); + zassert_true(enable & TCS_I2C_ENABLE_ADC_ENABLE, NULL); + zassert_true(enable & TCS_I2C_ENABLE_INT_ENABLE, NULL); + + /* Test starting read without calibration */ + tcs_emul_set_reg(emul, TCS_I2C_ATIME, 0); + tcs_emul_set_reg(emul, TCS_I2C_CONTROL, 0); + tcs_emul_set_reg(emul, TCS_I2C_ENABLE, 0); + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 0), NULL); + zassert_equal(EC_RES_IN_PROGRESS, ms->drv->read(ms, v), NULL); + enable = tcs_emul_get_reg(emul, TCS_I2C_ENABLE); + zassert_true(enable & TCS_I2C_ENABLE_POWER_ON, NULL); + zassert_true(enable & TCS_I2C_ENABLE_ADC_ENABLE, NULL); + zassert_true(enable & TCS_I2C_ENABLE_INT_ENABLE, NULL); +} + +/** Check if FIFO for RGB and clear sensor is empty */ +static void check_fifo_empty_f(struct motion_sensor_t *ms, + struct motion_sensor_t *ms_rgb, int line) +{ + struct ec_response_motion_sensor_data vector; + uint16_t size; + + /* Read all data committed to FIFO */ + while (motion_sense_fifo_read(sizeof(vector), 1, &vector, &size)) { + /* Ignore timestamp frames */ + if (vector.flags == MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) { + continue; + } + + if (ms - motion_sensors == vector.sensor_num) { + zassert_unreachable( + "Unexpected frame for clear sensor @line: %d", + line); + } + + if (ms_rgb - motion_sensors == vector.sensor_num) { + zassert_unreachable( + "Unexpected frame for rgb sensor @line: %d", + line); + } + } +} +#define check_fifo_empty(ms, ms_rgb) check_fifo_empty_f(ms, ms_rgb, __LINE__) + +/** + * Test different conditions where irq handler fail or commit no data + * to fifo + */ +ZTEST_USER(tcs3400, test_tcs_irq_handler_fail) +{ + struct motion_sensor_t *ms, *ms_rgb; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + struct i2c_common_emul_data *common_data = + emul_tcs3400_get_i2c_common_data(emul); + uint32_t event; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* Fail on wrong event */ + event = 0x1234 & ~TCS_INT_EVENT; + zassert_equal(EC_ERROR_NOT_HANDLED, ms->drv->irq_handler(ms, &event), + NULL); + check_fifo_empty(ms, ms_rgb); + + event = TCS_INT_EVENT; + /* Test error on reading status */ + i2c_common_emul_set_read_fail_reg(common_data, TCS_I2C_STATUS); + zassert_equal(EC_ERROR_INVAL, ms->drv->irq_handler(ms, &event), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + check_fifo_empty(ms, ms_rgb); + + /* Test fail on changing device power state */ + i2c_common_emul_set_write_fail_reg(common_data, TCS_I2C_ENABLE); + zassert_equal(EC_ERROR_INVAL, ms->drv->irq_handler(ms, &event), NULL); + i2c_common_emul_set_write_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + check_fifo_empty(ms, ms_rgb); + + /* Test that no data is committed when status is 0 */ + tcs_emul_set_reg(emul, TCS_I2C_STATUS, 0); + zassert_equal(EC_SUCCESS, ms->drv->irq_handler(ms, &event), NULL); + check_fifo_empty(ms, ms_rgb); +} + +/** + * Check if last data committed to FIFO for RGB and clear sensor equals to + * expected value. + */ +static void check_fifo_f(struct motion_sensor_t *ms, + struct motion_sensor_t *ms_rgb, int *exp_v, int eps, + int line) +{ + struct ec_response_motion_sensor_data vector; + uint16_t size; + int ret_v[4] = { -1, -1, -1, -1 }; + int i; + + /* Read all data committed to FIFO */ + while (motion_sense_fifo_read(sizeof(vector), 1, &vector, &size)) { + /* Ignore timestamp frames */ + if (vector.flags == MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) { + continue; + } + + /* Get clear frame */ + if (ms - motion_sensors == vector.sensor_num) { + ret_v[0] = vector.udata[0]; + } + + /* Get rgb frame */ + if (ms_rgb - motion_sensors == vector.sensor_num) { + ret_v[1] = vector.udata[0]; + ret_v[2] = vector.udata[1]; + ret_v[3] = vector.udata[2]; + } + } + + if (ret_v[0] == -1) { + zassert_unreachable("No frame for clear sensor, line %d", line); + } + + if (ret_v[1] == -1) { + zassert_unreachable("No frame for rgb sensor, line %d", line); + } + + /* Compare with last committed data */ + for (i = 0; i < 4; i++) { + zassert_within( + exp_v[i], ret_v[i], eps, + "Expected [%d; %d; %d; %d], got [%d; %d; %d; %d]; line: %d", + exp_v[0], exp_v[1], exp_v[2], exp_v[3], ret_v[0], + ret_v[1], ret_v[2], ret_v[3], line); + } +} +#define check_fifo(ms, ms_rgb, exp_v, eps) \ + check_fifo_f(ms, ms_rgb, exp_v, eps, __LINE__) + +/** Test calibration mode reading of light sensor values */ +ZTEST_USER(tcs3400, test_tcs_read_calibration) +{ + struct motion_sensor_t *ms, *ms_rgb; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + uint32_t event = TCS_INT_EVENT; + int emul_v[4]; + int exp_v[4]; + intv3_t v; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* Need to be set to collect all data in FIFO */ + ms->oversampling_ratio = 1; + ms_rgb->oversampling_ratio = 1; + /* Enable calibration mode */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 1), NULL); + /* Setup AGAIN and ATIME for calibration */ + zassert_equal(EC_RES_IN_PROGRESS, ms->drv->read(ms, v), NULL); + + /* Test data that are in calibration range */ + exp_v[0] = 12; + exp_v[1] = 123; + exp_v[2] = 1234; + exp_v[3] = 12345; + /* + * Emulator value is with gain 64, while expected value is + * with gain 16 + */ + emul_v[0] = exp_v[0] * 64 / 16; + emul_v[1] = exp_v[1] * 64 / 16; + emul_v[2] = exp_v[2] * 64 / 16; + emul_v[3] = exp_v[3] * 64 / 16; + tcs_emul_set_val(emul, TCS_EMUL_C, emul_v[0]); + tcs_emul_set_val(emul, TCS_EMUL_R, emul_v[1]); + tcs_emul_set_val(emul, TCS_EMUL_G, emul_v[2]); + tcs_emul_set_val(emul, TCS_EMUL_B, emul_v[3]); + /* Set status to show valid data */ + tcs_emul_set_reg(emul, TCS_I2C_STATUS, TCS_I2C_STATUS_RGBC_VALID); + + zassert_equal(EC_SUCCESS, ms->drv->irq_handler(ms, &event), NULL); + /* In calibration mode check for exact match */ + check_fifo(ms, ms_rgb, exp_v, 1); + + /* Test data that are outside of calibration range */ + exp_v[0] = 0; + exp_v[1] = UINT16_MAX; + exp_v[2] = UINT16_MAX; + exp_v[3] = 213; + /* + * Emulator value is with gain 64, while expected value is + * with gain 16 + */ + emul_v[0] = 0; + emul_v[1] = exp_v[1] * 64 / 16; + emul_v[2] = (UINT16_MAX + 23) * 64 / 16; + emul_v[3] = exp_v[3] * 64 / 16; + tcs_emul_set_val(emul, TCS_EMUL_C, emul_v[0]); + tcs_emul_set_val(emul, TCS_EMUL_R, emul_v[1]); + tcs_emul_set_val(emul, TCS_EMUL_G, emul_v[2]); + tcs_emul_set_val(emul, TCS_EMUL_B, emul_v[3]); + /* Set status to show valid data */ + tcs_emul_set_reg(emul, TCS_I2C_STATUS, TCS_I2C_STATUS_RGBC_VALID); + + zassert_equal(EC_SUCCESS, ms->drv->irq_handler(ms, &event), NULL); + /* In calibration mode check for exact match */ + check_fifo(ms, ms_rgb, exp_v, 1); +} + +/** + * Set emulator internal value using expected output value returned by + * the driver. First element of expected vector is IR value used in + * calculations. Based on that clear light value is calculated. + * First element of expected vector is updated by this function. + */ +static void set_emul_val_from_exp(int *exp_v, uint16_t *scale, + const struct emul *emul) +{ + int emul_v[4]; + int ir; + + /* We use exp_v[0] as IR value */ + ir = exp_v[0]; + /* Driver will return lux value as calculated blue light value */ + exp_v[0] = exp_v[2]; + + /* + * Driver takes care of different ATIME and AGAIN value, so expected + * value is always normalized to ATIME 256 and AGAIN 16. Convert it + * to internal emulator value (ATIME 256, AGAIN 64) and add expected IR + * value. Clear light is the sum of rgb light and IR component. + */ + emul_v[1] = (exp_v[1] + ir) * 64 / 16; + emul_v[2] = (exp_v[2] + ir) * 64 / 16; + emul_v[3] = (exp_v[3] + ir) * 64 / 16; + emul_v[0] = (exp_v[1] + exp_v[2] + exp_v[3] + ir) * 64 / 16; + + /* Apply scale, driver should divide by this value */ + emul_v[0] = SENSOR_APPLY_SCALE(emul_v[0], scale[0]); + emul_v[1] = SENSOR_APPLY_SCALE(emul_v[1], scale[1]); + emul_v[2] = SENSOR_APPLY_SCALE(emul_v[2], scale[2]); + emul_v[3] = SENSOR_APPLY_SCALE(emul_v[3], scale[3]); + + /* Set emulator values */ + tcs_emul_set_val(emul, TCS_EMUL_C, emul_v[0]); + tcs_emul_set_val(emul, TCS_EMUL_R, emul_v[1]); + tcs_emul_set_val(emul, TCS_EMUL_G, emul_v[2]); + tcs_emul_set_val(emul, TCS_EMUL_B, emul_v[3]); +} + +/** Test normal mode reading of light sensor values */ +ZTEST_USER(tcs3400, test_tcs_read_xyz) +{ + struct motion_sensor_t *ms, *ms_rgb; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + uint32_t event = TCS_INT_EVENT; + /* Expected data to test: IR, R, G, B */ + int exp_v[][4] = { + { 200, 1110, 870, 850 }, { 300, 1110, 10000, 8500 }, + { 600, 50000, 40000, 30000 }, { 1000, 3000, 40000, 2000 }, + { 1000, 65000, 65000, 65000 }, { 100, 214, 541, 516 }, + { 143, 2141, 5414, 5163 }, { 100, 50000, 40000, 30000 }, + { 1430, 2141, 5414, 5163 }, { 10000, 50000, 40000, 30000 }, + { 10000, 214, 541, 516 }, { 15000, 50000, 40000, 30000 }, + }; + uint16_t scale[4] = { MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE }; + int i, test; + intv3_t v; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* Need to be set to collect all data in FIFO */ + ms->oversampling_ratio = 1; + ms_rgb->oversampling_ratio = 1; + /* Disable calibration mode */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 0), NULL); + /* Setup AGAIN and ATIME for normal mode */ + zassert_equal(EC_RES_IN_PROGRESS, ms->drv->read(ms, v), NULL); + + /* Test different data in supported range */ + for (test = 0; test < ARRAY_SIZE(exp_v); test++) { + set_emul_val_from_exp(exp_v[test], scale, emul); + + /* Run few times to allow driver change gain */ + for (i = 0; i < 5; i++) { + tcs_emul_set_reg(emul, TCS_I2C_STATUS, + TCS_I2C_STATUS_RGBC_VALID); + zassert_equal(EC_SUCCESS, + ms->drv->irq_handler(ms, &event), NULL); + } + check_fifo(ms, ms_rgb, exp_v[test], V_EPS); + } + + /* Test data that are outside of supported range */ + exp_v[0][0] = 3000; + exp_v[0][1] = UINT16_MAX; + exp_v[0][2] = UINT16_MAX * 32; + exp_v[0][3] = 200; + set_emul_val_from_exp(exp_v[0], scale, emul); + + /* Run few times to allow driver change gain */ + for (i = 0; i < 10; i++) { + tcs_emul_set_reg(emul, TCS_I2C_STATUS, + TCS_I2C_STATUS_RGBC_VALID); + zassert_equal(EC_SUCCESS, ms->drv->irq_handler(ms, &event), + NULL); + } + /* + * If saturation value is exceeded on any rgb sensor, than data + * shouldn't be committed to FIFO. + */ + check_fifo_empty(ms, ms_rgb); +} + +/** + * Test getting and setting scale of light sensor. Checks if collected values + * are scaled properly. + */ +ZTEST_USER(tcs3400, test_tcs_scale) +{ + struct motion_sensor_t *ms, *ms_rgb; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + uint32_t event = TCS_INT_EVENT; + /* Expected data to test: IR, R, G, B */ + int exp_v[][4] = { + { 200, 1110, 870, 850 }, { 300, 1110, 10000, 8500 }, + { 600, 5000, 4000, 3000 }, { 100, 3000, 4000, 2000 }, + { 100, 1000, 1000, 1000 }, + }; + /* Scale for each test */ + uint16_t exp_scale[][4] = { + { MOTION_SENSE_DEFAULT_SCALE, MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, MOTION_SENSE_DEFAULT_SCALE }, + { MOTION_SENSE_DEFAULT_SCALE + 300, + MOTION_SENSE_DEFAULT_SCALE + 300, + MOTION_SENSE_DEFAULT_SCALE + 300, + MOTION_SENSE_DEFAULT_SCALE + 300 }, + { MOTION_SENSE_DEFAULT_SCALE - 300, + MOTION_SENSE_DEFAULT_SCALE - 300, + MOTION_SENSE_DEFAULT_SCALE - 300, + MOTION_SENSE_DEFAULT_SCALE - 300 }, + { MOTION_SENSE_DEFAULT_SCALE + 345, + MOTION_SENSE_DEFAULT_SCALE - 5423, + MOTION_SENSE_DEFAULT_SCALE - 30, + MOTION_SENSE_DEFAULT_SCALE + 400 }, + { MOTION_SENSE_DEFAULT_SCALE - 345, + MOTION_SENSE_DEFAULT_SCALE + 5423, + MOTION_SENSE_DEFAULT_SCALE + 30, + MOTION_SENSE_DEFAULT_SCALE - 400 }, + { MOTION_SENSE_DEFAULT_SCALE, MOTION_SENSE_DEFAULT_SCALE, + MOTION_SENSE_DEFAULT_SCALE, MOTION_SENSE_DEFAULT_SCALE } + }; + uint16_t scale[3]; + int16_t temp; + int i, test; + intv3_t v; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* Need to be set to collect all data in FIFO */ + ms->oversampling_ratio = 1; + ms_rgb->oversampling_ratio = 1; + /* Disable calibration mode */ + zassert_equal(EC_SUCCESS, ms->drv->perform_calib(ms, 0), NULL); + /* Setup AGAIN and ATIME for normal mode */ + zassert_equal(EC_RES_IN_PROGRESS, ms->drv->read(ms, v), NULL); + + /* Test different data in supported range */ + for (test = 0; test < ARRAY_SIZE(exp_v); test++) { + /* Set and test clear sensor scale */ + zassert_equal(EC_SUCCESS, + ms->drv->set_scale(ms, exp_scale[test], 0), + "test %d", test); + zassert_equal(EC_SUCCESS, ms->drv->get_scale(ms, scale, &temp), + "test %d", test); + zassert_equal((int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, temp, + "test %d, %d", test, temp); + zassert_equal(exp_scale[test][0], scale[0], "test %d", test); + + /* Set and test RGB sensor scale */ + zassert_equal(EC_SUCCESS, + ms_rgb->drv->set_scale(ms_rgb, + &(exp_scale[test][1]), 0), + "test %d", test); + zassert_equal(EC_SUCCESS, + ms_rgb->drv->get_scale(ms_rgb, scale, &temp), + "test %d", test); + zassert_equal((int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP, temp, + "test %d", test); + zassert_equal(exp_scale[test][1], scale[0], "test %d", test); + zassert_equal(exp_scale[test][2], scale[1], "test %d", test); + zassert_equal(exp_scale[test][3], scale[2], "test %d", test); + + set_emul_val_from_exp(exp_v[test], exp_scale[test], emul); + + /* Run few times to allow driver change gain */ + for (i = 0; i < 5; i++) { + tcs_emul_set_reg(emul, TCS_I2C_STATUS, + TCS_I2C_STATUS_RGBC_VALID); + zassert_equal(EC_SUCCESS, + ms->drv->irq_handler(ms, &event), NULL); + } + check_fifo(ms, ms_rgb, exp_v[test], V_EPS); + } + + /* Test fail if scale equals 0 */ + scale[0] = 0; + scale[1] = MOTION_SENSE_DEFAULT_SCALE; + scale[2] = MOTION_SENSE_DEFAULT_SCALE; + zassert_equal(EC_ERROR_INVAL, ms->drv->set_scale(ms, scale, 0), NULL); + + zassert_equal(EC_ERROR_INVAL, ms_rgb->drv->set_scale(ms_rgb, scale, 0), + NULL); + scale[0] = MOTION_SENSE_DEFAULT_SCALE; + scale[1] = 0; + scale[2] = MOTION_SENSE_DEFAULT_SCALE; + zassert_equal(EC_ERROR_INVAL, ms_rgb->drv->set_scale(ms_rgb, scale, 0), + NULL); + scale[0] = MOTION_SENSE_DEFAULT_SCALE; + scale[1] = MOTION_SENSE_DEFAULT_SCALE; + scale[2] = 0; + zassert_equal(EC_ERROR_INVAL, ms_rgb->drv->set_scale(ms_rgb, scale, 0), + NULL); +} + +/** Test setting and getting data rate of light sensor */ +ZTEST_USER(tcs3400, test_tcs_data_rate) +{ + struct motion_sensor_t *ms, *ms_rgb; + const struct emul *emul = EMUL_DT_GET(TCS_NODE); + struct i2c_common_emul_data *common_data = + emul_tcs3400_get_i2c_common_data(emul); + uint8_t enable; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + /* RGB sensor doesn't set rate, but return rate of clear sesnor */ + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* Test fail on reading device power state */ + i2c_common_emul_set_read_fail_reg(common_data, TCS_I2C_ENABLE); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 0, 0), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 0, 1), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 100, 0), NULL); + zassert_equal(EC_ERROR_INVAL, ms->drv->set_data_rate(ms, 100, 1), NULL); + i2c_common_emul_set_read_fail_reg(common_data, + I2C_COMMON_EMUL_NO_FAIL_REG); + + /* Test setting 0 rate disables device */ + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 0, 0), NULL); + zassert_equal(0, tcs_emul_get_reg(emul, TCS_I2C_ENABLE), NULL); + zassert_equal(0, ms->drv->get_data_rate(ms), NULL); + zassert_equal(0, ms_rgb->drv->get_data_rate(ms_rgb), NULL); + + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 0, 1), NULL); + zassert_equal(0, tcs_emul_get_reg(emul, TCS_I2C_ENABLE), NULL); + zassert_equal(0, tcs_emul_get_reg(emul, TCS_I2C_ENABLE), NULL); + zassert_equal(0, ms->drv->get_data_rate(ms), NULL); + zassert_equal(0, ms_rgb->drv->get_data_rate(ms_rgb), NULL); + + /* Test setting non-zero rate enables device */ + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 100, 0), NULL); + enable = tcs_emul_get_reg(emul, TCS_I2C_ENABLE); + zassert_true(enable & TCS_I2C_ENABLE_POWER_ON, NULL); + zassert_true(enable & TCS_I2C_ENABLE_ADC_ENABLE, NULL); + zassert_true(enable & TCS_I2C_ENABLE_INT_ENABLE, NULL); + zassert_equal(100, ms->drv->get_data_rate(ms), NULL); + zassert_equal(100, ms_rgb->drv->get_data_rate(ms_rgb), NULL); + + zassert_equal(EC_SUCCESS, ms->drv->set_data_rate(ms, 100, 1), NULL); + enable = tcs_emul_get_reg(emul, TCS_I2C_ENABLE); + zassert_true(enable & TCS_I2C_ENABLE_POWER_ON, NULL); + zassert_true(enable & TCS_I2C_ENABLE_ADC_ENABLE, NULL); + zassert_true(enable & TCS_I2C_ENABLE_INT_ENABLE, NULL); + zassert_equal(100, ms->drv->get_data_rate(ms), NULL); + zassert_equal(100, ms_rgb->drv->get_data_rate(ms_rgb), NULL); + + /* Test RGB sensor doesn't change data rate */ + zassert_equal(EC_SUCCESS, ms_rgb->drv->set_data_rate(ms_rgb, 300, 0), + NULL); + zassert_equal(100, ms->drv->get_data_rate(ms), NULL); + zassert_equal(100, ms_rgb->drv->get_data_rate(ms_rgb), NULL); + + zassert_equal(EC_SUCCESS, ms_rgb->drv->set_data_rate(ms_rgb, 300, 1), + NULL); + zassert_equal(100, ms->drv->get_data_rate(ms), NULL); + zassert_equal(100, ms_rgb->drv->get_data_rate(ms_rgb), NULL); +} + +/** Test set range function of clear and RGB sensors */ +ZTEST_USER(tcs3400, test_tcs_set_range) +{ + struct motion_sensor_t *ms, *ms_rgb; + + ms = &motion_sensors[TCS_CLR_SENSOR_ID]; + ms_rgb = &motion_sensors[TCS_RGB_SENSOR_ID]; + + /* RGB sensor doesn't set anything */ + zassert_equal(EC_SUCCESS, ms_rgb->drv->set_range(ms_rgb, 1, 0), NULL); + + /* Clear sensor doesn't change anything on device to set range */ + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 0x12300, 1), NULL); + zassert_equal(0x12300, ms->current_range, NULL); + + zassert_equal(EC_SUCCESS, ms->drv->set_range(ms, 0x10000, 0), NULL); + zassert_equal(0x10000, ms->current_range, NULL); +} + +struct tcs3400_test_fixture { + struct als_drv_data_t drv_data; + struct tcs3400_rgb_drv_data_t rgb_drv_data; +}; + +static void tcs3400_before(void *state) +{ + struct tcs3400_test_fixture *f = state; + + f->drv_data = *TCS3400_DRV_DATA(&motion_sensors[TCS_CLR_SENSOR_ID]); + f->rgb_drv_data = + *TCS3400_RGB_DRV_DATA(&motion_sensors[TCS_RGB_SENSOR_ID]); +} + +static void tcs3400_after(void *state) +{ + struct tcs3400_test_fixture *f = state; + + *TCS3400_DRV_DATA(&motion_sensors[TCS_CLR_SENSOR_ID]) = f->drv_data; + *TCS3400_RGB_DRV_DATA(&motion_sensors[TCS_RGB_SENSOR_ID]) = + f->rgb_drv_data; +} + +static void *tcs3400_setup(void) +{ + static struct tcs3400_test_fixture tcs3400_fixture = { 0 }; + + return &tcs3400_fixture; +} + +ZTEST_SUITE(tcs3400, drivers_predicate_post_main, tcs3400_setup, tcs3400_before, + tcs3400_after, NULL); diff --git a/zephyr/test/drivers/default/src/temp_sensor.c b/zephyr/test/drivers/default/src/temp_sensor.c new file mode 100644 index 0000000000..5caecc556c --- /dev/null +++ b/zephyr/test/drivers/default/src/temp_sensor.c @@ -0,0 +1,200 @@ +/* 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/adc.h> +#include <zephyr/drivers/adc/adc_emul.h> +#include <zephyr/drivers/gpio.h> +#include <zephyr/drivers/gpio/gpio_emul.h> + +#include <math.h> + +#include "common.h" +#include "temp_sensor.h" +#include "temp_sensor/temp_sensor.h" +#include "test/drivers/test_state.h" + +#define GPIO_PG_EC_DSW_PWROK_PATH DT_PATH(named_gpios, pg_ec_dsw_pwrok) +#define GPIO_PG_EC_DSW_PWROK_PORT DT_GPIO_PIN(GPIO_PG_EC_DSW_PWROK_PATH, gpios) + +#define GPIO_EC_PG_PIN_TEMP_PATH DT_PATH(named_gpios, ec_pg_pin_temp) +#define GPIO_EC_PG_PIN_TEMP_PORT DT_GPIO_PIN(GPIO_EC_PG_PIN_TEMP_PATH, gpios) + +#define ADC_DEVICE_NODE DT_NODELABEL(adc0) +#define ADC_CHANNELS_NUM DT_PROP(DT_NODELABEL(adc0), nchannels) + +/** Test error code when invalid sensor is passed to temp_sensor_read() */ +ZTEST_USER(temp_sensor, test_temp_sensor_wrong_id) +{ + int temp; + + zassert_equal(EC_ERROR_INVAL, + temp_sensor_read(TEMP_SENSOR_COUNT, &temp), NULL); +} + +/** Test error code when temp_sensor_read() is called with powered off ADC */ +ZTEST_USER(temp_sensor, test_temp_sensor_adc_error) +{ + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_PG_EC_DSW_PWROK_PATH, gpios)); + int temp; + + zassert_not_null(gpio_dev, "Cannot get GPIO device"); + + /* + * pg_ec_dsw_pwrok = 0 means ADC is not powered. + * adc_read will return error + */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_PG_EC_DSW_PWROK_PORT, 0), + NULL); + + zassert_equal(EC_ERROR_NOT_POWERED, + temp_sensor_read( + TEMP_SENSOR_ID(DT_NODELABEL(named_temp_charger)), + &temp), + NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + temp_sensor_read( + TEMP_SENSOR_ID(DT_NODELABEL(named_temp_ddr_soc)), + &temp), + NULL); + zassert_equal( + EC_ERROR_NOT_POWERED, + temp_sensor_read(TEMP_SENSOR_ID(DT_NODELABEL(named_temp_fan)), + &temp), + NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + temp_sensor_read(TEMP_SENSOR_ID(DT_NODELABEL( + named_temp_pp3300_regulator)), + &temp), + NULL); + + /* power ADC */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_PG_EC_DSW_PWROK_PORT, 1), + NULL); +} + +/** Test error code when temp_sensor_read() is called power-good-pin low */ +ZTEST_USER(temp_sensor, test_temp_sensor_pg_pin) +{ + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_EC_PG_PIN_TEMP_PATH, gpios)); + int temp; + + zassert_not_null(gpio_dev, "Cannot get GPIO device"); + + /* ec_pg_pin_temp = 0 means temperature sensors are not powered. */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_EC_PG_PIN_TEMP_PORT, 0), + NULL); + + zassert_equal(EC_ERROR_NOT_POWERED, + temp_sensor_read( + TEMP_SENSOR_ID(DT_NODELABEL(named_temp_charger)), + &temp), + NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + temp_sensor_read( + TEMP_SENSOR_ID(DT_NODELABEL(named_temp_ddr_soc)), + &temp), + NULL); + zassert_equal( + EC_ERROR_NOT_POWERED, + temp_sensor_read(TEMP_SENSOR_ID(DT_NODELABEL(named_temp_fan)), + &temp), + NULL); + zassert_equal(EC_ERROR_NOT_POWERED, + temp_sensor_read(TEMP_SENSOR_ID(DT_NODELABEL( + named_temp_pp3300_regulator)), + &temp), + NULL); + + /* power ADC */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_EC_PG_PIN_TEMP_PORT, 1), + NULL); +} + +/** Simple ADC emulator custom function which always return error */ +static int adc_error_func(const struct device *dev, unsigned int channel, + void *param, uint32_t *result) +{ + return -EINVAL; +} + +/** + * Set valid response only for ADC channel connected with tested sensor. + * Check if temp_sensor_read() from tested sensor returns EC_SUCCESS and + * valid temperature. Set invalid response on ADC channel for next test. + */ +static void check_valid_temperature(const struct device *adc_dev, int sensor) +{ + int temp; + + /* ADC channel of tested sensor return valid value */ + zassert_ok(adc_emul_const_value_set(adc_dev, temp_sensors[sensor].idx, + 1000), + "adc_emul_const_value_set() failed (sensor %d)", sensor); + zassert_equal(EC_SUCCESS, temp_sensor_read(sensor, &temp), NULL); + zassert_within( + temp, 273 + 50, 51, + "Expected temperature in 0*C-100*C, got %d*C (sensor %d)", + temp - 273, sensor); + /* Return error on ADC channel of tested sensor */ + zassert_ok(adc_emul_value_func_set(adc_dev, temp_sensors[sensor].idx, + adc_error_func, NULL), + "adc_emul_value_func_set() failed (sensor %d)", sensor); +} + +/** Test if temp_sensor_read() returns temperature on success */ +ZTEST_USER(temp_sensor, test_temp_sensor_read) +{ + const struct device *adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); + int chan; + + zassert_not_null(adc_dev, "Cannot get ADC device"); + + /* Return error on all ADC channels */ + for (chan = 0; chan < ADC_CHANNELS_NUM; chan++) { + zassert_ok(adc_emul_value_func_set(adc_dev, chan, + adc_error_func, NULL), + "channel %d adc_emul_value_func_set() failed", chan); + } + + check_valid_temperature( + adc_dev, TEMP_SENSOR_ID(DT_NODELABEL(named_temp_charger))); + check_valid_temperature( + adc_dev, TEMP_SENSOR_ID(DT_NODELABEL(named_temp_ddr_soc))); + check_valid_temperature(adc_dev, + TEMP_SENSOR_ID(DT_NODELABEL(named_temp_fan))); + check_valid_temperature(adc_dev, TEMP_SENSOR_ID(DT_NODELABEL( + named_temp_pp3300_regulator))); + + /* Return correct value on all ADC channels */ + for (chan = 0; chan < ADC_CHANNELS_NUM; chan++) { + zassert_ok(adc_emul_const_value_set(adc_dev, chan, 1000), + "channel %d adc_emul_const_value_set() failed", + chan); + } +} + +static void *temp_sensor_setup(void) +{ + const struct device *dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_PG_EC_DSW_PWROK_PATH, gpios)); + const struct device *dev_pin = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_EC_PG_PIN_TEMP_PATH, gpios)); + + zassert_not_null(dev, NULL); + /* Before tests make sure that power pins are set. */ + zassert_ok(gpio_emul_input_set(dev, GPIO_PG_EC_DSW_PWROK_PORT, 1), + NULL); + zassert_ok(gpio_emul_input_set(dev_pin, GPIO_EC_PG_PIN_TEMP_PORT, 1), + NULL); + + return NULL; +} + +ZTEST_SUITE(temp_sensor, drivers_predicate_post_main, temp_sensor_setup, NULL, + NULL, NULL); diff --git a/zephyr/test/drivers/default/src/thermistor.c b/zephyr/test/drivers/default/src/thermistor.c new file mode 100644 index 0000000000..417b482d99 --- /dev/null +++ b/zephyr/test/drivers/default/src/thermistor.c @@ -0,0 +1,327 @@ +/* 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/adc.h> +#include <zephyr/drivers/adc/adc_emul.h> +#include <zephyr/drivers/gpio.h> +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <temp_sensor.h> + +#include "common.h" +#include "../driver/temp_sensor/thermistor.h" +#include "temp_sensor/temp_sensor.h" +#include "test/drivers/test_state.h" + +#define GPIO_PG_EC_DSW_PWROK_PATH DT_PATH(named_gpios, pg_ec_dsw_pwrok) +#define GPIO_PG_EC_DSW_PWROK_PORT DT_GPIO_PIN(GPIO_PG_EC_DSW_PWROK_PATH, gpios) + +#define GPIO_EC_PG_PIN_TEMP_PATH DT_PATH(named_gpios, ec_pg_pin_temp) +#define GPIO_EC_PG_PIN_TEMP_PORT DT_GPIO_PIN(GPIO_EC_PG_PIN_TEMP_PATH, gpios) + +#define ADC_DEVICE_NODE DT_NODELABEL(adc0) + +/* TODO replace counting macros with DT macro when + * https://github.com/zephyrproject-rtos/zephyr/issues/38715 lands + */ +#define _ACCUMULATOR(x) 1 + +#define NAMED_TEMP_SENSORS_SIZE \ + DT_FOREACH_CHILD(TEMP_SENSORS_NODEID, _ACCUMULATOR) 0 + +#define TEMP_SENSORS_ENABLED_SIZE FOREACH_TEMP_SENSOR(_ACCUMULATOR) 0 + +/* Conversion of temperature doesn't need to be 100% accurate */ +#define TEMP_EPS 2 + +#define A_VALID_VOLTAGE 1000 +/** + * Test if get temp function return expected error when ADC is not powered + * (indicated as GPIO pin set to low) and return success after powering on ADC. + */ +ZTEST_USER(thermistor, test_thermistor_power_pin) +{ + int temp; + int sensor_idx; + + const struct device *gpio_dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_PG_EC_DSW_PWROK_PATH, gpios)); + const struct device *adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); + + zassert_not_null(gpio_dev, "Cannot get GPIO device"); + zassert_not_null(adc_dev, "Cannot get ADC device"); + + /* Make sure that ADC return a valid value */ + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; + sensor_idx++) { + const struct temp_sensor_t *sensor = &temp_sensors[sensor_idx]; + + zassert_ok(adc_emul_const_value_set(adc_dev, sensor->idx, + A_VALID_VOLTAGE), + "adc_emul_value_func_set() failed on %s", + sensor->name); + } + + /* pg_ec_dsw_pwrok = 0 means ADC is not powered. */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_PG_EC_DSW_PWROK_PORT, 0), + NULL); + + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; + sensor_idx++) { + const struct temp_sensor_t *sensor = &temp_sensors[sensor_idx]; + + zassert_equal(EC_ERROR_NOT_POWERED, + sensor->zephyr_info->read(sensor, &temp), + "%s failed", sensor->name); + } + + /* pg_ec_dsw_pwrok = 1 means ADC is powered. */ + zassert_ok(gpio_emul_input_set(gpio_dev, GPIO_PG_EC_DSW_PWROK_PORT, 1), + NULL); + + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; + sensor_idx++) { + const struct temp_sensor_t *sensor = &temp_sensors[sensor_idx]; + + zassert_equal(EC_SUCCESS, + sensor->zephyr_info->read(sensor, &temp), + "%s failed", sensor->name); + } +} + +/* Simple ADC emulator custom function which always return error */ +static int adc_error_func(const struct device *dev, unsigned int channel, + void *param, uint32_t *result) +{ + return -EINVAL; +} + +/** Test if get temp function return expected error on ADC malfunction */ +ZTEST_USER(thermistor, test_thermistor_adc_read_error) +{ + int temp; + int sensor_idx; + + const struct device *adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); + + zassert_not_null(adc_dev, "Cannot get ADC device"); + + /* Return error on all ADC channels */ + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; + sensor_idx++) { + const struct temp_sensor_t *sensor = &temp_sensors[sensor_idx]; + + zassert_ok(adc_emul_value_func_set(adc_dev, sensor->idx, + adc_error_func, NULL), + "adc_emul_value_func_set() failed on %s", + sensor->name); + } + + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; + sensor_idx++) { + const struct temp_sensor_t *sensor = &temp_sensors[sensor_idx]; + + zassert_equal(EC_ERROR_UNKNOWN, + sensor->zephyr_info->read(sensor, &temp), + "%s failed", sensor->name); + } +} + +/** Get resistance of thermistor for given temperature */ +static int resistance_47kohm_B4050(int t) +{ + /* Thermistor manufacturer resistance lookup table*/ + int r_table[] = { + 155700, 147900, 140600, 133700, 127200, /* 0*C - 4*C */ + 121000, 115100, 109600, 104300, 99310, /* 5*C - 9*C */ + 94600, 90130, 85890, 81870, 78070, /* 10*C - 14*C */ + 74450, 71020, 67770, 64680, 61750, /* 15*C - 19*C */ + 58970, 56320, 53810, 51430, 49160, /* 20*C - 24*C */ + 47000, 44950, 42990, 41130, 39360, /* 25*C - 29*C */ + 37680, 36070, 34540, 33080, 31690, /* 30*C - 34*C */ + 30360, 29100, 27900, 26750, 25650, /* 35*C - 39*C */ + 24610, 23610, 22660, 21750, 20880, /* 40*C - 44*C */ + 20050, 19260, 18500, 17780, 17090, /* 45*C - 49*C */ + 16430, 15800, 15200, 14620, 14070, /* 50*C - 54*C */ + 13540, 13030, 12550, 12090, 11640, /* 55*C - 59*C */ + 11210, 10800, 10410, 10040, 9676, /* 60*C - 64*C */ + 9331, 8999, 8680, 8374, 8081, /* 65*C - 69*C */ + 7799, 7528, 7268, 7018, 6777, /* 70*C - 74*C */ + 6546, 6324, 6111, 5906, 5708, /* 75*C - 79*C */ + 5518, 5335, 5160, 4990, 4827, /* 80*C - 84*C */ + 4671, 4519, 4374, 4233, 4098, /* 85*C - 89*C */ + 3968, 3842, 3721, 3605, 3492, /* 90*C - 94*C */ + 3384, 3279, 3179, 3082, 2988, /* 95*C - 99*C */ + 2898 /* 100*C */ + }; + + t -= 273; + if (t < 0) + return r_table[0] + 10000; + + if (t >= ARRAY_SIZE(r_table)) + return r_table[ARRAY_SIZE(r_table) - 1] - 100; + + return r_table[t]; +} + +/** + * Calculate output voltage in voltage divider circuit using formula + * Vout = Vs * r2 / (r1 + r2) + */ +static int volt_divider(int vs, int r1, int r2) +{ + return vs * r2 / (r1 + r2); +} + +struct thermistor_state { + const int v; + const int r; + int temp_expected; +}; + +/** ADC emulator function which calculate output voltage for given thermistor */ +static int adc_temperature_func(const struct device *dev, unsigned int channel, + void *param, uint32_t *result) +{ + struct thermistor_state *s = (struct thermistor_state *)param; + + *result = volt_divider(s->v, s->r, + resistance_47kohm_B4050(s->temp_expected)); + + return 0; +} + +/** Test conversion from ADC raw value to temperature */ +static void do_thermistor_test(const struct temp_sensor_t *temp_sensor, + int reference_mv, int reference_ohms) +{ + int temp_expected; + int temp; + + const struct device *adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); + struct thermistor_state state = { + .v = reference_mv, + .r = reference_ohms, + }; + + zassert_not_null(adc_dev, "Cannot get ADC device"); + + /* Setup ADC channel */ + zassert_ok(adc_emul_value_func_set(adc_dev, temp_sensor->idx, + adc_temperature_func, &state), + "adc_emul_value_func_set() failed on %s", temp_sensor->name); + + /* Makes sure that reference voltage is correct for given thermistor */ + zassert_ok(adc_emul_ref_voltage_set(adc_dev, ADC_REF_INTERNAL, state.v), + "adc_emul_ref_voltage_set() failed %s on ", + temp_sensor->name); + + /* Test whole supported range from 0*C to 100*C (273*K to 373*K) */ + for (temp_expected = 273; temp_expected <= 373; temp_expected++) { + state.temp_expected = temp_expected; + zassert_equal(EC_SUCCESS, + temp_sensor->zephyr_info->read(temp_sensor, + &temp), + "failed on %s", temp_sensor->name); + zassert_within(temp_expected, temp, TEMP_EPS, + "Expected %d*K, got %d*K on %s", temp_expected, + temp, temp_sensor->name); + } + + /* Temperatures below 0*C should be reported as 0*C */ + state.temp_expected = -15 + 273; + zassert_equal(EC_SUCCESS, + temp_sensor->zephyr_info->read(temp_sensor, &temp), + "failed on %s", temp_sensor->name); + zassert_equal(273, temp, "Expected %d*K, got %d*K on %s", 273, temp, + temp_sensor->name); + + /* Temperatures above 100*C should be reported as 100*C */ + state.temp_expected = 115 + 273; + zassert_equal(EC_SUCCESS, + temp_sensor->zephyr_info->read(temp_sensor, &temp), + "failed on %s", temp_sensor->name); + zassert_equal(373, temp, "Expected %d*K, got %d*K on %s", 373, temp, + temp_sensor->name); +} + +#define GET_THERMISTOR_REF_MV(node_id) \ + [TEMP_SENSOR_ID_BY_DEV(node_id)] = DT_PROP( \ + DT_PHANDLE(node_id, thermistor), steinhart_reference_mv), + +#define GET_THERMISTOR_REF_RES(node_id) \ + [TEMP_SENSOR_ID_BY_DEV(node_id)] = DT_PROP( \ + DT_PHANDLE(node_id, thermistor), steinhart_reference_res), + +ZTEST_USER(thermistor, test_thermistors_adc_temperature_conversion) +{ + int sensor_idx; + + const static int reference_mv_arr[] = { DT_FOREACH_STATUS_OKAY( + THERMISTOR_COMPAT, GET_THERMISTOR_REF_MV) }; + const static int reference_res_arr[] = { DT_FOREACH_STATUS_OKAY( + THERMISTOR_COMPAT, GET_THERMISTOR_REF_RES) }; + + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; sensor_idx++) + do_thermistor_test(&temp_sensors[sensor_idx], + reference_mv_arr[sensor_idx], + reference_res_arr[sensor_idx]); +} + +ZTEST_USER(thermistor, test_device_nodes_enabled) +{ + zassert_equal(NAMED_TEMP_SENSORS_SIZE, TEMP_SENSORS_ENABLED_SIZE, + "Temperature sensors in device tree and " + "those enabled for test differ"); + + /* Thermistor nodes being enabled are already tested by compilation. */ +} + +static void *thermistor_setup(void) +{ + const struct device *dev = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_PG_EC_DSW_PWROK_PATH, gpios)); + const struct device *dev_pin = + DEVICE_DT_GET(DT_GPIO_CTLR(GPIO_EC_PG_PIN_TEMP_PATH, gpios)); + + zassert_not_null(dev, NULL); + /* Before tests make sure that power pins are set. */ + zassert_ok(gpio_emul_input_set(dev, GPIO_PG_EC_DSW_PWROK_PORT, 1), + NULL); + zassert_ok(gpio_emul_input_set(dev_pin, GPIO_EC_PG_PIN_TEMP_PORT, 1), + NULL); + + return NULL; +} + +static void thermistor_cleanup(void *state) +{ + int sensor_idx; + const struct device *adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); + + const static int reference_mv_arr[] = { DT_FOREACH_STATUS_OKAY( + THERMISTOR_COMPAT, GET_THERMISTOR_REF_MV) }; + const static int reference_res_arr[] = { DT_FOREACH_STATUS_OKAY( + THERMISTOR_COMPAT, GET_THERMISTOR_REF_RES) }; + + if (adc_dev == NULL) + TC_ERROR("Cannot get ADC device"); + + for (sensor_idx = 0; sensor_idx < NAMED_TEMP_SENSORS_SIZE; + sensor_idx++) { + /* Setup ADC to return 27*C (300K) which is reasonable value */ + adc_emul_const_value_set( + adc_dev, temp_sensors[sensor_idx].idx, + volt_divider(reference_mv_arr[sensor_idx], + reference_res_arr[sensor_idx], + resistance_47kohm_B4050(300))); + adc_emul_ref_voltage_set(adc_dev, ADC_REF_INTERNAL, + reference_mv_arr[sensor_idx]); + } +} + +ZTEST_SUITE(thermistor, drivers_predicate_post_main, thermistor_setup, NULL, + NULL, thermistor_cleanup); diff --git a/zephyr/test/drivers/default/src/uart_hostcmd.c b/zephyr/test/drivers/default/src/uart_hostcmd.c new file mode 100644 index 0000000000..879e734837 --- /dev/null +++ b/zephyr/test/drivers/default/src/uart_hostcmd.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/kernel.h> +#include <zephyr/ztest.h> + +#include "console.h" +#include "host_command.h" +#include "uart.h" +#include "test/drivers/test_state.h" + +/** Messages used in test */ +static const char msg1[] = "test"; +static const char msg2[] = "uart_hostcmd"; +static const char msg3[] = "message3"; + +/** Length of message excluding NULL char at the end */ +#define MSG_LEN(msg) (sizeof(msg) - 1) + +/** + * Write message 1 before first snapshot. Read everything from buffer. Create + * second snapshot. Write message 2 after it. + */ +static void setup_snapshots_and_messages(void *unused) +{ + char response[1024]; + struct host_cmd_handler_args read_args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_CONSOLE_READ, 0, response); + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_CONSOLE_SNAPSHOT, 0); + + /* Set first snapshot before first message */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + cputs(CC_COMMAND, msg1); + + /* Read everything from buffer */ + do { + /* Clear response size before executing command */ + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), + NULL); + } while (read_args.response_size != 0); + + /* Set second snapshot after first message */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + cputs(CC_COMMAND, msg2); +} + +/** + * Test if read next variant of console read host command is working. ver + * argument allows to change version of command. In case of version 1, parameter + * with sub command is provided. + */ +static void test_uart_hc_read_next(int ver) +{ + /* Should be able to read whole buffer in one command */ + char response[CONFIG_PLATFORM_EC_HOSTCMD_CONSOLE_BUF_SIZE + 1]; + struct ec_params_console_read_v1 params; + struct host_cmd_handler_args read_args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_CONSOLE_READ, ver, response); + struct host_cmd_handler_args snap_args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_CONSOLE_SNAPSHOT, 0); + char *msg1_start; + char *msg2_start; + char *msg3_start; + + /* Set up host command parameters */ + if (ver == 1) { + read_args.params = ¶ms; + read_args.params_size = sizeof(params); + params.subcmd = CONSOLE_READ_NEXT; + } + + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal('\0', response[read_args.response_size], + "Last byte of response is not '\\0' (got 0x%x)", + response[read_args.response_size]); + + /* + * Whole buffer until snapshot should be in response, check if it ends + * with message 1 which should start at the end of response excluding + * NULL char. + */ + msg1_start = response + read_args.response_size - 1 - MSG_LEN(msg1); + zassert_mem_equal(msg1, msg1_start, MSG_LEN(msg1), + "expected \"%s\", got \"%.*s\"", msg1, MSG_LEN(msg1), + msg1_start); + + /* Set new snapshot which should include message 2 */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&snap_args), NULL); + + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal('\0', response[read_args.response_size], + "Last byte of response is not '\\0' (got 0x%x)", + response[read_args.response_size]); + + /* + * Whole buffer should be in response, check if it ends with both + * messages. Message 2 should start at the end of response excluding + * NULL char. + */ + msg2_start = response + read_args.response_size - 1 - MSG_LEN(msg2); + msg1_start = msg2_start - MSG_LEN(msg1); + zassert_mem_equal(msg2, msg2_start, MSG_LEN(msg2), + "expected \"%s\", got \"%.*s\"", msg2, MSG_LEN(msg2), + msg2_start); + zassert_mem_equal(msg1, msg1_start, MSG_LEN(msg1), + "expected \"%s\", got \"%.*s\"", msg1, MSG_LEN(msg1), + msg1_start); + + /* Append third message */ + cputs(CC_COMMAND, msg3); + + /* Check read next without new snapshot, no data should be read */ + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal(0, read_args.response_size, + "expected message length 0, got %d", + read_args.response_size); + + /* Set new snapshot which should include message 3 */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&snap_args), NULL); + + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal('\0', response[read_args.response_size], + "Last byte of response is not '\\0' (got 0x%x)", + response[read_args.response_size]); + + msg3_start = response + read_args.response_size - 1 - MSG_LEN(msg3); + msg2_start = msg3_start - MSG_LEN(msg2); + msg1_start = msg2_start - MSG_LEN(msg1); + zassert_mem_equal(msg3, msg3_start, MSG_LEN(msg3), + "expected \"%s\", got \"%.*s\"", msg3, MSG_LEN(msg3), + msg3_start); + zassert_mem_equal(msg2, msg2_start, MSG_LEN(msg2), + "expected \"%s\", got \"%.*s\"", msg2, MSG_LEN(msg2), + msg2_start); + zassert_mem_equal(msg1, msg1_start, MSG_LEN(msg1), + "expected \"%s\", got \"%.*s\"", msg1, MSG_LEN(msg1), + msg1_start); +} + +ZTEST_USER(uart_hostcmd, test_uart_hc_read_next_v0) +{ + test_uart_hc_read_next(0); +} + +ZTEST_USER(uart_hostcmd, test_uart_hc_read_next_v1) +{ + test_uart_hc_read_next(1); +} + +/** Test if read recent variant of console read host command is working */ +ZTEST_USER(uart_hostcmd, test_uart_hc_read_recent_v1) +{ + /* Should be able to read whole buffer in one command */ + char response[CONFIG_PLATFORM_EC_HOSTCMD_CONSOLE_BUF_SIZE + 1]; + struct ec_params_console_read_v1 params; + struct host_cmd_handler_args read_args = + BUILD_HOST_COMMAND(EC_CMD_CONSOLE_READ, 1, response, params); + struct host_cmd_handler_args snap_args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_CONSOLE_SNAPSHOT, 0); + + params.subcmd = CONSOLE_READ_RECENT; + + /* Only message 1 which is between two last snapshots should be read */ + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal('\0', response[read_args.response_size], + "Last byte of response is not '\\0' (got 0x%x)", + response[read_args.response_size]); + /* Account additional NULL char at the end */ + zassert_equal(MSG_LEN(msg1) + 1, read_args.response_size, + "expected message length %d, got %d", MSG_LEN(msg1) + 1, + read_args.response_size); + zassert_mem_equal(msg1, response, MSG_LEN(msg1), + "expected \"%s\", got \"%.*s\"", msg1, MSG_LEN(msg1), + response); + + /* Set new snapshot after second message */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&snap_args), NULL); + + /* Only message between two last snapshots should be read */ + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal('\0', response[read_args.response_size], + "Last byte of response is not '\\0' (got 0x%x)", + response[read_args.response_size]); + /* Account additional NULL char at the end */ + zassert_equal(MSG_LEN(msg2) + 1, read_args.response_size, + "expected message length %d, got %d", MSG_LEN(msg2) + 1, + read_args.response_size); + zassert_mem_equal(msg2, response, MSG_LEN(msg2), + "expected \"%s\", got \"%.*s\"", msg2, MSG_LEN(msg2), + response); + + /* Append third message */ + cputs(CC_COMMAND, msg3); + + /* Check that message is not read without setting snapshot */ + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal(0, read_args.response_size, + "expected message length 0, got %d", + read_args.response_size); + + /* Set new snapshot */ + zassert_equal(EC_RES_SUCCESS, host_command_process(&snap_args), NULL); + + /* This time only third message should be read */ + read_args.response_size = 0; + zassert_equal(EC_RES_SUCCESS, host_command_process(&read_args), NULL); + zassert_equal('\0', response[read_args.response_size], + "Last byte of response is not '\\0' (got 0x%x)", + response[read_args.response_size]); + /* Account additional NULL char at the end */ + zassert_equal(MSG_LEN(msg3) + 1, read_args.response_size, + "expected message length %d, got %d", MSG_LEN(msg3) + 1, + read_args.response_size); + zassert_mem_equal(msg3, response, MSG_LEN(msg3), + "expected \"%s\", got \"%.*s\"", msg3, MSG_LEN(msg3), + response); +} + +ZTEST_SUITE(uart_hostcmd, drivers_predicate_post_main, NULL, + setup_snapshots_and_messages, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/usb_mux.c b/zephyr/test/drivers/default/src/usb_mux.c new file mode 100644 index 0000000000..45b81d6ea5 --- /dev/null +++ b/zephyr/test/drivers/default/src/usb_mux.c @@ -0,0 +1,912 @@ +/* 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.h> +#include <zephyr/drivers/gpio/gpio_emul.h> +#include <zephyr/shell/shell.h> +#include <zephyr/shell/shell_uart.h> + +#include "common.h" +#include "ec_commands.h" +#include "ec_tasks.h" +#include <zephyr/fff.h> +#include "hooks.h" +#include "host_command.h" +#include "i2c.h" +#include "test/drivers/stubs.h" +#include "task.h" +#include "tcpm/ps8xxx_public.h" +#include "tcpm/tcpci.h" +#include "usb_prl_sm.h" +#include "usb_tc_sm.h" + +#include "usb_mux.h" +#include "test/drivers/test_state.h" +#include "test/drivers/utils.h" + +/** Copy of original usb_muxes[USB_PORT_C1] */ +struct usb_mux_chain usb_mux_c1; + +/** Number of usb mux proxies in chain */ +#define NUM_OF_PROXY 3 + +/** Pointers to original usb muxes chain of port c1 */ +const struct usb_mux *org_mux[NUM_OF_PROXY]; + +/** Proxy function which check calls from usb_mux framework to driver */ +FAKE_VALUE_FUNC(int, proxy_init, const struct usb_mux *); +static int proxy_init_custom(const struct usb_mux *me) +{ + int i = me->i2c_addr_flags; + int ec = EC_SUCCESS; + + zassert_true(i < NUM_OF_PROXY, "Proxy called for non proxy usb_mux"); + + if (org_mux[i] != NULL && org_mux[i]->driver->init != NULL) { + ec = org_mux[i]->driver->init(org_mux[i]); + } + + if (task_get_current() == TASK_ID_TEST_RUNNER) { + RETURN_FAKE_RESULT(proxy_init); + } + + /* Discard this call if made from different thread */ + proxy_init_fake.call_count--; + + return ec; +} + +/** Proxy function which check calls from usb_mux framework to driver */ +FAKE_VALUE_FUNC(int, proxy_set, const struct usb_mux *, mux_state_t, bool *); +static int proxy_set_custom(const struct usb_mux *me, mux_state_t mux_state, + bool *ack_required) +{ + int i = me->i2c_addr_flags; + int ec = EC_SUCCESS; + + zassert_true(i < NUM_OF_PROXY, "Proxy called for non proxy usb_mux"); + + if (org_mux[i] != NULL && org_mux[i]->driver->set != NULL) { + ec = org_mux[i]->driver->set(org_mux[i], mux_state, + ack_required); + /* Disable waiting for ACK in tests */ + *ack_required = false; + } + + if (task_get_current() == TASK_ID_TEST_RUNNER) { + RETURN_FAKE_RESULT(proxy_set); + } + + /* Discard this call if made from different thread */ + proxy_set_fake.call_count--; + + return ec; +} + +/** Proxy function which check calls from usb_mux framework to driver */ +FAKE_VALUE_FUNC(int, proxy_get, const struct usb_mux *, mux_state_t *); +/** Sequence of mux_state values returned by proxy_get function */ +static mux_state_t proxy_get_mux_state_seq[NUM_OF_PROXY]; +/** Index of next mux_state to return from proxy_get_function */ +static int proxy_get_mux_state_seq_idx; +/** Set all mux_state in sequence to the same state value */ +static void set_proxy_get_mux_state_seq(mux_state_t state) +{ + proxy_get_mux_state_seq_idx = 0; + for (int i = 0; i < NUM_OF_PROXY; i++) { + proxy_get_mux_state_seq[i] = state; + } +} + +static int proxy_get_custom(const struct usb_mux *me, mux_state_t *mux_state) +{ + int i = me->i2c_addr_flags; + int ec = EC_SUCCESS; + + zassert_true(i < NUM_OF_PROXY, "Proxy called for non proxy usb_mux"); + + if (org_mux[i] != NULL && org_mux[i]->driver->get != NULL) { + ec = org_mux[i]->driver->get(org_mux[i], mux_state); + } + + if (task_get_current() == TASK_ID_TEST_RUNNER) { + zassert_true(proxy_get_mux_state_seq_idx < NUM_OF_PROXY, + "%s called too many times without resetting " + "mux_state_seq", + __func__); + *mux_state = + proxy_get_mux_state_seq[proxy_get_mux_state_seq_idx]; + proxy_get_mux_state_seq_idx++; + RETURN_FAKE_RESULT(proxy_get); + } + + /* Discard this call if made from different thread */ + proxy_get_fake.call_count--; + + return ec; +} + +/** Proxy function which check calls from usb_mux framework to driver */ +FAKE_VALUE_FUNC(int, proxy_enter_low_power_mode, const struct usb_mux *); +static int proxy_enter_low_power_mode_custom(const struct usb_mux *me) +{ + int i = me->i2c_addr_flags; + int ec = EC_SUCCESS; + + zassert_true(i < NUM_OF_PROXY, "Proxy called for non proxy usb_mux"); + + if (org_mux[i] != NULL && + org_mux[i]->driver->enter_low_power_mode != NULL) { + ec = org_mux[i]->driver->enter_low_power_mode(org_mux[i]); + } + + if (task_get_current() == TASK_ID_TEST_RUNNER) { + RETURN_FAKE_RESULT(proxy_enter_low_power_mode); + } + + /* Discard this call if made from different thread */ + proxy_enter_low_power_mode_fake.call_count--; + + return ec; +} + +/** Proxy function which check calls from usb_mux framework to driver */ +FAKE_VALUE_FUNC(int, proxy_chipset_reset, const struct usb_mux *); +static int proxy_chipset_reset_custom(const struct usb_mux *me) +{ + int i = me->i2c_addr_flags; + int ec = EC_SUCCESS; + + zassert_true(i < NUM_OF_PROXY, "Proxy called for non proxy usb_mux"); + + if (org_mux[i] != NULL && org_mux[i]->driver->chipset_reset != NULL) { + ec = org_mux[i]->driver->chipset_reset(org_mux[i]); + } + + if (task_get_current() == TASK_ID_TEST_RUNNER) { + RETURN_FAKE_RESULT(proxy_chipset_reset); + } + + /* Discard this call if made from different thread */ + proxy_chipset_reset_fake.call_count--; + + return ec; +} + +/** Proxy function for fw update capability */ +static bool proxy_fw_update_cap(void) +{ + return true; +} + +/** Proxy function which check calls from usb_mux framework to driver */ +FAKE_VOID_FUNC(proxy_hpd_update, const struct usb_mux *, mux_state_t, bool *); +static void proxy_hpd_update_custom(const struct usb_mux *me, + mux_state_t mux_state, bool *ack_required) +{ + int i = me->i2c_addr_flags; + + zassert_true(i < NUM_OF_PROXY, "Proxy called for non proxy usb_mux"); + + if (org_mux[i] != NULL && org_mux[i]->hpd_update != NULL) { + org_mux[i]->hpd_update(org_mux[i], mux_state, ack_required); + /* Disable waiting for ACK in tests */ + *ack_required = false; + } + + if (task_get_current() != TASK_ID_TEST_RUNNER) { + /* Discard this call if made from different thread */ + proxy_hpd_update_fake.call_count--; + } +} + +/** Usb mux driver with proxy functions */ +const struct usb_mux_driver proxy_usb_mux = { + .init = &proxy_init, + .set = &proxy_set, + .get = &proxy_get, + .enter_low_power_mode = &proxy_enter_low_power_mode, + .chipset_reset = &proxy_chipset_reset, + .is_retimer_fw_update_capable = &proxy_fw_update_cap, +}; + +/** Mock function used in init test */ +FAKE_VALUE_FUNC(int, mock_board_init, const struct usb_mux *); +static int mock_board_init_custom(const struct usb_mux *me) +{ + if (task_get_current() == TASK_ID_TEST_RUNNER) { + RETURN_FAKE_RESULT(mock_board_init); + } + + /* Discard this call if made from different thread */ + mock_board_init_fake.call_count--; + + return EC_SUCCESS; +} + +/** Mock function used in set test */ +FAKE_VALUE_FUNC(int, mock_board_set, const struct usb_mux *, mux_state_t); +static int mock_board_set_custom(const struct usb_mux *me, + mux_state_t mux_state) +{ + if (task_get_current() == TASK_ID_TEST_RUNNER) { + RETURN_FAKE_RESULT(mock_board_set); + } + + /* Discard this call if made from different thread */ + mock_board_set_fake.call_count--; + + return EC_SUCCESS; +} + +/** + * Reset state of all fake functions, setup custom fake functions and set + * default return value to EC_SUCCESS (all functions which has return value) + */ +static void reset_proxy_fakes(void) +{ + RESET_FAKE(proxy_init); + RESET_FAKE(proxy_set); + RESET_FAKE(proxy_get); + RESET_FAKE(proxy_enter_low_power_mode); + RESET_FAKE(proxy_chipset_reset); + RESET_FAKE(proxy_hpd_update); + RESET_FAKE(mock_board_init); + RESET_FAKE(mock_board_set); + + /* Setup proxy functions */ + proxy_init_fake.custom_fake = proxy_init_custom; + proxy_set_fake.custom_fake = proxy_set_custom; + proxy_get_fake.custom_fake = proxy_get_custom; + proxy_enter_low_power_mode_fake.custom_fake = + proxy_enter_low_power_mode_custom; + proxy_chipset_reset_fake.custom_fake = proxy_chipset_reset_custom; + proxy_hpd_update_fake.custom_fake = proxy_hpd_update_custom; + mock_board_init_fake.custom_fake = mock_board_init_custom; + mock_board_set_fake.custom_fake = mock_board_set_custom; + + /* Set default return value */ + proxy_init_fake.return_val = EC_SUCCESS; + proxy_set_fake.return_val = EC_SUCCESS; + proxy_get_fake.return_val = EC_SUCCESS; + proxy_enter_low_power_mode_fake.return_val = EC_SUCCESS; + proxy_chipset_reset_fake.return_val = EC_SUCCESS; + mock_board_init_fake.return_val = EC_SUCCESS; + mock_board_set_fake.return_val = EC_SUCCESS; +} + +/** Chain of 3 proxy usb muxes */ +struct usb_mux proxy_mux_2 = { + .usb_port = USBC_PORT_C1, + .driver = &proxy_usb_mux, + .i2c_addr_flags = 2, + .hpd_update = &proxy_hpd_update, +}; + +struct usb_mux_chain proxy_chain_2 = { + .mux = &proxy_mux_2, +}; + +struct usb_mux proxy_mux_1 = { + .usb_port = USBC_PORT_C1, + .driver = &proxy_usb_mux, + .i2c_addr_flags = 1, + .hpd_update = &proxy_hpd_update, +}; + +struct usb_mux_chain proxy_chain_1 = { + .mux = &proxy_mux_1, + .next = &proxy_chain_2, +}; + +struct usb_mux proxy_mux_0 = { + .usb_port = USBC_PORT_C1, + .driver = &proxy_usb_mux, + .i2c_addr_flags = 0, + .hpd_update = &proxy_hpd_update, +}; + +struct usb_mux_chain proxy_chain_0 = { + .mux = &proxy_mux_0, + .next = &proxy_chain_1, +}; + +/** Setup first 3 usb muxes of port 1 with proxy */ +static void setup_usb_mux_proxy_chain(void) +{ + const struct usb_mux_chain *t; + int i; + + memcpy(&usb_mux_c1, &usb_muxes[USBC_PORT_C1], + sizeof(struct usb_mux_chain)); + memcpy(&usb_muxes[USBC_PORT_C1], &proxy_chain_0, + sizeof(struct usb_mux_chain)); + + /* + * Setup org_mux array to point real driver which should be called by + * each proxy + */ + t = &usb_mux_c1; + for (i = 0; i < NUM_OF_PROXY; i++) { + if (t != NULL) { + org_mux[i] = t->mux; + t = t->next; + } else { + org_mux[i] = NULL; + } + } + + if (t != NULL) { + proxy_chain_2.next = t; + } else { + proxy_chain_2.next = NULL; + } +} + +/** Restore original usb_mux chain without proxy */ +static void restore_usb_mux_chain(void) +{ + memcpy(&usb_muxes[USBC_PORT_C1], &usb_mux_c1, + sizeof(struct usb_mux_chain)); + + /* Reset flags to default */ + proxy_mux_0.flags = 0; + proxy_mux_1.flags = 0; + proxy_mux_2.flags = 0; +} + +/** + * Check if given proxy function was called num times and if first argument was + * pointer to the right proxy chain element. First argument is + * const struct usb_mux * for all struct usb_mux_driver callbacks. + */ +#define CHECK_PROXY_FAKE_CALL_CNT(proxy, num) \ + do { \ + zassert_equal(num, proxy##_fake.call_count, "%d != %d", num, \ + proxy##_fake.call_count); \ + if (num >= 1) { \ + zassert_equal(usb_muxes[USBC_PORT_C1].mux, \ + proxy##_fake.arg0_history[0], NULL); \ + } \ + if (num >= 2) { \ + zassert_equal(proxy_chain_1.mux, \ + proxy##_fake.arg0_history[1], NULL); \ + } \ + if (num >= 3) { \ + zassert_equal(proxy_chain_2.mux, \ + proxy##_fake.arg0_history[2], NULL); \ + } \ + } while (0) + +/** + * Do the same thing as CHECK_PROXY_FAKE_CALL_CNT and check if second argument + * was the same as given state. hpd_update and set callback have mux_state_t + * as second argument. + */ +#define CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy, num, state) \ + do { \ + CHECK_PROXY_FAKE_CALL_CNT(proxy, num); \ + if (num >= 1) { \ + zassert_equal(state, proxy##_fake.arg1_history[0], \ + "0x%x != 0x%x", state, \ + proxy##_fake.arg1_history[0]); \ + } \ + if (num >= 2) { \ + zassert_equal(state, proxy##_fake.arg1_history[1], \ + "0x%x != 0x%x", state, \ + proxy##_fake.arg1_history[1]); \ + } \ + if (num >= 3) { \ + zassert_equal(state, proxy##_fake.arg1_history[2], \ + "0x%x != 0x%x", state, \ + proxy##_fake.arg1_history[2]); \ + } \ + } while (0) + +/** Test usb_mux init */ +ZTEST(usb_uninit_mux, test_usb_mux_init) +{ + int fail_on_2nd_ret[] = { EC_SUCCESS, EC_ERROR_NOT_POWERED }; + + /* Set AP to normal state to init BB retimer */ + test_set_chipset_to_s0(); + + /* Test successful initialisation */ + usb_mux_init(USBC_PORT_C1); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + + /* + * Test failed initialisation. Muxes that are in chain after + * the one which fails shouldn't be called + */ + reset_proxy_fakes(); + SET_RETURN_SEQ(proxy_init, fail_on_2nd_ret, 2); + usb_mux_init(USBC_PORT_C1); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 2); + + /* Test board init callback */ + proxy_mux_1.board_init = &mock_board_init; + reset_proxy_fakes(); + usb_mux_init(USBC_PORT_C1); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + /* Check if board_init was called for proxy 1 */ + zassert_equal(1, mock_board_init_fake.call_count, NULL); + zassert_equal(proxy_chain_1.mux, mock_board_init_fake.arg0_history[0], + NULL); + + proxy_mux_1.board_init = NULL; +} + +/** Test usb_mux setting mux mode */ +ZTEST(usb_uninit_mux, test_usb_mux_set) +{ + int fail_on_2nd_ret[] = { EC_SUCCESS, EC_ERROR_UNKNOWN }; + mux_state_t exp_mode; + + /* Set flag for usb mux 1 to disable polarity setting */ + proxy_mux_1.flags = USB_MUX_FLAG_SET_WITHOUT_FLIP; + + /* Test setting mux mode without polarity inversion */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + /* All muxes should have the same mode */ + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Test setting mux mode with polarity inversion */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_TBT_COMPAT_ENABLED; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 1 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT(proxy_set, NUM_OF_PROXY); + /* usb mux 1 shouldn't be set with polarity mode, because of flag */ + zassert_equal(exp_mode | USB_PD_MUX_POLARITY_INVERTED, + proxy_set_fake.arg1_history[0], NULL); + zassert_equal(exp_mode, proxy_set_fake.arg1_history[1], NULL); + zassert_equal(exp_mode | USB_PD_MUX_POLARITY_INVERTED, + proxy_set_fake.arg1_history[2], NULL); + + /* Test board set callback */ + reset_proxy_fakes(); + proxy_mux_1.board_set = &mock_board_set; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + /* Check if board_set was called for proxy 1 */ + zassert_equal(1, mock_board_set_fake.call_count, NULL); + zassert_equal(proxy_chain_1.mux, mock_board_set_fake.arg0_history[0], + NULL); + zassert_equal(exp_mode, mock_board_set_fake.arg1_history[0], NULL); + + /* Test set function with error in usb_mux */ + reset_proxy_fakes(); + SET_RETURN_SEQ(proxy_set, fail_on_2nd_ret, 2); + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, 2, exp_mode); + /* board_set shouldn't be called after fail */ + zassert_equal(0, mock_board_set_fake.call_count, NULL); + + proxy_mux_1.board_set = NULL; +} + +/** Test usb_mux reset in g3 when required flag is set */ +ZTEST(usb_uninit_mux, test_usb_mux_reset_in_g3) +{ + mux_state_t exp_mode = USB_PD_MUX_USB_ENABLED; + + /* Test that init is called */ + reset_proxy_fakes(); + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Usb muxes of port 1 should stay initialised */ + proxy_mux_0.flags = 0; + hook_notify(HOOK_CHIPSET_HARD_OFF); + + /* Test that init is not called */ + reset_proxy_fakes(); + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); +} + +/** Test usb_mux getting mux mode */ +ZTEST(usb_uninit_mux, test_usb_mux_get) +{ + int fail_on_2nd_ret[] = { EC_SUCCESS, EC_ERROR_UNKNOWN }; + mux_state_t exp_mode, mode; + + /* Test getting mux mode */ + exp_mode = USB_PD_MUX_USB_ENABLED; + set_proxy_get_mux_state_seq(exp_mode); + mode = usb_mux_get(USBC_PORT_C1); + zassert_equal(exp_mode, mode, "mode is 0x%x (!= 0x%x)", mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + + /* Test getting mux mode with inverted polarisation in one mux */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_TBT_COMPAT_ENABLED; + set_proxy_get_mux_state_seq(exp_mode); + /* Set polarisation in usb mux 1 state */ + proxy_get_mux_state_seq[1] |= USB_PD_MUX_POLARITY_INVERTED; + exp_mode |= USB_PD_MUX_POLARITY_INVERTED; + mode = usb_mux_get(USBC_PORT_C1); + zassert_equal(exp_mode, mode, "mode is 0x%x (!= 0x%x)", mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + + /* Test get function with error in usb_mux */ + reset_proxy_fakes(); + SET_RETURN_SEQ(proxy_get, fail_on_2nd_ret, 2); + set_proxy_get_mux_state_seq(USB_PD_MUX_TBT_COMPAT_ENABLED); + exp_mode = USB_PD_MUX_NONE; + mode = usb_mux_get(USBC_PORT_C1); + zassert_equal(exp_mode, mode, "mode is 0x%x (!= 0x%x)", mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, 2); +} + +/** Test usb_mux entering and exiting low power mode */ +ZTEST(usb_init_mux, test_usb_mux_low_power_mode) +{ + int fail_on_2nd_ret[] = { EC_SUCCESS, EC_ERROR_NOT_POWERED }; + mux_state_t exp_mode, mode; + + /* Test enter to low power mode */ + exp_mode = USB_PD_MUX_NONE; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_DISCONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_enter_low_power_mode, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Test that nothing is changed when already in low power mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_NONE; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_DISCONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_enter_low_power_mode, 0); + CHECK_PROXY_FAKE_CALL_CNT(proxy_set, 0); + + /* Test that get return USB_PD_MUX_NONE in low power mode */ + exp_mode = USB_PD_MUX_NONE; + mode = usb_mux_get(USBC_PORT_C1); + zassert_equal(exp_mode, mode, "mode is 0x%x (!= 0x%x)", mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, 0); + + /* Test exiting from low power mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Test exiting from lpm, when init end with EC_ERROR_NOT_POWERED */ + reset_proxy_fakes(); + SET_RETURN_SEQ(proxy_init, fail_on_2nd_ret, 2); + usb_mux_init(USBC_PORT_C1); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 2); + + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Test enter to low power mode with polarity */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_NONE; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_DISCONNECT, + 1 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_enter_low_power_mode, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Test that nothing is changed on lpm exit error */ + reset_proxy_fakes(); + SET_RETURN_SEQ(proxy_init, fail_on_2nd_ret, 2); + exp_mode = USB_PD_MUX_USB_ENABLED; + usb_mux_set(USBC_PORT_C1, exp_mode, USB_SWITCH_CONNECT, + 0 /* = polarity */); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 2); + CHECK_PROXY_FAKE_CALL_CNT(proxy_set, 0); +} + +/** Test usb_mux flip */ +ZTEST(usb_uninit_mux, test_usb_mux_flip) +{ + mux_state_t exp_mode; + + /* Set flag for usb mux 1 to disable polarity setting */ + proxy_mux_1.flags = USB_MUX_FLAG_SET_WITHOUT_FLIP; + + /* Test flip port without polarity inverted */ + exp_mode = USB_PD_MUX_USB_ENABLED; + set_proxy_get_mux_state_seq(exp_mode); + usb_mux_flip(USBC_PORT_C1); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT(proxy_set, NUM_OF_PROXY); + /* usb mux 1 shouldn't be set with polarity mode, because of flag */ + zassert_equal(exp_mode | USB_PD_MUX_POLARITY_INVERTED, + proxy_set_fake.arg1_history[0], NULL); + zassert_equal(exp_mode, proxy_set_fake.arg1_history[1], NULL); + zassert_equal(exp_mode | USB_PD_MUX_POLARITY_INVERTED, + proxy_set_fake.arg1_history[2], NULL); + + /* Test flip port with polarity inverted */ + reset_proxy_fakes(); + exp_mode |= USB_PD_MUX_POLARITY_INVERTED; + set_proxy_get_mux_state_seq(exp_mode); + /* Clear polarity bit from usb mux 1 */ + proxy_get_mux_state_seq[1] &= ~USB_PD_MUX_POLARITY_INVERTED; + exp_mode &= ~USB_PD_MUX_POLARITY_INVERTED; + usb_mux_flip(USBC_PORT_C1); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); +} + +ZTEST(usb_uninit_mux, test_usb_mux_hpd_update) +{ + mux_state_t exp_mode, mode, virt_mode; + + /* Get current state of virtual usb mux and set mock */ + usbc1_virtual_usb_mux.driver->get(&usbc1_virtual_usb_mux, &virt_mode); + + /* Test no hpd level and no irq */ + exp_mode = virt_mode; + usb_mux_hpd_update(USBC_PORT_C1, exp_mode); + /* Check if virtual usb mux mode is updated correctly */ + usbc1_virtual_usb_mux.driver->get(&usbc1_virtual_usb_mux, &mode); + zassert_equal(exp_mode, mode, "virtual mux mode is 0x%x (!= 0x%x)", + mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_hpd_update, NUM_OF_PROXY, + exp_mode); + + /* Test hpd level and irq */ + reset_proxy_fakes(); + exp_mode = virt_mode | USB_PD_MUX_HPD_LVL | USB_PD_MUX_HPD_IRQ; + usb_mux_hpd_update(USBC_PORT_C1, exp_mode); + /* Check if virtual usb mux mode is updated correctly */ + usbc1_virtual_usb_mux.driver->get(&usbc1_virtual_usb_mux, &mode); + zassert_equal(exp_mode, mode, "virtual mux mode is 0x%x (!= 0x%x)", + mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_hpd_update, NUM_OF_PROXY, + exp_mode); + + /* Test no hpd level and irq */ + reset_proxy_fakes(); + exp_mode = virt_mode | USB_PD_MUX_HPD_IRQ; + usb_mux_hpd_update(USBC_PORT_C1, exp_mode); + /* Check if virtual usb mux mode is updated correctly */ + usbc1_virtual_usb_mux.driver->get(&usbc1_virtual_usb_mux, &mode); + zassert_equal(exp_mode, mode, "virtual mux mode is 0x%x (!= 0x%x)", + mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_hpd_update, NUM_OF_PROXY, + exp_mode); + + /* Test hpd level and no irq */ + reset_proxy_fakes(); + exp_mode = virt_mode | USB_PD_MUX_HPD_LVL; + usb_mux_hpd_update(USBC_PORT_C1, exp_mode); + /* Check if virtual usb mux mode is updated correctly */ + usbc1_virtual_usb_mux.driver->get(&usbc1_virtual_usb_mux, &mode); + zassert_equal(exp_mode, mode, "virtual mux mode is 0x%x (!= 0x%x)", + mode, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, 0); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_hpd_update, NUM_OF_PROXY, + exp_mode); + + /* Test ps8xxx hpd update */ + proxy_mux_0.usb_port = 1; + proxy_mux_0.driver = &tcpci_tcpm_usb_mux_driver; + proxy_mux_0.hpd_update = &ps8xxx_tcpc_update_hpd_status; + + reset_proxy_fakes(); + exp_mode = virt_mode | USB_PD_MUX_HPD_LVL | USB_PD_MUX_HPD_IRQ; + usb_mux_hpd_update(USBC_PORT_C1, exp_mode); + /* Check if PS8xxx mux mode is updated correctly */ + tcpci_tcpm_usb_mux_driver.get(usb_muxes[USBC_PORT_C1].mux, &mode); + + /* Restore proxy chain 0 */ + proxy_mux_0.usb_port = USBC_PORT_C1; + proxy_mux_0.driver = &proxy_usb_mux; + proxy_mux_0.hpd_update = &proxy_hpd_update; + + zassert_equal(0, mode, "mux mode is 0x%x (!= 0x%x)", mode, 0); +} + +ZTEST(usb_init_mux, test_usb_mux_fw_update_port_info) +{ + int port_info; + + port_info = usb_mux_retimer_fw_update_port_info(); + zassert_true(port_info & BIT(USBC_PORT_C1), + "fw update for port C1 should be set"); +} + +ZTEST(usb_init_mux, test_usb_mux_chipset_reset) +{ + /* After this hook chipset reset functions should be called */ + hook_notify(HOOK_CHIPSET_RESET); + CHECK_PROXY_FAKE_CALL_CNT(proxy_chipset_reset, NUM_OF_PROXY); +} + +/* Test host command get mux info */ +ZTEST(usb_init_mux, test_usb_mux_hc_mux_info) +{ + struct ec_response_usb_pd_mux_info response; + struct ec_params_usb_pd_mux_info params; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_USB_PD_MUX_INFO, 0, response, params); + mux_state_t exp_mode; + + /* Test invalid port parameter */ + params.port = 5; + zassert_equal(EC_RES_INVALID_PARAM, host_command_process(&args), NULL); + + /* Set correct port for rest of the test */ + params.port = USBC_PORT_C1; + + /* Test error on getting mux mode */ + set_proxy_get_mux_state_seq(USB_PD_MUX_USB_ENABLED); + proxy_get_fake.return_val = EC_ERROR_UNKNOWN; + zassert_equal(EC_RES_ERROR, host_command_process(&args), NULL); + + /* Test getting mux mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED; + set_proxy_get_mux_state_seq(exp_mode); + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(exp_mode, response.flags, "mode is 0x%x (!= 0x%x)", + response.flags, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + + /* Test clearing HPD IRQ */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED | USB_PD_MUX_HPD_LVL | + USB_PD_MUX_HPD_IRQ; + set_proxy_get_mux_state_seq(exp_mode); + zassert_equal(EC_RES_SUCCESS, host_command_process(&args), NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(exp_mode, response.flags, "mode is 0x%x (!= 0x%x)", + response.flags, exp_mode); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_hpd_update, NUM_OF_PROXY, + USB_PD_MUX_HPD_LVL); +} + +/** Test typec console command */ +ZTEST(usb_init_mux, test_usb_mux_typec_command) +{ + mux_state_t polarity; + mux_state_t exp_mode; + + /* Test error on command with no argument */ + zassert_equal(EC_ERROR_PARAM_COUNT, + shell_execute_cmd(get_ec_shell(), "typec"), NULL); + + /* + * Test success on passing "debug" as first argument. This will enable + * debug prints, but it is not possible to test that in unit test + * without accessing cprints output. + */ + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "typec debug"), NULL); + + /* Test error on port argument that is not a number */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "typec test1"), NULL); + + /* Test error on invalid port number */ + zassert_equal(EC_ERROR_PARAM1, + shell_execute_cmd(get_ec_shell(), "typec 5"), NULL); + + /* + * Test success on correct port number. Command should print mux state + * on console, but it is not possible to check that in unit test. + */ + set_proxy_get_mux_state_seq(USB_PD_MUX_TBT_COMPAT_ENABLED); + zassert_equal(EC_SUCCESS, shell_execute_cmd(get_ec_shell(), "typec 1"), + NULL); + CHECK_PROXY_FAKE_CALL_CNT(proxy_get, NUM_OF_PROXY); + + /* Test setting none mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_NONE; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "typec 1 none"), NULL); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + /* Mux will enter low power mode */ + CHECK_PROXY_FAKE_CALL_CNT(proxy_enter_low_power_mode, NUM_OF_PROXY); + + /* Polarity is set based on PD */ + polarity = polarity_rm_dts(pd_get_polarity(USBC_PORT_C1)) ? + USB_PD_MUX_POLARITY_INVERTED : + 0; + + /* Test setting USB mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED | polarity; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "typec 1 usb"), NULL); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + /* Mux will exit low power mode */ + CHECK_PROXY_FAKE_CALL_CNT(proxy_init, NUM_OF_PROXY); + + /* Test setting DP mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_DP_ENABLED | polarity; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "typec 1 dp"), NULL); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); + + /* Test setting dock mode */ + reset_proxy_fakes(); + exp_mode = USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED | polarity; + zassert_equal(EC_SUCCESS, + shell_execute_cmd(get_ec_shell(), "typec 1 dock"), NULL); + CHECK_PROXY_FAKE_CALL_CNT_MUX_STATE(proxy_set, NUM_OF_PROXY, exp_mode); +} + +/** Setup proxy chain and uninit usb muxes */ +void usb_uninit_mux_before(void *state) +{ + ARG_UNUSED(state); + setup_usb_mux_proxy_chain(); + set_test_runner_tid(); + + /* Makes sure that usb muxes of port 1 are not init */ + proxy_mux_0.flags = USB_MUX_FLAG_RESETS_IN_G3; + hook_notify(HOOK_CHIPSET_HARD_OFF); + reset_proxy_fakes(); +} + +void usb_uninit_mux_after(void *state) +{ + ARG_UNUSED(state); + restore_usb_mux_chain(); +} + +/** Setup proxy chain and init usb muxes */ +void usb_init_mux_before(void *state) +{ + ARG_UNUSED(state); + setup_usb_mux_proxy_chain(); + set_test_runner_tid(); + + /* Makes sure that usb muxes of port 1 are init */ + usb_mux_init(USBC_PORT_C1); + reset_proxy_fakes(); +} + +void usb_init_mux_after(void *state) +{ + ARG_UNUSED(state); + restore_usb_mux_chain(); +} + +ZTEST_SUITE(usb_uninit_mux, drivers_predicate_post_main, NULL, + usb_uninit_mux_before, usb_uninit_mux_after, NULL); + +ZTEST_SUITE(usb_init_mux, drivers_predicate_post_main, NULL, + usb_init_mux_before, usb_init_mux_after, NULL); diff --git a/zephyr/test/drivers/default/src/usb_pd_host_cmd.c b/zephyr/test/drivers/default/src/usb_pd_host_cmd.c new file mode 100644 index 0000000000..c8851fbeb1 --- /dev/null +++ b/zephyr/test/drivers/default/src/usb_pd_host_cmd.c @@ -0,0 +1,26 @@ +/* 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 "ec_commands.h" +#include "host_command.h" +#include "test/drivers/test_state.h" + +ZTEST_USER(usb_pd_host_cmd, test_host_command_hc_pd_ports) +{ + struct ec_response_usb_pd_ports response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_USB_PD_PORTS, 0, response); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(response.num_ports, CONFIG_USB_PD_PORT_MAX_COUNT, NULL); +} + +ZTEST_SUITE(usb_pd_host_cmd, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); diff --git a/zephyr/test/drivers/default/src/vboot_hash.c b/zephyr/test/drivers/default/src/vboot_hash.c new file mode 100644 index 0000000000..546fc8135f --- /dev/null +++ b/zephyr/test/drivers/default/src/vboot_hash.c @@ -0,0 +1,103 @@ +/* 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 <sha256.h> + +#include "ec_commands.h" +#include "host_command.h" +#include "test/drivers/test_state.h" + +ZTEST_USER(vboot_hash, test_hostcmd_abort) +{ + struct ec_response_vboot_hash response; + struct ec_params_vboot_hash start_params = { + .cmd = EC_VBOOT_HASH_START, + .hash_type = EC_VBOOT_HASH_TYPE_SHA256, + .offset = EC_VBOOT_HASH_OFFSET_RO, + .size = 0, + }; + struct host_cmd_handler_args start_args = BUILD_HOST_COMMAND( + EC_CMD_VBOOT_HASH, 0, response, start_params); + struct ec_params_vboot_hash abort_params = { + .cmd = EC_VBOOT_HASH_ABORT, + }; + struct host_cmd_handler_args abort_args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_VBOOT_HASH, 0, abort_params); + struct ec_params_vboot_hash get_params = { + .cmd = EC_VBOOT_HASH_GET, + }; + struct host_cmd_handler_args get_args = + BUILD_HOST_COMMAND(EC_CMD_VBOOT_HASH, 0, response, get_params); + + /* Start hashing. The command doesn't wait to finish. */ + zassert_ok(host_command_process(&start_args), NULL); + zassert_ok(start_args.result, NULL); + zassert_equal(start_args.response_size, sizeof(response), NULL); + zassert_equal(response.status, EC_VBOOT_HASH_STATUS_BUSY, + "response.status = %d", response.status); + + /* Abort it immediately */ + zassert_ok(host_command_process(&abort_args), NULL); + zassert_ok(abort_args.result, NULL); + + /* Give it a bit time. The abort is being processed in the background */ + k_msleep(20); + + /* Get the hash result. Should be NONE. */ + zassert_ok(host_command_process(&get_args), NULL); + zassert_ok(get_args.result, NULL); + zassert_equal(get_args.response_size, sizeof(response), NULL); + zassert_equal(response.status, EC_VBOOT_HASH_STATUS_NONE, + "response.status = %d", response.status); +} + +ZTEST_USER(vboot_hash, test_hostcmd_recalc) +{ + struct ec_response_vboot_hash response; + struct ec_params_vboot_hash recalc_params = { + .cmd = EC_VBOOT_HASH_RECALC, + .hash_type = EC_VBOOT_HASH_TYPE_SHA256, + .offset = EC_VBOOT_HASH_OFFSET_RO, + .size = 0, + }; + struct host_cmd_handler_args recalc_args = BUILD_HOST_COMMAND( + EC_CMD_VBOOT_HASH, 0, response, recalc_params); + + /* Recalculate the hash. The command waits to finish. */ + zassert_ok(host_command_process(&recalc_args), NULL); + zassert_ok(recalc_args.result, NULL); + zassert_equal(recalc_args.response_size, sizeof(response), NULL); + zassert_equal(response.status, EC_VBOOT_HASH_STATUS_DONE, + "response.status = %d", response.status); + zassert_equal(response.digest_size, SHA256_DIGEST_SIZE, + "response.digest_size = %d", response.digest_size); +} + +ZTEST_USER(vboot_hash, test_hostcmd_hash_arbitrary_size) +{ + struct ec_response_vboot_hash response; + struct ec_params_vboot_hash recalc_params = { + .cmd = EC_VBOOT_HASH_RECALC, + .hash_type = EC_VBOOT_HASH_TYPE_SHA256, + .offset = 0, + /* arbitrary size */ + .size = 0x12345, + }; + struct host_cmd_handler_args recalc_args = BUILD_HOST_COMMAND( + EC_CMD_VBOOT_HASH, 0, response, recalc_params); + + /* Recalculate the hash. The command waits to finish. */ + zassert_ok(host_command_process(&recalc_args), NULL); + zassert_ok(recalc_args.result, NULL); + zassert_equal(recalc_args.response_size, sizeof(response), NULL); + zassert_equal(response.status, EC_VBOOT_HASH_STATUS_DONE, + "response.status = %d", response.status); + zassert_equal(response.digest_size, SHA256_DIGEST_SIZE, + "response.digest_size = %d", response.digest_size); +} + +ZTEST_SUITE(vboot_hash, drivers_predicate_post_main, NULL, NULL, NULL, NULL); diff --git a/zephyr/test/drivers/default/src/virtual_battery.c b/zephyr/test/drivers/default/src/virtual_battery.c new file mode 100644 index 0000000000..0e69c641a5 --- /dev/null +++ b/zephyr/test/drivers/default/src/virtual_battery.c @@ -0,0 +1,259 @@ +/* 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 "battery.h" +#include "battery_smart.h" +#include "ec_commands.h" +#include "emul/emul_smart_battery.h" +#include "host_command.h" +#include "test/drivers/test_state.h" + +/* The param buffer has at most 2 msg's (write + read) and 1 byte write len. */ +static uint8_t param_buf[sizeof(struct ec_params_i2c_passthru) + + sizeof(struct ec_params_i2c_passthru_msg) * 2 + 1]; + +/* The response buffer has at most 32 bytes returned result. */ +static uint8_t response_buf[sizeof(struct ec_response_i2c_passthru) + 32]; + +static void i2c_passthru_xfer(uint8_t port, uint8_t addr, uint8_t *write_buf, + int write_len, uint8_t **read_buf, int read_len) +{ + struct ec_params_i2c_passthru *params = + (struct ec_params_i2c_passthru *)¶m_buf; + struct ec_response_i2c_passthru *response = + (struct ec_response_i2c_passthru *)&response_buf; + struct ec_params_i2c_passthru_msg *msg = params->msg; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_SIMPLE(EC_CMD_I2C_PASSTHRU, 0); + uint8_t *pdata; + int size; + + params->port = port; + params->num_msgs = (read_len != 0) + (write_len != 0); + + size = sizeof(*params) + params->num_msgs * sizeof(*msg); + pdata = (uint8_t *)params + size; + + if (write_len) { + msg->addr_flags = addr; + msg->len = write_len; + memcpy(pdata, write_buf, write_len); + msg++; + } + + if (read_len) { + msg->addr_flags = addr | EC_I2C_FLAG_READ; + msg->len = read_len; + } + + args.params = params; + args.params_size = size + write_len; + args.response = response; + args.response_max = sizeof(*response) + read_len; + + /* Execute the I2C passthru host command */ + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_ok(response->i2c_status, NULL); + zassert_equal(args.response_size, sizeof(*response) + read_len, NULL); + + /* Return the data portion */ + if (read_len) + *read_buf = response->data; +} + +static inline void virtual_battery_xfer(uint8_t *write_buf, int write_len, + uint8_t **read_buf, int read_len) +{ + i2c_passthru_xfer(I2C_PORT_VIRTUAL_BATTERY, VIRTUAL_BATTERY_ADDR_FLAGS, + write_buf, write_len, read_buf, read_len); +} + +static uint16_t virtual_battery_read16(uint8_t command) +{ + uint8_t write_buf[1] = { command }; + uint8_t *read_buf; + + virtual_battery_xfer(write_buf, 1, &read_buf, 2); + + /* Little endian */ + return ((int)read_buf[1] << 8) | read_buf[0]; +} + +static void virtual_battery_write16(uint8_t command, uint16_t data) +{ + uint8_t write_buf[3] = { command }; + + *((uint16_t *)&write_buf[1]) = data; + + virtual_battery_xfer(write_buf, 3, NULL, 0); +} + +static int virtual_battery_read_str(uint8_t command, char **read_buf, + int read_len) +{ + uint8_t write_buf[1] = { command }; + int len; + + virtual_battery_xfer(write_buf, 1, (uint8_t **)read_buf, read_len); + + /* Battery v2 embeds the strlen in the first byte so shift 1 byte. */ + len = **read_buf; + (*read_buf)++; + + return len; +} + +static void virtual_battery_read_data(uint8_t command, char **read_buf, + int read_len) +{ + uint8_t write_buf[1] = { command }; + + virtual_battery_xfer(write_buf, 1, (uint8_t **)read_buf, read_len); +} + +#define BATTERY_NODE DT_NODELABEL(battery) + +ZTEST_USER(virtual_battery, test_read_regs) +{ + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + struct sbat_emul_bat_data *bat = sbat_emul_get_bat_data(emul); + int16_t int16; + uint16_t word; + int expected; + char *str; + int len; + + /* + * Iterate all the registers, which issues the I2C passthru host + * command to query the emulated smart battery. Most of the values + * are the same as the emulated battery, but with some exceptions. + */ + word = virtual_battery_read16(SB_BATTERY_MODE); + zassert_equal(bat->mode, word, "%d != %d", bat->mode, word); + + word = virtual_battery_read16(SB_SERIAL_NUMBER); + zassert_equal(bat->sn, word, "%d != %d", bat->sn, word); + + word = virtual_battery_read16(SB_VOLTAGE); + zassert_equal(bat->volt, word, "%d != %d", bat->volt, word); + + /* The expected value is calculated */ + expected = 100 * bat->cap / bat->full_cap; + word = virtual_battery_read16(SB_RELATIVE_STATE_OF_CHARGE); + + zassert_equal(expected, word, "%d != %d", expected, word); + + word = virtual_battery_read16(SB_TEMPERATURE); + zassert_equal(bat->temp, word, "%d != %d", bat->temp, word); + + int16 = virtual_battery_read16(SB_CURRENT); + zassert_equal(bat->cur, int16, "%d != %d", bat->cur, int16); + + int16 = virtual_battery_read16(SB_AVERAGE_CURRENT); + zassert_equal(bat->avg_cur, int16, "%d != %d", bat->avg_cur, int16); + + /* The virtual battery modifies the return value to make kernel happy */ + expected = BATTERY_LEVEL_SHUTDOWN; + word = virtual_battery_read16(SB_MAX_ERROR); + zassert_equal(expected, word, "%d != %d", expected, word); + + word = virtual_battery_read16(SB_FULL_CHARGE_CAPACITY); + zassert_equal(bat->full_cap, word, "%d != %d", bat->full_cap, word); + + word = virtual_battery_read16(SB_CYCLE_COUNT); + zassert_equal(bat->cycle_count, word, "%d != %d", bat->cycle_count, + word); + + word = virtual_battery_read16(SB_DESIGN_CAPACITY); + zassert_equal(bat->design_cap, word, "%d != %d", bat->design_cap, word); + + word = virtual_battery_read16(SB_REMAINING_CAPACITY); + zassert_equal(bat->cap, word, "%d != %d", bat->cap, word); + + len = virtual_battery_read_str(SB_MANUFACTURER_NAME, &str, + SB_MAX_STR_SIZE); + zassert_equal(bat->mf_name_len, len, "%d != %d", bat->mf_name_len, len); + zassert_mem_equal(str, bat->mf_name, bat->mf_name_len, "%s != %s", str, + bat->mf_name); + + len = virtual_battery_read_str(SB_DEVICE_NAME, &str, SB_MAX_STR_SIZE); + zassert_equal(bat->dev_name_len, len, "%d != %d", bat->dev_name_len, + len); + zassert_mem_equal(str, bat->dev_name, bat->dev_name_len, "%s != %s", + str, bat->dev_name); + + len = virtual_battery_read_str(SB_DEVICE_CHEMISTRY, &str, + SB_MAX_STR_SIZE); + zassert_equal(bat->dev_chem_len, len, "%d != %d", bat->dev_chem_len, + len); + zassert_mem_equal(str, bat->dev_chem, bat->dev_chem_len, "%s != %s", + str, bat->dev_chem); + + /* Use the API to query the expected value */ + battery_time_to_full(&expected); + word = virtual_battery_read16(SB_AVERAGE_TIME_TO_FULL); + zassert_equal(expected, word, "%d != %d", expected, word); + + battery_time_to_empty(&expected); + word = virtual_battery_read16(SB_AVERAGE_TIME_TO_EMPTY); + zassert_equal(expected, word, "%d != %d", expected, word); + + battery_run_time_to_empty(&expected); + word = virtual_battery_read16(SB_RUN_TIME_TO_EMPTY); + zassert_equal(expected, word, "%d != %d", expected, word); + + word = virtual_battery_read16(SB_CHARGING_CURRENT); + zassert_equal(bat->desired_charg_cur, word, "%d != %d", + bat->desired_charg_cur, word); + + word = virtual_battery_read16(SB_CHARGING_VOLTAGE); + zassert_equal(bat->desired_charg_volt, word, "%d != %d", + bat->desired_charg_volt, word); + + word = virtual_battery_read16(SB_MANUFACTURE_DATE); + zassert_equal(bat->mf_date, word, "%d != %d", bat->mf_date, word); + + /* Hard-coded return value: v1.1 without PEC */ + expected = 0x0011; + word = virtual_battery_read16(SB_SPECIFICATION_INFO); + zassert_equal(expected, word, "%d != %d", expected, word); + + zassume_ok(battery_status(&expected)); + word = virtual_battery_read16(SB_BATTERY_STATUS); + zassert_equal(expected, word, "%d != %d", expected, word); + + zassume_ok(battery_design_voltage(&expected)); + word = virtual_battery_read16(SB_DESIGN_VOLTAGE); + zassert_equal(expected, word, "%d != %d", expected, word); + + virtual_battery_read_data(SB_MANUFACTURER_DATA, &str, bat->mf_data_len); + zassert_mem_equal(str, bat->mf_data, bat->mf_data_len, "%s != %s", str, + bat->mf_data); + + /* At present, this command is used nowhere in our codebase. */ + virtual_battery_read_data(SB_MANUFACTURE_INFO, &str, bat->mf_info_len); + zassert_mem_equal(str, bat->mf_info, bat->mf_info_len, "%s != %s", str, + bat->mf_info); +} + +ZTEST_USER(virtual_battery, test_write_mfgacc) +{ + struct sbat_emul_bat_data *bat; + const struct emul *emul = EMUL_DT_GET(BATTERY_NODE); + uint16_t cmd = PARAM_OPERATION_STATUS; + + bat = sbat_emul_get_bat_data(emul); + + /* Write the command to the SB_MANUFACTURER_ACCESS and check */ + virtual_battery_write16(SB_MANUFACTURER_ACCESS, cmd); + zassert_equal(bat->mf_access, cmd, "%d != %d", bat->mf_access, cmd); +} + +ZTEST_SUITE(virtual_battery, drivers_predicate_post_main, NULL, NULL, NULL, + NULL); diff --git a/zephyr/test/drivers/default/src/vstore.c b/zephyr/test/drivers/default/src/vstore.c new file mode 100644 index 0000000000..b4264aaeb3 --- /dev/null +++ b/zephyr/test/drivers/default/src/vstore.c @@ -0,0 +1,230 @@ +/* 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 <setjmp.h> + +#include <console.h> +#include <zephyr/fff.h> +#include <zephyr/shell/shell_dummy.h> +#include <zephyr/ztest.h> + +#include "ec_commands.h" +#include "host_command.h" +#include "system.h" +#include "system_fake.h" +#include "vstore.h" +#include "test/drivers/test_state.h" + +ZTEST_SUITE(vstore, drivers_predicate_post_main, NULL, NULL, NULL, NULL); + +ZTEST_USER(vstore, test_vstore_info) +{ + struct ec_response_vstore_info response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_RESPONSE(EC_CMD_VSTORE_INFO, 0, response); + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_equal(response.slot_count, CONFIG_VSTORE_SLOT_COUNT, + "response.slot_count = %d", response.slot_count); + zassert_equal(response.slot_locked, 0, "response.slot_locked = %#x", + response.slot_locked); +} + +ZTEST_USER(vstore, test_vstore_read) +{ + struct ec_params_vstore_read params = { + .slot = 0, + }; + struct ec_response_vstore_read response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_VSTORE_READ, 0, response, params); + uint8_t expect[EC_VSTORE_SLOT_SIZE] = {}; /* data should start as 0 */ + + zassert_ok(host_command_process(&args), NULL); + zassert_ok(args.result, NULL); + zassert_equal(args.response_size, sizeof(response), NULL); + zassert_mem_equal(expect, response.data, EC_VSTORE_SLOT_SIZE, + "response.data did not match"); +} + +ZTEST_USER(vstore, test_vstore_read_bad_slot) +{ + struct ec_params_vstore_read params = { + .slot = CONFIG_VSTORE_SLOT_COUNT, + }; + struct ec_response_vstore_read response; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND(EC_CMD_VSTORE_READ, 0, response, params); + + zassert_equal(host_command_process(&args), EC_RES_INVALID_PARAM, + "Failed to fail on invalid slot %d", params.slot); +} + +ZTEST_USER(vstore, test_vstore_write_bad_slot) +{ + struct ec_params_vstore_write params = { + .slot = CONFIG_VSTORE_SLOT_COUNT, + .data = {}, + }; + struct host_cmd_handler_args args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_VSTORE_WRITE, 0, params); + + zassert_equal(host_command_process(&args), EC_RES_INVALID_PARAM, + "Failed to fail on invalid slot %d", params.slot); +} + +static void do_vstore_write_read(unsigned int slot) +{ + struct ec_params_vstore_write write_params = { + .slot = slot, + /* .data is set up below */ + }; + struct host_cmd_handler_args write_args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_VSTORE_WRITE, 0, write_params); + struct ec_params_vstore_read read_params = { + .slot = slot, + }; + struct ec_response_vstore_read read_response; + struct host_cmd_handler_args read_args = BUILD_HOST_COMMAND( + EC_CMD_VSTORE_READ, 0, read_response, read_params); + struct ec_response_vstore_info info_response; + struct host_cmd_handler_args info_args = BUILD_HOST_COMMAND_RESPONSE( + EC_CMD_VSTORE_INFO, 0, info_response); + int i; + + for (i = 0; i < EC_VSTORE_SLOT_SIZE; i++) + write_params.data[i] = i + 1; + + /* Write to a slot */ + zassert_ok(host_command_process(&write_args), NULL); + zassert_ok(write_args.result, NULL); + + /* Check that it is now locked */ + zassert_ok(host_command_process(&info_args), NULL); + zassert_ok(info_args.result, NULL); + zassert_equal(info_args.response_size, sizeof(info_response), NULL); + zassert_equal(info_response.slot_count, CONFIG_VSTORE_SLOT_COUNT, + "response.slot_count = %d", info_response.slot_count); + zassert_equal(info_response.slot_locked, 1 << slot, + "response.slot_locked = %#x", info_response.slot_locked); + + /* Read to check data */ + zassert_ok(host_command_process(&read_args), NULL); + zassert_ok(read_args.result, NULL); + zassert_equal(read_args.response_size, sizeof(read_response), NULL); + zassert_mem_equal(write_params.data, read_response.data, + EC_VSTORE_SLOT_SIZE, "response.data did not match"); + + /* Try to write to it again */ + zassert_equal(host_command_process(&write_args), EC_RES_ACCESS_DENIED, + "Failed to fail on writing locked slot %d", + write_params.slot); + + /* Check that it is still locked after that attempt */ + zassert_ok(host_command_process(&info_args), NULL); + zassert_ok(info_args.result, NULL); + zassert_equal(info_args.response_size, sizeof(info_response), NULL); + zassert_equal(info_response.slot_count, CONFIG_VSTORE_SLOT_COUNT, + "response.slot_count = %d", info_response.slot_count); + zassert_equal(info_response.slot_locked, 1 << slot, + "response.slot_locked = %#x", info_response.slot_locked); + + /* Read to check the data didn't change */ + zassert_ok(host_command_process(&read_args), NULL); + zassert_ok(read_args.result, NULL); + zassert_equal(read_args.response_size, sizeof(read_response), NULL); + zassert_mem_equal(write_params.data, read_response.data, + EC_VSTORE_SLOT_SIZE, "response.data did not match"); + + /* Clear locks and try the write again, this time with zero bytes */ + vstore_clear_lock(); + memset(write_params.data, '\0', EC_VSTORE_SLOT_SIZE); + zassert_ok(host_command_process(&write_args), NULL); + zassert_ok(write_args.result, NULL); + + /* Check that it is now locked */ + zassert_ok(host_command_process(&info_args), NULL); + zassert_ok(info_args.result, NULL); + zassert_equal(info_args.response_size, sizeof(info_response), NULL); + zassert_equal(info_response.slot_count, CONFIG_VSTORE_SLOT_COUNT, + "response.slot_count = %d", info_response.slot_count); + zassert_equal(info_response.slot_locked, 1 << slot, + "response.slot_locked = %#x", info_response.slot_locked); + + /* Read to check the data changed */ + zassert_ok(host_command_process(&read_args), NULL); + zassert_ok(read_args.result, NULL); + zassert_equal(read_args.response_size, sizeof(read_response), NULL); + zassert_mem_equal(write_params.data, read_response.data, + EC_VSTORE_SLOT_SIZE, "response.data did not match"); + + /* Clear locks to put things into a normal state */ + vstore_clear_lock(); +} + +ZTEST_USER(vstore, test_vstore_write_read) +{ + /* Try on two different slots */ + zassert_true(CONFIG_VSTORE_SLOT_COUNT >= 2, + "Please set CONFIG_VSTORE_SLOT_COUNT to >= 2"); + do_vstore_write_read(0); + do_vstore_write_read(1); +} + +ZTEST_USER(vstore, test_vstore_state) +{ + struct ec_params_vstore_write write_params = { + .slot = 0, + /* .data is set up below */ + }; + struct host_cmd_handler_args write_args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_VSTORE_WRITE, 0, write_params); + + struct ec_params_reboot_ec reboot_params = { + .cmd = EC_REBOOT_JUMP_RW, + }; + struct host_cmd_handler_args reboot_args = + BUILD_HOST_COMMAND_PARAMS(EC_CMD_REBOOT_EC, 0, reboot_params); + struct ec_response_vstore_info info_response; + struct host_cmd_handler_args info_args = BUILD_HOST_COMMAND_RESPONSE( + EC_CMD_VSTORE_INFO, 0, info_response); + jmp_buf env; + int i; + + shell_backend_dummy_clear_output(get_ec_shell()); + system_common_pre_init(); + + for (i = 0; i < EC_VSTORE_SLOT_SIZE; i++) + write_params.data[i] = i + 1; + + /* Write to a slot */ + zassert_ok(host_command_process(&write_args), NULL); + zassert_ok(write_args.result, NULL); + + /* Set up so we get back to this test on a reboot */ + if (!setjmp(env)) { + system_fake_setenv(&env); + + /* Reboot to RW */ + zassert_ok(host_command_process(&reboot_args), NULL); + + /* Does not return unless something went wrong */ + zassert_unreachable("Failed to reboot"); + } + + /* the reboot should end up here: check the slot is still locked */ + zassert_ok(host_command_process(&info_args), NULL); + zassert_ok(info_args.result, NULL); + zassert_equal(info_args.response_size, sizeof(info_response), NULL); + zassert_equal(info_response.slot_count, CONFIG_VSTORE_SLOT_COUNT, + "response.slot_count = %d", info_response.slot_count); + zassert_equal(info_response.slot_locked, 1 << 0, + "response.slot_locked = %#x", info_response.slot_locked); + + /* Clear locks to put things into a normal state */ + vstore_clear_lock(); +} diff --git a/zephyr/test/drivers/default/src/watchdog.c b/zephyr/test/drivers/default/src/watchdog.c new file mode 100644 index 0000000000..958aa3eaaa --- /dev/null +++ b/zephyr/test/drivers/default/src/watchdog.c @@ -0,0 +1,142 @@ +/* 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. + */ + +/** + * @file + * @brief Unit Tests for watchdog. + */ + +#include <zephyr/device.h> +#include <zephyr/drivers/watchdog.h> + +#include <zephyr/logging/log.h> +#include <zephyr/kernel.h> +#include <zephyr/ztest.h> + +#include "common.h" +#include "ec_tasks.h" +#include <zephyr/fff.h> +#include "hooks.h" +#include "test/drivers/stubs.h" +#include "watchdog.h" +#include "test/drivers/test_state.h" + +#define wdt DEVICE_DT_GET(DT_CHOSEN(cros_ec_watchdog)) + +/** + * @brief Default watchdog timeout plus some time for it to expire. + */ +#define DEFAULT_WDT_EXPIRY_MS \ + (CONFIG_AUX_TIMER_PERIOD_MS + (CONFIG_AUX_TIMER_PERIOD_MS / 2)) + +/** + * @brief Boolean to indicate watchdog alert triggered + */ +bool wdt_warning_triggered; + +/** + * @brief timer to used to validate watchdog expiries. + */ +K_TIMER_DEFINE(ktimer, NULL, NULL); + +/** + * @brief Watchdog test setup handler. + */ +static void watchdog_before(void *state) +{ + ARG_UNUSED(state); + set_test_runner_tid(); + wdt_warning_triggered = false; + + /* When shuffling need watchdog initialized and running + * for other tests. + */ + (void)watchdog_init(); + (void)wdt_setup(wdt, 0); +} + +/** + * @brief Watchdog test teardown handler. + */ +static void watchdog_after(void *state) +{ + ARG_UNUSED(state); + wdt_warning_triggered = false; +} + +/** + * @brief TestPurpose: Verify watchdog initialization. + * + * @details + * Validate watchdog initialization. + * + * Expected Results + * - Successful on first init. + * - Failure on second init. + */ +ZTEST(watchdog, test_watchdog_init) +{ + int retval = EC_SUCCESS; + + /* Test already initialized (initialized in watchdog_before) */ + retval = watchdog_init(); + zassert_equal(-ENOMEM, retval, "Expected -ENOMEM, returned %d.", + retval); +} + +/** + * @brief TestPurpose: Verify watchdog reload. + * + * @details + * Validate watchdog is fed. + * + * Expected Results + * - watchdog warning handler function is never triggered + */ +ZTEST(watchdog, test_watchdog_reload) +{ + int i; + int safe_wait_ms = DEFAULT_WDT_EXPIRY_MS / 2; + + zassert_false(wdt_warning_triggered, "Watchdog timer expired early."); + for (i = 0; i < 10; i++) { + k_timer_start(&ktimer, K_MSEC(safe_wait_ms), K_NO_WAIT); + k_busy_wait(safe_wait_ms * 1000); + k_timer_stop(&ktimer); + watchdog_reload(); + zassert_false(wdt_warning_triggered, + "Watchdog timer expired unexpectedly on loop=%d", + i); + } +} + +/** + * @brief TestPurpose: Verify watchdog timer expires. + * + * @details + * Validate watchdog timer expiry occurs after busy wait + * + * Expected Results + * - Validate watchdog warning handler function is triggered. + */ +ZTEST(watchdog, test_wdt_warning_handler) +{ + /* Feed the dog so timer is reset */ + watchdog_reload(); + + zassert_false(wdt_warning_triggered, "Watchdog timer expired early."); + + k_timer_start(&ktimer, K_MSEC(DEFAULT_WDT_EXPIRY_MS), K_NO_WAIT); + k_busy_wait(DEFAULT_WDT_EXPIRY_MS * 1000); + k_timer_stop(&ktimer); + + zassert_true(wdt_warning_triggered, "Watchdog timer did not expire."); +} + +/** + * @brief Test Suite: Verifies watchdog functionality. + */ +ZTEST_SUITE(watchdog, drivers_predicate_post_main, NULL, watchdog_before, + watchdog_after, NULL); |