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>
This commit is contained in:
parent
410afbaf19
commit
11cbfeafe6
@ -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.
|
||||
|
||||
@ -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);
|
||||
#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);
|
||||
#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 = { \
|
||||
|
||||
@ -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…
Reference in New Issue
Block a user