aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfram Sang <wsa+renesas@sang-engineering.com>2018-07-11 00:24:22 +0200
committerSimon Horman <horms+renesas@verge.net.au>2018-08-09 17:21:45 +0200
commitd310246c026b9911093392903c52f7e2b29f0ae6 (patch)
treed84942a8ef34a60528d21ee5dd2937111238bbd7
parenta1cce9c3b0dcbb3e93213c8fadb23dc1fb5cd298 (diff)
downloadrenesas-backport-d310246c026b9911093392903c52f7e2b29f0ae6.tar.gz
i2c: recovery: add get_bus_free callback
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> (cherry picked from commit 7ca5f6be7900ca753ed01c0202dc5f998a41f4ee) Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
-rw-r--r--drivers/i2c/i2c-core-base.c27
-rw-r--r--include/linux/i2c.h3
2 files changed, 26 insertions, 4 deletions
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index f9df6f26494bca..64535da21519d0 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -192,6 +192,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
@@ -203,7 +219,7 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
static int i2c_generic_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);
@@ -241,14 +257,17 @@ static int i2c_generic_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);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 6bc0ddb850c857..c3387435e55f25 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -494,6 +494,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
@@ -510,6 +512,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);