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.
 
 
 
 
 
 

392 lines
13 KiB

/*
* Copyright (c) 2025 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rx_i2c
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/pinctrl.h>
#include <errno.h>
#include <r_riic_rx_if.h>
#include <r_riic_rx_private.h>
#include <zephyr/logging/log.h>
#include <soc.h>
LOG_MODULE_REGISTER(i2c_renesas_rx, CONFIG_I2C_LOG_LEVEL);
struct i2c_rx_config {
const struct pinctrl_dev_config *pcfg;
void (*irq_config_func)(const struct device *dev);
void (*riic_eei_sub)(void);
void (*riic_txi_sub)(void);
void (*riic_rxi_sub)(void);
void (*riic_tei_sub)(void);
};
struct i2c_rx_data {
riic_info_t rdp_info;
struct k_sem bus_lock;
struct k_sem bus_sync;
uint32_t dev_config;
i2c_callback_t user_callback;
void *user_data;
bool skip_callback;
};
static void riic_eei_isr(const struct device *dev)
{
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
const struct i2c_rx_config *config = dev->config;
riic_return_t rdp_ret;
riic_info_t iic_info_m;
riic_mcu_status_t iic_status;
iic_info_m.ch_no = data->rdp_info.ch_no;
rdp_ret = R_RIIC_GetStatus(&iic_info_m, &iic_status);
if (rdp_ret == RIIC_SUCCESS) {
if (iic_status.BIT.SP) {
k_sem_give(&data->bus_sync);
if ((data->user_callback != NULL) && !data->skip_callback) {
data->user_callback(dev, 0, data->user_data);
}
}
if (iic_status.BIT.TMO) {
k_sem_give(&data->bus_sync);
if ((data->user_callback != NULL) && !data->skip_callback) {
data->user_callback(dev, -ETIME, data->user_data);
}
}
}
config->riic_eei_sub();
}
static void riic_rxi_isr(const struct device *dev)
{
const struct i2c_rx_config *config = dev->config;
config->riic_rxi_sub();
}
static void riic_txi_isr(const struct device *dev)
{
const struct i2c_rx_config *config = dev->config;
config->riic_txi_sub();
}
static void riic_tei_isr(const struct device *dev)
{
const struct i2c_rx_config *config = dev->config;
config->riic_tei_sub();
}
static void rdp_callback(void)
{
/* Do nothing */
}
static void setup_rdp_info(struct i2c_rx_data *data, uint8_t *buf1, size_t len1, uint8_t *buf2,
size_t len2, uint16_t *addr)
{
data->rdp_info.cnt1st = len1;
data->rdp_info.cnt2nd = len2;
data->rdp_info.p_data1st = buf1 ? buf1 : FIT_NO_PTR;
data->rdp_info.p_data2nd = buf2 ? buf2 : FIT_NO_PTR;
data->rdp_info.p_slv_adr = (uint8_t *)addr;
}
static int run_rx_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr, bool async)
{
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
riic_return_t rdp_ret;
int ret;
ret = k_sem_take(&data->bus_lock, async ? K_NO_WAIT : K_FOREVER);
if (ret != 0) {
return -EBUSY;
}
k_sem_reset(&data->bus_sync);
if (addr == 0x00) {
/* Enter transmission pattern 4 */
LOG_DBG("RDP RX I2C master transmit pattern 4\n");
setup_rdp_info(data, NULL, 0, NULL, 0, NULL);
rdp_ret = R_RIIC_MasterSend(&data->rdp_info);
goto transfer_blocking;
}
if (num_msgs == 1) {
if (msgs[0].flags & I2C_MSG_READ) {
/* Enter master reception pattern 1 */
LOG_DBG("RDP RX I2C master reception pattern 1\n");
setup_rdp_info(data, NULL, 0, msgs[0].buf, msgs[0].len, &addr);
rdp_ret = R_RIIC_MasterReceive(&data->rdp_info);
goto transfer_blocking;
} else {
/* Enter master transmission pattern 2/3 */
LOG_DBG("RDP RX I2C master transmit pattern 2/3\n");
setup_rdp_info(data, NULL, 0, msgs[0].len ? msgs[0].buf : NULL, msgs[0].len,
&addr);
rdp_ret = R_RIIC_MasterSend(&data->rdp_info);
goto transfer_blocking;
}
} else if (num_msgs == 2) {
if (msgs[0].flags & I2C_MSG_READ) {
goto unsupport_pattern;
}
if (msgs[1].flags & I2C_MSG_READ) {
/* Enter master reception pattern 2 */
LOG_DBG("RDP RX I2C master reception pattern 2\n");
setup_rdp_info(data, msgs[0].buf, msgs[0].len, msgs[1].buf, msgs[1].len,
&addr);
rdp_ret = R_RIIC_MasterReceive(&data->rdp_info);
goto transfer_blocking;
} else {
/* Enter master transmission pattern 1 */
LOG_DBG("RDP RX I2C master transmit pattern 1\n");
setup_rdp_info(data, msgs[0].buf, msgs[0].len, msgs[1].buf, msgs[1].len,
&addr);
rdp_ret = R_RIIC_MasterSend(&data->rdp_info);
goto transfer_blocking;
}
}
unsupport_pattern:
/* Unsupport pattern, emit each fragment as a distinct transaction */
LOG_DBG("%s: \"Not a generic pattern ...\" !\n", __func__);
for (uint8_t i = 0; i < num_msgs; i++) {
if (msgs[i].flags & I2C_MSG_READ) {
/* Enter master reception pattern 1 */
LOG_DBG("RDP RX I2C master reception pattern 1\n");
setup_rdp_info(data, NULL, 0, msgs[i].buf, msgs[i].len, &addr);
rdp_ret = R_RIIC_MasterReceive(&data->rdp_info);
} else {
/* Enter master transmission pattern 2 */
LOG_DBG("RDP RX I2C master transmit pattern 2/3\n");
setup_rdp_info(data, NULL, 0, (msgs[i].len) ? msgs[i].buf : NULL,
msgs[i].len, &addr);
rdp_ret = R_RIIC_MasterSend(&data->rdp_info);
}
if (i == num_msgs - 1) {
data->skip_callback = false; /* Invoke callback in the last message */
}
if (!rdp_ret) {
k_sem_take(&data->bus_sync, K_FOREVER);
k_sem_reset(&data->bus_sync);
} else {
break;
}
}
goto transfer_exit;
transfer_blocking:
if (!rdp_ret && !async) {
data->skip_callback = false; /* Invoke callback */
k_sem_take(&data->bus_sync, K_FOREVER);
}
transfer_exit:
k_sem_give(&data->bus_lock);
if (!rdp_ret) {
return 0;
}
return -EIO;
}
static int i2c_rx_init(const struct device *dev)
{
const struct i2c_rx_config *config = dev->config;
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
int ret = 0;
/* Setup pin control */
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret) {
LOG_ERR("Pin control config failed.");
return ret;
}
/* Init kernal object */
k_sem_init(&data->bus_lock, 1, 1);
k_sem_init(&data->bus_sync, 0, 1);
/* Open Device */
ret = R_RIIC_Open(&data->rdp_info);
if (ret) {
LOG_ERR("Open i2c master failed.");
return -EIO;
}
/* Connect and enable Interrupts in zephyr */
config->irq_config_func(dev);
return 0;
}
static int i2c_rx_configure(const struct device *dev, uint32_t dev_config)
{
/* Setup bitrate */
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
uint16_t speed; /* bitrate in kbps */
int ret;
/* Validate input */
if (!(dev_config & I2C_MODE_CONTROLLER)) {
LOG_ERR("Only I2C Master mode supported.");
return -ENOTSUP;
}
if (dev_config & I2C_ADDR_10_BITS) {
LOG_ERR("Only address 7 bits supported.");
return -ENOTSUP;
}
switch (I2C_SPEED_GET(dev_config)) {
case I2C_SPEED_STANDARD:
speed = 100;
break;
case I2C_SPEED_FAST:
speed = 400;
break;
default:
LOG_ERR("Unsupported speed: %d", I2C_SPEED_GET(dev_config));
return -ENOTSUP;
}
k_sem_take(&data->bus_lock, K_FOREVER);
ret = riic_bps_calc(&data->rdp_info, speed);
k_sem_give(&data->bus_lock);
/* Validate result */
if (ret) {
return -EINVAL;
}
data->dev_config = dev_config;
return 0;
}
static int i2c_rx_get_config(const struct device *dev, uint32_t *dev_config)
{
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
*dev_config = data->dev_config;
return 0;
}
static int i2c_rx_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr)
{
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
data->user_callback = NULL;
data->user_data = NULL;
data->skip_callback = true;
/* Transfer */
return run_rx_transfer(dev, msgs, num_msgs, addr, false);
}
#ifdef CONFIG_I2C_RENESAS_RX_CALLBACK
static int i2c_rx_transfer_cb(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr, i2c_callback_t cb, void *userdata)
{
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
data->user_callback = cb;
data->user_data = userdata;
data->skip_callback = true;
/* Transfer */
return run_rx_transfer(dev, msgs, num_msgs, addr, true);
}
#endif
static int i2c_rx_recover_bus(const struct device *dev)
{
LOG_DBG("Recover I2C Bus\n");
struct i2c_rx_data *data = (struct i2c_rx_data *const)dev->data;
riic_return_t rdp_ret = RIIC_SUCCESS;
k_sem_take(&data->bus_lock, K_FOREVER);
rdp_ret |= R_RIIC_Control(&data->rdp_info, RIIC_GEN_START_CON);
rdp_ret |= R_RIIC_Control(&data->rdp_info, RIIC_GEN_STOP_CON);
k_sem_give(&data->bus_lock);
if (rdp_ret != 0) {
return -EIO;
}
return 0;
}
static DEVICE_API(i2c, i2c_rx_driver_api) = {
.configure = (i2c_api_configure_t)i2c_rx_configure,
.get_config = (i2c_api_get_config_t)i2c_rx_get_config,
.transfer = (i2c_api_full_io_t)i2c_rx_transfer,
#ifdef CONFIG_I2C_RENESAS_RX_CALLBACK
.transfer_cb = (i2c_api_transfer_cb_t)i2c_rx_transfer_cb,
#endif
.recover_bus = (i2c_api_recover_bus_t)i2c_rx_recover_bus,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_RX_RIIC_INIT(index) \
\
PINCTRL_DT_INST_DEFINE(index); \
\
static void i2c_rx_irq_config_func##index(const struct device *dev) \
{ \
\
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, eei, irq), \
DT_INST_IRQ_BY_NAME(index, eei, priority), riic_eei_isr, \
DEVICE_DT_INST_GET(index), 0); \
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, rxi, irq), \
DT_INST_IRQ_BY_NAME(index, rxi, priority), riic_rxi_isr, \
DEVICE_DT_INST_GET(index), 0); \
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, txi, irq), \
DT_INST_IRQ_BY_NAME(index, txi, priority), riic_txi_isr, \
DEVICE_DT_INST_GET(index), 0); \
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, tei, irq), \
DT_INST_IRQ_BY_NAME(index, tei, priority), riic_tei_isr, \
DEVICE_DT_INST_GET(index), 0); \
\
irq_enable(DT_INST_IRQ_BY_NAME(index, eei, irq)); \
irq_enable(DT_INST_IRQ_BY_NAME(index, rxi, irq)); \
irq_enable(DT_INST_IRQ_BY_NAME(index, txi, irq)); \
irq_enable(DT_INST_IRQ_BY_NAME(index, tei, irq)); \
}; \
\
static const struct i2c_rx_config i2c_rx_config_##index = { \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
.irq_config_func = i2c_rx_irq_config_func##index, \
.riic_eei_sub = riic##index##_eei_sub, \
.riic_rxi_sub = riic##index##_rxi_sub, \
.riic_txi_sub = riic##index##_txi_sub, \
.riic_tei_sub = riic##index##_tei_sub, \
}; \
\
static struct i2c_rx_data i2c_rx_data_##index = { \
.rdp_info = \
{ \
.dev_sts = RIIC_NO_INIT, \
.ch_no = DT_INST_PROP(index, channel), \
.callbackfunc = rdp_callback, \
}, \
}; \
\
I2C_DEVICE_DT_INST_DEFINE(index, i2c_rx_init, NULL, &i2c_rx_data_##index, \
&i2c_rx_config_##index, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \
&i2c_rx_driver_api);
DT_INST_FOREACH_STATUS_OKAY(I2C_RX_RIIC_INIT)