summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiana Z <dzigterman@chromium.org>2023-03-31 15:25:34 -0600
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-04-07 15:26:52 +0000
commit53f35199ba36aa3d983015399453326ee4724240 (patch)
tree318a5412ccbb5d8aa8fc67ed62365b13819a8a43
parent414bd1fd6b99ae3d28cbccd458ce66e129592de2 (diff)
downloadchrome-ec-53f35199ba36aa3d983015399453326ee4724240.tar.gz
Zephyr test: Test NXP PPC interrupts
Add support for testing interrupts with the NX20P384X driver. BRANCH=None BUG=b:276468569 TEST=./twister -T ./zephyr/test Change-Id: I3856063c367ec6d8ffcbd8599c3b62fa73e01b1f Signed-off-by: Diana Z <dzigterman@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4389620 Reviewed-by: Tomasz Michalec <tmichalec@google.com>
-rw-r--r--driver/ppc/nx20p348x.c1
-rw-r--r--zephyr/emul/emul_nx20p348x.c59
-rw-r--r--zephyr/include/emul/emul_nx20p348x.h7
-rw-r--r--zephyr/test/drivers/Kconfig1
-rw-r--r--zephyr/test/drivers/nx20p348x/src/nx20p348x.c132
-rw-r--r--zephyr/test/drivers/nx20p348x/usbc.dts1
-rw-r--r--zephyr/test/drivers/usbc_ppc/ppc_alts.dts1
7 files changed, 184 insertions, 18 deletions
diff --git a/driver/ppc/nx20p348x.c b/driver/ppc/nx20p348x.c
index b11dee30f4..cb35e9ff23 100644
--- a/driver/ppc/nx20p348x.c
+++ b/driver/ppc/nx20p348x.c
@@ -502,7 +502,6 @@ static int nx20p348x_dump(int port)
int reg;
int rv;
- ccprintf("Port %d NX20P348X registers\n", port);
for (reg_addr = NX20P348X_DEVICE_ID_REG;
reg_addr <= NX20P348X_DEVICE_CONTROL_REG; reg_addr++) {
rv = read_reg(port, reg_addr, &reg);
diff --git a/zephyr/emul/emul_nx20p348x.c b/zephyr/emul/emul_nx20p348x.c
index a483295a69..c6704c2278 100644
--- a/zephyr/emul/emul_nx20p348x.c
+++ b/zephyr/emul/emul_nx20p348x.c
@@ -11,6 +11,9 @@
#include "util.h"
#include <zephyr/device.h>
+#include <zephyr/devicetree/gpio.h>
+#include <zephyr/drivers/emul.h>
+#include <zephyr/drivers/gpio/gpio_emul.h>
#include <zephyr/logging/log.h>
#include <zephyr/ztest.h>
@@ -26,6 +29,7 @@ LOG_MODULE_REGISTER(emul_nx20p348x);
struct nx20p348x_emul_data {
struct i2c_common_emul_data common;
+ struct gpio_dt_spec irq_gpio;
uint8_t regs[NX20P348X_MAX_REG + 1];
};
@@ -42,6 +46,16 @@ struct nx20p348x_reg_default nx20p348x_defaults[] = {
{ .offset = NX20P348X_5V_SRC_OCP_THRESHOLD_REG, .val = 0x0B },
};
+static void nx20p348x_emul_interrupt_set(const struct emul *emul, int val)
+{
+ struct nx20p348x_emul_data *data =
+ (struct nx20p348x_emul_data *)emul->data;
+
+ int res = gpio_emul_input_set(data->irq_gpio.port, data->irq_gpio.pin,
+ val);
+ __ASSERT_NO_MSG(res == 0);
+}
+
void nx20p348x_emul_reset_regs(const struct emul *emul)
{
struct nx20p348x_emul_data *data =
@@ -54,6 +68,7 @@ void nx20p348x_emul_reset_regs(const struct emul *emul)
data->regs[def.offset] = def.val;
}
+ nx20p348x_emul_interrupt_set(emul, 1);
}
uint8_t nx20p348x_emul_peek(const struct emul *emul, int reg)
@@ -66,26 +81,40 @@ uint8_t nx20p348x_emul_peek(const struct emul *emul, int reg)
return data->regs[reg];
}
+void nx20p348x_emul_set_interrupt1(const struct emul *emul, uint8_t val)
+{
+ struct nx20p348x_emul_data *data =
+ (struct nx20p348x_emul_data *)emul->data;
+
+ data->regs[NX20P348X_INTERRUPT1_REG] = val;
+
+ nx20p348x_emul_interrupt_set(emul, 0);
+}
+
static int nx20p348x_emul_read(const struct emul *emul, int reg, uint8_t *val,
int bytes, void *unused_data)
{
struct nx20p348x_emul_data *data =
(struct nx20p348x_emul_data *)emul->data;
- if (!IN_RANGE(reg, 0, NX20P348X_MAX_REG)) {
- LOG_ERR("Register out of range: %d", reg);
+ if (!IN_RANGE(reg, 0, NX20P348X_MAX_REG))
return -EINVAL;
- }
- if (bytes != 0) {
- LOG_ERR("Emulator expects single byte transactions: "
- "%d bytes requested",
- bytes);
+ if (bytes != 0)
return -EINVAL;
- }
*val = data->regs[reg];
+ /* Interrupt registers are clear on read and de-assert when serviced */
+ if (reg == NX20P348X_INTERRUPT1_REG ||
+ reg == NX20P348X_INTERRUPT2_REG) {
+ data->regs[reg] = 0;
+
+ if (data->regs[NX20P348X_INTERRUPT1_REG] == 0 &&
+ data->regs[NX20P348X_INTERRUPT2_REG] == 0)
+ nx20p348x_emul_interrupt_set(emul, 1);
+ }
+
return 0;
}
@@ -95,17 +124,11 @@ static int nx20p348x_emul_write(const struct emul *emul, int reg, uint8_t val,
struct nx20p348x_emul_data *data =
(struct nx20p348x_emul_data *)emul->data;
- if (!IN_RANGE(reg, 0, NX20P348X_MAX_REG)) {
- LOG_ERR("Register out of range: %d", reg);
+ if (!IN_RANGE(reg, 0, NX20P348X_MAX_REG))
return -EINVAL;
- }
- if (bytes != 1) {
- LOG_ERR("Emulator expects single byte transactions: "
- "%d bytes written",
- bytes);
+ if (bytes != 1)
return -EINVAL;
- }
data->regs[reg] = val;
@@ -129,13 +152,15 @@ static int nx20p348x_emul_init(const struct emul *emul,
}
#define INIT_NX20P348X_EMUL(n) \
- static struct i2c_common_emul_cfg common_cfg_##n; \
static struct nx20p348x_emul_data nx20p348x_emul_data_##n; \
static struct i2c_common_emul_cfg common_cfg_##n = { \
.dev_label = DT_NODE_FULL_NAME(DT_DRV_INST(n)), \
.data = &nx20p348x_emul_data_##n.common, \
.addr = DT_INST_REG_ADDR(n) \
}; \
+ static struct nx20p348x_emul_data nx20p348x_emul_data_##n = { \
+ .irq_gpio = GPIO_DT_SPEC_INST_GET_OR(n, irq_gpios, {}), \
+ }; \
EMUL_DT_INST_DEFINE(n, nx20p348x_emul_init, &nx20p348x_emul_data_##n, \
&common_cfg_##n, &i2c_common_emul_api, NULL)
diff --git a/zephyr/include/emul/emul_nx20p348x.h b/zephyr/include/emul/emul_nx20p348x.h
index 74e0041230..a8435ee530 100644
--- a/zephyr/include/emul/emul_nx20p348x.h
+++ b/zephyr/include/emul/emul_nx20p348x.h
@@ -17,4 +17,11 @@
*/
uint8_t nx20p348x_emul_peek(const struct emul *emul, int reg);
+/**
+ * Set an interrupt in the first interrupt register
+ *
+ * @param emul - NX20P383X emulator data
+ * @param val - value for interrupt register
+ */
+void nx20p348x_emul_set_interrupt1(const struct emul *emul, uint8_t val);
#endif
diff --git a/zephyr/test/drivers/Kconfig b/zephyr/test/drivers/Kconfig
index 1cafa840df..eed9233cd7 100644
--- a/zephyr/test/drivers/Kconfig
+++ b/zephyr/test/drivers/Kconfig
@@ -101,6 +101,7 @@ config LINK_TEST_SUITE_NX20P348X
bool "Link and test the nx20p348x tests"
select PLATFORM_EC_CONSOLE_CMD_PPC_DUMP
select PLATFORM_EC_USBC_PPC_NX20P3483
+ select PLATFORM_EC_USB_PD_LOGGING
config LINK_TEST_SUITE_PANIC_OUTPUT
bool "Link and test the panic_output tests"
diff --git a/zephyr/test/drivers/nx20p348x/src/nx20p348x.c b/zephyr/test/drivers/nx20p348x/src/nx20p348x.c
index 5f98768d06..151a7dea66 100644
--- a/zephyr/test/drivers/nx20p348x/src/nx20p348x.c
+++ b/zephyr/test/drivers/nx20p348x/src/nx20p348x.c
@@ -112,3 +112,135 @@ ZTEST(nx20p348x_driver, test_ppc_dump)
/* Weakly verify something reasonable was output to console */
zassert_not_null(strstr(outbuffer, "]: 0x"));
}
+
+ZTEST_F(nx20p348x_driver, test_db_exit_err)
+{
+ uint8_t reg;
+
+ /* Test an error to exit dead battery mode */
+ nx20p348x_emul_set_interrupt1(fixture->nx20p348x_emul,
+ NX20P348X_INT1_DBEXIT_ERR);
+
+ /* Give the interrupt time to process */
+ k_sleep(K_MSEC(500));
+
+ /* Interrupt should have set DB exit in the control register */
+ reg = nx20p348x_emul_peek(fixture->nx20p348x_emul,
+ NX20P348X_DEVICE_CONTROL_REG);
+ zassert_equal((reg & NX20P348X_CTRL_DB_EXIT), NX20P348X_CTRL_DB_EXIT);
+}
+
+ZTEST_F(nx20p348x_driver, test_db_exit_err_max)
+{
+ uint8_t reg;
+
+ /* Set a DB exit error 10 times */
+ for (int i = 0; i < 10; i++) {
+ nx20p348x_emul_set_interrupt1(fixture->nx20p348x_emul,
+ NX20P348X_INT1_DBEXIT_ERR);
+ k_sleep(K_MSEC(500));
+ }
+
+ /* Interrupt should now be masked by the driver */
+ reg = nx20p348x_emul_peek(fixture->nx20p348x_emul,
+ NX20P348X_INTERRUPT1_MASK_REG);
+ zassert_equal((reg & NX20P348X_INT1_DBEXIT_ERR),
+ NX20P348X_INT1_DBEXIT_ERR);
+}
+
+/* Add filler in case of event data */
+#define MAX_RESPONSE_PD_LOG_ENTRY_SIZE (sizeof(struct ec_response_pd_log) + 16)
+
+static void flush_pd_log(void)
+{
+ uint8_t response_buffer[MAX_RESPONSE_PD_LOG_ENTRY_SIZE];
+ struct ec_response_pd_log *response =
+ (struct ec_response_pd_log *)response_buffer;
+ struct host_cmd_handler_args args =
+ BUILD_HOST_COMMAND_SIMPLE(EC_CMD_PD_GET_LOG_ENTRY, 0);
+
+ args.response = response;
+ args.response_max = sizeof(response_buffer);
+
+ for (int i = 0; i < 10; i++) {
+ zassert_ok(host_command_process(&args));
+
+ if (response->type == PD_EVENT_NO_ENTRY)
+ return;
+
+ k_sleep(K_MSEC(500));
+ }
+
+ zassert_unreachable("Failed to flush PD log");
+}
+
+ZTEST_F(nx20p348x_driver, test_vbus_overcurrent)
+{
+ uint8_t response_buffer[MAX_RESPONSE_PD_LOG_ENTRY_SIZE];
+ struct ec_response_pd_log *response =
+ (struct ec_response_pd_log *)response_buffer;
+ struct host_cmd_handler_args args =
+ BUILD_HOST_COMMAND_SIMPLE(EC_CMD_PD_GET_LOG_ENTRY, 0);
+
+ flush_pd_log();
+
+ /* Set up overcurrent */
+ nx20p348x_emul_set_interrupt1(fixture->nx20p348x_emul,
+ NX20P348X_INT1_OC_5VSRC);
+ k_sleep(K_MSEC(500));
+
+ args.response = response;
+ args.response_max = sizeof(response_buffer);
+
+ zassert_ok(host_command_process(&args));
+ zassert_equal(TEST_PORT, PD_LOG_PORT(response->size_port));
+ zassert_equal(0, PD_LOG_SIZE(response->size_port));
+ zassert_equal(PD_EVENT_PS_FAULT, response->type);
+ zassert_equal(PS_FAULT_OCP, response->data);
+}
+
+ZTEST_F(nx20p348x_driver, test_vbus_reverse_current)
+{
+ uint8_t response_buffer[MAX_RESPONSE_PD_LOG_ENTRY_SIZE];
+ struct ec_response_pd_log *response =
+ (struct ec_response_pd_log *)response_buffer;
+ struct host_cmd_handler_args args =
+ BUILD_HOST_COMMAND_SIMPLE(EC_CMD_PD_GET_LOG_ENTRY, 0);
+
+ flush_pd_log();
+
+ /* Set up reverse current */
+ nx20p348x_emul_set_interrupt1(fixture->nx20p348x_emul,
+ NX20P348X_INT1_RCP_5VSRC);
+ k_sleep(K_MSEC(500));
+
+ args.response = response;
+ args.response_max = sizeof(response_buffer);
+
+ zassert_ok(host_command_process(&args));
+ zassert_equal(TEST_PORT, PD_LOG_PORT(response->size_port));
+ zassert_equal(0, PD_LOG_SIZE(response->size_port));
+ zassert_equal(PD_EVENT_PS_FAULT, response->type);
+ zassert_equal(PS_FAULT_OCP, response->data);
+}
+
+ZTEST_F(nx20p348x_driver, test_vbus_short)
+{
+ const struct shell *shell_zephyr = get_ec_shell();
+ const char *outbuffer;
+ size_t buffer_size;
+
+ shell_backend_dummy_clear_output(shell_zephyr);
+
+ /* Set up Vbus short, which we only report in the console */
+ nx20p348x_emul_set_interrupt1(fixture->nx20p348x_emul,
+ NX20P348X_INT1_SC_5VSRC);
+ k_sleep(K_MSEC(500));
+
+ outbuffer = shell_backend_dummy_get_output(shell_zephyr, &buffer_size);
+
+ zassert_true(buffer_size > 0);
+
+ /* Weakly verify something reasonable was output to console */
+ zassert_not_null(strstr(outbuffer, "short"));
+}
diff --git a/zephyr/test/drivers/nx20p348x/usbc.dts b/zephyr/test/drivers/nx20p348x/usbc.dts
index 09650cea69..86fb5781d6 100644
--- a/zephyr/test/drivers/nx20p348x/usbc.dts
+++ b/zephyr/test/drivers/nx20p348x/usbc.dts
@@ -21,5 +21,6 @@
compatible = "nxp,nx20p348x";
status = "okay";
reg = <0x71>;
+ irq-gpios = < &gpio0 14 GPIO_ACTIVE_LOW >;
};
};
diff --git a/zephyr/test/drivers/usbc_ppc/ppc_alts.dts b/zephyr/test/drivers/usbc_ppc/ppc_alts.dts
index d0978238c3..ed84ff0662 100644
--- a/zephyr/test/drivers/usbc_ppc/ppc_alts.dts
+++ b/zephyr/test/drivers/usbc_ppc/ppc_alts.dts
@@ -20,6 +20,7 @@
compatible = "nxp,nx20p348x";
status = "okay";
reg = <0x77>;
+ irq-gpios = < &gpio0 14 GPIO_ACTIVE_LOW >;
is-alt;
};