Skip to content

i2c: designware: Fix SDA hold default being computed before LCNT - #7741

Merged
pelwell merged 1 commit into
raspberrypi:rpi-7.3.yfrom
jflecool2:i2c-designware-sda-hold-fix
Nov 6, 2026
Merged

pelwell merged 1 commit into
raspberrypi:rpi-7.3.yfrom
jflecool2:i2c-designware-sda-hold-fix

Conversation

@jflecool2

Copy link
Copy Markdown

Fixes #7740.

Disclosure: this PR was prepared by an AI assistant (GitHub Copilot CLI, Claude Sonnet 5.5) on behalf of @jflecool2, who owns the hardware and is responsible for the change.

The "SDA hold = LCNT / 2" default from #6091 is computed in i2c_dw_probe() before i2c_dw_probe_master() fills in fs_lcnt, so it is always 0 on rpi-7.x and the hardware reset value (~5 ns) is used. This moves the default and i2c_dw_set_sda_hold() after i2c_dw_probe_master(). i2c_dw_init() (which writes the register) still runs afterwards.

Tested on Pi 5 (7.3.1, custom build): IC_SDA_HOLD 0x10001 -> 0x10251 (same as 6.18), Adafruit PiOLED SSD1306 works again.

Not tested: other boards, slave mode. The same ordering is present in rpi-7.0.y, 7.1.y and 7.2.y, so this will need backporting there.

The "SDA hold time = LCNT / 2" default added by commit "i2c: designware:
Make the SDA hold time half LCNT" (see raspberrypi#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
raspberrypi#6091 and raspberrypi#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>
@popcornmix

Copy link
Copy Markdown
Collaborator

Claude review:

Thanks for the clear report and the register readings — the analysis is correct.

Upstream's "Combine the init functions" rework moved i2c_dw_set_sda_hold() ahead of i2c_dw_probe_master(), and when "i2c: designware: Make the SDA hold time half LCNT" was rebased on top of it the fs_lcnt / 2 default came along with it, so it now always evaluates to 0 and the 1-cycle reset value is kept.

The reordering looks right to me:

  • i2c_dw_probe_master() doesn't read sda_hold_time, so moving i2c_dw_set_sda_hold() after it has no side effects there.
  • i2c_dw_init() (which writes DW_IC_SDA_HOLD) still runs after both.
  • Slave mode is fine too: i2c_dw_probe_master() is called unconditionally from i2c_dw_probe(), so fs_lcnt is populated in either mode.
  • An explicit i2c-sda-hold-time-ns from DT/ACPI still takes precedence, as before.

The same change applies cleanly to rpi-7.4.y as well.

After merging, this can be squashed into the original "Make the SDA hold time half LCNT" commit at the next rebase.

@pelwell
pelwell merged commit e4d5c8f into raspberrypi:rpi-7.3.y Nov 6, 2026
11 of 12 checks passed
@pelwell

pelwell commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants