Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: omap: Trigger bus recovery in lockup case

A very conservative check for bus activity (to prevent interference
in multimaster setups) prevented the bus recovery methods from being
triggered in the case that SDA or SCL was stuck low.
This defeats the purpose of the recovery mechanism, which was introduced
for exactly this situation (a slave device keeping SDA pulled down).

Also added a check to make sure SDA is low before attempting recovery.
If SDA is not stuck low, recovery will not help, so we can skip it.

Note that bus lockups can persist across reboots. The only other options
are to reset or power cycle the offending slave device, and many i2c
slaves do not even have a reset pin.

If we see that one of the lines is low for the entire timeout duration,
we can actually be sure that there is no other master driving the bus.
It is therefore save for us to attempt a bus recovery.

Signed-off-by: Claudio Foellmi <claudio.foellmi@ergon.ch>
Tested-by: Vignesh R <vigneshr@ti.com>
Reviewed-by: Grygorii Strashko <grygorii.strashko@ti.com>
[wsa: fixed one return code to -EBUSY]
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

authored by

Claudio Foellmi and committed by
Wolfram Sang
93367bfc 1f35b865

+23 -2
+23 -2
drivers/i2c/busses/i2c-omap.c
··· 487 487 } 488 488 489 489 /* 490 + * Try bus recovery, but only if SDA is actually low. 491 + */ 492 + static int omap_i2c_recover_bus(struct omap_i2c_dev *omap) 493 + { 494 + u16 systest; 495 + 496 + systest = omap_i2c_read_reg(omap, OMAP_I2C_SYSTEST_REG); 497 + if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) && 498 + (systest & OMAP_I2C_SYSTEST_SDA_I_FUNC)) 499 + return 0; /* bus seems to already be fine */ 500 + if (!(systest & OMAP_I2C_SYSTEST_SCL_I_FUNC)) 501 + return -EBUSY; /* recovery would not fix SCL */ 502 + return i2c_recover_bus(&omap->adapter); 503 + } 504 + 505 + /* 490 506 * Waiting on Bus Busy 491 507 */ 492 508 static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap) ··· 512 496 timeout = jiffies + OMAP_I2C_TIMEOUT; 513 497 while (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { 514 498 if (time_after(jiffies, timeout)) 515 - return i2c_recover_bus(&omap->adapter); 499 + return omap_i2c_recover_bus(omap); 516 500 msleep(1); 517 501 } 518 502 ··· 593 577 } 594 578 595 579 if (time_after(jiffies, timeout)) { 580 + /* 581 + * SDA or SCL were low for the entire timeout without 582 + * any activity detected. Most likely, a slave is 583 + * locking up the bus with no master driving the clock. 584 + */ 596 585 dev_warn(omap->dev, "timeout waiting for bus ready\n"); 597 - return -ETIMEDOUT; 586 + return omap_i2c_recover_bus(omap); 598 587 } 599 588 600 589 msleep(1);