From 49f2167974fc164782ab508a4b6c10cd19f3ff94 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 20 Aug 2020 08:14:35 -0600 Subject: [PATCH] emul: i2c: Add support for I2C emulators Add an emulation controller which routes I2C traffic to attached emulators depending on the I2C address selected. This allows drivers for I2C peripherals to be tested on systems that don't have that peripheral attached, with the emulator handling the I2C traffic. Signed-off-by: Simon Glass Signed-off-by: Peter Bigot --- CODEOWNERS | 4 + drivers/i2c/CMakeLists.txt | 1 + drivers/i2c/Kconfig | 1 + drivers/i2c/Kconfig.i2c_emul | 10 ++ drivers/i2c/i2c_emul.c | 155 ++++++++++++++++++++++++++ dts/bindings/i2c/zephyr,i2c-emul.yaml | 12 ++ include/drivers/i2c_emul.h | 92 +++++++++++++++ 7 files changed, 275 insertions(+) create mode 100644 drivers/i2c/Kconfig.i2c_emul create mode 100644 drivers/i2c/i2c_emul.c create mode 100644 dts/bindings/i2c/zephyr,i2c-emul.yaml create mode 100644 include/drivers/i2c_emul.h diff --git a/CODEOWNERS b/CODEOWNERS index 6c6600653a4..574b76ff3fc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -190,7 +190,9 @@ /drivers/i2c/*litex* @mateusz-holenko @kgugala @pgielda /drivers/i2s/i2s_ll_stm32* @avisconti /drivers/i2c/i2c_common.c @sjg20 +/drivers/i2c/i2c_emul.c @sjg20 /drivers/i2c/i2c_shell.c @nashif +/drivers/i2c/Kconfig.i2c_emul @sjg20 /drivers/i2s/*litex* @mateusz-holenko @kgugala @pgielda /drivers/ieee802154/ @jukkar @tbursztyka /drivers/ieee802154/ieee802154_rf2xx* @jukkar @tbursztyka @nandojve @@ -302,6 +304,7 @@ /dts/xtensa/intel/ @dcpleung /dts/bindings/ @galak /dts/bindings/can/ @alexanderwachter +/dts/bindings/i2c/zephyr*i2c-emul.yaml @sjg20 /dts/bindings/iio/adc/st*stm32-adc.yaml @cybertale /dts/bindings/serial/ns16550.yaml @andrewboie /dts/bindings/*/nordic* @anangl @@ -324,6 +327,7 @@ /include/drivers/espi.h @albertofloyd @franciscomunoz @scottwcpg /include/drivers/bluetooth/ @joerchan @jhedberg @Vudentz /include/drivers/flash.h @nashif @carlescufi @galak @MaureenHelm @nvlsianpu +/include/drivers/i2c_emul.h @sjg20 /include/drivers/led/ht16k33.h @henrikbrixandersen /include/drivers/interrupt_controller/ @andrewboie /include/drivers/interrupt_controller/gic.h @stephanosio diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index a5596fe8907..8887ac09842 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -15,6 +15,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_XEC i2c_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_I2C_MCUX i2c_mcux.c) zephyr_library_sources_ifdef(CONFIG_I2C_MCUX_FLEXCOMM i2c_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_I2C_MCUX_LPI2C i2c_mcux_lpi2c.c) +zephyr_library_sources_ifdef(CONFIG_I2C_EMUL i2c_emul.c) zephyr_library_sources_ifdef(CONFIG_NRFX_TWI i2c_nrfx_twi.c) zephyr_library_sources_ifdef(CONFIG_NRFX_TWIM i2c_nrfx_twim.c) zephyr_library_sources_ifdef(CONFIG_I2C_SAM_TWI i2c_sam_twi.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 3c51ee8e125..caf483e21fc 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -32,6 +32,7 @@ source "drivers/i2c/slave/Kconfig" source "drivers/i2c/Kconfig.gpio" source "drivers/i2c/Kconfig.xec" source "drivers/i2c/Kconfig.nrfx" +source "drivers/i2c/Kconfig.i2c_emul" source "drivers/i2c/Kconfig.sbcon" source "drivers/i2c/Kconfig.sifive" source "drivers/i2c/Kconfig.stm32" diff --git a/drivers/i2c/Kconfig.i2c_emul b/drivers/i2c/Kconfig.i2c_emul new file mode 100644 index 00000000000..2c9102fcdc3 --- /dev/null +++ b/drivers/i2c/Kconfig.i2c_emul @@ -0,0 +1,10 @@ +# Copyright 2020 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +config I2C_EMUL + bool "I2C emulator" + help + Enable the I2C emulator driver. This is a fake driver in that it + does not talk to real hardware. Instead it talks to emulation + drivers that pretend to be devices on the emulated I2C bus. It is + used for testing drivers for I2C devices. diff --git a/drivers/i2c/i2c_emul.c b/drivers/i2c/i2c_emul.c new file mode 100644 index 00000000000..c219c4b5083 --- /dev/null +++ b/drivers/i2c/i2c_emul.c @@ -0,0 +1,155 @@ +/* + * This driver creates fake I2C buses which can contain emulated devices, + * implemented by a separate emulation driver. The API between this driver and + * its emulators is defined by struct i2c_emul_driver_api. + * + * Copyright 2020 Google LLC + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zephyr_i2c_emul_controller + +#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL +#include +LOG_MODULE_REGISTER(i2c_emul_ctlr); + +#include +#include +#include +#include + +/** Working data for the device */ +struct i2c_emul_data { + /* List of struct i2c_emul associated with the device */ + sys_slist_t emuls; + /* I2C host configuration */ + uint32_t config; +}; + +uint32_t i2c_emul_get_config(const struct device *dev) +{ + struct i2c_emul_data *data = dev->data; + + return data->config; +} + +/** + * Find an emulator by its I2C address + * + * @param dev I2C emulation controller device + * @param addr I2C address of that device + * @return emulator ro use + * @return NULL if not found + */ +static struct i2c_emul *i2c_emul_find(struct device *dev, int addr) +{ + struct i2c_emul_data *data = dev->data; + sys_snode_t *node; + + SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) { + struct i2c_emul *emul = NULL; + + emul = CONTAINER_OF(node, struct i2c_emul, node); + if (emul->addr == addr) { + return emul; + } + } + + return NULL; +} + +static int i2c_emul_configure(struct device *dev, uint32_t dev_config) +{ + struct i2c_emul_data *data = dev->data; + + data->config = dev_config; + + return 0; +} + +static int i2c_emul_transfer(struct device *dev, struct i2c_msg *msgs, + uint8_t num_msgs, uint16_t addr) +{ + struct i2c_emul *emul; + const struct i2c_emul_api *api; + int ret; + + emul = i2c_emul_find(dev, addr); + if (!emul) { + return -EIO; + } + + api = emul->api; + __ASSERT_NO_MSG(emul->api); + __ASSERT_NO_MSG(emul->api->transfer); + + ret = api->transfer(emul, msgs, num_msgs, addr); + if (ret) { + return ret; + } + + return 0; +} + +/** + * Set up a new emulator and add it to the list + * + * @param dev I2C emulation controller device + */ +static int i2c_emul_init(struct device *dev) +{ + struct i2c_emul_data *data = dev->data; + const struct emul_list_for_bus *list = dev->config; + int rc; + + sys_slist_init(&data->emuls); + + rc = emul_init_for_bus_from_list(dev, list); + + return rc; +} + +int i2c_emul_register(struct device *dev, const char *name, + struct i2c_emul *emul) +{ + struct i2c_emul_data *data = dev->data; + + sys_slist_append(&data->emuls, &emul->node); + + LOG_INF("Register emulator '%s' at I2C addr %02x\n", name, emul->addr); + + return 0; +} + +/* Device instantiation */ + +static struct i2c_driver_api i2c_emul_api = { + .configure = i2c_emul_configure, + .transfer = i2c_emul_transfer, +}; + +#define EMUL_LINK_AND_COMMA(node_id) { \ + .label = DT_LABEL(node_id), \ +}, + +#define I2C_EMUL_INIT(n) \ + static const struct emul_link_for_bus emuls_##n[] = { \ + DT_FOREACH_CHILD(DT_DRV_INST(0), EMUL_LINK_AND_COMMA) \ + }; \ + static struct emul_list_for_bus i2c_emul_cfg_##n = { \ + .children = emuls_##n, \ + .num_children = ARRAY_SIZE(emuls_##n), \ + }; \ + static struct i2c_emul_data i2c_emul_data_##n; \ + DEVICE_AND_API_INIT(i2c_##n, \ + DT_INST_LABEL(n), \ + i2c_emul_init, \ + &i2c_emul_data_##n, \ + &i2c_emul_cfg_##n, \ + POST_KERNEL, \ + CONFIG_I2C_INIT_PRIORITY, \ + &i2c_emul_api); + +DT_INST_FOREACH_STATUS_OKAY(I2C_EMUL_INIT) diff --git a/dts/bindings/i2c/zephyr,i2c-emul.yaml b/dts/bindings/i2c/zephyr,i2c-emul.yaml new file mode 100644 index 00000000000..7bd4f58acbc --- /dev/null +++ b/dts/bindings/i2c/zephyr,i2c-emul.yaml @@ -0,0 +1,12 @@ +# Copyright 2020 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +description: Zephyr I2C Emulation controller + +compatible: "zephyr,i2c-emul-controller" + +include: i2c-controller.yaml + +properties: + reg: + required: true diff --git a/include/drivers/i2c_emul.h b/include/drivers/i2c_emul.h new file mode 100644 index 00000000000..c464d9da2e9 --- /dev/null +++ b/include/drivers/i2c_emul.h @@ -0,0 +1,92 @@ +/** + * @file + * + * @brief Public APIs for the I2C emulation drivers. + */ + +/* + * Copyright 2020 Google LLC + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DRIVERS_I2C_I2C_EMUL_H_ +#define ZEPHYR_INCLUDE_DRIVERS_I2C_I2C_EMUL_H_ + +/** + * @brief I2C Emulation Interface + * @defgroup i2c_emul_interface I2C Emulation Interface + * @ingroup io_emulators + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct i2c_msg; +struct i2c_emul_api; + +/** Node in a linked list of emulators for I2C devices */ +struct i2c_emul { + sys_snode_t node; + + /* API provided for this device */ + const struct i2c_emul_api *api; + + /* I2C address of the emulated device */ + uint16_t addr; +}; + +/** + * Passes I2C messages to the emulator. The emulator updates the data with what + * was read back. + * + * @param emul Emulator instance + * @param msgs Array of messages to transfer. For 'read' messages, this function + * updates the 'buf' member with the data that was read + * @param num_msgs Number of messages to transfer. + * @param addr Address of the I2C target device. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + */ +typedef int (*i2c_emul_transfer_t)(struct i2c_emul *emul, struct i2c_msg *msgs, + int num_msgs, int addr); + +/** + * 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 I2C emulator to use + * @return 0 indicating success (always) + */ +int i2c_emul_register(struct device *dev, const char *name, + struct i2c_emul *emul); + +/** Definition of the emulator API */ +struct i2c_emul_api { + i2c_emul_transfer_t transfer; +}; + +/** + * Back door to allow an emulator to retrieve the host configuration. + * + * @param dev I2C device associated with the emulator + * @return Bit-packed 32-bit value containing the device's runtime configuration + */ +uint32_t i2c_emul_get_config(const struct device *dev); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_I2C_I2C_EMUL_H_ */