Skip to content

drivers: i2c: rts5912 i2c dirver #89386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 29, 2025

Conversation

Titan-Realtek
Copy link
Contributor

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

@@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS is deprecated and will be removed. The RTS5912 driver needs to work without this option.

Copy link
Contributor Author

@Titan-Realtek Titan-Realtek May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, Keith
currently I need to enable the CONFIG to allow driver do not send a STOP bit at a special i2c msg combination,
such as CROS i2c_controller.c , i2c_read_sized_block() funciton, the first message transaction only has START flag and without STOP flag :

/*
* Send device reg space offset, and read back block length.
* Keep this session open without a stop.
*/
rv = i2c_xfer_unlocked(port, addr_flags, &reg, 1, &block_length,
					1, I2C_XFER_START);

if I don't enable this CONFIG , ZEPHYR i2c transport layer will force to add STOP bit

if (!IS_ENABLED(CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS)) {
	msgs[num_msgs - 1].flags |= I2C_MSG_STOP;
}

then after first transaction done, the dw i2c ip will setup again and send a START+ADDR at second message.
so, I need to enable the flag and combine the new variable "need_setup" to control the IP don't setup again and continue to read the data with STOP flag.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan to deprecate CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS has been delayed. This is okay for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it, thanks.

Comment on lines 51 to 54
#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_STOP_DET | DW_INTR_STAT_SCL_STUCK_LOW)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#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_STOP_DET | DW_INTR_STAT_SCL_STUCK_LOW)
#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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@kartben kartben requested a review from Copilot May 3, 2025 01:00
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements the Realtek RTS5912 I2C driver based on the DesignWare I2C driver and enhances bus recovery, error handling, and initialization. Key changes include support for a customizable bus recovery function (including stuck‐at‐low handling and bus clear), a fix for ISR timing issues via TX empty control, and the integration of a new DTS binding with additional timeout and GPIO parameters.

Reviewed Changes

Copilot reviewed 6 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
dts/bindings/i2c/realtek,rts5912-i2c.yaml Adds DTS binding for RTS5912 with new properties for timeout values and GPIO pins.
drivers/i2c/i2c_realtek_rts5912.h Introduces the header file for the RTS5912 driver.
drivers/i2c/i2c_realtek_rts5912.c Implements the RTS5912 driver functions including bus recovery and initialization.
drivers/i2c/i2c_dw_registers.h Updates register definitions and adds new bit-fields used by the RTS5912 driver.
drivers/i2c/i2c_dw.h Adjusts the DW driver header structures and replaces mutex usage with a semaphore.
drivers/i2c/i2c_dw.c Updates the DW core implementation with enhanced error handling, recovery callbacks, and a fix for pending interrupt clearing.
Files not reviewed (5)
  • drivers/i2c/CMakeLists.txt: Language not supported
  • drivers/i2c/Kconfig: Language not supported
  • drivers/i2c/Kconfig.dw: Language not supported
  • drivers/i2c/Kconfig.rts5912: Language not supported
  • dts/arm/realtek/ec/rts5912.dtsi: Language not supported

LOG_ERR("ERROR: ABORT Fail!");
ret = false;
} else {
LOG_DBG("ABORT seccuess");
Copy link
Preview

Copilot AI May 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in log message: 'seccuess' should be changed to 'success'.

Suggested change
LOG_DBG("ABORT seccuess");
LOG_DBG("ABORT success");

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

LOG_ERR("ERROR: ABORT Fail!");
ret = false;
} else {
LOG_DBG("ABORT seccuess");
Copy link
Preview

Copilot AI May 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in log message: 'seccuess' should be changed to 'success'.

Suggested change
LOG_DBG("ABORT seccuess");
LOG_DBG("ABORT success");

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

uint32_t value;
uint32_t reg_base = get_regs(dev);

write_intr_mask(DW_DISABLE_ALL_I2C_INT, reg_base);
value = read_clr_intr(reg_base);

/* clear pedning interrupt */
Copy link
Preview

Copilot AI May 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in the comment: 'pedning' should be corrected to 'pending'.

Suggested change
/* clear pedning interrupt */
/* clear pending interrupt */

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


uint32_t value;
uint32_t start;
int ret = true;
Copy link
Preview

Copilot AI May 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider initializing 'ret' with 0 for success rather than 'true' to clearly indicate that 0 is the success code.

Suggested change
int ret = true;
int ret = 0;

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@Titan-Realtek Titan-Realtek force-pushed the drivers_i2c_dw_rts5912 branch 12 times, most recently from f82d638 to 3851d51 Compare May 8, 2025 00:06
# Copyright (c) 2025 Realtek, SIBG-SD7
# SPDX-License-Identifier: Apache-2.0

config I2C_DW_EXTENDED_SUPPORT
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be defined in Kconfig.dw

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 209 to 210
uint32_t sda_timeout = config->sda_timeout_value * CONFIG_I2C_DW_CLOCK_SPEED * 1000;
uint32_t scl_timeout = config->scl_timeout_value * CONFIG_I2C_DW_CLOCK_SPEED * 1000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be moved into the i2c.dw.c, guarded with CONFIG_I2C_DW_EXTENDED_SUPPORT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

Comment on lines 30 to 32
#define REALTEK_RTS5912_PINMUX_GET_GPIO_PIN(n) \
(((((n) >> REALTEK_RTS5912_GPIO_LOW_POS) & REALTEK_RTS5912_GPIO_LOW_MSK)) | \
(((((n) >> REALTEK_RTS5912_GPIO_HIGH_POS) & REALTEK_RTS5912_GPIO_HIGH_MSK)) << 5))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

Comment on lines 148 to 152
/* set high level to scl and sda line */
*GPIO_SCL = 0x28003;
*GPIO_SDA = 0x28003;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The i2c_bitbang driver already provides the code to recovery the bus using GPIOs.

You don't need to write into the Realtek GPIO registers directly.

Look at the existing uses of i2c_bitbang_recover_bus() in the tree for examples of you to use the driver.

Copy link
Contributor Author

@Titan-Realtek Titan-Realtek May 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried using bitbang to recover the bus signal but the RTMR clock is too low to calculate the delayed HW cycles in i2c_bitbang.c.
Therefore, I still used my own code to restore the bus signal.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still shouldn't access the GPIO registers directly. The devicetree node should include phandles for the GPIO pins used, and then you can use gpio_pin_get_dt to read the pin state and gpio_pin_set_dt to change the pin state.

You will also need to use gpio_pin_configure at the start of the recover sequence and then call pinctrl_apply_state at the end to restore the pins back to the I2C function.

The ITE I2C driver provides an example of this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@Titan-Realtek Titan-Realtek force-pushed the drivers_i2c_dw_rts5912 branch 4 times, most recently from 1d198d2 to afe4808 Compare May 14, 2025 12:42
@@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan to deprecate CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS has been delayed. This is okay for now.

description: |
DesignWare i2c device

sda-gpio-pin:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the a GPIO phandles;

  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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its kind of horrible to be frank that our devicetree setup wants this to be "gpios" plural (we regex on this suffix...) with a phandle-array type, but yes this is the right way to do it for zephyr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 148 to 152
/* set high level to scl and sda line */
*GPIO_SCL = 0x28003;
*GPIO_SDA = 0x28003;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still shouldn't access the GPIO registers directly. The devicetree node should include phandles for the GPIO pins used, and then you can use gpio_pin_get_dt to read the pin state and gpio_pin_set_dt to change the pin state.

You will also need to use gpio_pin_configure at the start of the recover sequence and then call pinctrl_apply_state at the end to restore the pins back to the I2C function.

The ITE I2C driver provides an example of this


#ifdef CONFIG_CPU_CORTEX_M
/* clear pending interrupt */
NVIC_ClearPendingIRQ(rom->irqnumber);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teburd Is there an architecture generic call to clear a pending interrupt?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything in zephyr/arch/arch_interface.h for this, and I see a lot of drivers directly manipulating the nvic with these cmsis functions, so I'd say no

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I also see a lot of drivers directly call the nvic funcitons, but I got [Run tests with twister / twister-build (1) (pull_request)] Failed if I don't use CONFIG_CPU_CORTEX_M to separate.
does anyone know what's happen?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This driver is compiled by multiple architectures. NVIC_ClearPendingIRQ() is only provided by the CORTEX_M architecture.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ifdef arch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try CONFIG_ARM to test but [Run tests with twister / twister-build (1) (pull_request)] still failed, so I back to use CONFIG_CPU_CORTEX_M

if (ret) {
goto error;
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
if (dw->need_setup) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this check into i2c_dw_setup. It will make the code easier to read.

static int i2c_dw_setup(const struct device *dev, uint16_t slave_address)
{
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
	if (!dw->need_setup) {
		return 0;
	}
#endif

	/* Existing code ... */

#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
	dw->need_setup = false
#endif
	return 0;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 727 to 731
if ((test_bit_status_activity(reg_base) || (dw->state & I2C_DW_BUSY))
#if CONFIG_I2C_ALLOW_NO_STOP_TRANSACTIONS
&& dw->need_setup
#endif
) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline #ifdef here is hard to read.

Create a helper function to hide the details.

bool i2c_dw_is_busy(dev) {
	struct i2c_dw_dev_config *const dw = dev->data;

#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;
}
111

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 1342 to 1343
.sda_timeout_value = DT_INST_PROP_OR(n, sda_timeout_value, 0), \
.scl_timeout_value = DT_INST_PROP_OR(n, scl_timeout_value, 0), \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guard with CONFIG_I2C_DW_EXTENDED_SUPPORT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@Titan-Realtek Titan-Realtek force-pushed the drivers_i2c_dw_rts5912 branch 4 times, most recently from e19183c to 3e37c3a Compare May 19, 2025 05:38
Copy link
Collaborator

@keith-zephyr keith-zephyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is big improvement over the original copy/pasted driver. Thank you for making the changes.

Overall, looks pretty good. I do wonder whether the i2c_bitbang support for bus recovery can be enhanced to work with a slower system clock instead of creating a realtek specific implementation.

/* 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these delays need to be different based on the configured I2C clock rate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, Keith
Since we don't know what the external pull-up resistor is, I set the clock to a low enough frequency, like 10KHz, to ensure the waveform has enough rise time for all devices total capacitance.

@Titan-Realtek Titan-Realtek force-pushed the drivers_i2c_dw_rts5912 branch 2 times, most recently from 5212ed2 to 582d0c8 Compare May 22, 2025 06:16
@@ -341,12 +398,18 @@ static int i2c_dw_data_send(const struct device *dev)
static inline void i2c_dw_transfer_complete(const struct device *dev)
{
struct i2c_dw_dev_config *const dw = dev->data;
#ifdef CONFIG_CPU_CORTEX_M
const struct i2c_dw_rom_config *const rom = dev->config;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit - move the rom definition into the #ifdef block at line 409.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

} else if (dw->state & (I2C_DW_NACK | I2C_DW_CMD_ERROR)) {
ret = -EIO;
}
k_sem_reset(&dw->device_sync_sem);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other I2C transfer errors are not resetting the semaphore. I don't think this is needed on this path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

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 <[email protected]>
support errors check for
1. tx_abrt: nack and sda stuck low
2. scl stuck low

Signed-off-by: Titan Chen <[email protected]>
@Titan-Realtek Titan-Realtek force-pushed the drivers_i2c_dw_rts5912 branch from 582d0c8 to ddf91b8 Compare May 29, 2025 05:36
Copy link

@kartben kartben merged commit 13a0242 into zephyrproject-rtos:main May 29, 2025
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants