From f1fafde82874f57e5aede2eaf15ec9fcdaa20aa8 Mon Sep 17 00:00:00 2001 From: Dawid Niedzwiecki Date: Wed, 16 Dec 2020 15:07:27 +0100 Subject: [PATCH 1/2] emul: espi: Add support for eSPI emulators Add an emulation controller which routes eSPI traffic to attached emulators depending on the selected chip(mostly host). This allows drivers for eSPI peripherals to be tested on systems that don't have that peripheral attached, with the emulator handling the eSPI traffic. Signed-off-by: Dawid Niedzwiecki --- boards/posix/native_posix/native_posix.dts | 9 + doc/guides/emulator/index.rst | 3 + drivers/espi/CMakeLists.txt | 1 + drivers/espi/Kconfig | 2 + drivers/espi/Kconfig.espi_emul | 16 ++ drivers/espi/espi_emul.c | 207 +++++++++++++++++++++ dts/bindings/espi/zephyr,espi-emul.yaml | 12 ++ include/drivers/espi_emul.h | 137 ++++++++++++++ 8 files changed, 387 insertions(+) create mode 100644 drivers/espi/Kconfig.espi_emul create mode 100644 drivers/espi/espi_emul.c create mode 100644 dts/bindings/espi/zephyr,espi-emul.yaml create mode 100644 include/drivers/espi_emul.h diff --git a/boards/posix/native_posix/native_posix.dts b/boards/posix/native_posix/native_posix.dts index 0498414466ef2..fe53d1f0a0e17 100644 --- a/boards/posix/native_posix/native_posix.dts +++ b/boards/posix/native_posix/native_posix.dts @@ -112,6 +112,15 @@ label = "SPI_0"; }; + espi0: espi@300 { + status = "okay"; + compatible = "zephyr,espi-emul-controller"; + reg = <0x300 4>; + #address-cells = <1>; + #size-cells = <0>; + label = "ESPI_0"; + }; + uart0: uart { status = "okay"; compatible = "zephyr,native-posix-uart"; diff --git a/doc/guides/emulator/index.rst b/doc/guides/emulator/index.rst index 6e4597e2f177f..442bef849b922 100644 --- a/doc/guides/emulator/index.rst +++ b/doc/guides/emulator/index.rst @@ -85,6 +85,9 @@ Zephyr includes the following emulators: * SPI emulator driver, which does the same for SPI +* eSPI emulator driver, which does the same for eSPI. The emulator is being + developed to support more functionalities. + A GPIO emulator is planned but is not yet complete. Samples diff --git a/drivers/espi/CMakeLists.txt b/drivers/espi/CMakeLists.txt index 7fdd637e7779b..601462a55e1bd 100644 --- a/drivers/espi/CMakeLists.txt +++ b/drivers/espi/CMakeLists.txt @@ -6,3 +6,4 @@ zephyr_library_sources_ifdef(CONFIG_ESPI_XEC espi_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX espi_npcx.c) zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX host_subs_npcx.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE espi_handlers.c) +zephyr_library_sources_ifdef(CONFIG_ESPI_EMUL espi_emul.c) diff --git a/drivers/espi/Kconfig b/drivers/espi/Kconfig index 78629e886b92d..b04c58c46dd65 100644 --- a/drivers/espi/Kconfig +++ b/drivers/espi/Kconfig @@ -14,6 +14,8 @@ source "drivers/espi/Kconfig.xec" source "drivers/espi/Kconfig.npcx" +source "drivers/espi/Kconfig.espi_emul" + module = ESPI module-str = espi source "subsys/logging/Kconfig.template.log_config" diff --git a/drivers/espi/Kconfig.espi_emul b/drivers/espi/Kconfig.espi_emul new file mode 100644 index 0000000000000..4f21bca243b51 --- /dev/null +++ b/drivers/espi/Kconfig.espi_emul @@ -0,0 +1,16 @@ +# Copyright 2020 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +config ESPI_EMUL + bool "eSPI emulator" + help + Enable the eSPI emulator driver. This is a fake driver, + it does not talk to real hardware. Instead it talks to emulation + drivers that pretend to be devices on the emulated eSPI bus. It is + used for testing drivers for eSPI devices. + + eSPI is an interface using SPI wires, whose main goal is to reduce the + number of required pins. It includes the functionality of LPC, SMB, SPI + itself (flash access) and GPIO (virtual wires). Please refer to the + specification for more details (it is good for the introduction as well) + https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0_cb.pdf diff --git a/drivers/espi/espi_emul.c b/drivers/espi/espi_emul.c new file mode 100644 index 0000000000000..23045de6a201a --- /dev/null +++ b/drivers/espi/espi_emul.c @@ -0,0 +1,207 @@ +/* + * Copyright 2020 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + * + * This driver creates fake eSPI buses which can contain emulated devices + * (mainly host), implemented by a separate emulation driver. + * The API between this driver/controller and device emulators attached + * to its bus is defined by struct emul_espi_device_api. + */ + +#define DT_DRV_COMPAT zephyr_espi_emul_controller + +#define LOG_LEVEL CONFIG_ESPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(espi_emul_ctlr); + +#include +#include +#include +#include +#include "espi_utils.h" + +/** Working data for the controller */ +struct espi_emul_data { + /* List of struct espi_emul associated with the device */ + sys_slist_t emuls; + /* eSPI host configuration */ + struct espi_cfg cfg; + /** List of eSPI callbacks */ + sys_slist_t callbacks; +}; + +static struct espi_emul *espi_emul_find(const struct device *dev, + unsigned int chipsel) +{ + struct espi_emul_data *data = dev->data; + sys_snode_t *node; + + SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) { + struct espi_emul *emul; + + emul = CONTAINER_OF(node, struct espi_emul, node); + if (emul->chipsel == chipsel) { + return emul; + } + } + + return NULL; +} + +static int espi_emul_config(const struct device *dev, struct espi_cfg *cfg) +{ + struct espi_emul_data *data = dev->data; + + __ASSERT_NO_MSG(cfg); + + data->cfg = *cfg; + + return 0; +} + + +static int emul_espi_trigger_event(const struct device *dev, + struct espi_event *evt) +{ + struct espi_emul_data *data = dev->data; + + if (((evt->evt_type & ESPI_BUS_EVENT_VWIRE_RECEIVED) && + !(data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) || + ((evt->evt_type & ESPI_BUS_EVENT_OOB_RECEIVED) && + !(data->cfg.channel_caps & ESPI_CHANNEL_OOB)) || + ((evt->evt_type & ESPI_BUS_PERIPHERAL_NOTIFICATION) + && !(data->cfg.channel_caps & ESPI_CHANNEL_PERIPHERAL))) { + return -EIO; + } + + espi_send_callbacks(&data->callbacks, dev, *evt); + + return 0; +} + +static bool espi_emul_get_channel_status(const struct device *dev, enum espi_channel ch) +{ + struct espi_emul_data *data = dev->data; + + return (data->cfg.channel_caps & ch); +} + +static int espi_emul_send_vwire(const struct device *dev, enum espi_vwire_signal vw, uint8_t level) +{ + const struct emul_espi_device_api *api; + struct espi_emul *emul; + struct espi_emul_data *data = dev->data; + + if (!(data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) { + return -EIO; + } + + emul = espi_emul_find(dev, EMUL_ESPI_HOST_CHIPSEL); + if (!emul) { + LOG_DBG("espi_emul not found"); + return -EIO; + } + + __ASSERT_NO_MSG(emul->api); + __ASSERT_NO_MSG(emul->api->set_vw); + api = emul->api; + + return api->set_vw(emul, vw, level); +} + +static int espi_emul_receive_vwire(const struct device *dev, enum espi_vwire_signal vw, uint8_t *level) +{ + const struct emul_espi_device_api *api; + struct espi_emul *emul; + struct espi_emul_data *data = dev->data; + + if (!(data->cfg.channel_caps & ESPI_CHANNEL_VWIRE)) { + return -EIO; + } + + emul = espi_emul_find(dev, EMUL_ESPI_HOST_CHIPSEL); + if (!emul) { + LOG_INF("espi_emul not found"); + return -EIO; + } + + __ASSERT_NO_MSG(emul->api); + __ASSERT_NO_MSG(emul->api->get_vw); + api = emul->api; + + return api->get_vw(emul, vw, level); +} + +static int espi_emul_manage_callback(const struct device *dev, struct espi_callback *callback, bool set) +{ + struct espi_emul_data *data = dev->data; + + return espi_manage_callback(&data->callbacks, callback, set); +} + +/** + * Set up a new emulator and add it to the list + * + * @param dev eSPI emulation controller device + */ +static int espi_emul_init(const struct device *dev) +{ + struct espi_emul_data *data = dev->data; + const struct emul_list_for_bus *list = dev->config; + + sys_slist_init(&data->emuls); + + return emul_init_for_bus_from_list(dev, list); +} + +int espi_emul_register(const struct device *dev, const char *name, + struct espi_emul *emul) +{ + struct espi_emul_data *data = dev->data; + + sys_slist_append(&data->emuls, &emul->node); + + LOG_INF("Register emulator '%s' at cs %u\n", name, emul->chipsel); + + return 0; +} + +/* Device instantiation */ +static struct emul_espi_driver_api emul_espi_driver_api = { + .espi_api = { + .config = espi_emul_config, + .get_channel_status = espi_emul_get_channel_status, + .send_vwire = espi_emul_send_vwire, + .receive_vwire = espi_emul_receive_vwire, + .manage_callback = espi_emul_manage_callback + }, + .trigger_event = emul_espi_trigger_event, + .find_emul = espi_emul_find, +}; + + +#define EMUL_LINK_AND_COMMA(node_id) { \ + .label = DT_LABEL(node_id), \ +}, + +#define ESPI_EMUL_INIT(n) \ + static const struct emul_link_for_bus emuls_##n[] = { \ + DT_FOREACH_CHILD(DT_DRV_INST(n), EMUL_LINK_AND_COMMA) \ + }; \ + static struct emul_list_for_bus espi_emul_cfg_##n = { \ + .children = emuls_##n, \ + .num_children = ARRAY_SIZE(emuls_##n), \ + }; \ + static struct espi_emul_data espi_emul_data_##n; \ + DEVICE_DT_INST_DEFINE(n, \ + &espi_emul_init, \ + device_pm_control_nop, \ + &espi_emul_data_##n, \ + &espi_emul_cfg_##n, \ + POST_KERNEL, \ + CONFIG_ESPI_INIT_PRIORITY, \ + &emul_espi_driver_api); + + +DT_INST_FOREACH_STATUS_OKAY(ESPI_EMUL_INIT) diff --git a/dts/bindings/espi/zephyr,espi-emul.yaml b/dts/bindings/espi/zephyr,espi-emul.yaml new file mode 100644 index 0000000000000..cc6eb08282336 --- /dev/null +++ b/dts/bindings/espi/zephyr,espi-emul.yaml @@ -0,0 +1,12 @@ +# Copyright 2020 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +description: Zephyr eSPI Emulation controller + +compatible: "zephyr,espi-emul-controller" + +include: espi-controller.yaml + +properties: + reg: + required: true diff --git a/include/drivers/espi_emul.h b/include/drivers/espi_emul.h new file mode 100644 index 0000000000000..3153a29b13907 --- /dev/null +++ b/include/drivers/espi_emul.h @@ -0,0 +1,137 @@ +/* + * Copyright 2020 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_ESPI_SPI_EMUL_H_ +#define ZEPHYR_INCLUDE_DRIVERS_ESPI_SPI_EMUL_H_ + +/** + * @file + * + * @brief Public APIs for the eSPI emulation drivers. + */ + +#include +#include + +/** + * @brief eSPI Emulation Interface + * @defgroup espi_emul_interface eSPI Emulation Interface + * @ingroup io_emulators + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define EMUL_ESPI_HOST_CHIPSEL 0 + +struct espi_emul; + +/** + * Passes eSPI virtual wires set request (virtual wire packet) to the emulator. + * The emulator updates the state (level) of its virtual wire. + * + * @param emul Emulator instance + * @param vw The signal to be set. + * @param level The level of signal requested LOW(0) or HIGH(1). + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + */ +typedef int (*emul_espi_api_set_vw)(struct espi_emul *emul, + enum espi_vwire_signal vw, + uint8_t level); + +/** + * Passes eSPI virtual wires get request (virtual wire packet) to the emulator. + * The emulator returns the state (level) of its virtual wire. + * + * @param emul Emulator instance + * @param vw The signal to be get. + * @param level The level of the signal to be get. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + */ +typedef int (*emul_espi_api_get_vw)(struct espi_emul *emul, + enum espi_vwire_signal vw, + uint8_t *level); + +/** + * Find an emulator present on a eSPI bus + * + * At present the function is used only to find an emulator of the host + * device. It may be useful in systems with the SPI flash chips. + * + * @param dev eSPI emulation controller device + * @param chipsel Chip-select value + * @return emulator to use + * @return NULL if not found + */ +typedef struct espi_emul *(*emul_find_emul)(const struct device *dev, + unsigned int chipsel); + +/** + * Triggers an event on the emulator of eSPI controller side which causes + * calling specific callbacks. + * + * @param dev Device instance of emulated eSPI controller + * @param evt Event to be triggered + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + */ +typedef int (*emul_trigger_event)(const struct device *dev, + struct espi_event *evt); + + +/** Definition of the eSPI device emulator API */ +struct emul_espi_device_api { + emul_espi_api_set_vw set_vw; + emul_espi_api_get_vw get_vw; +}; + +/** Node in a linked list of emulators for eSPI devices */ +struct espi_emul { + sys_snode_t node; + /* API provided for this device */ + const struct emul_espi_device_api *api; + /* eSPI chip-select of the emulated device */ + uint16_t chipsel; +}; + +/** Definition of the eSPI controller emulator API */ +struct emul_espi_driver_api { + /* The struct espi_driver_api has to be first in + * struct emul_espi_driver_api to make pointer casting working + */ + struct espi_driver_api espi_api; + /* The rest, emulator specific functions */ + emul_trigger_event trigger_event; + emul_find_emul find_emul; +}; + +/** + * Register an emulated device on the controller + * + * @param dev Device that will use the emulator + * @param name User-friendly name for this emulator + * @param emul eSPI emulator to use + * @return 0 indicating success (always) + */ +int espi_emul_register(const struct device *dev, const char *name, + struct espi_emul *emul); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_ESPI_SPI_EMUL_H_ */ From 2f5fc28c84ece8e34e4a000128db82148511e056 Mon Sep 17 00:00:00 2001 From: Dawid Niedzwiecki Date: Wed, 16 Dec 2020 15:08:59 +0100 Subject: [PATCH 2/2] emul: Add an emulator for the eSPI Host This emulator pretends a generic eSPI Host. It supports basic virtual wires and port80 operations. There are functions to trigger actions on the host side e.g. for setting a virtual wire from the host to the eSPI slave, use emul_espi_host_send_vw. It will prepare data and set a proper event on the slave side which will trigger callback (if there is any). Signed-off-by: Dawid Niedzwiecki --- include/drivers/espi_emul.h | 26 +++ subsys/emul/CMakeLists.txt | 1 + subsys/emul/Kconfig | 1 + subsys/emul/espi/CMakeLists.txt | 3 + subsys/emul/espi/Kconfig | 11 ++ subsys/emul/espi/emul_espi_host.c | 256 ++++++++++++++++++++++++++++++ 6 files changed, 298 insertions(+) create mode 100644 subsys/emul/espi/CMakeLists.txt create mode 100644 subsys/emul/espi/Kconfig create mode 100644 subsys/emul/espi/emul_espi_host.c diff --git a/include/drivers/espi_emul.h b/include/drivers/espi_emul.h index 3153a29b13907..07481f10e441b 100644 --- a/include/drivers/espi_emul.h +++ b/include/drivers/espi_emul.h @@ -126,6 +126,32 @@ struct emul_espi_driver_api { int espi_emul_register(const struct device *dev, const char *name, struct espi_emul *emul); +/** + * Sets the eSPI virtual wire on the host side, which will + * trigger a proper event(and callbacks) on the emulated eSPI controller + * + * @param espi_dev eSPI emulation controller device + * @param vw The signal to be set. + * @param level The level of the signal to be set. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + */ +int emul_espi_host_send_vw(const struct device *espi_dev, + enum espi_vwire_signal vw, uint8_t level); + +/** + * Perform port80 write on the emulated host side, which will + * trigger a proper event(and callbacks) on the emulated eSPI controller + * + * @param espi_dev eSPI emulation controller device + * @param data The date to be written. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + */ +int emul_espi_host_port80_write(const struct device *espi_dev, uint32_t data); + #ifdef __cplusplus } #endif diff --git a/subsys/emul/CMakeLists.txt b/subsys/emul/CMakeLists.txt index c759ea9a074cb..0eb5ae3cf68fb 100644 --- a/subsys/emul/CMakeLists.txt +++ b/subsys/emul/CMakeLists.txt @@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_EMUL_BMI160 emul_bmi160.c) add_subdirectory(i2c) add_subdirectory(spi) +add_subdirectory(espi) diff --git a/subsys/emul/Kconfig b/subsys/emul/Kconfig index 7cc3fd89d65ef..7cf4f778fda46 100644 --- a/subsys/emul/Kconfig +++ b/subsys/emul/Kconfig @@ -47,5 +47,6 @@ config EMUL_BMI160 source "subsys/emul/i2c/Kconfig" source "subsys/emul/spi/Kconfig" +source "subsys/emul/espi/Kconfig" endif diff --git a/subsys/emul/espi/CMakeLists.txt b/subsys/emul/espi/CMakeLists.txt new file mode 100644 index 0000000000000..1425949df42ab --- /dev/null +++ b/subsys/emul/espi/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources_ifdef(CONFIG_EMUL_ESPI_HOST emul_espi_host.c) diff --git a/subsys/emul/espi/Kconfig b/subsys/emul/espi/Kconfig new file mode 100644 index 0000000000000..1e9ed31edfe03 --- /dev/null +++ b/subsys/emul/espi/Kconfig @@ -0,0 +1,11 @@ +# Configuration options for eSPI emulators + +# Copyright 2020 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +config EMUL_ESPI_HOST + bool "Emulate an eSPI host" + help + This is an emulator of the generic eSPI host. The emulator supports + basic host operations - virtual wires and writing to port 80. It can be + extended. diff --git a/subsys/emul/espi/emul_espi_host.c b/subsys/emul/espi/emul_espi_host.c new file mode 100644 index 0000000000000..33a161c753bbc --- /dev/null +++ b/subsys/emul/espi/emul_espi_host.c @@ -0,0 +1,256 @@ +/* + * Copyright 2020 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + * + * Emulator for the generic eSPI Host. This supports basic + * host operations. + */ + +#define DT_DRV_COMPAT zephyr_espi_emul_espi_host + +#define LOG_LEVEL CONFIG_ESPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(espi_host); + +#include +#include +#include +#include + +/** Data about the virtual wire */ +struct vw_data { + /* Virtual wire signal */ + enum espi_vwire_signal sig; + /* The level(state) of the virtual wire */ + uint8_t level; + /* The direction of the virtual wire. Possible values: + * ESPI_MASTER_TO_SLAVE or ESPI_SLAVE_TO_MASTER */ + uint8_t dir; +}; + +/** Declare the default state of virtual wires */ +const static struct vw_data vw_state_default[] = { + { ESPI_VWIRE_SIGNAL_SLP_S3, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SLP_S4, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SLP_S5, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SUS_STAT, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_PLTRST, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_OOB_RST_WARN, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_OOB_RST_ACK, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_WAKE, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_PME, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_ERR_FATAL, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_ERR_NON_FATAL, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_SLV_BOOT_STS, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_SCI, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_SMI, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_RST_CPU_INIT, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_HOST_RST_ACK, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_HOST_RST_WARN, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SUS_ACK, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_DNX_ACK, 0, ESPI_SLAVE_TO_MASTER }, + { ESPI_VWIRE_SIGNAL_SUS_WARN, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SLP_A, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SLP_LAN, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_SLP_WLAN, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_HOST_C10, 0, ESPI_MASTER_TO_SLAVE }, + { ESPI_VWIRE_SIGNAL_DNX_WARN, 0, ESPI_MASTER_TO_SLAVE }, +}; + +#define NUMBER_OF_VWIRES ARRAY_SIZE(vw_state_default) + +/** Run-time data used by the emulator */ +struct espi_host_emul_data { + /** eSPI emulator detail */ + struct espi_emul emul; + /** eSPI controller device */ + const struct device *espi; + /** Configuration information */ + const struct espi_host_emul_cfg *cfg; + /** Virtual Wires states, for one slave only. + * With multi-slaves config, the states should be saved per slave */ + struct vw_data vw_state[NUMBER_OF_VWIRES]; +}; + +/** Static configuration for the emulator */ +struct espi_host_emul_cfg { + /** Label of the eSPI bus this emulator connects to */ + const char *espi_label; + /** Label of the emulated AP*/ + const char *label; + /** Pointer to run-time data */ + struct espi_host_emul_data *data; + /* eSPI chip-select of the emulated device */ + uint16_t chipsel; +}; + +/** + * Initialize the state of virtual wires to default based on + * the vw_state_default array. + * + * @param data Host emulator data with the vwire array + */ +static void emul_host_init_vw_state(struct espi_host_emul_data *data) +{ + unsigned i; + + for (i = 0; i < NUMBER_OF_VWIRES; i++) { + data->vw_state[i] = vw_state_default[i]; + } + return; +} + +/** + * Find a virtual wire in the array placed in the host data. + * + * @param data Host emulator data with the vwire array + * @param vw Virtual wire signal to be found + * @return index in the array + * @return -1 if not found + */ +static int emul_host_find_index(struct espi_host_emul_data *data, + enum espi_vwire_signal vw) +{ + int idx; + + for (idx = 0; idx < NUMBER_OF_VWIRES; idx++) { + if (data->vw_state[idx].sig == vw) { + return idx; + } + } + + return -1; +} + +static int emul_host_set_vw(struct espi_emul *emul, enum espi_vwire_signal vw, + uint8_t level) +{ + struct espi_host_emul_data *data; + int idx; + + data = CONTAINER_OF(emul, struct espi_host_emul_data, emul); + idx = emul_host_find_index(data, vw); + + if (idx < 0 || data->vw_state[idx].dir != ESPI_SLAVE_TO_MASTER) { + LOG_ERR("%s: invalid vw: %d", __func__, vw); + return -EPERM; + } + + data->vw_state[idx].level = level; + + return 0; +} + +static int emul_host_get_vw(struct espi_emul *emul, enum espi_vwire_signal vw, + uint8_t *level) +{ + struct espi_host_emul_data *data; + int idx; + + data = CONTAINER_OF(emul, struct espi_host_emul_data, emul); + idx = emul_host_find_index(data, vw); + + if (idx < 0 || data->vw_state[idx].dir != ESPI_MASTER_TO_SLAVE) { + LOG_ERR("%s: invalid vw: %d", __func__, vw); + return -EPERM; + } + + *level = data->vw_state[idx].level; + + return 0; +} + +int emul_espi_host_send_vw(const struct device *espi_dev, enum espi_vwire_signal vw, + uint8_t level) +{ + struct espi_emul *emul_espi; + struct espi_event evt; + struct espi_host_emul_data *data_host; + struct emul_espi_driver_api *api; + int idx; + + api = (struct emul_espi_driver_api *)espi_dev->api; + + __ASSERT_NO_MSG(api); + __ASSERT_NO_MSG(api->trigger_event); + __ASSERT_NO_MSG(api->find_emul); + + emul_espi = api->find_emul(espi_dev, EMUL_ESPI_HOST_CHIPSEL); + data_host = CONTAINER_OF(emul_espi, struct espi_host_emul_data, emul); + + idx = emul_host_find_index(data_host, vw); + if (idx < 0 || data_host->vw_state[idx].dir != ESPI_MASTER_TO_SLAVE) { + LOG_ERR("%s: invalid vw: %d", __func__, vw); + return -EPERM; + } + + data_host->vw_state[idx].level = level; + + evt.evt_type = ESPI_BUS_EVENT_VWIRE_RECEIVED; + evt.evt_details = vw; + evt.evt_data = level; + + api->trigger_event(espi_dev, &evt); + + return 0; +} + +int emul_espi_host_port80_write(const struct device *espi_dev, uint32_t data) +{ + struct espi_event evt; + struct emul_espi_driver_api *api; + + api = (struct emul_espi_driver_api *)espi_dev->api; + + __ASSERT_NO_MSG(api); + __ASSERT_NO_MSG(api->trigger_event); + + evt.evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION; + evt.evt_details = ESPI_PERIPHERAL_DEBUG_PORT80; + evt.evt_data = data; + + api->trigger_event(espi_dev, &evt); + + return 0; +} + +/* Device instantiation */ +static struct emul_espi_device_api ap_emul_api = { + .set_vw = emul_host_set_vw, + .get_vw = emul_host_get_vw, +}; + +/** + * Set up a new eSPI host emulator + * + * @param emul Emulation information + * @param bus Device to emulated eSPI controller + * @return 0 indicating success (always) + */ +static int emul_host_init(const struct emul *emul, const struct device *bus) +{ + const struct espi_host_emul_cfg *cfg = emul->cfg; + struct espi_host_emul_data *data = cfg->data; + + data->emul.api = &ap_emul_api; + data->emul.chipsel = cfg->chipsel; + data->espi = bus; + data->cfg = cfg; + emul_host_init_vw_state(data); + + return espi_emul_register(bus, emul->dev_label, &data->emul); +} + +#define HOST_EMUL(n) \ + static struct espi_host_emul_data espi_host_emul_data_##n; \ + static const struct espi_host_emul_cfg espi_host_emul_cfg_##n = { \ + .espi_label = DT_INST_BUS_LABEL(n), \ + .data = &espi_host_emul_data_##n, \ + .chipsel = DT_INST_REG_ADDR(n), \ + }; \ + EMUL_DEFINE(emul_host_init, DT_DRV_INST(n), &espi_host_emul_cfg_##n) + +DT_INST_FOREACH_STATUS_OKAY(HOST_EMUL)