|
@@ -0,0 +1,93 @@
|
|
|
+diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
|
|
|
+index ccfc49f..77819c3 100644
|
|
|
+--- a/drivers/i2c/busses/i2c-omap.c
|
|
|
++++ b/drivers/i2c/busses/i2c-omap.c
|
|
|
+@@ -474,6 +474,73 @@ static int omap_i2c_init(struct omap_i2c_dev *omap)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
++static void omap_i2c_clock_pulse(struct omap_i2c_dev *dev)
|
|
|
++{
|
|
|
++ u32 reg;
|
|
|
++ int i;
|
|
|
++
|
|
|
++ /* Enable testmode */
|
|
|
++ reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
|
|
|
++ reg |= OMAP_I2C_SYSTEST_ST_EN;
|
|
|
++ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
|
|
|
++
|
|
|
++ for (i = 0; i < 9; i++) {
|
|
|
++ reg |= OMAP_I2C_SYSTEST_SCL_O;
|
|
|
++ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
|
|
|
++ mdelay(100);
|
|
|
++ reg &= ~OMAP_I2C_SYSTEST_SCL_O;
|
|
|
++ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
|
|
|
++ mdelay(100);
|
|
|
++ }
|
|
|
++
|
|
|
++ /* Disable testmode */
|
|
|
++ reg &= ~OMAP_I2C_SYSTEST_ST_EN;
|
|
|
++ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
|
|
|
++}
|
|
|
++
|
|
|
++static void omap_i2c_bus_recover(struct omap_i2c_dev *dev)
|
|
|
++{
|
|
|
++ u32 reg1;
|
|
|
++ u32 reg2;
|
|
|
++
|
|
|
++ /*
|
|
|
++ * First differentiate SCL stuck low from SDA stuck low using our
|
|
|
++ * SYSTEST register. Depending on which line is stuck low, we will
|
|
|
++ * either Reset our I2C IP (SCL stuck low) or drive 9 clock pulses on
|
|
|
++ * SCL (SDA stuck low) to tell the device to release the bus.
|
|
|
++ *
|
|
|
++ * If, after 9 clock pulses on SCL device still doesn't release the
|
|
|
++ * bus, there's nothing more we can do; we will still try to Reset
|
|
|
++ * our I2C IP anyway.
|
|
|
++ */
|
|
|
++
|
|
|
++ reg1 = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
|
|
|
++ msleep(1);
|
|
|
++ reg2 = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
|
|
|
++
|
|
|
++ if (!(reg1 & OMAP_I2C_SYSTEST_SCL_I_FUNC) &&
|
|
|
++ !(reg2 & OMAP_I2C_SYSTEST_SCL_I_FUNC)) {
|
|
|
++ dev_err(dev->dev, "SCL is stuck low, resetting\n");
|
|
|
++ omap_i2c_reset(dev);
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!(reg1 & OMAP_I2C_SYSTEST_SDA_I_FUNC) &&
|
|
|
++ !(reg2 & OMAP_I2C_SYSTEST_SDA_I_FUNC)) {
|
|
|
++ dev_err(dev->dev, "SDA is stuck low, driving 9 pulses on SCL\n");
|
|
|
++ omap_i2c_clock_pulse(dev);
|
|
|
++
|
|
|
++ reg1 = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
|
|
|
++ msleep(1);
|
|
|
++ reg2 = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
|
|
|
++
|
|
|
++ if ((reg1 & OMAP_I2C_SYSTEST_SDA_I_FUNC) &&
|
|
|
++ (reg2 & OMAP_I2C_SYSTEST_SDA_I_FUNC)) {
|
|
|
++ dev_err(dev->dev, "SDA still stuck, resetting\n");
|
|
|
++ omap_i2c_reset(dev);
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
+ /*
|
|
|
+ * Waiting on Bus Busy
|
|
|
+ */
|
|
|
+@@ -483,8 +550,12 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *omap)
|
|
|
+
|
|
|
+ timeout = jiffies + OMAP_I2C_TIMEOUT;
|
|
|
+ while (omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
|
|
|
+- if (time_after(jiffies, timeout))
|
|
|
+- return i2c_recover_bus(&omap->adapter);
|
|
|
++ if (time_after(jiffies, timeout)) {
|
|
|
++ //return i2c_recover_bus(&omap->adapter);
|
|
|
++ dev_err(omap->dev, "Timeout on BusBusy -> recover\n");
|
|
|
++ omap_i2c_bus_recover(omap);
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
+ msleep(1);
|
|
|
+ }
|
|
|
+
|