[PATCH] i2c: designware: fix i2c probe error
Coben Han
cobenhan at mediastek.com
Sat May 30 17:35:34 CEST 2026
Probing i2c slave device just needs its device address.
A certain type of chipsets such as RTC S35390A have no offset,
which means address length should be 0.
But the current designware_i2c_probe_chip function requires
offset 0 and its length 1.
This causes the designware-i2c to be initialized again and again.
Furthermore, after booting into kernel, the designware-i2c kernel
driver complains its controller timeout.
This patch fixes this misbehaviour in such way that only device
address is issued, no reinitialization needs when target device
not detected, the designware-i2c kernel driver continues working.
Signed-off-by: Coben Han <cobenhan at mediastek.com>
---
drivers/i2c/designware_i2c.c | 45 +++++++++++++++++++++++++++++++-----
1 file changed, 39 insertions(+), 6 deletions(-)
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index 8ad716f410e..69aeb266612 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -740,13 +740,46 @@ static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
{
struct dw_i2c *i2c = dev_get_priv(bus);
struct i2c_regs *i2c_base = i2c->regs;
- u32 tmp;
- int ret;
+ u32 start_time, ic_status;
+ int ret = 0;
- /* Try to read the first location of the chip */
- ret = __dw_i2c_read(i2c_base, chip_addr, 0, 1, (uchar *)&tmp, 1);
- if (ret)
- __dw_i2c_init(i2c_base, 0, 0);
+ if (i2c_wait_for_bb(i2c_base) < 0)
+ return 1;
+
+ dw_i2c_enable(i2c_base, false);
+ writel(chip_addr, &i2c_base->ic_tar);
+ dw_i2c_enable(i2c_base, true);
+
+ writel(IC_STOP, &i2c_base->ic_cmd_data);
+
+ start_time = get_timer(0);
+ while (1) {
+ ic_status = readl(&i2c_base->ic_status);
+
+ if ((ic_status & IC_STATUS_TFE) && !(ic_status & IC_STATUS_MA))
+ break;
+
+ if (readl(&i2c_base->ic_raw_intr_stat) & IC_TX_ABRT) {
+ readl(&i2c_base->ic_clr_tx_abrt);
+ ret = -EREMOTEIO;
+ break;
+ }
+
+ if (get_timer(start_time) > I2C_BYTE_TO) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ start_time = get_timer(0);
+ while (1) {
+ if ((readl(&i2c_base->ic_raw_intr_stat) & IC_STOP_DET)) {
+ readl(&i2c_base->ic_clr_stop_det);
+ break;
+ } else if (get_timer(start_time) > I2C_STOPDET_TO) {
+ break;
+ }
+ }
return ret;
}
--
2.34.1
More information about the U-Boot
mailing list