diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 42 |
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; |