drivers: i2c: rts5912 i2c dirver
base on DesignWare I2C driver to implement RTS5912 I2C driver. 1. support customize bus recovery function. 2. fix isr timing issue by enable tx empty control. 3. support stuck at low handle by enable bus clear feature. 4. support custom stuck at low timeout set from dts 5. disable block mode in rts5912 i2c. 6. support I2C_ALLOW_NO_STOP_TRANSACTIONS Signed-off-by: Titan Chen <titan.chen@realtek.com>
This commit is contained in:
parent
11c543e24c
commit
748789eadf
12 changed files with 755 additions and 38 deletions
|
@ -57,6 +57,7 @@ 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_RTS5912 i2c_realtek_rts5912.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_RV32M1_LPI2C i2c_rv32m1_lpi2c.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_SAM0 i2c_sam0.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2C_SAM_TWI i2c_sam_twi.c)
|
||||
|
|
|
@ -61,7 +61,7 @@ config I2C_ALLOW_NO_STOP_TRANSACTIONS
|
|||
depends on !I2C_STM32
|
||||
depends on !I2C_GD32
|
||||
depends on !I2C_ESP32
|
||||
depends on !I2C_DW
|
||||
depends on (!I2C_DW || I2C_RTS5912)
|
||||
select DEPRECATED
|
||||
help
|
||||
Allow I2C transactions with no STOP on the last message. This is
|
||||
|
@ -146,6 +146,7 @@ source "drivers/i2c/Kconfig.omap"
|
|||
source "drivers/i2c/Kconfig.rcar"
|
||||
source "drivers/i2c/Kconfig.renesas_ra"
|
||||
source "drivers/i2c/Kconfig.renesas_rz"
|
||||
source "drivers/i2c/Kconfig.rts5912"
|
||||
source "drivers/i2c/Kconfig.sam0"
|
||||
source "drivers/i2c/Kconfig.sam_twihs"
|
||||
source "drivers/i2c/Kconfig.sbcon"
|
||||
|
|
|
@ -12,11 +12,12 @@ menuconfig I2C_DW
|
|||
config I2C_DW_CLOCK_SPEED
|
||||
int "Set the clock speed for I2C"
|
||||
depends on I2C_DW
|
||||
default 110 if I2C_RTS5912
|
||||
default 32
|
||||
|
||||
config I2C_DW_LPSS_DMA
|
||||
bool "Use I2C integrated DMA for asynchronous transfer"
|
||||
depends on I2C_DW
|
||||
depends on (I2C_DW && !I2C_RTS5912)
|
||||
select DMA
|
||||
select DMA_INTEL_LPSS
|
||||
help
|
||||
|
@ -28,3 +29,9 @@ config I2C_DW_RW_TIMEOUT_MS
|
|||
int "Set the Read/Write timeout in milliseconds"
|
||||
depends on I2C_DW
|
||||
default 100
|
||||
|
||||
config I2C_DW_EXTENDED_SUPPORT
|
||||
bool "Extended DW features"
|
||||
help
|
||||
This option enables support for the SCL/SDA timeout registers and some
|
||||
additional features of the DW I2C controller.
|
||||
|
|
18
drivers/i2c/Kconfig.rts5912
Normal file
18
drivers/i2c/Kconfig.rts5912
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) 2025 Realtek, SIBG-SD7
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig I2C_RTS5912
|
||||
bool "Realtek RTS5912 I2C support"
|
||||
default y
|
||||
depends on DT_HAS_REALTEK_RTS5912_I2C_ENABLED
|
||||
select I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
select I2C_DW_EXTENDED_SUPPORT
|
||||
help
|
||||
Enable the Realtek RTS5912 I2C driver
|
||||
|
||||
config I2C_RTS5912_INIT_PRIORITY
|
||||
int "RTS5912 Init priority"
|
||||
depends on I2C_RTS5912
|
||||
default 51
|
||||
help
|
||||
I2C device driver initialization priority.
|
|
@ -6,6 +6,11 @@
|
|||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include <cmsis_core.h>
|
||||
#include <soc.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -16,6 +21,7 @@
|
|||
#include <zephyr/init.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(CONFIG_PINCTRL)
|
||||
|
@ -53,6 +59,57 @@ static inline uint32_t get_regs(const struct device *dev)
|
|||
return (uint32_t)DEVICE_MMIO_GET(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param dev: DW I2C device instance
|
||||
* @param wrapper_dev: Callers device instance
|
||||
*/
|
||||
void i2c_dw_register_recover_bus_cb(const struct device *dev, i2c_api_recover_bus_t recover_bus_cb,
|
||||
const struct device *wrapper_dev)
|
||||
{
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
dw->recover_bus_cb = recover_bus_cb;
|
||||
dw->recover_bus_dev = (struct device *)wrapper_dev;
|
||||
}
|
||||
|
||||
/* it might call from i2c_transfer api and have already
|
||||
* lock the bus, no need to lock bus again here
|
||||
*/
|
||||
static int i2c_recovery_bus(const struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
if (dw->recover_bus_cb) {
|
||||
/* callback customize recovery function */
|
||||
ret = dw->recover_bus_cb(dw->recover_bus_dev);
|
||||
if (ret == 0) {
|
||||
/* recovery success */
|
||||
dw->state = I2C_DW_STATE_READY;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_dw_recovery_bus(const struct device *dev)
|
||||
{
|
||||
|
||||
int ret = 0;
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
/* lock bus */
|
||||
ret = k_sem_take(&dw->bus_sem, K_FOREVER);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
/* do bus recovery */
|
||||
ret = i2c_recovery_bus(dev);
|
||||
/* unlock bus */
|
||||
k_sem_give(&dw->bus_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
void i2c_dw_enable_idma(const struct device *dev, bool enable)
|
||||
{
|
||||
|
@ -346,7 +403,11 @@ static inline void i2c_dw_transfer_complete(const struct device *dev)
|
|||
|
||||
write_intr_mask(DW_DISABLE_ALL_I2C_INT, reg_base);
|
||||
value = read_clr_intr(reg_base);
|
||||
|
||||
#ifdef CONFIG_CPU_CORTEX_M
|
||||
const struct i2c_dw_rom_config *const rom = dev->config;
|
||||
/* clear pending interrupt */
|
||||
NVIC_ClearPendingIRQ(rom->irqnumber);
|
||||
#endif
|
||||
k_sem_give(&dw->device_sync_sem);
|
||||
}
|
||||
|
||||
|
@ -398,9 +459,12 @@ static void i2c_dw_isr(const struct device *port)
|
|||
|
||||
/* Bail early if there is any error. */
|
||||
if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER | DW_INTR_STAT_RX_OVER |
|
||||
DW_INTR_STAT_RX_UNDER) &
|
||||
DW_INTR_STAT_RX_UNDER | DW_INTR_STAT_SCL_STUCK_LOW) &
|
||||
intr_stat.raw) {
|
||||
dw->state = I2C_DW_CMD_ERROR;
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
dw->need_setup = true;
|
||||
#endif
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -438,6 +502,9 @@ static void i2c_dw_isr(const struct device *port)
|
|||
/* STOP detected: finish processing this message */
|
||||
if (intr_stat.bits.stop_det) {
|
||||
value = read_clr_stop_det(reg_base);
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
dw->need_setup = true;
|
||||
#endif
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -495,11 +562,22 @@ static int i2c_dw_setup(const struct device *dev, uint16_t slave_address)
|
|||
union ic_tar_register ic_tar;
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
if (!dw->need_setup) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
ic_con.raw = 0U;
|
||||
|
||||
/* Disable the device controller to be able set TAR */
|
||||
clear_bit_enable_en(reg_base);
|
||||
|
||||
/* enable bus clear feature */
|
||||
ic_con.bits.bus_clear = 1U;
|
||||
/* enable tx empty control to fix timing issue */
|
||||
ic_con.bits.tx_empty_ctl = 1U;
|
||||
|
||||
/* Disable interrupts */
|
||||
write_intr_mask(0, reg_base);
|
||||
|
||||
|
@ -611,9 +689,32 @@ static int i2c_dw_setup(const struct device *dev, uint16_t slave_address)
|
|||
|
||||
write_tar(ic_tar.raw, reg_base);
|
||||
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
dw->need_setup = false;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool i2c_dw_is_busy(const struct device *dev)
|
||||
{
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
/* The application explicitly started a transaction without
|
||||
* a STOP. Allow the application to continue sending transactions.
|
||||
*/
|
||||
if (!dw->need_setup) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (test_bit_status_activity(reg_base) || (dw->state & I2C_DW_BUSY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
|
||||
uint16_t slave_address)
|
||||
{
|
||||
|
@ -630,13 +731,27 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = k_mutex_lock(&dw->bus_mutex, K_FOREVER);
|
||||
/* semaphore to support I2C_CALLBACK */
|
||||
ret = k_sem_take(&dw->bus_sem, K_FOREVER);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* First step, check if there is current activity */
|
||||
if (test_bit_status_activity(reg_base) || (dw->state & I2C_DW_BUSY)) {
|
||||
/* check if the state is sda/scl stuck at low state */
|
||||
if (dw->state & I2C_DW_STUCK_ERR_MASK) {
|
||||
/* try to recovery */
|
||||
if (i2c_recovery_bus(dev)) {
|
||||
ret = -ETIME;
|
||||
goto error;
|
||||
}
|
||||
/* reset state */
|
||||
dw->state = I2C_DW_STATE_READY;
|
||||
}
|
||||
|
||||
/* First step, check if there is current activity
|
||||
* Skip if last read did not stop
|
||||
*/
|
||||
if (i2c_dw_is_busy(dev)) {
|
||||
ret = -EBUSY;
|
||||
goto error;
|
||||
}
|
||||
|
@ -730,6 +845,15 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8
|
|||
break;
|
||||
}
|
||||
|
||||
if (dw->state & I2C_DW_ERR_MASK) {
|
||||
if (dw->state & (I2C_DW_SDA_STUCK | I2C_DW_SCL_STUCK)) {
|
||||
ret = -ETIME;
|
||||
} else if (dw->state & (I2C_DW_NACK | I2C_DW_CMD_ERROR)) {
|
||||
ret = -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cur_msg++;
|
||||
msg_left--;
|
||||
}
|
||||
|
@ -737,8 +861,9 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8
|
|||
pm_device_busy_clear(dev);
|
||||
|
||||
error:
|
||||
dw->state = I2C_DW_STATE_READY;
|
||||
k_mutex_unlock(&dw->bus_mutex);
|
||||
/* keep error mask for bus recovery */
|
||||
dw->state &= I2C_DW_STUCK_ERR_MASK;
|
||||
k_sem_give(&dw->bus_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1033,6 +1158,7 @@ static DEVICE_API(i2c, funcs) = {
|
|||
#ifdef CONFIG_I2C_RTIO
|
||||
.iodev_submit = i2c_iodev_submit_fallback,
|
||||
#endif
|
||||
.recover_bus = i2c_dw_recovery_bus,
|
||||
};
|
||||
|
||||
static int i2c_dw_initialize(const struct device *dev)
|
||||
|
@ -1041,6 +1167,10 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
union ic_con_register ic_con;
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_I2C_DW_EXTENDED_SUPPORT
|
||||
uint32_t sda_timeout = rom->sda_timeout_value * CONFIG_I2C_DW_CLOCK_SPEED * 1000;
|
||||
uint32_t scl_timeout = rom->scl_timeout_value * CONFIG_I2C_DW_CLOCK_SPEED * 1000;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RESET)
|
||||
if (rom->reset.dev) {
|
||||
|
@ -1097,7 +1227,7 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
}
|
||||
|
||||
k_sem_init(&dw->device_sync_sem, 0, K_SEM_MAX_LIMIT);
|
||||
k_mutex_init(&dw->bus_mutex);
|
||||
k_sem_init(&dw->bus_sem, 1, 1);
|
||||
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
|
@ -1134,6 +1264,14 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
}
|
||||
|
||||
dw->state = I2C_DW_STATE_READY;
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
dw->need_setup = true;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_DW_EXTENDED_SUPPORT
|
||||
write_sdatimeout(sda_timeout, reg_base);
|
||||
write_scltimeout(scl_timeout, reg_base);
|
||||
#endif
|
||||
LOG_DBG("initialize done");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1147,7 +1285,7 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
#endif
|
||||
|
||||
#if defined(CONFIG_RESET)
|
||||
#define RESET_DW_CONFIG(n) \
|
||||
#define RESET_DW_CONFIG(n) \
|
||||
IF_ENABLED(DT_INST_NODE_HAS_PROP(n, resets), \
|
||||
(.reset = RESET_DT_SPEC_INST_GET(n),))
|
||||
#else
|
||||
|
@ -1208,6 +1346,14 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
(.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_IDX(n, 0)),), \
|
||||
())), ())
|
||||
|
||||
#ifdef CONFIG_I2C_DW_EXTENDED_SUPPORT
|
||||
#define TIMEOUT_DW_CONFIG(n) \
|
||||
.sda_timeout_value = DT_INST_PROP_OR(n, sda_timeout_value, __UINT32_MAX__), \
|
||||
.scl_timeout_value = DT_INST_PROP_OR(n, scl_timeout_value, __UINT32_MAX__),
|
||||
#else
|
||||
#define TIMEOUT_DW_CONFIG(n)
|
||||
#endif
|
||||
|
||||
#define I2C_DEVICE_INIT_DW(n) \
|
||||
PINCTRL_DW_DEFINE(n); \
|
||||
I2C_PCIE_DEFINE(n); \
|
||||
|
@ -1215,9 +1361,10 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
static const struct i2c_dw_rom_config i2c_config_dw_##n = { \
|
||||
I2C_CONFIG_REG_INIT(n).config_func = i2c_config_##n, \
|
||||
.bitrate = DT_INST_PROP(n, clock_frequency), \
|
||||
.irqnumber = DT_INST_IRQN(n), \
|
||||
.lcnt_offset = (int16_t)DT_INST_PROP_OR(n, lcnt_offset, 0), \
|
||||
.hcnt_offset = (int16_t)DT_INST_PROP_OR(n, hcnt_offset, 0), \
|
||||
RESET_DW_CONFIG(n) PINCTRL_DW_CONFIG(n) I2C_DW_INIT_PCIE(n) \
|
||||
TIMEOUT_DW_CONFIG(n) RESET_DW_CONFIG(n) PINCTRL_DW_CONFIG(n) I2C_DW_INIT_PCIE(n) \
|
||||
I2C_CONFIG_DMA_INIT(n)}; \
|
||||
static struct i2c_dw_dev_config i2c_##n##_runtime; \
|
||||
I2C_DEVICE_DT_INST_DEFINE(n, i2c_dw_initialize, NULL, &i2c_##n##_runtime, \
|
||||
|
|
|
@ -39,10 +39,24 @@ typedef void (*i2c_isr_cb_t)(const struct device *port);
|
|||
#define I2C_DW_CMD_RECV (1 << 1)
|
||||
#define I2C_DW_CMD_ERROR (1 << 2)
|
||||
#define I2C_DW_BUSY (1 << 3)
|
||||
#define I2C_DW_TX_ABRT (1 << 4)
|
||||
#define I2C_DW_NACK (1 << 5)
|
||||
#define I2C_DW_SCL_STUCK (1 << 6)
|
||||
#define I2C_DW_SDA_STUCK (1 << 7)
|
||||
|
||||
#define I2C_DW_ERR_MASK (I2C_DW_CMD_ERROR | I2C_DW_SCL_STUCK | I2C_DW_SDA_STUCK | I2C_DW_NACK)
|
||||
|
||||
#define I2C_DW_STUCK_ERR_MASK (I2C_DW_SCL_STUCK | I2C_DW_SDA_STUCK)
|
||||
|
||||
#ifdef CONFIG_I2C_DW_EXTENDED_SUPPORT
|
||||
#define DW_ENABLE_TX_INT_I2C_MASTER \
|
||||
(DW_INTR_STAT_TX_OVER | DW_INTR_STAT_TX_EMPTY | DW_INTR_STAT_TX_ABRT | \
|
||||
DW_INTR_STAT_STOP_DET | DW_INTR_STAT_SCL_STUCK_LOW)
|
||||
#else
|
||||
#define DW_ENABLE_TX_INT_I2C_MASTER \
|
||||
(DW_INTR_STAT_TX_OVER | DW_INTR_STAT_TX_EMPTY | DW_INTR_STAT_TX_ABRT | \
|
||||
DW_INTR_STAT_STOP_DET)
|
||||
#endif
|
||||
#define DW_ENABLE_RX_INT_I2C_MASTER \
|
||||
(DW_INTR_STAT_RX_UNDER | DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_FULL | \
|
||||
DW_INTR_STAT_STOP_DET)
|
||||
|
@ -84,6 +98,7 @@ struct i2c_dw_rom_config {
|
|||
DEVICE_MMIO_ROM;
|
||||
i2c_isr_cb_t config_func;
|
||||
uint32_t bitrate;
|
||||
uint32_t irqnumber;
|
||||
int16_t lcnt_offset;
|
||||
int16_t hcnt_offset;
|
||||
|
||||
|
@ -101,17 +116,22 @@ struct i2c_dw_rom_config {
|
|||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
const struct device *dma_dev;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_I2C_DW_EXTENDED_SUPPORT
|
||||
uint32_t sda_timeout_value;
|
||||
uint32_t scl_timeout_value;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct i2c_dw_dev_config {
|
||||
DEVICE_MMIO_RAM;
|
||||
struct k_sem device_sync_sem;
|
||||
struct k_mutex bus_mutex;
|
||||
struct k_sem bus_sem;
|
||||
uint32_t app_config;
|
||||
|
||||
uint8_t *xfr_buf;
|
||||
uint32_t xfr_len;
|
||||
uint32_t rx_pending;
|
||||
volatile uint8_t *xfr_buf;
|
||||
volatile uint32_t xfr_len;
|
||||
volatile uint32_t rx_pending;
|
||||
|
||||
uint16_t hcnt;
|
||||
uint16_t lcnt;
|
||||
|
@ -128,6 +148,12 @@ struct i2c_dw_dev_config {
|
|||
#endif
|
||||
|
||||
struct i2c_target_config *slave_cfg;
|
||||
|
||||
i2c_api_recover_bus_t recover_bus_cb;
|
||||
struct device *recover_bus_dev;
|
||||
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
|
||||
bool need_setup;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define Z_REG_READ(__sz) sys_read##__sz
|
||||
|
@ -165,6 +191,10 @@ struct i2c_dw_dev_config {
|
|||
return Z_REG_TEST_BIT(addr + __reg_off, __bit); \
|
||||
}
|
||||
|
||||
void i2c_dw_register_recover_bus_cb(const struct device *dw_i2c_dev,
|
||||
i2c_api_recover_bus_t recover_bus_cb,
|
||||
const struct device *wrapper_dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,8 @@ union ic_con_register {
|
|||
uint32_t stop_det: 1 __packed;
|
||||
uint32_t tx_empty_ctl: 1 __packed;
|
||||
uint32_t rx_fifo_full: 1 __packed;
|
||||
uint32_t stop_det_mstactive: 1 __packed;
|
||||
uint32_t bus_clear: 1 __packed;
|
||||
} bits;
|
||||
};
|
||||
|
||||
|
@ -35,20 +37,21 @@ union ic_con_register {
|
|||
#define IC_DATA_CMD_RESTART BIT(10)
|
||||
|
||||
/* DesignWare Interrupt bits positions */
|
||||
#define DW_INTR_STAT_RX_UNDER BIT(0)
|
||||
#define DW_INTR_STAT_RX_OVER BIT(1)
|
||||
#define DW_INTR_STAT_RX_FULL BIT(2)
|
||||
#define DW_INTR_STAT_TX_OVER BIT(3)
|
||||
#define DW_INTR_STAT_TX_EMPTY BIT(4)
|
||||
#define DW_INTR_STAT_RD_REQ BIT(5)
|
||||
#define DW_INTR_STAT_TX_ABRT BIT(6)
|
||||
#define DW_INTR_STAT_RX_DONE BIT(7)
|
||||
#define DW_INTR_STAT_ACTIVITY BIT(8)
|
||||
#define DW_INTR_STAT_STOP_DET BIT(9)
|
||||
#define DW_INTR_STAT_START_DET BIT(10)
|
||||
#define DW_INTR_STAT_GEN_CALL BIT(11)
|
||||
#define DW_INTR_STAT_RESTART_DET BIT(12)
|
||||
#define DW_INTR_STAT_MST_ON_HOLD BIT(13)
|
||||
#define DW_INTR_STAT_RX_UNDER BIT(0)
|
||||
#define DW_INTR_STAT_RX_OVER BIT(1)
|
||||
#define DW_INTR_STAT_RX_FULL BIT(2)
|
||||
#define DW_INTR_STAT_TX_OVER BIT(3)
|
||||
#define DW_INTR_STAT_TX_EMPTY BIT(4)
|
||||
#define DW_INTR_STAT_RD_REQ BIT(5)
|
||||
#define DW_INTR_STAT_TX_ABRT BIT(6)
|
||||
#define DW_INTR_STAT_RX_DONE BIT(7)
|
||||
#define DW_INTR_STAT_ACTIVITY BIT(8)
|
||||
#define DW_INTR_STAT_STOP_DET BIT(9)
|
||||
#define DW_INTR_STAT_START_DET BIT(10)
|
||||
#define DW_INTR_STAT_GEN_CALL BIT(11)
|
||||
#define DW_INTR_STAT_RESTART_DET BIT(12)
|
||||
#define DW_INTR_STAT_MST_ON_HOLD BIT(13)
|
||||
#define DW_INTR_STAT_SCL_STUCK_LOW BIT(14)
|
||||
|
||||
#define DW_INTR_MASK_RX_UNDER BIT(0)
|
||||
#define DW_INTR_MASK_RX_OVER BIT(1)
|
||||
|
@ -83,6 +86,32 @@ union ic_interrupt_register {
|
|||
uint32_t gen_call: 1 __packed;
|
||||
uint32_t restart_det: 1 __packed;
|
||||
uint32_t mst_on_hold: 1 __packed;
|
||||
uint32_t scl_stuck_low: 1 __packed;
|
||||
uint32_t reserved: 2 __packed;
|
||||
} bits;
|
||||
};
|
||||
|
||||
union ic_txabrt_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t ADDR7BNACK: 1 __packed;
|
||||
uint32_t ADDR10BNACK1: 1 __packed;
|
||||
uint32_t ADDR10BNACK2: 1 __packed;
|
||||
uint32_t TXDATANACK: 1 __packed;
|
||||
uint32_t GCALLNACK: 1 __packed;
|
||||
uint32_t GCALLREAD: 1 __packed;
|
||||
uint32_t HSACKDET: 1 __packed;
|
||||
uint32_t SBYTEACKET: 1 __packed;
|
||||
uint32_t HSNORSTRT: 1 __packed;
|
||||
uint32_t SBYTENORSTRT: 1 __packed;
|
||||
uint32_t ADDR10BRDNORSTRT: 1 __packed;
|
||||
uint32_t MASTERIDS: 1 __packed;
|
||||
uint32_t ARBLOST: 1 __packed;
|
||||
uint32_t SLVFLUSHTXFIFO: 1 __packed;
|
||||
uint32_t SLVARBLOST: 1 __packed;
|
||||
uint32_t SLVRDINTX: 1 __packed;
|
||||
uint32_t USRABRT: 1 __packed;
|
||||
uint32_t SDASTUCKLOW: 1 __packed;
|
||||
uint32_t reserved: 2 __packed;
|
||||
} bits;
|
||||
};
|
||||
|
@ -127,6 +156,7 @@ union ic_comp_param_1_register {
|
|||
#define DW_IC_REG_HS_SCL_LCNT (0x28)
|
||||
#define DW_IC_REG_INTR_STAT (0x2C)
|
||||
#define DW_IC_REG_INTR_MASK (0x30)
|
||||
#define DW_IC_REG_RAWINTR_MASK (0x34)
|
||||
#define DW_IC_REG_RX_TL (0x38)
|
||||
#define DW_IC_REG_TX_TL (0x3C)
|
||||
#define DW_IC_REG_CLR_INTR (0x40)
|
||||
|
@ -144,11 +174,15 @@ union ic_comp_param_1_register {
|
|||
#define DW_IC_REG_STATUS (0x70)
|
||||
#define DW_IC_REG_TXFLR (0x74)
|
||||
#define DW_IC_REG_RXFLR (0x78)
|
||||
#define DW_IC_REG_SDAHOLD (0x7c)
|
||||
#define DW_IC_REG_TXABRTSRC (0x80)
|
||||
#define DW_IC_REG_DMA_CR (0x88)
|
||||
#define DW_IC_REG_TDLR (0x8C)
|
||||
#define DW_IC_REG_RDLR (0x90)
|
||||
#define DW_IC_REG_FS_SPKLEN (0xA0)
|
||||
#define DW_IC_REG_HS_SPKLEN (0xA4)
|
||||
#define DW_IC_REG_SCL_TIMEOUT (0xAC)
|
||||
#define DW_IC_REG_SDA_TIMEOUT (0xB0)
|
||||
#define DW_IC_REG_COMP_PARAM_1 (0xF4)
|
||||
#define DW_IC_REG_COMP_TYPE (0xFC)
|
||||
|
||||
|
@ -167,6 +201,11 @@ DEFINE_TEST_BIT_OP(con_master_mode, DW_IC_REG_CON, DW_IC_CON_MASTER_MODE_BIT)
|
|||
DEFINE_MM_REG_WRITE(con, DW_IC_REG_CON, 32)
|
||||
DEFINE_MM_REG_READ(con, DW_IC_REG_CON, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(tar, DW_IC_REG_TAR, 32)
|
||||
DEFINE_MM_REG_WRITE(tar, DW_IC_REG_TAR, 32)
|
||||
|
||||
DEFINE_MM_REG_WRITE(sar, DW_IC_REG_SAR, 32)
|
||||
|
||||
DEFINE_MM_REG_WRITE(cmd_data, DW_IC_REG_DATA_CMD, 32)
|
||||
DEFINE_MM_REG_READ(cmd_data, DW_IC_REG_DATA_CMD, 32)
|
||||
|
||||
|
@ -184,6 +223,8 @@ DEFINE_MM_REG_READ(intr_stat, DW_IC_REG_INTR_STAT, 32)
|
|||
DEFINE_TEST_BIT_OP(intr_stat_tx_abrt, DW_IC_REG_INTR_STAT, DW_IC_INTR_STAT_TX_ABRT_BIT)
|
||||
|
||||
DEFINE_MM_REG_WRITE(intr_mask, DW_IC_REG_INTR_MASK, 32)
|
||||
DEFINE_MM_REG_READ(rawintr_stat, DW_IC_REG_RAWINTR_MASK, 32)
|
||||
|
||||
#define DW_IC_INTR_MASK_TX_EMPTY_BIT (4)
|
||||
DEFINE_CLEAR_BIT_OP(intr_mask_tx_empty, DW_IC_REG_INTR_MASK, DW_IC_INTR_MASK_TX_EMPTY_BIT)
|
||||
DEFINE_SET_BIT_OP(intr_mask_tx_empty, DW_IC_REG_INTR_MASK, DW_IC_INTR_MASK_TX_EMPTY_BIT)
|
||||
|
@ -203,22 +244,41 @@ DEFINE_MM_REG_READ(clr_rx_done, DW_IC_REG_CLR_RX_DONE, 32)
|
|||
DEFINE_MM_REG_READ(clr_rd_req, DW_IC_REG_CLR_RD_REQ, 32)
|
||||
DEFINE_MM_REG_READ(clr_activity, DW_IC_REG_CLR_ACTIVITY, 32)
|
||||
|
||||
#define DW_IC_ENABLE_EN_BIT (0)
|
||||
#define DW_IC_ENABLE_ABORT_BIT (1)
|
||||
#define DW_IC_ENABLE_EN_BIT (0)
|
||||
#define DW_IC_ENABLE_ABORT_BIT (1)
|
||||
#define DW_IC_ENABLE_BLOCK_BIT (2)
|
||||
#define DW_IC_ENABLE_SDARECOVEN_BIT (3)
|
||||
#define DW_IC_ENABLE_CLK_RESET_BIT (16)
|
||||
DEFINE_CLEAR_BIT_OP(enable_en, DW_IC_REG_ENABLE, DW_IC_ENABLE_EN_BIT)
|
||||
DEFINE_SET_BIT_OP(enable_en, DW_IC_REG_ENABLE, DW_IC_ENABLE_EN_BIT)
|
||||
DEFINE_SET_BIT_OP(enable_abort, DW_IC_REG_ENABLE, DW_IC_ENABLE_ABORT_BIT)
|
||||
DEFINE_TEST_BIT_OP(enable_abort, DW_IC_REG_ENABLE, DW_IC_ENABLE_ABORT_BIT)
|
||||
DEFINE_CLEAR_BIT_OP(enable_block, DW_IC_REG_ENABLE, DW_IC_ENABLE_BLOCK_BIT)
|
||||
DEFINE_SET_BIT_OP(enable_block, DW_IC_REG_ENABLE, DW_IC_ENABLE_BLOCK_BIT)
|
||||
DEFINE_CLEAR_BIT_OP(enable_sdarecov, DW_IC_REG_ENABLE, DW_IC_ENABLE_SDARECOVEN_BIT)
|
||||
DEFINE_SET_BIT_OP(enable_sdarecov, DW_IC_REG_ENABLE, DW_IC_ENABLE_SDARECOVEN_BIT)
|
||||
DEFINE_TEST_BIT_OP(enable_sdarecov, DW_IC_REG_ENABLE, DW_IC_ENABLE_SDARECOVEN_BIT)
|
||||
DEFINE_CLEAR_BIT_OP(enable_clk_reset, DW_IC_REG_ENABLE, DW_IC_ENABLE_CLK_RESET_BIT)
|
||||
DEFINE_SET_BIT_OP(enable_clk_reset, DW_IC_REG_ENABLE, DW_IC_ENABLE_CLK_RESET_BIT)
|
||||
DEFINE_TEST_BIT_OP(enable_clk_reset, DW_IC_REG_ENABLE, DW_IC_ENABLE_CLK_RESET_BIT)
|
||||
|
||||
#define DW_IC_STATUS_ACTIVITY_BIT (0)
|
||||
#define DW_IC_STATUS_TFNT_BIT (1)
|
||||
#define DW_IC_STATUS_RFNE_BIT (3)
|
||||
#define DW_IC_STATUS_ACTIVITY_BIT (0)
|
||||
#define DW_IC_STATUS_TFNT_BIT (1)
|
||||
#define DW_IC_STATUS_RFNE_BIT (3)
|
||||
#define DW_IC_STATUS_SDANOTRECOV_BIT (11)
|
||||
DEFINE_TEST_BIT_OP(status_activity, DW_IC_REG_STATUS, DW_IC_STATUS_ACTIVITY_BIT)
|
||||
DEFINE_TEST_BIT_OP(status_tfnt, DW_IC_REG_STATUS, DW_IC_STATUS_TFNT_BIT)
|
||||
DEFINE_TEST_BIT_OP(status_rfne, DW_IC_REG_STATUS, DW_IC_STATUS_RFNE_BIT)
|
||||
DEFINE_TEST_BIT_OP(status_sdanotrecov, DW_IC_REG_STATUS, DW_IC_STATUS_SDANOTRECOV_BIT)
|
||||
|
||||
DEFINE_MM_REG_READ(txflr, DW_IC_REG_TXFLR, 32)
|
||||
DEFINE_MM_REG_READ(rxflr, DW_IC_REG_RXFLR, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(sdahold, DW_IC_REG_SDAHOLD, 32)
|
||||
DEFINE_MM_REG_WRITE(sdahold, DW_IC_REG_SDAHOLD, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(txabrt_src, DW_IC_REG_TXABRTSRC, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(dma_cr, DW_IC_REG_DMA_CR, 32)
|
||||
DEFINE_MM_REG_WRITE(dma_cr, DW_IC_REG_DMA_CR, 32)
|
||||
|
||||
|
@ -230,11 +290,13 @@ DEFINE_MM_REG_WRITE(rdlr, DW_IC_REG_RDLR, 32)
|
|||
DEFINE_MM_REG_READ(fs_spklen, DW_IC_REG_FS_SPKLEN, 32)
|
||||
DEFINE_MM_REG_READ(hs_spklen, DW_IC_REG_HS_SPKLEN, 32)
|
||||
|
||||
DEFINE_MM_REG_WRITE(scltimeout, DW_IC_REG_SCL_TIMEOUT, 32)
|
||||
DEFINE_MM_REG_READ(scltimeout, DW_IC_REG_SCL_TIMEOUT, 32)
|
||||
DEFINE_MM_REG_WRITE(sdatimeout, DW_IC_REG_SDA_TIMEOUT, 32)
|
||||
DEFINE_MM_REG_READ(sdatimeout, DW_IC_REG_SDA_TIMEOUT, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(comp_param_1, DW_IC_REG_COMP_PARAM_1, 32)
|
||||
DEFINE_MM_REG_READ(comp_type, DW_IC_REG_COMP_TYPE, 32)
|
||||
DEFINE_MM_REG_READ(tar, DW_IC_REG_TAR, 32)
|
||||
DEFINE_MM_REG_WRITE(tar, DW_IC_REG_TAR, 32)
|
||||
DEFINE_MM_REG_WRITE(sar, DW_IC_REG_SAR, 32)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
247
drivers/i2c/i2c_realtek_rts5912.c
Normal file
247
drivers/i2c/i2c_realtek_rts5912.c
Normal file
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Realtek, SIBG-SD7
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/clock_control/clock_control_rts5912.h>
|
||||
#include <zephyr/dt-bindings/gpio/realtek-gpio.h>
|
||||
#include <reg/reg_gpio.h>
|
||||
#include "i2c_realtek_rts5912.h"
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(i2c_rts5912, CONFIG_I2C_LOG_LEVEL);
|
||||
|
||||
BUILD_ASSERT(CONFIG_I2C_RTS5912_INIT_PRIORITY > CONFIG_I2C_INIT_PRIORITY,
|
||||
"The I2C Realtek RTS5912 driver must be initialized after the I2C DW driver");
|
||||
|
||||
/* i2c_dw has define the DT_DRV_COMPAT at i2c_dw.h
|
||||
* so, need to undefine and define our own DT_DRV_COMPAT
|
||||
*/
|
||||
#ifdef DT_DRV_COMPAT
|
||||
#undef DT_DRV_COMPAT
|
||||
#define DT_DRV_COMPAT realtek_rts5912_i2c
|
||||
#endif
|
||||
|
||||
#define RECOVERY_TIME 30 /* in ms */
|
||||
|
||||
struct i2c_rts5912_config {
|
||||
const struct device *clk_dev;
|
||||
struct rts5912_sccon_subsys sccon_cfg;
|
||||
const struct device *dw_i2c_dev;
|
||||
/* SCL GPIO cells */
|
||||
struct gpio_dt_spec scl_gpios;
|
||||
/* SDA GPIO cells */
|
||||
struct gpio_dt_spec sda_gpios;
|
||||
};
|
||||
|
||||
static inline uint32_t get_regs(const struct device *dev)
|
||||
{
|
||||
return (uint32_t)DEVICE_MMIO_GET(dev);
|
||||
}
|
||||
|
||||
static int i2c_rts5912_recover_bus(const struct device *dev)
|
||||
{
|
||||
struct i2c_rts5912_config const *config = dev->config;
|
||||
|
||||
/* DW configure data */
|
||||
struct device const *dw_i2c_dev = config->dw_i2c_dev;
|
||||
const struct i2c_dw_rom_config *const rom = dw_i2c_dev->config;
|
||||
struct i2c_dw_dev_config *bus = dw_i2c_dev->data;
|
||||
uint32_t reg_base = get_regs(dw_i2c_dev);
|
||||
|
||||
uint32_t value;
|
||||
uint32_t start;
|
||||
int ret = 0;
|
||||
gpio_flags_t flags;
|
||||
int i;
|
||||
|
||||
LOG_DBG("starting bus recover");
|
||||
/* disable all interrupt mask */
|
||||
write_intr_mask(DW_DISABLE_ALL_I2C_INT, reg_base);
|
||||
/* enable controller to make sure function works */
|
||||
set_bit_enable_en(reg_base);
|
||||
|
||||
if (bus->state & I2C_DW_SDA_STUCK) {
|
||||
/*
|
||||
* initiate the SDA Recovery Mechanism
|
||||
* (that is, send at most 9 SCL clocks and STOP to release the
|
||||
* SDA line) and then this bit gets auto clear
|
||||
*/
|
||||
LOG_DBG("CLK Recovery Start");
|
||||
/* initiate the Master Clock Reset */
|
||||
start = k_uptime_get_32();
|
||||
set_bit_enable_clk_reset(reg_base);
|
||||
while (test_bit_enable_clk_reset(reg_base) &&
|
||||
(k_uptime_get_32() - start < RECOVERY_TIME)) {
|
||||
;
|
||||
}
|
||||
/* check if SCL bus clk is not reset */
|
||||
if (test_bit_enable_clk_reset(reg_base)) {
|
||||
LOG_ERR("ERROR: CLK recovery Fail");
|
||||
ret = -1;
|
||||
} else {
|
||||
LOG_DBG("CLK Recovery Success");
|
||||
}
|
||||
|
||||
LOG_DBG("SDA Recovery Start");
|
||||
start = k_uptime_get_32();
|
||||
set_bit_enable_sdarecov(reg_base);
|
||||
while (test_bit_enable_sdarecov(reg_base) &&
|
||||
(k_uptime_get_32() - start < RECOVERY_TIME)) {
|
||||
;
|
||||
}
|
||||
/* Check if bus is not clear */
|
||||
if (test_bit_status_sdanotrecov(reg_base)) {
|
||||
LOG_ERR("ERROR: SDA Recovery Fail");
|
||||
ret = -1;
|
||||
} else {
|
||||
LOG_DBG("SDA Recovery Success");
|
||||
}
|
||||
} else if (bus->state & I2C_DW_SCL_STUCK) {
|
||||
/* the controller initiates the transfer abort */
|
||||
LOG_DBG("ABORT transfer");
|
||||
start = k_uptime_get_32();
|
||||
set_bit_enable_abort(reg_base);
|
||||
while (test_bit_enable_abort(reg_base) &&
|
||||
(k_uptime_get_32() - start < RECOVERY_TIME)) {
|
||||
;
|
||||
}
|
||||
/* check if Controller is not abort */
|
||||
if (test_bit_enable_abort(reg_base)) {
|
||||
LOG_ERR("ERROR: ABORT Fail!");
|
||||
ret = -1;
|
||||
} else {
|
||||
LOG_DBG("ABORT success");
|
||||
}
|
||||
}
|
||||
value = read_clr_intr(reg_base);
|
||||
value = read_clr_tx_abrt(reg_base);
|
||||
/* disable controller */
|
||||
clear_bit_enable_en(reg_base);
|
||||
|
||||
/* Input type selection */
|
||||
flags = GPIO_INPUT | RTS5912_GPIO_SCHEN;
|
||||
/* Set SCL of I2C as GPIO pin */
|
||||
gpio_pin_configure_dt(&config->scl_gpios, flags);
|
||||
/* Get SCL GPIO status */
|
||||
gpio_pin_get_dt(&config->scl_gpios);
|
||||
|
||||
/* Output type selection */
|
||||
flags = GPIO_OUTPUT_HIGH | RTS5912_GPIO_SCHEN;
|
||||
/* Set SCL of I2C as GPIO pin */
|
||||
gpio_pin_configure_dt(&config->scl_gpios, flags);
|
||||
/* Set SDA of I2C as GPIO pin */
|
||||
gpio_pin_configure_dt(&config->sda_gpios, flags);
|
||||
|
||||
/* send a ACK */
|
||||
gpio_pin_set_dt(&config->sda_gpios, 0);
|
||||
k_busy_wait(10);
|
||||
gpio_pin_set_dt(&config->scl_gpios, 0);
|
||||
k_busy_wait(10);
|
||||
gpio_pin_set_dt(&config->sda_gpios, 1);
|
||||
k_busy_wait(10);
|
||||
|
||||
/* 9 cycles of SCL with SDA held high */
|
||||
for (i = 0; i < 9; i++) {
|
||||
gpio_pin_set_dt(&config->scl_gpios, 1);
|
||||
k_busy_wait(50);
|
||||
gpio_pin_set_dt(&config->scl_gpios, 0);
|
||||
k_busy_wait(50);
|
||||
}
|
||||
|
||||
/* send a stop bit */
|
||||
gpio_pin_set_dt(&config->sda_gpios, 0);
|
||||
k_busy_wait(10);
|
||||
gpio_pin_set_dt(&config->scl_gpios, 1);
|
||||
k_busy_wait(10);
|
||||
gpio_pin_set_dt(&config->sda_gpios, 1);
|
||||
k_busy_wait(10);
|
||||
|
||||
/* Set GPIO back to I2C alternate function */
|
||||
ret = pinctrl_apply_state(rom->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure I2C pins");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable controller */
|
||||
set_bit_enable_en(reg_base);
|
||||
|
||||
start = k_uptime_get_32();
|
||||
set_bit_enable_abort(reg_base);
|
||||
while (test_bit_enable_abort(reg_base) && (k_uptime_get_32() - start < RECOVERY_TIME)) {
|
||||
;
|
||||
}
|
||||
if (test_bit_enable_abort(reg_base)) {
|
||||
LOG_ERR("ERROR: ABORT Fail!");
|
||||
ret = -1;
|
||||
} else {
|
||||
LOG_DBG("ABORT success");
|
||||
}
|
||||
/* disable controller */
|
||||
clear_bit_enable_en(reg_base);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("ERROR: Bus Recover Fail, a slave device may be faulty or require a power "
|
||||
"reset");
|
||||
} else {
|
||||
LOG_DBG("BUS Recover success");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_rts5912_initialize(const struct device *dev)
|
||||
{
|
||||
const struct i2c_rts5912_config *const config = dev->config;
|
||||
int ret = 0;
|
||||
|
||||
/* Register our recovery routine with the DW I2C driver. */
|
||||
if (!device_is_ready(config->dw_i2c_dev)) {
|
||||
LOG_ERR("DW i2c not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
i2c_dw_register_recover_bus_cb(config->dw_i2c_dev, i2c_rts5912_recover_bus, dev);
|
||||
|
||||
if (!device_is_ready(config->clk_dev)) {
|
||||
LOG_ERR("clock source not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
ret = clock_control_on(config->clk_dev, (clock_control_subsys_t)&config->sccon_cfg);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("enable i2c[%s] clock source power fail", dev->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t reg_base = get_regs(config->dw_i2c_dev);
|
||||
/* clear enable register */
|
||||
clear_bit_enable_en(reg_base);
|
||||
/* disable block mode */
|
||||
clear_bit_enable_block(reg_base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define DEV_CONFIG_CLK_DEV_INIT(n) \
|
||||
.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
|
||||
.sccon_cfg = { \
|
||||
.clk_grp = DT_INST_CLOCKS_CELL(n, clk_grp), \
|
||||
.clk_idx = DT_INST_CLOCKS_CELL(n, clk_idx), \
|
||||
}
|
||||
|
||||
#define I2C_DEVICE_INIT_RTS5912(n) \
|
||||
static const struct i2c_rts5912_config i2c_rts5912_##n##_config = { \
|
||||
DEV_CONFIG_CLK_DEV_INIT(n), \
|
||||
.dw_i2c_dev = DEVICE_DT_GET(DT_INST_PHANDLE(n, dw_i2c_dev)), \
|
||||
.scl_gpios = GPIO_DT_SPEC_INST_GET(n, scl_gpios), \
|
||||
.sda_gpios = GPIO_DT_SPEC_INST_GET(n, sda_gpios)}; \
|
||||
I2C_DEVICE_DT_INST_DEFINE(n, i2c_rts5912_initialize, NULL, NULL, \
|
||||
&i2c_rts5912_##n##_config, POST_KERNEL, \
|
||||
CONFIG_I2C_RTS5912_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(I2C_DEVICE_INIT_RTS5912)
|
12
drivers/i2c/i2c_realtek_rts5912.h
Normal file
12
drivers/i2c/i2c_realtek_rts5912.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Realtek Semiconductor Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef ZEPHYR_DRIVERS_I2C_I2C_REALTEK_RTS5912_H_
|
||||
#define ZEPHYR_DRIVERS_I2C_I2C_REALTEK_RTS5912_H_
|
||||
|
||||
#include "i2c_dw.h"
|
||||
#include "i2c_dw_registers.h"
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_I2C_I2C_REALTEK_RTS5912_H_ */
|
|
@ -11,6 +11,7 @@
|
|||
#include <zephyr/dt-bindings/gpio/realtek-gpio.h>
|
||||
#include <zephyr/dt-bindings/pwm/pwm.h>
|
||||
#include <mem.h>
|
||||
#include <zephyr/dt-bindings/i2c/i2c.h>
|
||||
|
||||
/ {
|
||||
cpus {
|
||||
|
@ -444,6 +445,158 @@
|
|||
write-block-size = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_0: i2c@4000d000 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000d000 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <182 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_1: i2c@4000d200 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000d200 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <183 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_2: i2c@4000d400 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000d400 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <184 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_3: i2c@4000d600 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000d600 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <185 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_4: i2c@4000d800 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000d800 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <186 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_5: i2c@4000da00 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000da00 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <187 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_6: i2c@4000dc00 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000dc00 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <188 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
i2c_7: i2c@4000de00 {
|
||||
compatible = "snps,designware-i2c";
|
||||
reg = <0x4000de00 0x154>;
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
interrupt-parent = <&nvic>;
|
||||
interrupts = <189 1>;
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_0_wrapper: i2c_0_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_0>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C0_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_1_wrapper: i2c_1_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_1>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C1_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_2_wrapper: i2c_2_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_2>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C2_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_3_wrapper: i2c_3_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_3>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C3_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_4_wrapper: i2c_4_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_4>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C4_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_5_wrapper: i2c_5_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_5>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C5_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_6_wrapper: i2c_6_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_6>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C6_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c_7_wrapper: i2c_7_wrapper {
|
||||
compatible = "realtek,rts5912-i2c";
|
||||
dw-i2c-dev = <&i2c_7>;
|
||||
clocks = <&sccon RTS5912_SCCON_I2C I2C7_CLKPWR>;
|
||||
clock-names = "i2c";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
swj_port: swj-port {
|
||||
|
|
27
dts/bindings/i2c/realtek,rts5912-i2c.yaml
Normal file
27
dts/bindings/i2c/realtek,rts5912-i2c.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) 2025 Realtek, SIBG-SD7
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Realtek RTS5912 I2C node
|
||||
|
||||
compatible: "realtek,rts5912-i2c"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
dw-i2c-dev:
|
||||
type: phandle
|
||||
required: true
|
||||
description: |
|
||||
DesignWare i2c device
|
||||
|
||||
scl-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
The SCL pin for the selected port.
|
||||
|
||||
sda-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
The SDA pin for the selected port.
|
|
@ -20,3 +20,15 @@ properties:
|
|||
type: int
|
||||
description: |
|
||||
A fixed offset to apply to the SCL hcnt setting.
|
||||
|
||||
sda-timeout-value:
|
||||
type: int
|
||||
default: 30
|
||||
description: |
|
||||
Describe the SDA stuck at low timeout value, unit in ms
|
||||
|
||||
scl-timeout-value:
|
||||
type: int
|
||||
default: 30
|
||||
description: |
|
||||
Describe the SCL stuck at low timeout value, unit in ms
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue