summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiana Z <dzigterman@chromium.org>2023-03-15 15:57:03 -0600
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-03-27 17:49:36 +0000
commit8a4e2e2c3e1255eda5c239859f2348d93873dd34 (patch)
tree282de3d7a2946bc5fbdd582aa1eea46e4f84bdf6
parentdb0ec9e25fbf899a7ca3739771a2e2ea2706d054 (diff)
downloadchrome-ec-8a4e2e2c3e1255eda5c239859f2348d93873dd34.tar.gz
Zephyr test: Add AMD FP6 USB Mux emulator
Add an emulator to imitate the AMD FP6 USB mux i2c target. BRANCH=None BUG=b:274159180 TEST=builds Change-Id: Ib079884aaeabfd361585982b94051665edc32181 Signed-off-by: Diana Z <dzigterman@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4350995 Commit-Queue: Simon Glass <sjg@chromium.org> Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r--zephyr/emul/CMakeLists.txt1
-rw-r--r--zephyr/emul/Kconfig10
-rw-r--r--zephyr/emul/emul_amd_fp6.c166
-rw-r--r--zephyr/include/emul/emul_amd_fp6.h38
4 files changed, 215 insertions, 0 deletions
diff --git a/zephyr/emul/CMakeLists.txt b/zephyr/emul/CMakeLists.txt
index ad8c209c11..77a8de3213 100644
--- a/zephyr/emul/CMakeLists.txt
+++ b/zephyr/emul/CMakeLists.txt
@@ -30,6 +30,7 @@ zephyr_library_sources_ifdef(CONFIG_EMUL_SMART_BATTERY emul_smart_battery.c)
zephyr_library_sources_ifdef(CONFIG_EMUL_SN5S330 emul_sn5s330.c)
zephyr_library_sources_ifdef(CONFIG_EMUL_TCS3400 emul_tcs3400.c)
zephyr_library_sources_ifdef(CONFIG_EMUL_TUSB1064 emul_tusb1064.c)
+zephyr_library_sources_ifdef(CONFIG_EMUL_USB_MUX_AMD_FP6 emul_amd_fp6.c)
zephyr_library_sources_ifdef(CONFIG_I2C_MOCK i2c_mock.c)
zephyr_library_sources_ifdef(CONFIG_PWM_MOCK pwm_mock.c)
zephyr_library_sources_ifdef(CONFIG_VCMP_MOCK vcmp_mock.c)
diff --git a/zephyr/emul/Kconfig b/zephyr/emul/Kconfig
index 42ad604ea1..b7bfc88aa2 100644
--- a/zephyr/emul/Kconfig
+++ b/zephyr/emul/Kconfig
@@ -92,6 +92,16 @@ config EMUL_TUSB1064
Enable the TUSB1064 usb mux. This driver use emulated I2C bus.
Emulator API is available in zephyr/include/emul/emul_tusb1064.h.
+config EMUL_USB_MUX_AMD_FP6
+ bool "AMD FP6 USB mux emulator"
+ default y
+ depends on ZTEST && DT_HAS_AMD_USBC_MUX_AMD_FP6_ENABLED
+ select EMUL_COMMON_I2C
+ help
+ Enable emulator for the AMD FP6 USB mux target. This is a mux
+ which, while it resides on the SoC, presents itself to the EC as a
+ I2C target device
+
config EMUL_BB_RETIMER
bool "BB retimer emulator"
default y
diff --git a/zephyr/emul/emul_amd_fp6.c b/zephyr/emul/emul_amd_fp6.c
new file mode 100644
index 0000000000..dfb763e1c8
--- /dev/null
+++ b/zephyr/emul/emul_amd_fp6.c
@@ -0,0 +1,166 @@
+/* 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/usb_mux/amd_fp6.h"
+#include "emul/emul_common_i2c.h"
+#include "emul/emul_stub_device.h"
+#include "usbc/amd_fp6_usb_mux.h"
+#include "util.h"
+
+#include <zephyr/device.h>
+#include <zephyr/logging/log.h>
+#include <zephyr/ztest.h>
+
+#define DT_DRV_COMPAT AMD_FP6_USB_MUX_COMPAT
+
+#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
+LOG_MODULE_REGISTER(emul_amd_fp6);
+
+/* Target supports only 3-byte reads */
+enum amd_fp6_read_bytes {
+ AMD_FP6_STATUS,
+ AMD_FP6_PORT0,
+ AMD_FP6_PORT1,
+ AMD_FP6_MAX_REG,
+};
+
+struct amd_fp6_data {
+ struct i2c_common_emul_data common;
+ int64_t finish_delay; /* How long before mux set "completes"? */
+ int64_t set_time; /* Time of last set call */
+ uint8_t last_mux_set; /* Last value of mux set call */
+ uint8_t regs[AMD_FP6_MAX_REG];
+};
+
+/* Helper for setting the mux register with a value and "done" */
+static uint8_t amd_fp6_emul_mux_complete(uint8_t mux_val)
+{
+ return (AMD_FP6_MUX_PORT_CMD_COMPLETE
+ << AMD_FP6_MUX_PORT_STATUS_OFFSET) |
+ mux_val;
+}
+
+void amd_fp6_emul_reset_regs(const struct emul *emul)
+{
+ struct amd_fp6_data *data = (struct amd_fp6_data *)emul->data;
+
+ /* Default to easy use (ready and no delay) */
+ data->finish_delay = 0;
+
+ /* Mux starts in low power mode and ready */
+ data->regs[AMD_FP6_STATUS] = AMD_FP6_MUX_PD_STATUS_READY
+ << AMD_FP6_MUX_PD_STATUS_OFFSET;
+ data->regs[AMD_FP6_PORT0] =
+ amd_fp6_emul_mux_complete(AMD_FP6_MUX_LOW_POWER);
+ /* "Port 1" is unused in current code */
+ data->regs[AMD_FP6_PORT1] = 0;
+}
+
+void amd_fp6_emul_set_delay(const struct emul *emul, int delay_ms)
+{
+ struct amd_fp6_data *data = (struct amd_fp6_data *)emul->data;
+
+ data->finish_delay = delay_ms;
+}
+
+void amd_fp6_emul_set_xbar(const struct emul *emul, bool ready)
+{
+ struct amd_fp6_data *data = (struct amd_fp6_data *)emul->data;
+
+ data->regs[AMD_FP6_STATUS] = ready ? (AMD_FP6_MUX_PD_STATUS_READY
+ << AMD_FP6_MUX_PD_STATUS_OFFSET) :
+ 0;
+}
+
+static int amd_fp6_emul_read(const struct emul *emul, int reg, uint8_t *val,
+ int bytes, void *unused_data)
+{
+ struct amd_fp6_data *data = (struct amd_fp6_data *)emul->data;
+ uint8_t *regs = data->regs;
+ int pos = reg + bytes;
+
+ if (!IN_RANGE(pos, 0, AMD_FP6_MAX_REG)) {
+ return -EINVAL;
+ }
+
+ /* Decide if we've finally finished our operation */
+ if (pos == AMD_FP6_PORT0 && data->finish_delay > 0) {
+ int64_t uptime = k_uptime_delta(&data->set_time);
+
+ if (((regs[pos] >> AMD_FP6_MUX_PORT_STATUS_OFFSET) ==
+ AMD_FP6_MUX_PORT_CMD_BUSY) &&
+ (uptime >= data->finish_delay))
+ regs[pos] =
+ amd_fp6_emul_mux_complete(data->last_mux_set);
+ }
+
+ *val = regs[pos];
+
+ return 0;
+}
+
+static int amd_fp6_emul_write(const struct emul *emul, int reg, uint8_t val,
+ int bytes, void *unused_data)
+{
+ struct amd_fp6_data *data = (struct amd_fp6_data *)emul->data;
+ uint8_t *regs = data->regs;
+
+ /* We only support Port 0 */
+ if (reg != 0 || bytes != 1)
+ return -EINVAL;
+
+ data->last_mux_set = val;
+
+ if (data->finish_delay == 0)
+ regs[AMD_FP6_PORT0] = amd_fp6_emul_mux_complete(val);
+ else
+ regs[AMD_FP6_PORT0] = AMD_FP6_MUX_PORT_CMD_BUSY
+ << AMD_FP6_MUX_PORT_STATUS_OFFSET;
+
+ return 0;
+}
+
+static int amd_fp6_emul_init(const struct emul *emul,
+ const struct device *parent)
+{
+ struct amd_fp6_data *data = (struct amd_fp6_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, amd_fp6_emul_read, NULL);
+ i2c_common_emul_set_write_func(common_data, amd_fp6_emul_write, NULL);
+
+ amd_fp6_emul_reset_regs(emul);
+
+ return 0;
+}
+
+#define INIT_AMD_FP6_EMUL(n) \
+ static struct i2c_common_emul_cfg common_cfg_##n; \
+ static struct amd_fp6_data amd_fp6_data_##n; \
+ static struct i2c_common_emul_cfg common_cfg_##n = { \
+ .dev_label = DT_NODE_FULL_NAME(DT_DRV_INST(n)), \
+ .data = &amd_fp6_data_##n.common, \
+ .addr = DT_INST_REG_ADDR(n) \
+ }; \
+ EMUL_DT_INST_DEFINE(n, amd_fp6_emul_init, &amd_fp6_data_##n, \
+ &common_cfg_##n, &i2c_common_emul_api, NULL)
+
+DT_INST_FOREACH_STATUS_OKAY(INIT_AMD_FP6_EMUL)
+
+DT_INST_FOREACH_STATUS_OKAY(EMUL_STUB_DEVICE);
+
+static void amd_fp6_emul_reset_rule_before(const struct ztest_unit_test *test,
+ void *data)
+{
+ ARG_UNUSED(test);
+ ARG_UNUSED(data);
+
+#define AMD_FP6_EMUL_RESET_RULE_BEFORE(n) \
+ amd_fp6_emul_reset_regs(EMUL_DT_GET(DT_DRV_INST(n)))
+
+ DT_INST_FOREACH_STATUS_OKAY(AMD_FP6_EMUL_RESET_RULE_BEFORE);
+}
+ZTEST_RULE(amd_fp6_usb_mux_emul_reset, amd_fp6_emul_reset_rule_before, NULL);
diff --git a/zephyr/include/emul/emul_amd_fp6.h b/zephyr/include/emul/emul_amd_fp6.h
new file mode 100644
index 0000000000..6c63a6caab
--- /dev/null
+++ b/zephyr/include/emul/emul_amd_fp6.h
@@ -0,0 +1,38 @@
+/* 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.
+ */
+
+#ifndef EMUL_AMD_FP6_USB_MUX_H
+#define EMUL_AMD_FP6_USB_MUX_H
+
+#include <zephyr/drivers/emul.h>
+
+/**
+ * Reset the emulator's registers to their power-on value.
+ *
+ * @param emul - AMD FP6 emulator data
+ */
+void amd_fp6_emul_reset_regs(const struct emul *emul);
+
+/**
+ * Set whether the crossbar is ready to process commands. On a real
+ * system, it is typically not ready for some time after powering on to S0.
+ *
+ * @param emul - AMD FP6 emulator data
+ * @param ready - whether the xbar should report it's ready
+ * (emulator init default is on)
+ */
+void amd_fp6_emul_set_xbar(const struct emul *emul, bool ready);
+
+/**
+ * Set how long a command will take to complete. On a real system this can be
+ * anywhere from 50-100ms and the datasheet defines it can take up to 250ms.
+ *
+ * @param emul - AMD FP6 emulator data
+ * @param delay_ms - how long after a mux set to wait before reporting the
+ * status of the set as complete.
+ */
+void amd_fp6_emul_set_delay(const struct emul *emul, int delay_ms);
+
+#endif