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>
This commit is contained in:
parent
4103c8236b
commit
02bf44d590
4 changed files with 460 additions and 28 deletions
|
|
@ -67,6 +67,7 @@ else()
|
|||
i2c_nrfx_twim_common.c
|
||||
)
|
||||
endif()
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_NRFX_TWIS i2c_nrfx_twis.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_NUMAKER i2c_numaker.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_SAM_TWI i2c_sam_twi.c)
|
||||
if(CONFIG_I2C_RTIO)
|
||||
|
|
|
|||
|
|
@ -49,4 +49,33 @@ config I2C_NRFX_TRANSFER_TIMEOUT
|
|||
0 means that the driver should use the K_FOREVER value,
|
||||
i.e. it should wait as long as necessary.
|
||||
|
||||
config I2C_NRFX_TWIS
|
||||
def_bool y
|
||||
depends on DT_HAS_NORDIC_NRF_TWIS_ENABLED
|
||||
depends on I2C_TARGET
|
||||
depends on I2C_TARGET_BUFFER_MODE
|
||||
select NRFX_TWIS0 if HAS_HW_NRF_TWIS0
|
||||
select NRFX_TWIS1 if HAS_HW_NRF_TWIS1
|
||||
select NRFX_TWIS2 if HAS_HW_NRF_TWIS2
|
||||
select NRFX_TWIS3 if HAS_HW_NRF_TWIS3
|
||||
select NRFX_TWIS20 if HAS_HW_NRF_TWIS20
|
||||
select NRFX_TWIS21 if HAS_HW_NRF_TWIS21
|
||||
select NRFX_TWIS22 if HAS_HW_NRF_TWIS22
|
||||
select NRFX_TWIS30 if HAS_HW_NRF_TWIS30
|
||||
select NRFX_TWIS130 if HAS_HW_NRF_TWIS130
|
||||
select NRFX_TWIS131 if HAS_HW_NRF_TWIS131
|
||||
select NRFX_TWIS133 if HAS_HW_NRF_TWIS133
|
||||
select NRFX_TWIS134 if HAS_HW_NRF_TWIS134
|
||||
select NRFX_TWIS135 if HAS_HW_NRF_TWIS135
|
||||
select NRFX_TWIS136 if HAS_HW_NRF_TWIS136
|
||||
select NRFX_TWIS137 if HAS_HW_NRF_TWIS137
|
||||
|
||||
if I2C_NRFX_TWIS
|
||||
|
||||
config I2C_NRFX_TWIS_BUF_SIZE
|
||||
int "DMA buffer size in bytes"
|
||||
default 64
|
||||
|
||||
endif # I2C_NRFX_TWIS
|
||||
|
||||
endif # I2C_NRFX
|
||||
|
|
|
|||
395
drivers/i2c/i2c_nrfx_twis.c
Normal file
395
drivers/i2c/i2c_nrfx_twis.c
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,39 +1,46 @@
|
|||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Nordic nRF family TWIS (TWI slave with EasyDMA).
|
||||
Nordic nRF family TWIS
|
||||
|
||||
Note: for Zephyr users, the I2C slave API is not available for
|
||||
these devices. See this issue for more details and a HAL-based
|
||||
workaround:
|
||||
The TWIS peripheral is an I2C controller which supports the I2C
|
||||
peripheral role, and EasyDMA. TWIS shares resources with TWIM and TWI,
|
||||
only one of them can be enabled for each peripheral instance.
|
||||
Overwrite the compatible of the i2c node to select between TWIM/TWI
|
||||
and TWIS, along with the pinctrl instances to select between TWIM/TWI
|
||||
and TWIS pin functions.
|
||||
|
||||
https://github.com/zephyrproject-rtos/zephyr/issues/21445
|
||||
Example:
|
||||
|
||||
This binding can be used for nodes which can represent TWIS
|
||||
peripherals. A single SoC peripheral ID is often associated with
|
||||
multiple I2C peripherals, like a TWIM and a TWIS. You can choose
|
||||
TWIS by setting the node's "compatible" to "nordic,nrf-twis"
|
||||
and its "status" to "okay", e.g. using an overlay file like this:
|
||||
|
||||
/* This is for TWIS0 -- change to "i2c1" for TWIS1, etc. */
|
||||
&i2c0 {
|
||||
compatible = "nordic,nrf-twis";
|
||||
status = "okay";
|
||||
/* other property settings can go here */
|
||||
&pinctrl {
|
||||
i2c2_default: i2c2_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIS_SDA, 0, 26)>,
|
||||
<NRF_PSEL(TWIS_SCL, 0, 25)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
This works on any supported SoC, for all TWIS instances.
|
||||
i2c2_sleep: i2c2_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIS_SDA, 0, 26)>,
|
||||
<NRF_PSEL(TWIS_SCL, 0, 25)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c2 {
|
||||
compatible = "nordic,nrf-twis";
|
||||
pinctrl-0 = <&i2c2_default>;
|
||||
pinctrl-1 = <&i2c2_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
compatible: "nordic,nrf-twis"
|
||||
|
||||
include: ["nordic,nrf-twi-common.yaml", "memory-region.yaml"]
|
||||
|
||||
properties:
|
||||
address-0:
|
||||
type: int
|
||||
description: TWI slave address 0
|
||||
|
||||
address-1:
|
||||
type: int
|
||||
description: TWI slave address 1
|
||||
include:
|
||||
- "nordic,nrf-twi-common.yaml"
|
||||
- "memory-region.yaml"
|
||||
|
|
|
|||
Loading…
Reference in a new issue