Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

442 lines
10 KiB

/*
* 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(SHIM_NRF_TWIS_NODE(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(id))))
#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;
}
static bool shim_nrf_twis_is_suspended(const struct device *dev)
{
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
return state == PM_DEVICE_STATE_SUSPENDED ||
state == PM_DEVICE_STATE_OFF;
}
#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(dev);
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);
}
static int shim_nrf_twis_deinit(const struct device *dev)
{
const struct shim_nrf_twis_config *dev_config = dev->config;
struct shim_nrf_twis_data *dev_data = dev->data;
if (dev_data->target_config != NULL) {
LOG_ERR("target registered");
return -EPERM;
}
#if CONFIG_PM_DEVICE
/*
* PM must have suspended the device before driver can
* be deinitialized
*/
if (!shim_nrf_twis_is_suspended(dev)) {
LOG_ERR("device active");
return -EBUSY;
}
#else
/* Suspend device */
shim_nrf_twis_disable(dev);
#endif
/* Uninit device hardware */
nrfx_twis_uninit(&dev_config->twis);
return 0;
}
#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_DEINIT_DEFINE( \
SHIM_NRF_TWIS_NODE(id), \
shim_nrf_twis_init, \
shim_nrf_twis_deinit, \
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_TWIS23
SHIM_NRF_TWIS_DEVICE_DEFINE(23);
#endif
#ifdef CONFIG_HAS_HW_NRF_TWIS24
SHIM_NRF_TWIS_DEVICE_DEFINE(24);
#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