Candidate fix for SCL getting stretched when high resulting in slave timeouts. Signed-off-by: Jean Delvare --- drivers/i2c/algos/i2c-algo-bit.c | 34 ++++++++++++++++++++++++++++++---- include/linux/i2c-algo-bit.h | 3 +++ 2 files changed, 33 insertions(+), 4 deletions(-) --- linux-2.6.27.orig/drivers/i2c/algos/i2c-algo-bit.c 2010-12-15 15:25:12.000000000 +0100 +++ linux-2.6.27/drivers/i2c/algos/i2c-algo-bit.c 2010-12-15 15:25:26.000000000 +0100 @@ -30,6 +30,7 @@ #include #include #include +#include /* ----- global defines ----------------------------------------------- */ @@ -131,12 +132,17 @@ static void i2c_start(struct i2c_algo_bi static void i2c_repstart(struct i2c_algo_bit_data *adap) { + unsigned long flags; + /* assert: scl is low */ sdahi(adap); + spin_lock_irqsave(&adap->lockscl, flags); sclhi(adap); setsda(adap, 0); udelay(adap->udelay); - scllo(adap); + setscl(adap, 0); + spin_unlock_irqrestore(&adap->lockscl, flags); + udelay(adap->udelay / 2); } @@ -164,13 +170,16 @@ static int i2c_outb(struct i2c_adapter * int sb; int ack; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + unsigned long flags; /* assert: scl is low */ for (i = 7; i >= 0; i--) { sb = (c >> i) & 1; setsda(adap, sb); udelay((adap->udelay + 1) / 2); + spin_lock_irqsave(&adap->lockscl, flags); if (sclhi(adap) < 0) { /* timed out */ + spin_unlock_irqrestore(&adap->lockscl, flags); bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at bit #%d\n", (int)c, i); return -ETIMEDOUT; @@ -181,10 +190,14 @@ static int i2c_outb(struct i2c_adapter * * Report a unique code, so higher level code can retry * the whole (combined) message and *NOT* issue STOP. */ - scllo(adap); + setscl(adap, 0); + spin_unlock_irqrestore(&adap->lockscl, flags); + udelay(adap->udelay / 2); } sdahi(adap); + spin_lock_irqsave(&adap->lockscl, flags); if (sclhi(adap) < 0) { /* timeout */ + spin_unlock_irqrestore(&adap->lockscl, flags); bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at ack\n", (int)c); return -ETIMEDOUT; @@ -194,10 +207,13 @@ static int i2c_outb(struct i2c_adapter * * NAK (usually to report problems with the data we wrote). */ ack = !getsda(adap); /* ack: sda is pulled low -> success */ + setscl(adap, 0); + spin_unlock_irqrestore(&adap->lockscl, flags); + udelay(adap->udelay / 2); + bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c, ack ? "A" : "NA"); - scllo(adap); return ack; /* assert: scl is low (sda undef) */ } @@ -210,11 +226,14 @@ static int i2c_inb(struct i2c_adapter *i int i; unsigned char indata = 0; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + unsigned long flags; /* assert: scl is low */ sdahi(adap); for (i = 0; i < 8; i++) { + spin_lock_irqsave(&adap->lockscl, flags); if (sclhi(adap) < 0) { /* timeout */ + spin_unlock_irqrestore(&adap->lockscl, flags); bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit " "#%d\n", 7 - i); return -ETIMEDOUT; @@ -223,6 +242,7 @@ static int i2c_inb(struct i2c_adapter *i if (getsda(adap)) indata |= 0x01; setscl(adap, 0); + spin_unlock_irqrestore(&adap->lockscl, flags); udelay(i == 7 ? adap->udelay / 2 : adap->udelay); } /* assert: scl is low */ @@ -385,16 +405,21 @@ static int sendbytes(struct i2c_adapter static int acknak(struct i2c_adapter *i2c_adap, int is_ack) { struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + unsigned long flags; /* assert: sda is high */ if (is_ack) /* send ack */ setsda(adap, 0); udelay((adap->udelay + 1) / 2); + spin_lock_irqsave(&adap->lockscl, flags); if (sclhi(adap) < 0) { /* timeout */ + spin_unlock_irqrestore(&adap->lockscl, flags); dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n"); return -ETIMEDOUT; } - scllo(adap); + setscl(adap, 0); + spin_unlock_irqrestore(&adap->lockscl, flags); + udelay(adap->udelay / 2); return 0; } @@ -603,6 +628,7 @@ static int i2c_bit_prepare_bus(struct i2 } /* register new adapter to i2c module... */ + spin_lock_init(&bit_adap->lockscl); adap->algo = &i2c_bit_algo; adap->timeout = 100; /* default values, should */ --- linux-2.6.27.orig/include/linux/i2c-algo-bit.h 2010-12-15 15:25:12.000000000 +0100 +++ linux-2.6.27/include/linux/i2c-algo-bit.h 2010-12-15 15:25:44.000000000 +0100 @@ -24,6 +24,8 @@ #ifndef _LINUX_I2C_ALGO_BIT_H #define _LINUX_I2C_ALGO_BIT_H +#include + /* --- Defines for bit-adapters --------------------------------------- */ /* * This struct contains the hw-dependent functions of bit-style adapters to @@ -36,6 +38,7 @@ struct i2c_algo_bit_data { void (*setscl) (void *data, int state); int (*getsda) (void *data); int (*getscl) (void *data); + spinlock_t lockscl; /* local settings */ int udelay; /* half clock cycle time in us,