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>
diff --git a/drivers/spi/Kconfig.max32 b/drivers/spi/Kconfig.max32
index 0f9e408..13584e9 100644
--- a/drivers/spi/Kconfig.max32
+++ b/drivers/spi/Kconfig.max32
@@ -23,4 +23,28 @@
 	help
 	  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
diff --git a/drivers/spi/spi_max32.c b/drivers/spi/spi_max32.c
index d0238c2..efa1024 100644
--- a/drivers/spi/spi_max32.c
+++ b/drivers/spi/spi_max32.c
@@ -17,6 +17,10 @@
 #include <zephyr/drivers/clock_control/adi_max32_clock_control.h>
 #include <zephyr/logging/log.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>
 
@@ -51,12 +55,18 @@
 	const struct device *dev;
 	mxc_spi_req_t req;
 	uint8_t dummy[2];
+
 #ifdef CONFIG_SPI_MAX32_DMA
 	volatile uint8_t dma_stat;
 #endif /* CONFIG_SPI_MAX32_DMA */
+
 #ifdef CONFIG_SPI_ASYNC
 	struct k_work async_work;
 #endif /* CONFIG_SPI_ASYNC */
+
+#ifdef CONFIG_SPI_RTIO
+	struct spi_rtio *rtio_ctx;
+#endif
 };
 
 #ifdef CONFIG_SPI_MAX32_DMA
@@ -234,6 +244,10 @@
 	const struct max32_spi_config *cfg = dev->config;
 	struct max32_spi_data *data = dev->data;
 	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;
 	uint8_t dfs_shift;
 
@@ -242,12 +256,48 @@
 	dfs_shift = spi_max32_get_dfs_shift(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.txData = (uint8_t *)ctx->tx_buf;
 	data->req.rxLen = len >> dfs_shift;
 	data->req.rxData = ctx->rx_buf;
 
 	data->req.rxData = ctx->rx_buf;
+
 	data->req.rxLen = len >> dfs_shift;
 	if (!data->req.rxData) {
 		/* Pass a dummy buffer to HAL if receive buffer is NULL, otherwise
@@ -256,6 +306,7 @@
 		data->req.rxData = data->dummy;
 		data->req.rxLen = 0;
 	}
+#endif
 	data->req.spi = cfg->regs;
 	data->req.ssIdx = ctx->config->slave;
 	data->req.ssDeassert = 0;
@@ -296,10 +347,12 @@
 		      bool async, spi_callback_t cb, void *userdata)
 {
 	int ret = 0;
-	const struct max32_spi_config *cfg = dev->config;
 	struct max32_spi_data *data = dev->data;
 	struct spi_context *ctx = &data->ctx;
+#ifndef CONFIG_SPI_RTIO
+	const struct max32_spi_config *cfg = dev->config;
 	bool hw_cs_ctrl = true;
+#endif
 
 #ifndef CONFIG_SPI_MAX32_INTERRUPT
 	if (async) {
@@ -309,6 +362,7 @@
 
 	spi_context_lock(ctx, async, cb, userdata, config);
 
+#ifndef CONFIG_SPI_RTIO
 	ret = spi_configure(dev, config);
 	if (ret != 0) {
 		spi_context_release(ctx, ret);
@@ -363,9 +417,12 @@
 			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);
-
 	return ret;
 }
 
@@ -565,6 +622,99 @@
 }
 #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,
 			  const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
 {
@@ -596,6 +746,13 @@
 	const struct device *dev = data->dev;
 	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);
 	spi_context_update_tx(ctx, 1, len);
 	spi_context_update_rx(ctx, 1, len);
@@ -685,12 +842,12 @@
 {
 	struct max32_spi_data *data = dev->data;
 
+#ifndef CONFIG_SPI_RTIO
 	if (!spi_context_configured(&data->ctx, config)) {
 		return -EINVAL;
 	}
-
+#endif
 	spi_context_unlock_unconditionally(&data->ctx);
-
 	return 0;
 }
 
@@ -724,6 +881,10 @@
 
 	data->dev = dev;
 
+#ifdef CONFIG_SPI_RTIO
+	spi_rtio_init(data->rtio_ctx, dev);
+#endif
+
 #ifdef CONFIG_SPI_MAX32_INTERRUPT
 	cfg->irq_config_func(dev);
 #ifdef CONFIG_SPI_ASYNC
@@ -743,8 +904,8 @@
 	.transceive_async = api_transceive_async,
 #endif /* CONFIG_SPI_ASYNC */
 #ifdef CONFIG_SPI_RTIO
-	.iodev_submit = spi_rtio_iodev_default_submit,
-#endif
+	.iodev_submit = api_iodev_submit,
+#endif /* CONFIG_SPI_RTIO */
 	.release = api_release,
 };
 
@@ -784,9 +945,14 @@
 #define MAX32_SPI_DMA_INIT(n)
 #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)                                                                     \
 	PINCTRL_DT_INST_DEFINE(_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 = {                           \
 		.regs = (mxc_spi_regs_t *)DT_INST_REG_ADDR(_num),                                  \
 		.pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(_num),                                     \
@@ -797,7 +963,8 @@
 	static struct max32_spi_data max32_spi_data_##_num = {                                     \
 		SPI_CONTEXT_INIT_LOCK(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,                  \
 			      &max32_spi_config_##_num, PRE_KERNEL_2, CONFIG_SPI_INIT_PRIORITY,    \
 			      &spi_max32_api);