`spi_transceive_async`, `spi_read_async` and `spi_write_async` are deprecated since v3.2. Signed-off-by: Bartosz Bilas <bartosz.bilas@hotmail.com>
1273 lines
38 KiB
C
1273 lines
38 KiB
C
/*
|
|
* Copyright (c) 2015 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* @brief Public API for SPI drivers and applications
|
|
*/
|
|
|
|
#ifndef ZEPHYR_INCLUDE_DRIVERS_SPI_H_
|
|
#define ZEPHYR_INCLUDE_DRIVERS_SPI_H_
|
|
|
|
/**
|
|
* @brief SPI Interface
|
|
* @defgroup spi_interface SPI Interface
|
|
* @since 1.0
|
|
* @version 1.0.0
|
|
* @ingroup io_interfaces
|
|
* @{
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
#include <stddef.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/dt-bindings/spi/spi.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/rtio/rtio.h>
|
|
#include <zephyr/stats/stats.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @name SPI operational mode
|
|
* @{
|
|
*/
|
|
#define SPI_OP_MODE_MASTER 0U /**< Master mode. */
|
|
#define SPI_OP_MODE_SLAVE BIT(0) /**< Slave mode. */
|
|
/** @cond INTERNAL_HIDDEN */
|
|
#define SPI_OP_MODE_MASK 0x1U
|
|
/** @endcond */
|
|
/** Get SPI operational mode. */
|
|
#define SPI_OP_MODE_GET(_operation_) ((_operation_) & SPI_OP_MODE_MASK)
|
|
/** @} */
|
|
|
|
/**
|
|
* @name SPI Polarity & Phase Modes
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Clock Polarity: if set, clock idle state will be 1
|
|
* and active state will be 0. If untouched, the inverse will be true
|
|
* which is the default.
|
|
*/
|
|
#define SPI_MODE_CPOL BIT(1)
|
|
|
|
/**
|
|
* Clock Phase: this dictates when is the data captured, and depends
|
|
* clock's polarity. When SPI_MODE_CPOL is set and this bit as well,
|
|
* capture will occur on low to high transition and high to low if
|
|
* this bit is not set (default). This is fully reversed if CPOL is
|
|
* not set.
|
|
*/
|
|
#define SPI_MODE_CPHA BIT(2)
|
|
|
|
/**
|
|
* Whatever data is transmitted is looped-back to the receiving buffer of
|
|
* the controller. This is fully controller dependent as some may not
|
|
* support this, and can be used for testing purposes only.
|
|
*/
|
|
#define SPI_MODE_LOOP BIT(3)
|
|
/** @cond INTERNAL_HIDDEN */
|
|
#define SPI_MODE_MASK (0xEU)
|
|
/** @endcond */
|
|
/** Get SPI polarity and phase mode bits. */
|
|
#define SPI_MODE_GET(_mode_) \
|
|
((_mode_) & SPI_MODE_MASK)
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name SPI Transfer modes (host controller dependent)
|
|
* @{
|
|
*/
|
|
#define SPI_TRANSFER_MSB (0U) /**< Most significant bit first. */
|
|
#define SPI_TRANSFER_LSB BIT(4) /**< Least significant bit first. */
|
|
/** @} */
|
|
|
|
/**
|
|
* @name SPI word size
|
|
* @{
|
|
*/
|
|
/** @cond INTERNAL_HIDDEN */
|
|
#define SPI_WORD_SIZE_SHIFT (5U)
|
|
#define SPI_WORD_SIZE_MASK (0x3FU << SPI_WORD_SIZE_SHIFT)
|
|
/** @endcond */
|
|
/** Get SPI word size. */
|
|
#define SPI_WORD_SIZE_GET(_operation_) \
|
|
(((_operation_) & SPI_WORD_SIZE_MASK) >> SPI_WORD_SIZE_SHIFT)
|
|
/** Set SPI word size. */
|
|
#define SPI_WORD_SET(_word_size_) \
|
|
((_word_size_) << SPI_WORD_SIZE_SHIFT)
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Specific SPI devices control bits
|
|
* @{
|
|
*/
|
|
/** Requests - if possible - to keep CS asserted after the transaction */
|
|
#define SPI_HOLD_ON_CS BIT(12)
|
|
/** Keep the device locked after the transaction for the current config.
|
|
* Use this with extreme caution (see spi_release() below) as it will
|
|
* prevent other callers to access the SPI device until spi_release() is
|
|
* properly called.
|
|
*/
|
|
#define SPI_LOCK_ON BIT(13)
|
|
|
|
/** Active high logic on CS. Usually, and by default, CS logic is active
|
|
* low. However, some devices may require the reverse logic: active high.
|
|
* This bit will request the controller to use that logic. Note that not
|
|
* all controllers are able to handle that natively. In this case deferring
|
|
* the CS control to a gpio line through struct spi_cs_control would be
|
|
* the solution.
|
|
*/
|
|
#define SPI_CS_ACTIVE_HIGH BIT(14)
|
|
/** @} */
|
|
|
|
/**
|
|
* @name SPI MISO lines
|
|
* @{
|
|
*
|
|
* Some controllers support dual, quad or octal MISO lines connected to slaves.
|
|
* Default is single, which is the case most of the time.
|
|
* Without @kconfig{CONFIG_SPI_EXTENDED_MODES} being enabled, single is the
|
|
* only supported one.
|
|
*/
|
|
#define SPI_LINES_SINGLE (0U << 16) /**< Single line */
|
|
#define SPI_LINES_DUAL (1U << 16) /**< Dual lines */
|
|
#define SPI_LINES_QUAD (2U << 16) /**< Quad lines */
|
|
#define SPI_LINES_OCTAL (3U << 16) /**< Octal lines */
|
|
|
|
#define SPI_LINES_MASK (0x3U << 16) /**< Mask for MISO lines in spi_operation_t */
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @brief SPI Chip Select control structure
|
|
*
|
|
* This can be used to control a CS line via a GPIO line, instead of
|
|
* using the controller inner CS logic.
|
|
*
|
|
*/
|
|
struct spi_cs_control {
|
|
/**
|
|
* GPIO devicetree specification of CS GPIO.
|
|
* The device pointer can be set to NULL to fully inhibit CS control if
|
|
* necessary. The GPIO flags GPIO_ACTIVE_LOW/GPIO_ACTIVE_HIGH should be
|
|
* equivalent to SPI_CS_ACTIVE_HIGH/SPI_CS_ACTIVE_LOW options in struct
|
|
* spi_config.
|
|
*/
|
|
struct gpio_dt_spec gpio;
|
|
/**
|
|
* Delay in microseconds to wait before starting the
|
|
* transmission and before releasing the CS line.
|
|
*/
|
|
uint32_t delay;
|
|
};
|
|
|
|
/**
|
|
* @brief Get a <tt>struct gpio_dt_spec</tt> for a SPI device's chip select pin
|
|
*
|
|
* Example devicetree fragment:
|
|
*
|
|
* @code{.devicetree}
|
|
* gpio1: gpio@abcd0001 { ... };
|
|
*
|
|
* gpio2: gpio@abcd0002 { ... };
|
|
*
|
|
* spi@abcd0003 {
|
|
* compatible = "vnd,spi";
|
|
* cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>,
|
|
* <&gpio2 20 GPIO_ACTIVE_LOW>;
|
|
*
|
|
* a: spi-dev-a@0 {
|
|
* reg = <0>;
|
|
* };
|
|
*
|
|
* b: spi-dev-b@1 {
|
|
* reg = <1>;
|
|
* };
|
|
* };
|
|
* @endcode
|
|
*
|
|
* Example usage:
|
|
*
|
|
* @code{.c}
|
|
* SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(a)) \
|
|
* // { DEVICE_DT_GET(DT_NODELABEL(gpio1)), 10, GPIO_ACTIVE_LOW }
|
|
* SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(b)) \
|
|
* // { DEVICE_DT_GET(DT_NODELABEL(gpio2)), 20, GPIO_ACTIVE_LOW }
|
|
* @endcode
|
|
*
|
|
* @param spi_dev a SPI device node identifier
|
|
* @return #gpio_dt_spec struct corresponding with spi_dev's chip select
|
|
*/
|
|
#define SPI_CS_GPIOS_DT_SPEC_GET(spi_dev) \
|
|
GPIO_DT_SPEC_GET_BY_IDX_OR(DT_BUS(spi_dev), cs_gpios, \
|
|
DT_REG_ADDR(spi_dev), {})
|
|
|
|
/**
|
|
* @brief Get a <tt>struct gpio_dt_spec</tt> for a SPI device's chip select pin
|
|
*
|
|
* This is equivalent to
|
|
* <tt>SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst))</tt>.
|
|
*
|
|
* @param inst Devicetree instance number
|
|
* @return #gpio_dt_spec struct corresponding with spi_dev's chip select
|
|
*/
|
|
#define SPI_CS_GPIOS_DT_SPEC_INST_GET(inst) \
|
|
SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst))
|
|
|
|
/**
|
|
* @brief Initialize and get a pointer to a @p spi_cs_control from a
|
|
* devicetree node identifier
|
|
*
|
|
* This helper is useful for initializing a device on a SPI bus. It
|
|
* initializes a struct spi_cs_control and returns a pointer to it.
|
|
* Here, @p node_id is a node identifier for a SPI device, not a SPI
|
|
* controller.
|
|
*
|
|
* Example devicetree fragment:
|
|
*
|
|
* @code{.devicetree}
|
|
* spi@abcd0001 {
|
|
* cs-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
|
* spidev: spi-device@0 { ... };
|
|
* };
|
|
* @endcode
|
|
*
|
|
* Example usage:
|
|
*
|
|
* @code{.c}
|
|
* struct spi_cs_control ctrl =
|
|
* SPI_CS_CONTROL_INIT(DT_NODELABEL(spidev), 2);
|
|
* @endcode
|
|
*
|
|
* This example is equivalent to:
|
|
*
|
|
* @code{.c}
|
|
* struct spi_cs_control ctrl = {
|
|
* .gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(spidev)),
|
|
* .delay = 2,
|
|
* };
|
|
* @endcode
|
|
*
|
|
* @param node_id Devicetree node identifier for a device on a SPI bus
|
|
* @param delay_ The @p delay field to set in the @p spi_cs_control
|
|
* @return a pointer to the @p spi_cs_control structure
|
|
*/
|
|
#define SPI_CS_CONTROL_INIT(node_id, delay_) \
|
|
{ \
|
|
.gpio = SPI_CS_GPIOS_DT_SPEC_GET(node_id), \
|
|
.delay = (delay_), \
|
|
}
|
|
|
|
/**
|
|
* @brief Get a pointer to a @p spi_cs_control from a devicetree node
|
|
*
|
|
* This is equivalent to
|
|
* <tt>SPI_CS_CONTROL_INIT(DT_DRV_INST(inst), delay)</tt>.
|
|
*
|
|
* Therefore, @p DT_DRV_COMPAT must already be defined before using
|
|
* this macro.
|
|
*
|
|
* @param inst Devicetree node instance number
|
|
* @param delay_ The @p delay field to set in the @p spi_cs_control
|
|
* @return a pointer to the @p spi_cs_control structure
|
|
*/
|
|
#define SPI_CS_CONTROL_INIT_INST(inst, delay_) \
|
|
SPI_CS_CONTROL_INIT(DT_DRV_INST(inst), delay_)
|
|
|
|
/**
|
|
* @typedef spi_operation_t
|
|
* Opaque type to hold the SPI operation flags.
|
|
*/
|
|
#if defined(CONFIG_SPI_EXTENDED_MODES)
|
|
typedef uint32_t spi_operation_t;
|
|
#else
|
|
typedef uint16_t spi_operation_t;
|
|
#endif
|
|
|
|
/**
|
|
* @brief SPI controller configuration structure
|
|
*/
|
|
struct spi_config {
|
|
/** @brief Bus frequency in Hertz. */
|
|
uint32_t frequency;
|
|
/**
|
|
* @brief Operation flags.
|
|
*
|
|
* It is a bit field with the following parts:
|
|
*
|
|
* - 0: Master or slave.
|
|
* - 1..3: Polarity, phase and loop mode.
|
|
* - 4: LSB or MSB first.
|
|
* - 5..10: Size of a data frame in bits.
|
|
* - 11: Full/half duplex.
|
|
* - 12: Hold on the CS line if possible.
|
|
* - 13: Keep resource locked for the caller.
|
|
* - 14: Active high CS logic.
|
|
* - 15: Motorola or TI frame format (optional).
|
|
*
|
|
* If @kconfig{CONFIG_SPI_EXTENDED_MODES} is enabled:
|
|
*
|
|
* - 16..17: MISO lines (Single/Dual/Quad/Octal).
|
|
* - 18..31: Reserved for future use.
|
|
*/
|
|
spi_operation_t operation;
|
|
/** @brief Slave number from 0 to host controller slave limit. */
|
|
uint16_t slave;
|
|
/**
|
|
* @brief GPIO chip-select line (optional, must be initialized to zero
|
|
* if not used).
|
|
*/
|
|
struct spi_cs_control cs;
|
|
};
|
|
|
|
/**
|
|
* @brief Structure initializer for spi_config from devicetree
|
|
*
|
|
* This helper macro expands to a static initializer for a <tt>struct
|
|
* spi_config</tt> by reading the relevant @p frequency, @p slave, and
|
|
* @p cs data from the devicetree.
|
|
*
|
|
* @param node_id Devicetree node identifier for the SPI device whose
|
|
* struct spi_config to create an initializer for
|
|
* @param operation_ the desired @p operation field in the struct spi_config
|
|
* @param delay_ the desired @p delay field in the struct spi_config's
|
|
* spi_cs_control, if there is one
|
|
*/
|
|
#define SPI_CONFIG_DT(node_id, operation_, delay_) \
|
|
{ \
|
|
.frequency = DT_PROP(node_id, spi_max_frequency), \
|
|
.operation = (operation_) | \
|
|
DT_PROP(node_id, duplex) | \
|
|
DT_PROP(node_id, frame_format) | \
|
|
COND_CODE_1(DT_PROP(node_id, spi_cpol), SPI_MODE_CPOL, (0)) | \
|
|
COND_CODE_1(DT_PROP(node_id, spi_cpha), SPI_MODE_CPHA, (0)) | \
|
|
COND_CODE_1(DT_PROP(node_id, spi_hold_cs), SPI_HOLD_ON_CS, (0)), \
|
|
.slave = DT_REG_ADDR(node_id), \
|
|
.cs = SPI_CS_CONTROL_INIT(node_id, delay_), \
|
|
}
|
|
|
|
/**
|
|
* @brief Structure initializer for spi_config from devicetree instance
|
|
*
|
|
* This is equivalent to
|
|
* <tt>SPI_CONFIG_DT(DT_DRV_INST(inst), operation_, delay_)</tt>.
|
|
*
|
|
* @param inst Devicetree instance number
|
|
* @param operation_ the desired @p operation field in the struct spi_config
|
|
* @param delay_ the desired @p delay field in the struct spi_config's
|
|
* spi_cs_control, if there is one
|
|
*/
|
|
#define SPI_CONFIG_DT_INST(inst, operation_, delay_) \
|
|
SPI_CONFIG_DT(DT_DRV_INST(inst), operation_, delay_)
|
|
|
|
/**
|
|
* @brief Complete SPI DT information
|
|
*/
|
|
struct spi_dt_spec {
|
|
/** SPI bus */
|
|
const struct device *bus;
|
|
/** Slave specific configuration */
|
|
struct spi_config config;
|
|
};
|
|
|
|
/**
|
|
* @brief Structure initializer for spi_dt_spec from devicetree
|
|
*
|
|
* This helper macro expands to a static initializer for a <tt>struct
|
|
* spi_dt_spec</tt> by reading the relevant bus, frequency, slave, and cs
|
|
* data from the devicetree.
|
|
*
|
|
* Important: multiple fields are automatically constructed by this macro
|
|
* which must be checked before use. @ref spi_is_ready_dt performs the required
|
|
* @ref device_is_ready checks.
|
|
*
|
|
* @param node_id Devicetree node identifier for the SPI device whose
|
|
* struct spi_dt_spec to create an initializer for
|
|
* @param operation_ the desired @p operation field in the struct spi_config
|
|
* @param delay_ the desired @p delay field in the struct spi_config's
|
|
* spi_cs_control, if there is one
|
|
*/
|
|
#define SPI_DT_SPEC_GET(node_id, operation_, delay_) \
|
|
{ \
|
|
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
|
|
.config = SPI_CONFIG_DT(node_id, operation_, delay_) \
|
|
}
|
|
|
|
/**
|
|
* @brief Structure initializer for spi_dt_spec from devicetree instance
|
|
*
|
|
* This is equivalent to
|
|
* <tt>SPI_DT_SPEC_GET(DT_DRV_INST(inst), operation_, delay_)</tt>.
|
|
*
|
|
* @param inst Devicetree instance number
|
|
* @param operation_ the desired @p operation field in the struct spi_config
|
|
* @param delay_ the desired @p delay field in the struct spi_config's
|
|
* spi_cs_control, if there is one
|
|
*/
|
|
#define SPI_DT_SPEC_INST_GET(inst, operation_, delay_) \
|
|
SPI_DT_SPEC_GET(DT_DRV_INST(inst), operation_, delay_)
|
|
|
|
/**
|
|
* @brief SPI buffer structure
|
|
*/
|
|
struct spi_buf {
|
|
/** Valid pointer to a data buffer, or NULL otherwise */
|
|
void *buf;
|
|
/** Length of the buffer @a buf.
|
|
* If @a buf is NULL, length which as to be sent as dummy bytes (as TX
|
|
* buffer) or the length of bytes that should be skipped (as RX buffer).
|
|
*/
|
|
size_t len;
|
|
};
|
|
|
|
/**
|
|
* @brief SPI buffer array structure
|
|
*/
|
|
struct spi_buf_set {
|
|
/** Pointer to an array of spi_buf, or NULL */
|
|
const struct spi_buf *buffers;
|
|
/** Length of the array pointed by @a buffers */
|
|
size_t count;
|
|
};
|
|
|
|
#if defined(CONFIG_SPI_STATS)
|
|
STATS_SECT_START(spi)
|
|
STATS_SECT_ENTRY32(rx_bytes)
|
|
STATS_SECT_ENTRY32(tx_bytes)
|
|
STATS_SECT_ENTRY32(transfer_error)
|
|
STATS_SECT_END;
|
|
|
|
STATS_NAME_START(spi)
|
|
STATS_NAME(spi, rx_bytes)
|
|
STATS_NAME(spi, tx_bytes)
|
|
STATS_NAME(spi, transfer_error)
|
|
STATS_NAME_END(spi);
|
|
|
|
/**
|
|
* @brief SPI specific device state which allows for SPI device class specific additions
|
|
*/
|
|
struct spi_device_state {
|
|
struct device_state devstate;
|
|
struct stats_spi stats;
|
|
};
|
|
|
|
/**
|
|
* @brief Get pointer to SPI statistics structure
|
|
*/
|
|
#define Z_SPI_GET_STATS(dev_) \
|
|
CONTAINER_OF(dev_->state, struct spi_device_state, devstate)->stats
|
|
|
|
/**
|
|
* @brief Increment the rx bytes for a SPI device
|
|
*
|
|
* @param dev_ Pointer to the device structure for the driver instance.
|
|
*/
|
|
#define SPI_STATS_RX_BYTES_INCN(dev_, n) \
|
|
STATS_INCN(Z_SPI_GET_STATS(dev_), rx_bytes, n)
|
|
|
|
/**
|
|
* @brief Increment the tx bytes for a SPI device
|
|
*
|
|
* @param dev_ Pointer to the device structure for the driver instance.
|
|
*/
|
|
#define SPI_STATS_TX_BYTES_INCN(dev_, n) \
|
|
STATS_INCN(Z_SPI_GET_STATS(dev_), tx_bytes, n)
|
|
|
|
/**
|
|
* @brief Increment the transfer error counter for a SPI device
|
|
*
|
|
* The transfer error count is incremented when there occurred a transfer error
|
|
*
|
|
* @param dev_ Pointer to the device structure for the driver instance.
|
|
*/
|
|
#define SPI_STATS_TRANSFER_ERROR_INC(dev_) \
|
|
STATS_INC(Z_SPI_GET_STATS(dev_), transfer_error)
|
|
|
|
/**
|
|
* @brief Define a statically allocated and section assigned SPI device state
|
|
*/
|
|
#define Z_SPI_DEVICE_STATE_DEFINE(dev_id) \
|
|
static struct spi_device_state Z_DEVICE_STATE_NAME(dev_id) \
|
|
__attribute__((__section__(".z_devstate")));
|
|
|
|
/**
|
|
* @brief Define an SPI device init wrapper function
|
|
*
|
|
* This does device instance specific initialization of common data (such as stats)
|
|
* and calls the given init_fn
|
|
*/
|
|
#define Z_SPI_INIT_FN(dev_id, init_fn) \
|
|
static inline int UTIL_CAT(dev_id, _init)(const struct device *dev) \
|
|
{ \
|
|
struct spi_device_state *state = \
|
|
CONTAINER_OF(dev->state, struct spi_device_state, devstate); \
|
|
stats_init(&state->stats.s_hdr, STATS_SIZE_32, 3, \
|
|
STATS_NAME_INIT_PARMS(spi)); \
|
|
stats_register(dev->name, &(state->stats.s_hdr)); \
|
|
return init_fn(dev); \
|
|
}
|
|
|
|
/**
|
|
* @brief Like DEVICE_DT_DEFINE() with SPI specifics.
|
|
*
|
|
* @details Defines a device which implements the SPI API. May
|
|
* generate a custom device_state container struct and init_fn
|
|
* wrapper when needed depending on SPI @kconfig{CONFIG_SPI_STATS}.
|
|
*
|
|
* @param node_id The devicetree node identifier.
|
|
* @param init_fn Name of the init function of the driver.
|
|
* @param pm_device PM device resources reference (NULL if device does not use PM).
|
|
* @param data_ptr Pointer to the device's private data.
|
|
* @param cfg_ptr The address to the structure containing the configuration
|
|
* information for this instance of the driver.
|
|
* @param level The initialization level. See SYS_INIT() for details.
|
|
* @param prio Priority within the selected initialization level. See SYS_INIT()
|
|
* for details.
|
|
* @param api_ptr Provides an initial pointer to the API function struct used by
|
|
* the driver. Can be NULL.
|
|
*/
|
|
#define SPI_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \
|
|
data_ptr, cfg_ptr, level, prio, \
|
|
api_ptr, ...) \
|
|
Z_SPI_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \
|
|
Z_SPI_INIT_FN(Z_DEVICE_DT_DEV_ID(node_id), init_fn) \
|
|
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
|
|
DEVICE_DT_NAME(node_id), \
|
|
&UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \
|
|
pm_device, \
|
|
data_ptr, cfg_ptr, level, prio, \
|
|
api_ptr, \
|
|
&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \
|
|
__VA_ARGS__)
|
|
|
|
static inline void spi_transceive_stats(const struct device *dev, int error,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
uint32_t tx_bytes;
|
|
uint32_t rx_bytes;
|
|
|
|
if (error) {
|
|
SPI_STATS_TRANSFER_ERROR_INC(dev);
|
|
}
|
|
|
|
if (tx_bufs) {
|
|
tx_bytes = tx_bufs->count ? tx_bufs->buffers->len : 0;
|
|
SPI_STATS_TX_BYTES_INCN(dev, tx_bytes);
|
|
}
|
|
|
|
if (rx_bufs) {
|
|
rx_bytes = rx_bufs->count ? rx_bufs->buffers->len : 0;
|
|
SPI_STATS_RX_BYTES_INCN(dev, rx_bytes);
|
|
}
|
|
}
|
|
|
|
#else /*CONFIG_SPI_STATS*/
|
|
|
|
#define SPI_DEVICE_DT_DEFINE(node_id, init_fn, pm, \
|
|
data, config, level, prio, \
|
|
api, ...) \
|
|
Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \
|
|
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
|
|
DEVICE_DT_NAME(node_id), init_fn, pm, data, config, \
|
|
level, prio, api, \
|
|
&Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \
|
|
__VA_ARGS__)
|
|
|
|
#define SPI_STATS_RX_BYTES_INC(dev_)
|
|
#define SPI_STATS_TX_BYTES_INC(dev_)
|
|
#define SPI_STATS_TRANSFER_ERROR_INC(dev_)
|
|
|
|
#define spi_transceive_stats(dev, error, tx_bufs, rx_bufs)
|
|
|
|
#endif /*CONFIG_SPI_STATS*/
|
|
|
|
/**
|
|
* @typedef spi_api_io
|
|
* @brief Callback API for I/O
|
|
* See spi_transceive() for argument descriptions
|
|
*/
|
|
typedef int (*spi_api_io)(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs);
|
|
|
|
/**
|
|
* @brief SPI callback for asynchronous transfer requests
|
|
*
|
|
* @param dev SPI device which is notifying of transfer completion or error
|
|
* @param result Result code of the transfer request. 0 is success, -errno for failure.
|
|
* @param data Transfer requester supplied data which is passed along to the callback.
|
|
*/
|
|
typedef void (*spi_callback_t)(const struct device *dev, int result, void *data);
|
|
|
|
/**
|
|
* @typedef spi_api_io
|
|
* @brief Callback API for asynchronous I/O
|
|
* See spi_transceive_signal() for argument descriptions
|
|
*/
|
|
typedef int (*spi_api_io_async)(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs,
|
|
spi_callback_t cb,
|
|
void *userdata);
|
|
|
|
#if defined(CONFIG_SPI_RTIO) || defined(DOXYGEN)
|
|
|
|
/**
|
|
* @typedef spi_api_iodev_submit
|
|
* @brief Callback API for submitting work to a SPI device with RTIO
|
|
*/
|
|
typedef void (*spi_api_iodev_submit)(const struct device *dev,
|
|
struct rtio_iodev_sqe *iodev_sqe);
|
|
#endif /* CONFIG_SPI_RTIO */
|
|
|
|
/**
|
|
* @typedef spi_api_release
|
|
* @brief Callback API for unlocking SPI device.
|
|
* See spi_release() for argument descriptions
|
|
*/
|
|
typedef int (*spi_api_release)(const struct device *dev,
|
|
const struct spi_config *config);
|
|
|
|
|
|
/**
|
|
* @brief SPI driver API
|
|
* This is the mandatory API any SPI driver needs to expose.
|
|
*/
|
|
__subsystem struct spi_driver_api {
|
|
spi_api_io transceive;
|
|
#ifdef CONFIG_SPI_ASYNC
|
|
spi_api_io_async transceive_async;
|
|
#endif /* CONFIG_SPI_ASYNC */
|
|
#ifdef CONFIG_SPI_RTIO
|
|
spi_api_iodev_submit iodev_submit;
|
|
#endif /* CONFIG_SPI_RTIO */
|
|
spi_api_release release;
|
|
};
|
|
|
|
/**
|
|
* @brief Check if SPI CS is controlled using a GPIO.
|
|
*
|
|
* @param config SPI configuration.
|
|
* @return true If CS is controlled using a GPIO.
|
|
* @return false If CS is controlled by hardware or any other means.
|
|
*/
|
|
static inline bool spi_cs_is_gpio(const struct spi_config *config)
|
|
{
|
|
return config->cs.gpio.port != NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if SPI CS in @ref spi_dt_spec is controlled using a GPIO.
|
|
*
|
|
* @param spec SPI specification from devicetree.
|
|
* @return true If CS is controlled using a GPIO.
|
|
* @return false If CS is controlled by hardware or any other means.
|
|
*/
|
|
static inline bool spi_cs_is_gpio_dt(const struct spi_dt_spec *spec)
|
|
{
|
|
return spi_cs_is_gpio(&spec->config);
|
|
}
|
|
|
|
/**
|
|
* @brief Validate that SPI bus (and CS gpio if defined) is ready.
|
|
*
|
|
* @param spec SPI specification from devicetree
|
|
*
|
|
* @retval true if the SPI bus is ready for use.
|
|
* @retval false if the SPI bus (or the CS gpio defined) is not ready for use.
|
|
*/
|
|
static inline bool spi_is_ready_dt(const struct spi_dt_spec *spec)
|
|
{
|
|
/* Validate bus is ready */
|
|
if (!device_is_ready(spec->bus)) {
|
|
return false;
|
|
}
|
|
/* Validate CS gpio port is ready, if it is used */
|
|
if (spi_cs_is_gpio_dt(spec) &&
|
|
!gpio_is_ready_dt(&spec->config.cs.gpio)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Read/write the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is synchronous.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver 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 frames Positive number of frames received in slave mode.
|
|
* @retval 0 If successful in master mode.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
__syscall int spi_transceive(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs);
|
|
|
|
static inline int z_impl_spi_transceive(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
const struct spi_driver_api *api =
|
|
(const struct spi_driver_api *)dev->api;
|
|
int ret;
|
|
|
|
ret = api->transceive(dev, config, tx_bufs, rx_bufs);
|
|
spi_transceive_stats(dev, ret, tx_bufs, rx_bufs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Read/write data from an SPI bus specified in @p spi_dt_spec.
|
|
*
|
|
* This is equivalent to:
|
|
*
|
|
* spi_transceive(spec->bus, &spec->config, tx_bufs, rx_bufs);
|
|
*
|
|
* @param spec SPI specification from devicetree
|
|
* @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.
|
|
*
|
|
* @return a value from spi_transceive().
|
|
*/
|
|
static inline int spi_transceive_dt(const struct spi_dt_spec *spec,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
return spi_transceive(spec->bus, &spec->config, tx_bufs, rx_bufs);
|
|
}
|
|
|
|
/**
|
|
* @brief Read the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is synchronous.
|
|
*
|
|
* @note This function is a helper function calling spi_transceive.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
* @param config Pointer to a valid spi_config structure instance.
|
|
* Pointer-comparison may be used to detect changes from
|
|
* previous operations.
|
|
* @param rx_bufs Buffer array where data to be read will be written to.
|
|
*
|
|
* @retval frames Positive number of frames received in slave mode.
|
|
* @retval 0 If successful.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
static inline int spi_read(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
return spi_transceive(dev, config, NULL, rx_bufs);
|
|
}
|
|
|
|
/**
|
|
* @brief Read data from a SPI bus specified in @p spi_dt_spec.
|
|
*
|
|
* This is equivalent to:
|
|
*
|
|
* spi_read(spec->bus, &spec->config, rx_bufs);
|
|
*
|
|
* @param spec SPI specification from devicetree
|
|
* @param rx_bufs Buffer array where data to be read will be written to.
|
|
*
|
|
* @return a value from spi_read().
|
|
*/
|
|
static inline int spi_read_dt(const struct spi_dt_spec *spec,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
return spi_read(spec->bus, &spec->config, rx_bufs);
|
|
}
|
|
|
|
/**
|
|
* @brief Write the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is synchronous.
|
|
*
|
|
* @note This function is a helper function calling spi_transceive.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver 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.
|
|
*
|
|
* @retval 0 If successful.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
static inline int spi_write(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs)
|
|
{
|
|
return spi_transceive(dev, config, tx_bufs, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Write data to a SPI bus specified in @p spi_dt_spec.
|
|
*
|
|
* This is equivalent to:
|
|
*
|
|
* spi_write(spec->bus, &spec->config, tx_bufs);
|
|
*
|
|
* @param spec SPI specification from devicetree
|
|
* @param tx_bufs Buffer array where data to be sent originates from.
|
|
*
|
|
* @return a value from spi_write().
|
|
*/
|
|
static inline int spi_write_dt(const struct spi_dt_spec *spec,
|
|
const struct spi_buf_set *tx_bufs)
|
|
{
|
|
return spi_write(spec->bus, &spec->config, tx_bufs);
|
|
}
|
|
|
|
#if defined(CONFIG_SPI_ASYNC) || defined(__DOXYGEN__)
|
|
|
|
/**
|
|
* @brief Read/write the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is asynchronous.
|
|
*
|
|
* @note This function is available only if @kconfig{CONFIG_SPI_ASYNC}
|
|
* is selected.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver 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.
|
|
* @param callback Function pointer to completion callback.
|
|
* (Note: if NULL this function will not
|
|
* notify the end of the transaction, and whether it went
|
|
* successfully or not).
|
|
* @param userdata Userdata passed to callback
|
|
*
|
|
* @retval frames Positive number of frames received in slave mode.
|
|
* @retval 0 If successful in master mode.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
static inline int spi_transceive_cb(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs,
|
|
spi_callback_t callback,
|
|
void *userdata)
|
|
{
|
|
const struct spi_driver_api *api =
|
|
(const struct spi_driver_api *)dev->api;
|
|
|
|
return api->transceive_async(dev, config, tx_bufs, rx_bufs, callback, userdata);
|
|
}
|
|
|
|
#if defined(CONFIG_POLL) || defined(__DOXYGEN__)
|
|
|
|
/** @cond INTERNAL_HIDDEN */
|
|
void z_spi_transfer_signal_cb(const struct device *dev, int result, void *userdata);
|
|
/** @endcond */
|
|
|
|
/**
|
|
* @brief Read/write the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is asynchronous.
|
|
*
|
|
* @note This function is available only if @kconfig{CONFIG_SPI_ASYNC}
|
|
* and @kconfig{CONFIG_POLL} are selected.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver 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.
|
|
* @param sig A pointer to a valid and ready to be signaled
|
|
* struct k_poll_signal. (Note: if NULL this function will not
|
|
* notify the end of the transaction, and whether it went
|
|
* successfully or not).
|
|
*
|
|
* @retval frames Positive number of frames received in slave mode.
|
|
* @retval 0 If successful in master mode.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
static inline int spi_transceive_signal(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs,
|
|
struct k_poll_signal *sig)
|
|
{
|
|
const struct spi_driver_api *api =
|
|
(const struct spi_driver_api *)dev->api;
|
|
spi_callback_t cb = (sig == NULL) ? NULL : z_spi_transfer_signal_cb;
|
|
|
|
return api->transceive_async(dev, config, tx_bufs, rx_bufs, cb, sig);
|
|
}
|
|
|
|
/**
|
|
* @brief Read the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is asynchronous.
|
|
*
|
|
* @note This function is a helper function calling spi_transceive_signal.
|
|
*
|
|
* @note This function is available only if @kconfig{CONFIG_SPI_ASYNC}
|
|
* and @kconfig{CONFIG_POLL} are selected.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
* @param config Pointer to a valid spi_config structure instance.
|
|
* Pointer-comparison may be used to detect changes from
|
|
* previous operations.
|
|
* @param rx_bufs Buffer array where data to be read will be written to.
|
|
* @param sig A pointer to a valid and ready to be signaled
|
|
* struct k_poll_signal. (Note: if NULL this function will not
|
|
* notify the end of the transaction, and whether it went
|
|
* successfully or not).
|
|
*
|
|
* @retval frames Positive number of frames received in slave mode.
|
|
* @retval 0 If successful
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
static inline int spi_read_signal(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *rx_bufs,
|
|
struct k_poll_signal *sig)
|
|
{
|
|
return spi_transceive_signal(dev, config, NULL, rx_bufs, sig);
|
|
}
|
|
|
|
/**
|
|
* @brief Write the specified amount of data from the SPI driver.
|
|
*
|
|
* @note This function is asynchronous.
|
|
*
|
|
* @note This function is a helper function calling spi_transceive_signal.
|
|
*
|
|
* @note This function is available only if @kconfig{CONFIG_SPI_ASYNC}
|
|
* and @kconfig{CONFIG_POLL} are selected.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver 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.
|
|
* @param sig A pointer to a valid and ready to be signaled
|
|
* struct k_poll_signal. (Note: if NULL this function will not
|
|
* notify the end of the transaction, and whether it went
|
|
* successfully or not).
|
|
*
|
|
* @retval 0 If successful.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
static inline int spi_write_signal(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
struct k_poll_signal *sig)
|
|
{
|
|
return spi_transceive_signal(dev, config, tx_bufs, NULL, sig);
|
|
}
|
|
|
|
#endif /* CONFIG_POLL */
|
|
|
|
#endif /* CONFIG_SPI_ASYNC */
|
|
|
|
|
|
#if defined(CONFIG_SPI_RTIO) || defined(__DOXYGEN__)
|
|
|
|
/**
|
|
* @brief Submit a SPI device with a request
|
|
*
|
|
* @param iodev_sqe Prepared submissions queue entry connected to an iodev
|
|
* defined by SPI_IODEV_DEFINE.
|
|
* Must live as long as the request is in flight.
|
|
*/
|
|
static inline void spi_iodev_submit(struct rtio_iodev_sqe *iodev_sqe)
|
|
{
|
|
const struct spi_dt_spec *dt_spec = iodev_sqe->sqe.iodev->data;
|
|
const struct device *dev = dt_spec->bus;
|
|
const struct spi_driver_api *api = (const struct spi_driver_api *)dev->api;
|
|
|
|
api->iodev_submit(dt_spec->bus, iodev_sqe);
|
|
}
|
|
|
|
extern const struct rtio_iodev_api spi_iodev_api;
|
|
|
|
/**
|
|
* @brief Define an iodev for a given dt node on the bus
|
|
*
|
|
* These do not need to be shared globally but doing so
|
|
* will save a small amount of memory.
|
|
*
|
|
* @param name Symbolic name to use for defining the iodev
|
|
* @param node_id Devicetree node identifier
|
|
* @param operation_ SPI operational mode
|
|
* @param delay_ Chip select delay in microseconds
|
|
*/
|
|
#define SPI_DT_IODEV_DEFINE(name, node_id, operation_, delay_) \
|
|
const struct spi_dt_spec _spi_dt_spec_##name = \
|
|
SPI_DT_SPEC_GET(node_id, operation_, delay_); \
|
|
RTIO_IODEV_DEFINE(name, &spi_iodev_api, (void *)&_spi_dt_spec_##name)
|
|
|
|
/**
|
|
* @brief Validate that SPI bus (and CS gpio if defined) is ready.
|
|
*
|
|
* @param spi_iodev SPI iodev defined with SPI_DT_IODEV_DEFINE
|
|
*
|
|
* @retval true if the SPI bus is ready for use.
|
|
* @retval false if the SPI bus (or the CS gpio defined) is not ready for use.
|
|
*/
|
|
static inline bool spi_is_ready_iodev(const struct rtio_iodev *spi_iodev)
|
|
{
|
|
struct spi_dt_spec *spec = spi_iodev->data;
|
|
|
|
return spi_is_ready_dt(spec);
|
|
}
|
|
|
|
/**
|
|
* @brief Copy the tx_bufs and rx_bufs into a set of RTIO requests
|
|
*
|
|
* @param[in] r rtio context
|
|
* @param[in] iodev iodev to transceive with
|
|
* @param[in] tx_bufs transmit buffer set
|
|
* @param[in] rx_bufs receive buffer set
|
|
* @param[out] last_sqe last sqe submitted, NULL if not enough memory
|
|
*
|
|
* @retval Number of submission queue entries
|
|
* @retval -ENOMEM out of memory
|
|
*/
|
|
static inline int spi_rtio_copy(struct rtio *r,
|
|
struct rtio_iodev *iodev,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs,
|
|
struct rtio_sqe **last_sqe)
|
|
{
|
|
int ret = 0;
|
|
size_t tx_count = tx_bufs ? tx_bufs->count : 0;
|
|
size_t rx_count = rx_bufs ? rx_bufs->count : 0;
|
|
|
|
uint32_t tx = 0, tx_len = 0;
|
|
uint32_t rx = 0, rx_len = 0;
|
|
uint8_t *tx_buf, *rx_buf;
|
|
|
|
struct rtio_sqe *sqe = NULL;
|
|
|
|
if (tx < tx_count) {
|
|
tx_buf = tx_bufs->buffers[tx].buf;
|
|
tx_len = tx_bufs->buffers[tx].len;
|
|
} else {
|
|
tx_buf = NULL;
|
|
tx_len = rx_bufs->buffers[rx].len;
|
|
}
|
|
|
|
if (rx < rx_count) {
|
|
rx_buf = rx_bufs->buffers[rx].buf;
|
|
rx_len = rx_bufs->buffers[rx].len;
|
|
} else {
|
|
rx_buf = NULL;
|
|
rx_len = tx_bufs->buffers[tx].len;
|
|
}
|
|
|
|
|
|
while ((tx < tx_count || rx < rx_count) && (tx_len > 0 || rx_len > 0)) {
|
|
sqe = rtio_sqe_acquire(r);
|
|
|
|
if (sqe == NULL) {
|
|
ret = -ENOMEM;
|
|
rtio_sqe_drop_all(r);
|
|
goto out;
|
|
}
|
|
|
|
ret++;
|
|
|
|
/* If tx/rx len are same, we can do a simple transceive */
|
|
if (tx_len == rx_len) {
|
|
if (tx_buf == NULL) {
|
|
rtio_sqe_prep_read(sqe, iodev, RTIO_PRIO_NORM,
|
|
rx_buf, rx_len, NULL);
|
|
} else if (rx_buf == NULL) {
|
|
rtio_sqe_prep_write(sqe, iodev, RTIO_PRIO_NORM,
|
|
tx_buf, tx_len, NULL);
|
|
} else {
|
|
rtio_sqe_prep_transceive(sqe, iodev, RTIO_PRIO_NORM,
|
|
tx_buf, rx_buf, rx_len, NULL);
|
|
}
|
|
tx++;
|
|
rx++;
|
|
if (rx < rx_count) {
|
|
rx_buf = rx_bufs->buffers[rx].buf;
|
|
rx_len = rx_bufs->buffers[rx].len;
|
|
} else {
|
|
rx_buf = NULL;
|
|
rx_len = 0;
|
|
}
|
|
if (tx < tx_count) {
|
|
tx_buf = tx_bufs->buffers[tx].buf;
|
|
tx_len = tx_bufs->buffers[tx].len;
|
|
} else {
|
|
tx_buf = NULL;
|
|
tx_len = 0;
|
|
}
|
|
} else if (tx_len == 0) {
|
|
rtio_sqe_prep_read(sqe, iodev, RTIO_PRIO_NORM,
|
|
(uint8_t *)rx_buf,
|
|
(uint32_t)rx_len,
|
|
NULL);
|
|
rx++;
|
|
if (rx < rx_count) {
|
|
rx_buf = rx_bufs->buffers[rx].buf;
|
|
rx_len = rx_bufs->buffers[rx].len;
|
|
} else {
|
|
rx_buf = NULL;
|
|
rx_len = 0;
|
|
}
|
|
} else if (rx_len == 0) {
|
|
rtio_sqe_prep_write(sqe, iodev, RTIO_PRIO_NORM,
|
|
(uint8_t *)tx_buf,
|
|
(uint32_t)tx_len,
|
|
NULL);
|
|
tx++;
|
|
if (tx < tx_count) {
|
|
tx_buf = rx_bufs->buffers[rx].buf;
|
|
tx_len = rx_bufs->buffers[rx].len;
|
|
} else {
|
|
tx_buf = NULL;
|
|
tx_len = 0;
|
|
}
|
|
} else if (tx_len > rx_len) {
|
|
rtio_sqe_prep_transceive(sqe, iodev, RTIO_PRIO_NORM,
|
|
(uint8_t *)tx_buf,
|
|
(uint8_t *)rx_buf,
|
|
(uint32_t)rx_len,
|
|
NULL);
|
|
tx_len -= rx_len;
|
|
tx_buf += rx_len;
|
|
rx++;
|
|
if (rx < rx_count) {
|
|
rx_buf = rx_bufs->buffers[rx].buf;
|
|
rx_len = rx_bufs->buffers[rx].len;
|
|
} else {
|
|
rx_buf = NULL;
|
|
rx_len = tx_len;
|
|
}
|
|
} else if (rx_len > tx_len) {
|
|
rtio_sqe_prep_transceive(sqe, iodev, RTIO_PRIO_NORM,
|
|
(uint8_t *)tx_buf,
|
|
(uint8_t *)rx_buf,
|
|
(uint32_t)tx_len,
|
|
NULL);
|
|
rx_len -= tx_len;
|
|
rx_buf += tx_len;
|
|
tx++;
|
|
if (tx < tx_count) {
|
|
tx_buf = tx_bufs->buffers[tx].buf;
|
|
tx_len = tx_bufs->buffers[tx].len;
|
|
} else {
|
|
tx_buf = NULL;
|
|
tx_len = rx_len;
|
|
}
|
|
} else {
|
|
__ASSERT_NO_MSG("Invalid spi_rtio_copy state");
|
|
}
|
|
|
|
sqe->flags = RTIO_SQE_TRANSACTION;
|
|
}
|
|
|
|
if (sqe != NULL) {
|
|
sqe->flags = 0;
|
|
*last_sqe = sqe;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
#endif /* CONFIG_SPI_RTIO */
|
|
|
|
/**
|
|
* @brief Release the SPI device locked on and/or the CS by the current config
|
|
*
|
|
* Note: This synchronous function is used to release either the lock on the
|
|
* SPI device and/or the CS line that was kept if, and if only,
|
|
* given config parameter was the last one to be used (in any of the
|
|
* above functions) and if it has the SPI_LOCK_ON bit set and/or the
|
|
* SPI_HOLD_ON_CS bit set into its operation bits field.
|
|
* This can be used if the caller needs to keep its hand on the SPI
|
|
* device for consecutive transactions and/or if it needs the device to
|
|
* stay selected. Usually both bits will be used along each other, so the
|
|
* the device is locked and stays on until another operation is necessary
|
|
* or until it gets released with the present function.
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
* @param config Pointer to a valid spi_config structure instance.
|
|
*
|
|
* @retval 0 If successful.
|
|
* @retval -errno Negative errno code on failure.
|
|
*/
|
|
__syscall int spi_release(const struct device *dev,
|
|
const struct spi_config *config);
|
|
|
|
static inline int z_impl_spi_release(const struct device *dev,
|
|
const struct spi_config *config)
|
|
{
|
|
const struct spi_driver_api *api =
|
|
(const struct spi_driver_api *)dev->api;
|
|
|
|
return api->release(dev, config);
|
|
}
|
|
|
|
/**
|
|
* @brief Release the SPI device specified in @p spi_dt_spec.
|
|
*
|
|
* This is equivalent to:
|
|
*
|
|
* spi_release(spec->bus, &spec->config);
|
|
*
|
|
* @param spec SPI specification from devicetree
|
|
*
|
|
* @return a value from spi_release().
|
|
*/
|
|
static inline int spi_release_dt(const struct spi_dt_spec *spec)
|
|
{
|
|
return spi_release(spec->bus, &spec->config);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
#include <zephyr/syscalls/spi.h>
|
|
|
|
#endif /* ZEPHYR_INCLUDE_DRIVERS_SPI_H_ */
|