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