drivers: i2c: initial support for i2c sci-b driver on Renesas RA family
First commit to add support for Renesas RA i2c sci-b driver Signed-off-by: Khoa Tran <khoa.tran.yj@bp.renesas.com> Signed-off-by: Thao Luong <thao.luong.uw@renesas.com>
This commit is contained in:
parent
459a078d22
commit
d0c0f1d036
5 changed files with 789 additions and 1 deletions
|
@ -53,6 +53,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_NXP_II2C i2c_nxp_ii2c.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_I2C_OMAP i2c_omap.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_RCAR i2c_rcar.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_RENESAS_RA_IIC i2c_renesas_ra_iic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_RENESAS_RA_SCI_B i2c_renesas_ra_sci_b.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_RENESAS_RZ_RIIC i2c_renesas_rz_riic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_RV32M1_LPI2C i2c_rv32m1_lpi2c.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_SAM0 i2c_sam0.c)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Renesas RA Family
|
||||
|
||||
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||
# Copyright (c) 2024-2025 Renesas Electronics Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config I2C_RENESAS_RA_IIC
|
||||
|
@ -11,3 +11,23 @@ config I2C_RENESAS_RA_IIC
|
|||
select PINCTRL
|
||||
help
|
||||
Enable Renesas RA I2C IIC Driver.
|
||||
|
||||
config I2C_RENESAS_RA_SCI_B
|
||||
bool "Renesas RA SCI-B I2C"
|
||||
default y
|
||||
depends on DT_HAS_RENESAS_RA_I2C_SCI_B_ENABLED
|
||||
select USE_RA_FSP_SCI_B_I2C
|
||||
select PINCTRL
|
||||
help
|
||||
Enable Renesas RA SCI-B I2C Driver.
|
||||
|
||||
if I2C_RENESAS_RA_SCI_B
|
||||
|
||||
config I2C_RENESAS_RA_SCI_B_DTC
|
||||
bool "DTC on Transmission and Reception"
|
||||
default y
|
||||
select USE_RA_FSP_DTC
|
||||
help
|
||||
Enable DTC on transmission and reception
|
||||
|
||||
endif
|
||||
|
|
728
drivers/i2c/i2c_renesas_ra_sci_b.c
Normal file
728
drivers/i2c/i2c_renesas_ra_sci_b.c
Normal file
|
@ -0,0 +1,728 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Renesas Electronics Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT renesas_ra_i2c_sci_b
|
||||
|
||||
#define MDDR_DISABLE 256
|
||||
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "r_sci_b_i2c.h"
|
||||
#ifdef CONFIG_I2C_RENESAS_RA_SCI_B_DTC
|
||||
#include "r_dtc.h"
|
||||
#endif
|
||||
|
||||
#include <soc.h>
|
||||
|
||||
LOG_MODULE_REGISTER(renesas_ra_i2c_sci_b, CONFIG_I2C_LOG_LEVEL);
|
||||
|
||||
#define I2C_MAX_MSG_LEN (1 << (sizeof(uint8_t) * 8))
|
||||
|
||||
typedef void (*init_func_t)(const struct device *dev);
|
||||
struct sci_b_i2c_config {
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
uint16_t sda_output_delay;
|
||||
};
|
||||
|
||||
struct sci_b_i2c_data {
|
||||
sci_b_i2c_instance_ctrl_t ctrl;
|
||||
i2c_master_cfg_t i2c_config;
|
||||
sci_b_i2c_extended_cfg_t ext_cfg;
|
||||
struct k_sem bus_lock;
|
||||
struct k_sem complete_sem;
|
||||
i2c_master_event_t event;
|
||||
uint32_t dev_config;
|
||||
|
||||
#ifdef CONFIG_I2C_CALLBACK
|
||||
uint16_t addr;
|
||||
uint32_t msg_idx;
|
||||
struct i2c_msg *msgs;
|
||||
uint32_t num_msgs;
|
||||
i2c_callback_t cb;
|
||||
void *p_context;
|
||||
#endif /* CONFIG_I2C_CALLBACK */
|
||||
|
||||
#ifdef CONFIG_I2C_RENESAS_RA_SCI_B_DTC
|
||||
/* RX */
|
||||
transfer_instance_t rx_transfer;
|
||||
transfer_info_t rx_transfer_info;
|
||||
transfer_cfg_t rx_transfer_cfg;
|
||||
dtc_instance_ctrl_t rx_transfer_ctrl;
|
||||
dtc_extended_cfg_t rx_transfer_cfg_extend;
|
||||
|
||||
/* TX */
|
||||
transfer_instance_t tx_transfer;
|
||||
transfer_info_t tx_transfer_info;
|
||||
transfer_cfg_t tx_transfer_cfg;
|
||||
dtc_instance_ctrl_t tx_transfer_ctrl;
|
||||
dtc_extended_cfg_t tx_transfer_cfg_extend;
|
||||
#endif /* CONFIG_I2C_RENESAS_RA_SCI_B_DTC */
|
||||
};
|
||||
|
||||
static void calc_sci_b_iic_clock_setting(const struct device *dev, const uint32_t fsp_i2c_rate,
|
||||
sci_b_i2c_clock_settings_t *clk_cfg);
|
||||
|
||||
/* FSP interruption handlers. */
|
||||
void sci_b_i2c_txi_isr(void);
|
||||
void sci_b_i2c_tei_isr(void);
|
||||
void sci_b_i2c_rxi_isr(void);
|
||||
|
||||
static int renesas_ra_sci_b_i2c_configure(const struct device *dev, uint32_t dev_config)
|
||||
{
|
||||
struct sci_b_i2c_data *data = (struct sci_b_i2c_data *const)dev->data;
|
||||
|
||||
if (!(dev_config & I2C_MODE_CONTROLLER)) {
|
||||
LOG_ERR("Only I2C Master mode supported.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (I2C_SPEED_GET(dev_config)) {
|
||||
case I2C_SPEED_STANDARD:
|
||||
data->i2c_config.rate = I2C_MASTER_RATE_STANDARD;
|
||||
break;
|
||||
case I2C_SPEED_FAST:
|
||||
data->i2c_config.rate = I2C_MASTER_RATE_FAST;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid I2C speed rate flag: %d", I2C_SPEED_GET(dev_config));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
calc_sci_b_iic_clock_setting(dev, data->i2c_config.rate, &data->ext_cfg.clock_settings);
|
||||
|
||||
R_SCI_B_I2C_Close(&data->ctrl);
|
||||
R_SCI_B_I2C_Open(&data->ctrl, &data->i2c_config);
|
||||
|
||||
/* save current devconfig. */
|
||||
data->dev_config = dev_config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int renesas_ra_sci_b_i2c_get_config(const struct device *dev, uint32_t *dev_config)
|
||||
{
|
||||
struct sci_b_i2c_data *data = (struct sci_b_i2c_data *const)dev->data;
|
||||
*dev_config = data->dev_config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OPERATION(msg) (((struct i2c_msg *)msg)->flags & I2C_MSG_RW_MASK)
|
||||
|
||||
static int renesas_ra_sci_b_i2c_transfer(const struct device *dev, struct i2c_msg *msgs,
|
||||
uint8_t num_msgs, uint16_t addr)
|
||||
{
|
||||
struct sci_b_i2c_data *data = (struct sci_b_i2c_data *const)dev->data;
|
||||
struct i2c_msg *current, *next;
|
||||
fsp_err_t fsp_err;
|
||||
int ret;
|
||||
|
||||
if (!num_msgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle i2c burst write, restructure message to be compatible with HAL*/
|
||||
if (num_msgs == 2) {
|
||||
if (msgs[0].len == 1U && !(msgs[0].flags & I2C_MSG_READ) &&
|
||||
!(msgs[1].flags & I2C_MSG_READ)) {
|
||||
uint16_t tmp_len = msgs[0].len + msgs[1].len;
|
||||
|
||||
if (tmp_len <= I2C_MAX_MSG_LEN) {
|
||||
static uint8_t merge_buf[I2C_MAX_MSG_LEN];
|
||||
struct i2c_msg tmp_msg;
|
||||
|
||||
memcpy(&merge_buf[0], msgs[0].buf, msgs[0].len);
|
||||
memcpy(&merge_buf[msgs[0].len], msgs[1].buf, msgs[1].len);
|
||||
tmp_msg.buf = &merge_buf[0];
|
||||
tmp_msg.flags = I2C_MSG_WRITE | I2C_MSG_STOP;
|
||||
tmp_msg.len = (uint8_t)tmp_len;
|
||||
/* Merge 2 msgs into 1 msg */
|
||||
msgs[0] = tmp_msg;
|
||||
num_msgs = 1;
|
||||
} else {
|
||||
LOG_DBG("messages are too large to merge");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current = msgs;
|
||||
ret = 0;
|
||||
|
||||
/* Check for validity of all messages before transfer */
|
||||
for (int i = 1; i <= num_msgs; i++) {
|
||||
if (i < num_msgs) {
|
||||
next = current + 1;
|
||||
|
||||
/*
|
||||
* Restart condition between messages
|
||||
* of different directions is required
|
||||
*/
|
||||
if (OPERATION(current) != OPERATION(next)) {
|
||||
if (!(next->flags & I2C_MSG_RESTART)) {
|
||||
LOG_ERR("Restart condition between messages of "
|
||||
"different directions is required."
|
||||
"Current/Total: [%d/%d]",
|
||||
i, num_msgs);
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop condition is only allowed on last message */
|
||||
if (current->flags & I2C_MSG_STOP) {
|
||||
LOG_ERR("Invalid stop flag. Stop condition is only allowed on "
|
||||
"last message. "
|
||||
"Current/Total: [%d/%d]",
|
||||
i, num_msgs);
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
current->flags |= I2C_MSG_STOP;
|
||||
}
|
||||
|
||||
current++;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_sem_take(&data->bus_lock, K_FOREVER);
|
||||
|
||||
/* Set destination address with configured address mode before sending msg. */
|
||||
|
||||
i2c_master_addr_mode_t addr_mode = 0;
|
||||
|
||||
if (I2C_MSG_ADDR_10_BITS & data->dev_config) {
|
||||
addr_mode = I2C_MASTER_ADDR_MODE_10BIT;
|
||||
} else {
|
||||
addr_mode = I2C_MASTER_ADDR_MODE_7BIT;
|
||||
}
|
||||
|
||||
R_SCI_B_I2C_SlaveAddressSet(&data->ctrl, addr, addr_mode);
|
||||
|
||||
/* Process input `msgs`. */
|
||||
|
||||
current = msgs;
|
||||
|
||||
while (num_msgs > 0) {
|
||||
if (num_msgs > 1) {
|
||||
next = current + 1;
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
if (current->flags & I2C_MSG_READ) {
|
||||
fsp_err = R_SCI_B_I2C_Read(&data->ctrl, current->buf, current->len,
|
||||
next != NULL && (next->flags & I2C_MSG_RESTART));
|
||||
} else {
|
||||
fsp_err =
|
||||
R_SCI_B_I2C_Write(&data->ctrl, current->buf, current->len,
|
||||
next != NULL && (next->flags & I2C_MSG_RESTART));
|
||||
}
|
||||
|
||||
if (fsp_err != FSP_SUCCESS) {
|
||||
switch (fsp_err) {
|
||||
case FSP_ERR_INVALID_SIZE:
|
||||
LOG_ERR("Provided number of bytes more than uint16_t size "
|
||||
"(65535) while DTC is used for data transfer.");
|
||||
break;
|
||||
case FSP_ERR_IN_USE:
|
||||
LOG_ERR("Bus busy condition. Another transfer was in progress.");
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unknown error.");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
goto RELEASE_BUS;
|
||||
}
|
||||
|
||||
/* Wait for callback to return. */
|
||||
k_sem_take(&data->complete_sem, K_FOREVER);
|
||||
|
||||
/* Handle event msg from callback. */
|
||||
switch (data->event) {
|
||||
case I2C_MASTER_EVENT_ABORTED:
|
||||
LOG_ERR("%s failed.", (current->flags & I2C_MSG_READ) ? "Read" : "Write");
|
||||
ret = -EIO;
|
||||
goto RELEASE_BUS;
|
||||
case I2C_MASTER_EVENT_RX_COMPLETE:
|
||||
break;
|
||||
case I2C_MASTER_EVENT_TX_COMPLETE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
current++;
|
||||
num_msgs--;
|
||||
}
|
||||
|
||||
RELEASE_BUS:
|
||||
k_sem_give(&data->bus_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_CALLBACK
|
||||
|
||||
static void renesas_ra_sci_b_i2c_async_done(const struct device *dev, struct sci_b_i2c_data *data,
|
||||
int result)
|
||||
{
|
||||
|
||||
i2c_callback_t cb = data->cb;
|
||||
void *p_context = data->p_context;
|
||||
|
||||
data->msg_idx = 0;
|
||||
data->msgs = NULL;
|
||||
data->num_msgs = 0;
|
||||
data->cb = NULL;
|
||||
data->p_context = NULL;
|
||||
data->addr = 0;
|
||||
|
||||
k_sem_give(&data->bus_lock);
|
||||
|
||||
/* Callback may wish to start another transfer */
|
||||
cb(dev, result, p_context);
|
||||
}
|
||||
|
||||
/* Start a transfer asynchronously */
|
||||
static void renesas_ra_sci_b_i2c_async_iter(const struct device *dev)
|
||||
{
|
||||
struct sci_b_i2c_data *data = dev->data;
|
||||
fsp_err_t fsp_err;
|
||||
struct i2c_msg *current, *next;
|
||||
|
||||
struct i2c_msg *msg = &data->msgs[data->msg_idx];
|
||||
|
||||
/* Check for validity of all messages before transfer */
|
||||
current = msg;
|
||||
if (data->msg_idx < (data->num_msgs - 1)) {
|
||||
next = current + 1;
|
||||
|
||||
/*
|
||||
* Restart condition between messages
|
||||
* of different directions is required
|
||||
*/
|
||||
if (OPERATION(current) != OPERATION(next)) {
|
||||
if (!(next->flags & I2C_MSG_RESTART)) {
|
||||
LOG_ERR("Restart condition between messages of "
|
||||
"different directions is required."
|
||||
"Current/Total: [%d/%d]",
|
||||
data->msg_idx + 1, data->num_msgs);
|
||||
renesas_ra_sci_b_i2c_async_done(dev, data, -EIO);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (current->flags & I2C_MSG_STOP) {
|
||||
LOG_ERR("Invalid stop flag. Stop condition is only allowed on "
|
||||
"last message. "
|
||||
"Current/Total: [%d/%d]",
|
||||
data->msg_idx + 1, data->num_msgs);
|
||||
renesas_ra_sci_b_i2c_async_done(dev, data, -EIO);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
current->flags |= I2C_MSG_STOP;
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
if (current->flags & I2C_MSG_READ) {
|
||||
fsp_err = R_SCI_B_I2C_Read(&data->ctrl, current->buf, current->len,
|
||||
(next != NULL) && (next->flags & I2C_MSG_RESTART));
|
||||
} else {
|
||||
fsp_err = R_SCI_B_I2C_Write(&data->ctrl, current->buf, current->len,
|
||||
(next != NULL) && (next->flags & I2C_MSG_RESTART));
|
||||
}
|
||||
|
||||
/* Return an error if the transfer didn't start successfully
|
||||
* e.g., if the bus was busy
|
||||
*/
|
||||
if (fsp_err != FSP_SUCCESS) {
|
||||
switch (fsp_err) {
|
||||
case FSP_ERR_INVALID_SIZE:
|
||||
LOG_ERR("Provided number of bytes more than uint16_t size "
|
||||
"(65535) while DTC is used for data transfer.");
|
||||
break;
|
||||
case FSP_ERR_IN_USE:
|
||||
LOG_ERR("Bus busy condition. Another transfer was in progress.");
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unknown error.");
|
||||
break;
|
||||
}
|
||||
|
||||
R_SCI_B_I2C_Abort(&data->ctrl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int renesas_ra_sci_b_i2c_transfer_cb(const struct device *dev, struct i2c_msg *msgs,
|
||||
uint8_t num_msgs, uint16_t addr, i2c_callback_t cb,
|
||||
void *p_context)
|
||||
{
|
||||
struct sci_b_i2c_data *data = dev->data;
|
||||
|
||||
int res = k_sem_take(&data->bus_lock, K_NO_WAIT);
|
||||
|
||||
if (res != 0) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
data->msg_idx = 0;
|
||||
data->msgs = msgs;
|
||||
data->num_msgs = num_msgs;
|
||||
data->addr = addr;
|
||||
data->cb = cb;
|
||||
data->p_context = p_context;
|
||||
|
||||
renesas_ra_sci_b_i2c_async_iter(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_I2C_CALLBACK */
|
||||
|
||||
static void renesas_ra_sci_b_i2c_callback(i2c_master_callback_args_t *p_args)
|
||||
{
|
||||
const struct device *dev = p_args->p_context;
|
||||
struct sci_b_i2c_data *data = dev->data;
|
||||
|
||||
#ifdef CONFIG_I2C_CALLBACK
|
||||
if (data->cb != NULL) {
|
||||
/* Async transfer */
|
||||
if (p_args->event == I2C_MASTER_EVENT_ABORTED) {
|
||||
R_SCI_B_I2C_Abort(&data->ctrl);
|
||||
renesas_ra_sci_b_i2c_async_done(dev, data, -EIO);
|
||||
} else if (data->msg_idx == data->num_msgs - 1) {
|
||||
renesas_ra_sci_b_i2c_async_done(dev, data, 0);
|
||||
} else {
|
||||
data->msg_idx++;
|
||||
renesas_ra_sci_b_i2c_async_iter(dev);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_I2C_CALLBACK */
|
||||
|
||||
data->event = p_args->event;
|
||||
|
||||
k_sem_give(&data->complete_sem);
|
||||
}
|
||||
|
||||
static int renesas_ra_sci_b_i2c_init(const struct device *dev)
|
||||
{
|
||||
const struct sci_b_i2c_config *config = dev->config;
|
||||
struct sci_b_i2c_data *data = (struct sci_b_i2c_data *)dev->data;
|
||||
fsp_err_t fsp_err;
|
||||
int ret;
|
||||
|
||||
data->dev_config |= I2C_MODE_CONTROLLER;
|
||||
|
||||
/* Configure dt provided device signals when available */
|
||||
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Pinctrl config failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_sem_init(&data->bus_lock, 1, 1);
|
||||
k_sem_init(&data->complete_sem, 0, 1);
|
||||
|
||||
switch (data->i2c_config.rate) {
|
||||
case I2C_MASTER_RATE_STANDARD:
|
||||
calc_sci_b_iic_clock_setting(dev, data->i2c_config.rate,
|
||||
&data->ext_cfg.clock_settings);
|
||||
data->i2c_config.p_extend = &data->ext_cfg;
|
||||
data->dev_config |= I2C_SPEED_SET(I2C_SPEED_STANDARD);
|
||||
break;
|
||||
case I2C_MASTER_RATE_FAST:
|
||||
calc_sci_b_iic_clock_setting(dev, data->i2c_config.rate,
|
||||
&data->ext_cfg.clock_settings);
|
||||
data->i2c_config.p_extend = &data->ext_cfg;
|
||||
data->dev_config |= I2C_SPEED_SET(I2C_SPEED_FAST);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid I2C speed rate: %d", data->i2c_config.rate);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_RENESAS_RA_SCI_B_DTC
|
||||
data->i2c_config.p_transfer_rx = &data->rx_transfer;
|
||||
data->i2c_config.p_transfer_tx = &data->tx_transfer;
|
||||
#endif
|
||||
|
||||
fsp_err = R_SCI_B_I2C_Open(&data->ctrl, &data->i2c_config);
|
||||
|
||||
if (fsp_err != FSP_SUCCESS) {
|
||||
LOG_ERR("I2C init failed.");
|
||||
}
|
||||
|
||||
config->irq_config_func(dev);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static void calc_sci_b_iic_clock_setting(const struct device *dev, const uint32_t fsp_i2c_rate,
|
||||
sci_b_i2c_clock_settings_t *clk_cfg)
|
||||
{
|
||||
const struct sci_b_i2c_config *config = dev->config;
|
||||
|
||||
uint32_t bitrate = 0;
|
||||
bool use_mddr = clk_cfg->bitrate_modulation;
|
||||
|
||||
uint32_t divisor = 0;
|
||||
uint32_t divisor_bitrate_multiple = 0;
|
||||
uint32_t brr = 0;
|
||||
int32_t cks = 0;
|
||||
uint32_t delta_error = 0;
|
||||
|
||||
uint32_t sda_delay_clock = 0;
|
||||
uint32_t sda_delay_counts = 0;
|
||||
uint32_t temp_mddr;
|
||||
uint32_t calculated_bitrate = 0;
|
||||
|
||||
uint32_t mddr = MDDR_DISABLE;
|
||||
|
||||
uint32_t peripheral_clock = R_FSP_SciClockHzGet();
|
||||
|
||||
uint32_t sda_delay_ns = config->sda_output_delay;
|
||||
|
||||
if (I2C_MASTER_RATE_FAST == fsp_i2c_rate) {
|
||||
bitrate = 400000;
|
||||
} else {
|
||||
bitrate = 100000;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i <= 3; i++) {
|
||||
|
||||
divisor_bitrate_multiple = (1 << (2 * (i + 1))) * 8;
|
||||
divisor = divisor_bitrate_multiple * bitrate;
|
||||
|
||||
/* Calculate BRR so that the bit rate is the largest possible value less than or
|
||||
* equal to the desired bitrate.
|
||||
*/
|
||||
brr = (uint32_t)ceil(((double)peripheral_clock) / divisor - 1);
|
||||
if (brr <= 255) {
|
||||
break;
|
||||
}
|
||||
cks++;
|
||||
}
|
||||
calculated_bitrate = (uint32_t)((double)peripheral_clock) /
|
||||
(divisor_bitrate_multiple * (256 / mddr) * (brr + 1));
|
||||
delta_error = bitrate - calculated_bitrate;
|
||||
|
||||
if (use_mddr) {
|
||||
for (uint32_t temp_brr = brr; temp_brr > 0; temp_brr--) {
|
||||
|
||||
/** Calculate the MDDR (M) value if bit rate modulation is enabled,
|
||||
* The formula to calculate MBBR (from the M and N relationship given in the
|
||||
* hardware manual) is as follows and it must be between 128 and 256. MDDR =
|
||||
* ((divisor * 256) * (BRR + 1)) / PCLK
|
||||
*/
|
||||
temp_mddr = (uint32_t)floor(((double)divisor) * 256 * (temp_brr + 1) /
|
||||
peripheral_clock);
|
||||
|
||||
/* The maximum value that could result from the calculation above is 256,
|
||||
* which is a valid MDDR value, so only the lower bound is checked.
|
||||
*/
|
||||
if (temp_mddr < 128) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* The maximum for MDDR is 256 (MDDR unused). */
|
||||
if (temp_mddr > 256) {
|
||||
continue;
|
||||
}
|
||||
|
||||
calculated_bitrate =
|
||||
(uint32_t)(peripheral_clock /
|
||||
(divisor_bitrate_multiple * (256 / ((double)temp_mddr)) *
|
||||
(temp_brr + 1)));
|
||||
|
||||
/** If the bit rate error is less than the previous lowest bit rate error,
|
||||
* then store these settings as the best value.
|
||||
*/
|
||||
if ((bitrate - calculated_bitrate) < delta_error) {
|
||||
delta_error = bitrate - calculated_bitrate;
|
||||
brr = temp_brr;
|
||||
mddr = temp_mddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If MDDR == 256, disable bitrate modulation and set MDDR to a valid value. */
|
||||
if (mddr == 256) {
|
||||
mddr = 255;
|
||||
use_mddr = false;
|
||||
}
|
||||
|
||||
/* Calculate SDA delay. */
|
||||
sda_delay_clock = peripheral_clock >> cks;
|
||||
sda_delay_counts = (uint32_t)ceil(((double)sda_delay_ns) * sda_delay_clock / 1000000000);
|
||||
if (sda_delay_counts > 31) {
|
||||
sda_delay_counts = 31;
|
||||
}
|
||||
|
||||
clk_cfg->clk_divisor_value = (uint8_t)cks;
|
||||
clk_cfg->brr_value = (uint8_t)brr;
|
||||
clk_cfg->mddr_value = (uint8_t)mddr;
|
||||
clk_cfg->bitrate_modulation = use_mddr;
|
||||
clk_cfg->cycles_value = (uint8_t)sda_delay_counts;
|
||||
}
|
||||
|
||||
static const struct i2c_driver_api renesas_ra_sci_b_i2c_driver_api = {
|
||||
.configure = renesas_ra_sci_b_i2c_configure,
|
||||
.get_config = renesas_ra_sci_b_i2c_get_config,
|
||||
.transfer = renesas_ra_sci_b_i2c_transfer,
|
||||
#ifdef CONFIG_I2C_CALLBACK
|
||||
.transfer_cb = renesas_ra_sci_b_i2c_transfer_cb,
|
||||
#endif /* CONFIG_I2C_CALLBACK */
|
||||
};
|
||||
|
||||
#define _ELC_EVENT_SCI_RXI(channel) ELC_EVENT_SCI##channel##_RXI
|
||||
#define _ELC_EVENT_SCI_TXI(channel) ELC_EVENT_SCI##channel##_TXI
|
||||
#define _ELC_EVENT_SCI_TEI(channel) ELC_EVENT_SCI##channel##_TEI
|
||||
|
||||
#define ELC_EVENT_SCI_RXI(channel) _ELC_EVENT_SCI_RXI(channel)
|
||||
#define ELC_EVENT_SCI_TXI(channel) _ELC_EVENT_SCI_TXI(channel)
|
||||
#define ELC_EVENT_SCI_TEI(channel) _ELC_EVENT_SCI_TEI(channel)
|
||||
|
||||
#ifndef CONFIG_I2C_RENESAS_RA_SCI_B_DTC
|
||||
#define SCI_B_I2C_DTC_INIT(index)
|
||||
#define RXI_TRANSFER(index)
|
||||
#else
|
||||
#define SCI_B_I2C_DTC_INIT(index) \
|
||||
.rx_transfer_info = \
|
||||
{ \
|
||||
.transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED, \
|
||||
.transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_DESTINATION, \
|
||||
.transfer_settings_word_b.irq = TRANSFER_IRQ_END, \
|
||||
.transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED, \
|
||||
.transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_FIXED, \
|
||||
.transfer_settings_word_b.size = TRANSFER_SIZE_1_BYTE, \
|
||||
.transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL, \
|
||||
.p_dest = (void *)NULL, \
|
||||
.p_src = (void const *)NULL, \
|
||||
.num_blocks = 0, \
|
||||
.length = 0, \
|
||||
}, \
|
||||
.rx_transfer_cfg_extend = {.activation_source = \
|
||||
DT_IRQ_BY_NAME(DT_INST_PARENT(index), rxi, irq)}, \
|
||||
.rx_transfer_cfg = \
|
||||
{ \
|
||||
.p_info = &sci_b_i2c_data_##index.rx_transfer_info, \
|
||||
.p_extend = &sci_b_i2c_data_##index.rx_transfer_cfg_extend, \
|
||||
}, \
|
||||
.rx_transfer = \
|
||||
{ \
|
||||
.p_ctrl = &sci_b_i2c_data_##index.rx_transfer_ctrl, \
|
||||
.p_cfg = &sci_b_i2c_data_##index.rx_transfer_cfg, \
|
||||
.p_api = &g_transfer_on_dtc, \
|
||||
}, \
|
||||
.tx_transfer_info = \
|
||||
{ \
|
||||
.transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED, \
|
||||
.transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_SOURCE, \
|
||||
.transfer_settings_word_b.irq = TRANSFER_IRQ_END, \
|
||||
.transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED, \
|
||||
.transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED, \
|
||||
.transfer_settings_word_b.size = TRANSFER_SIZE_1_BYTE, \
|
||||
.transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL, \
|
||||
.p_dest = (void *)NULL, \
|
||||
.p_src = (void const *)NULL, \
|
||||
.num_blocks = 0, \
|
||||
.length = 0, \
|
||||
}, \
|
||||
.tx_transfer_cfg_extend = {.activation_source = \
|
||||
DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, irq)}, \
|
||||
.tx_transfer_cfg = \
|
||||
{ \
|
||||
.p_info = &sci_b_i2c_data_##index.tx_transfer_info, \
|
||||
.p_extend = &sci_b_i2c_data_##index.tx_transfer_cfg_extend, \
|
||||
}, \
|
||||
.tx_transfer = { \
|
||||
.p_ctrl = &sci_b_i2c_data_##index.tx_transfer_ctrl, \
|
||||
.p_cfg = &sci_b_i2c_data_##index.tx_transfer_cfg, \
|
||||
.p_api = &g_transfer_on_dtc, \
|
||||
},
|
||||
|
||||
#define RXI_TRANSFER(index) \
|
||||
/* rxi */ \
|
||||
R_ICU->IELSR[DT_IRQ_BY_NAME(DT_INST_PARENT(index), rxi, irq)] = \
|
||||
ELC_EVENT_SCI_RXI(DT_INST_PROP(index, channel)); \
|
||||
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_INST_PARENT(index), rxi, irq), \
|
||||
DT_IRQ_BY_NAME(DT_INST_PARENT(index), rxi, priority), sci_b_i2c_rxi_isr, \
|
||||
DEVICE_DT_INST_GET(index), 0); \
|
||||
irq_enable(DT_IRQ_BY_NAME(DT_INST_PARENT(index), rxi, irq));
|
||||
#endif
|
||||
|
||||
#define SCI_B_I2C_RA_INIT(index) \
|
||||
static void renesas_ra_sci_b_i2c_irq_config_func##index(const struct device *dev) \
|
||||
{ \
|
||||
RXI_TRANSFER(index) \
|
||||
\
|
||||
/* txi */ \
|
||||
R_ICU->IELSR[DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, irq)] = \
|
||||
ELC_EVENT_SCI_TXI(DT_INST_PROP(index, channel)); \
|
||||
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, irq), \
|
||||
DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, priority), \
|
||||
sci_b_i2c_txi_isr, DEVICE_DT_INST_GET(index), 0); \
|
||||
irq_enable(DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, irq)); \
|
||||
\
|
||||
/* tei */ \
|
||||
R_ICU->IELSR[DT_IRQ_BY_NAME(DT_INST_PARENT(index), tei, irq)] = \
|
||||
ELC_EVENT_SCI_TEI(DT_INST_PROP(index, channel)); \
|
||||
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_INST_PARENT(index), tei, irq), \
|
||||
DT_IRQ_BY_NAME(DT_INST_PARENT(index), tei, priority), \
|
||||
sci_b_i2c_tei_isr, DEVICE_DT_INST_GET(index), 0); \
|
||||
irq_enable(DT_IRQ_BY_NAME(DT_INST_PARENT(index), tei, irq)); \
|
||||
} \
|
||||
PINCTRL_DT_DEFINE(DT_INST_PARENT(index)); \
|
||||
\
|
||||
static const struct sci_b_i2c_config sci_b_i2c_config_##index = { \
|
||||
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST_PARENT(index)), \
|
||||
.irq_config_func = renesas_ra_sci_b_i2c_irq_config_func##index, \
|
||||
.sda_output_delay = DT_INST_PROP(index, sda_output_delay), \
|
||||
}; \
|
||||
static struct sci_b_i2c_data sci_b_i2c_data_##index = { \
|
||||
.i2c_config = \
|
||||
{ \
|
||||
.channel = DT_INST_PROP(index, channel), \
|
||||
.slave = 0, \
|
||||
.rate = I2C_MASTER_RATE_STANDARD, \
|
||||
.addr_mode = I2C_MASTER_ADDR_MODE_7BIT, \
|
||||
.ipl = DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, priority), \
|
||||
.rxi_irq = DT_IRQ_BY_NAME(DT_INST_PARENT(index), rxi, irq), \
|
||||
.txi_irq = DT_IRQ_BY_NAME(DT_INST_PARENT(index), txi, irq), \
|
||||
.tei_irq = DT_IRQ_BY_NAME(DT_INST_PARENT(index), tei, irq), \
|
||||
.p_callback = renesas_ra_sci_b_i2c_callback, \
|
||||
.p_context = DEVICE_DT_GET(DT_DRV_INST(index)), \
|
||||
}, \
|
||||
.ext_cfg = \
|
||||
{ \
|
||||
.clock_settings.snfr_value = \
|
||||
DT_INST_PROP(index, noise_filter_clock_select), \
|
||||
.clock_settings.bitrate_modulation = DT_INST_NODE_HAS_PROP( \
|
||||
DT_DRV_INST(index), bit_rate_modulation), \
|
||||
.clock_settings.clock_source = SCI_B_I2C_CLOCK_SOURCE_SCISPICLK, \
|
||||
}, \
|
||||
SCI_B_I2C_DTC_INIT(index)}; \
|
||||
I2C_DEVICE_DT_INST_DEFINE(index, renesas_ra_sci_b_i2c_init, NULL, &sci_b_i2c_data_##index, \
|
||||
&sci_b_i2c_config_##index, POST_KERNEL, \
|
||||
CONFIG_I2C_INIT_PRIORITY, &renesas_ra_sci_b_i2c_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SCI_B_I2C_RA_INIT)
|
34
dts/bindings/i2c/renesas,ra-i2c-sci-b.yaml
Normal file
34
dts/bindings/i2c/renesas,ra-i2c-sci-b.yaml
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2025 Renesas Electronics Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Renesas RA SCI-B I2C controller
|
||||
|
||||
compatible: "renesas,ra-i2c-sci-b"
|
||||
|
||||
include: [i2c-controller.yaml, pinctrl-device.yaml]
|
||||
|
||||
properties:
|
||||
|
||||
channel:
|
||||
required: true
|
||||
type: int
|
||||
|
||||
sda-output-delay:
|
||||
type: int
|
||||
default: 300
|
||||
description: |
|
||||
SDA Output Delay in nano seconds.
|
||||
|
||||
noise-filter-clock-select:
|
||||
type: int
|
||||
default: 1
|
||||
enum: [1, 2, 3, 4]
|
||||
description: |
|
||||
Select the on-chip baud rate generator source clock
|
||||
division setting for the digital noise filter.
|
||||
|
||||
bit-rate-modulation:
|
||||
type: boolean
|
||||
description: |
|
||||
Enabling bitrate modulation reduces the percent error
|
||||
of the actual bitrate with respect to the requested baud rate.
|
|
@ -35,6 +35,11 @@ config USE_RA_FSP_I2C_IIC
|
|||
help
|
||||
Enable Renesas RA I2C IIC Master driver
|
||||
|
||||
config USE_RA_FSP_SCI_B_I2C
|
||||
bool
|
||||
help
|
||||
Enable RA FSP SCI-B I2C driver
|
||||
|
||||
config USE_RA_FSP_SCI_UART
|
||||
bool
|
||||
help
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue