driver: spi: MAX32 add RTIO support plus refactor

Implements the SPIO RTIO API. Refactors internal transcive
fucntios to work with both existing SPI API and RTIO functions.

When SPI_RTIO is enabled the spi_transcieve call translates
the request into an RTIO transaction placed in the queue
that device will execute.

Include the latest refacor changes of RTIO.

Signed-off-by: Dimitrije Lilic <dimitrije.lilic@orioninc.com>
This commit is contained in:
Dimitrije Lilic 2024-09-12 15:44:22 +02:00 committed by Henrik Brix Andersen
parent e78811bcd6
commit 15ec1f2016
2 changed files with 198 additions and 7 deletions

View file

@ -23,4 +23,28 @@ config SPI_MAX32_DMA
help help
Enable DMA support for MAX32 MCU SPI driver. Enable DMA support for MAX32 MCU SPI driver.
config SPI_MAX32_RTIO
bool "MAX32 SPI RTIO Support"
default y if SPI_RTIO
depends on !SPI_ASYNC
select SPI_MAX32_INTERRUPT
if SPI_MAX32_RTIO
config SPI_MAX32_RTIO_SQ_SIZE
int "Number of available submission queue entries"
default 8 # Sensible default that covers most common spi transactions
help
When RTIO is used with SPI, each driver holds a context with which blocking
API calls use to perform SPI transactions. This queue needs to be as deep
as the longest set of spi_buf_sets used, where normal SPI operations are
used (equal length buffers). It may need to be slightly deeper where the
spi buffer sets for transmit/receive are not always matched equally in
length as these are transformed into normal transceives.
config SPI_MAX32_RTIO_CQ_SIZE
int "Number of available completion queue entries"
default 8 # Sensible default that covers most common spi transactions
endif # SPI_MAX32_RTIO
endif # SPI_MAX32 endif # SPI_MAX32

View file

@ -17,6 +17,10 @@
#include <zephyr/drivers/clock_control/adi_max32_clock_control.h> #include <zephyr/drivers/clock_control/adi_max32_clock_control.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/irq.h> #include <zephyr/irq.h>
#include <zephyr/rtio/rtio.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/spi/rtio.h>
#include <wrap_max32_spi.h> #include <wrap_max32_spi.h>
@ -51,12 +55,18 @@ struct max32_spi_data {
const struct device *dev; const struct device *dev;
mxc_spi_req_t req; mxc_spi_req_t req;
uint8_t dummy[2]; uint8_t dummy[2];
#ifdef CONFIG_SPI_MAX32_DMA #ifdef CONFIG_SPI_MAX32_DMA
volatile uint8_t dma_stat; volatile uint8_t dma_stat;
#endif /* CONFIG_SPI_MAX32_DMA */ #endif /* CONFIG_SPI_MAX32_DMA */
#ifdef CONFIG_SPI_ASYNC #ifdef CONFIG_SPI_ASYNC
struct k_work async_work; struct k_work async_work;
#endif /* CONFIG_SPI_ASYNC */ #endif /* CONFIG_SPI_ASYNC */
#ifdef CONFIG_SPI_RTIO
struct spi_rtio *rtio_ctx;
#endif
}; };
#ifdef CONFIG_SPI_MAX32_DMA #ifdef CONFIG_SPI_MAX32_DMA
@ -234,6 +244,10 @@ static int spi_max32_transceive(const struct device *dev)
const struct max32_spi_config *cfg = dev->config; const struct max32_spi_config *cfg = dev->config;
struct max32_spi_data *data = dev->data; struct max32_spi_data *data = dev->data;
struct spi_context *ctx = &data->ctx; struct spi_context *ctx = &data->ctx;
#ifdef CONFIG_SPI_RTIO
struct spi_rtio *rtio_ctx = data->rtio_ctx;
struct rtio_sqe *sqe = &rtio_ctx->txn_curr->sqe;
#endif
uint32_t len; uint32_t len;
uint8_t dfs_shift; uint8_t dfs_shift;
@ -242,12 +256,48 @@ static int spi_max32_transceive(const struct device *dev)
dfs_shift = spi_max32_get_dfs_shift(ctx); dfs_shift = spi_max32_get_dfs_shift(ctx);
len = spi_context_max_continuous_chunk(ctx); len = spi_context_max_continuous_chunk(ctx);
#ifdef CONFIG_SPI_RTIO
switch (sqe->op) {
case RTIO_OP_RX:
len = sqe->rx.buf_len;
data->req.rxData = sqe->rx.buf;
data->req.rxLen = sqe->rx.buf_len;
data->req.txData = NULL;
data->req.txLen = len >> dfs_shift;
break;
case RTIO_OP_TX:
len = sqe->tx.buf_len;
data->req.rxLen = 0;
data->req.rxData = data->dummy;
data->req.txData = (uint8_t *)sqe->tx.buf;
data->req.txLen = len >> dfs_shift;
break;
case RTIO_OP_TINY_TX:
len = sqe->tiny_tx.buf_len;
data->req.txData = (uint8_t *)sqe->tiny_tx.buf;
data->req.rxData = data->dummy;
data->req.txLen = len >> dfs_shift;
data->req.rxLen = 0;
break;
case RTIO_OP_TXRX:
len = sqe->txrx.buf_len;
data->req.txData = (uint8_t *)sqe->txrx.tx_buf;
data->req.rxData = sqe->txrx.rx_buf;
data->req.txLen = len >> dfs_shift;
data->req.rxLen = len >> dfs_shift;
break;
default:
break;
}
#else
data->req.txLen = len >> dfs_shift; data->req.txLen = len >> dfs_shift;
data->req.txData = (uint8_t *)ctx->tx_buf; data->req.txData = (uint8_t *)ctx->tx_buf;
data->req.rxLen = len >> dfs_shift; data->req.rxLen = len >> dfs_shift;
data->req.rxData = ctx->rx_buf; data->req.rxData = ctx->rx_buf;
data->req.rxData = ctx->rx_buf; data->req.rxData = ctx->rx_buf;
data->req.rxLen = len >> dfs_shift; data->req.rxLen = len >> dfs_shift;
if (!data->req.rxData) { if (!data->req.rxData) {
/* Pass a dummy buffer to HAL if receive buffer is NULL, otherwise /* Pass a dummy buffer to HAL if receive buffer is NULL, otherwise
@ -256,6 +306,7 @@ static int spi_max32_transceive(const struct device *dev)
data->req.rxData = data->dummy; data->req.rxData = data->dummy;
data->req.rxLen = 0; data->req.rxLen = 0;
} }
#endif
data->req.spi = cfg->regs; data->req.spi = cfg->regs;
data->req.ssIdx = ctx->config->slave; data->req.ssIdx = ctx->config->slave;
data->req.ssDeassert = 0; data->req.ssDeassert = 0;
@ -296,10 +347,12 @@ static int transceive(const struct device *dev, const struct spi_config *config,
bool async, spi_callback_t cb, void *userdata) bool async, spi_callback_t cb, void *userdata)
{ {
int ret = 0; int ret = 0;
const struct max32_spi_config *cfg = dev->config;
struct max32_spi_data *data = dev->data; struct max32_spi_data *data = dev->data;
struct spi_context *ctx = &data->ctx; struct spi_context *ctx = &data->ctx;
#ifndef CONFIG_SPI_RTIO
const struct max32_spi_config *cfg = dev->config;
bool hw_cs_ctrl = true; bool hw_cs_ctrl = true;
#endif
#ifndef CONFIG_SPI_MAX32_INTERRUPT #ifndef CONFIG_SPI_MAX32_INTERRUPT
if (async) { if (async) {
@ -309,6 +362,7 @@ static int transceive(const struct device *dev, const struct spi_config *config,
spi_context_lock(ctx, async, cb, userdata, config); spi_context_lock(ctx, async, cb, userdata, config);
#ifndef CONFIG_SPI_RTIO
ret = spi_configure(dev, config); ret = spi_configure(dev, config);
if (ret != 0) { if (ret != 0) {
spi_context_release(ctx, ret); spi_context_release(ctx, ret);
@ -363,9 +417,12 @@ static int transceive(const struct device *dev, const struct spi_config *config,
cfg->regs->ctrl0 |= MXC_F_SPI_CTRL0_EN; cfg->regs->ctrl0 |= MXC_F_SPI_CTRL0_EN;
} }
} }
#else
struct spi_rtio *rtio_ctx = data->rtio_ctx;
ret = spi_rtio_transceive(rtio_ctx, config, tx_bufs, rx_bufs);
#endif
spi_context_release(ctx, ret); spi_context_release(ctx, ret);
return ret; return ret;
} }
@ -565,6 +622,99 @@ unlock:
} }
#endif /* CONFIG_SPI_MAX32_DMA */ #endif /* CONFIG_SPI_MAX32_DMA */
#ifdef CONFIG_SPI_RTIO
static void spi_max32_iodev_complete(const struct device *dev, int status);
static void spi_max32_iodev_start(const struct device *dev)
{
struct max32_spi_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;
struct rtio_sqe *sqe = &rtio_ctx->txn_curr->sqe;
int ret = 0;
switch (sqe->op) {
case RTIO_OP_RX:
case RTIO_OP_TX:
case RTIO_OP_TINY_TX:
case RTIO_OP_TXRX:
ret = spi_max32_transceive(dev);
break;
default:
spi_max32_iodev_complete(dev, -EINVAL);
break;
}
if (ret != 0) {
spi_max32_iodev_complete(dev, -EIO);
}
}
static inline void spi_max32_iodev_prepare_start(const struct device *dev)
{
struct max32_spi_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;
struct spi_dt_spec *spi_dt_spec = rtio_ctx->txn_curr->sqe.iodev->data;
struct spi_config *spi_config = &spi_dt_spec->config;
struct max32_spi_config *cfg = (struct max32_spi_config *)dev->config;
int ret;
bool hw_cs_ctrl = true;
ret = spi_configure(dev, spi_config);
__ASSERT(!ret, "%d", ret);
/* Check if CS GPIO exists */
if (spi_cs_is_gpio(spi_config)) {
hw_cs_ctrl = false;
}
MXC_SPI_HWSSControl(cfg->regs, hw_cs_ctrl);
/* Assert the CS line if HW control disabled */
if (!hw_cs_ctrl) {
spi_context_cs_control(&data->ctx, true);
} else {
cfg->regs->ctrl0 = (cfg->regs->ctrl0 & ~MXC_F_SPI_CTRL0_START) |
MXC_F_SPI_CTRL0_SS_CTRL;
};
}
static void spi_max32_iodev_complete(const struct device *dev, int status)
{
struct max32_spi_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;
if (!status && rtio_ctx->txn_curr->sqe.flags & RTIO_SQE_TRANSACTION) {
rtio_ctx->txn_curr = rtio_txn_next(rtio_ctx->txn_curr);
spi_max32_iodev_start(dev);
} else {
struct max32_spi_config *cfg = (struct max32_spi_config *)dev->config;
bool hw_cs_ctrl = true;
if (!hw_cs_ctrl) {
spi_context_cs_control(&data->ctx, false);
} else {
cfg->regs->ctrl0 &= ~(MXC_F_SPI_CTRL0_START | MXC_F_SPI_CTRL0_SS_CTRL |
MXC_F_SPI_CTRL0_EN);
cfg->regs->ctrl0 |= MXC_F_SPI_CTRL0_EN;
}
if (spi_rtio_complete(rtio_ctx, status)) {
spi_max32_iodev_prepare_start(dev);
spi_max32_iodev_start(dev);
}
}
}
static void api_iodev_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
struct max32_spi_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;
if (spi_rtio_submit(rtio_ctx, iodev_sqe)) {
spi_max32_iodev_prepare_start(dev);
spi_max32_iodev_start(dev);
}
}
#endif
static int api_transceive(const struct device *dev, const struct spi_config *config, static int api_transceive(const struct device *dev, const struct spi_config *config,
const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs) const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
{ {
@ -596,6 +746,13 @@ static void spi_max32_callback(mxc_spi_req_t *req, int error)
const struct device *dev = data->dev; const struct device *dev = data->dev;
uint32_t len; uint32_t len;
#ifdef CONFIG_SPI_RTIO
struct spi_rtio *rtio_ctx = data->rtio_ctx;
if (rtio_ctx->txn_head != NULL) {
spi_max32_iodev_complete(data->dev, 0);
}
#endif
len = spi_context_max_continuous_chunk(ctx); len = spi_context_max_continuous_chunk(ctx);
spi_context_update_tx(ctx, 1, len); spi_context_update_tx(ctx, 1, len);
spi_context_update_rx(ctx, 1, len); spi_context_update_rx(ctx, 1, len);
@ -685,12 +842,12 @@ static int api_release(const struct device *dev, const struct spi_config *config
{ {
struct max32_spi_data *data = dev->data; struct max32_spi_data *data = dev->data;
#ifndef CONFIG_SPI_RTIO
if (!spi_context_configured(&data->ctx, config)) { if (!spi_context_configured(&data->ctx, config)) {
return -EINVAL; return -EINVAL;
} }
#endif
spi_context_unlock_unconditionally(&data->ctx); spi_context_unlock_unconditionally(&data->ctx);
return 0; return 0;
} }
@ -724,6 +881,10 @@ static int spi_max32_init(const struct device *dev)
data->dev = dev; data->dev = dev;
#ifdef CONFIG_SPI_RTIO
spi_rtio_init(data->rtio_ctx, dev);
#endif
#ifdef CONFIG_SPI_MAX32_INTERRUPT #ifdef CONFIG_SPI_MAX32_INTERRUPT
cfg->irq_config_func(dev); cfg->irq_config_func(dev);
#ifdef CONFIG_SPI_ASYNC #ifdef CONFIG_SPI_ASYNC
@ -743,8 +904,8 @@ static const struct spi_driver_api spi_max32_api = {
.transceive_async = api_transceive_async, .transceive_async = api_transceive_async,
#endif /* CONFIG_SPI_ASYNC */ #endif /* CONFIG_SPI_ASYNC */
#ifdef CONFIG_SPI_RTIO #ifdef CONFIG_SPI_RTIO
.iodev_submit = spi_rtio_iodev_default_submit, .iodev_submit = api_iodev_submit,
#endif #endif /* CONFIG_SPI_RTIO */
.release = api_release, .release = api_release,
}; };
@ -784,9 +945,14 @@ static const struct spi_driver_api spi_max32_api = {
#define MAX32_SPI_DMA_INIT(n) #define MAX32_SPI_DMA_INIT(n)
#endif #endif
#define DEFINE_SPI_MAX32_RTIO(_num) SPI_RTIO_DEFINE(max32_spi_rtio_##_num, \
CONFIG_SPI_MAX32_RTIO_SQ_SIZE, \
CONFIG_SPI_MAX32_RTIO_CQ_SIZE)
#define DEFINE_SPI_MAX32(_num) \ #define DEFINE_SPI_MAX32(_num) \
PINCTRL_DT_INST_DEFINE(_num); \ PINCTRL_DT_INST_DEFINE(_num); \
SPI_MAX32_IRQ_CONFIG_FUNC(_num) \ SPI_MAX32_IRQ_CONFIG_FUNC(_num) \
COND_CODE_1(CONFIG_SPI_RTIO, (DEFINE_SPI_MAX32_RTIO(_num)), ()); \
static const struct max32_spi_config max32_spi_config_##_num = { \ static const struct max32_spi_config max32_spi_config_##_num = { \
.regs = (mxc_spi_regs_t *)DT_INST_REG_ADDR(_num), \ .regs = (mxc_spi_regs_t *)DT_INST_REG_ADDR(_num), \
.pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(_num), \ .pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(_num), \
@ -797,7 +963,8 @@ static const struct spi_driver_api spi_max32_api = {
static struct max32_spi_data max32_spi_data_##_num = { \ static struct max32_spi_data max32_spi_data_##_num = { \
SPI_CONTEXT_INIT_LOCK(max32_spi_data_##_num, ctx), \ SPI_CONTEXT_INIT_LOCK(max32_spi_data_##_num, ctx), \
SPI_CONTEXT_INIT_SYNC(max32_spi_data_##_num, ctx), \ SPI_CONTEXT_INIT_SYNC(max32_spi_data_##_num, ctx), \
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(_num), ctx)}; \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(_num), ctx) \
IF_ENABLED(CONFIG_SPI_RTIO, (.rtio_ctx = &max32_spi_rtio_##_num))}; \
DEVICE_DT_INST_DEFINE(_num, spi_max32_init, NULL, &max32_spi_data_##_num, \ DEVICE_DT_INST_DEFINE(_num, spi_max32_init, NULL, &max32_spi_data_##_num, \
&max32_spi_config_##_num, PRE_KERNEL_2, CONFIG_SPI_INIT_PRIORITY, \ &max32_spi_config_##_num, PRE_KERNEL_2, CONFIG_SPI_INIT_PRIORITY, \
&spi_max32_api); &spi_max32_api);