After a transfer timeout, some faulty I2C slave devices might hold down
the SCL or the SDA pins. We can generate a bus clear command, hoping that
the slave might release the pins.
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@xxxxxxxxxxxxx>
---
drivers/i2c/busses/i2c-at91-master.c | 20 ++++++++++++++++++++
drivers/i2c/busses/i2c-at91.h | 6 +++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c
index a3fcc35ffd3b..5f544a16db96 100644
--- a/drivers/i2c/busses/i2c-at91-master.c
+++ b/drivers/i2c/busses/i2c-at91-master.c
@@ -599,6 +599,26 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
at91_twi_write(dev, AT91_TWI_CR,
AT91_TWI_THRCLR | AT91_TWI_LOCKCLR);
}
+
+ /*
+ * After timeout, some faulty I2C slave devices might hold SCL/SDA down;
+ * we can send a bus clear command, hoping that the pins will be
+ * released
+ */
+ if (!(dev->transfer_status & AT91_TWI_SDA) ||
+ !(dev->transfer_status & AT91_TWI_SCL)) {
+ dev_dbg(dev->dev,
+ "SDA/SCL are down; sending bus clear command\n");
+ if (dev->use_alt_cmd) {
+ unsigned int acr;
+
+ acr = at91_twi_read(dev, AT91_TWI_ACR);
+ acr &= ~AT91_TWI_ACR_DATAL_MASK;
+ at91_twi_write(dev, AT91_TWI_ACR, acr);
+ }
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_CLEAR);