linux-gbmc: i2c-npcm7xx.c: add multi_master bus recoevery
The previous multi_master hack bypassed the npcm_i2c_recovery_tgclk.
Also, some NVMe-Mi device may reset their i2c module without notify
BMC i2c driver. In this case, the i2c-npcm7xx.c will enter a bad state
(bus_busy is false but bus->ber_state is true ) and it cannot send out
MCTP packet anymore.
Here we introduced the multi_master bus recovery mechanism:
When the accumulated bus->busy_cnt exceeds the adap->retries, we will
trigger npcm_i2c_reset and i2c_recover_bus.
Tested:
Before the fix, when stressing NVMe-Mi device i2c reset, we will
observe a lot of failure messages in kernel log:
Oct 30 23:17:18 kernel: i2c i2c-6: __i2c_transfer failed -11
Oct 30 23:17:18 kernel: i2c i2c-6: __i2c_transfer failed -11
Oct 30 23:17:18 kernel: i2c i2c-6: __i2c_transfer failed -11
After the fix, when stressing NVMe-Mi device i2c reset, there
is only a limited number of __i2c_transfer failed.
Google-Bug-Id: 297058513
Google-Bug-Id: 301615133
Google-Bug-Id: 305746801#comment11
Google-Bug-Id: 308495306
Change-Id: Ifeccfecc22f9ae92a7284975abea11e2593f1581
Signed-off-by: Jinliang Wang <jinliangw@google.com>
(cherry picked from commit 0dfc2659dbbb3ae4d8aac4a3bb70e40692af837c)
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 4f5427e..6e3b124 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 3d32e67d9488850502873ee61bd85666cf460873 Mon Sep 17 00:00:00 2001
+From 04e48856a6f899c821ea3578cf57a2e0c203fe55 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
@@ -10,32 +10,44 @@
special handling. Nuvoton is still trying to find a more proper way to
support multiple master mode in upstream i2c-npcm7xx.c.
-Signed-off-by: Jinliang Wang <jinliangw@google.com>
-
Patch Tracking Bug: b/275108914
Upstream info / review: N/A
Upstream-Status: Pending
Justification: The patch is upstreamable however Nuvoton is still
working on more appropriate and general implementation
+Google-Bug-Id: 266723609
+Google-Bug-Id: 295580753
+Google-Bug-Id: 297058513
Signed-off-by: William A. Kennington III <wak@google.com>
+Signed-off-by: Jinliang Wang <jinliangw@google.com>
---
- drivers/i2c/busses/i2c-npcm7xx.c | 69 +++++++++++++++++++++++++-------
- 1 file changed, 54 insertions(+), 15 deletions(-)
+ drivers/i2c/busses/i2c-npcm7xx.c | 81 +++++++++++++++++++++++++++-----
+ 1 file changed, 68 insertions(+), 13 deletions(-)
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
-index bfef6ae45988..a255d152734f 100644
+index bfef6ae45988..d193efbb33b1 100644
--- a/drivers/i2c/busses/i2c-npcm7xx.c
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
-@@ -433,6 +433,7 @@ struct npcm_i2c {
+@@ -433,6 +433,8 @@ struct npcm_i2c {
bool read_block_use;
unsigned long int_time_stamp;
unsigned long bus_freq; /* in Hz */
+ bool multi_master;
++ int busy_cnt;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
u8 own_slave_addr;
struct i2c_client *slave;
-@@ -1218,7 +1219,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
+@@ -800,6 +802,8 @@ static void npcm_i2c_reset(struct npcm_i2c *bus)
+ npcm_i2c_clear_master_status(bus);
+
+ bus->state = I2C_IDLE;
++ bus->busy_cnt = 0;
++ bus->multi_master = false;
+ }
+
+ static inline bool npcm_i2c_is_master(struct npcm_i2c *bus)
+@@ -1218,7 +1222,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
* master ( for a channel which is master\slave switching)
*/
if (completion_done(&bus->cmd_complete) == false) {
@@ -44,7 +56,7 @@
complete(&bus->cmd_complete);
}
bus->own_slave_addr = 0xFF;
-@@ -1643,6 +1644,7 @@ static void npcm_i2c_irq_handle_ber(struct npcm_i2c *bus)
+@@ -1643,6 +1647,7 @@ static void npcm_i2c_irq_handle_ber(struct npcm_i2c *bus)
bus->stop_ind = I2C_BUS_ERR_IND;
if (npcm_i2c_is_master(bus)) {
npcm_i2c_master_abort(bus);
@@ -52,7 +64,7 @@
} else {
bus->ber_state = true;
npcm_i2c_clear_master_status(bus);
-@@ -1788,6 +1790,14 @@ static int npcm_i2c_int_master_handler(struct npcm_i2c *bus)
+@@ -1788,6 +1793,14 @@ static int npcm_i2c_int_master_handler(struct npcm_i2c *bus)
(FIELD_GET(NPCM_I2CCST3_EO_BUSY,
ioread8(bus->reg + NPCM_I2CCST3)))) {
npcm_i2c_irq_handle_eob(bus);
@@ -67,7 +79,7 @@
return 0;
}
-@@ -1819,11 +1829,16 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
+@@ -1819,11 +1832,16 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
/* Allow 3 bytes (27 toggles) to be read from the slave: */
int iter = 27;
@@ -85,7 +97,7 @@
return 0;
}
-@@ -1888,7 +1903,6 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
+@@ -1888,7 +1906,6 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
if (bus->rec_succ_cnt < ULLONG_MAX)
bus->rec_succ_cnt++;
}
@@ -93,7 +105,7 @@
return status;
}
-@@ -2107,7 +2121,7 @@ static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus,
+@@ -2107,7 +2124,7 @@ static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus,
bool use_PEC, bool use_read_block)
{
if (bus->state != I2C_IDLE) {
@@ -102,7 +114,15 @@
return false;
}
bus->dest_addr = slave_addr << 1;
-@@ -2184,6 +2198,11 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2165,6 +2182,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ return -EINVAL;
+ }
+
++ bus->multi_master = false;
+ msg0 = &msgs[0];
+ slave_addr = msg0->addr;
+ if (msg0->flags & I2C_M_RD) { /* read */
+@@ -2184,6 +2202,11 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
write_data = msg0->buf;
nread = 0;
read_data = NULL;
@@ -114,7 +134,7 @@
if (num == 2) {
msg1 = &msgs[1];
read_data = msg1->buf;
-@@ -2201,6 +2220,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2201,6 +2224,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (nwrite >= 32 * 1024 || nread >= 32 * 1024) {
dev_err(bus->dev, "i2c%d buffer too big\n", bus->num);
@@ -122,7 +142,7 @@
return -EINVAL;
}
-@@ -2212,7 +2232,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2212,7 +2236,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
* entire while since it is too long.
*/
spin_lock_irqsave(&bus->lock, flags);
@@ -131,7 +151,7 @@
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (!bus_busy && bus->slave)
iowrite8((bus->slave->addr & 0x7F),
-@@ -2220,20 +2240,29 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2220,21 +2244,41 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
#endif
spin_unlock_irqrestore(&bus->lock, flags);
@@ -146,14 +166,22 @@
bus->dest_addr = slave_addr << 1;
if (bus_busy || bus->ber_state) {
iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
-- npcm_i2c_reset(bus);
-- i2c_recover_bus(adap);
-- return -EAGAIN;
-+ if(bus->multi_master == false)
-+ {
-+ npcm_i2c_reset(bus);
-+ i2c_recover_bus(adap);
++ if (bus->multi_master) {
++ bus->busy_cnt++;
++ if (bus->busy_cnt < adap->retries) {
++ /* skip recovery */
++ bus->cmd_err = -EAGAIN;
++ goto npcm_i2c_xfer_complete;
++ }
++
++ /* need recovery */
++ dev_err(bus->dev, "i2c%d multi_master busy recover\n",
++ bus->num);
+ }
++
+ npcm_i2c_reset(bus);
+ i2c_recover_bus(adap);
+- return -EAGAIN;
+ bus->cmd_err = -EAGAIN;
+ goto npcm_i2c_xfer_complete;
}
@@ -164,9 +192,11 @@
- bus->cmd_err = 0;
+ bus->cmd_err = -EAGAIN;
bus->read_block_use = read_block;
++ bus->busy_cnt = 0;
reinit_completion(&bus->cmd_complete);
-@@ -2258,8 +2287,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+
+@@ -2258,8 +2302,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) {
@@ -176,7 +206,7 @@
bus->state = I2C_IDLE;
}
}
-@@ -2267,7 +2295,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2267,7 +2310,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)
@@ -185,7 +215,7 @@
/*
* After any type of error, check if LAST bit is still set,
-@@ -2282,6 +2310,15 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+@@ -2282,6 +2325,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);
@@ -201,7 +231,7 @@
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* reenable slave if it was enabled */
if (bus->slave)
-@@ -2423,8 +2460,10 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
+@@ -2423,8 +2475,11 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
adap = &bus->adap;
adap->owner = THIS_MODULE;
@@ -211,9 +241,10 @@
+ adap->retries = 10;
+ adap->timeout = msecs_to_jiffies(400) * adap->retries;
+ bus->multi_master = false;
++ bus->busy_cnt = 0;
adap->algo = &npcm_i2c_algo;
adap->quirks = &npcm_i2c_quirks;
adap->algo_data = bus;
--
-2.41.0.640.ga95def55d0-goog
+2.42.0.820.g83a721a137-goog