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 <sjg@chromium.org> Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
parent
302d671ea2
commit
49f2167974
7 changed files with 275 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
10
drivers/i2c/Kconfig.i2c_emul
Normal file
10
drivers/i2c/Kconfig.i2c_emul
Normal file
|
@ -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.
|
155
drivers/i2c/i2c_emul.c
Normal file
155
drivers/i2c/i2c_emul.c
Normal file
|
@ -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 <logging/log.h>
|
||||
LOG_MODULE_REGISTER(i2c_emul_ctlr);
|
||||
|
||||
#include <device.h>
|
||||
#include <emul.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <drivers/i2c_emul.h>
|
||||
|
||||
/** 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)
|
12
dts/bindings/i2c/zephyr,i2c-emul.yaml
Normal file
12
dts/bindings/i2c/zephyr,i2c-emul.yaml
Normal file
|
@ -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
|
92
include/drivers/i2c_emul.h
Normal file
92
include/drivers/i2c_emul.h
Normal file
|
@ -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 <zephyr/types.h>
|
||||
#include <device.h>
|
||||
|
||||
#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_ */
|
Loading…
Add table
Add a link
Reference in a new issue