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