diff options
author | Diana Z <dzigterman@chromium.org> | 2023-03-15 15:57:03 -0600 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-03-27 17:49:36 +0000 |
commit | 8a4e2e2c3e1255eda5c239859f2348d93873dd34 (patch) | |
tree | 282de3d7a2946bc5fbdd582aa1eea46e4f84bdf6 | |
parent | db0ec9e25fbf899a7ca3739771a2e2ea2706d054 (diff) | |
download | chrome-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.txt | 1 | ||||
-rw-r--r-- | zephyr/emul/Kconfig | 10 | ||||
-rw-r--r-- | zephyr/emul/emul_amd_fp6.c | 166 | ||||
-rw-r--r-- | zephyr/include/emul/emul_amd_fp6.h | 38 |
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 |