mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-14 07:01:56 +00:00
I/O Devices were meant to be handles of sorts and had a built in mpsc queue as this made sense initially. As time has gone on it turned out that often we wanted the mpsc queue to be an implementation detail hidden in a driver. In fact pretty much all drivers work this way now. Keeping the struct mpsc queue as a member of rtio_iodev meant wasted memory in cases where it wasn't used. It also meant a bit of confusion as the queue might be accidently used in places where it shouldn't be. Remove the mpsc queue member from struct rtio_iodev and the last remaining usages of it. Will ensure RTIO for 3.7 LTS avoids causing unneeded churn for future users. Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
219 lines
4.7 KiB
C
219 lines
4.7 KiB
C
/*
|
|
* Copyright (c) 2023 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/drivers/i2c.h>
|
|
#include <zephyr/drivers/i2c/rtio.h>
|
|
#include <zephyr/rtio/rtio.h>
|
|
#include <zephyr/sys/mpsc_lockfree.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
|
|
#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(i2c_rtio);
|
|
|
|
const struct rtio_iodev_api i2c_iodev_api = {
|
|
.submit = i2c_iodev_submit,
|
|
};
|
|
|
|
struct rtio_sqe *i2c_rtio_copy(struct rtio *r, struct rtio_iodev *iodev, const struct i2c_msg *msgs,
|
|
uint8_t num_msgs)
|
|
{
|
|
__ASSERT(num_msgs > 0, "Expecting at least one message to copy");
|
|
|
|
struct rtio_sqe *sqe = NULL;
|
|
|
|
for (uint8_t i = 0; i < num_msgs; i++) {
|
|
sqe = rtio_sqe_acquire(r);
|
|
|
|
if (sqe == NULL) {
|
|
rtio_sqe_drop_all(r);
|
|
return NULL;
|
|
}
|
|
|
|
if (msgs[i].flags & I2C_MSG_READ) {
|
|
rtio_sqe_prep_read(sqe, iodev, RTIO_PRIO_NORM, msgs[i].buf, msgs[i].len,
|
|
NULL);
|
|
} else {
|
|
rtio_sqe_prep_write(sqe, iodev, RTIO_PRIO_NORM, msgs[i].buf, msgs[i].len,
|
|
NULL);
|
|
}
|
|
sqe->flags |= RTIO_SQE_TRANSACTION;
|
|
sqe->iodev_flags =
|
|
((msgs[i].flags & I2C_MSG_STOP) ? RTIO_IODEV_I2C_STOP : 0) |
|
|
((msgs[i].flags & I2C_MSG_RESTART) ? RTIO_IODEV_I2C_RESTART : 0) |
|
|
((msgs[i].flags & I2C_MSG_ADDR_10_BITS) ? RTIO_IODEV_I2C_10_BITS : 0);
|
|
}
|
|
|
|
sqe->flags &= ~RTIO_SQE_TRANSACTION;
|
|
|
|
return sqe;
|
|
}
|
|
|
|
void i2c_rtio_init(struct i2c_rtio *ctx, const struct device *dev)
|
|
{
|
|
k_sem_init(&ctx->lock, 1, 1);
|
|
mpsc_init(&ctx->io_q);
|
|
ctx->txn_curr = NULL;
|
|
ctx->txn_head = NULL;
|
|
ctx->dt_spec.bus = dev;
|
|
ctx->iodev.data = &ctx->dt_spec;
|
|
ctx->iodev.api = &i2c_iodev_api;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @brief Setup the next transaction (could be a single op) if needed
|
|
*
|
|
* @retval true New transaction to start with the hardware is setup
|
|
* @retval false No new transaction to start
|
|
*/
|
|
static bool i2c_rtio_next(struct i2c_rtio *ctx, bool completion)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&ctx->slock);
|
|
|
|
/* Already working on something, bail early */
|
|
if (!completion && ctx->txn_head != NULL) {
|
|
k_spin_unlock(&ctx->slock, key);
|
|
return false;
|
|
}
|
|
|
|
struct mpsc_node *next = mpsc_pop(&ctx->io_q);
|
|
|
|
/* Nothing left to do */
|
|
if (next == NULL) {
|
|
ctx->txn_head = NULL;
|
|
ctx->txn_curr = NULL;
|
|
k_spin_unlock(&ctx->slock, key);
|
|
return false;
|
|
}
|
|
|
|
ctx->txn_head = CONTAINER_OF(next, struct rtio_iodev_sqe, q);
|
|
ctx->txn_curr = ctx->txn_head;
|
|
|
|
k_spin_unlock(&ctx->slock, key);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool i2c_rtio_complete(struct i2c_rtio *ctx, int status)
|
|
{
|
|
/* On error bail */
|
|
if (status < 0) {
|
|
rtio_iodev_sqe_err(ctx->txn_head, status);
|
|
return i2c_rtio_next(ctx, true);
|
|
}
|
|
|
|
/* Try for next submission in the transaction */
|
|
ctx->txn_curr = rtio_txn_next(ctx->txn_curr);
|
|
if (ctx->txn_curr) {
|
|
return true;
|
|
}
|
|
|
|
rtio_iodev_sqe_ok(ctx->txn_head, status);
|
|
return i2c_rtio_next(ctx, true);
|
|
}
|
|
bool i2c_rtio_submit(struct i2c_rtio *ctx, struct rtio_iodev_sqe *iodev_sqe)
|
|
{
|
|
mpsc_push(&ctx->io_q, &iodev_sqe->q);
|
|
return i2c_rtio_next(ctx, false);
|
|
}
|
|
|
|
int i2c_rtio_transfer(struct i2c_rtio *ctx, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr)
|
|
{
|
|
struct rtio_iodev *iodev = &ctx->iodev;
|
|
struct rtio *const r = ctx->r;
|
|
struct rtio_sqe *sqe = NULL;
|
|
struct rtio_cqe *cqe = NULL;
|
|
int res = 0;
|
|
|
|
k_sem_take(&ctx->lock, K_FOREVER);
|
|
|
|
ctx->dt_spec.addr = addr;
|
|
|
|
sqe = i2c_rtio_copy(r, iodev, msgs, num_msgs);
|
|
if (sqe == NULL) {
|
|
LOG_ERR("Not enough submission queue entries");
|
|
res = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
rtio_submit(r, 1);
|
|
|
|
cqe = rtio_cqe_consume(r);
|
|
while (cqe != NULL) {
|
|
res = cqe->result;
|
|
rtio_cqe_release(r, cqe);
|
|
cqe = rtio_cqe_consume(r);
|
|
}
|
|
|
|
out:
|
|
k_sem_give(&ctx->lock);
|
|
return res;
|
|
}
|
|
|
|
int i2c_rtio_configure(struct i2c_rtio *ctx, uint32_t i2c_config)
|
|
{
|
|
struct rtio_iodev *iodev = &ctx->iodev;
|
|
struct rtio *const r = ctx->r;
|
|
struct rtio_sqe *sqe = NULL;
|
|
struct rtio_cqe *cqe = NULL;
|
|
int res = 0;
|
|
|
|
k_sem_take(&ctx->lock, K_FOREVER);
|
|
|
|
sqe = rtio_sqe_acquire(r);
|
|
if (sqe == NULL) {
|
|
LOG_ERR("Not enough submission queue entries");
|
|
res = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
sqe->op = RTIO_OP_I2C_CONFIGURE;
|
|
sqe->iodev = iodev;
|
|
sqe->i2c_config = i2c_config;
|
|
|
|
rtio_submit(r, 1);
|
|
|
|
cqe = rtio_cqe_consume(r);
|
|
res = cqe->result;
|
|
rtio_cqe_release(r, cqe);
|
|
|
|
out:
|
|
k_sem_give(&ctx->lock);
|
|
return res;
|
|
}
|
|
|
|
int i2c_rtio_recover(struct i2c_rtio *ctx)
|
|
{
|
|
struct rtio_iodev *iodev = &ctx->iodev;
|
|
struct rtio *const r = ctx->r;
|
|
struct rtio_sqe *sqe = NULL;
|
|
struct rtio_cqe *cqe = NULL;
|
|
int res = 0;
|
|
|
|
k_sem_take(&ctx->lock, K_FOREVER);
|
|
|
|
sqe = rtio_sqe_acquire(r);
|
|
if (sqe == NULL) {
|
|
LOG_ERR("Not enough submission queue entries");
|
|
res = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
sqe->op = RTIO_OP_I2C_RECOVER;
|
|
sqe->iodev = iodev;
|
|
|
|
rtio_submit(r, 1);
|
|
|
|
cqe = rtio_cqe_consume(r);
|
|
res = cqe->result;
|
|
rtio_cqe_release(r, cqe);
|
|
|
|
out:
|
|
k_sem_give(&ctx->lock);
|
|
return res;
|
|
}
|