summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2019-12-15 16:39:05 +0000
committerWolfram Sang <wsa@the-dreams.de>2020-01-09 22:21:08 +0100
commitcf8ce8b80f8bf9669f6ec4e71e16668430febdac (patch)
tree8b3afdb2fa09a0a1ae660a53f8799bc3239746f4
parent3b722da6672df8392f9c43d7c7e04bddd81d7e37 (diff)
i2c: fix bus recovery stop mode timing
The I2C specification states that tsu:sto for standard mode timing must be at minimum 4us. Pictographically, this is: SCL: ____/~~~~~~~~~ SDA: _________/~~~~ ->| |<- 4us minimum We are currently waiting 2.5us between asserting SCL and SDA, which is in violation of the standard. Adjust the timings to ensure that we meet what is stipulated as the minimum timings to ensure that all devices correctly interpret the STOP bus transition. This is more important than trying to generate a square wave with even duty cycle. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--drivers/i2c/i2c-core-base.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 9f8dcd3f8385..35b209797d7b 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -186,10 +186,11 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
* If we can set SDA, we will always create a STOP to ensure additional
* pulses will do no harm. This is achieved by letting SDA follow SCL
* half a cycle later. Check the 'incomplete_write_byte' fault injector
- * for details.
+ * for details. Note that we must honour tsu:sto, 4us, but lets use 5us
+ * here for simplicity.
*/
bri->set_scl(adap, scl);
- ndelay(RECOVERY_NDELAY / 2);
+ ndelay(RECOVERY_NDELAY);
if (bri->set_sda)
bri->set_sda(adap, scl);
ndelay(RECOVERY_NDELAY / 2);
@@ -211,7 +212,13 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
scl = !scl;
bri->set_scl(adap, scl);
/* Creating STOP again, see above */
- ndelay(RECOVERY_NDELAY / 2);
+ if (scl) {
+ /* Honour minimum tsu:sto */
+ ndelay(RECOVERY_NDELAY);
+ } else {
+ /* Honour minimum tf and thd:dat */
+ ndelay(RECOVERY_NDELAY / 2);
+ }
if (bri->set_sda)
bri->set_sda(adap, scl);
ndelay(RECOVERY_NDELAY / 2);