[PATCH] i2c: rk3x: Disable the controller between transfers
David Wu
david.wu at rock-chips.com
Mon Oct 19 19:20:33 PDT 2026
Hi Alexey,
There's no deterministic way to reproduce this, looping suspend/resume
seems to be able to trigger it eventually. The problem case is IEN != 0
while pclk is already disabled: the irq handler doesn't re-enable pclk,
so it can't actually clear the interrupt, and you get "irq in
STATE_IDLE" printing forever. So the extra IEN write here is just a
belt-and-suspenders safeguard.
在 2026/10/20 08:48, Shawn Lin 写道:
> Hi
>
> 在 2026/10/20 星期二 0:50, Alexey Charkov 写道:
>> Hi Shawn,
>>
>> On Fri, Oct 16, 2026 at 4:33 AM Shawn Lin <shawn.lin at rock-chips.com>
>> wrote:
>>>
>>> Cc: + David
>>>
>>> 在 2026/10/15 星期四 22:00, Alexey Charkov 写道:
>>>> The driver leaves the controller enabled once a transfer completes, and
>>>> an enabled controller keeps tracking the bus. If the lines collapse
>>>> under
>>>> it while idle, as happens when a target's supply is switched off while
>>>> its bus pull-ups stay powered, its state machine wedges driving SDA
>>>> low.
>>>> Every later transfer then times out, since the timeout path only
>>>> requests
>>>> a STOP and never disables the controller, and the stuck state even
>>>> survives a kexec.
>>>>
>>>> Clear the enable bit once each transfer is done, successful or not, so
>>>> that the controller does not watch an idle bus and starts every
>>>> transfer
>>>> from a clean state.
>>>>
>>>
>>> I checked the vendor kernel tree and saw another case that you perhaps
>>> need to fold something similar below into your patch, along with your
>>> disabling controller?
>>>
>>> In some case,log like this:
>>>
>>> [ 12.393926] rk3x-i2c ff150000.i2c: irq in STATE_IDLE, ipd =
>>> 0x51
>>> [ 12.416592] rk3x-i2c ff150000.i2c: irq in STATE_IDLE, ipd =
>>> 0x51
>>>
>>> The i2c clock is disabled, so the pending irq clean is not
>>> worked. Disable the interrupt after the i2c jobs were done,
>>> the error log would not happen.
>>>
>>>
>>> /* Disable i2c all irqs */
>>> #define IEN_ALL_DISABLE 0
>>>
>>> i2c_writel(i2c, IEN_ALL_DISABLE, REG_IEN);i2c_writel(i2c,
>>> IEN_ALL_DISABLE, REG_IEN);
>>
>> Thanks for looking into this! Do you happen to have a reproducer or
>> any additional info on how this state gets triggered? I haven't run
>> into it in my setups - did I just get lucky? :)
>>
>
> Unfortunately, I don't. That came from what I found when checking
> the vendor kernel commit to see how we handled your case.
>
> So I have looped David who made the internal submision for the same case
> as yours 8 years ago and hope him could have a comment here.
>
>> I see the same write of 0 to REG_IEN just above my changed lines for
>> the case if (time_left == 0), which makes sense because a
>> too-long-running transfer might eventually complete and trigger the
>> interrupt with the controller disabled, unless the interrupt is
>> disabled too. For other cases I'd be interested to know what can raise
>> the interrupt.
>>
>> Best regards,
>> Alexey
>>
>
>
More information about the Linux-rockchip
mailing list