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:
parent
5a9f389159
commit
a1d8e55cad
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
10
drivers/spi/Kconfig.spi_emul
Normal file
10
drivers/spi/Kconfig.spi_emul
Normal 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
142
drivers/spi/spi_emul.c
Normal 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)
|
12
dts/bindings/spi/zephyr,spi-emul.yaml
Normal file
12
dts/bindings/spi/zephyr,spi-emul.yaml
Normal 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
|
97
include/drivers/spi_emul.h
Normal file
97
include/drivers/spi_emul.h
Normal 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_ */
|
Loading…
Reference in a new issue