diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 2b4f8e20cda1..b6642956e2d6 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -44,7 +44,8 @@ zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_FLEXCOMM spi_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_FLEXIO spi_mcux_flexio.c) zephyr_library_sources_ifdef(CONFIG_SPI_NPCX_SPIP spi_npcx_spip.c) zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPI spi_nrfx_spi.c spi_nrfx_common.c) -zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIM spi_nrfx_spim.c spi_nrfx_common.c) +zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIM spi_nrfx_spim.c spi_nrfx_spim_common.c) +zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIM_RTIO spi_nrfx_spim_rtio.c spi_nrfx_spim_common.c) zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIS spi_nrfx_spis.c) zephyr_library_sources_ifdef(CONFIG_SPI_NUMAKER spi_numaker.c) zephyr_library_sources_ifdef(CONFIG_SPI_OC_SIMPLE spi_oc_simple.c) diff --git a/drivers/spi/Kconfig.nrfx b/drivers/spi/Kconfig.nrfx index b391b919beff..3eff3da6c864 100644 --- a/drivers/spi/Kconfig.nrfx +++ b/drivers/spi/Kconfig.nrfx @@ -20,8 +20,27 @@ config SPI_NRFX_SPI config SPI_NRFX_SPIM def_bool y depends on DT_HAS_NORDIC_NRF_SPIM_ENABLED + depends on !SPI_RTIO select NRFX_SPIM +config SPI_NRFX_SPIM_RTIO + def_bool y + depends on DT_HAS_NORDIC_NRF_SPIM_ENABLED + depends on SPI_RTIO + select NRFX_SPIM + +if SPI_NRFX_SPIM_RTIO + +config SPI_NRFX_SPIM_RTIO_SQE_POOL_SIZE + int "Size of SQE pool size for NRFX SPIM RTIO context" + default 8 + +config SPI_NRFX_SPIM_RTIO_CQE_POOL_SIZE + int "Size of SQE pool size for NRFX SPIM RTIO context" + default 8 + +endif # SPI_NRFX_SPIM_RTIO + config SPI_NRFX_SPIS def_bool y depends on DT_HAS_NORDIC_NRF_SPIS_ENABLED @@ -30,6 +49,7 @@ config SPI_NRFX_SPIS config SPI_NRFX_RAM_BUFFER_SIZE int "Size of RAM buffers for SPIM peripherals" + default 0 if HAS_NORDIC_DMM default 8 depends on SPI_NRFX_SPIM help diff --git a/drivers/spi/spi_ambiq_bleif.c b/drivers/spi/spi_ambiq_bleif.c index 075edc4f8025..05cc22c32f75 100644 --- a/drivers/spi/spi_ambiq_bleif.c +++ b/drivers/spi/spi_ambiq_bleif.c @@ -14,7 +14,7 @@ LOG_MODULE_REGISTER(spi_ambiq_bleif); #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_ambiq_spic.c b/drivers/spi/spi_ambiq_spic.c index 7ae57bb1ca6b..c2cc44cc8bad 100644 --- a/drivers/spi/spi_ambiq_spic.c +++ b/drivers/spi/spi_ambiq_spic.c @@ -10,7 +10,7 @@ LOG_MODULE_REGISTER(spi_ambiq); #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_b91.c b/drivers/spi/spi_b91.c index 1c018d88539b..ff62712c9179 100644 --- a/drivers/spi/spi_b91.c +++ b/drivers/spi/spi_b91.c @@ -19,7 +19,7 @@ LOG_MODULE_REGISTER(spi_telink); #include -#include +#include "spi_rtio.h" #include "spi_context.h" #include diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index d19b7a5b6c44..e963c2d3ad29 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -12,7 +12,7 @@ LOG_MODULE_REGISTER(spi_bitbang); #include #include -#include +#include "spi_rtio.h" #include "spi_context.h" struct spi_bitbang_data { diff --git a/drivers/spi/spi_cc13xx_cc26xx.c b/drivers/spi/spi_cc13xx_cc26xx.c index 68a28fbadc1f..5aa74a64c78a 100644 --- a/drivers/spi/spi_cc13xx_cc26xx.c +++ b/drivers/spi/spi_cc13xx_cc26xx.c @@ -11,7 +11,7 @@ LOG_MODULE_REGISTER(spi_cc13xx_cc26xx); #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_context.h b/drivers/spi/spi_context.h index 25fdbf8d8ad2..e2146bff354b 100644 --- a/drivers/spi/spi_context.h +++ b/drivers/spi/spi_context.h @@ -62,6 +62,8 @@ struct spi_context { const struct spi_buf *current_rx; size_t rx_count; + size_t max_count; + const uint8_t *tx_buf; size_t tx_len; uint8_t *rx_buf; @@ -206,13 +208,8 @@ static inline int spi_context_wait_for_completion(struct spi_context *ctx) if (IS_ENABLED(CONFIG_SPI_SLAVE) && spi_context_is_slave(ctx)) { timeout = K_FOREVER; } else { - uint32_t tx_len = spi_context_total_tx_len(ctx); - uint32_t rx_len = spi_context_total_rx_len(ctx); - - timeout_ms = MAX(tx_len, rx_len) * 8 * 1000 / - ctx->config->frequency; + timeout_ms = ctx->max_count * 8 * 1000 / ctx->config->frequency; timeout_ms += CONFIG_SPI_COMPLETION_TIMEOUT_TOLERANCE; - timeout = K_MSEC(timeout_ms); } #ifdef CONFIG_MULTITHREADING @@ -495,6 +492,7 @@ void spi_context_buffers_setup(struct spi_context *ctx, &ctx->rx_len, dfs); ctx->sync_status = 0; + ctx->max_count = MAX(spi_context_total_tx_len(ctx), spi_context_total_rx_len(ctx)); #ifdef CONFIG_SPI_SLAVE ctx->recv_frames = 0; diff --git a/drivers/spi/spi_dw.c b/drivers/spi/spi_dw.c index 8025d8d28d7c..54f19efd5919 100644 --- a/drivers/spi/spi_dw.c +++ b/drivers/spi/spi_dw.c @@ -31,7 +31,7 @@ LOG_MODULE_REGISTER(spi_dw); #endif #include -#include +#include "spi_rtio.h" #include #include "spi_dw.h" diff --git a/drivers/spi/spi_emul.c b/drivers/spi/spi_emul.c index d040d4077e4c..a7fb805f9349 100644 --- a/drivers/spi/spi_emul.c +++ b/drivers/spi/spi_emul.c @@ -17,7 +17,7 @@ LOG_MODULE_REGISTER(spi_emul_ctlr); #include #include #include -#include +#include "spi_rtio.h" #include /** Working data for the device */ diff --git a/drivers/spi/spi_esp32_spim.c b/drivers/spi/spi_esp32_spim.c index 4b1e549ea3b4..6aa71306523d 100644 --- a/drivers/spi/spi_esp32_spim.c +++ b/drivers/spi/spi_esp32_spim.c @@ -21,7 +21,7 @@ LOG_MODULE_REGISTER(esp32_spi, CONFIG_SPI_LOG_LEVEL); #include #include #include -#include +#include "spi_rtio.h" #include #ifdef SOC_GDMA_SUPPORTED #include diff --git a/drivers/spi/spi_gd32.c b/drivers/spi/spi_gd32.c index acc29859f63f..9f99cd8f1a03 100644 --- a/drivers/spi/spi_gd32.c +++ b/drivers/spi/spi_gd32.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include "spi_rtio.h" #ifdef CONFIG_SPI_GD32_DMA #include #include diff --git a/drivers/spi/spi_grlib_spimctrl.c b/drivers/spi/spi_grlib_spimctrl.c index 463cd6b8c922..f9c297fee6a4 100644 --- a/drivers/spi/spi_grlib_spimctrl.c +++ b/drivers/spi/spi_grlib_spimctrl.c @@ -7,7 +7,7 @@ #define DT_DRV_COMPAT gaisler_spimctrl #include -#include +#include "spi_rtio.h" #include LOG_MODULE_REGISTER(spi_spimctrl); diff --git a/drivers/spi/spi_infineon.c b/drivers/spi/spi_infineon.c index 33bb70440853..97fd673ad744 100644 --- a/drivers/spi/spi_infineon.c +++ b/drivers/spi/spi_infineon.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(cat1_spi); #include #include -#include +#include "spi_rtio.h" #include #include diff --git a/drivers/spi/spi_litex_common.h b/drivers/spi/spi_litex_common.h index 14a52b0ae93b..ca078c295e57 100644 --- a/drivers/spi/spi_litex_common.h +++ b/drivers/spi/spi_litex_common.h @@ -6,7 +6,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include diff --git a/drivers/spi/spi_max32.c b/drivers/spi/spi_max32.c index ee533d46e700..8c2927b43511 100644 --- a/drivers/spi/spi_max32.c +++ b/drivers/spi/spi_max32.c @@ -13,7 +13,7 @@ #endif #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_mchp_mss.c b/drivers/spi/spi_mchp_mss.c index 336edf0ae45a..ec3ec5bb1f51 100644 --- a/drivers/spi/spi_mchp_mss.c +++ b/drivers/spi/spi_mchp_mss.c @@ -8,7 +8,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_mchp_mss_qspi.c b/drivers/spi/spi_mchp_mss_qspi.c index 33b673d046b6..a3b4da14fa05 100644 --- a/drivers/spi/spi_mchp_mss_qspi.c +++ b/drivers/spi/spi_mchp_mss_qspi.c @@ -8,7 +8,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_mchp_sercom_g1.c b/drivers/spi/spi_mchp_sercom_g1.c index 3d16c467d891..6c68c6a9f7ce 100644 --- a/drivers/spi/spi_mchp_sercom_g1.c +++ b/drivers/spi/spi_mchp_sercom_g1.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include #include #if CONFIG_SPI_MCHP_DMA_DRIVEN diff --git a/drivers/spi/spi_mcux_dspi.c b/drivers/spi/spi_mcux_dspi.c index e5211eaff840..0bd108d3531a 100644 --- a/drivers/spi/spi_mcux_dspi.c +++ b/drivers/spi/spi_mcux_dspi.c @@ -9,7 +9,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_mcux_ecspi.c b/drivers/spi/spi_mcux_ecspi.c index 8138e7fdc2a7..061e245f8def 100644 --- a/drivers/spi/spi_mcux_ecspi.c +++ b/drivers/spi/spi_mcux_ecspi.c @@ -13,7 +13,7 @@ LOG_MODULE_REGISTER(spi_mcux_ecspi, CONFIG_SPI_LOG_LEVEL); #include #include #include -#include +#include "spi_rtio.h" #include #include "spi_context.h" diff --git a/drivers/spi/spi_mcux_flexcomm.c b/drivers/spi/spi_mcux_flexcomm.c index dfbf734144ec..f0b1c0290125 100644 --- a/drivers/spi/spi_mcux_flexcomm.c +++ b/drivers/spi/spi_mcux_flexcomm.c @@ -9,7 +9,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_mcux_flexio.c b/drivers/spi/spi_mcux_flexio.c index 1f4a4ebe158b..b8bef5589e51 100644 --- a/drivers/spi/spi_mcux_flexio.c +++ b/drivers/spi/spi_mcux_flexio.c @@ -9,7 +9,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_npcx_spip.c b/drivers/spi/spi_npcx_spip.c index 8263fe9e8783..9df2dc5e9178 100644 --- a/drivers/spi/spi_npcx_spip.c +++ b/drivers/spi/spi_npcx_spip.c @@ -7,7 +7,7 @@ #define DT_DRV_COMPAT nuvoton_npcx_spip #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_nrfx_spi.c b/drivers/spi/spi_nrfx_spi.c index 3b27a05c85f3..4e9cd84cb687 100644 --- a/drivers/spi/spi_nrfx_spi.c +++ b/drivers/spi/spi_nrfx_spi.c @@ -5,7 +5,7 @@ */ #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index 7496c54958f4..f7923fe5e503 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -1,364 +1,100 @@ /* - * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * Copyright (c) 2026 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nordic_nrf_spim -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SOC_NRF5340_CPUAPP -#include -#endif -#include -#include -#include +#include "spi_nrfx_spim_common.h" -#include -#include -LOG_MODULE_REGISTER(spi_nrfx_spim, CONFIG_SPI_LOG_LEVEL); +LOG_MODULE_DECLARE(spi_nrfx_spim); #include "spi_context.h" -#include "spi_nrfx_common.h" - -#if (CONFIG_SPI_NRFX_RAM_BUFFER_SIZE > 0) -#define SPI_BUFFER_IN_RAM 1 -#endif -struct spi_nrfx_data { - nrfx_spim_t spim; +struct driver_data { + struct spi_nrfx_common_data common; struct spi_context ctx; - const struct device *dev; - size_t chunk_len; - bool busy; - bool initialized; -#ifdef SPI_BUFFER_IN_RAM - uint8_t *tx_buffer; - uint8_t *rx_buffer; -#endif + struct k_sem wake_sem; }; -struct spi_nrfx_config { - uint32_t max_freq; - nrfx_spim_config_t def_config; - void (*irq_connect)(void); - uint16_t max_chunk_len; - const struct pinctrl_dev_config *pcfg; - nrfx_gpiote_t *wake_gpiote; - uint32_t wake_pin; - void *mem_reg; +struct driver_config { + struct spi_nrfx_common_config common; }; -static void event_handler(const nrfx_spim_event_t *p_event, void *p_context); - -static inline void finalize_spi_transaction(const struct device *dev, bool deactivate_cs) +static void transfer_end(const struct device *dev, int ret) { - struct spi_nrfx_data *dev_data = dev->data; - void *reg = dev_data->spim.p_reg; + struct driver_data *dev_data = dev->data; + const struct spi_config *spi_cfg = dev_data->ctx.config; - if (deactivate_cs) { - spi_context_cs_control(&dev_data->ctx, false); - } + pm_device_runtime_put(dev); - if (NRF_SPIM_IS_320MHZ_SPIM(reg) && !(dev_data->ctx.config->operation & SPI_HOLD_ON_CS)) { - nrfy_spim_disable(reg); + if (ret || (spi_cfg->operation & SPI_HOLD_ON_CS) == 0) { + spi_nrfx_spim_common_cs_clear(dev, spi_cfg); } -} -static inline uint32_t get_nrf_spim_frequency(uint32_t frequency) -{ - if (NRF_SPIM_HAS_PRESCALER) { - return frequency; - } - /* Get the highest supported frequency not exceeding the requested one. - */ - if (frequency >= MHZ(32) && NRF_SPIM_HAS_32_MHZ_FREQ) { - return MHZ(32); - } else if (frequency >= MHZ(16) && NRF_SPIM_HAS_16_MHZ_FREQ) { - return MHZ(16); - } else if (frequency >= MHZ(8)) { - return MHZ(8); - } else if (frequency >= MHZ(4)) { - return MHZ(4); - } else if (frequency >= MHZ(2)) { - return MHZ(2); - } else if (frequency >= MHZ(1)) { - return MHZ(1); - } else if (frequency >= KHZ(500)) { - return KHZ(500); - } else if (frequency >= KHZ(250)) { - return KHZ(250); - } else { - return KHZ(125); - } + spi_context_complete(&dev_data->ctx, dev, ret); } -static inline nrf_spim_mode_t get_nrf_spim_mode(uint16_t operation) +static void transfer_start(const struct device *dev) { - if (SPI_MODE_GET(operation) & SPI_MODE_CPOL) { - if (SPI_MODE_GET(operation) & SPI_MODE_CPHA) { - return NRF_SPIM_MODE_3; - } else { - return NRF_SPIM_MODE_2; - } + struct driver_data *dev_data = dev->data; + size_t chunk_len; + const uint8_t *tx_buf; + size_t tx_buf_len; + uint8_t *rx_buf; + size_t rx_buf_len; + int ret; + + chunk_len = spi_context_max_continuous_chunk(&dev_data->ctx); + if (chunk_len == 0) { + transfer_end(dev, 0); + return; + } + + if (spi_context_tx_buf_on(&dev_data->ctx)) { + tx_buf = dev_data->ctx.tx_buf; + tx_buf_len = chunk_len; } else { - if (SPI_MODE_GET(operation) & SPI_MODE_CPHA) { - return NRF_SPIM_MODE_1; - } else { - return NRF_SPIM_MODE_0; - } + tx_buf = NULL; + tx_buf_len = 0; } -} -static inline nrf_spim_bit_order_t get_nrf_spim_bit_order(uint16_t operation) -{ - if (operation & SPI_TRANSFER_LSB) { - return NRF_SPIM_BIT_ORDER_LSB_FIRST; + if (spi_context_rx_buf_on(&dev_data->ctx)) { + rx_buf = dev_data->ctx.rx_buf; + rx_buf_len = chunk_len; } else { - return NRF_SPIM_BIT_ORDER_MSB_FIRST; - } -} - -static int configure(const struct device *dev, - const struct spi_config *spi_cfg) -{ - struct spi_nrfx_data *dev_data = dev->data; - const struct spi_nrfx_config *dev_config = dev->config; - struct spi_context *ctx = &dev_data->ctx; - uint32_t max_freq = dev_config->max_freq; - nrfx_spim_config_t config; - int result; - uint32_t sck_pin; - - if (dev_data->initialized && spi_context_configured(ctx, spi_cfg)) { - /* Already configured. No need to do it again. */ - return 0; - } - - if (spi_cfg->operation & SPI_HALF_DUPLEX) { - LOG_ERR("Half-duplex not supported"); - return -ENOTSUP; - } - - if (SPI_OP_MODE_GET(spi_cfg->operation) != SPI_OP_MODE_MASTER) { - LOG_ERR("Slave mode is not supported on %s", dev->name); - return -EINVAL; - } - - if (spi_cfg->operation & SPI_MODE_LOOP) { - LOG_ERR("Loopback mode is not supported"); - return -EINVAL; - } - - if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && - (spi_cfg->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { - LOG_ERR("Only single line mode is supported"); - return -EINVAL; - } - - if (SPI_WORD_SIZE_GET(spi_cfg->operation) != 8) { - LOG_ERR("Word sizes other than 8 bits are not supported"); - return -EINVAL; - } - - if (spi_cfg->frequency < 125000) { - LOG_ERR("Frequencies lower than 125 kHz are not supported"); - return -EINVAL; - } - -#if defined(CONFIG_SOC_NRF5340_CPUAPP) - /* On nRF5340, the 32 Mbps speed is supported by the application core - * when it is running at 128 MHz (see the Timing specifications section - * in the nRF5340 PS). - */ - if (max_freq > 16000000 && - nrf_clock_hfclk_div_get(NRF_CLOCK) != NRF_CLOCK_HFCLK_DIV_1) { - max_freq = 16000000; + rx_buf = NULL; + rx_buf_len = 0; } -#endif - - config = dev_config->def_config; - - /* Limit the frequency to that supported by the SPIM instance. */ - config.frequency = get_nrf_spim_frequency(MIN(spi_cfg->frequency, - max_freq)); - config.mode = get_nrf_spim_mode(spi_cfg->operation); - config.bit_order = get_nrf_spim_bit_order(spi_cfg->operation); - sck_pin = nrfy_spim_sck_pin_get(dev_data->spim.p_reg); - - if (sck_pin != NRF_SPIM_PIN_NOT_CONNECTED) { - nrfy_gpio_pin_write(sck_pin, spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0); + ret = spi_nrfx_spim_common_transfer_start(dev, + tx_buf, + tx_buf_len, + rx_buf, + rx_buf_len); + if (ret) { + transfer_end(dev, ret); + return; } - - if (dev_data->initialized) { - nrfx_spim_uninit(&dev_data->spim); - dev_data->initialized = false; - } - - result = nrfx_spim_init(&dev_data->spim, &config, - event_handler, (void *)dev); - if (result < 0) { - LOG_ERR("Failed to initialize nrfx driver: %d", result); - return result; - } - -#if defined(CONFIG_NRF52_ANOMALY_58_WORKAROUND) -#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpiote), okay) - nrfx_spim_nrf52_anomaly_58_init(&dev_data->spim, - &GPIOTE_NRFX_INST_BY_NODE(DT_NODELABEL(gpiote))); -#else -#error "GPIOTE is not enabled, anomaly 58 workaround cannot be applied for SPIM" -#endif -#endif - - dev_data->initialized = true; - - ctx->config = spi_cfg; - - return 0; } -static void finish_transaction(const struct device *dev, int error) +static void spim_wake_handler(const struct device *dev) { - struct spi_nrfx_data *dev_data = dev->data; - struct spi_context *ctx = &dev_data->ctx; - - LOG_DBG("Transaction finished with status %d", error); - - spi_context_complete(ctx, dev, error); - dev_data->busy = false; - - finalize_spi_transaction(dev, true); + struct driver_data *dev_data = dev->data; -#ifdef CONFIG_SPI_ASYNC - if (ctx->asynchronous) { - pm_device_runtime_put_async(dev, K_NO_WAIT); - } -#endif + k_sem_give(&dev_data->wake_sem); } -static void transfer_next_chunk(const struct device *dev) +static void spim_evt_handler(const struct device *dev, nrfx_spim_event_t *evt) { - struct spi_nrfx_data *dev_data = dev->data; - const struct spi_nrfx_config *dev_config = dev->config; - struct spi_context *ctx = &dev_data->ctx; - int error = 0; - - size_t chunk_len = spi_context_max_continuous_chunk(ctx); - - if (chunk_len > 0) { - nrfx_spim_xfer_desc_t xfer; - const uint8_t *tx_buf = ctx->tx_buf; - uint8_t *rx_buf = ctx->rx_buf; - - if (chunk_len > dev_config->max_chunk_len) { - chunk_len = dev_config->max_chunk_len; - } - -#ifdef SPI_BUFFER_IN_RAM - if (spi_context_tx_buf_on(ctx) && - !nrf_dma_accessible_check(&dev_data->spim.p_reg, tx_buf)) { - - if (chunk_len > CONFIG_SPI_NRFX_RAM_BUFFER_SIZE) { - chunk_len = CONFIG_SPI_NRFX_RAM_BUFFER_SIZE; - } - - memcpy(dev_data->tx_buffer, tx_buf, chunk_len); - tx_buf = dev_data->tx_buffer; - } - - if (spi_context_rx_buf_on(ctx) && - !nrf_dma_accessible_check(&dev_data->spim.p_reg, rx_buf)) { - - if (chunk_len > CONFIG_SPI_NRFX_RAM_BUFFER_SIZE) { - chunk_len = CONFIG_SPI_NRFX_RAM_BUFFER_SIZE; - } - - rx_buf = dev_data->rx_buffer; - } -#endif - - dev_data->chunk_len = chunk_len; + struct driver_data *dev_data = dev->data; + uint32_t len = MAX(evt->xfer_desc.tx_length, evt->xfer_desc.rx_length); - xfer.tx_length = spi_context_tx_buf_on(ctx) ? chunk_len : 0; - xfer.rx_length = spi_context_rx_buf_on(ctx) ? chunk_len : 0; - - error = dmm_buffer_out_prepare(dev_config->mem_reg, tx_buf, xfer.tx_length, - (void **)&xfer.p_tx_buffer); - if (error != 0) { - goto out_alloc_failed; - } - - error = dmm_buffer_in_prepare(dev_config->mem_reg, rx_buf, xfer.rx_length, - (void **)&xfer.p_rx_buffer); - if (error != 0) { - goto in_alloc_failed; - } - - error = nrfx_spim_xfer(&dev_data->spim, &xfer, 0); - if (error == 0) { - return; - } - - /* On nrfx_spim_xfer() error */ - dmm_buffer_in_release(dev_config->mem_reg, rx_buf, xfer.rx_length, - (void **)&xfer.p_rx_buffer); -in_alloc_failed: - dmm_buffer_out_release(dev_config->mem_reg, (void **)&xfer.p_tx_buffer); - } - -out_alloc_failed: - finish_transaction(dev, error); -} - -static void event_handler(const nrfx_spim_event_t *p_event, void *p_context) -{ - const struct device *dev = p_context; - struct spi_nrfx_data *dev_data = dev->data; - const struct spi_nrfx_config *dev_config = dev->config; - - if (p_event->type == NRFX_SPIM_EVENT_DONE) { - /* Chunk length is set to 0 when a transaction is aborted - * due to a timeout. - */ - if (dev_data->chunk_len == 0) { - finish_transaction(dev_data->dev, -ETIMEDOUT); - return; - } - - if (spi_context_tx_buf_on(&dev_data->ctx)) { - dmm_buffer_out_release(dev_config->mem_reg, - (void **)p_event->xfer_desc.p_tx_buffer); - } - - if (spi_context_rx_buf_on(&dev_data->ctx)) { - dmm_buffer_in_release(dev_config->mem_reg, dev_data->ctx.rx_buf, - dev_data->chunk_len, p_event->xfer_desc.p_rx_buffer); - } - -#ifdef SPI_BUFFER_IN_RAM - if (spi_context_rx_buf_on(&dev_data->ctx) && - p_event->xfer_desc.p_rx_buffer != NULL && - p_event->xfer_desc.p_rx_buffer != dev_data->ctx.rx_buf) { - (void)memcpy(dev_data->ctx.rx_buf, - dev_data->rx_buffer, - dev_data->chunk_len); - } -#endif - spi_context_update_tx(&dev_data->ctx, 1, dev_data->chunk_len); - spi_context_update_rx(&dev_data->ctx, 1, dev_data->chunk_len); - - transfer_next_chunk(dev_data->dev); - } + spi_nrfx_spim_common_transfer_end(dev, &evt->xfer_desc); + spi_context_update_tx(&dev_data->ctx, 1, len); + spi_context_update_rx(&dev_data->ctx, 1, len); + transfer_start(dev); } static int transceive(const struct device *dev, @@ -369,324 +105,162 @@ static int transceive(const struct device *dev, spi_callback_t cb, void *userdata) { - struct spi_nrfx_data *dev_data = dev->data; - const struct spi_nrfx_config *dev_config = dev->config; - void *reg = dev_data->spim.p_reg; - int error; + struct driver_data *dev_data = dev->data; + int ret; spi_context_lock(&dev_data->ctx, asynchronous, cb, userdata, spi_cfg); spi_context_buffers_setup(&dev_data->ctx, tx_bufs, rx_bufs, 1); if (!spi_context_tx_buf_on(&dev_data->ctx) && !spi_context_rx_buf_on(&dev_data->ctx)) { - spi_context_release(&dev_data->ctx, -EINVAL); - return -EINVAL; + ret = -EINVAL; + goto release_exit; } - pm_device_runtime_get(dev); - error = configure(dev, spi_cfg); - - if (error == 0) { - dev_data->busy = true; - - if (dev_config->wake_pin != WAKE_PIN_NOT_USED) { - error = spi_nrfx_wake_request(dev_config->wake_gpiote, - dev_config->wake_pin); - if (error == -ETIMEDOUT) { - LOG_WRN("Waiting for WAKE acknowledgment timed out"); - /* If timeout occurs, try to perform the transfer - * anyway, just in case the slave device was unable - * to signal that it was already awaken and prepared - * for the transfer. - */ - } - } + ret = pm_device_runtime_get(dev); + if (ret) { + goto release_exit; + } - if (NRF_SPIM_IS_320MHZ_SPIM(reg)) { - nrfy_spim_enable(reg); - } - spi_context_cs_control(&dev_data->ctx, true); - - transfer_next_chunk(dev); - - error = spi_context_wait_for_completion(&dev_data->ctx); - if (error == -ETIMEDOUT) { - /* Set the chunk length to 0 so that event_handler() - * knows that the transaction timed out and is to be - * aborted. - */ - dev_data->chunk_len = 0; - /* Abort the current transfer by deinitializing - * the nrfx driver. - */ - nrfx_spim_uninit(&dev_data->spim); - dev_data->initialized = false; - - /* Make sure the transaction is finished (it may be - * already finished if it actually did complete before - * the nrfx driver was deinitialized). - */ - finish_transaction(dev, -ETIMEDOUT); - - /* Clean up the driver state. */ -#ifdef CONFIG_MULTITHREADING - k_sem_reset(&dev_data->ctx.sync); -#else - dev_data->ctx.ready = 0; -#endif /* CONFIG_MULTITHREADING */ - } else if (error) { - finalize_spi_transaction(dev, true); - } + ret = spi_nrfx_spim_common_configure(dev, spi_cfg); + if (ret) { + goto put_release_exit; } - if (error || !(dev_data->ctx.config->operation & SPI_LOCK_ON)) { - spi_context_release(&dev_data->ctx, error); + dev_data->ctx.config = spi_cfg; + + spi_nrfx_spim_common_wake_start(dev, spim_wake_handler); + k_sem_take(&dev_data->wake_sem, K_FOREVER); + + spi_nrfx_spim_common_cs_set(dev, spi_cfg); + transfer_start(dev); + + ret = spi_context_wait_for_completion(&dev_data->ctx); + if (ret == 0) { + if (spi_cfg->operation & SPI_LOCK_ON) { + goto exit; + } else { + goto release_exit; + } } - if (error || !asynchronous) { - pm_device_runtime_put(dev); + if (ret == -ETIMEDOUT) { + spi_nrfx_spim_common_transfer_stop(dev); + spi_context_wait_for_completion(&dev_data->ctx); } - return error; + +put_release_exit: + pm_device_runtime_put(dev); + +release_exit: + spi_context_release(&dev_data->ctx, ret); + +exit: + return ret; } -static int spi_nrfx_transceive(const struct device *dev, - const struct spi_config *spi_cfg, - const struct spi_buf_set *tx_bufs, - const struct spi_buf_set *rx_bufs) +static int driver_api_transceive(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) { return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL); } #ifdef CONFIG_SPI_ASYNC -static int spi_nrfx_transceive_async(const struct device *dev, - const struct spi_config *spi_cfg, - const struct spi_buf_set *tx_bufs, - const struct spi_buf_set *rx_bufs, - spi_callback_t cb, - void *userdata) +static int driver_api_transceive_async(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, + spi_callback_t cb, + void *userdata) { return transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata); } -#endif /* CONFIG_SPI_ASYNC */ +#endif -static int spi_nrfx_release(const struct device *dev, - const struct spi_config *spi_cfg) +static int driver_api_release(const struct device *dev, + const struct spi_config *spi_cfg) { - struct spi_nrfx_data *dev_data = dev->data; - -#ifdef CONFIG_MULTITHREADING - if (dev_data->ctx.owner != spi_cfg) { - return -EALREADY; - } -#endif + struct driver_data *dev_data = dev->data; if (!spi_context_configured(&dev_data->ctx, spi_cfg)) { - return -EINVAL; - } - - if (dev_data->busy) { - return -EBUSY; + return -EPERM; } spi_context_unlock_unconditionally(&dev_data->ctx); - finalize_spi_transaction(dev, false); - return 0; } -static DEVICE_API(spi, spi_nrfx_driver_api) = { - .transceive = spi_nrfx_transceive, +static DEVICE_API(spi, driver_api) = { + .transceive = driver_api_transceive, #ifdef CONFIG_SPI_ASYNC - .transceive_async = spi_nrfx_transceive_async, + .transceive_async = driver_api_transceive_async, #endif #ifdef CONFIG_SPI_RTIO .iodev_submit = spi_rtio_iodev_default_submit, #endif - .release = spi_nrfx_release, + .release = driver_api_release, }; -static int spim_resume(const struct device *dev) -{ - const struct spi_nrfx_config *dev_config = dev->config; - struct spi_nrfx_data *dev_data = dev->data; - (void)dev_data; - - (void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT); - /* nrfx_spim_init() will be called at configuration before - * the next transfer. - */ - - if (spi_context_cs_get_all(&dev_data->ctx)) { - return -EAGAIN; - } - - return 0; -} - -static void spim_suspend(const struct device *dev) -{ - const struct spi_nrfx_config *dev_config = dev->config; - struct spi_nrfx_data *dev_data = dev->data; - int err; - - if (dev_data->initialized) { - nrfx_spim_uninit(&dev_data->spim); - dev_data->initialized = false; - } - - err = spi_context_cs_put_all(&dev_data->ctx); - (void)err; - - (void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP); -} - -static int spim_nrfx_pm_action(const struct device *dev, enum pm_device_action action) +static int driver_init(const struct device *dev) { - if (action == PM_DEVICE_ACTION_RESUME) { - return spim_resume(dev); - } else if (IS_ENABLED(CONFIG_PM_DEVICE) && (action == PM_DEVICE_ACTION_SUSPEND)) { - spim_suspend(dev); - } else { - return -ENOTSUP; - } - - return 0; -} + struct driver_data *dev_data = dev->data; + int ret; -static int spi_nrfx_init(const struct device *dev) -{ - const struct spi_nrfx_config *dev_config = dev->config; - struct spi_nrfx_data *dev_data = dev->data; - int err; - - /* Apply sleep state by default. - * If PM is disabled, the default state will be applied in pm_device_driver_init. - */ - (void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP); - - if (dev_config->wake_pin != WAKE_PIN_NOT_USED) { - err = spi_nrfx_wake_init(dev_config->wake_gpiote, dev_config->wake_pin); - if (err == -ENODEV) { - LOG_ERR("Failed to allocate GPIOTE channel for WAKE"); - return err; - } - if (err == -EIO) { - LOG_ERR("Failed to configure WAKE pin"); - return err; - } + ret = spi_nrfx_spim_common_init(dev); + if (ret) { + return ret; } - dev_config->irq_connect(); - - err = spi_context_cs_configure_all(&dev_data->ctx); - if (err < 0) { - return err; - } + k_sem_init(&dev_data->wake_sem, 0, 1); spi_context_unlock_unconditionally(&dev_data->ctx); - return pm_device_driver_init(dev, spim_nrfx_pm_action); + return pm_device_driver_init(dev, spi_nrfx_spim_common_pm_action); } #ifdef CONFIG_DEVICE_DEINIT_SUPPORT -static int spi_nrfx_deinit(const struct device *dev) +static int driver_deinit(const struct device *dev) { -#if defined(CONFIG_PM_DEVICE) - enum pm_device_state state; - - /* - * PM must have suspended the device before driver can - * be deinitialized - */ - (void)pm_device_state_get(dev, &state); - return state == PM_DEVICE_STATE_SUSPENDED || - state == PM_DEVICE_STATE_OFF ? - 0 : -EBUSY; -#else - /* PM suspend implementation does everything we need */ - spim_suspend(dev); -#endif - - return 0; + return spi_nrfx_spim_common_deinit(dev); } #endif -#define SPI_NRFX_SPIM_EXTENDED_CONFIG(inst) \ - IF_ENABLED(NRFX_SPIM_EXTENDED_ENABLED, \ - (.dcx_pin = NRF_SPIM_PIN_NOT_CONNECTED, \ - COND_CODE_1(DT_INST_PROP(inst, rx_delay_supported), \ - (.rx_delay = DT_INST_PROP(inst, rx_delay),), \ - ()) \ - )) - -#define SPI_NRFX_SPIM_DEFINE(inst) \ - NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(DT_DRV_INST(inst)); \ - NRF_DT_CHECK_NODE_HAS_REQUIRED_MEMORY_REGIONS(DT_DRV_INST(inst)); \ - IF_ENABLED(SPI_BUFFER_IN_RAM, \ - (static uint8_t spim_##inst##_tx_buffer \ - [CONFIG_SPI_NRFX_RAM_BUFFER_SIZE] \ - DMM_MEMORY_SECTION(DT_DRV_INST(inst)); \ - static uint8_t spim_##inst##_rx_buffer \ - [CONFIG_SPI_NRFX_RAM_BUFFER_SIZE] \ - DMM_MEMORY_SECTION(DT_DRV_INST(inst));)) \ - static struct spi_nrfx_data spi_##inst##_data = { \ - .spim = NRFX_SPIM_INSTANCE(DT_INST_REG_ADDR(inst)), \ - IF_ENABLED(CONFIG_MULTITHREADING, \ - (SPI_CONTEXT_INIT_LOCK(spi_##inst##_data, ctx),)) \ - IF_ENABLED(CONFIG_MULTITHREADING, \ - (SPI_CONTEXT_INIT_SYNC(spi_##inst##_data, ctx),)) \ - SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(inst), ctx) \ - IF_ENABLED(SPI_BUFFER_IN_RAM, \ - (.tx_buffer = spim_##inst##_tx_buffer, \ - .rx_buffer = spim_##inst##_rx_buffer,)) \ - .dev = DEVICE_DT_GET(DT_DRV_INST(inst)), \ - .busy = false, \ - }; \ - NRF_DT_INST_IRQ_DIRECT_DEFINE( \ - inst, \ - nrfx_spim_irq_handler, \ - &CONCAT(spi_, inst, _data.spim) \ - ) \ - static void irq_connect##inst(void) \ - { \ - NRF_DT_INST_IRQ_CONNECT( \ - inst, \ - nrfx_spim_irq_handler, \ - &CONCAT(spi_, inst, _data.spim) \ - ); \ - } \ - PINCTRL_DT_INST_DEFINE(inst); \ - static const struct spi_nrfx_config spi_##inst##z_config = { \ - .max_freq = DT_INST_PROP(inst, max_frequency), \ - .def_config = { \ - .skip_gpio_cfg = true, \ - .skip_psel_cfg = true, \ - .ss_pin = NRF_SPIM_PIN_NOT_CONNECTED, \ - .orc = DT_INST_PROP(inst, overrun_character), \ - SPI_NRFX_SPIM_EXTENDED_CONFIG(inst) \ - }, \ - .irq_connect = irq_connect##inst, \ - .max_chunk_len = BIT_MASK( \ - DT_INST_PROP(inst, easydma_maxcnt_bits)), \ - .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ - .wake_gpiote = WAKE_GPIOTE_NODE(DT_DRV_INST(inst)), \ - .wake_pin = NRF_DT_GPIOS_TO_PSEL_OR(DT_DRV_INST(inst), \ - wake_gpios, \ - WAKE_PIN_NOT_USED), \ - .mem_reg = DMM_DEV_TO_REG(DT_DRV_INST(inst)), \ - }; \ - BUILD_ASSERT(!DT_INST_NODE_HAS_PROP(inst, wake_gpios) || \ - !(DT_GPIO_FLAGS(DT_DRV_INST(inst), wake_gpios) & \ - GPIO_ACTIVE_LOW), \ - "WAKE line must be configured as active high"); \ - PM_DEVICE_DT_INST_DEFINE(inst, spim_nrfx_pm_action); \ - SPI_DEVICE_DT_INST_DEINIT_DEFINE(inst, \ - spi_nrfx_init, \ - spi_nrfx_deinit, \ - PM_DEVICE_DT_INST_GET(inst), \ - &spi_##inst##_data, \ - &spi_##inst##z_config, \ - POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ - &spi_nrfx_driver_api) - -DT_INST_FOREACH_STATUS_OKAY(SPI_NRFX_SPIM_DEFINE) +#define DRIVER_DEFINE(inst) \ + static struct driver_data CONCAT(data, inst) = { \ + .common = SPI_NRFX_COMMON_DATA_INIT(inst), \ + IF_ENABLED( \ + CONFIG_MULTITHREADING, \ + (SPI_CONTEXT_INIT_LOCK(CONCAT(data, inst), ctx),) \ + ) \ + IF_ENABLED( \ + CONFIG_MULTITHREADING, \ + (SPI_CONTEXT_INIT_SYNC(CONCAT(data, inst), ctx),) \ + ) \ + }; \ + \ + SPI_NRFX_COMMON_DEFINE(inst, &CONCAT(data, inst)); \ + \ + static const struct driver_config CONCAT(config, inst) = { \ + .common = SPI_NRFX_COMMON_CONFIG_INIT( \ + inst, \ + spim_evt_handler \ + ), \ + }; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, spi_nrfx_spim_common_pm_action, 1); \ + \ + SPI_DEVICE_DT_INST_DEINIT_DEFINE( \ + inst, \ + driver_init, \ + driver_deinit, \ + PM_DEVICE_DT_INST_GET(inst), \ + &CONCAT(data, inst), \ + &CONCAT(config, inst), \ + POST_KERNEL, \ + CONFIG_SPI_INIT_PRIORITY, \ + &driver_api \ + ) + +DT_INST_FOREACH_STATUS_OKAY(DRIVER_DEFINE) diff --git a/drivers/spi/spi_nrfx_spim_common.c b/drivers/spi/spi_nrfx_spim_common.c new file mode 100644 index 000000000000..845d1edf8ac9 --- /dev/null +++ b/drivers/spi/spi_nrfx_spim_common.c @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2026 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "spi_nrfx_spim_common.h" + +#include + +#ifdef CONFIG_SOC_NRF5340_CPUAPP +#include +#endif + +#include + +#define WAKE_PIN_START_FLAGS (GPIO_INPUT | GPIO_PULL_UP) +#define WAKE_PIN_STOP_FLAGS (GPIO_INPUT | GPIO_PULL_DOWN | GPIO_DISCONNECTED) + +LOG_MODULE_REGISTER(spi_nrfx_spim, CONFIG_SPI_LOG_LEVEL); + +#if SPI_NRFX_HAS_WAKE +void spi_nrfx_spim_common_wake_start(const struct device *dev, + spi_nrfx_data_common_wake_handler handler) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + + dev_data->wake_handler = handler; + + k_timer_start(&dev_data->wake_timer, + K_USEC(CONFIG_SPI_NRFX_WAKE_TIMEOUT_US), + K_FOREVER); + + gpio_pin_configure_dt(&dev_config->wake_pin, WAKE_PIN_START_FLAGS); + gpio_pin_interrupt_configure_dt(&dev_config->wake_pin, GPIO_INT_EDGE_FALLING); +} + +#else + +void spi_nrfx_spim_common_wake_start(const struct device *dev, + spi_nrfx_data_common_wake_handler handler) +{ + handler(dev); +} +#endif + +void spi_nrfx_spim_common_cs_set(const struct device *dev, const struct spi_config *spi_cfg) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + NRF_SPIM_Type *spim_reg = dev_data->spim.p_reg; + + /* + * Enable the SPIM peripheral so it drives the SPI bus, ensuring correct initial bus state + * before transfer starts, and setting CS if its controlled by the SPIM peripheral. + */ + nrfy_spim_enable(spim_reg); + + if (spi_cfg->cs.cs_is_gpio) { + gpio_pin_set_dt(&spi_cfg->cs.gpio, 1); + } + + /* Wait only if we control the CS pin */ + if (spi_cfg->cs.cs_is_gpio || NRF_SPIM_IS_320MHZ_SPIM(spim_reg)) { + k_busy_wait(spi_cfg->cs.delay); + } +} + +void spi_nrfx_spim_common_cs_clear(const struct device *dev, const struct spi_config *spi_cfg) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + NRF_SPIM_Type *spim_reg = dev_data->spim.p_reg; + + /* Wait only if we control the CS pin */ + if (spi_cfg->cs.cs_is_gpio || NRF_SPIM_IS_320MHZ_SPIM(spim_reg)) { + k_busy_wait(spi_cfg->cs.delay); + } + + if (spi_cfg->cs.cs_is_gpio) { + gpio_pin_set_dt(&spi_cfg->cs.gpio, 0); + } + + /* + * Disable the SPIM peripheral so it no longer drives the SPI bus, clearing CS if its + * controlled by the SPIM peripheral. + */ + nrfy_spim_disable(spim_reg); +} + +static void evt_handler(nrfx_spim_event_t const *evt, void *data) +{ + const struct device *dev = data; + const struct spi_nrfx_common_config *dev_config = dev->config; + + dev_config->evt_handler(dev, (nrfx_spim_event_t *)evt); +} + +static void spi_config_copy(struct spi_config *des, const struct spi_config *src) +{ + memcpy(des, src, sizeof(struct spi_config)); +} + +static bool spi_config_equal(const struct spi_config *a, const struct spi_config *b) +{ + if (a->frequency != b->frequency || + a->operation != b->operation || + a->slave != b->slave || + a->cs.cs_is_gpio != b->cs.cs_is_gpio || + a->word_delay != b->word_delay) { + return false; + } + + if (a->cs.cs_is_gpio) { + if (a->cs.gpio.port != b->cs.gpio.port || + a->cs.gpio.pin != b->cs.gpio.pin || + a->cs.gpio.dt_flags != b->cs.gpio.dt_flags || + a->cs.delay != b->cs.delay) { + return false; + } + } else { + if (a->cs.setup_ns != b->cs.setup_ns || + a->cs.hold_ns != b->cs.hold_ns) { + return false; + } + } + + return true; +} + +static nrf_spim_mode_t mode_from_op(uint16_t operation) +{ + if (SPI_MODE_GET(operation) & SPI_MODE_CPOL) { + if (SPI_MODE_GET(operation) & SPI_MODE_CPHA) { + return NRF_SPIM_MODE_3; + } else { + return NRF_SPIM_MODE_2; + } + } else { + if (SPI_MODE_GET(operation) & SPI_MODE_CPHA) { + return NRF_SPIM_MODE_1; + } else { + return NRF_SPIM_MODE_0; + } + } +} + +static nrf_spim_bit_order_t bit_order_from_op(uint16_t operation) +{ + if (operation & SPI_TRANSFER_LSB) { + return NRF_SPIM_BIT_ORDER_LSB_FIRST; + } else { + return NRF_SPIM_BIT_ORDER_MSB_FIRST; + } +} + +uint32_t resolve_freq(uint32_t frequency) +{ +#if defined(CONFIG_SOC_NRF5340_CPUAPP) + /* + * On nRF5340, the 32 Mbps speed is supported by the application core + * when it is running at 128 MHz (see the Timing specifications section + * in the nRF5340 PS). + */ + if (frequency > MHZ(16) && + nrf_clock_hfclk_div_get(NRF_CLOCK) != NRF_CLOCK_HFCLK_DIV_1) { + frequency = MHZ(16); + } +#endif + + if (NRF_SPIM_HAS_PRESCALER) { + return frequency; + } + + /* Get the highest supported frequency not exceeding the requested one */ + if (frequency >= MHZ(32) && NRF_SPIM_HAS_32_MHZ_FREQ) { + return MHZ(32); + } else if (frequency >= MHZ(16) && NRF_SPIM_HAS_16_MHZ_FREQ) { + return MHZ(16); + } else if (frequency >= MHZ(8)) { + return MHZ(8); + } else if (frequency >= MHZ(4)) { + return MHZ(4); + } else if (frequency >= MHZ(2)) { + return MHZ(2); + } else if (frequency >= MHZ(1)) { + return MHZ(1); + } else if (frequency >= KHZ(500)) { + return KHZ(500); + } else if (frequency >= KHZ(250)) { + return KHZ(250); + } else { + return KHZ(125); + } +} + +#if SPI_NRFX_HAS_RAM_BUF || CONFIG_HAS_NORDIC_DMM +#if SPI_NRFX_HAS_RAM_BUF +static int prepare_tx_ram_buf(const struct device *dev, + const uint8_t **tx_buf, + size_t tx_buf_len) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + NRF_SPIM_Type *spim_reg = dev_data->spim.p_reg; + + if (nrf_dma_accessible_check(spim_reg, *tx_buf)) { + return 0; + } + + if (tx_buf_len > SPI_NRFX_RAM_BUF_SIZE) { + return -ENOSPC; + } + + memcpy(dev_config->tx_ram_buf, *tx_buf, tx_buf_len); + *tx_buf = dev_config->tx_ram_buf; + return 0; +} + +static int prepare_rx_ram_buf(const struct device *dev, + uint8_t **rx_buf, + size_t rx_buf_len) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + NRF_SPIM_Type *spim_reg = dev_data->spim.p_reg; + + if (nrf_dma_accessible_check(spim_reg, *rx_buf)) { + return 0; + } + + if (rx_buf_len > SPI_NRFX_RAM_BUF_SIZE) { + return -ENOSPC; + } + + *rx_buf = dev_config->rx_ram_buf; + return 0; +} + +static void release_rx_ram_buf(const struct device *dev, const uint8_t *rx_buf) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + + if (rx_buf == dev_data->rx_user_buf) { + return; + } + + if (rx_buf != dev_config->rx_ram_buf) { + return; + } + + memcpy(dev_data->rx_user_buf, rx_buf, dev_data->rx_user_buf_len); +} + +#else /* SPI_NRFX_HAS_RAM_BUF */ + +static int prepare_tx_ram_buf(const struct device *dev, + const uint8_t **tx_buf, + size_t tx_buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(tx_buf); + ARG_UNUSED(tx_buf_len); + return 0; +} + +static int prepare_rx_ram_buf(const struct device *dev, + uint8_t **rx_buf, + size_t rx_buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(rx_buf); + ARG_UNUSED(rx_buf_len); + return 0; +} + +static void release_rx_ram_buf(const struct device *dev, const uint8_t *rx_buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(rx_buf); +} +#endif /* SPI_NRFX_HAS_RAM_BUF */ + +#if CONFIG_HAS_NORDIC_DMM +static int prepare_tx_dmm_buf(const struct device *dev, + const uint8_t **tx_buf, + size_t tx_buf_len) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + + if (*tx_buf == NULL || tx_buf_len == 0) { + return 0; + } + + return dmm_buffer_out_prepare(dev_config->mem_reg, + (void *)*tx_buf, + tx_buf_len, + (void **)tx_buf); +} + +static int prepare_rx_dmm_buf(const struct device *dev, + uint8_t **rx_buf, + size_t rx_buf_len) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + + if (*rx_buf == NULL || rx_buf_len == 0) { + return 0; + } + + return dmm_buffer_in_prepare(dev_config->mem_reg, + *rx_buf, + rx_buf_len, + (void **)rx_buf); +} + +static void release_tx_dmm_buf(const struct device *dev, const uint8_t *tx_buf) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + + dmm_buffer_out_release(dev_config->mem_reg, (void *)tx_buf); +} + +static void release_rx_dmm_buf(const struct device *dev, const uint8_t *rx_buf) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + + dmm_buffer_in_release(dev_config->mem_reg, + dev_data->rx_user_buf, + dev_data->rx_user_buf_len, + (void *)rx_buf); +} + +#else /* CONFIG_HAS_NORDIC_DMM */ + +static int prepare_tx_dmm_buf(const struct device *dev, + const uint8_t **tx_buf, + size_t tx_buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(tx_buf); + ARG_UNUSED(tx_buf_len); + return 0; +} + +static int prepare_rx_dmm_buf(const struct device *dev, + uint8_t **rx_buf, + size_t rx_buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(rx_buf); + ARG_UNUSED(rx_buf_len); + return 0; +} + +static void release_tx_dmm_buf(const struct device *dev, const uint8_t *tx_buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(tx_buf); +} + +static void release_rx_dmm_buf(const struct device *dev, const uint8_t *rx_buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(rx_buf); +} +#endif /* CONFIG_HAS_NORDIC_DMM */ + +static int prepare_tx_buf(const struct device *dev, + const uint8_t **tx_buf, + size_t tx_buf_len) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + int ret; + + dev_data->tx_user_buf = *tx_buf; + + ret = prepare_tx_ram_buf(dev, tx_buf, tx_buf_len); + if (ret) { + return ret; + } + + ret = prepare_tx_dmm_buf(dev, tx_buf, tx_buf_len); + if (ret) { + return ret; + } + + return 0; +} + +static int prepare_rx_buf(const struct device *dev, + uint8_t **rx_buf, + size_t rx_buf_len) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + int ret; + + dev_data->rx_user_buf = *rx_buf; + dev_data->rx_user_buf_len = rx_buf_len; + + ret = prepare_rx_ram_buf(dev, rx_buf, rx_buf_len); + if (ret) { + return ret; + } + + ret = prepare_rx_dmm_buf(dev, rx_buf, rx_buf_len); + if (ret) { + return ret; + } + + return 0; +} + +static void release_tx_buf(const struct device *dev, const uint8_t *tx_buf) +{ + release_tx_dmm_buf(dev, tx_buf); +} + +static void release_rx_buf(const struct device *dev, const uint8_t *rx_buf) +{ + release_rx_ram_buf(dev, rx_buf); + release_rx_dmm_buf(dev, rx_buf); +} + +#else /* SPI_NRFX_HAS_RAM_BUF || CONFIG_HAS_NORDIC_DMM */ + +static int prepare_tx_buf(const struct device *dev, + const uint8_t **tx_buf, + size_t tx_buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(tx_buf); + ARG_UNUSED(tx_buf_len); + return 0; +} + +static int prepare_rx_buf(const struct device *dev, + uint8_t **rx_buf, + size_t rx_buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(rx_buf); + ARG_UNUSED(rx_buf_len); + return 0; +} + +static void release_tx_buf(const struct device *dev, const uint8_t *tx_buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(tx_buf); +} + +static void release_rx_buf(const struct device *dev, const uint8_t *rx_buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(rx_buf); +} +#endif /* SPI_NRFX_HAS_RAM_BUF || CONFIG_HAS_NORDIC_DMM */ + +int spi_nrfx_spim_common_transfer_start(const struct device *dev, + const uint8_t *tx_buf, + size_t tx_buf_len, + uint8_t *rx_buf, + size_t rx_buf_len) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + nrfx_spim_t *spim = &dev_data->spim; + struct nrfy_spim_xfer_desc_t xfer; + int ret; + + if (tx_buf_len > dev_config->max_transfer_len || + rx_buf_len > dev_config->max_transfer_len) { + return -EINVAL; + } + + ret = prepare_tx_buf(dev, &tx_buf, tx_buf_len); + if (ret) { + return ret; + } + + ret = prepare_rx_buf(dev, &rx_buf, rx_buf_len); + if (ret) { + release_tx_buf(dev, tx_buf); + return ret; + } + + /* Set the accessible and aligned buffers */ + xfer.p_tx_buffer = (uint8_t *)tx_buf, + xfer.tx_length = tx_buf_len, + xfer.p_rx_buffer = rx_buf, + xfer.rx_length = rx_buf_len, + + ret = nrfx_spim_xfer(spim, &xfer, 0); + if (ret) { + release_tx_buf(dev, tx_buf); + release_rx_buf(dev, rx_buf); + return ret; + } + + return 0; +} + +void spi_nrfx_spim_common_transfer_stop(const struct device *dev) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + nrfx_spim_t *spim = &dev_data->spim; + + nrfx_spim_abort(spim); +} + +void spi_nrfx_spim_common_transfer_end(const struct device *dev, + const nrfx_spim_xfer_desc_t *xfer) +{ + release_tx_buf(dev, (const uint8_t *)xfer->p_tx_buffer); + release_rx_buf(dev, (const uint8_t *)xfer->p_rx_buffer); +} + +int spi_nrfx_spim_common_configure(const struct device *dev, const struct spi_config *spi_cfg) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + nrfx_spim_config_t spim_cfg; + int ret; + + if (dev_data->configured && spi_config_equal(&dev_data->spi_cfg, spi_cfg)) { + return 0; + } + + if (spi_cfg->operation & SPI_HALF_DUPLEX) { + LOG_ERR("Half-duplex not supported"); + return -ENOTSUP; + } + + if (SPI_OP_MODE_GET(spi_cfg->operation) != SPI_OP_MODE_MASTER) { + LOG_ERR("Slave mode is not supported on %s", dev->name); + return -EINVAL; + } + + if (spi_cfg->operation & SPI_MODE_LOOP) { + LOG_ERR("Loopback mode is not supported"); + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && + (spi_cfg->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { + LOG_ERR("Only single line mode is supported"); + return -EINVAL; + } + + if (SPI_WORD_SIZE_GET(spi_cfg->operation) != 8) { + LOG_ERR("Word sizes other than 8 bits are not supported"); + return -EINVAL; + } + + if (spi_cfg->frequency < KHZ(125)) { + LOG_ERR("Frequencies lower than 125 kHz are not supported"); + return -EINVAL; + } + + spim_cfg.ss_pin = NRF_SPIM_PIN_NOT_CONNECTED; + spim_cfg.orc = dev_config->orc; + spim_cfg.frequency = MIN(resolve_freq(spi_cfg->frequency), dev_config->max_freq); + spim_cfg.mode = mode_from_op(spi_cfg->operation); + spim_cfg.bit_order = bit_order_from_op(spi_cfg->operation); +#if NRF_SPIM_HAS_DCX + spim_cfg.dcx_pin = NRF_SPIM_PIN_NOT_CONNECTED; +#endif +#if NRF_SPIM_HAS_RXDELAY + spim_cfg.rx_delay = dev_config->rx_delay; +#endif + spim_cfg.skip_gpio_cfg = true; + spim_cfg.skip_psel_cfg = true; + + if (dev_data->configured) { + ret = nrfx_spim_reconfigure(&dev_data->spim, &spim_cfg); + } else { + ret = nrfx_spim_init(&dev_data->spim, &spim_cfg, evt_handler, (void *)dev); + } + + if (ret) { + LOG_ERR("Failed to configure nrfx driver: %d", ret); + return ret; + } + +#if defined(CONFIG_NRF52_ANOMALY_58_WORKAROUND) +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpiote), okay) + nrfx_spim_nrf52_anomaly_58_init(&dev_data->spim, + &GPIOTE_NRFX_INST_BY_NODE(DT_NODELABEL(gpiote))); +#else +#error "GPIOTE is not enabled, anomaly 58 workaround cannot be applied for SPIM" +#endif +#endif + + spi_config_copy(&dev_data->spi_cfg, spi_cfg); + dev_data->configured = true; + return 0; +} + +static int cs_put(const struct device *dev) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + const struct gpio_dt_spec *cs_gpios = dev_config->cs_gpios; + const uint8_t cs_gpios_size = dev_config->cs_gpios_size; + int ret; + + for (size_t i = 0; i < cs_gpios_size; i++) { + ret = pm_device_runtime_put(cs_gpios[i].port); + if (ret) { + return ret; + } + } + + return 0; +} + +static int pm_suspend(const struct device *dev) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + int ret; + +#if CONFIG_PINCTRL_KEEP_SLEEP_STATE + const struct spi_nrfx_common_config *dev_config = dev->config; +#endif + + if (dev_data->configured) { + nrfx_spim_uninit(&dev_data->spim); + dev_data->configured = false; + } + + ret = cs_put(dev); + if (ret) { + return ret; + } + +#if CONFIG_PINCTRL_KEEP_SLEEP_STATE + ret = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP); + if (ret) { + return ret; + } +#endif + + return 0; +} + +static int cs_get(const struct device *dev) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + const struct gpio_dt_spec *cs_gpios = dev_config->cs_gpios; + const uint8_t cs_gpios_size = dev_config->cs_gpios_size; + int ret; + + for (size_t i = 0; i < cs_gpios_size; i++) { + ret = pm_device_runtime_get(cs_gpios[i].port); + if (ret) { + return ret; + } + } + + return 0; +} + +static int pm_resume(const struct device *dev) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + int ret; + + ret = cs_get(dev); + if (ret) { + return ret; + } + + ret = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + return 0; +} + +int spi_nrfx_spim_common_pm_action(const struct device *dev, enum pm_device_action action) +{ + int ret; + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + ret = pm_suspend(dev); + break; + + case PM_DEVICE_ACTION_RESUME: + ret = pm_resume(dev); + break; + + case PM_DEVICE_ACTION_TURN_OFF: + case PM_DEVICE_ACTION_TURN_ON: + ret = -ENOTSUP; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int cs_init(const struct device *dev) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + const struct gpio_dt_spec *cs_gpios = dev_config->cs_gpios; + const uint8_t cs_gpios_size = dev_config->cs_gpios_size; + int ret; + + for (size_t i = 0; i < cs_gpios_size; i++) { + ret = gpio_pin_configure_dt(&cs_gpios[i], GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + } + + return 0; +} + +#if SPI_NRFX_HAS_WAKE +static void wake_stop(const struct device *dev) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + + gpio_pin_interrupt_configure_dt(&dev_config->wake_pin, GPIO_INT_DISABLE); + gpio_pin_configure_dt(&dev_config->wake_pin, WAKE_PIN_STOP_FLAGS); + + dev_data->wake_handler(dev_data->dev); +} + +static void wake_pin_callback_handler(const struct device *port, + struct gpio_callback *cb, + uint32_t pins) +{ + struct spi_nrfx_common_data *dev_data = + CONTAINER_OF(cb, struct spi_nrfx_common_data, wake_pin_callback); + + ARG_UNUSED(port); + ARG_UNUSED(pins); + + k_timer_stop(&dev_data->wake_timer); + + if (k_timer_status_get(&dev_data->wake_timer)) { + return; + } + + wake_stop(dev_data->dev); +} + +static void wake_timer_expired_handler(struct k_timer *timer) +{ + struct spi_nrfx_common_data *dev_data = + CONTAINER_OF(timer, struct spi_nrfx_common_data, wake_timer); + + wake_stop(dev_data->dev); +} + +static int wake_init(const struct device *dev) +{ + struct spi_nrfx_common_data *dev_data = dev->data; + const struct spi_nrfx_common_config *dev_config = dev->config; + int ret; + + ret = gpio_pin_configure_dt(&dev_config->wake_pin, WAKE_PIN_STOP_FLAGS); + if (ret) { + return ret; + } + + gpio_init_callback(&dev_data->wake_pin_callback, + wake_pin_callback_handler, + BIT(dev_config->wake_pin.pin)); + + ret = gpio_add_callback(dev_config->wake_pin.port, &dev_data->wake_pin_callback); + if (ret) { + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(&dev_config->wake_pin, GPIO_INT_DISABLE); + if (ret) { + return ret; + } + + k_timer_init(&dev_data->wake_timer, wake_timer_expired_handler, NULL); + return 0; +} + +#else + +static int wake_init(const struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} +#endif + +int spi_nrfx_spim_common_init(const struct device *dev) +{ + const struct spi_nrfx_common_config *dev_config = dev->config; + int ret; + + dev_config->irq_connect(); + + ret = cs_init(dev); + if (ret) { + return ret; + } + + ret = wake_init(dev); + if (ret) { + return ret; + } + + return 0; +} + +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT +int spi_nrfx_spim_common_deinit(const struct device *dev) +{ + return pm_device_driver_deinit(dev, spi_nrfx_spim_common_pm_action); +} +#endif diff --git a/drivers/spi/spi_nrfx_spim_common.h b/drivers/spi/spi_nrfx_spim_common.h new file mode 100644 index 000000000000..6eb125b3c2f5 --- /dev/null +++ b/drivers/spi/spi_nrfx_spim_common.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2026 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SPI_NRFX_SPIM_COMMON_H_ +#define ZEPHYR_DRIVERS_SPI_NRFX_SPIM_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * @cond INTERNAL_HIDDEN + */ + +#if CONFIG_SPI_NRFX_RAM_BUFFER_SIZE +#define SPI_NRFX_HAS_RAM_BUF 1 +#define SPI_NRFX_RAM_BUF_SIZE CONFIG_SPI_NRFX_RAM_BUFFER_SIZE +#endif + +#if DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(nordic_nrf_spim, wake_gpios) +#define SPI_NRFX_HAS_WAKE 1 +#endif + +typedef void (*spi_nrfx_data_common_wake_handler)(const struct device *dev); + +typedef void (*spi_nrfx_data_common_evt_handler)(const struct device *dev, + nrfx_spim_event_t *evt); + +struct spi_nrfx_common_data { + nrfx_spim_t spim; + bool configured; + struct spi_config spi_cfg; +#if SPI_NRFX_HAS_RAM_BUF || CONFIG_HAS_NORDIC_DMM + const uint8_t *tx_user_buf; + uint8_t *rx_user_buf; + size_t rx_user_buf_len; +#endif +#if SPI_NRFX_HAS_WAKE + const struct device *dev; + struct gpio_callback wake_pin_callback; + struct k_timer wake_timer; + spi_nrfx_data_common_wake_handler wake_handler; +#endif +}; + +struct spi_nrfx_common_config { + void (*irq_connect)(void); + const struct pinctrl_dev_config *pcfg; + spi_nrfx_data_common_evt_handler evt_handler; + const struct gpio_dt_spec *cs_gpios; +#if SPI_NRFX_HAS_RAM_BUF + uint8_t *tx_ram_buf; + uint8_t *rx_ram_buf; +#endif +#if CONFIG_HAS_NORDIC_DMM + void *mem_reg; +#endif + uint32_t max_freq; + uint16_t max_transfer_len; + uint8_t orc; + uint8_t cs_gpios_size; +#if NRF_SPIM_HAS_RXDELAY + uint8_t rx_delay; +#endif +#if SPI_NRFX_HAS_WAKE + struct gpio_dt_spec wake_pin; +#endif +}; + +void spi_nrfx_spim_common_wake_start(const struct device *dev, + spi_nrfx_data_common_wake_handler handler); + +void spi_nrfx_spim_common_cs_set(const struct device *dev, const struct spi_config *spi_cfg); +void spi_nrfx_spim_common_cs_clear(const struct device *dev, const struct spi_config *spi_cfg); + +int spi_nrfx_spim_common_transfer_start(const struct device *dev, + const uint8_t *tx_buf, + size_t tx_buf_len, + uint8_t *rx_buf, + size_t rx_buf_len); + +void spi_nrfx_spim_common_transfer_stop(const struct device *dev); + +void spi_nrfx_spim_common_transfer_end(const struct device *dev, + const nrfx_spim_xfer_desc_t *xfer); + +int spi_nrfx_spim_common_configure(const struct device *dev, const struct spi_config *spi_cfg); +int spi_nrfx_spim_common_pm_action(const struct device *dev, enum pm_device_action action); + +int spi_nrfx_spim_common_init(const struct device *dev); +int spi_nrfx_spim_common_deinit(const struct device *dev); + +#define SPI_NRFX_COMMON_IRQ_DEFINE(inst, _data) \ + NRF_DT_INST_IRQ_DIRECT_DEFINE( \ + inst, \ + nrfx_spim_irq_handler, \ + _data \ + ) \ + \ + static void CONCAT(irq_connect, inst)(void) \ + { \ + NRF_DT_INST_IRQ_CONNECT( \ + inst, \ + nrfx_spim_irq_handler, \ + _data \ + ); \ + } + +#define SPI_NRFX_COMMON_RAM_BUF_DEFINE(inst) \ + IF_ENABLED( \ + SPI_NRFX_HAS_RAM_BUF, \ + ( \ + static uint8_t CONCAT(tx_ram_buf, inst)[SPI_NRFX_RAM_BUF_SIZE] \ + DMM_MEMORY_SECTION(DT_DRV_INST(inst)); \ + \ + static uint8_t CONCAT(rx_ram_buf, inst)[SPI_NRFX_RAM_BUF_SIZE] \ + DMM_MEMORY_SECTION(DT_DRV_INST(inst)); \ + ) \ + ) + +#define SPI_NRFX_COMMON_CS_GPIO_INIT(idx, inst) \ + GPIO_DT_SPEC_INST_GET_BY_IDX(inst, cs_gpios, idx) + +#define SPI_NRFX_COMMON_CS_GPIOS_DEFINE(inst) \ + static const struct gpio_dt_spec CONCAT(cs_gpios, inst)[] = { \ + LISTIFY( \ + DT_INST_PROP_LEN_OR(inst, cs_gpios, 0), \ + SPI_NRFX_COMMON_CS_GPIO_INIT, \ + (,), \ + inst \ + ) \ + } + +#define SPI_NRFX_COMMON_DEFINE(inst, _data) \ + SPI_NRFX_COMMON_IRQ_DEFINE(inst, _data) \ + SPI_NRFX_COMMON_RAM_BUF_DEFINE(inst); \ + SPI_NRFX_COMMON_CS_GPIOS_DEFINE(inst); \ + PINCTRL_DT_INST_DEFINE(inst); + +#define SPI_NRFX_COMMON_DATA_WAKE_INIT(inst) \ + IF_ENABLED( \ + SPI_NRFX_HAS_WAKE, \ + ( \ + .dev = DEVICE_DT_INST_GET(inst), \ + ) \ + ) + +#define SPI_NRFX_COMMON_DATA_INIT(inst) \ + { \ + .spim = NRFX_SPIM_INSTANCE(DT_INST_REG_ADDR(inst)), \ + SPI_NRFX_COMMON_DATA_WAKE_INIT(inst) \ + } + +#define SPI_NRFX_COMMON_CONFIG_INIT_RX_DELAY(inst) \ + IF_ENABLED( \ + NRF_SPIM_HAS_RXDELAY, \ + ( \ + .rx_delay = DT_INST_PROP_OR(inst, rx_delay, 0), \ + ) \ + ) + +#define SPI_NRFX_COMMON_CONFIG_WAKE_INIT(inst) \ + IF_ENABLED( \ + SPI_NRFX_HAS_WAKE, \ + ( \ + .wake_pin = GPIO_DT_SPEC_INST_GET_OR(inst, wake_gpios, {0}), \ + ) \ + ) + +#define SPI_NRFX_COMMON_CONFIG_RAM_BUF_INIT(inst) \ + IF_ENABLED( \ + SPI_NRFX_HAS_RAM_BUF, \ + ( \ + .tx_ram_buf = CONCAT(tx_ram_buf, inst), \ + .rx_ram_buf = CONCAT(rx_ram_buf, inst), \ + ) \ + ) + +#define SPI_NRFX_COMMON_CONFIG_MEM_REG_INIT(inst) \ + IF_ENABLED( \ + CONFIG_HAS_NORDIC_DMM, \ + ( \ + .mem_reg = DMM_DEV_TO_REG(DT_DRV_INST(inst)), \ + ) \ + ) + +#define SPI_NRFX_COMMON_CONFIG_INIT(inst, _evt_handler) \ + { \ + .irq_connect = CONCAT(irq_connect, inst), \ + .evt_handler = _evt_handler, \ + .cs_gpios = CONCAT(cs_gpios, inst), \ + .cs_gpios_size = ARRAY_SIZE(CONCAT(cs_gpios, inst)), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .max_freq = DT_INST_PROP(inst, max_frequency), \ + .max_transfer_len = BIT_MASK(DT_INST_PROP(inst, easydma_maxcnt_bits)), \ + .orc = DT_INST_PROP(inst, overrun_character), \ + SPI_NRFX_COMMON_CONFIG_INIT_RX_DELAY(inst) \ + SPI_NRFX_COMMON_CONFIG_WAKE_INIT(inst) \ + SPI_NRFX_COMMON_CONFIG_RAM_BUF_INIT(inst) \ + SPI_NRFX_COMMON_CONFIG_MEM_REG_INIT(inst) \ + } + +/** + * @endcond + */ + +#endif /* ZEPHYR_DRIVERS_SPI_NRFX_SPIM_COMMON_H_ */ diff --git a/drivers/spi/spi_nrfx_spim_rtio.c b/drivers/spi/spi_nrfx_spim_rtio.c new file mode 100644 index 000000000000..35990b96f231 --- /dev/null +++ b/drivers/spi/spi_nrfx_spim_rtio.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2026 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_nrf_spim + +#include "spi_nrfx_spim_common.h" +#include "spi_rtio.h" + +LOG_MODULE_DECLARE(spi_nrfx_spim); + +struct driver_data { + struct spi_nrfx_common_data common; +}; + +struct driver_config { + struct spi_nrfx_common_config common; + struct spi_rtio *spi_rtio_ctx; +}; + +static void iodev_end_curr(const struct device *dev); +static void iodev_start_curr(const struct device *dev); +static void iodev_end_txn(const struct device *dev, int result); + +static void iodev_await_callback(struct rtio_iodev_sqe *iodev_sqe, void *userdata) +{ + const struct device *dev = userdata; + + ARG_UNUSED(iodev_sqe); + + iodev_end_curr(dev); +} + +static void iodev_start_curr(const struct device *dev) +{ + const struct driver_config *dev_config = dev->config; + struct spi_rtio *spi_rtio_ctx = dev_config->spi_rtio_ctx; + struct rtio_sqe *sqe = &spi_rtio_ctx->txn_curr->sqe; + struct spi_dt_spec *spi_spec = sqe->iodev->data; + struct spi_config *spi_cfg = &spi_spec->config; + const uint8_t *tx_buf; + size_t tx_buf_len; + uint8_t *rx_buf; + size_t rx_buf_len; + struct rtio_iodev_sqe *iodev_sqe; + int ret; + + switch (sqe->op) { + case RTIO_OP_TX: + tx_buf = sqe->tx.buf; + tx_buf_len = sqe->tx.buf_len; + rx_buf = NULL; + rx_buf_len = 0; + break; + + case RTIO_OP_RX: + tx_buf = NULL; + tx_buf_len = 0; + rx_buf = sqe->rx.buf; + rx_buf_len = sqe->rx.buf_len; + break; + + case RTIO_OP_TINY_TX: + tx_buf = sqe->tiny_tx.buf; + tx_buf_len = sqe->tiny_tx.buf_len; + rx_buf = NULL; + rx_buf_len = 0; + break; + + case RTIO_OP_TXRX: + tx_buf = sqe->txrx.tx_buf; + tx_buf_len = sqe->txrx.buf_len; + rx_buf = sqe->txrx.rx_buf; + rx_buf_len = sqe->txrx.buf_len; + break; + + default: + tx_buf = NULL; + tx_buf_len = 0; + rx_buf = NULL; + rx_buf_len = 0; + break; + } + + switch (sqe->op) { + case RTIO_OP_TX: + case RTIO_OP_RX: + case RTIO_OP_TINY_TX: + case RTIO_OP_TXRX: + ret = spi_nrfx_spim_common_configure(dev, spi_cfg); + if (ret) { + break; + } + + spi_nrfx_spim_common_cs_set(dev, spi_cfg); + + ret = spi_nrfx_spim_common_transfer_start(dev, + tx_buf, + tx_buf_len, + rx_buf, + rx_buf_len); + break; + + case RTIO_OP_AWAIT: + iodev_sqe = CONTAINER_OF(sqe, struct rtio_iodev_sqe, sqe); + rtio_iodev_sqe_await_signal(iodev_sqe, iodev_await_callback, (void *)dev); + ret = 0; + break; + + default: + ret = -ENOTSUP; + break; + } + + if (ret) { + iodev_end_txn(dev, ret); + } +} + +static void iodev_end_txn(const struct device *dev, int result) +{ + const struct driver_config *dev_config = dev->config; + struct spi_rtio *spi_rtio_ctx = dev_config->spi_rtio_ctx; + struct rtio_sqe *sqe = &spi_rtio_ctx->txn_head->sqe; + struct spi_dt_spec *spi_spec = sqe->iodev->data; + struct spi_config *spi_cfg = &spi_spec->config; + + if (spi_rtio_complete(spi_rtio_ctx, result) == false) { + if ((spi_cfg->operation & SPI_HOLD_ON_CS) == 0) { + spi_nrfx_spim_common_cs_clear(dev, spi_cfg); + } + pm_device_runtime_put(dev); + return; + } + + iodev_start_curr(dev); +} + +static void iodev_end_curr(const struct device *dev) +{ + const struct driver_config *dev_config = dev->config; + struct spi_rtio *spi_rtio_ctx = dev_config->spi_rtio_ctx; + + spi_rtio_ctx->txn_curr = rtio_txn_next(spi_rtio_ctx->txn_curr); + if (spi_rtio_ctx->txn_curr == NULL) { + iodev_end_txn(dev, 0); + return; + } + + iodev_start_curr(dev); +} + +static void spim_evt_handler(const struct device *dev, nrfx_spim_event_t *evt) +{ + spi_nrfx_spim_common_transfer_end(dev, &evt->xfer_desc); + iodev_end_curr(dev); +} + +static int driver_api_transceive(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + const struct driver_config *dev_config = dev->config; + + return spi_rtio_transceive(dev_config->spi_rtio_ctx, spi_cfg, tx_bufs, rx_bufs); +} + +#ifdef CONFIG_SPI_ASYNC +static int driver_api_transceive_async(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, + spi_callback_t cb, + void *userdata) +{ + const struct driver_config *dev_config = dev->config; + + return spi_rtio_transceive_cb(dev_config->spi_rtio_ctx, + spi_cfg, + tx_bufs, + rx_bufs, + cb, + userdata); +} +#endif + +static void spim_wake_handler(const struct device *dev) +{ + iodev_start_curr(dev); +} + +static void driver_api_iodev_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct driver_config *dev_config = dev->config; + struct spi_rtio *spi_rtio_ctx = dev_config->spi_rtio_ctx; + + if (spi_rtio_submit(spi_rtio_ctx, iodev_sqe)) { + pm_device_runtime_get(dev); + spi_nrfx_spim_common_wake_start(dev, spim_wake_handler); + } +} + +static DEVICE_API(spi, driver_api) = { + .transceive = driver_api_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = driver_api_transceive_async, +#endif + .iodev_submit = driver_api_iodev_submit, + .release = spi_rtio_release, +}; + +static int driver_init(const struct device *dev) +{ + const struct driver_config *dev_config = dev->config; + int ret; + + ret = spi_nrfx_spim_common_init(dev); + if (ret) { + return ret; + } + + spi_rtio_init(dev_config->spi_rtio_ctx, dev); + + return pm_device_driver_init(dev, spi_nrfx_spim_common_pm_action); +} + +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT +static int driver_deinit(const struct device *dev) +{ + return spi_nrfx_spim_common_deinit(dev); +} +#endif + +#define DRIVER_DEFINE(inst) \ + static struct driver_data CONCAT(data, inst) = { \ + .common = SPI_NRFX_COMMON_DATA_INIT(inst), \ + }; \ + \ + SPI_NRFX_COMMON_DEFINE(inst, &CONCAT(data, inst)); \ + \ + SPI_RTIO_DEFINE( \ + CONCAT(spi_rtio_ctx, inst), \ + CONFIG_SPI_NRFX_SPIM_RTIO_SQE_POOL_SIZE, \ + CONFIG_SPI_NRFX_SPIM_RTIO_CQE_POOL_SIZE \ + ); \ + \ + static const struct driver_config CONCAT(config, inst) = { \ + .common = SPI_NRFX_COMMON_CONFIG_INIT( \ + inst, \ + spim_evt_handler \ + ), \ + .spi_rtio_ctx = &CONCAT(spi_rtio_ctx, inst), \ + }; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, spi_nrfx_spim_common_pm_action, 1); \ + \ + SPI_DEVICE_DT_INST_DEINIT_DEFINE( \ + inst, \ + driver_init, \ + driver_deinit, \ + PM_DEVICE_DT_INST_GET(inst), \ + &CONCAT(data, inst), \ + &CONCAT(config, inst), \ + POST_KERNEL, \ + CONFIG_SPI_INIT_PRIORITY, \ + &driver_api \ + ) + +DT_INST_FOREACH_STATUS_OKAY(DRIVER_DEFINE) diff --git a/drivers/spi/spi_nrfx_spis.c b/drivers/spi/spi_nrfx_spis.c index 1f667fb8b289..c06c8e263f5c 100644 --- a/drivers/spi/spi_nrfx_spis.c +++ b/drivers/spi/spi_nrfx_spis.c @@ -7,7 +7,7 @@ #define DT_DRV_COMPAT nordic_nrf_spis #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_numaker.c b/drivers/spi/spi_numaker.c index dc5e0327634b..c8681c1b01d9 100644 --- a/drivers/spi/spi_numaker.c +++ b/drivers/spi/spi_numaker.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include LOG_MODULE_REGISTER(spi_numaker, CONFIG_SPI_LOG_LEVEL); diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h index 3bf662818eda..ee42c0dc43ce 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h @@ -8,12 +8,12 @@ #define ZEPHYR_DRIVERS_SPI_SPI_NXP_LPSPI_PRIV_H_ #include -#include #include #include #include #include +#include "../spi_rtio.h" #include "../spi_context.h" #if CONFIG_NXP_LP_FLEXCOMM diff --git a/drivers/spi/spi_nxp_s32.h b/drivers/spi/spi_nxp_s32.h index 4f6fe6a9b0c0..18ff0ef167d7 100644 --- a/drivers/spi/spi_nxp_s32.h +++ b/drivers/spi/spi_nxp_s32.h @@ -7,7 +7,7 @@ #define ZEPHYR_DRIVERS_SPI_SPI_NXP_S32_H_ #include -#include +#include "spi_rtio.h" #include #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL diff --git a/drivers/spi/spi_oc_simple.c b/drivers/spi/spi_oc_simple.c index d3069366bf71..77789e719e5b 100644 --- a/drivers/spi/spi_oc_simple.c +++ b/drivers/spi/spi_oc_simple.c @@ -12,7 +12,7 @@ LOG_MODULE_REGISTER(spi_oc_simple); #include #include -#include +#include "spi_rtio.h" #include "spi_context.h" #include "spi_oc_simple.h" diff --git a/drivers/spi/spi_opentitan.c b/drivers/spi/spi_opentitan.c index efcf4d0298c4..9e9bf2d0109b 100644 --- a/drivers/spi/spi_opentitan.c +++ b/drivers/spi/spi_opentitan.c @@ -11,7 +11,7 @@ LOG_MODULE_REGISTER(spi_opentitan); #include #include -#include +#include "spi_rtio.h" #include #include diff --git a/drivers/spi/spi_pl022.c b/drivers/spi/spi_pl022.c index 9a2c493d99f8..8c47247bb862 100644 --- a/drivers/spi/spi_pl022.c +++ b/drivers/spi/spi_pl022.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_psoc6.c b/drivers/spi/spi_psoc6.c index cfbc2880d7f8..f9aa3088c634 100644 --- a/drivers/spi/spi_psoc6.c +++ b/drivers/spi/spi_psoc6.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(spi_psoc6); #include #include #include -#include +#include "spi_rtio.h" #include #include "spi_context.h" diff --git a/drivers/spi/spi_pw.c b/drivers/spi/spi_pw.c index cff2135976cb..ee024884df4c 100644 --- a/drivers/spi/spi_pw.c +++ b/drivers/spi/spi_pw.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "spi_rtio.h" #if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie) BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "DT need CONFIG_PCIE"); diff --git a/drivers/spi/spi_renesas_rz.c b/drivers/spi/spi_renesas_rz.c index 34dc519db6b1..28a0b47310cf 100644 --- a/drivers/spi/spi_renesas_rz.c +++ b/drivers/spi/spi_renesas_rz.c @@ -13,7 +13,7 @@ #include #include "r_spi.h" #ifdef CONFIG_SPI_RTIO -#include +#include "spi_rtio.h" #include #endif #include diff --git a/drivers/spi/spi_renesas_rz_rspi.c b/drivers/spi/spi_renesas_rz_rspi.c index 6576572f15b5..75b1dd2ac649 100644 --- a/drivers/spi/spi_renesas_rz_rspi.c +++ b/drivers/spi/spi_renesas_rz_rspi.c @@ -16,7 +16,7 @@ #include "r_dmac_b.h" #endif #ifdef CONFIG_SPI_RTIO -#include +#include "spi_rtio.h" #include #endif #include diff --git a/drivers/spi/spi_rtio.c b/drivers/spi/spi_rtio.c index 4d21f092febd..7fd38fd0c235 100644 --- a/drivers/spi/spi_rtio.c +++ b/drivers/spi/spi_rtio.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include #include @@ -167,6 +167,7 @@ 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, + bool no_response, struct rtio_sqe **last_sqe) { int ret = 0; @@ -262,11 +263,19 @@ int spi_rtio_copy(struct rtio *r, 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); + if (rx_buf) { + rtio_sqe_prep_transceive(sqe, iodev, RTIO_PRIO_NORM, + (uint8_t *)tx_buf, + (uint8_t *)rx_buf, + (uint32_t)rx_len, + NULL); + } else { + rtio_sqe_prep_write(sqe, iodev, RTIO_PRIO_NORM, + (uint8_t *)tx_buf, + (uint32_t)rx_len, + NULL); + } + tx_len -= rx_len; tx_buf += rx_len; rx++; @@ -278,11 +287,19 @@ int spi_rtio_copy(struct rtio *r, 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); + if (tx_buf) { + rtio_sqe_prep_transceive(sqe, iodev, RTIO_PRIO_NORM, + (uint8_t *)tx_buf, + (uint8_t *)rx_buf, + (uint32_t)tx_len, + NULL); + } else { + rtio_sqe_prep_read(sqe, iodev, RTIO_PRIO_NORM, + (uint8_t *)rx_buf, + (uint32_t)tx_len, + NULL); + } + rx_len -= tx_len; rx_buf += tx_len; tx++; @@ -298,10 +315,14 @@ int spi_rtio_copy(struct rtio *r, } sqe->flags = RTIO_SQE_TRANSACTION; + + if (no_response) { + sqe->flags |= RTIO_SQE_NO_RESPONSE; + } } if (sqe != NULL) { - sqe->flags = 0; + sqe->flags = no_response ? RTIO_SQE_NO_RESPONSE : 0; *last_sqe = sqe; } @@ -336,6 +357,18 @@ static inline void spi_spin_unlock(struct spi_rtio *ctx, k_spinlock_key_t key) k_spin_unlock(&ctx->lock, key); } +/** Lock the shared rtio context used for fallback implementations */ +static inline void spi_r_lock(struct spi_rtio *ctx) +{ + (void)k_sem_take(&ctx->r_lock, K_FOREVER); +} + +/** Unlock the shared rtio context used for fallback implementations */ +static inline void spi_r_unlock(struct spi_rtio *ctx) +{ + k_sem_give(&ctx->r_lock); +} + void spi_rtio_init(struct spi_rtio *ctx, const struct device *dev) { @@ -345,6 +378,7 @@ void spi_rtio_init(struct spi_rtio *ctx, ctx->dt_spec.bus = dev; ctx->iodev.data = &ctx->dt_spec; ctx->iodev.api = &spi_iodev_api; + k_sem_init(&ctx->r_lock, 1, 1); } /** @@ -419,10 +453,17 @@ int spi_rtio_transceive(struct spi_rtio *ctx, return -EINVAL; } + if (config->operation & SPI_LOCK_ON) { + return -EINVAL; + } + + spi_r_lock(ctx); + dt_spec->config = *config; - ret = spi_rtio_copy(ctx->r, &ctx->iodev, tx_bufs, rx_bufs, &sqe); + ret = spi_rtio_copy(ctx->r, &ctx->iodev, tx_bufs, rx_bufs, false, &sqe); if (ret < 0) { + spi_r_unlock(ctx); return ret; } @@ -444,5 +485,89 @@ int spi_rtio_transceive(struct spi_rtio *ctx, ret--; } + spi_r_unlock(ctx); return err; } + +#if CONFIG_SPI_ASYNC +static void transceive_cb_callback(struct rtio *r, + const struct rtio_sqe *sqe, + int res, + void *arg0) +{ + struct spi_rtio *ctx = arg0; + spi_callback_t cb = ctx->async_cb; + struct spi_dt_spec *dt_spec = &ctx->dt_spec; + const struct device *dev = dt_spec->bus; + void *userdata = sqe->userdata; + + /* + * We have stored the context data needed for the callback so + * we can unlock the context here + */ + spi_r_unlock(ctx); + + if (cb == NULL) { + return; + } + + cb(dev, res, userdata); +} + +int spi_rtio_transceive_cb(struct spi_rtio *ctx, + 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) +{ + struct spi_dt_spec *dt_spec = &ctx->dt_spec; + struct rtio_sqe *sqe; + int ret = 0; + + if (tx_bufs == NULL && rx_bufs == NULL) { + return -EINVAL; + } + + if (config->operation & SPI_LOCK_ON) { + return -EINVAL; + } + + spi_r_lock(ctx); + + dt_spec->config = *config; + + ret = spi_rtio_copy(ctx->r, &ctx->iodev, tx_bufs, rx_bufs, true, &sqe); + if (ret < 0) { + spi_r_unlock(ctx); + return ret; + } + + sqe->flags |= RTIO_SQE_CHAINED; + + sqe = rtio_sqe_acquire(ctx->r); + if (sqe == NULL) { + rtio_sqe_drop_all(ctx->r); + spi_r_unlock(ctx); + return ret; + } + + ctx->async_cb = cb; + + rtio_sqe_prep_callback_no_cqe(sqe, + transceive_cb_callback, + ctx, + userdata); + + rtio_submit(ctx->r, 0); + + return 0; +} +#endif /* CONFIG_SPI_ASYNC */ + +int spi_rtio_release(const struct device *dev, const struct spi_config *config) +{ + ARG_UNUSED(dev); + ARG_UNUSED(config); + return -ENOTSUP; +} diff --git a/include/zephyr/drivers/spi/rtio.h b/drivers/spi/spi_rtio.h similarity index 75% rename from include/zephyr/drivers/spi/rtio.h rename to drivers/spi/spi_rtio.h index 04d5bad8e691..1bab4d253c3f 100644 --- a/include/zephyr/drivers/spi/rtio.h +++ b/drivers/spi/spi_rtio.h @@ -21,11 +21,16 @@ extern "C" { struct spi_rtio { struct k_spinlock lock; struct rtio *r; + /** Thread lock for rtio instance */ + struct k_sem r_lock; struct mpsc io_q; struct rtio_iodev iodev; struct rtio_iodev_sqe *txn_head; struct rtio_iodev_sqe *txn_curr; struct spi_dt_spec dt_spec; +#if CONFIG_SPI_ASYNC + spi_callback_t async_cb; +#endif /* CONFIG_SPI_ASYNC */ }; /** @@ -48,6 +53,7 @@ struct spi_rtio { * @param[in] iodev iodev to transceive with * @param[in] tx_bufs transmit buffer set * @param[in] rx_bufs receive buffer set + * @param[in] no_response do not generate any CQEs, useful if last SQE is a callback * @param[out] last_sqe last sqe submitted, NULL if not enough memory * * @retval Number of submission queue entries @@ -57,6 +63,7 @@ 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, + bool no_response, struct rtio_sqe **last_sqe); /** @@ -98,6 +105,28 @@ int spi_rtio_transceive(struct spi_rtio *ctx, const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs); +/** + * @brief Perform a SPI Transfer (transceive) and call back once done + * + * Provides a compatible API for the existing spi_transceive_cb API calling + * the caller once the operation is complete. + * For details see @ref spi_transceive_cb. + */ +int spi_rtio_transceive_cb(struct spi_rtio *ctx, + 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); + +/** + * @brief Stub spi_release implementation + * + * Provides a stub implementation of the spi_release API returning -ENOTSUP as SPI_LOCKED + * is not supported with RTIO. RTIO transactions with RTIO_OP_AWAIT can be used instead. + */ +int spi_rtio_release(const struct device *dev, const struct spi_config *config); + /** * @brief Fallback SPI RTIO submit implementation. * diff --git a/drivers/spi/spi_rv32m1_lpspi.c b/drivers/spi/spi_rv32m1_lpspi.c index 16f5d52e7a4f..b80cec2ac790 100644 --- a/drivers/spi/spi_rv32m1_lpspi.c +++ b/drivers/spi/spi_rv32m1_lpspi.c @@ -10,7 +10,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_sam.c b/drivers/spi/spi_sam.c index 68e726bef63b..12d2ce68df11 100644 --- a/drivers/spi/spi_sam.c +++ b/drivers/spi/spi_sam.c @@ -17,7 +17,7 @@ LOG_MODULE_REGISTER(spi_sam); #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_sam0.c b/drivers/spi/spi_sam0.c index 5c48c0bbc99f..594b756e1705 100644 --- a/drivers/spi/spi_sam0.c +++ b/drivers/spi/spi_sam0.c @@ -16,7 +16,7 @@ LOG_MODULE_REGISTER(spi_sam0); #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_sedi.c b/drivers/spi/spi_sedi.c index 9f6a9bc28940..dd7190a4238c 100644 --- a/drivers/spi/spi_sedi.c +++ b/drivers/spi/spi_sedi.c @@ -8,7 +8,7 @@ #include #include -#include +#include "spi_rtio.h" #include #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL diff --git a/drivers/spi/spi_sifive.h b/drivers/spi/spi_sifive.h index 255f76c95fe8..4c4bcf418457 100644 --- a/drivers/spi/spi_sifive.h +++ b/drivers/spi/spi_sifive.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include #define SPI_CFG(dev) ((struct spi_sifive_cfg *) ((dev)->config)) diff --git a/drivers/spi/spi_silabs_eusart.c b/drivers/spi/spi_silabs_eusart.c index 49963776247f..1342f34c7ae3 100644 --- a/drivers/spi/spi_silabs_eusart.c +++ b/drivers/spi/spi_silabs_eusart.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_silabs_siwx91x_gspi.c b/drivers/spi/spi_silabs_siwx91x_gspi.c index 3ecea64e3b38..bfebd7b1b19a 100644 --- a/drivers/spi/spi_silabs_siwx91x_gspi.c +++ b/drivers/spi/spi_silabs_siwx91x_gspi.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_silabs_usart.c b/drivers/spi/spi_silabs_usart.c index 9e167c8e217e..40da9ae6f7f4 100644 --- a/drivers/spi/spi_silabs_usart.c +++ b/drivers/spi/spi_silabs_usart.c @@ -14,7 +14,7 @@ LOG_MODULE_REGISTER(spi_silabs_usart); #include #include #include -#include +#include "spi_rtio.h" #include #include "em_cmu.h" diff --git a/drivers/spi/spi_smartbond.c b/drivers/spi/spi_smartbond.c index 6a54eb6eec2b..fad6ab825e39 100644 --- a/drivers/spi/spi_smartbond.c +++ b/drivers/spi/spi_smartbond.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(spi_smartbond); #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_stm32.c b/drivers/spi/spi_stm32.c index 5f134e6c681c..f3fdf1c6eb24 100644 --- a/drivers/spi/spi_stm32.c +++ b/drivers/spi/spi_stm32.c @@ -16,7 +16,7 @@ LOG_MODULE_REGISTER(spi_stm32); #include #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_test.c b/drivers/spi/spi_test.c index 081aa6bfbf5e..a5c5eb8b36de 100644 --- a/drivers/spi/spi_test.c +++ b/drivers/spi/spi_test.c @@ -11,7 +11,7 @@ #include #include -#include +#include "spi_rtio.h" #define DT_DRV_COMPAT vnd_spi diff --git a/drivers/spi/spi_wch.c b/drivers/spi/spi_wch.c index 023b87e6af0c..80a0a63eb116 100644 --- a/drivers/spi/spi_wch.c +++ b/drivers/spi/spi_wch.c @@ -13,7 +13,7 @@ LOG_MODULE_REGISTER(spi_wch); #include #include #include -#include +#include "spi_rtio.h" #include #include diff --git a/drivers/spi/spi_xec_qmspi.c b/drivers/spi/spi_xec_qmspi.c index c552377ba1c2..11f5ef8e87d0 100644 --- a/drivers/spi/spi_xec_qmspi.c +++ b/drivers/spi/spi_xec_qmspi.c @@ -13,7 +13,7 @@ LOG_MODULE_REGISTER(spi_xec, CONFIG_SPI_LOG_LEVEL); #include #include #include -#include +#include "spi_rtio.h" #include #include diff --git a/drivers/spi/spi_xlnx_axi_quadspi.c b/drivers/spi/spi_xlnx_axi_quadspi.c index 33918728e8ca..ed71924ae7a4 100644 --- a/drivers/spi/spi_xlnx_axi_quadspi.c +++ b/drivers/spi/spi_xlnx_axi_quadspi.c @@ -8,7 +8,7 @@ #include #include -#include +#include "spi_rtio.h" #include #include #include diff --git a/drivers/spi/spi_xmc4xxx.c b/drivers/spi/spi_xmc4xxx.c index fc4cf86f60a5..0d68e73a7e12 100644 --- a/drivers/spi/spi_xmc4xxx.c +++ b/drivers/spi/spi_xmc4xxx.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(spi_xmc4xxx); #include #include #include -#include +#include "spi_rtio.h" #include #include diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay index ec14118e6b37..37526d41b315 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15_dvk_nrf54l15_cpuapp.overlay @@ -59,7 +59,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay index ec14118e6b37..37526d41b315 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/bl54l15u_dvk_nrf54l15_cpuapp.overlay @@ -59,7 +59,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/nrf54l15dk_nrf54l15_cpuapp.overlay index cf83ac20a270..e26a549a97a1 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/nrf54l15dk_nrf54l15_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -58,7 +58,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay index 0c782e8ff9cc..5003d8e3e701 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay @@ -65,7 +65,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20b_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20b_cpuapp.overlay index 56d1cf573d00..23cc3e1dc38d 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20b_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/nrf54lm20dk_nrf54lm20b_cpuapp.overlay @@ -65,7 +65,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/nrf7120dk_nrf7120_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/nrf7120dk_nrf7120_cpuapp.overlay index 8e9d6ce4c265..264bd06c587a 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/nrf7120dk_nrf7120_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/nrf7120dk_nrf7120_cpuapp.overlay @@ -58,7 +58,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_controller_peripheral/boards/ophelia4ev_nrf54l15_cpuapp.overlay b/tests/drivers/spi/spi_controller_peripheral/boards/ophelia4ev_nrf54l15_cpuapp.overlay index 5a6b6928e056..977d9de5154b 100644 --- a/tests/drivers/spi/spi_controller_peripheral/boards/ophelia4ev_nrf54l15_cpuapp.overlay +++ b/tests/drivers/spi/spi_controller_peripheral/boards/ophelia4ev_nrf54l15_cpuapp.overlay @@ -58,7 +58,7 @@ dut_spi_dt: test-spi-dev@0 { compatible = "vnd,spi-device"; reg = <0>; - spi-max-frequency = ; + spi-max-frequency = ; }; }; diff --git a/tests/drivers/spi/spi_loopback/src/spi.c b/tests/drivers/spi/spi_loopback/src/spi.c index 5f3598ea89d0..5a4c97bc58c8 100644 --- a/tests/drivers/spi/spi_loopback/src/spi.c +++ b/tests/drivers/spi/spi_loopback/src/spi.c @@ -676,7 +676,10 @@ ZTEST(spi_loopback, test_spi_write_back) /* similar to test_spi_write_back, simulates the real common case of 1 word command */ ZTEST(spi_loopback, test_spi_same_buf_cmd) { - if (IS_ENABLED(CONFIG_SPI_STM32_DMA) || IS_ENABLED(CONFIG_DSPI_MCUX_EDMA)) { + if (IS_ENABLED(CONFIG_SPI_STM32_DMA) || + IS_ENABLED(CONFIG_DSPI_MCUX_EDMA) || + IS_ENABLED(CONFIG_SPI_NRFX_SPIM) || + IS_ENABLED(CONFIG_SPI_NRFX_SPIM_RTIO)) { ztest_test_skip(); } diff --git a/tests/drivers/spi/spi_loopback/testcase.yaml b/tests/drivers/spi/spi_loopback/testcase.yaml index 5dcf4c651f1c..25cd348b520b 100644 --- a/tests/drivers/spi/spi_loopback/testcase.yaml +++ b/tests/drivers/spi/spi_loopback/testcase.yaml @@ -36,6 +36,8 @@ tests: - mimxrt1170_evk/mimxrt1176/cm7 - bg29_rb4420a - xg29_rb4412a + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp integration_platforms: - robokit1 drivers.spi.mcux_dspi_dma.loopback: @@ -284,7 +286,7 @@ tests: fixture: spi_loopback type: one_line regex: - - "Failed to initialize nrfx driver" + - "Failed to configure nrfx driver" drivers.spi.nrf54h_fast_8mhz: extra_args: DTC_OVERLAY_FILE="boards/nrf54h20dk_nrf54h20_cpuapp_fast.overlay" platform_allow: @@ -317,7 +319,7 @@ tests: fixture: spi_loopback type: one_line regex: - - "Failed to initialize nrfx driver" + - "Failed to configure nrfx driver" drivers.spi.nrf54l_8mhz: extra_args: EXTRA_DTC_OVERLAY_FILE="boards/nrf_at_8mhz.overlay" platform_allow: @@ -330,13 +332,11 @@ tests: extra_args: EXTRA_DTC_OVERLAY_FILE="boards/nrf_at_16mhz.overlay" platform_allow: - nrf54l15dk/nrf54l15/cpuapp - - nrf7120dk/nrf7120/cpuapp - ophelia4ev/nrf54l15/cpuapp drivers.spi.nrf54l_32mhz: extra_args: EXTRA_DTC_OVERLAY_FILE="boards/nrf_at_32mhz.overlay" platform_allow: - nrf54l15dk/nrf54l15/cpuapp - - nrf7120dk/nrf7120/cpuapp - ophelia4ev/nrf54l15/cpuapp drivers.spi.nrf54lm20_16mhz_32mhz: harness: ztest