/* * 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 #include #include #include #include #include #include #include #include #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 struct gpio_dt_spec 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 struct gpio_dt_spec for a SPI device's chip select pin * * This is equivalent to * SPI_CS_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst)). * * @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 * SPI_CS_CONTROL_INIT(DT_DRV_INST(inst), delay). * * 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 struct * spi_config 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 * SPI_CONFIG_DT(DT_DRV_INST(inst), operation_, delay_). * * @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 struct * spi_dt_spec 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 * SPI_DT_SPEC_GET(DT_DRV_INST(inst), operation_, delay_). * * @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 #endif /* ZEPHYR_INCLUDE_DRIVERS_SPI_H_ */