zephyr/drivers/i2c/i2c_nrfx_twim_rtio.c
Bjarki Arge Andreasen 2e0092356f drivers: i2c: nrfx_twim_rtio: cast buf to non-const
RTIO correctly declares tx buffers as const, however, the
existing I2C API and NRFX (SDK) drivers don't. Therefor cast the
const buf to non-const when passing the tx buf to the twim driver
from the RTIO call to avoid the const warning.

The tx buffer is being treated as const data naturally in the
NRFX driver, its just not declared as such since we reuse the
buffer for both RX and TX data.

Alternatively the SDK and "shim" drivers built on top of it need
to be updated, which is quite a bit of work :)

Signed-off-by: Bjarki Arge Andreasen <bjarki.andreasen@nordicsemi.no>
2025-01-13 20:23:36 +01:00

291 lines
10 KiB
C

/*
* Copyright (c) 2018, Nordic Semiconductor ASA
* Copyright (c) 2024, Croxel Inc
* Copyright (c) 2024, Embeint Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c/rtio.h>
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/drivers/pinctrl.h>
#include <soc.h>
#include <nrfx_twim.h>
#include "i2c_nrfx_twim_common.h"
LOG_MODULE_REGISTER(i2c_nrfx_twim, CONFIG_I2C_LOG_LEVEL);
struct i2c_nrfx_twim_rtio_config {
struct i2c_nrfx_twim_common_config common;
struct i2c_rtio *ctx;
};
static bool i2c_nrfx_twim_rtio_msg_start(const struct device *dev, uint8_t flags, uint8_t *buf,
size_t buf_len, uint16_t i2c_addr)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
int ret = 0;
ret = i2c_nrfx_twim_msg_transfer(dev, flags, buf, buf_len, i2c_addr);
if (ret != 0) {
return i2c_rtio_complete(ctx, ret);
}
return false;
}
static bool i2c_nrfx_twim_rtio_start(const struct device *dev)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
struct rtio_sqe *sqe = &ctx->txn_curr->sqe;
struct i2c_dt_spec *dt_spec = sqe->iodev->data;
switch (sqe->op) {
case RTIO_OP_RX:
return i2c_nrfx_twim_rtio_msg_start(dev, I2C_MSG_READ | sqe->iodev_flags,
sqe->rx.buf, sqe->rx.buf_len, dt_spec->addr);
case RTIO_OP_TINY_TX:
return i2c_nrfx_twim_rtio_msg_start(dev, I2C_MSG_WRITE | sqe->iodev_flags,
sqe->tiny_tx.buf, sqe->tiny_tx.buf_len,
dt_spec->addr);
case RTIO_OP_TX:
/* If buffer is not accessible by DMA, copy it into the internal driver buffer */
if (!nrf_dma_accessible_check(&config->common.twim, sqe->tx.buf)) {
/* Validate buffer will fit */
if (sqe->tx.buf_len > config->common.msg_buf_size) {
LOG_ERR("Need to use the internal driver "
"buffer but its size is insufficient "
"(%u > %u). "
"Adjust the zephyr,concat-buf-size or "
"zephyr,flash-buf-max-size property "
"(the one with greater value) in the "
"\"%s\"' node.",
sqe->tx.buf_len, config->common.msg_buf_size, dev->name);
return i2c_rtio_complete(ctx, -ENOSPC);
}
memcpy(config->common.msg_buf, sqe->tx.buf, sqe->tx.buf_len);
sqe->tx.buf = config->common.msg_buf;
}
return i2c_nrfx_twim_rtio_msg_start(dev, I2C_MSG_WRITE | sqe->iodev_flags,
(uint8_t *)sqe->tx.buf, sqe->tx.buf_len,
dt_spec->addr);
case RTIO_OP_I2C_CONFIGURE:
(void)i2c_nrfx_twim_configure(dev, sqe->i2c_config);
return false;
case RTIO_OP_I2C_RECOVER:
(void)i2c_nrfx_twim_recover_bus(dev);
return false;
default:
LOG_ERR("Invalid op code %d for submission %p\n", sqe->op, (void *)sqe);
return i2c_rtio_complete(ctx, -EINVAL);
}
}
static void i2c_nrfx_twim_rtio_complete(const struct device *dev, int status)
{
/** Finalize if there are no more pending xfers */
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
if (i2c_rtio_complete(ctx, status)) {
(void)i2c_nrfx_twim_rtio_start(dev);
} else {
/* Release bus on completion */
pm_device_runtime_put(dev);
}
}
static int i2c_nrfx_twim_rtio_configure(const struct device *dev, uint32_t i2c_config)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
return i2c_rtio_configure(ctx, i2c_config);
}
static int i2c_nrfx_twim_rtio_transfer(const struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
return i2c_rtio_transfer(ctx, msgs, num_msgs, addr);
}
static int i2c_nrfx_twim_rtio_recover_bus(const struct device *dev)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
return i2c_rtio_recover(ctx);
}
static void i2c_nrfx_twim_rtio_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_seq)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
struct i2c_rtio *ctx = config->ctx;
if (i2c_rtio_submit(ctx, iodev_seq)) {
if (pm_device_runtime_get(dev) < 0) {
(void)i2c_rtio_complete(ctx, -EINVAL);
} else {
(void)i2c_nrfx_twim_rtio_start(dev);
}
}
}
static void event_handler(nrfx_twim_evt_t const *p_event, void *p_context)
{
const struct device *dev = p_context;
int status = p_event->type == NRFX_TWIM_EVT_DONE ? 0 : -EIO;
i2c_nrfx_twim_rtio_complete(dev, status);
}
static DEVICE_API(i2c, i2c_nrfx_twim_driver_api) = {
.configure = i2c_nrfx_twim_rtio_configure,
.transfer = i2c_nrfx_twim_rtio_transfer,
.recover_bus = i2c_nrfx_twim_rtio_recover_bus,
.iodev_submit = i2c_nrfx_twim_rtio_submit,
};
int i2c_nrfx_twim_rtio_init(const struct device *dev)
{
const struct i2c_nrfx_twim_rtio_config *config = dev->config;
i2c_rtio_init(config->ctx, dev);
return i2c_nrfx_twim_common_init(dev);
}
#define CONCAT_BUF_SIZE(idx) \
COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), zephyr_concat_buf_size), \
(DT_PROP(I2C(idx), zephyr_concat_buf_size)), (0))
#define FLASH_BUF_MAX_SIZE(idx) \
COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), zephyr_flash_buf_max_size), \
(DT_PROP(I2C(idx), zephyr_flash_buf_max_size)), (0))
#define USES_MSG_BUF(idx) \
COND_CODE_0(CONCAT_BUF_SIZE(idx), (COND_CODE_0(FLASH_BUF_MAX_SIZE(idx), (0), (1))), (1))
#define MSG_BUF_SIZE(idx) MAX(CONCAT_BUF_SIZE(idx), FLASH_BUF_MAX_SIZE(idx))
#define I2C_NRFX_TWIM_RTIO_DEVICE(idx) \
NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(I2C(idx)); \
BUILD_ASSERT(I2C_FREQUENCY(idx) != I2C_NRFX_TWIM_INVALID_FREQUENCY, \
"Wrong I2C " #idx " frequency setting in dts"); \
static void irq_connect##idx(void) \
{ \
IRQ_CONNECT(DT_IRQN(I2C(idx)), DT_IRQ(I2C(idx), priority), nrfx_isr, \
nrfx_twim_##idx##_irq_handler, 0); \
} \
IF_ENABLED( \
USES_MSG_BUF(idx), \
(static uint8_t twim_##idx##_msg_buf[MSG_BUF_SIZE(idx)] I2C_MEMORY_SECTION(idx);)) \
I2C_RTIO_DEFINE(_i2c##idx##_twim_rtio, \
DT_INST_PROP_OR(n, sq_size, CONFIG_I2C_RTIO_SQ_SIZE), \
DT_INST_PROP_OR(n, cq_size, CONFIG_I2C_RTIO_CQ_SIZE)); \
PINCTRL_DT_DEFINE(I2C(idx)); \
static const struct i2c_nrfx_twim_rtio_config twim_##idx##z_config = { \
.common = \
{ \
.twim = NRFX_TWIM_INSTANCE(idx), \
.twim_config = \
{ \
.skip_gpio_cfg = true, \
.skip_psel_cfg = true, \
.frequency = I2C_FREQUENCY(idx), \
}, \
.event_handler = event_handler, \
.msg_buf_size = MSG_BUF_SIZE(idx), \
.irq_connect = irq_connect##idx, \
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(I2C(idx)), \
IF_ENABLED(USES_MSG_BUF(idx), (.msg_buf = twim_##idx##_msg_buf,)) \
.max_transfer_size = \
BIT_MASK(DT_PROP(I2C(idx), easydma_maxcnt_bits)), \
}, \
.ctx = &_i2c##idx##_twim_rtio, \
}; \
PM_DEVICE_DT_DEFINE(I2C(idx), twim_nrfx_pm_action, PM_DEVICE_ISR_SAFE); \
I2C_DEVICE_DT_DEFINE(I2C(idx), i2c_nrfx_twim_rtio_init, PM_DEVICE_DT_GET(I2C(idx)), NULL, \
&twim_##idx##z_config, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \
&i2c_nrfx_twim_driver_api)
#define I2C_MEMORY_SECTION(idx) \
COND_CODE_1(I2C_HAS_PROP(idx, memory_regions), \
(__attribute__((__section__( \
LINKER_DT_NODE_REGION_NAME(DT_PHANDLE(I2C(idx), memory_regions)))))), \
())
#ifdef CONFIG_HAS_HW_NRF_TWIM0
I2C_NRFX_TWIM_RTIO_DEVICE(0);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM1
I2C_NRFX_TWIM_RTIO_DEVICE(1);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM2
I2C_NRFX_TWIM_RTIO_DEVICE(2);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM3
I2C_NRFX_TWIM_RTIO_DEVICE(3);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM20
I2C_NRFX_TWIM_RTIO_DEVICE(20);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM21
I2C_NRFX_TWIM_RTIO_DEVICE(21);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM22
I2C_NRFX_TWIM_RTIO_DEVICE(22);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM30
I2C_NRFX_TWIM_RTIO_DEVICE(30);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM120
I2C_NRFX_TWIM_RTIO_DEVICE(120);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM130
I2C_NRFX_TWIM_RTIO_DEVICE(130);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM131
I2C_NRFX_TWIM_RTIO_DEVICE(131);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM132
I2C_NRFX_TWIM_RTIO_DEVICE(132);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM133
I2C_NRFX_TWIM_RTIO_DEVICE(133);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM134
I2C_NRFX_TWIM_RTIO_DEVICE(134);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM135
I2C_NRFX_TWIM_RTIO_DEVICE(135);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM136
I2C_NRFX_TWIM_RTIO_DEVICE(136);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIM137
I2C_NRFX_TWIM_RTIO_DEVICE(137);
#endif