diff options
author | Michael Hennerich <michael.hennerich@analog.com> | 2011-01-11 00:25:08 -0500 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2011-01-12 22:23:05 +0000 |
commit | 540ac5553e8169413a90a8e68b3a10f801640eb7 (patch) | |
tree | 628dcd685d392ddc2c8fe692035050db2609dbcf /drivers/i2c | |
parent | f878133bf022717b880d0e0995b8f91436fd605c (diff) | |
download | linux-540ac5553e8169413a90a8e68b3a10f801640eb7.tar.gz linux-540ac5553e8169413a90a8e68b3a10f801640eb7.tar.bz2 linux-540ac5553e8169413a90a8e68b3a10f801640eb7.zip |
i2c-bfin-twi: handle faulty slave devices better
Faulty slave devices might drive SDA low after a transfer finishes. So,
when this scenario is detected, have the master generate up to 9 extra
clocks until the SDA is properly released.
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-bfin-twi.c | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index fb26e5c67515..1ee9d5a31928 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -20,6 +20,7 @@ #include <linux/completion.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/delay.h> #include <asm/blackfin.h> #include <asm/portmux.h> @@ -159,6 +160,27 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, if (mast_stat & BUFWRERR) dev_dbg(&iface->adap.dev, "Buffer Write Error\n"); + /* Faulty slave devices, may drive SDA low after a transfer + * finishes. To release the bus this code generates up to 9 + * extra clocks until SDA is released. + */ + + if (read_MASTER_STAT(iface) & SDASEN) { + int cnt = 9; + do { + write_MASTER_CTL(iface, SCLOVR); + udelay(6); + write_MASTER_CTL(iface, 0); + udelay(6); + } while ((read_MASTER_STAT(iface) & SDASEN) && cnt--); + + write_MASTER_CTL(iface, SDAOVR | SCLOVR); + udelay(6); + write_MASTER_CTL(iface, SDAOVR); + udelay(6); + write_MASTER_CTL(iface, 0); + } + /* If it is a quick transfer, only address without data, * not an err, return 1. */ |