zephyr/drivers/i2c/i2c_rtio_default.c
Yuval Peress 8974c248cf rtio: Add default i2c submit handler
Use the RTIO work queue to fake the i2c submit calls for drivers which
haven't yet implemented the API. Applications can change the size of
the work queue pool depending on how much traffic they have on the buses.

Signed-off-by: Yuval Peress <peress@google.com>
2024-09-04 21:28:26 +02:00

146 lines
4.4 KiB
C

/*
* Copyright (c) 2024 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c/rtio.h>
#include <zephyr/rtio/rtio.h>
#include <zephyr/rtio/work.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(i2c_rtio, CONFIG_I2C_LOG_LEVEL);
static int i2c_iodev_submit_rx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_RX);
msgs[0].buf = iodev_sqe->sqe.rx.buf;
msgs[0].len = iodev_sqe->sqe.rx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_READ;
*num_msgs = 1;
return 0;
}
static int i2c_iodev_submit_tx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TX);
msgs[0].buf = (uint8_t *)iodev_sqe->sqe.tx.buf;
msgs[0].len = iodev_sqe->sqe.tx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_WRITE;
*num_msgs = 1;
return 0;
}
static int i2c_iodev_submit_tiny_tx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TINY_TX);
msgs[0].buf = (uint8_t *)iodev_sqe->sqe.tiny_tx.buf;
msgs[0].len = iodev_sqe->sqe.tiny_tx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_WRITE;
*num_msgs = 1;
return 0;
}
static int i2c_iodev_submit_txrx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TXRX);
msgs[0].buf = (uint8_t *)iodev_sqe->sqe.txrx.tx_buf;
msgs[0].len = iodev_sqe->sqe.txrx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_WRITE;
msgs[1].buf = iodev_sqe->sqe.txrx.rx_buf;
msgs[1].len = iodev_sqe->sqe.txrx.buf_len;
msgs[1].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_READ;
*num_msgs = 2;
return 0;
}
void i2c_iodev_submit_work_handler(struct rtio_iodev_sqe *iodev_sqe)
{
const struct i2c_dt_spec *dt_spec = (const struct i2c_dt_spec *)iodev_sqe->sqe.iodev->data;
const struct device *dev = dt_spec->bus;
LOG_DBG("Sync RTIO work item for: %p", (void *)iodev_sqe);
struct rtio_iodev_sqe *transaction_current = iodev_sqe;
struct i2c_msg msgs[2];
uint8_t num_msgs;
int rc = 0;
do {
/* Convert the iodev_sqe back to an i2c_msg */
switch (transaction_current->sqe.op) {
case RTIO_OP_RX:
rc = i2c_iodev_submit_rx(transaction_current, msgs, &num_msgs);
break;
case RTIO_OP_TX:
rc = i2c_iodev_submit_tx(transaction_current, msgs, &num_msgs);
break;
case RTIO_OP_TINY_TX:
rc = i2c_iodev_submit_tiny_tx(transaction_current, msgs, &num_msgs);
break;
case RTIO_OP_TXRX:
rc = i2c_iodev_submit_txrx(transaction_current, msgs, &num_msgs);
break;
default:
LOG_ERR("Invalid op code %d for submission %p", transaction_current->sqe.op,
(void *)&transaction_current->sqe);
rc = -EIO;
break;
}
if (rc == 0) {
__ASSERT_NO_MSG(num_msgs > 0);
rc = i2c_transfer(dev, msgs, num_msgs, dt_spec->addr);
transaction_current = rtio_txn_next(transaction_current);
}
} while (rc == 0 && transaction_current != NULL);
if (rc != 0) {
rtio_iodev_sqe_err(iodev_sqe, rc);
} else {
rtio_iodev_sqe_ok(iodev_sqe, 0);
}
}
void i2c_iodev_submit_fallback(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
LOG_DBG("Executing fallback for dev: %p, sqe: %p", (void *)dev, (void *)iodev_sqe);
struct rtio_work_req *req = rtio_work_req_alloc();
if (req == NULL) {
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
return;
}
rtio_work_req_submit(req, iodev_sqe, i2c_iodev_submit_work_handler);
}