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
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
|
|
|