| From 691895208e6ff75ed6866b2825e173685c77aabe 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 |
| |
| This patch is backported from Avi.Fishman@nuvoton.com and |
| tali.perry@nuvoton.com 's debug version driver v15. |
| Link (https://drive.google.com/corp/drive/folders/19RaTMJcarpN-dpuAlbTFPm_0xKx7TKjU?resourcekey=0-UI41jAZA4xd_5VINmDcN0Q) |
| It contains some quirk to detect MCTP packet and then apply some |
| special handling. Nuvoton is still trying to find a more proper way to |
| support multiple master mode in upstream i2c-npcm7xx.c. |
| |
| 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 | 73 ++++++++++++++++++++++++++------ |
| 1 file changed, 60 insertions(+), 13 deletions(-) |
| |
| diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c |
| index 7e71b89bb1fd..05c0a1b32b0f 100644 |
| --- a/drivers/i2c/busses/i2c-npcm7xx.c |
| +++ b/drivers/i2c/busses/i2c-npcm7xx.c |
| @@ -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; |
| @@ -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) |
| @@ -1215,7 +1219,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) { |
| - bus->cmd_err = -EIO; |
| + bus->cmd_err = -EAGAIN; |
| complete(&bus->cmd_complete); |
| } |
| bus->own_slave_addr = 0xFF; |
| @@ -1640,6 +1644,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); |
| + bus->cmd_err = -EAGAIN; |
| } else { |
| bus->ber_state = true; |
| npcm_i2c_clear_master_status(bus); |
| @@ -1822,11 +1827,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; |
| |
| + bus->ber_state = false; |
| + |
| + if (bus->multi_master) { |
| + return 0; |
| + } |
| + |
| if ((npcm_i2c_get_SDA(_adap) == 1) && (npcm_i2c_get_SCL(_adap) == 1)) { |
| dev_dbg(bus->dev, "bus%d-0x%x recovery skipped, bus not stuck", |
| bus->num, bus->dest_addr); |
| npcm_i2c_reset(bus); |
| - bus->ber_state = false; |
| return 0; |
| } |
| |
| @@ -1891,7 +1901,6 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap) |
| if (bus->rec_succ_cnt < ULLONG_MAX) |
| bus->rec_succ_cnt++; |
| } |
| - bus->ber_state = false; |
| return status; |
| } |
| |
| @@ -2110,7 +2119,7 @@ static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus, |
| bool use_PEC, bool use_read_block) |
| { |
| if (bus->state != I2C_IDLE) { |
| - bus->cmd_err = -EBUSY; |
| + bus->cmd_err = -EAGAIN; |
| return false; |
| } |
| bus->dest_addr = slave_addr << 1; |
| @@ -2168,6 +2177,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 */ |
| @@ -2187,6 +2197,11 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, |
| write_data = msg0->buf; |
| nread = 0; |
| read_data = NULL; |
| + if ((num == 1) && (nwrite > 7) && (write_data[0] == 0xF) && ((write_data[1] + 3) == nwrite)) |
| + { |
| + printk_once("i2c%d: Met MCTP packet\n", bus->num); |
| + bus->multi_master = true; |
| + } |
| if (num == 2) { |
| msg1 = &msgs[1]; |
| read_data = msg1->buf; |
| @@ -2204,6 +2219,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); |
| + bus->multi_master = false; |
| return -EINVAL; |
| } |
| |
| @@ -2215,7 +2231,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); |
| - bus_busy = ioread8(bus->reg + NPCM_I2CCST) & NPCM_I2CCST_BB; |
| + bus_busy = (ioread8(bus->reg + NPCM_I2CCST) & NPCM_I2CCST_BB) || (bus->state != I2C_IDLE); |
| #if IS_ENABLED(CONFIG_I2C_SLAVE) |
| if (!bus_busy && bus->slave) |
| iowrite8((bus->slave->addr & 0x7F), |
| @@ -2223,21 +2239,41 @@ 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); |
| + 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; |
| } |
| |
| npcm_i2c_init_params(bus); |
| bus->msgs = msgs; |
| bus->msgs_num = num; |
| - bus->cmd_err = 0; |
| + bus->cmd_err = -EAGAIN; |
| bus->read_block_use = read_block; |
| + bus->busy_cnt = 0; |
| |
| reinit_completion(&bus->cmd_complete); |
| |
| @@ -2261,8 +2297,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) { |
| - i2c_recover_bus(adap); |
| - bus->cmd_err = -EIO; |
| + bus->cmd_err = -EAGAIN; |
| bus->state = I2C_IDLE; |
| } |
| } |
| @@ -2270,7 +2305,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) |
| - bus->cmd_err = i2c_recover_bus(adap); |
| + i2c_recover_bus(adap); |
| |
| /* |
| * After any type of error, check if LAST bit is still set, |
| @@ -2285,6 +2320,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); |
| |
| +npcm_i2c_xfer_complete: |
| + if (bus->multi_master) { |
| + if ((bus->cmd_err < 0 && bus->cmd_err != -EAGAIN)|| |
| + (bus->cmd_err >= 0 && bus->cmd_err != num)){ |
| + dev_err(bus->dev, "i2c%d xfer returned %d\n", bus->num, bus->cmd_err); |
| + } |
| + /* reset it to false upon exit */ |
| + bus->multi_master = false; |
| + } |
| #if IS_ENABLED(CONFIG_I2C_SLAVE) |
| /* reenable slave if it was enabled */ |
| if (bus->slave) |
| @@ -2426,8 +2470,11 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev) |
| |
| adap = &bus->adap; |
| adap->owner = THIS_MODULE; |
| - adap->retries = 3; |
| - adap->timeout = 2 * HZ; |
| + /* 10 retries x 400 ms = 4 seconds(HZ) */ |
| + 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.42.0.820.g83a721a137-goog |
| |