146 lines
4.4 KiB
C
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);
|
|
}
|