Skip to contentSkip to conversation

I2C: Add bus recovery infrastructure

PATCH Superseded Viresh Kumar 7 participants 11 messages Mar 2, 2012 – Apr 24, 2012 6 messages not in the archive

Subsystems (4)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v3 cover I2C: Add bus recovery infrastructure

Hello,

This patchset adds i2c bus recovery infrastructure to i2c adapters as specified in the i2c protocol Rev. 03 section 3.16 titled "Bus clear".

http://www.nxp.com/documents/user_manual/UM10204.pdf

This patch was earlier part of a separate thread:
http://www.spinics.net/lists/linux-i2c/msg07267.html

Changes since V2:
- gpio flags are now passed from controller drivers
- added support for sda line polling
- Aligned i2c-designware driver with generic recovery support

Viresh Kumar (2):
i2c/adapter: Add bus recovery infrastructure i2c/designware: Provide optional i2c bus recovery function

 drivers/i2c/busses/i2c-designware-core.c    |    7 ++-
 drivers/i2c/busses/i2c-designware-platdrv.c |   24 +++++
 drivers/i2c/i2c-core.c                      |  125 +++++++++++++++++++++++++++
 include/linux/i2c.h                         |   52 +++++++++++
 include/linux/i2c/i2c-designware.h          |   49 +++++++++++
 5 files changed, 256 insertions(+), 1 deletions(-)
 create mode 100644 include/linux/i2c/i2c-designware.h
Signature · 1 line
1.7.8.110.g4cb5d

A message that is not in the archive (sent off-list or not yet received): [email protected]

#2 lore raw

PATCH v3 1/2 i2c/adapter: Add bus recovery infrastructure

Add i2c bus recovery infrastructure to i2c adapters as specified in the i2c protocol Rev. 03 section 3.16 titled "Bus clear".

http://www.nxp.com/documents/user_manual/UM10204.pdf

Sometimes during operation i2c bus hangs and we need to give dummy clocks to slave device to start the transfer again. Now we may have capability in the bus controller to generate these clocks or platform may have gpio pins which can be toggled to generate dummy clocks. This patch supports both.

This patch also adds in generic bus recovery routines gpio or scl line based which can be used by bus controller. In addition controller driver may provide its own version of the bus recovery routine.

---
 drivers/i2c/i2c-core.c |  125 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c.h    |   52 ++++++++++++++++++++
 2 files changed, 177 insertions(+), 0 deletions(-)
drivers/i2c/i2c-core.c +125 -0
index e9c1893..5cd5f83 100644
@@ -26,7 +26,9 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/gpio.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -103,6 +105,88 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 #define i2c_device_uevent	NULL
 #endif	/* CONFIG_HOTPLUG */
 
+/* i2c bus recovery routines */
+static int i2c_gpio_recover_bus(struct i2c_adapter *adap)
+{
+	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+	unsigned long delay = 1000000;
+	int i, ret, val = 1;
+
+	if (bri->get_gpio)
+		bri->get_gpio(bri->scl_gpio);
+
+	ret = gpio_request_one(bri->scl_gpio, bri->scl_gpio_flags, "i2c-scl");
+	if (ret < 0) {
+		dev_warn(&adap->dev, "gpio request fail: %d\n", bri->scl_gpio);
+		return ret;
+	}
+
+	if (!bri->skip_sda_polling) {
+		if (bri->get_gpio)
+			bri->get_gpio(bri->sda_gpio);
+
+		ret = gpio_request_one(bri->sda_gpio, bri->sda_gpio_flags,
+				"i2c-sda");
+		if (ret < 0) {
+			/* work without sda polling */
+			dev_warn(&adap->dev,
+				"sda_gpio request fail: %d. Skip sda polling\n",
+				bri->scl_gpio);
+			bri->skip_sda_polling = true;
+			if (bri->put_gpio)
+				bri->put_gpio(bri->sda_gpio);
+		}
+	}
+
+	delay /= bri->clock_rate_khz * 2;
+
+	for (i = 0; i < bri->clock_cnt * 2; i++, val = !val) {
+		ndelay(delay);
+		gpio_set_value(bri->scl_gpio, val);
+
+		/* break if sda got high, check only when scl line is high */
+		if (!bri->skip_sda_polling && val)
+			if (gpio_get_value(bri->sda_gpio))
+				break;
+	}
+
+	gpio_free(bri->scl_gpio);
+
+	if (bri->put_gpio)
+		bri->put_gpio(bri->scl_gpio);
+
+	if (!bri->skip_sda_polling) {
+		gpio_free(bri->sda_gpio);
+
+		if (bri->put_gpio)
+			bri->put_gpio(bri->sda_gpio);
+	}
+
+	return 0;
+}
+
+static int i2c_scl_recover_bus(struct i2c_adapter *adap)
+{
+	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+	int i, val = 0;
+	unsigned long delay = 1000000;
+
+	delay /= bri->clock_rate_khz * 2;
+
+	for (i = 0; i < bri->clock_cnt * 2; i++,
+			val = !val) {
+		bri->set_scl(adap, val);
+		ndelay(delay);
+
+		/* break if sda got high, check only when scl line is high */
+		if (!bri->skip_sda_polling && val)
+			if (bri->get_sda(adap))
+				break;
+	}
+
+	return 0;
+}
+
 static int i2c_device_probe(struct device *dev)
 {
 	struct i2c_client	*client = i2c_verify_client(dev);
@@ -861,6 +945,47 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
 			 "Failed to create compatibility class link\n");
 #endif
 
+	/* bus recovery specific initialization */
+	if (adap->bus_recovery_info) {
+		struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+
+		if (bri->recover_bus) {
+			dev_info(&adap->dev,
+				"registered for non-generic bus recovery\n");
+		} else {
+			/* Use generic recovery routines */
+			if (!bri->clock_rate_khz) {
+				dev_warn(&adap->dev,
+					"doesn't have valid recovery clock rate\n");
+				goto exit_recovery;
+			}
+
+			/* Most controller need 9 clocks at max */
+			if (!bri->clock_cnt)
+				bri->clock_cnt = 9;
+
+			if (bri->is_gpio_recovery) {
+				bri->recover_bus = i2c_gpio_recover_bus;
+				dev_info(&adap->dev,
+					"registered for gpio bus recovery\n");
+			} else if (bri->set_scl) {
+				if (!bri->skip_sda_polling && !bri->get_sda) {
+					dev_warn(&adap->dev,
+						"!get_sda. skip sda polling\n");
+					bri->skip_sda_polling = true;
+				}
+
+				bri->recover_bus = i2c_scl_recover_bus;
+				dev_info(&adap->dev,
+					"registered for scl bus recovery\n");
+			} else {
+				dev_warn(&adap->dev,
+					"doesn't have valid recovery type\n");
+			}
+		}
+	}
+
+exit_recovery:
 	/* create pre-declared device nodes */
 	if (adap->nr < __i2c_first_dynamic_bus_num)
 		i2c_scan_static_board_info(adap);
include/linux/i2c.h +52 -0

64 lines not on this page · raw message

Signature · 1 line
1.7.8.110.g4cb5d

A message that is not in the archive (sent off-list or not yet received): 3d25a5406975dbab9d21bfe406e5f779480da17f.1330669025.git.viresh.kumar-qxv4g6HH51o@public.gmane.org

#4 lore raw

Re: [PATCH V3 1/2] i2c/adapter: Add bus recovery infrastructure

Quoted 10 lines · Viresh Kumar

Add i2c bus recovery infrastructure to i2c adapters as specified in the i2c protocol Rev. 03 section 3.16 titled "Bus clear".

http://www.nxp.com/documents/user_manual/UM10204.pdf

Sometimes during operation i2c bus hangs and we need to give dummy clocks to slave device to start the transfer again. Now we may have capability in the bus controller to generate these clocks or platform may have gpio pins which can be toggled to generate dummy clocks. This patch supports both.

Quoted 5 lines

This patch also adds in generic bus recovery routines gpio or scl line based which can be used by bus controller. In addition controller driver may provide its own version of the bus recovery routine.

Finally, a review \o/

There are some comments regarding the code, but the most important thing is to get the interface right.

Jean, I don't think this is embedded only, so an additional view might be helpful here.

Linus, can you have a look wearing your pinmux/pinctrl hat? This functionality may need to switch SDA/SCL pins to GPIOs so they can be manually clocked. I'd think this is a prime example for pinmux, but am unsure if we should rely on it only. Is it enforced for active platforms?

Quoted 20 lines
---
 drivers/i2c/i2c-core.c |  125 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c.h    |   52 ++++++++++++++++++++
 2 files changed, 177 insertions(+), 0 deletions(-)
drivers/i2c/i2c-core.c +2 -0
index e9c1893..5cd5f83 100644
@@ -26,7 +26,9 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/gpio.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/init.h>

drivers/i2c/i2c-core.c static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) · line 111

Quoted 7 lines
drivers/i2c/i2c-core.c +4 -0
@@ -103,6 +105,88 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 #define i2c_device_uevent	NULL
 #endif	/* CONFIG_HOTPLUG */
 
+/* i2c bus recovery routines */
+static int i2c_gpio_recover_bus(struct i2c_adapter *adap)
+{
+	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;

I think something alike 'recov_info' is more readable than 'bri'.

drivers/i2c/i2c-core.c static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) · line 112

Quoted 1 line
drivers/i2c/i2c-core.c +1 -0
+	unsigned long delay = 1000000;

udelay?

Quoted 66 lines
drivers/i2c/i2c-core.c +66 -0
+	int i, ret, val = 1;
+
+	if (bri->get_gpio)
+		bri->get_gpio(bri->scl_gpio);
+
+	ret = gpio_request_one(bri->scl_gpio, bri->scl_gpio_flags, "i2c-scl");
+	if (ret < 0) {
+		dev_warn(&adap->dev, "gpio request fail: %d\n", bri->scl_gpio);
+		return ret;
+	}
+
+	if (!bri->skip_sda_polling) {
+		if (bri->get_gpio)
+			bri->get_gpio(bri->sda_gpio);
+
+		ret = gpio_request_one(bri->sda_gpio, bri->sda_gpio_flags,
+				"i2c-sda");
+		if (ret < 0) {
+			/* work without sda polling */
+			dev_warn(&adap->dev,
+				"sda_gpio request fail: %d. Skip sda polling\n",
+				bri->scl_gpio);
+			bri->skip_sda_polling = true;
+			if (bri->put_gpio)
+				bri->put_gpio(bri->sda_gpio);
+		}
+	}
+
+	delay /= bri->clock_rate_khz * 2;
+
+	for (i = 0; i < bri->clock_cnt * 2; i++, val = !val) {
+		ndelay(delay);
+		gpio_set_value(bri->scl_gpio, val);
+
+		/* break if sda got high, check only when scl line is high */
+		if (!bri->skip_sda_polling && val)
+			if (gpio_get_value(bri->sda_gpio))
+				break;
+	}
+
+	gpio_free(bri->scl_gpio);
+
+	if (bri->put_gpio)
+		bri->put_gpio(bri->scl_gpio);
+
+	if (!bri->skip_sda_polling) {
+		gpio_free(bri->sda_gpio);
+
+		if (bri->put_gpio)
+			bri->put_gpio(bri->sda_gpio);
+	}
+
+	return 0;
+}
+
+static int i2c_scl_recover_bus(struct i2c_adapter *adap)
+{
+	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+	int i, val = 0;
+	unsigned long delay = 1000000;
+
+	delay /= bri->clock_rate_khz * 2;
+
+	for (i = 0; i < bri->clock_cnt * 2; i++,
+			val = !val) {
+		bri->set_scl(adap, val);

drivers/i2c/i2c-core.c static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) · line 188

Quoted 10 lines
drivers/i2c/i2c-core.c +10 -0
+		ndelay(delay);
+
+		/* break if sda got high, check only when scl line is high */
+		if (!bri->skip_sda_polling && val)
+			if (bri->get_sda(adap))
+				break;
+	}
+
+	return 0;
+}

We have two recover bus functions here. I think it would be better to have only one. set_scl and get_sda can be mapped to helper functions doing needed gpio-calls during init. Something like this pseudo-code:

	if (!bri->set_scl && we_have_a_scl_gpio)
		set_scl = i2c_set_scl_via_gpio()
Quoted 34 lines
drivers/i2c/i2c-core.c +27 -0
+
 static int i2c_device_probe(struct device *dev)
 {
 	struct i2c_client	*client = i2c_verify_client(dev);
@@ -861,6 +945,47 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
 			 "Failed to create compatibility class link\n");
 #endif
 
+	/* bus recovery specific initialization */
+	if (adap->bus_recovery_info) {
+		struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+
+		if (bri->recover_bus) {
+			dev_info(&adap->dev,
+				"registered for non-generic bus recovery\n");
+		} else {
+			/* Use generic recovery routines */
+			if (!bri->clock_rate_khz) {
+				dev_warn(&adap->dev,
+					"doesn't have valid recovery clock rate\n");
+				goto exit_recovery;
+			}
+
+			/* Most controller need 9 clocks at max */
+			if (!bri->clock_cnt)
+				bri->clock_cnt = 9;
+
+			if (bri->is_gpio_recovery) {
+				bri->recover_bus = i2c_gpio_recover_bus;
+				dev_info(&adap->dev,
+					"registered for gpio bus recovery\n");
+			} else if (bri->set_scl) {
+				if (!bri->skip_sda_polling && !bri->get_sda) {
+					dev_warn(&adap->dev,

drivers/i2c/i2c-core.c static int i2c_register_adapter(struct i2c_adapter *adap) · line 983

Quoted 10 lines
drivers/i2c/i2c-core.c +10 -0
+						"!get_sda. skip sda polling\n");
+					bri->skip_sda_polling = true;
+				}
+
+				bri->recover_bus = i2c_scl_recover_bus;
+				dev_info(&adap->dev,
+					"registered for scl bus recovery\n");
+			} else {
+				dev_warn(&adap->dev,
+					"doesn't have valid recovery type\n");

Printouts here should probably be dev_dbg (if not left out), most users won't care.

Quoted 13 lines
drivers/i2c/i2c-core.c +5 -0
+			}
+		}
+	}
+
+exit_recovery:
 	/* create pre-declared device nodes */
 	if (adap->nr < __i2c_first_dynamic_bus_num)
 		i2c_scan_static_board_info(adap);
include/linux/i2c.h +0 -0
index 8e25a91..1310d1a 100644

include/linux/i2c.h struct i2c_algorithm { · line 367

Quoted 3 lines
include/linux/i2c.h +0 -0
@@ -365,6 +365,55 @@ struct i2c_algorithm {
 	u32 (*functionality) (struct i2c_adapter *);
 };
 

The description of the members is a good start. To be complete, I'd think we need some higher level description in Documentation/i2c, too, with an example how to set up.

include/linux/i2c.h struct i2c_algorithm { · line 373

Quoted 6 lines
include/linux/i2c.h +6 -0
+/**
+ * struct i2c_bus_recovery_info - I2c bus recovery information
+ * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
+ *	pass it NULL to use generic ones, i.e. gpio or scl based.
+ * @skip_sda_polling: if true, bus recovery will not poll sda line to check if
+ *	it became high or not. Only required if recover_bus == NULL.

Assume this when there is no get_sda() and/or SDA GPIO defined?

include/linux/i2c.h struct i2c_algorithm { · line 375

Quoted 2 lines
include/linux/i2c.h +2 -0
+ * @is_gpio_recovery: true, select gpio type else scl type. Only required if
+ *	recover_bus == NULL.

Assume this when there is no GPIO defined (and no recover_bus)?

include/linux/i2c.h struct i2c_algorithm { · line 377

Quoted 2 lines
include/linux/i2c.h +2 -0
+ * @clock_rate_khz: clock rate of dummy clock in khz. Required for both gpio and
+ *	scl type recovery.

Hrmm, current bus speed should really be in i2c_adapter somewhen. Not your problem, though (unless you want it to be :)) Jean, what do you think?

include/linux/i2c.h struct i2c_algorithm { · line 379

Quoted 2 lines
include/linux/i2c.h +2 -0
+ * @clock_cnt: count of max clocks to be generated. Required for both gpio and
+ *	scl type recovery.

Why should that be different from 9 (as said in the docs)?

include/linux/i2c.h struct i2c_algorithm { · line 387

Quoted 8 lines
include/linux/i2c.h +8 -0
+ * @set_scl: controller specific scl configuration routine. Only required if
+ *	is_gpio_recovery == false
+ * @get_sda: controller specific sda read routine. Only required if
+ *	is_gpio_recovery == false and skip_sda_polling == false.
+ * @get_gpio: called before recover_bus() to get padmux configured for scl line.
+ *	as gpio. Only required if is_gpio_recovery == true.
+ * @put_gpio: called after recover_bus() to get padmux configured for scl line
+ *	as scl. Only required if is_gpio_recovery == true.

Can't we use the pinmux/pinctrl subsystem here? Not knowing too much about it, CCing Linus.

include/linux/i2c.h struct i2c_algorithm { · line 395

Quoted 8 lines
include/linux/i2c.h +8 -0
+ * @scl_gpio: gpio number of the scl line. Only required if is_gpio_recovery ==
+ *	true.
+ * @sda_gpio: gpio number of the sda line. Only required if is_gpio_recovery ==
+ *	true and skip_sda_polling == false.
+ * @scl_gpio_flags: flag for gpio_request_one of scl_gpio. 0 implies
+ *	GPIOF_OUT_INIT_LOW.
+ * @sda_gpio_flags: flag for gpio_request_one of sda_gpio. 0 implies
+ *	GPIOF_OUT_INIT_LOW.

For the last 4, I'd suggest to pass a struct gpio scl_sda_gpio_array[2] or something.

Quoted 25 lines
include/linux/i2c.h +21 -0
+ */
+struct i2c_bus_recovery_info {
+	int (*recover_bus)(struct i2c_adapter *);
+	bool skip_sda_polling;
+	bool is_gpio_recovery;
+	u32 clock_rate_khz;
+	u8 clock_cnt;
+
+	/* scl/sda recovery */
+	void (*set_scl)(struct i2c_adapter *, int val);
+	int (*get_sda)(struct i2c_adapter *);
+
+	/* gpio recovery */
+	int (*get_gpio)(unsigned gpio);
+	int (*put_gpio)(unsigned gpio);
+	u32 scl_gpio;
+	u32 sda_gpio;
+	u32 scl_gpio_flags;
+	u32 sda_gpio_flags;
+};
+
 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.

include/linux/i2c.h struct i2c_adapter { · line 445

Quoted 9 lines
include/linux/i2c.h +3 -0
@@ -388,6 +437,9 @@ struct i2c_adapter {
 
 	struct mutex userspace_clients_lock;
 	struct list_head userspace_clients;
+
+	/* Pass valid pointer if recovery infrastructure is required */
+	struct i2c_bus_recovery_info *bus_recovery_info;
 };
 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
 

I'd also think calling the recover function should be in the core and not in all the drivers.

Regards,

Wolfram

Signature · 2 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

A message that is not in the archive (sent off-list or not yet received): [email protected]

#6 lore raw

Re: [PATCH V3 1/2] i2c/adapter: Add bus recovery infrastructure

Quoted 2 lines · Wolfram Sang

On Fri, Mar 02, 2012 at 11:53:42AM +0530, Viresh Kumar wrote:
Finally, a review \o/

:)

Quoted 7 lines

drivers/i2c/i2c-core.c static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) · line 111

Quoted 5 lines
drivers/i2c/i2c-core.c +4 -0
+/* i2c bus recovery routines */
+static int i2c_gpio_recover_bus(struct i2c_adapter *adap)
+{
+     struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;

I think something alike 'recov_info' is more readable than 'bri'.

Sure.

Quoted 3 lines
Quoted 1 line

+     unsigned long delay = 1000000;

udelay?

No. It's ndelay. Will rename it.

Quoted 29 lines
Quoted 14 lines

+     return 0;

drivers/i2c/i2c-core.c +13 -0
+}
+
+static int i2c_scl_recover_bus(struct i2c_adapter *adap)
+{
+     struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+     int i, val = 0;
+     unsigned long delay = 1000000;
+
+     delay /= bri->clock_rate_khz * 2;
+
+     for (i = 0; i < bri->clock_cnt * 2; i++,
+                     val = !val) {
+             bri->set_scl(adap, val);

drivers/i2c/i2c-core.c static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) · line 188

Quoted 10 lines
drivers/i2c/i2c-core.c +10 -0
+             ndelay(delay);
+
+             /* break if sda got high, check only when scl line is high */
+             if (!bri->skip_sda_polling && val)
+                     if (bri->get_sda(adap))
+                             break;
+     }
+
+     return 0;
+}

We have two recover bus functions here. I think it would be better to have only one. set_scl and get_sda can be mapped to helper functions doing needed gpio-calls during init. Something like this pseudo-code:

Quoted 2 lines

       if (!bri->set_scl && we_have_a_scl_gpio)
               set_scl = i2c_set_scl_via_gpio()

gpio calls can't be done at init. As we have to request and free them again and again. So would be required to keep two calls.

Not sure, if i understand your point here.

Quoted 38 lines
Quoted 38 lines

 static int i2c_device_probe(struct device *dev)
 {
      struct i2c_client       *client = i2c_verify_client(dev);

drivers/i2c/i2c-core.c +31 -0
@@ -861,6 +945,47 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
                        "Failed to create compatibility class link\n");
  #endif
 
+     /* bus recovery specific initialization */
+     if (adap->bus_recovery_info) {
+             struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+
+             if (bri->recover_bus) {
+                     dev_info(&adap->dev,
+                             "registered for non-generic bus recovery\n");
+             } else {
+                     /* Use generic recovery routines */
+                     if (!bri->clock_rate_khz) {
+                             dev_warn(&adap->dev,
+                                     "doesn't have valid recovery clock rate\n");
+                             goto exit_recovery;
+                     }
+
+                     /* Most controller need 9 clocks at max */
+                     if (!bri->clock_cnt)
+                             bri->clock_cnt = 9;
+
+                     if (bri->is_gpio_recovery) {
+                             bri->recover_bus = i2c_gpio_recover_bus;
+                             dev_info(&adap->dev,
+                                     "registered for gpio bus recovery\n");
+                     } else if (bri->set_scl) {
+                             if (!bri->skip_sda_polling && !bri->get_sda) {
+                                     dev_warn(&adap->dev,
+                                             "!get_sda. skip sda polling\n");
+                                     bri->skip_sda_polling = true;
+                             }
+
+                             bri->recover_bus = i2c_scl_recover_bus;
Quoted 8 lines

drivers/i2c/i2c-core.c static int i2c_register_adapter(struct i2c_adapter *adap) · line 983

Quoted 5 lines
drivers/i2c/i2c-core.c +5 -0
+                             dev_info(&adap->dev,
+                                     "registered for scl bus recovery\n");
+                     } else {
+                             dev_warn(&adap->dev,
+                                     "doesn't have valid recovery type\n");

Printouts here should probably be dev_dbg (if not left out), most users won't care.

Hmm. Actually this is a error case. User wanted to have recovery infra, but didn't mention the method of recovery: via gpio or sda/scl. Don't know if we could make it dev_err() instead.

Quoted 12 lines
Quoted 12 lines
drivers/i2c/i2c-core.c +5 -0
+                     }
+             }
+     }
+
+exit_recovery:
       /* create pre-declared device nodes */
       if (adap->nr < __i2c_first_dynamic_bus_num)
               i2c_scan_static_board_info(adap);
include/linux/i2c.h +0 -0
index 8e25a91..1310d1a 100644
Quoted 8 lines

include/linux/i2c.h struct i2c_algorithm { · line 367

Quoted 4 lines
include/linux/i2c.h +0 -0
@@ -365,6 +365,55 @@ struct i2c_algorithm {
       u32 (*functionality) (struct i2c_adapter *);
  };
 

The description of the members is a good start. To be complete, I'd think we need some higher level description in Documentation/i2c, too, with an example how to set up.

Sure.

Quoted 8 lines

include/linux/i2c.h struct i2c_algorithm { · line 373

Quoted 6 lines
include/linux/i2c.h +6 -0
+/**
+ * struct i2c_bus_recovery_info - I2c bus recovery information
+ * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
+ *   pass it NULL to use generic ones, i.e. gpio or scl based.
+ * @skip_sda_polling: if true, bus recovery will not poll sda line to check if
+ *   it became high or not. Only required if recover_bus == NULL.

Assume this when there is no get_sda() and/or SDA GPIO  defined?

Can be done with get_sda(), but have doubt for GPIO. How to check if sda polling is required in GPIO case. Obviously by checking its value. Zero is a valid value.
And -1 doesn't look good.

Quoted 4 lines

include/linux/i2c.h struct i2c_algorithm { · line 375

Quoted 2 lines
include/linux/i2c.h +2 -0
+ * @is_gpio_recovery: true, select gpio type else scl type. Only required if
+ *   recover_bus == NULL.

Assume this when there is no GPIO defined (and no recover_bus)?

Can be done based on value of set_scl, but not on GPIO because of earlier mentioned reasons.

Quoted 4 lines

include/linux/i2c.h struct i2c_algorithm { · line 379

Quoted 2 lines
include/linux/i2c.h +2 -0
+ * @clock_cnt: count of max clocks to be generated. Required for both gpio and
+ *   scl type recovery.

Why should that be different from 9 (as said in the docs)?

Don't know. Somebody, probably Tegra guys, asked for it during earlier versions of this
patch. IIRC, they needed 10.

Quoted 11 lines

include/linux/i2c.h struct i2c_algorithm { · line 387

Quoted 8 lines
include/linux/i2c.h +8 -0
+ * @set_scl: controller specific scl configuration routine. Only required if
+ *   is_gpio_recovery == false
+ * @get_sda: controller specific sda read routine. Only required if
+ *   is_gpio_recovery == false and skip_sda_polling == false.
+ * @get_gpio: called before recover_bus() to get padmux configured for scl line.
+ *   as gpio. Only required if is_gpio_recovery == true.
+ * @put_gpio: called after recover_bus() to get padmux configured for scl line
+ *   as scl. Only required if is_gpio_recovery == true.

Can't we use the pinmux/pinctrl subsystem here? Not knowing too much about it, CCing Linus.

Can be. But probably true only for architectures where pinctrl is defined. Linus?

Quoted 11 lines

include/linux/i2c.h struct i2c_algorithm { · line 395

Quoted 8 lines
include/linux/i2c.h +8 -0
+ * @scl_gpio: gpio number of the scl line. Only required if is_gpio_recovery ==
+ *   true.
+ * @sda_gpio: gpio number of the sda line. Only required if is_gpio_recovery ==
+ *   true and skip_sda_polling == false.
+ * @scl_gpio_flags: flag for gpio_request_one of scl_gpio. 0 implies
+ *   GPIOF_OUT_INIT_LOW.
+ * @sda_gpio_flags: flag for gpio_request_one of sda_gpio. 0 implies
+ *   GPIOF_OUT_INIT_LOW.

For the last 4, I'd suggest to pass a struct gpio scl_sda_gpio_array[2] or something.

Ok.

Quoted 26 lines
Quoted 26 lines
include/linux/i2c.h +21 -0
+ */
+struct i2c_bus_recovery_info {
+     int (*recover_bus)(struct i2c_adapter *);
+     bool skip_sda_polling;
+     bool is_gpio_recovery;
+     u32 clock_rate_khz;
+     u8 clock_cnt;
+
+     /* scl/sda recovery */
+     void (*set_scl)(struct i2c_adapter *, int val);
+     int (*get_sda)(struct i2c_adapter *);
+
+     /* gpio recovery */
+     int (*get_gpio)(unsigned gpio);
+     int (*put_gpio)(unsigned gpio);
+     u32 scl_gpio;
+     u32 sda_gpio;
+     u32 scl_gpio_flags;
+     u32 sda_gpio_flags;
+};
+
  /*
   * i2c_adapter is the structure used to identify a physical i2c bus along
   * with the access algorithms necessary to access it.
@@ -388,6 +437,9 @@ struct i2c_adapter {
 
Quoted 11 lines

include/linux/i2c.h struct i2c_adapter { · line 445

Quoted 8 lines
include/linux/i2c.h +3 -0
       struct mutex userspace_clients_lock;
       struct list_head userspace_clients;
+
+     /* Pass valid pointer if recovery infrastructure is required */
+     struct i2c_bus_recovery_info *bus_recovery_info;
  };
  #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
 

I'd also think calling the recover function should be in the core and not in all the drivers.

Sounds good. Based on, if timeout is returned by controller or not.

--
Viresh

A message that is not in the archive (sent off-list or not yet received): CAOh2x==SfyVVvoK83c4fBO5HVkLtJhd3CDcDfULXTNcZzfZ4Dg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#8 lore raw

Re: [PATCH V3 1/2] i2c/adapter: Add bus recovery infrastructure

Quoted 11 lines · viresh kumar
Quoted 9 lines · Wolfram Sang
Quoted 8 lines

+ * @set_scl: controller specific scl configuration routine. Only required if
+ *   is_gpio_recovery == false
+ * @get_sda: controller specific sda read routine. Only required if
+ *   is_gpio_recovery == false and skip_sda_polling == false.
+ * @get_gpio: called before recover_bus() to get padmux configured for scl line.
+ *   as gpio. Only required if is_gpio_recovery == true.
+ * @put_gpio: called after recover_bus() to get padmux configured for scl line
+ *   as scl. Only required if is_gpio_recovery == true.

Quoted 5 lines
Quoted 2 lines

Can't we use the pinmux/pinctrl subsystem here? Not knowing too much about it, CCing Linus.

Can be. But probably true only for architectures where pinctrl is defined. Linus?

We *could* do it by defining a standard state for the pinctrl handle in <linux/pinctrl/pinctrl-state.h> (pending in linux-next) like:

#define PINCTRL_STATE_GPIO "gpio"

Or so, then have the core grab that using the struct device * of this pin controller, returing it to PINCTRL_STATE_DEFAULT afterwards.

It seems a bit scary to do that in core code though, because we don't know which state we should return to, should it really be default state?

And what if we're using GPIO bit-banging, then we grab the pins into some state they're already in.

So to me it seems better to do this at driver level using these hooks so the driver is in full control of the pinctrl states.

Stephen, do you agree?

But:

Many platforms I've seen actually have the ability to mux their I2C pins into GPIO and bitbang them, sometimes this seems to be used to work around broken silicon for example. And it seems what is done here is simply some instance of bitbanging (am I right?).

I wonder if it would make sense to model this in the core, so any I2C driver can specify that it also supports bit-banging using GPIO pins A,B and then this can be used for anything, not just for this recovery.

So the above hooks should be possible to use to switch the entire driver over to bitbang mode.

Yours,
Linus Walleij

A message that is not in the archive (sent off-list or not yet received): CACRpkdboocdbmx-cFR_ApYX=B4e=CDMzWn+oZMWrn7vxTpnvvw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#10 lore raw

Re: [PATCH V3 1/2] i2c/adapter: Add bus recovery infrastructure

Quoted 30 lines · Linus Walleij
Quoted 11 lines · viresh kumar
Quoted 9 lines · Wolfram Sang
Quoted 8 lines

+ * @set_scl: controller specific scl configuration routine. Only required if
+ * is_gpio_recovery == false
+ * @get_sda: controller specific sda read routine. Only required if
+ * is_gpio_recovery == false and skip_sda_polling == false.
+ * @get_gpio: called before recover_bus() to get padmux configured for scl line.
+ * as gpio. Only required if is_gpio_recovery == true.
+ * @put_gpio: called after recover_bus() to get padmux configured for scl line
+ * as scl. Only required if is_gpio_recovery == true.

Quoted 5 lines
Quoted 2 lines

Can't we use the pinmux/pinctrl subsystem here? Not knowing too much about it, CCing Linus.

Can be. But probably true only for architectures where pinctrl is defined. Linus?

We *could* do it by defining a standard state for the pinctrl handle in <linux/pinctrl/pinctrl-state.h> (pending in linux-next) like:

#define PINCTRL_STATE_GPIO "gpio"

Or so, then have the core grab that using the struct device * of this pin controller, returing it to PINCTRL_STATE_DEFAULT afterwards.

It seems a bit scary to do that in core code though, because we don't know which state we should return to, should it really be default state?

Quoted 7 lines

And what if we're using GPIO bit-banging, then we grab the pins into some state they're already in.

So to me it seems better to do this at driver level using these hooks so the driver is in full control of the pinctrl states.

Stephen, do you agree?

Yes: I don't think pinctrl alone is the correct way here.

The reason here is there's no guarantee that pinctrl is able to interact with GPIOs in any way.

On some systems, there may be dedicated pins for (at least some) GPIOs, so there may be no pinctrl driver that affects them (that'd cover the I2C-over-GPIO-bit-banging case).

We've defined that gpio_direction_input/output() must set up any pinmux as required to enable usage of those pins as GPIOs. However, we have not defined that the pinctrl driver must expose a (pinctrl, not C) function that allows configuring a pin/group as GPIOs. Hence, again, there may be no way for pinctrl to switch pins into GPIO mode even when they could be used as GPIOs.

In particular, on Tegra, GPIO vs. pinctrl is controlled by the GPIO HW and driver, not the pinctrl HW or driver, and so the Tegra pinctrl driver doesn't expose any GPIO function. Rather, the GPIO driver programs the relevant HW bits inside gpio_direction_input/output(), and simply notifies the pinctrl core that it has, rather than having the pinctrl driver program any HW.

That said, other HW might have multiple GPIOs that can be routed to a single pin, and might allow configuration of which via pinctrl.

I guess what we need to do is have I2C drivers provide the following information to the I2C core:

a) GPIO ID for SCL and SDA
b) Optional pinctrl state for GPIO-based recovery, and the pinctrl state to return to afterwards

The I2C core would have to do something like:

if (state_bitbang)
pinctrl_select_state(state_bitbang)
gpio_request(gpio_scl)
gpio_request(gpio_sda)
// do recovery
gpio_free(gpio_sda)
gpio_free(gpio_scl)
if (state_bitbang_done)
pinctrl_select_state(state_bitbang_done)

Noting that perhaps state_bitbang_done could be changed dynamically by the driver for internal reasons, and hence shouldn't be cached by the I2C core, but rather read from the I2C driver struct each time it's used.

Quoted 7 lines

But:

Many platforms I've seen actually have the ability to mux their I2C pins into GPIO and bitbang them, sometimes this seems to be used to work around broken silicon for example. And it seems what is done here is simply some instance of bitbanging (am I right?).

Finally, in order to cover any other special cases, I guess I2C drivers need to have a top-level entry-point to perform bus recovery, so they can do any HW programming of the I2C controller they need, then call into the I2C core recovery utility code I outlined above. That should allow complete flexibility.

Quoted 7 lines

I wonder if it would make sense to model this in the core, so any I2C driver can specify that it also supports bit-banging using GPIO pins A,B and then this can be used for anything, not just for this recovery.

So the above hooks should be possible to use to switch the entire driver over to bitbang mode.

#11 lore raw

Re: [PATCH V3 1/2] i2c/adapter: Add bus recovery infrastructure

In reply to a message not in the archive

Hi Wolfram,

Quoted 17 lines · Wolfram Sang
Quoted 10 lines · Viresh Kumar

Add i2c bus recovery infrastructure to i2c adapters as specified in the i2c protocol Rev. 03 section 3.16 titled "Bus clear".

http://www.nxp.com/documents/user_manual/UM10204.pdf

Sometimes during operation i2c bus hangs and we need to give dummy clocks to slave device to start the transfer again. Now we may have capability in the bus controller to generate these clocks or platform may have gpio pins which can be toggled to generate dummy clocks. This patch supports both.

Quoted 5 lines

This patch also adds in generic bus recovery routines gpio or scl line based which can be used by bus controller. In addition controller driver may provide its own version of the bus recovery routine.

Quoted 7 lines

Finally, a review \o/

There are some comments regarding the code, but the most important thing is to get the interface right.

Jean, I don't think this is embedded only, so an additional view might be helpful here.

I'm afraid I won't have any time for this in a near future. If/when you are happy with the code, feel free to merge it.

Thanks,

Signature · 1 line
Jean Delvare
#12 lore raw

PATCH v3 2/2 i2c/designware: Provide i2c bus recovery support

In reply to a message not in the archive

Add bus recovery support for designware_i2c controller. It uses generic gpio based i2c_gpio_recover_bus() routine.

---
 drivers/i2c/busses/i2c-designware-core.c    |    7 +++-
 drivers/i2c/busses/i2c-designware-platdrv.c |   24 +++++++++++++
 include/linux/i2c/i2c-designware.h          |   49 +++++++++++++++++++++++++++
 3 files changed, 79 insertions(+), 1 deletions(-)
 create mode 100644 include/linux/i2c/i2c-designware.h
drivers/i2c/busses/i2c-designware-core.c +6 -1
index df87992..db70fcb 100644
@@ -525,7 +525,12 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
 	if (ret == 0) {
 		dev_err(dev->dev, "controller timed out\n");
-		i2c_dw_init(dev);
+		if (adap->bus_recovery_info &&
+				adap->bus_recovery_info->recover_bus) {
+			dev_dbg(dev->dev, "try i2c bus recovery\n");
+			adap->bus_recovery_info->recover_bus(adap);
+		}
+
 		ret = -ETIMEDOUT;
 		goto done;
 	} else if (ret < 0)
drivers/i2c/busses/i2c-designware-platdrv.c +24 -0
index f08d024..76bf108 100644
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/i2c/i2c-designware.h>
 #include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
@@ -45,6 +46,11 @@ static struct i2c_algorithm i2c_dw_algo = {
 	.master_xfer	= i2c_dw_xfer,
 	.functionality	= i2c_dw_func,
 };
+
+static struct i2c_bus_recovery_info dw_recovery_info = {
+	.is_gpio_recovery = true,
+};
+
 static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
 {
 	return clk_get_rate(dev->clk)/1000;
@@ -55,6 +61,7 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
 	struct dw_i2c_dev *dev;
 	struct i2c_adapter *adap;
 	struct resource *mem, *ioarea;
+	struct i2c_dw_pdata *pdata;
 	int irq, r;
 
 	/* NOTE: driver uses the static register mapping */
@@ -141,6 +148,23 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
 	adap->dev.parent = &pdev->dev;
 	adap->dev.of_node = pdev->dev.of_node;
 
+	/* Bus recovery support */
+	pdata = dev_get_platdata(&pdev->dev);
+	if (pdata) {
+		dw_recovery_info.get_gpio = pdata->get_gpio;
+		dw_recovery_info.put_gpio = pdata->put_gpio;
+		dw_recovery_info.scl_gpio = pdata->scl_gpio;
+		dw_recovery_info.scl_gpio_flags = pdata->scl_gpio_flags;
+		dw_recovery_info.clock_rate_khz = clk_get_rate(dev->clk) / 1000;
+
+		if (!pdata->skip_sda_polling) {
+			dw_recovery_info.sda_gpio = pdata->sda_gpio;
+			dw_recovery_info.sda_gpio_flags = pdata->sda_gpio_flags;
+		}
+
+		adap->bus_recovery_info = &dw_recovery_info;
+	}
+
 	adap->nr = pdev->id;
 	r = i2c_add_numbered_adapter(adap);
 	if (r) {
include/linux/i2c/i2c-designware.h new file +49 -0

49 lines not on this page · raw message

Signature · 1 line
1.7.8.110.g4cb5d
#14 lore raw
In reply to a message not in the archive
Quoted 7 lines · Viresh KUMAR

This patchset adds i2c bus recovery infrastructure to i2c adapters as specified in the i2c protocol Rev. 03 section 3.16 titled "Bus clear".

http://www.nxp.com/documents/user_manual/UM10204.pdf

This patch was earlier part of a separate thread:
http://www.spinics.net/lists/linux-i2c/msg07267.html

Hi Wolfram,

I know that you have been very busy with other stuff, but Can you please apply this patchset if it looks fine to you?

It had been in queue for over 1.5 months now :(

Signature · 1 line
viresh

A message that is not in the archive (sent off-list or not yet received): [email protected]

#16 lore raw

Hello Wolfram,

Quoted 9 lines · Viresh KUMAR
Quoted 7 lines · Viresh KUMAR

This patchset adds i2c bus recovery infrastructure to i2c adapters as specified in the i2c protocol Rev. 03 section 3.16 titled "Bus clear".

http://www.nxp.com/documents/user_manual/UM10204.pdf

This patch was earlier part of a separate thread:
http://www.spinics.net/lists/linux-i2c/msg07267.html

Quoted 7 lines

Hi Wolfram,

I know that you have been very busy with other stuff, but Can you please apply this patchset if it looks fine to you?

It had been in queue for over 1.5 months now :(

Tested By: Rajeev Kumar <[email protected]>

Rgds
Rajeev