i2c: omap: implement bus recovery
authorFelipe Balbi <balbi@ti.com>
Wed, 6 May 2015 16:50:27 +0000 (11:50 -0500)
committerWolfram Sang <wsa@the-dreams.de>
Tue, 12 May 2015 19:16:04 +0000 (21:16 +0200)
implement bus recovery methods for i2c-omap
so we can recover from situations where SCL/SDA
are stuck low.

Signed-off-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
drivers/i2c/busses/i2c-omap.c

index 82619418211893d9879e5a2f9d4bff754ef3c141..d1c22e3fdd146a34d96adcdd35b81a6e15984824 100644 (file)
@@ -482,10 +482,8 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
 
        timeout = jiffies + OMAP_I2C_TIMEOUT;
        while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
-               if (time_after(jiffies, timeout)) {
-                       dev_warn(dev->dev, "timeout waiting for bus ready\n");
-                       return -ETIMEDOUT;
-               }
+               if (time_after(jiffies, timeout))
+                       return i2c_recover_bus(&dev->adapter);
                msleep(1);
        }
 
@@ -1210,6 +1208,68 @@ MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
 #define OMAP_I2C_SCHEME_0              0
 #define OMAP_I2C_SCHEME_1              1
 
+static int omap_i2c_get_scl(struct i2c_adapter *adap)
+{
+       struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+       u32 reg;
+
+       reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+
+       return reg & OMAP_I2C_SYSTEST_SCL_I_FUNC;
+}
+
+static int omap_i2c_get_sda(struct i2c_adapter *adap)
+{
+       struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+       u32 reg;
+
+       reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+
+       return reg & OMAP_I2C_SYSTEST_SDA_I_FUNC;
+}
+
+static void omap_i2c_set_scl(struct i2c_adapter *adap, int val)
+{
+       struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+       u32 reg;
+
+       reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+       if (val)
+               reg |= OMAP_I2C_SYSTEST_SCL_O;
+       else
+               reg &= ~OMAP_I2C_SYSTEST_SCL_O;
+       omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
+}
+
+static void omap_i2c_prepare_recovery(struct i2c_adapter *adap)
+{
+       struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+       u32 reg;
+
+       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);
+}
+
+static void omap_i2c_unprepare_recovery(struct i2c_adapter *adap)
+{
+       struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+       u32 reg;
+
+       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);
+}
+
+static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = {
+       .get_scl                = omap_i2c_get_scl,
+       .get_sda                = omap_i2c_get_sda,
+       .set_scl                = omap_i2c_set_scl,
+       .prepare_recovery       = omap_i2c_prepare_recovery,
+       .unprepare_recovery     = omap_i2c_unprepare_recovery,
+       .recover_bus            = i2c_generic_scl_recovery,
+};
+
 static int
 omap_i2c_probe(struct platform_device *pdev)
 {
@@ -1359,6 +1419,7 @@ omap_i2c_probe(struct platform_device *pdev)
        adap->algo = &omap_i2c_algo;
        adap->dev.parent = &pdev->dev;
        adap->dev.of_node = pdev->dev.of_node;
+       adap->bus_recovery_info = &omap_i2c_bus_recovery_info;
 
        /* i2c device drivers may be active on return from add_adapter() */
        adap->nr = pdev->id;