Browse Source

drivers: spi: cc23x0: Add support for DMA mode

Two DMA channels are assigned to TX and RX respectively:
- A TX DMA single request is asserted when there is space in the FIFO.
- A RX DMA single request is asserted when data is in the FIFO.

Signed-off-by: Julien Panis <jpanis@baylibre.com>
pull/74835/merge
Julien Panis 10 months ago committed by Benjamin Cabé
parent
commit
11cbfeafe6
  1. 9
      drivers/spi/Kconfig.cc23x0
  2. 142
      drivers/spi/spi_cc23x0.c
  3. 17
      dts/bindings/spi/ti,cc23x0-spi.yaml

9
drivers/spi/Kconfig.cc23x0

@ -10,3 +10,12 @@ config SPI_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.

142
drivers/spi/spi_cc23x0.c

@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL);
#include <zephyr/device.h>
#include <zephyr/drivers/dma.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/irq.h>
@ -18,6 +19,8 @@ LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL); @@ -18,6 +19,8 @@ LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL);
#include <driverlib/clkctl.h>
#include <driverlib/spi.h>
#include <inc/hw_memmap.h>
#include "spi_context.h"
/*
@ -30,20 +33,34 @@ LOG_MODULE_REGISTER(spi_cc23x0, CONFIG_SPI_LOG_LEVEL); @@ -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) @@ -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) @@ -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) @@ -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, @@ -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, @@ -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, @@ -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) @@ -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) @@ -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) @@ -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 = { \

17
dts/bindings/spi/ti,cc23x0-spi.yaml

@ -19,3 +19,20 @@ properties: @@ -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";

Loading…
Cancel
Save