[PATCH 8/8] i2c: nomadik: reduce FIFO flush timeout to avoid long interrupt handling

Dmitry Guzman Dmitry.Guzman at mobileye.com
Tue Nov 3 00:29:44 PST 2026


In flush_i2c_fifo(), the driver sets FTX and FRX flags and waits until
they are reset (these bits are self-clearing). If after the first read
one or both of these bits is set, it waits until both are reset. The
timeout is 10 * I2C transfer timeout. I2C transfer timeout is by default
200 milliseconds. 2 seconds are much more than needed: as stated in the
comment above, the flushing operation takes just several cycles before
completed.

flush_i2c_fifo() is called from init_hw() that may be called from
interrupt handler. So, if the I2C controller is stuck and the FIFO does
not get flushed, the CPU may spend up to 2 seconds in the interrupt
handler and cause watchdog timeout or other unwanted consequences.
Moreover, if the interrupt is handled by the same CPU core that updates
jiffies, the loop may not finish at all.

To avoid too long waiting, reduce the timeout to 100 microseconds and
use read_poll_timeout_atomic() instead of relying on jiffies.

Fixes: 3f9900f17cdb ("i2c: Add support for Ux500/Nomadik I2C controller")

Signed-off-by: Dmitry Guzman <Dmitry.Guzman at mobileye.com>
---
 drivers/i2c/busses/i2c-nomadik.c | 26 ++++++++++----------------
 1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index cc8b5a357f06..1f332e66a243 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -278,9 +278,8 @@ static inline void nmk_i2c_writeb(const struct nmk_i2c_dev *priv, u32 val,
  */
 static int flush_i2c_fifo(struct nmk_i2c_dev *priv)
 {
-#define LOOP_ATTEMPTS 10
-	ktime_t timeout;
-	int i;
+#define FIFO_FLUSH_TIMEOUT_US 100
+	u32 reg, err;
 
 	/*
 	 * flush the transmit and receive FIFO. The flushing
@@ -291,21 +290,16 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *priv)
 	 */
 	writel((I2C_CR_FTX | I2C_CR_FRX), priv->virtbase + I2C_CR);
 
-	for (i = 0; i < LOOP_ATTEMPTS; i++) {
-		timeout = ktime_add_us(ktime_get(), priv->timeout_usecs);
-
-		while (ktime_after(timeout, ktime_get())) {
-			if ((readl(priv->virtbase + I2C_CR) &
-				(I2C_CR_FTX | I2C_CR_FRX)) == 0)
-				return 0;
-		}
-	}
+	/* This may execute in interrupt handler, so use read_poll_timeout_atomic() */
+	err = read_poll_timeout_atomic(readl, reg, (reg & (I2C_CR_FTX | I2C_CR_FRX)) == 0,
+				       1, FIFO_FLUSH_TIMEOUT_US, 0, priv->virtbase + I2C_CR);
 
-	dev_err(&priv->adev->dev,
-		"flushing operation timed out giving up after %d attempts",
-		LOOP_ATTEMPTS);
+	if (err)
+		dev_err(&priv->adev->dev,
+			"flushing operation timed out giving up after %d microseconds",
+			FIFO_FLUSH_TIMEOUT_US);
 
-	return -ETIMEDOUT;
+	return err;
 }
 
 /**

-- 
2.43.0




More information about the linux-arm-kernel mailing list