summaryrefslogtreecommitdiff
path: root/zephyr/test/drivers/src/ppc_sn5s330.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/test/drivers/src/ppc_sn5s330.c')
-rw-r--r--zephyr/test/drivers/src/ppc_sn5s330.c326
1 files changed, 318 insertions, 8 deletions
diff --git a/zephyr/test/drivers/src/ppc_sn5s330.c b/zephyr/test/drivers/src/ppc_sn5s330.c
index 95556e28cf..429b9b8c26 100644
--- a/zephyr/test/drivers/src/ppc_sn5s330.c
+++ b/zephyr/test/drivers/src/ppc_sn5s330.c
@@ -3,10 +3,12 @@
* found in the LICENSE file.
*/
+#include <kernel.h>
#include <device.h>
#include <devicetree.h>
#include <emul.h>
#include <ztest.h>
+#include <fff.h>
#include "driver/ppc/sn5s330.h"
#include "driver/ppc/sn5s330_public.h"
@@ -17,12 +19,49 @@
/** This must match the index of the sn5s330 in ppc_chips[] */
#define SN5S330_PORT 0
#define EMUL emul_get_binding(DT_LABEL(DT_NODELABEL(sn5s330_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(struct i2c_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(struct i2c_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(struct i2c_emul *emul, int reg, uint8_t val,
int bytes, void *data)
{
@@ -40,29 +79,300 @@ static void test_fail_once_func_set1(void)
const struct emul *emul = EMUL;
struct i2c_emul *i2c_emul = sn5s330_emul_to_i2c_emul(emul);
uint32_t count = 1;
- uint32_t func_set1_value;
+ uint8_t func_set1_value;
i2c_common_emul_set_write_func(i2c_emul, fail_until_write_func, &count);
zassert_ok(sn5s330_drv.init(SN5S330_PORT), NULL);
zassert_equal(count, 0, NULL);
- zassert_ok(sn5s330_emul_peek_reg(emul, SN5S330_FUNC_SET1,
- &func_set1_value),
- 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(i2c_emul, NULL, NULL);
}
+static void test_dead_battery_boot_force_pp2_fets_set(void)
+{
+ const struct emul *emul = EMUL;
+ struct i2c_emul *i2c_emul = sn5s330_emul_to_i2c_emul(emul);
+ 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(i2c_emul, intercept_write_func,
+ &test_write_data);
+ i2c_common_emul_set_read_func(i2c_emul, 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);
+}
+
+static void test_enter_low_power_mode(void)
+{
+ 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);
+}
+
+static void test_vbus_source_sink_enable(void)
+{
+ 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);
+}
+
+static void test_vbus_discharge(void)
+{
+ 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);
+}
+
+static void test_set_vbus_source_current_limit(void)
+{
+ 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);
+}
+
+static void test_sn5s330_set_sbu(void)
+#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 */
+
+static void test_sn5s330_vbus_overcurrent(void)
+{
+ 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);
+}
+
+static void test_sn5s330_disable_vbus_low_interrupt(void)
+#ifdef CONFIG_USBC_PPC_VCONN
+{
+ 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);
+}
+#else
+{
+ ztest_test_skip();
+}
+#endif /* CONFIG_USBC_PPC_VCONN */
+
+static void test_sn5s330_set_vconn_fet(void)
+{
+ 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);
+}
+
static void reset_sn5s330_state(void)
{
+ struct i2c_emul *i2c_emul = sn5s330_emul_to_i2c_emul(EMUL);
+
+ i2c_common_emul_set_write_func(i2c_emul, NULL, NULL);
+ i2c_common_emul_set_read_func(i2c_emul, NULL, NULL);
sn5s330_emul_reset(EMUL);
+ RESET_FAKE(sn5s330_emul_interrupt_set_stub);
}
void test_suite_ppc_sn5s330(void)
{
- ztest_test_suite(ppc_sn5s330,
- ztest_unit_test_setup_teardown(
- test_fail_once_func_set1, reset_sn5s330_state,
- reset_sn5s330_state));
+ ztest_test_suite(
+ ppc_sn5s330,
+ ztest_unit_test_setup_teardown(test_sn5s330_set_vconn_fet,
+ reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(
+ test_sn5s330_disable_vbus_low_interrupt,
+ reset_sn5s330_state, reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(test_sn5s330_vbus_overcurrent,
+ reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(test_sn5s330_set_sbu,
+ reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(
+ test_set_vbus_source_current_limit, reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(test_vbus_discharge,
+ reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(test_vbus_source_sink_enable,
+ reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(test_enter_low_power_mode,
+ reset_sn5s330_state,
+ reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(
+ test_dead_battery_boot_force_pp2_fets_set,
+ reset_sn5s330_state, reset_sn5s330_state),
+ ztest_unit_test_setup_teardown(test_fail_once_func_set1,
+ reset_sn5s330_state,
+ reset_sn5s330_state));
ztest_run_test_suite(ppc_sn5s330);
}