[PATCH v2 1/2] i2c: imx: Don't recover bus when arbitration lost
Dan Scally
dan.scally at ideasonboard.com
Tue May 19 01:42:21 PDT 2026
Hello Oleksij / all
On 24/04/2026 13:36, Daniel Scally wrote:
> In i2c_imx_xfer_common(), the driver attempts bus recovery whenever
> i2c_imx_start() fails. One of the failure modes for i2c_imx_start()
> is an arbitration-lost signal which results when a second I2C master
> on the bus tries to control the bus simultaneously, which is a normal
> and expected behaviour.
>
> Bus recovery is not the right response for this case. Add a check for
> the -EAGAIN return code to avoid running the bus recovery.
>
> Fixes: 1c4b6c3bcf30d ("i2c: imx: implement bus recovery")
> Signed-off-by: Daniel Scally <dan.scally at ideasonboard.com>
> ---
I raised this patch after we had issues with one of the i2c controllers on imx8mp. In that case, the
bus had multiple masters that were causing the SoC's i2c controller to lose arbitration. The result
was that the framework attempted to run i2c_generic_scl_recovery() and regularly hit the "SCL is
stuck low, exit recovery" message [1] because the bus was busy rather than stuck.
I'm now experiencing a different issue with the imx8mp in which a different controller - which isn't
on a multiple-masters bus - starts transacting fine early in boot, but then seems to get stuck - any
attempt to start a transaction by either a driver or i2ctransfer results in the IAL bit in I2C_I2SR
being set and so the driver reports that it's lost arbitration [2]. In this case, the bus recovery
is needed to fix the problem, and so this commit hurts things rather than helps them. This problem
isn't consistent - I get it on maybe 10% of boots.
I'm trying to find a way to handle the two problems together. I can move the retries into the
i2x-imx driver like below, and that works so far:
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index a208fefd3c3b3..cee333236646a 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -1551,7 +1551,20 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
int use_dma = 0;
/* Start I2C transfer */
- result = i2c_imx_start(i2c_imx, atomic);
+ for (unsigned int try = 0; try <= 3; try++) {
+ result = i2c_imx_start(i2c_imx, atomic);
+ if (result == -EAGAIN) {
+ i2c_imx_stop(i2c_imx, atomic);
+
+ if (i2c_imx->slave)
+ i2c_imx_slave_init(i2c_imx);
+
+ continue;
+ }
+
+ break;
+ }
+
if (result) {
/*
* Bus recovery uses gpiod_get_value_cansleep() which is not
That way it retries 3 times if i2c_bus_busy() reports -EAGAIN from lost arbitration, but then gives
up and runs the bus recovery if none of the retries works. I do wonder why the i2c controller is
setting the IAL bit though given there's no other master on the bus. If I use a logic analyser to
track traffic on the bus I can't see anything anomalous, so I thought I'd check whether anyone has
experienced something similar before.
[1] https://elixir.bootlin.com/linux/v7.0.1/source/drivers/i2c/i2c-core-base.c#L254
[2] https://elixir.bootlin.com/linux/v7.0.1/source/drivers/i2c/busses/i2c-imx.c#L548
> drivers/i2c/busses/i2c-imx.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> index a208fefd3c3b35672a00eda8448f24859aaa793a..b68a0f7105682006bbcfee52891c9a9c2d8c009e 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c
> @@ -1552,7 +1552,7 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
>
> /* Start I2C transfer */
> result = i2c_imx_start(i2c_imx, atomic);
> - if (result) {
> + if (result && result != -EAGAIN) {
> /*
> * Bus recovery uses gpiod_get_value_cansleep() which is not
> * allowed within atomic context.
>
More information about the linux-arm-kernel
mailing list