Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spi: rtio: Refactor common SPI RTIO APIs to use across common drivers #77136

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 63 additions & 82 deletions drivers/spi/spi_mcux_lpspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <zephyr/drivers/pinctrl.h>
#ifdef CONFIG_SPI_RTIO
#include <zephyr/rtio/rtio.h>
#include <zephyr/drivers/spi/rtio.h>
#include <zephyr/spinlock.h>
#endif

Expand Down Expand Up @@ -74,13 +75,7 @@
size_t transfer_len;

#ifdef CONFIG_SPI_RTIO
struct rtio *r;
struct mpsc io_q;
struct rtio_iodev iodev;
struct rtio_iodev_sqe *txn_head;
struct rtio_iodev_sqe *txn_curr;
struct spi_dt_spec dt_spec;
struct k_spinlock lock;
struct spi_rtio *rtio_ctx;
#endif

#ifdef CONFIG_SPI_MCUX_LPSPI_DMA
Expand Down Expand Up @@ -181,7 +176,9 @@
struct spi_mcux_data *data = userData;

#ifdef CONFIG_SPI_RTIO
if (data->txn_head != NULL) {
struct spi_rtio *rtio_ctx = data->rtio_ctx;

if (rtio_ctx->txn_head != NULL) {
spi_mcux_iodev_complete(data->dev, status);
return;
}
Expand Down Expand Up @@ -574,6 +571,28 @@
}
#endif

#ifdef CONFIG_SPI_RTIO

static inline int transceive_rtio(const struct device *dev,
const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,

Check notice on line 578 in drivers/spi/spi_mcux_lpspi.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/spi/spi_mcux_lpspi.c:578 -static inline int transceive_rtio(const struct device *dev, - const struct spi_config *spi_cfg, +static inline int transceive_rtio(const struct device *dev, const struct spi_config *spi_cfg,
const struct spi_buf_set *rx_bufs)
{
struct spi_mcux_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;
int ret;

spi_context_lock(&data->ctx, false, NULL, NULL, spi_cfg);

ret = spi_rtio_transceive(rtio_ctx, spi_cfg, tx_bufs, rx_bufs);

spi_context_release(&data->ctx, ret);

return ret;
}

#endif /* CONFIG_SPI_RTIO */

static int transceive(const struct device *dev,
const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,
Expand Down Expand Up @@ -608,12 +627,15 @@
return ret;
}


static int spi_mcux_transceive(const struct device *dev,
const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
{
#ifdef CONFIG_SPI_RTIO
return transceive_rtio(dev, spi_cfg, tx_bufs, rx_bufs);
#endif /* CONFIG_SPI_RTIO */

#ifdef CONFIG_SPI_MCUX_LPSPI_DMA
const struct spi_mcux_data *data = dev->data;

Expand Down Expand Up @@ -700,10 +722,7 @@
#endif /* CONFIG_SPI_MCUX_LPSPI_DMA */

#ifdef CONFIG_SPI_RTIO
data->dt_spec.bus = dev;
data->iodev.api = &spi_iodev_api;
data->iodev.data = &data->dt_spec;
mpsc_init(&data->io_q);
spi_rtio_init(data->rtio_ctx, dev);
#endif

err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
Expand All @@ -717,31 +736,29 @@
}

#ifdef CONFIG_SPI_RTIO
static inline k_spinlock_key_t spi_spin_lock(const struct device *dev)
{
struct spi_mcux_data *data = dev->data;

return k_spin_lock(&data->lock);
}

static inline void spi_spin_unlock(const struct device *dev, k_spinlock_key_t key)
static inline void spi_mcux_iodev_prepare_start(const struct device *dev)
{
struct spi_mcux_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;
int err;

k_spin_unlock(&data->lock, key);
}
err = spi_mcux_configure(dev, spi_config);
__ASSERT(!err, "%d", err);

spi_context_cs_control(&data->ctx, true);
}

static void spi_mcux_iodev_next(const struct device *dev, bool completion);

Check notice on line 754 in drivers/spi/spi_mcux_lpspi.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/spi/spi_mcux_lpspi.c:754 -
static void spi_mcux_iodev_start(const struct device *dev)
{
/* const struct spi_mcux_config *config = dev->config; */
struct spi_mcux_data *data = dev->data;
struct rtio_sqe *sqe = &data->txn_curr->sqe;
struct spi_rtio *rtio_ctx = data->rtio_ctx;
struct rtio_sqe *sqe = &rtio_ctx->txn_curr->sqe;
struct spi_dt_spec *spi_dt_spec = sqe->iodev->data;
struct spi_config *spi_cfg = &spi_dt_spec->config;
struct rtio_iodev_sqe *txn_head = data->txn_head;

LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
lpspi_transfer_t transfer;
Expand Down Expand Up @@ -773,87 +790,51 @@
break;
default:
LOG_ERR("Invalid op code %d for submission %p\n", sqe->op, (void *)sqe);

spi_mcux_iodev_next(dev, true);
rtio_iodev_sqe_err(txn_head, -EINVAL);
spi_mcux_iodev_complete(dev, 0);
spi_mcux_iodev_complete(dev, -EINVAL);
return;
}

data->transfer_len = transfer.dataSize;

k_spinlock_key_t key = spi_spin_lock(dev);

status = LPSPI_MasterTransferNonBlocking(base, &data->handle,
&transfer);
spi_spin_unlock(dev, key);
if (status != kStatus_Success) {
LOG_ERR("Transfer could not start");
rtio_iodev_sqe_err(txn_head, -EIO);
}
}

static void spi_mcux_iodev_next(const struct device *dev, bool completion)
{
struct spi_mcux_data *data = dev->data;

k_spinlock_key_t key = spi_spin_lock(dev);

if (!completion && data->txn_curr != NULL) {
spi_spin_unlock(dev, key);
return;
}

struct mpsc_node *next = mpsc_pop(&data->io_q);

if (next != NULL) {
struct rtio_iodev_sqe *next_sqe = CONTAINER_OF(next, struct rtio_iodev_sqe, q);

data->txn_head = next_sqe;
data->txn_curr = next_sqe;
} else {
data->txn_head = NULL;
data->txn_curr = NULL;
}

spi_spin_unlock(dev, key);

if (data->txn_curr != NULL) {
struct spi_dt_spec *spi_dt_spec = data->txn_curr->sqe.iodev->data;
struct spi_config *spi_cfg = &spi_dt_spec->config;

spi_mcux_configure(dev, spi_cfg);
spi_context_cs_control(&data->ctx, true);
spi_mcux_iodev_start(dev);
spi_mcux_iodev_complete(dev, -EIO);
}
}

static void spi_mcux_iodev_submit(const struct device *dev,
struct rtio_iodev_sqe *iodev_sqe)
{
struct spi_mcux_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;

mpsc_push(&data->io_q, &iodev_sqe->q);
spi_mcux_iodev_next(dev, false);
if (spi_rtio_submit(rtio_ctx, iodev_sqe)) {
spi_mcux_iodev_prepare_start(dev);
spi_mcux_iodev_start(dev);
}
}

static void spi_mcux_iodev_complete(const struct device *dev, int status)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The i2c_rtio_complete does much the same thing and returns true/false from what I recall to inform the driver whether more work should be started. Perhaps spi_rtio_complete could be an equivalent here.

That would reduce the code duplication further.

It's possible that i2c_rtio and spi_rtio do almost the same things entirely and could be converged into bus_rtio, with the blocking call wrappers still being done per bus type (as they each have their own call parameters). That sort of thing can be done after this though.

Copy link
Member Author

@ubieda ubieda Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads-up: I did come up with a spi_rtio_complete() (see further down); it's just that it's very limited as there's no handling of spi_context to perform spi_context_cs_control() calls.

I can take another look to further optimize it.

Copy link
Member Author

@ubieda ubieda Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a second pass, I find it a bit hard to optimize (without having spi_rtio deal with the spi_context, or change the API signature to diverge from i2c_rtio's). May we explore this further optimization out of scope for this PR?

It's possible that i2c_rtio and spi_rtio do almost the same things entirely and could be converged into bus_rtio, with the blocking call wrappers still being done per bus type (as they each have their own call parameters). That sort of thing can be done after this though.

BTW - I do agree we'd gain a lot from this bus_rtio refactoring.

{
struct spi_mcux_data *data = dev->data;
struct spi_rtio *rtio_ctx = data->rtio_ctx;

if (data->txn_curr->sqe.flags & RTIO_SQE_TRANSACTION) {
data->txn_curr = rtio_txn_next(data->txn_curr);
if (!status && rtio_ctx->txn_curr->sqe.flags & RTIO_SQE_TRANSACTION) {
rtio_ctx->txn_curr = rtio_txn_next(rtio_ctx->txn_curr);
spi_mcux_iodev_start(dev);
} else {
struct rtio_iodev_sqe *txn_head = data->txn_head;

/** De-assert CS-line to space from next transaction */
spi_context_cs_control(&data->ctx, false);
spi_mcux_iodev_next(dev, true);
rtio_iodev_sqe_ok(txn_head, status);

if (spi_rtio_complete(rtio_ctx, status)) {
spi_mcux_iodev_prepare_start(dev);
spi_mcux_iodev_start(dev);
}
}
}


#endif


Expand All @@ -868,10 +849,10 @@
.release = spi_mcux_release,
};


#define SPI_MCUX_RTIO_DEFINE(n) RTIO_DEFINE(spi_mcux_rtio_##n, CONFIG_SPI_MCUX_RTIO_SQ_SIZE, \
CONFIG_SPI_MCUX_RTIO_SQ_SIZE)
#define SPI_MCUX_RTIO_DEFINE(n) SPI_RTIO_DEFINE(spi_mcux_rtio_##n, \
CONFIG_SPI_MCUX_RTIO_SQ_SIZE, \
CONFIG_SPI_MCUX_RTIO_SQ_SIZE)

Check notice on line 855 in drivers/spi/spi_mcux_lpspi.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/spi/spi_mcux_lpspi.c:855 -#define SPI_MCUX_RTIO_DEFINE(n) SPI_RTIO_DEFINE(spi_mcux_rtio_##n, \ - CONFIG_SPI_MCUX_RTIO_SQ_SIZE, \ - CONFIG_SPI_MCUX_RTIO_SQ_SIZE) +#define SPI_MCUX_RTIO_DEFINE(n) \ + SPI_RTIO_DEFINE(spi_mcux_rtio_##n, CONFIG_SPI_MCUX_RTIO_SQ_SIZE, \ + CONFIG_SPI_MCUX_RTIO_SQ_SIZE)
#ifdef CONFIG_SPI_MCUX_LPSPI_DMA
#define SPI_DMA_CHANNELS(n) \
IF_ENABLED(DT_INST_DMAS_HAS_NAME(n, tx), \
Expand Down Expand Up @@ -962,7 +943,7 @@
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \
SPI_DMA_CHANNELS(n) \
IF_ENABLED(CONFIG_SPI_RTIO, \
(.r = &spi_mcux_rtio_##n,)) \
(.rtio_ctx = &spi_mcux_rtio_##n,)) \
\
}; \
\
Expand All @@ -975,6 +956,6 @@
static void spi_mcux_config_func_##n(const struct device *dev) \
{ \
SPI_MCUX_LPSPI_MODULE_IRQ(n); \
}

Check notice on line 959 in drivers/spi/spi_mcux_lpspi.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/spi/spi_mcux_lpspi.c:959 -#define SPI_MCUX_LPSPI_INIT(n) \ - PINCTRL_DT_INST_DEFINE(n); \ - COND_CODE_1(CONFIG_SPI_RTIO, (SPI_MCUX_RTIO_DEFINE(n)), ()); \ - \ - static void spi_mcux_config_func_##n(const struct device *dev); \ - \ - static const struct spi_mcux_config spi_mcux_config_##n = { \ - DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(n)), \ - PARENT_DEV(n) \ - .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ - .clock_subsys = \ - (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \ - .irq_config_func = spi_mcux_config_func_##n, \ - .pcs_sck_delay = UTIL_AND( \ - DT_INST_NODE_HAS_PROP(n, pcs_sck_delay), \ - DT_INST_PROP(n, pcs_sck_delay)), \ - .sck_pcs_delay = UTIL_AND( \ - DT_INST_NODE_HAS_PROP(n, sck_pcs_delay), \ - DT_INST_PROP(n, sck_pcs_delay)), \ - .transfer_delay = UTIL_AND( \ - DT_INST_NODE_HAS_PROP(n, transfer_delay), \ - DT_INST_PROP(n, transfer_delay)), \ - .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ - .data_pin_config = DT_INST_ENUM_IDX(n, data_pin_config),\ - }; \ - \ - static struct spi_mcux_data spi_mcux_data_##n = { \ - SPI_CONTEXT_INIT_LOCK(spi_mcux_data_##n, ctx), \ - SPI_CONTEXT_INIT_SYNC(spi_mcux_data_##n, ctx), \ - SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \ - SPI_DMA_CHANNELS(n) \ - IF_ENABLED(CONFIG_SPI_RTIO, \ - (.rtio_ctx = &spi_mcux_rtio_##n,)) \ - \ - }; \ - \ - DEVICE_DT_INST_DEFINE(n, spi_mcux_init, NULL, \ - &spi_mcux_data_##n, \ - &spi_mcux_config_##n, POST_KERNEL, \ - CONFIG_SPI_INIT_PRIORITY, \ - &spi_mcux_driver_api); \ - \ - static void spi_mcux_config_func_##n(const struct device *dev) \ - { \ - SPI_MCUX_LPSPI_MODULE_IRQ(n); \ +#define SPI_MCUX_LPSPI_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + COND_CODE_1(CONFIG_SPI_RTIO, (SPI_MCUX_RTIO_DEFINE(n)), ()); \ + \ + static void spi_mcux_config_func_##n(const struct device *dev); \ + \ + static const struct spi_mcux_config spi_mcux_config_##n = { \ + DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(n)), \ + PARENT_DEV(n).clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \ + .irq_config_func = spi_mcux_config_func_##n, \ + .pcs_sck_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, pcs_sck_delay), \ + DT_INST_PROP(n, pcs_sck_delay)), \ + .sck_pcs_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, sck_pcs_delay), \ + DT_INST_PROP(n, sck_pcs_delay)), \ + .transfer_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, transfer_delay), \ + DT_INST_PROP(n, transfer_delay)), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .data_pin_config = DT_INST_ENUM_IDX(n, data_pin_config), \ + }; \ + \ + static struct spi_mcux_data spi_mcux_data_##n = { \ + SPI_CONTEXT_INIT_LOCK(spi_mcux_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_mcux_data_##n, ctx), \ + SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) SPI_DMA_CHANNELS(n) \ + IF_ENABLED(CONFIG_SPI_RTIO, (.rtio_ctx = &spi_mcux_rtio_##n,

DT_INST_FOREACH_STATUS_OKAY(SPI_MCUX_LPSPI_INIT)
Loading
Loading