zephyr/include/zephyr/drivers/emul.h
Swift Tian f5554ca762 emul: mspi: Add the mspi controller emulator
Add bus emulator support for MSPI and the MSPI controller emulator.
The mspi_emul.c not only serves as an emulator but also provides an
example implementation of the MSPI API. It does not actually do anything
other than validating parameters and forwarding transceive request back
to the device driver emulators.

Signed-off-by: Swift Tian <swift.tian@ambiq.com>
2024-06-14 21:07:00 -04:00

230 lines
8.3 KiB
C

/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright 2020 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_EMUL_H_
#define ZEPHYR_INCLUDE_DRIVERS_EMUL_H_
/**
* @brief Emulators used to test drivers and higher-level code that uses them
* @defgroup io_emulators Emulator interface
* @ingroup testing
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct emul;
/* #includes required after forward-declaration of struct emul later defined in this header. */
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/espi_emul.h>
#include <zephyr/drivers/i2c_emul.h>
#include <zephyr/drivers/spi_emul.h>
#include <zephyr/drivers/mspi_emul.h>
#include <zephyr/sys/iterable_sections.h>
/**
* The types of supported buses.
*/
enum emul_bus_type {
EMUL_BUS_TYPE_I2C,
EMUL_BUS_TYPE_ESPI,
EMUL_BUS_TYPE_SPI,
EMUL_BUS_TYPE_MSPI,
EMUL_BUS_TYPE_NONE,
};
/**
* Structure uniquely identifying a device to be emulated
*/
struct emul_link_for_bus {
const struct device *dev;
};
/** List of emulators attached to a bus */
struct emul_list_for_bus {
/** Identifiers for children of the node */
const struct emul_link_for_bus *children;
/** Number of children of the node */
unsigned int num_children;
};
/**
* Standard callback for emulator initialisation providing the initialiser
* record and the device that calls the emulator functions.
*
* @param emul Emulator to init
* @param parent Parent device that is using the emulator
*/
typedef int (*emul_init_t)(const struct emul *emul, const struct device *parent);
/**
* Emulator API stub when an emulator is not actually placed on a bus.
*/
struct no_bus_emul {
void *api;
uint16_t addr;
};
/** An emulator instance - represents the *target* emulated device/peripheral that is
* interacted with through an emulated bus. Instances of emulated bus nodes (e.g. i2c_emul)
* and emulators (i.e. struct emul) are exactly 1..1
*/
struct emul {
/** function used to initialise the emulator state */
emul_init_t init;
/** handle to the device for which this provides low-level emulation */
const struct device *dev;
/** Emulator-specific configuration data */
const void *cfg;
/** Emulator-specific data */
void *data;
/** The bus type that the emulator is attached to */
enum emul_bus_type bus_type;
/** Pointer to the emulated bus node */
union bus {
struct i2c_emul *i2c;
struct espi_emul *espi;
struct spi_emul *spi;
struct mspi_emul *mspi;
struct no_bus_emul *none;
} bus;
/** Address of the API structure exposed by the emulator instance */
const void *backend_api;
};
/**
* @brief Use the devicetree node identifier as a unique name.
*
* @param node_id A devicetree node identifier
*/
#define EMUL_DT_NAME_GET(node_id) _CONCAT(__emulreg_, node_id)
/* Get a unique identifier based on the given _dev_node_id's reg property and
* the bus its on. Intended for use in other internal macros when declaring {bus}_emul
* structs in peripheral emulators.
*/
#define Z_EMUL_REG_BUS_IDENTIFIER(_dev_node_id) (_CONCAT(_CONCAT(__emulreg_, _dev_node_id), _bus))
/* Conditionally places text based on what bus _dev_node_id is on. */
#define Z_EMUL_BUS(_dev_node_id, _i2c, _espi, _spi, _mspi, _none) \
COND_CODE_1(DT_ON_BUS(_dev_node_id, i2c), (_i2c), \
(COND_CODE_1(DT_ON_BUS(_dev_node_id, espi), (_espi), \
(COND_CODE_1(DT_ON_BUS(_dev_node_id, spi), (_spi), \
(COND_CODE_1(DT_ON_BUS(_dev_node_id, mspi), (_mspi), \
(_none))))))))
/**
* @brief Define a new emulator
*
* This adds a new struct emul to the linker list of emulations. This is
* typically used in your emulator's DT_INST_FOREACH_STATUS_OKAY() clause.
*
* @param node_id Node ID of the driver to emulate (e.g. DT_DRV_INST(n)); the node_id *MUST* have a
* corresponding DEVICE_DT_DEFINE().
* @param init_fn function to call to initialise the emulator (see emul_init typedef)
* @param data_ptr emulator-specific data
* @param cfg_ptr emulator-specific configuration data
* @param bus_api emulator-specific bus api
* @param _backend_api emulator-specific backend api
*/
#define EMUL_DT_DEFINE(node_id, init_fn, data_ptr, cfg_ptr, bus_api, _backend_api) \
static struct Z_EMUL_BUS(node_id, i2c_emul, espi_emul, spi_emul, mspi_emul, no_bus_emul) \
Z_EMUL_REG_BUS_IDENTIFIER(node_id) = { \
.api = bus_api, \
.Z_EMUL_BUS(node_id, addr, chipsel, chipsel, dev_idx, addr) = \
DT_REG_ADDR(node_id), \
}; \
const STRUCT_SECTION_ITERABLE(emul, EMUL_DT_NAME_GET(node_id)) __used = { \
.init = (init_fn), \
.dev = DEVICE_DT_GET(node_id), \
.cfg = (cfg_ptr), \
.data = (data_ptr), \
.bus_type = Z_EMUL_BUS(node_id, EMUL_BUS_TYPE_I2C, EMUL_BUS_TYPE_ESPI, \
EMUL_BUS_TYPE_SPI, EMUL_BUS_TYPE_MSPI, EMUL_BUS_TYPE_NONE), \
.bus = {.Z_EMUL_BUS(node_id, i2c, espi, spi, mspi, none) = \
&(Z_EMUL_REG_BUS_IDENTIFIER(node_id))}, \
.backend_api = (_backend_api), \
};
/**
* @brief Like EMUL_DT_DEFINE(), but uses an instance of a DT_DRV_COMPAT compatible instead of a
* node identifier.
*
* @param inst instance number. The @p node_id argument to EMUL_DT_DEFINE is set to
* <tt>DT_DRV_INST(inst)</tt>.
* @param ... other parameters as expected by EMUL_DT_DEFINE.
*/
#define EMUL_DT_INST_DEFINE(inst, ...) EMUL_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)
/**
* @brief Get a <tt>const struct emul*</tt> from a devicetree node identifier
*
* @details Returns a pointer to an emulator object created from a devicetree
* node, if any device was allocated by an emulator implementation.
*
* If no such device was allocated, this will fail at linker time. If you get an
* error that looks like <tt>undefined reference to __device_dts_ord_<N></tt>,
* that is what happened. Check to make sure your emulator implementation is
* being compiled, usually by enabling the Kconfig options it requires.
*
* @param node_id A devicetree node identifier
* @return A pointer to the emul object created for that node
*/
#define EMUL_DT_GET(node_id) (&EMUL_DT_NAME_GET(node_id))
/**
* @brief Utility macro to obtain an optional reference to an emulator
*
* If the node identifier referes to a node with status `okay`, this returns `EMUL_DT_GET(node_id)`.
* Otherwise, it returns `NULL`.
*
* @param node_id A devicetree node identifier
* @return a @ref emul reference for the node identifier, which may be `NULL`.
*/
#define EMUL_DT_GET_OR_NULL(node_id) \
COND_CODE_1(DT_NODE_HAS_STATUS(node_id, okay), (EMUL_DT_GET(node_id)), (NULL))
/**
* @brief Set up a list of emulators
*
* @param dev Device the emulators are attached to (e.g. an I2C controller)
* @return 0 if OK
* @return negative value on error
*/
int emul_init_for_bus(const struct device *dev);
/**
* @brief Retrieve the emul structure for an emulator by name
*
* @details Emulator objects are created via the EMUL_DT_DEFINE() macro and placed in memory by the
* linker. If the emulator structure is needed for custom API calls, it can be retrieved by the name
* that the emulator exposes to the system (this is the devicetree node's label by default).
*
* @param name Emulator name to search for. A null pointer, or a pointer to an
* empty string, will cause NULL to be returned.
*
* @return pointer to emulator structure; NULL if not found or cannot be used.
*/
const struct emul *emul_get_binding(const char *name);
/**
* @}
*/
#define Z_MAYBE_EMUL_DECLARE_INTERNAL(node_id) extern const struct emul EMUL_DT_NAME_GET(node_id);
DT_FOREACH_STATUS_OKAY_NODE(Z_MAYBE_EMUL_DECLARE_INTERNAL);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* ZEPHYR_INCLUDE_DRIVERS_EMUL_H_ */