linux-gbmc: i2c-npcm7xx.c: add msleep in polling loop to avoid RCU stall
When some malfunctional I2C device hangs the bus, the npcm_i2c_master_xfer
will almost stall the current CPU because there is a do {} while loop (used to
detect I2C bus idle condition) which will keep calling spin_lock_irqsave again and again.
Adding a msleep_interruptible(10) can help release the current CPU so it can do
other tasks.
Tested:
Before the fix, when dealing with hanging I2C device:
kernel: i2c i2c-10: __i2c_transfer failed -11
kernel: rcu: INFO: rcu_sched self-detected stall on CPU
After the fix, when dealing with hanging I2C device, there
is only :
kernel: i2c i2c-6: __i2c_transfer failed -11
Also, we built staging image 91.23.18.200 and tested passed on 3 machines.
Google-Bug-Id: 295580753
Change-Id: I96538284a7d11fe19374a2f17696bf2e0f3ba29a
Signed-off-by: Jinliang Wang <jinliangw@google.com>
(cherry picked from commit 36886d473729667e51852f4c61de5661181dedaa)
diff --git a/recipes-kernel/linux/files/0001-i2c-npcm-quirk-to-fix-multiple-master-xfer-failure.patch b/recipes-kernel/linux/files/0001-i2c-npcm-quirk-to-fix-multiple-master-xfer-failure.patch
index d7661f1..4f5427e 100644
--- a/recipes-kernel/linux/files/0001-i2c-npcm-quirk-to-fix-multiple-master-xfer-failure.patch
+++ b/recipes-kernel/linux/files/0001-i2c-npcm-quirk-to-fix-multiple-master-xfer-failure.patch
@@ -1,4 +1,4 @@
-From 7f6c7de2533bab63212a164b29e1fa352880a05b Mon Sep 17 00:00:00 2001
+From 3d32e67d9488850502873ee61bd85666cf460873 Mon Sep 17 00:00:00 2001
From: Jinliang Wang <jinliangw@google.com>
Date: Tue, 28 Mar 2023 11:16:27 -0700
Subject: [PATCH] i2c: npcm: quirk to fix multiple master xfer failure
@@ -20,11 +20,11 @@
Signed-off-by: William A. Kennington III <wak@google.com>
---
- drivers/i2c/busses/i2c-npcm7xx.c | 62 ++++++++++++++++++++++++--------
- 1 file changed, 48 insertions(+), 14 deletions(-)
+ drivers/i2c/busses/i2c-npcm7xx.c | 69 +++++++++++++++++++++++++-------
+ 1 file changed, 54 insertions(+), 15 deletions(-)
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
-index bfef6ae45988..f2a4a7ee14f2 100644
+index bfef6ae45988..a255d152734f 100644
--- a/drivers/i2c/busses/i2c-npcm7xx.c
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -433,6 +433,7 @@ struct npcm_i2c {
@@ -131,7 +131,18 @@
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (!bus_busy && bus->slave)
iowrite8((bus->slave->addr & 0x7F),
-@@ -2225,15 +2245,19 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2220,20 +2240,29 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ #endif
+ spin_unlock_irqrestore(&bus->lock, flags);
+
+- } while (time_is_after_jiffies(time_left) && bus_busy);
++ if (time_is_after_jiffies(time_left) && bus_busy)
++ msleep_interruptible(10);
++ else
++ break;
++
++ } while (true);
+
bus->dest_addr = slave_addr << 1;
if (bus_busy || bus->ber_state) {
iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
@@ -155,7 +166,7 @@
bus->read_block_use = read_block;
reinit_completion(&bus->cmd_complete);
-@@ -2258,8 +2282,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2258,8 +2287,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (bus->timeout_cnt < ULLONG_MAX)
bus->timeout_cnt++;
if (bus->master_or_slave == I2C_MASTER) {
@@ -165,7 +176,7 @@
bus->state = I2C_IDLE;
}
}
-@@ -2267,7 +2290,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2267,7 +2295,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
/* if there was BER, check if need to recover the bus: */
if (bus->cmd_err == -EAGAIN)
@@ -174,7 +185,7 @@
/*
* After any type of error, check if LAST bit is still set,
-@@ -2282,6 +2305,15 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2282,6 +2310,15 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
npcm_i2c_stall_after_start(bus, false);
npcm_i2c_eob_int(bus, false);
@@ -190,7 +201,7 @@
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* reenable slave if it was enabled */
if (bus->slave)
-@@ -2423,8 +2455,10 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
+@@ -2423,8 +2460,10 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
adap = &bus->adap;
adap->owner = THIS_MODULE;
@@ -204,5 +215,5 @@
adap->quirks = &npcm_i2c_quirks;
adap->algo_data = bus;
--
-2.40.1.495.gc816e09b53d-goog
+2.41.0.640.ga95def55d0-goog