aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-omap.c42
1 files changed, 28 insertions, 14 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index c0bc3dbb9d1..1b10630760b 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -148,7 +148,8 @@ enum {
#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
-#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_TMODE_TEST (2 << 12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_TMODE_LOOP (3 << 12) /* Test mode select */
#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
@@ -491,6 +492,30 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
}
/*
+ * Bus Clear
+ */
+static int omap_i2c_bus_clear(struct omap_i2c_dev *dev)
+{
+ u16 w;
+
+ /* Per the I2C specification, if we are stuck in a bus busy state
+ * we can attempt a bus clear to try and recover the bus by sending
+ * at least 9 clock pulses on SCL. Put the I2C in a test mode so it
+ * will output a continuous clock on SCL.
+ */
+ w = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG,
+ (OMAP_I2C_SYSTEST_ST_EN | OMAP_I2C_SYSTEST_TMODE_TEST));
+ msleep(1);
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, w);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ omap_i2c_reset(dev);
+ omap_i2c_init(dev);
+ return omap_i2c_wait_for_bb(dev);
+}
+
+/*
* Low level master read/write transaction.
*/
static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
@@ -608,7 +633,6 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
int i;
int r;
- u16 val;
if (dev == NULL)
return -EINVAL;
@@ -622,18 +646,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
omap_i2c_unidle(dev);
r = omap_i2c_wait_for_bb(dev);
- /* If timeout, try to again check after soft reset of I2C block */
- if (WARN_ON(r == -ETIMEDOUT)) {
- /* Provide a permanent clock to recover the peripheral */
- val = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
- val |= (OMAP_I2C_SYSTEST_ST_EN |
- OMAP_I2C_SYSTEST_FREE |
- (2 << OMAP_I2C_SYSTEST_TMODE_SHIFT));
- omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, val);
- msleep(1);
- omap_i2c_init(dev);
- r = omap_i2c_wait_for_bb(dev);
- }
+ if (r < 0)
+ r = omap_i2c_bus_clear(dev);
if (r < 0)
goto out;