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:
Simon Glass 2020-08-20 08:14:35 -06:00 committed by Anas Nashif
commit 49f2167974
7 changed files with 275 additions and 0 deletions

View file

@ -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

View file

@ -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)

View file

@ -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"

View 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
View 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)

View 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

View 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_ */