diff --git a/drivers/spi/Kconfig.cc23x0 b/drivers/spi/Kconfig.cc23x0 index 83747b56343..6250f36f733 100644 --- a/drivers/spi/Kconfig.cc23x0 +++ b/drivers/spi/Kconfig.cc23x0 @@ -10,3 +10,12 @@ config SPI_CC23X0 select PINCTRL help Enable support for the TI SimpleLink CC23x0 SPI peripheral + +config SPI_CC23X0_DMA_DRIVEN + bool "DMA support for TI CC23X0 SPI devices" + depends on SPI_CC23X0 + select DMA + help + DMA driven transactions for the SPI. + DMA driven mode offloads data transfer tasks from the CPU + and requires fewer interrupts to handle the SPI transfers. diff --git a/drivers/spi/spi_cc23x0.c b/drivers/spi/spi_cc23x0.c index 3e391b3086b..ac8efc7bdaa 100644 --- a/drivers/spi/spi_cc23x0.c +++ b/drivers/spi/spi_cc23x0.c @@ -10,6 +10,7 @@ LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL); #include +#include #include #include #include @@ -18,6 +19,8 @@ LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL); #include #include +#include + #include "spi_context.h" /* @@ -30,20 +33,34 @@ LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL); #define SPI_CC23_DATA_WIDTH 8 #define SPI_CC23_DFS (SPI_CC23_DATA_WIDTH >> 3) +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN +#define SPI_CC23_REG_GET(base, offset) ((base) + (offset)) +#define SPI_CC23_INT_MASK SPI_DMA_DONE_RX +#else #define SPI_CC23_INT_MASK (SPI_TXEMPTY | SPI_IDLE | SPI_RX) +#endif struct spi_cc23x0_config { uint32_t base; const struct pinctrl_dev_config *pincfg; void (*irq_config_func)(void); +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN + const struct device *dma_dev; + uint8_t dma_channel_tx; + uint8_t dma_trigsrc_tx; + uint8_t dma_channel_rx; + uint8_t dma_trigsrc_rx; +#endif }; struct spi_cc23x0_data { struct spi_context ctx; size_t tx_len_left; +#ifndef CONFIG_SPI_CC23X0_DMA_DRIVEN uint32_t tx_count; uint32_t rx_count; bool xfer_done; +#endif }; static void spi_cc23x0_isr(const struct device *dev) @@ -51,12 +68,20 @@ static void spi_cc23x0_isr(const struct device *dev) const struct spi_cc23x0_config *cfg = dev->config; struct spi_cc23x0_data *data = dev->data; struct spi_context *ctx = &data->ctx; - uint32_t txd = 0, rxd; uint32_t status; +#ifndef CONFIG_SPI_CC23X0_DMA_DRIVEN + uint32_t txd = 0, rxd; +#endif status = SPIIntStatus(cfg->base, true); LOG_DBG("status = %08x", status); +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN + if (status & SPI_DMA_DONE_RX) { + SPIClearInt(cfg->base, SPI_DMA_DONE_RX); + spi_context_complete(ctx, dev, 0); + } +#else /* * Disabling the interrupts in this ISR when SPI has completed * the transfer triggers a subsequent spurious interrupt, with @@ -124,6 +149,7 @@ static void spi_cc23x0_isr(const struct device *dev) SPIDisableInt(cfg->base, SPI_CC23_INT_MASK); spi_context_complete(ctx, dev, 0); } +#endif } static int spi_cc23x0_configure(const struct device *dev, @@ -221,9 +247,11 @@ static void spi_cc23x0_initialize_data(struct spi_cc23x0_data *data) { data->tx_len_left = MAX(spi_context_total_tx_len(&data->ctx), spi_context_total_rx_len(&data->ctx)); +#ifndef CONFIG_SPI_CC23X0_DMA_DRIVEN data->tx_count = 0; data->rx_count = 0; data->xfer_done = false; +#endif } static int spi_cc23x0_transceive(const struct device *dev, @@ -235,6 +263,43 @@ static int spi_cc23x0_transceive(const struct device *dev, struct spi_cc23x0_data *data = dev->data; struct spi_context *ctx = &data->ctx; int ret; +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN + struct dma_block_config block_cfg_tx = { + .dest_address = SPI_CC23_REG_GET(cfg->base, SPI_O_TXDATA), + .dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE, + .block_size = 1, + }; + + struct dma_config dma_cfg_tx = { + .dma_slot = cfg->dma_trigsrc_tx, + .channel_direction = MEMORY_TO_PERIPHERAL, + .block_count = 1, + .head_block = &block_cfg_tx, + .source_data_size = SPI_CC23_DFS, + .dest_data_size = SPI_CC23_DFS, + .source_burst_length = SPI_CC23_DFS, + .dma_callback = NULL, + .user_data = NULL, + }; + + struct dma_block_config block_cfg_rx = { + .source_address = SPI_CC23_REG_GET(cfg->base, SPI_O_RXDATA), + .source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE, + .block_size = 1, + }; + + struct dma_config dma_cfg_rx = { + .dma_slot = cfg->dma_trigsrc_rx, + .channel_direction = PERIPHERAL_TO_MEMORY, + .block_count = 1, + .head_block = &block_cfg_rx, + .source_data_size = SPI_CC23_DFS, + .dest_data_size = SPI_CC23_DFS, + .source_burst_length = SPI_CC23_DFS, + .dma_callback = NULL, + .user_data = NULL, + }; +#endif spi_context_lock(ctx, false, NULL, NULL, config); @@ -245,12 +310,65 @@ static int spi_cc23x0_transceive(const struct device *dev, spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, SPI_CC23_DFS); +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN + if (spi_context_total_tx_len(ctx) != spi_context_total_rx_len(ctx)) { + LOG_ERR("In DMA mode, RX and TX buffer lengths must be the same"); + ret = -EINVAL; + goto ctx_release; + } +#endif + spi_cc23x0_initialize_data(data); spi_context_cs_control(ctx, true); SPIEnableInt(cfg->base, SPI_CC23_INT_MASK); +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN + block_cfg_tx.source_address = (uint32_t)ctx->tx_buf; + block_cfg_tx.source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + block_cfg_tx.block_size = SPI_CC23_DFS * data->tx_len_left; + + block_cfg_rx.dest_address = (uint32_t)ctx->rx_buf; + block_cfg_rx.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + block_cfg_rx.block_size = SPI_CC23_DFS * data->tx_len_left; + + ret = dma_config(cfg->dma_dev, cfg->dma_channel_tx, &dma_cfg_tx); + if (ret) { + LOG_ERR("Failed to configure DMA TX channel"); + goto int_disable; + } + + ret = dma_config(cfg->dma_dev, cfg->dma_channel_rx, &dma_cfg_rx); + if (ret) { + LOG_ERR("Failed to configure DMA RX channel"); + goto int_disable; + } + + /* Disable DMA triggers */ + SPIDisableDMA(cfg->base, SPI_DMA_TX | SPI_DMA_RX); + + /* Start DMA channels */ + dma_start(cfg->dma_dev, cfg->dma_channel_rx); + dma_start(cfg->dma_dev, cfg->dma_channel_tx); + + /* Enable DMA triggers to start transfer */ + SPIEnableDMA(cfg->base, SPI_DMA_TX | SPI_DMA_RX); + + ret = spi_context_wait_for_completion(&data->ctx); + if (ret) { + LOG_ERR("SPI transfer failed (%d)", ret); + goto int_disable; + } + + spi_context_update_tx(ctx, SPI_CC23_DFS, data->tx_len_left); + spi_context_update_rx(ctx, SPI_CC23_DFS, data->tx_len_left); + + LOG_DBG("SPI transfer completed"); + +int_disable: + SPIDisableInt(cfg->base, SPI_CC23_INT_MASK); +#else ret = spi_context_wait_for_completion(ctx); if (ret) { SPIDisableInt(cfg->base, SPI_CC23_INT_MASK); @@ -258,6 +376,7 @@ static int spi_cc23x0_transceive(const struct device *dev, } else { LOG_DBG("SPI transfer completed"); } +#endif spi_context_cs_control(ctx, false); @@ -304,6 +423,13 @@ static int spi_cc23x0_init(const struct device *dev) return ret; } +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN + if (!device_is_ready(cfg->dma_dev)) { + LOG_ERR("DMA not ready"); + return -ENODEV; + } +#endif + ret = spi_context_cs_configure_all(&data->ctx); if (ret) { return ret; @@ -314,6 +440,17 @@ static int spi_cc23x0_init(const struct device *dev) return 0; } +#ifdef CONFIG_SPI_CC23X0_DMA_DRIVEN +#define SPI_CC23X0_DMA_INIT(n) \ + .dma_dev = DEVICE_DT_GET(TI_CC23X0_DT_INST_DMA_CTLR(n, tx)), \ + .dma_channel_tx = TI_CC23X0_DT_INST_DMA_CHANNEL(n, tx), \ + .dma_trigsrc_tx = TI_CC23X0_DT_INST_DMA_TRIGSRC(n, tx), \ + .dma_channel_rx = TI_CC23X0_DT_INST_DMA_CHANNEL(n, rx), \ + .dma_trigsrc_rx = TI_CC23X0_DT_INST_DMA_TRIGSRC(n, rx), +#else +#define SPI_CC23X0_DMA_INIT(n) +#endif + #define SPI_CC23X0_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ \ @@ -329,7 +466,8 @@ static int spi_cc23x0_init(const struct device *dev) static const struct spi_cc23x0_config spi_cc23x0_config_##n = { \ .base = DT_INST_REG_ADDR(n), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ - .irq_config_func = spi_irq_config_func_##n \ + .irq_config_func = spi_irq_config_func_##n, \ + SPI_CC23X0_DMA_INIT(n) \ }; \ \ static struct spi_cc23x0_data spi_cc23x0_data_##n = { \ diff --git a/dts/bindings/spi/ti,cc23x0-spi.yaml b/dts/bindings/spi/ti,cc23x0-spi.yaml index c58d46901f2..54bda179280 100644 --- a/dts/bindings/spi/ti,cc23x0-spi.yaml +++ b/dts/bindings/spi/ti,cc23x0-spi.yaml @@ -19,3 +19,20 @@ properties: pinctrl-names: required: true + + dmas: + description: | + Optional TX & RX DMA specifiers. Each specifier will have a phandle + reference to the DMA controller, the channel number, and the peripheral + trigger source. + + Example for channels 0/1 with spi0txtrg/spi0rxtrg trigger sources: + dmas = <&dma 0 0>, <&dma 1 1>; + + dma-names: + description: | + Required if the dmas property exists. This should be "tx" and "rx" + to match the dmas property. + + Example: + dma-names = "tx", "rx";