emul: spi: Add support for SPI emulators

Add an emulation controller which routes SPI traffic to an attached
emulator. Only one emulator is supported per bus at present, since
chip-selction functionality is not present.

This allows drivers for SPI peripherals to be tested on systems that
don't have that peripheral attached, with the emulator handling the SPI
traffic.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2020-09-18 16:31:23 -06:00 committed by Anas Nashif
parent 5a9f389159
commit a1d8e55cad
6 changed files with 264 additions and 0 deletions

View file

@ -4,6 +4,7 @@ zephyr_library()
zephyr_library_sources_ifdef(CONFIG_SPI_CC13XX_CC26XX spi_cc13xx_cc26xx.c)
zephyr_library_sources_ifdef(CONFIG_SPI_DW spi_dw.c)
zephyr_library_sources_ifdef(CONFIG_SPI_EMUL spi_emul.c)
zephyr_library_sources_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c)
zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_DSPI spi_mcux_dspi.c)
zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_FLEXCOMM spi_mcux_flexcomm.c)

View file

@ -207,6 +207,8 @@ source "drivers/spi/Kconfig.sam0"
source "drivers/spi/Kconfig.sifive"
source "drivers/spi/Kconfig.spi_emul"
source "drivers/spi/Kconfig.nrfx"
source "drivers/spi/Kconfig.cc13xx_cc26xx"

View file

@ -0,0 +1,10 @@
# Copyright 2020 Google LLC
# SPDX-License-Identifier: Apache-2.0
config SPI_EMUL
bool "SPI emulator"
help
Enable the SPI 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 SPI bus. It is
used for testing drivers for SPI devices.

142
drivers/spi/spi_emul.c Normal file
View file

@ -0,0 +1,142 @@
/*
* Copyright 2020 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*
* This driver creates fake SPI buses which can contain emulated devices,
* implemented by a separate emulation driver. The API between this driver and
* its emulators is defined by struct spi_emul_driver_api.
*/
#define DT_DRV_COMPAT zephyr_spi_emul_controller
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(spi_emul_ctlr);
#include <device.h>
#include <emul.h>
#include <drivers/spi.h>
#include <drivers/spi_emul.h>
/** Working data for the device */
struct spi_emul_data {
/* List of struct spi_emul associated with the device */
sys_slist_t emuls;
/* SPI host configuration */
uint32_t config;
};
uint32_t spi_emul_get_config(const struct device *dev)
{
struct spi_emul_data *data = dev->data;
return data->config;
}
/**
* Find an emulator for a SPI bus
*
* At present only a single emulator is supported on the bus, since we do not
* support chip selects, despite there being a chipsel field. It cannot be
* implemented until we have a GPIO emulator.
*
* @param dev SPI emulation controller device
* @param chipsel Chip-select value
* @return emulator to use
* @return NULL if not found
*/
static struct spi_emul *spi_emul_find(const struct device *dev,
unsigned int chipsel)
{
struct spi_emul_data *data = dev->data;
sys_snode_t *node;
SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) {
struct spi_emul *emul;
emul = CONTAINER_OF(node, struct spi_emul, node);
if (emul->chipsel == chipsel) {
return emul;
}
}
return NULL;
}
static int spi_emul_io(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
{
struct spi_emul *emul;
const struct spi_emul_api *api;
emul = spi_emul_find(dev, config->slave);
if (!emul) {
return -EIO;
}
api = emul->api;
__ASSERT_NO_MSG(emul->api);
__ASSERT_NO_MSG(emul->api->io);
return api->io(emul, config, tx_bufs, rx_bufs);
}
/**
* Set up a new emulator and add it to the list
*
* @param dev SPI emulation controller device
*/
static int spi_emul_init(const struct device *dev)
{
struct spi_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 spi_emul_register(const struct device *dev, const char *name,
struct spi_emul *emul)
{
struct spi_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 spi_driver_api spi_emul_api = {
.transceive = spi_emul_io,
};
#define EMUL_LINK_AND_COMMA(node_id) { \
.label = DT_LABEL(node_id), \
},
#define SPI_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 spi_emul_cfg_##n = { \
.children = emuls_##n, \
.num_children = ARRAY_SIZE(emuls_##n), \
}; \
static struct spi_emul_data spi_emul_data_##n; \
DEVICE_AND_API_INIT(spi_##n, \
DT_INST_LABEL(n), \
spi_emul_init, \
&spi_emul_data_##n, \
&spi_emul_cfg_##n, \
POST_KERNEL, \
CONFIG_SPI_INIT_PRIORITY, \
&spi_emul_api);
DT_INST_FOREACH_STATUS_OKAY(SPI_EMUL_INIT)

View file

@ -0,0 +1,12 @@
# Copyright 2020 Google LLC
# SPDX-License-Identifier: Apache-2.0
description: Zephyr SPI Emulation controller
compatible: "zephyr,spi-emul-controller"
include: spi-controller.yaml
properties:
reg:
required: true

View file

@ -0,0 +1,97 @@
/*
* Copyright 2020 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_SPI_SPI_EMUL_H_
#define ZEPHYR_INCLUDE_DRIVERS_SPI_SPI_EMUL_H_
/**
* @file
*
* @brief Public APIs for the SPI emulation drivers.
*/
#include <zephyr/types.h>
#include <device.h>
/**
* @brief SPI Emulation Interface
* @defgroup spi_emul_interface SPI Emulation Interface
* @ingroup io_emulators
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
struct spi_msg;
struct spi_emul_api;
/** Node in a linked list of emulators for SPI devices */
struct spi_emul {
sys_snode_t node;
/* API provided for this device */
const struct spi_emul_api *api;
/* SPI chip-slect of the emulated device */
uint16_t chipsel;
};
/**
* Passes SPI messages to the emulator. The emulator updates the data with what
* was read back.
*
* @param emul Emulator instance
* @param config Pointer to a valid spi_config structure instance.
* Pointer-comparison may be used to detect changes from
* previous operations.
* @param tx_bufs Buffer array where data to be sent originates from,
* or NULL if none.
* @param rx_bufs Buffer array where data to be read will be written to,
* or NULL if none.
*
* @retval 0 If successful.
* @retval -EIO General input / output error.
*/
typedef int (*spi_emul_io_t)(struct spi_emul *emul,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs);
/**
* 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 SPI emulator to use
* @return 0 indicating success (always)
*/
int spi_emul_register(const struct device *dev, const char *name,
struct spi_emul *emul);
/** Definition of the emulator API */
struct spi_emul_api {
spi_emul_io_t io;
};
/**
* Back door to allow an emulator to retrieve the host configuration.
*
* @param dev SPI device associated with the emulator
* @return Bit-packed 32-bit value containing the device's runtime configuration
*/
uint32_t spi_emul_get_config(const struct device *dev);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_DRIVERS_SPI_SPI_EMUL_H_ */