aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorClaudio Foellmi <claudio.foellmi@ergon.ch>2017-10-04 05:43:45 -0400
committerWolfram Sang <wsa@the-dreams.de>2017-10-30 10:18:31 -0400
commit93367bfca98f36cece57c01dbce6ea1b4ac58245 (patch)
tree3752c6741d03204705ee9764649af1a9b10668ad /drivers/i2c
parent1f35b8653687b9c08c8d58489c1b5cb9cf961c17 (diff)
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>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-omap.c25
1 files changed, 23 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 23c2ea2baedc..b9172f08fd05 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -487,6 +487,22 @@ static int omap_i2c_init(struct omap_i2c_dev *omap)
487} 487}
488 488
489/* 489/*
490 * Try bus recovery, but only if SDA is actually low.
491 */
492static 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 * Waiting on Bus Busy 506 * Waiting on Bus Busy
491 */ 507 */
492static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap) 508static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap)
@@ -496,7 +512,7 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap)
496 timeout = jiffies + OMAP_I2C_TIMEOUT; 512 timeout = jiffies + OMAP_I2C_TIMEOUT;
497 while (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { 513 while (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
498 if (time_after(jiffies, timeout)) 514 if (time_after(jiffies, timeout))
499 return i2c_recover_bus(&omap->adapter); 515 return omap_i2c_recover_bus(omap);
500 msleep(1); 516 msleep(1);
501 } 517 }
502 518
@@ -577,8 +593,13 @@ static int omap_i2c_wait_for_bb_valid(struct omap_i2c_dev *omap)
577 } 593 }
578 594
579 if (time_after(jiffies, timeout)) { 595 if (time_after(jiffies, timeout)) {
596 /*
597 * SDA or SCL were low for the entire timeout without
598 * any activity detected. Most likely, a slave is
599 * locking up the bus with no master driving the clock.
600 */
580 dev_warn(omap->dev, "timeout waiting for bus ready\n"); 601 dev_warn(omap->dev, "timeout waiting for bus ready\n");
581 return -ETIMEDOUT; 602 return omap_i2c_recover_bus(omap);
582 } 603 }
583 604
584 msleep(1); 605 msleep(1);