662 lines
19 KiB
C
662 lines
19 KiB
C
/*
|
|
* Copyright (c) 2022 Renesas Electronics Corporation and/or its affiliates
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_smartbond_i2c
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/drivers/i2c.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <DA1469xAB.h>
|
|
#include <da1469x_pd.h>
|
|
#include <zephyr/pm/device.h>
|
|
#include <zephyr/pm/policy.h>
|
|
#include <zephyr/pm/device_runtime.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(i2c_smartbond);
|
|
|
|
#include "i2c-priv.h"
|
|
|
|
struct i2c_smartbond_cfg {
|
|
I2C_Type *regs;
|
|
int periph_clock_config;
|
|
const struct pinctrl_dev_config *pcfg;
|
|
uint32_t bitrate;
|
|
};
|
|
|
|
struct i2c_smartbond_data {
|
|
struct k_spinlock lock;
|
|
struct i2c_msg *msgs;
|
|
uint8_t num_msgs;
|
|
uint32_t transmit_cnt, receive_cnt;
|
|
i2c_callback_t cb;
|
|
void *userdata;
|
|
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
|
|
ATOMIC_DEFINE(pm_policy_state_flag, 1);
|
|
#endif
|
|
};
|
|
|
|
static inline void i2c_smartbond_pm_policy_state_lock_get(struct i2c_smartbond_data *data)
|
|
{
|
|
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
|
|
if (atomic_test_and_set_bit(data->pm_policy_state_flag, 0) == 0) {
|
|
/*
|
|
* Prevent the SoC from etering the normal sleep state as PDC does not support
|
|
* waking up the application core following I2C events.
|
|
*/
|
|
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static inline void i2c_smartbond_pm_policy_state_lock_put(struct i2c_smartbond_data *data)
|
|
{
|
|
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
|
|
if (atomic_test_and_clear_bit(data->pm_policy_state_flag, 0) == 1) {
|
|
/*
|
|
* Allow the SoC to enter the nornmal sleep state once I2C transactions are done.
|
|
*/
|
|
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static inline bool i2c_smartbond_is_idle(const struct device *dev)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
uint32_t mask = I2C_I2C_STATUS_REG_I2C_ACTIVITY_Msk |
|
|
I2C_I2C_STATUS_REG_RFNE_Msk |
|
|
I2C_I2C_STATUS_REG_TFE_Msk;
|
|
|
|
return ((config->regs->I2C_STATUS_REG & mask) == I2C_I2C_STATUS_REG_TFE_Msk);
|
|
}
|
|
|
|
static void i2c_smartbond_disable_when_inactive(const struct device *dev)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
|
|
if ((config->regs->I2C_ENABLE_REG & I2C_I2C_ENABLE_REG_I2C_EN_Msk)) {
|
|
while (!i2c_smartbond_is_idle(dev)) {
|
|
};
|
|
config->regs->I2C_ENABLE_REG &= ~I2C_I2C_ENABLE_REG_I2C_EN_Msk;
|
|
}
|
|
}
|
|
|
|
static int i2c_smartbond_configure(const struct device *dev, uint32_t dev_config)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = dev->data;
|
|
uint32_t con_reg = 0x0UL;
|
|
k_spinlock_key_t key;
|
|
|
|
/* Configure Speed (SCL frequency) */
|
|
switch (I2C_SPEED_GET(dev_config)) {
|
|
case I2C_SPEED_STANDARD:
|
|
con_reg |= 1UL << I2C_I2C_CON_REG_I2C_SPEED_Pos;
|
|
break;
|
|
case I2C_SPEED_FAST:
|
|
con_reg |= 2UL << I2C_I2C_CON_REG_I2C_SPEED_Pos;
|
|
break;
|
|
/* TODO: Currently 1 MHz, add switching to 96 MHz PLL sys_clk to support 3.4 Mbit/s */
|
|
/*
|
|
* case I2C_SPEED_HIGH:
|
|
* con_reg |= 3UL << I2C_I2C_CON_REG_I2C_SPEED_Pos;
|
|
* break;
|
|
*/
|
|
default:
|
|
LOG_ERR("Speed not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Configure Mode */
|
|
if ((dev_config & I2C_MODE_CONTROLLER) == I2C_MODE_CONTROLLER) {
|
|
con_reg |=
|
|
I2C_I2C_CON_REG_I2C_MASTER_MODE_Msk | I2C_I2C_CON_REG_I2C_SLAVE_DISABLE_Msk;
|
|
} else {
|
|
LOG_ERR("Only I2C Controller mode supported");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Enable sending RESTART as master */
|
|
con_reg |= I2C_I2C_CON_REG_I2C_RESTART_EN_Msk;
|
|
|
|
key = k_spin_lock(&data->lock);
|
|
|
|
i2c_smartbond_disable_when_inactive(dev);
|
|
|
|
/* Write control register*/
|
|
config->regs->I2C_CON_REG = con_reg;
|
|
|
|
/* Reset interrupt mask */
|
|
config->regs->I2C_INTR_MASK_REG = 0x0000U;
|
|
|
|
config->regs->I2C_ENABLE_REG |= I2C_I2C_ENABLE_REG_I2C_EN_Msk;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2c_smartbond_get_config(const struct device *dev, uint32_t *dev_config)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = data = dev->data;
|
|
uint32_t reg;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
/* Read the value of the control register */
|
|
reg = config->regs->I2C_CON_REG;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
*dev_config = 0UL;
|
|
|
|
/* Check if I2C is in controller or target mode */
|
|
if ((reg & I2C_I2C_CON_REG_I2C_MASTER_MODE_Msk) &&
|
|
(reg & I2C_I2C_CON_REG_I2C_SLAVE_DISABLE_Msk)) {
|
|
*dev_config |= I2C_MODE_CONTROLLER;
|
|
} else if (!(reg & I2C_I2C_CON_REG_I2C_MASTER_MODE_Msk) &&
|
|
!(reg & I2C_I2C_CON_REG_I2C_SLAVE_DISABLE_Msk)) {
|
|
*dev_config &= ~I2C_MODE_CONTROLLER;
|
|
} else {
|
|
return -EIO;
|
|
}
|
|
|
|
/* Get the operating speed */
|
|
switch ((reg & I2C_I2C_CON_REG_I2C_SPEED_Msk) >> I2C_I2C_CON_REG_I2C_SPEED_Pos) {
|
|
case 1UL:
|
|
*dev_config |= I2C_SPEED_SET(I2C_SPEED_STANDARD);
|
|
break;
|
|
case 2UL:
|
|
*dev_config |= I2C_SPEED_SET(I2C_SPEED_FAST);
|
|
break;
|
|
case 3UL:
|
|
*dev_config |= I2C_SPEED_SET(I2C_SPEED_HIGH);
|
|
break;
|
|
default:
|
|
return -ERANGE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void i2c_smartbond_set_target_address(const struct i2c_smartbond_cfg *const config,
|
|
struct i2c_smartbond_data *data,
|
|
const struct i2c_msg *const msg, uint16_t addr)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
/* Disable I2C Controller */
|
|
config->regs->I2C_ENABLE_REG &= ~I2C_I2C_ENABLE_REG_I2C_EN_Msk;
|
|
/* Configure addressing mode*/
|
|
if (msg->flags & I2C_MSG_ADDR_10_BITS) {
|
|
config->regs->I2C_CON_REG |= I2C_I2C_CON_REG_I2C_10BITADDR_MASTER_Msk;
|
|
} else {
|
|
config->regs->I2C_CON_REG &= ~(I2C_I2C_CON_REG_I2C_10BITADDR_MASTER_Msk);
|
|
}
|
|
|
|
/* Change the Target Address */
|
|
config->regs->I2C_TAR_REG = ((config->regs->I2C_TAR_REG & ~I2C_I2C_TAR_REG_IC_TAR_Msk) |
|
|
(addr & I2C_I2C_TAR_REG_IC_TAR_Msk));
|
|
/* Enable again the I2C to use the new address */
|
|
config->regs->I2C_ENABLE_REG |= I2C_I2C_ENABLE_REG_I2C_EN_Msk;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static inline int i2c_smartbond_set_msg_flags(struct i2c_msg *msgs, uint8_t num_msgs)
|
|
{
|
|
struct i2c_msg *current, *next;
|
|
|
|
current = msgs;
|
|
for (uint8_t i = 1; i <= num_msgs; i++) {
|
|
if (i < num_msgs) {
|
|
next = current + 1;
|
|
if ((current->flags & I2C_MSG_RW_MASK) != (next->flags & I2C_MSG_RW_MASK)) {
|
|
next->flags |= I2C_MSG_RESTART;
|
|
}
|
|
if (current->flags & I2C_MSG_STOP) {
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
current->flags |= I2C_MSG_STOP;
|
|
}
|
|
current++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int i2c_smartbond_prep_transfer(const struct device *dev, struct i2c_msg *msgs,
|
|
uint8_t num_msgs, uint16_t addr)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = dev->data;
|
|
int ret = 0;
|
|
|
|
ret = i2c_smartbond_set_msg_flags(msgs, num_msgs);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
i2c_smartbond_set_target_address(config, data, msgs, addr);
|
|
|
|
data->msgs = msgs;
|
|
data->num_msgs = num_msgs;
|
|
data->transmit_cnt = 0;
|
|
data->receive_cnt = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int i2c_smartbond_tx(const struct i2c_smartbond_cfg *const config,
|
|
struct i2c_smartbond_data *data)
|
|
{
|
|
k_spinlock_key_t key;
|
|
const bool rw = ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ);
|
|
int ret = 0;
|
|
|
|
if (!data->msgs->buf || data->msgs->len == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
key = k_spin_lock(&data->lock);
|
|
|
|
/* Transmits data or read commands with correct flags */
|
|
while ((data->transmit_cnt < data->msgs->len) &&
|
|
(config->regs->I2C_STATUS_REG & I2C_I2C_STATUS_REG_TFNF_Msk)) {
|
|
config->regs->I2C_DATA_CMD_REG =
|
|
(rw ? I2C_I2C_DATA_CMD_REG_I2C_CMD_Msk
|
|
: (data->msgs->buf[data->transmit_cnt] &
|
|
I2C_I2C_DATA_CMD_REG_I2C_DAT_Msk)) |
|
|
((data->transmit_cnt == 0) && (data->msgs->flags & I2C_MSG_RESTART)
|
|
? I2C_I2C_DATA_CMD_REG_I2C_RESTART_Msk
|
|
: 0) |
|
|
((data->transmit_cnt == (data->msgs->len - 1)) &&
|
|
(data->msgs->flags & I2C_MSG_STOP)
|
|
? I2C_I2C_DATA_CMD_REG_I2C_STOP_Msk
|
|
: 0);
|
|
data->transmit_cnt++;
|
|
|
|
/* Return IO error if any of the abort flags are set */
|
|
if (config->regs->I2C_TX_ABRT_SOURCE_REG & 0x1FFFF) {
|
|
ret = -EIO;
|
|
}
|
|
}
|
|
|
|
while (!(config->regs->I2C_STATUS_REG & I2C_I2C_STATUS_REG_TFE_Msk)) {
|
|
};
|
|
|
|
if (config->regs->I2C_TX_ABRT_SOURCE_REG & 0x1FFFF) {
|
|
ret = -EIO;
|
|
}
|
|
|
|
if (ret) {
|
|
(void)config->regs->I2C_CLR_TX_ABRT_REG;
|
|
}
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline int i2c_smartbond_rx(const struct i2c_smartbond_cfg *const config,
|
|
struct i2c_smartbond_data *data)
|
|
{
|
|
k_spinlock_key_t key;
|
|
int ret = 0;
|
|
|
|
if (!data->msgs->buf || data->msgs->len == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
key = k_spin_lock(&data->lock);
|
|
|
|
/* Reads the data register until fifo is empty */
|
|
while ((data->receive_cnt < data->transmit_cnt) &&
|
|
(config->regs->I2C_STATUS_REG & I2C2_I2C2_STATUS_REG_RFNE_Msk)) {
|
|
data->msgs->buf[data->receive_cnt] =
|
|
config->regs->I2C_DATA_CMD_REG & I2C_I2C_DATA_CMD_REG_I2C_DAT_Msk;
|
|
data->receive_cnt++;
|
|
}
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int i2c_smartbond_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
|
|
uint16_t addr)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = dev->data;
|
|
int ret = 0;
|
|
|
|
while (!i2c_smartbond_is_idle(dev)) {
|
|
};
|
|
|
|
ret = i2c_smartbond_prep_transfer(dev, msgs, num_msgs, addr);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
i2c_smartbond_pm_policy_state_lock_get(data);
|
|
|
|
for (; data->num_msgs > 0; data->num_msgs--, data->msgs++) {
|
|
data->transmit_cnt = 0;
|
|
data->receive_cnt = 0;
|
|
if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
|
|
/* Repeating transmit and receives until all data has been read */
|
|
while (data->receive_cnt < data->msgs->len) {
|
|
/* Transmit read commands */
|
|
ret = i2c_smartbond_tx(config, data);
|
|
if (ret < 0) {
|
|
goto finish;
|
|
}
|
|
/* Read received data */
|
|
ret = i2c_smartbond_rx(config, data);
|
|
if (ret < 0) {
|
|
goto finish;
|
|
}
|
|
}
|
|
} else {
|
|
while (data->transmit_cnt < data->msgs->len) {
|
|
/* Transmit data */
|
|
ret = i2c_smartbond_tx(config, data);
|
|
if (ret < 0) {
|
|
goto finish;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finish:
|
|
i2c_smartbond_pm_policy_state_lock_put(data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_I2C_CALLBACK
|
|
|
|
static int i2c_smartbond_enable_msg_interrupts(const struct i2c_smartbond_cfg *const config,
|
|
struct i2c_smartbond_data *data)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
if ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
|
|
uint32_t remaining = data->msgs->len - data->receive_cnt;
|
|
uint32_t tx_space = 32 - config->regs->I2C_TXFLR_REG;
|
|
uint32_t rx_tl = ((remaining < tx_space) ? remaining : tx_space) - 1;
|
|
|
|
config->regs->I2C_RX_TL_REG = rx_tl & I2C_I2C_RX_TL_REG_RX_TL_Msk;
|
|
config->regs->I2C_INTR_MASK_REG |= I2C_I2C_INTR_MASK_REG_M_RX_FULL_Msk;
|
|
} else {
|
|
config->regs->I2C_INTR_MASK_REG &= ~I2C_I2C_INTR_MASK_REG_M_RX_FULL_Msk;
|
|
}
|
|
|
|
config->regs->I2C_TX_TL_REG = 0UL;
|
|
config->regs->I2C_INTR_MASK_REG |= I2C_I2C_INTR_MASK_REG_M_TX_EMPTY_Msk;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2c_smartbond_transfer_cb(const struct device *dev, struct i2c_msg *msgs,
|
|
uint8_t num_msgs, uint16_t addr, i2c_callback_t cb,
|
|
void *userdata)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = dev->data;
|
|
int ret = 0;
|
|
|
|
if (cb == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (data->cb != NULL) {
|
|
return -EWOULDBLOCK;
|
|
}
|
|
|
|
data->cb = cb;
|
|
data->userdata = userdata;
|
|
|
|
ret = i2c_smartbond_prep_transfer(dev, msgs, num_msgs, addr);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
i2c_smartbond_pm_policy_state_lock_get(data);
|
|
|
|
i2c_smartbond_enable_msg_interrupts(config, data);
|
|
|
|
LOG_INF("async transfer started");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void isr_tx(const struct i2c_smartbond_cfg *config, struct i2c_smartbond_data *data)
|
|
{
|
|
|
|
const bool rw = ((data->msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ);
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
while ((data->transmit_cnt < data->msgs->len) &&
|
|
(config->regs->I2C_STATUS_REG & I2C_I2C_STATUS_REG_TFNF_Msk)) {
|
|
config->regs->I2C_DATA_CMD_REG =
|
|
(rw ? I2C_I2C_DATA_CMD_REG_I2C_CMD_Msk
|
|
: (data->msgs->buf[data->transmit_cnt] &
|
|
I2C_I2C_DATA_CMD_REG_I2C_DAT_Msk)) |
|
|
((data->transmit_cnt == 0) && (data->msgs->flags & I2C_MSG_RESTART)
|
|
? I2C_I2C_DATA_CMD_REG_I2C_RESTART_Msk
|
|
: 0) |
|
|
((data->transmit_cnt == (data->msgs->len - 1)) &&
|
|
(data->msgs->flags & I2C_MSG_STOP)
|
|
? I2C_I2C_DATA_CMD_REG_I2C_STOP_Msk
|
|
: 0);
|
|
data->transmit_cnt++;
|
|
}
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static inline void isr_rx(const struct i2c_smartbond_cfg *config, struct i2c_smartbond_data *data)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
while ((data->receive_cnt < data->transmit_cnt) &&
|
|
(config->regs->I2C_STATUS_REG & I2C2_I2C2_STATUS_REG_RFNE_Msk)) {
|
|
data->msgs->buf[data->receive_cnt] =
|
|
config->regs->I2C_DATA_CMD_REG & I2C_I2C_DATA_CMD_REG_I2C_DAT_Msk;
|
|
data->receive_cnt++;
|
|
}
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static inline void i2c_smartbond_async_msg_done(const struct device *dev)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = dev->data;
|
|
|
|
data->num_msgs--;
|
|
if (data->num_msgs > 0) {
|
|
data->msgs++;
|
|
data->transmit_cnt = 0;
|
|
data->receive_cnt = 0;
|
|
i2c_smartbond_enable_msg_interrupts(config, data);
|
|
} else {
|
|
i2c_callback_t cb = data->cb;
|
|
|
|
data->msgs = NULL;
|
|
data->cb = NULL;
|
|
LOG_INF("async transfer finished");
|
|
cb(dev, 0, data->userdata);
|
|
i2c_smartbond_pm_policy_state_lock_put(data);
|
|
}
|
|
}
|
|
|
|
static void i2c_smartbond_isr(const struct device *dev)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
struct i2c_smartbond_data *data = dev->data;
|
|
uint32_t flags = config->regs->I2C_INTR_STAT_REG;
|
|
|
|
if (flags & I2C_I2C_INTR_STAT_REG_R_TX_EMPTY_Msk) {
|
|
isr_tx(config, data);
|
|
if (data->transmit_cnt == data->msgs->len) {
|
|
config->regs->I2C_INTR_MASK_REG &= ~I2C_I2C_INTR_MASK_REG_M_TX_EMPTY_Msk;
|
|
if ((data->msgs->flags & I2C_MSG_RW_MASK) != I2C_MSG_READ) {
|
|
i2c_smartbond_async_msg_done(dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & I2C_I2C_INTR_STAT_REG_R_RX_FULL_Msk) {
|
|
isr_rx(config, data);
|
|
if (data->receive_cnt == data->msgs->len) {
|
|
config->regs->I2C_INTR_MASK_REG &= ~I2C_I2C_INTR_MASK_REG_M_RX_FULL_Msk;
|
|
i2c_smartbond_async_msg_done(dev);
|
|
} else {
|
|
uint32_t remaining = data->msgs->len - data->receive_cnt;
|
|
uint32_t tx_space = 32 - config->regs->I2C_TXFLR_REG;
|
|
uint32_t rx_tl = ((remaining < tx_space) ? remaining : tx_space) - 1;
|
|
|
|
config->regs->I2C_RX_TL_REG = rx_tl & I2C_I2C_RX_TL_REG_RX_TL_Msk;
|
|
config->regs->I2C_INTR_MASK_REG |= I2C_I2C_INTR_MASK_REG_M_RX_FULL_Msk;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define I2C_SMARTBOND_CONFIGURE(id) \
|
|
IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), i2c_smartbond_isr, \
|
|
DEVICE_DT_INST_GET(id), 0); \
|
|
irq_enable(DT_INST_IRQN(id));
|
|
#else
|
|
#define I2C_SMARTBOND_CONFIGURE(id)
|
|
#endif
|
|
|
|
static const struct i2c_driver_api i2c_smartbond_driver_api = {
|
|
.configure = i2c_smartbond_configure,
|
|
.get_config = i2c_smartbond_get_config,
|
|
.transfer = i2c_smartbond_transfer,
|
|
#ifdef CONFIG_I2C_CALLBACK
|
|
.transfer_cb = i2c_smartbond_transfer_cb,
|
|
#endif
|
|
};
|
|
|
|
static int i2c_smartbond_resume(const struct device *dev)
|
|
{
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
int err;
|
|
|
|
config->regs->I2C_ENABLE_REG &= ~I2C_I2C_ENABLE_REG_I2C_EN_Msk;
|
|
|
|
/* Reset I2C CLK_SEL */
|
|
CRG_COM->RESET_CLK_COM_REG = (config->periph_clock_config << 1);
|
|
/* Set I2C CLK ENABLE */
|
|
CRG_COM->SET_CLK_COM_REG = config->periph_clock_config;
|
|
|
|
err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (err < 0) {
|
|
LOG_ERR("Failed to configure I2C pins");
|
|
return err;
|
|
}
|
|
|
|
return i2c_smartbond_configure(dev,
|
|
I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
|
|
}
|
|
|
|
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
|
|
static int i2c_smartbond_suspend(const struct device *dev)
|
|
{
|
|
int ret;
|
|
const struct i2c_smartbond_cfg *config = dev->config;
|
|
|
|
/* Disable the I2C digital block */
|
|
config->regs->I2C_ENABLE_REG &= ~I2C_I2C_ENABLE_REG_I2C_EN_Msk;
|
|
/* Gate I2C clocking */
|
|
CRG_COM->RESET_CLK_COM_REG = config->periph_clock_config;
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
|
|
if (ret < 0) {
|
|
LOG_WRN("Fialid to configure the I2C pins to inactive state");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int i2c_smartbond_pm_action(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
/*
|
|
* Although the GPIO driver should already be initialized, make sure PD_COM
|
|
* is up and running before accessing the I2C block.
|
|
*/
|
|
da1469x_pd_acquire(MCU_PD_DOMAIN_COM);
|
|
ret = i2c_smartbond_resume(dev);
|
|
break;
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
ret = i2c_smartbond_suspend(dev);
|
|
/*
|
|
* Once the I2C block is turned off its power domain can
|
|
* be released, as well.
|
|
*/
|
|
da1469x_pd_release(MCU_PD_DOMAIN_COM);
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
static int i2c_smartbond_init(const struct device *dev)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef CONFIG_PM_DEVICE_RUNTIME
|
|
/* Make sure device state is marked as suspended */
|
|
pm_device_init_suspended(dev);
|
|
|
|
ret = pm_device_runtime_enable(dev);
|
|
#else
|
|
da1469x_pd_acquire(MCU_PD_DOMAIN_COM);
|
|
ret = i2c_smartbond_resume(dev);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define I2C_SMARTBOND_DEVICE(id) \
|
|
PM_DEVICE_DT_INST_DEFINE(id, i2c_smartbond_pm_action); \
|
|
PINCTRL_DT_INST_DEFINE(id); \
|
|
static const struct i2c_smartbond_cfg i2c_smartbond_##id##_cfg = { \
|
|
.regs = (I2C_Type *)DT_INST_REG_ADDR(id), \
|
|
.periph_clock_config = DT_INST_PROP(id, periph_clock_config), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \
|
|
.bitrate = DT_INST_PROP_OR(id, clock_frequency, 100000)}; \
|
|
static struct i2c_smartbond_data i2c_smartbond_##id##_data = {.msgs = NULL, .cb = NULL}; \
|
|
static int i2c_smartbond_##id##_init(const struct device *dev) \
|
|
{ \
|
|
int ret = i2c_smartbond_init(dev); \
|
|
I2C_SMARTBOND_CONFIGURE(id); \
|
|
return ret; \
|
|
} \
|
|
I2C_DEVICE_DT_INST_DEFINE(id, i2c_smartbond_##id##_init, PM_DEVICE_DT_INST_GET(id), \
|
|
&i2c_smartbond_##id##_data, \
|
|
&i2c_smartbond_##id##_cfg, POST_KERNEL, \
|
|
CONFIG_I2C_INIT_PRIORITY, &i2c_smartbond_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(I2C_SMARTBOND_DEVICE)
|