| From 768fc12ebec67918df197053431d284eb99cfd6b Mon Sep 17 00:00:00 2001 |
| From: Russell King <rmk+kernel@armlinux.org.uk> |
| Date: Sun, 15 Dec 2019 16:39:05 +0000 |
| Subject: [PATCH] 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: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c |
| index 9e43508d4567..5a2783ad52ea 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); |
| -- |
| 2.7.4 |
| |