i2c: recovery: add get_bus_free callback
authorWolfram Sang <wsa+renesas@sang-engineering.com>
Tue, 10 Jul 2018 22:24:22 +0000 (00:24 +0200)
committerWolfram Sang <wsa@the-dreams.de>
Tue, 17 Jul 2018 08:46:51 +0000 (10:46 +0200)
Some IP cores have an internal 'bus free' logic which may be more
advanced than just checking if SDA is high. Add a separate callback to
get this status. Filling it is optional.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
drivers/i2c/i2c-core-base.c
include/linux/i2c.h

index c7995efd58ea78a32d8b1767580f73fcfaec9868..59f8dfc5be363a3ec4f57bcaa1a868912e7eac39 100644 (file)
@@ -158,6 +158,22 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
        gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val);
 }
 
+static int i2c_generic_bus_free(struct i2c_adapter *adap)
+{
+       struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+       int ret = -EOPNOTSUPP;
+
+       if (bri->get_bus_free)
+               ret = bri->get_bus_free(adap);
+       else if (bri->get_sda)
+               ret = bri->get_sda(adap);
+
+       if (ret < 0)
+               return ret;
+
+       return ret ? 0 : -EBUSY;
+}
+
 /*
  * We are generating clock pulses. ndelay() determines durating of clk pulses.
  * We will generate clock with rate 100 KHz and so duration of both clock levels
@@ -169,7 +185,7 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
 int i2c_generic_scl_recovery(struct i2c_adapter *adap)
 {
        struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
-       int i = 0, val = 1, ret = 0;
+       int i = 0, val = 1, ret;
 
        if (bri->prepare_recovery)
                bri->prepare_recovery(adap);
@@ -207,14 +223,17 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
                        bri->set_sda(adap, val);
                ndelay(RECOVERY_NDELAY / 2);
 
-               /* Break if SDA is high */
-               if (val && bri->get_sda) {
-                       ret = bri->get_sda(adap) ? 0 : -EBUSY;
+               if (val) {
+                       ret = i2c_generic_bus_free(adap);
                        if (ret == 0)
                                break;
                }
        }
 
+       /* If we can't check bus status, assume recovery worked */
+       if (ret == -EOPNOTSUPP)
+               ret = 0;
+
        if (bri->unprepare_recovery)
                bri->unprepare_recovery(adap);
 
index 4c4360f947869465f8387e663dfd6273013a34b9..bc8d42f8544f4fe4170e4525671e026eba06786e 100644 (file)
@@ -587,6 +587,8 @@ struct i2c_timings {
  * @set_sda: This sets/clears the SDA line. This or get_sda() is mandatory for
  *     generic SCL recovery. Populated internally, if sda_gpio is a valid GPIO,
  *     for generic GPIO recovery.
+ * @get_bus_free: Returns the bus free state as seen from the IP core in case it
+ *     has a more complex internal logic than just reading SDA. Optional.
  * @prepare_recovery: This will be called before starting recovery. Platform may
  *     configure padmux here for SDA/SCL line or something else they want.
  * @unprepare_recovery: This will be called after completing recovery. Platform
@@ -601,6 +603,7 @@ struct i2c_bus_recovery_info {
        void (*set_scl)(struct i2c_adapter *adap, int val);
        int (*get_sda)(struct i2c_adapter *adap);
        void (*set_sda)(struct i2c_adapter *adap, int val);
+       int (*get_bus_free)(struct i2c_adapter *adap);
 
        void (*prepare_recovery)(struct i2c_adapter *adap);
        void (*unprepare_recovery)(struct i2c_adapter *adap);