From ae60961b5f0f15914c2eff950f22b6afb594f88c Mon Sep 17 00:00:00 2001 From: jflecool2 <1393416+jflecool2@users.noreply.github.com> Date: Thu, 5 Nov 2026 16:11:06 -0800 Subject: [PATCH] i2c: designware: Fix SDA hold default being computed before LCNT The "SDA hold time = LCNT / 2" default added by commit "i2c: designware: Make the SDA hold time half LCNT" (see raspberrypi/linux#6091) is applied in i2c_dw_probe(), before i2c_dw_probe_master() has run i2c_dw_set_timings_master() to populate dev->fs_lcnt. fs_lcnt is therefore still 0, the default evaluates to 0, and i2c_dw_set_sda_hold() keeps the hardware reset value (1 cycle, ~5 ns) instead of the intended ~3 us. On a Pi 5 (200 MHz clk_sys, 100 kHz bus) this leaves IC_SDA_HOLD at 0x10001 rather than 0x10251 as on 6.18, and reintroduces the early-ACK failures that #6091 and #6056 fixed: an Adafruit PiOLED (SSD1306, 0x3c) fails with "SDA stuck at low" / "Remote I/O error" on 7.3.1 while it works on 6.18. Do the lcnt/2 default and i2c_dw_set_sda_hold() after i2c_dw_probe_master(), so fs_lcnt is valid. i2c_dw_init(), which writes the register, still runs afterwards. Tested on a Pi 5 running 7.3.1: IC_SDA_HOLD reads 0x10251 and the OLED works. Assisted-by: GitHub Copilot CLI:claude-sonnet-5.5 Signed-off-by: jflecool2 <1393416+jflecool2@users.noreply.github.com> --- drivers/i2c/busses/i2c-designware-common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index a4efb891da9fac..2eb5cba21ffb45 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -949,18 +949,18 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) if (ret) return ret; - if (!dev->sda_hold_time) - dev->sda_hold_time = dev->fs_lcnt / 2; - - ret = i2c_dw_set_sda_hold(dev); + ret = i2c_dw_set_fifo_size(dev); if (ret) return ret; - ret = i2c_dw_set_fifo_size(dev); + ret = i2c_dw_probe_master(dev); if (ret) return ret; - ret = i2c_dw_probe_master(dev); + if (!dev->sda_hold_time) + dev->sda_hold_time = dev->fs_lcnt / 2; + + ret = i2c_dw_set_sda_hold(dev); if (ret) return ret;