diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index f2cb0a7763c..27fad63c27b 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -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) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 0ae73d78808..162e59d6b49 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -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" diff --git a/drivers/i2c/Kconfig.dw b/drivers/i2c/Kconfig.dw index d666b2aceb2..549dd20419b 100644 --- a/drivers/i2c/Kconfig.dw +++ b/drivers/i2c/Kconfig.dw @@ -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. diff --git a/drivers/i2c/Kconfig.rts5912 b/drivers/i2c/Kconfig.rts5912 new file mode 100644 index 00000000000..c82a82f0dee --- /dev/null +++ b/drivers/i2c/Kconfig.rts5912 @@ -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. diff --git a/drivers/i2c/i2c_dw.c b/drivers/i2c/i2c_dw.c index 1ee30a647dc..3136cbcd736 100644 --- a/drivers/i2c/i2c_dw.c +++ b/drivers/i2c/i2c_dw.c @@ -6,6 +6,11 @@ * * SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include + #include #include #include @@ -16,6 +21,7 @@ #include #include #include +#include #include #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, \ diff --git a/drivers/i2c/i2c_dw.h b/drivers/i2c/i2c_dw.h index 2a4f64cd86b..d6457cb1314 100644 --- a/drivers/i2c/i2c_dw.h +++ b/drivers/i2c/i2c_dw.h @@ -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 diff --git a/drivers/i2c/i2c_dw_registers.h b/drivers/i2c/i2c_dw_registers.h index 12966b6ab7d..b98ba31c4a1 100644 --- a/drivers/i2c/i2c_dw_registers.h +++ b/drivers/i2c/i2c_dw_registers.h @@ -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 } diff --git a/drivers/i2c/i2c_realtek_rts5912.c b/drivers/i2c/i2c_realtek_rts5912.c new file mode 100644 index 00000000000..bd863ace3a4 --- /dev/null +++ b/drivers/i2c/i2c_realtek_rts5912.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2025 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c_realtek_rts5912.h" +#include + +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) diff --git a/drivers/i2c/i2c_realtek_rts5912.h b/drivers/i2c/i2c_realtek_rts5912.h new file mode 100644 index 00000000000..828dbab8520 --- /dev/null +++ b/drivers/i2c/i2c_realtek_rts5912.h @@ -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_ */ diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index f29d1212ba0..8a9f2d719bc 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include / { cpus { @@ -444,6 +445,158 @@ write-block-size = <4>; }; }; + + i2c_0: i2c@4000d000 { + compatible = "snps,designware-i2c"; + reg = <0x4000d000 0x154>; + clock-frequency = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 { diff --git a/dts/bindings/i2c/realtek,rts5912-i2c.yaml b/dts/bindings/i2c/realtek,rts5912-i2c.yaml new file mode 100644 index 00000000000..d7591f2d45e --- /dev/null +++ b/dts/bindings/i2c/realtek,rts5912-i2c.yaml @@ -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. diff --git a/dts/bindings/i2c/snps,designware-i2c.yaml b/dts/bindings/i2c/snps,designware-i2c.yaml index cc02989d927..653ddbc199a 100644 --- a/dts/bindings/i2c/snps,designware-i2c.yaml +++ b/dts/bindings/i2c/snps,designware-i2c.yaml @@ -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