[v2,2/3] i2c: imx: Check for I2SR_IAL after every byte

Message ID 20201002152305.4963-3-ceggers@arri.de
State New
Headers show
Series
  • i2c: imx: Fix handling of arbitration loss
Related show

Commit Message

Christian Eggers Oct. 2, 2020, 3:23 p.m.
Arbitration Lost (IAL) can happen after every single byte transfer. If
arbitration is lost, the I2C hardware will autonomously switch from
master mode to slave. If a transfer is not aborted in this state,
consecutive transfers will not be executed by the hardware and will
timeout.

Signed-off-by: Christian Eggers <ceggers@arri.de>
Cc: stable@vger.kernel.org
---
 drivers/i2c/busses/i2c-imx.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

Comments

Krzysztof Kozlowski Oct. 5, 2020, 8:07 a.m. | #1
On Fri, Oct 02, 2020 at 05:23:04PM +0200, Christian Eggers wrote:
> Arbitration Lost (IAL) can happen after every single byte transfer. If
> arbitration is lost, the I2C hardware will autonomously switch from
> master mode to slave. If a transfer is not aborted in this state,
> consecutive transfers will not be executed by the hardware and will
> timeout.
> 
> Signed-off-by: Christian Eggers <ceggers@arri.de>
> Cc: stable@vger.kernel.org
> ---
>  drivers/i2c/busses/i2c-imx.c | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> 

Tested (not extensively) on Vybrid VF500 (Toradex VF50):
Tested-by: Krzysztof Kozlowski <krzk@kernel.org>

The I2C on Vybrid VF500 still works fine. I did not test this actual
condition (arbitration) but only a regular I2C driver (BQ27xxx fuel
gauge). Obviously this only proves that regular operation is not
broken...

Alternatively if you have a specific testing procedure (reproduction of
a problem), please share.

Best regards,
Krzysztof
Christian Eggers Oct. 5, 2020, 9:25 a.m. | #2
On Monday, 5 October 2020, 10:07:25 CEST, Krzysztof Kozlowski wrote:
> The I2C on Vybrid VF500 still works fine. I did not test this actual
> condition (arbitration) but only a regular I2C driver (BQ27xxx fuel
> gauge). Obviously this only proves that regular operation is not
> broken...
thank you very much for testing on Vybrid.

> Alternatively if you have a specific testing procedure (reproduction of
> a problem), please share.

The IAL errors happen due to noise on our I2C bus. We have our power supply 
connected via I2C. The hardware designers wanted to make sure that no
high currents flow through the ground pins of the I2C interface. So they added 
a series resistor (30 Ohm) in the GND line between the power supply and the 
i.MX board.

If you have an I2C device on an external PCB, adding some small series 
resistance in the GND line may cause IAL errors. On the other hand, if 
everything else works fine, also handling if IAL should work on Vybrid.

> Best regards,
> Krzysztof
Best regards
Christian

Patch

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 34648df7f1a6..1db1e2f5296e 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -483,6 +483,24 @@  static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
 		dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
 		return -ETIMEDOUT;
 	}
+
+	/* check for arbitration lost */
+	if (i2c_imx->i2csr & I2SR_IAL) {
+		unsigned int temp;
+
+		dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__);
+		/*
+		 * i2sr_clr_opcode is the value to clear all interrupts.
+		 * Here we want to clear only I2SR_IAL, so we write
+		 * ~i2sr_clr_opcode with just the I2SR_IAL bit toggled.
+		 */
+		temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ I2SR_IAL;
+		imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
+
+		i2c_imx->i2csr = 0;
+		return -EAGAIN;
+	}
+
 	dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
 	i2c_imx->i2csr = 0;
 	return 0;