diff options
author | Chunhe Lan <Chunhe.Lan@freescale.com> | 2013-08-16 15:10:36 +0800 |
---|---|---|
committer | Heiko Schocher <hs@denx.de> | 2013-08-20 11:15:31 +0200 |
commit | b8ce3343b64434b95b9a25dea0534bf9f1593e8c (patch) | |
tree | 5076a3bb52764df13495ee85b29efdf1d691aebf /drivers/i2c/fsl_i2c.c | |
parent | 40a60c6e8bdd00330310eaa351c9391e74d0bc2c (diff) |
fsl_i2c: generate nine pulses on SCL if the I2C bus is hung
When the code detected that the bus is hung (e.g. SDA stuck low),
send 9 pulses on SCL to try to fixup the bus.
Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com>
Signed-off-by: Chunhe Lan <Chunhe.Lan@freescale.com>
Cc: Scott Wood <scottwood@freescale.com>
Cc: Heiko Schocher <hs@denx.de>
Diffstat (limited to 'drivers/i2c/fsl_i2c.c')
-rw-r--r-- | drivers/i2c/fsl_i2c.c | 59 |
1 files changed, 57 insertions, 2 deletions
diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 38455e1c66..44b08f7671 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -206,9 +206,50 @@ static unsigned int get_i2c_clock(int bus) return gd->arch.i2c1_clk; /* I2C1 clock */ } +static int fsl_i2c_fixup(const struct fsl_i2c *dev) +{ + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval = 0; + int ret = -1; + + writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MBB)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + + if (readb(&dev->sr) & I2C_SR_MAL) { + /* SDA is stuck low */ + writeb(0, &dev->cr); + udelay(100); + writeb(I2C_CR_MSTA, &dev->cr); + writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + } + + readb(&dev->dr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MIF)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + ret = 0; + +err: + writeb(I2C_CR_MEN, &dev->cr); + writeb(0, &dev->sr); + udelay(100); + + return ret; +} + static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) { const struct fsl_i2c *dev; + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval; #ifdef CONFIG_SYS_I2C_INIT_BOARD /* Call board specific i2c bus reset routine before accessing the @@ -226,6 +267,18 @@ static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) writeb(0x0, &dev->sr); /* clear status register */ writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ + timeval = get_ticks(); + while (readb(&dev->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) < timeout) + continue; + + if (fsl_i2c_fixup(dev)) + debug("i2c_init: BUS#%d failed to init\n", + adap->hwadapnr); + + break; + } + #ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT /* Call board specific i2c bus reset routine AFTER the bus has been * initialized. Use either this callpoint or i2c_init_board; @@ -394,8 +447,10 @@ fsl_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, int alen, int i = -1; /* signal error */ u8 *a = (u8*)&addr; - if (i2c_wait4bus(adap) >= 0 && - i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + if (i2c_wait4bus(adap) < 0) + return -1; + + if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && __i2c_write(adap, &a[4 - alen], alen) == alen) { i = __i2c_write(adap, data, length); } |