zephyr/drivers/i2c/i2c_nrfx_twis.c
Bjarki Arge Andreasen 02bf44d590 drivers: i2c: add nrf twis suppport
Add nrf twis (I2C controller supporting I2C peripheral role and
EasyDMA) support, including updating the existing twis dt binding
to match the hardware with proper examples.

Signed-off-by: Bjarki Arge Andreasen <bjarki.andreasen@nordicsemi.no>
2024-12-20 16:14:05 +01:00

395 lines
9.4 KiB
C

/*
* Copyright (c) 2024, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/irq.h>
#include <zephyr/linker/devicetree_regions.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <nrfx_twis.h>
#include <string.h>
#define DT_DRV_COMPAT nordic_nrf_twis
#define SHIM_NRF_TWIS_NODE(id) \
DT_NODELABEL(_CONCAT(i2c, id))
#define SHIM_NRF_TWIS_DEVICE_GET(id) \
DEVICE_DT_GET(SHIM_NRF_TWIS_NODE(id))
#define SHIM_NRF_TWIS_IRQ_HANDLER(id) \
_CONCAT_3(nrfx_twis_, id, _irq_handler)
#define SHIM_NRF_TWIS_IRQN(id) \
DT_IRQN(SHIM_NRF_TWIS_NODE(id))
#define SHIM_NRF_TWIS_IRQ_PRIO(id) \
DT_IRQ(SHIM_NRF_TWIS_NODE(id), priority)
#define SHIM_NRF_TWIS_HAS_MEMORY_REGIONS(id) \
DT_NODE_HAS_PROP(id, memory_regions)
#define SHIM_NRF_TWIS_LINKER_REGION_NAME(id) \
LINKER_DT_NODE_REGION_NAME(DT_PHANDLE(SHIM_NRF_TWIS_NODE(id), memory_regions))
#define SHIM_NRF_TWIS_BUF_ATTR_SECTION(id) \
__attribute__((__section__(SHIM_NRF_TWIS_LINKER_REGION_NAME, ())))
#define SHIM_NRF_TWIS_BUF_ATTR(id) \
COND_CODE_1( \
SHIM_NRF_TWIS_HAS_MEMORY_REGIONS(id), \
(SHIM_NRF_TWIS_BUF_ATTR_SECTION(id)), \
() \
)
#define SHIM_NRF_TWIS_BUF_SIZE \
CONFIG_I2C_NRFX_TWIS_BUF_SIZE
LOG_MODULE_REGISTER(i2c_nrfx_twis, CONFIG_I2C_LOG_LEVEL);
struct shim_nrf_twis_config {
nrfx_twis_t twis;
void (*irq_connect)(void);
void (*event_handler)(nrfx_twis_evt_t const *event);
const struct pinctrl_dev_config *pcfg;
uint8_t *buf;
};
struct shim_nrf_twis_data {
struct i2c_target_config *target_config;
bool enabled;
};
#if CONFIG_PM_DEVICE
static bool shim_nrf_twis_is_resumed(const struct device *dev)
{
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
return state == PM_DEVICE_STATE_ACTIVE;
}
#else
static bool shim_nrf_twis_is_resumed(const struct device *dev)
{
ARG_UNUSED(dev);
return true;
}
#endif
static void shim_nrf_twis_enable(const struct device *dev)
{
struct shim_nrf_twis_data *dev_data = dev->data;
const struct shim_nrf_twis_config *dev_config = dev->config;
if (dev_data->enabled) {
return;
}
if (dev_data->target_config == NULL) {
return;
}
(void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT);
nrfx_twis_enable(&dev_config->twis);
dev_data->enabled = true;
}
static void shim_nrf_twis_disable(const struct device *dev)
{
struct shim_nrf_twis_data *dev_data = dev->data;
const struct shim_nrf_twis_config *dev_config = dev->config;
if (!dev_data->enabled) {
return;
}
dev_data->enabled = false;
nrfx_twis_disable(&dev_config->twis);
(void)pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP);
}
static void shim_nrf_twis_handle_read_req(const struct device *dev)
{
struct shim_nrf_twis_data *dev_data = dev->data;
const struct shim_nrf_twis_config *dev_config = dev->config;
struct i2c_target_config *target_config = dev_data->target_config;
const struct i2c_target_callbacks *callbacks = target_config->callbacks;
const nrfx_twis_t *twis = &dev_config->twis;
uint8_t *buf;
uint32_t buf_size;
nrfx_err_t err;
if (callbacks->buf_read_requested(target_config, &buf, &buf_size)) {
LOG_ERR("no buffer provided");
return;
}
if (SHIM_NRF_TWIS_BUF_SIZE < buf_size) {
LOG_ERR("provided buffer too large");
return;
}
memcpy(dev_config->buf, buf, buf_size);
err = nrfx_twis_tx_prepare(twis, dev_config->buf, buf_size);
if (err != NRFX_SUCCESS) {
LOG_ERR("tx prepare failed");
return;
}
}
static void shim_nrf_twis_handle_write_req(const struct device *dev)
{
const struct shim_nrf_twis_config *dev_config = dev->config;
const nrfx_twis_t *twis = &dev_config->twis;
nrfx_err_t err;
err = nrfx_twis_rx_prepare(twis, dev_config->buf, SHIM_NRF_TWIS_BUF_SIZE);
if (err != NRFX_SUCCESS) {
LOG_ERR("rx prepare failed");
return;
}
}
static void shim_nrf_twis_handle_write_done(const struct device *dev)
{
struct shim_nrf_twis_data *dev_data = dev->data;
const struct shim_nrf_twis_config *dev_config = dev->config;
struct i2c_target_config *target_config = dev_data->target_config;
const struct i2c_target_callbacks *callbacks = target_config->callbacks;
const nrfx_twis_t *twis = &dev_config->twis;
callbacks->buf_write_received(target_config, dev_config->buf, nrfx_twis_rx_amount(twis));
}
static void shim_nrf_twis_event_handler(const struct device *dev,
nrfx_twis_evt_t const *event)
{
switch (event->type) {
case NRFX_TWIS_EVT_READ_REQ:
shim_nrf_twis_handle_read_req(dev);
break;
case NRFX_TWIS_EVT_WRITE_REQ:
shim_nrf_twis_handle_write_req(dev);
break;
case NRFX_TWIS_EVT_WRITE_DONE:
shim_nrf_twis_handle_write_done(dev);
break;
default:
break;
}
}
static int shim_nrf_twis_pm_action_cb(const struct device *dev,
enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_RESUME:
shim_nrf_twis_enable(dev);
break;
#if CONFIG_PM_DEVICE
case PM_DEVICE_ACTION_SUSPEND:
shim_nrf_twis_disable();
break;
#endif
default:
return -ENOTSUP;
}
return 0;
}
static int shim_nrf_twis_target_register(const struct device *dev,
struct i2c_target_config *target_config)
{
struct shim_nrf_twis_data *dev_data = dev->data;
const struct shim_nrf_twis_config *dev_config = dev->config;
const nrfx_twis_t *twis = &dev_config->twis;
nrfx_err_t err;
const nrfx_twis_config_t config = {
.addr = {
target_config->address,
},
.skip_gpio_cfg = true,
.skip_psel_cfg = true,
};
if (target_config->flags) {
LOG_ERR("16-bit address unsupported");
return -EINVAL;
}
shim_nrf_twis_disable(dev);
err = nrfx_twis_reconfigure(twis, &config);
if (err != NRFX_SUCCESS) {
return -ENODEV;
}
dev_data->target_config = target_config;
if (shim_nrf_twis_is_resumed(dev)) {
shim_nrf_twis_enable(dev);
}
return 0;
}
static int shim_nrf_twis_target_unregister(const struct device *dev,
struct i2c_target_config *target_config)
{
struct shim_nrf_twis_data *dev_data = dev->data;
if (dev_data->target_config != target_config) {
return -EINVAL;
}
shim_nrf_twis_disable(dev);
dev_data->target_config = NULL;
return 0;
}
const struct i2c_driver_api shim_nrf_twis_api = {
.target_register = shim_nrf_twis_target_register,
.target_unregister = shim_nrf_twis_target_unregister,
};
static int shim_nrf_twis_init(const struct device *dev)
{
const struct shim_nrf_twis_config *dev_config = dev->config;
nrfx_err_t err;
const nrfx_twis_config_t config = {
.skip_gpio_cfg = true,
.skip_psel_cfg = true,
};
err = nrfx_twis_init(&dev_config->twis, &config, dev_config->event_handler);
if (err != NRFX_SUCCESS) {
return -ENODEV;
}
dev_config->irq_connect();
return pm_device_driver_init(dev, shim_nrf_twis_pm_action_cb);
}
#define SHIM_NRF_TWIS_NAME(id, name) \
_CONCAT_4(shim_nrf_twis_, name, _, id)
#define SHIM_NRF_TWIS_DEVICE_DEFINE(id) \
static void SHIM_NRF_TWIS_NAME(id, irq_connect)(void) \
{ \
IRQ_CONNECT( \
SHIM_NRF_TWIS_IRQN(id), \
SHIM_NRF_TWIS_IRQ_PRIO(id), \
nrfx_isr, \
SHIM_NRF_TWIS_IRQ_HANDLER(id), \
0 \
); \
} \
\
static void SHIM_NRF_TWIS_NAME(id, event_handler)(nrfx_twis_evt_t const *event) \
{ \
shim_nrf_twis_event_handler(SHIM_NRF_TWIS_DEVICE_GET(id), event); \
} \
\
static struct shim_nrf_twis_data SHIM_NRF_TWIS_NAME(id, data); \
\
PINCTRL_DT_DEFINE(SHIM_NRF_TWIS_NODE(id)); \
\
static uint8_t SHIM_NRF_TWIS_NAME(id, buf) \
[SHIM_NRF_TWIS_BUF_SIZE] SHIM_NRF_TWIS_BUF_ATTR(id); \
\
static const struct shim_nrf_twis_config SHIM_NRF_TWIS_NAME(id, config) = { \
.twis = NRFX_TWIS_INSTANCE(id), \
.irq_connect = SHIM_NRF_TWIS_NAME(id, irq_connect), \
.event_handler = SHIM_NRF_TWIS_NAME(id, event_handler), \
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(SHIM_NRF_TWIS_NODE(id)), \
.buf = SHIM_NRF_TWIS_NAME(id, buf), \
}; \
\
PM_DEVICE_DT_DEFINE( \
SHIM_NRF_TWIS_NODE(id), \
shim_nrf_twis_pm_action_cb, \
); \
\
DEVICE_DT_DEFINE( \
SHIM_NRF_TWIS_NODE(id), \
shim_nrf_twis_init, \
PM_DEVICE_DT_GET(SHIM_NRF_TWIS_NODE(id)), \
&SHIM_NRF_TWIS_NAME(id, data), \
&SHIM_NRF_TWIS_NAME(id, config), \
POST_KERNEL, \
CONFIG_I2C_INIT_PRIORITY, \
&shim_nrf_twis_api \
);
#ifdef CONFIG_HAS_HW_NRF_TWIS0
SHIM_NRF_TWIS_DEVICE_DEFINE(0);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS1
SHIM_NRF_TWIS_DEVICE_DEFINE(1);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS2
SHIM_NRF_TWIS_DEVICE_DEFINE(2);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS3
SHIM_NRF_TWIS_DEVICE_DEFINE(3);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS20
SHIM_NRF_TWIS_DEVICE_DEFINE(20);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS21
SHIM_NRF_TWIS_DEVICE_DEFINE(21);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS22
SHIM_NRF_TWIS_DEVICE_DEFINE(22);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS30
SHIM_NRF_TWIS_DEVICE_DEFINE(30);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS130
SHIM_NRF_TWIS_DEVICE_DEFINE(130);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS131
SHIM_NRF_TWIS_DEVICE_DEFINE(131);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS133
SHIM_NRF_TWIS_DEVICE_DEFINE(133);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS134
SHIM_NRF_TWIS_DEVICE_DEFINE(134);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS135
SHIM_NRF_TWIS_DEVICE_DEFINE(135);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS136
SHIM_NRF_TWIS_DEVICE_DEFINE(136);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS137
SHIM_NRF_TWIS_DEVICE_DEFINE(137);
#endif