summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2019-12-15 19:39:05 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-01-14 22:06:58 +0300
commitbeab31b2af28f8c5a2580846c886cae530b7a137 (patch)
tree8f4516104dc36c6cde968d88474a942c6c4bbf41 /drivers/i2c
parentec576895d61356a2cab096e1ca23bf7cc765e5b2 (diff)
downloadlinux-beab31b2af28f8c5a2580846c886cae530b7a137.tar.xz
i2c: fix bus recovery stop mode timing
commit cf8ce8b80f8bf9669f6ec4e71e16668430febdac upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/i2c')
-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 1de10e5c70d7..f225bef1e043 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -194,10 +194,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);
@@ -219,7 +220,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);