summaryrefslogtreecommitdiff
path: root/zephyr/emul/emul_nx20p348x.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/emul/emul_nx20p348x.c')
-rw-r--r--zephyr/emul/emul_nx20p348x.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/zephyr/emul/emul_nx20p348x.c b/zephyr/emul/emul_nx20p348x.c
new file mode 100644
index 0000000000..e7b45531d6
--- /dev/null
+++ b/zephyr/emul/emul_nx20p348x.c
@@ -0,0 +1,204 @@
+/* Copyright 2023 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "driver/ppc/nx20p348x.h"
+#include "emul/emul_common_i2c.h"
+#include "emul/emul_stub_device.h"
+#include "usbc/ppc_nx20p348x.h"
+#include "usbc_ppc.h"
+#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>
+
+#define DT_DRV_COMPAT NX20P348X_COMPAT
+
+#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
+LOG_MODULE_REGISTER(emul_nx20p348x);
+
+/*
+ * Device control reg marks the end of defined regs for the NX20P3483 (0x0B)
+ */
+#define NX20P348X_MAX_REG NX20P348X_DEVICE_CONTROL_REG
+
+struct nx20p348x_emul_data {
+ struct i2c_common_emul_data common;
+ struct gpio_dt_spec irq_gpio;
+ uint8_t regs[NX20P348X_MAX_REG + 1];
+};
+
+struct nx20p348x_reg_default {
+ uint8_t offset;
+ uint8_t val;
+};
+
+/* Chip defaults for non-zero registers (spec Rev 0.4 Table 9) */
+struct nx20p348x_reg_default nx20p348x_defaults[] = {
+ { .offset = NX20P348X_DEVICE_ID_REG, .val = 0x09 },
+ { .offset = NX20P348X_OVLO_THRESHOLD_REG, .val = 0x01 },
+ { .offset = NX20P348X_HV_SRC_OCP_THRESHOLD_REG, .val = 0x0B },
+ { .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 =
+ (struct nx20p348x_emul_data *)emul->data;
+
+ memset(data->regs, 0, sizeof(data->regs));
+
+ for (int i = 0; i < ARRAY_SIZE(nx20p348x_defaults); i++) {
+ struct nx20p348x_reg_default def = nx20p348x_defaults[i];
+
+ data->regs[def.offset] = def.val;
+ }
+ nx20p348x_emul_interrupt_set(emul, 1);
+}
+
+uint8_t nx20p348x_emul_peek(const struct emul *emul, int reg)
+{
+ __ASSERT_NO_MSG(IN_RANGE(reg, 0, NX20P348X_MAX_REG));
+
+ struct nx20p348x_emul_data *data =
+ (struct nx20p348x_emul_data *)emul->data;
+
+ 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))
+ return -EINVAL;
+
+ 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;
+}
+
+static int nx20p348x_emul_write(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))
+ return -EINVAL;
+
+ if (bytes != 1)
+ return -EINVAL;
+
+ data->regs[reg] = val;
+
+ if (IS_ENABLED(CONFIG_PLATFORM_EC_USBC_PPC_NX20P3481) &&
+ reg == NX20P348X_SWITCH_CONTROL_REG) {
+ bool enabled = val & NX20P3481_SWITCH_CONTROL_HVSNK;
+
+ /* Update our status as if we turned on/off Vbus sinking */
+ if (enabled)
+ data->regs[NX20P348X_SWITCH_STATUS_REG] |=
+ NX20P348X_SWITCH_STATUS_HVSNK;
+ else
+ data->regs[NX20P348X_SWITCH_STATUS_REG] &=
+ ~NX20P348X_SWITCH_STATUS_HVSNK;
+
+ /* Do the same for sourcing */
+ enabled = val & NX20P3481_SWITCH_CONTROL_5VSRC;
+ if (enabled)
+ data->regs[NX20P348X_SWITCH_STATUS_REG] |=
+ NX20P348X_SWITCH_STATUS_5VSRC;
+ else
+ data->regs[NX20P348X_SWITCH_STATUS_REG] &=
+ ~NX20P348X_SWITCH_STATUS_5VSRC;
+ }
+
+ return 0;
+}
+
+static int nx20p348x_emul_init(const struct emul *emul,
+ const struct device *parent)
+{
+ struct nx20p348x_emul_data *data =
+ (struct nx20p348x_emul_data *)emul->data;
+ struct i2c_common_emul_data *common_data = &data->common;
+
+ i2c_common_emul_init(common_data);
+ i2c_common_emul_set_read_func(common_data, nx20p348x_emul_read, NULL);
+ i2c_common_emul_set_write_func(common_data, nx20p348x_emul_write, NULL);
+
+ nx20p348x_emul_reset_regs(emul);
+
+ return 0;
+}
+
+#define INIT_NX20P348X_EMUL(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)
+
+DT_INST_FOREACH_STATUS_OKAY(INIT_NX20P348X_EMUL)
+
+DT_INST_FOREACH_STATUS_OKAY(EMUL_STUB_DEVICE);
+
+static void nx20p348x_emul_reset_rule_before(const struct ztest_unit_test *test,
+ void *data)
+{
+ ARG_UNUSED(test);
+ ARG_UNUSED(data);
+
+#define NX20P348X_EMUL_RESET_RULE_BEFORE(n) \
+ nx20p348x_emul_reset_regs(EMUL_DT_GET(DT_DRV_INST(n)))
+
+ DT_INST_FOREACH_STATUS_OKAY(NX20P348X_EMUL_RESET_RULE_BEFORE);
+}
+ZTEST_RULE(nx20p348x_emul_reset, nx20p348x_emul_reset_rule_before, NULL);