mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* i2c bus recovery support
@ 2015-07-08 13:29 Jan Luebbe
  2015-07-08 13:29 ` [PATCH 1/4] i2c: add bus recovery infrastructure Jan Luebbe
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Jan Luebbe @ 2015-07-08 13:29 UTC (permalink / raw)
  To: barebox

This series ports the kernel's i2c bus recovery support to barebox.

Bus recovery is necessary when a slave device pulls the SDA line low because it
is in a wrong state. The fix is to toggle the SCL line, which move the slaves
into the idle state. Using the same structure and callbacks as the kernel makes
it easy to port the per-driver code to barebox.


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] i2c: add bus recovery infrastructure
  2015-07-08 13:29 i2c bus recovery support Jan Luebbe
@ 2015-07-08 13:29 ` Jan Luebbe
  2015-07-13  6:47   ` Sascha Hauer
  2015-07-08 13:29 ` [PATCH 2/4] i2c: only use set_scl for recovery after calling prepare_recovery Jan Luebbe
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Jan Luebbe @ 2015-07-08 13:29 UTC (permalink / raw)
  To: barebox

This is based on the code introduced to the kernel in
5f9296ba21b3c395e53dd84e7ff9578f97f24295.

Signed-off-by: Jan Luebbe <jluebbe@debian.org>
---
 drivers/i2c/i2c.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/i2c/i2c.h |  42 ++++++++++++++
 2 files changed, 203 insertions(+)

diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index ebc7e23..e350ac5 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -23,6 +23,7 @@
 #include <xfuncs.h>
 #include <init.h>
 #include <of.h>
+#include <gpio.h>
 
 #include <i2c/i2c.h>
 
@@ -228,6 +229,133 @@ int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count)
 }
 EXPORT_SYMBOL(i2c_write_reg);
 
+/* i2c bus recovery routines */
+static int get_scl_gpio_value(struct i2c_adapter *adap)
+{
+	gpio_direction_input(adap->bus_recovery_info->scl_gpio);
+	return gpio_get_value(adap->bus_recovery_info->scl_gpio);
+}
+
+static void set_scl_gpio_value(struct i2c_adapter *adap, int val)
+{
+	if (val)
+		gpio_direction_input(adap->bus_recovery_info->scl_gpio);
+	else
+		gpio_direction_output(adap->bus_recovery_info->scl_gpio, 0);
+}
+
+static int get_sda_gpio_value(struct i2c_adapter *adap)
+{
+	return gpio_get_value(adap->bus_recovery_info->sda_gpio);
+}
+
+static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
+{
+	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+	struct device_d *dev = &adap->dev;
+	int ret = 0;
+
+	ret = gpio_request_one(bri->scl_gpio, GPIOF_IN, "i2c-scl");
+	if (ret) {
+		dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio);
+		return ret;
+	}
+
+	if (bri->get_sda) {
+		if (gpio_request_one(bri->sda_gpio, GPIOF_IN, "i2c-sda")) {
+			/* work without SDA polling */
+			dev_warn(dev, "Can't get SDA gpio: %d. Not using SDA polling\n",
+					bri->sda_gpio);
+			bri->get_sda = NULL;
+		}
+	}
+
+	return ret;
+}
+
+static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap)
+{
+	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+
+	if (bri->get_sda)
+		gpio_free(bri->sda_gpio);
+
+	gpio_free(bri->scl_gpio);
+}
+
+/*
+ * 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
+ * is: delay in ns = (10^6 / 100) / 2
+ */
+#define RECOVERY_NDELAY		5000
+#define RECOVERY_CLK_CNT	9
+
+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;
+
+	if (bri->prepare_recovery)
+		bri->prepare_recovery(adap);
+
+	/*
+	 * By this time SCL is high, as we need to give 9 falling-rising edges
+	 */
+	while (i++ < RECOVERY_CLK_CNT * 2) {
+		if (val) {
+			/* Break if SDA is high */
+			if (bri->get_sda && bri->get_sda(adap))
+					break;
+			/* SCL shouldn't be low here */
+			if (!bri->get_scl(adap)) {
+				dev_err(&adap->dev,
+					"SCL is stuck low, exit recovery\n");
+				ret = -EBUSY;
+				break;
+			}
+		}
+
+		val = !val;
+		bri->set_scl(adap, val);
+		ndelay(RECOVERY_NDELAY);
+	}
+
+	if (bri->unprepare_recovery)
+		bri->unprepare_recovery(adap);
+
+	return ret;
+}
+
+int i2c_generic_scl_recovery(struct i2c_adapter *adap)
+{
+	adap->bus_recovery_info->set_scl(adap, 1);
+	return i2c_generic_recovery(adap);
+}
+
+int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
+{
+	int ret;
+
+	ret = i2c_get_gpios_for_recovery(adap);
+	if (ret)
+		return ret;
+
+	ret = i2c_generic_recovery(adap);
+	i2c_put_gpios_for_recovery(adap);
+
+	return ret;
+}
+
+int i2c_recover_bus(struct i2c_adapter *adap)
+{
+	if (!adap->bus_recovery_info)
+		return -EOPNOTSUPP;
+
+	dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
+	return adap->bus_recovery_info->recover_bus(adap);
+}
+
 /**
  * i2c_new_device - instantiate one new I2C device
  *
@@ -456,6 +584,39 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
 
 	list_add_tail(&adapter->list, &adapter_list);
 
+	/* bus recovery specific initialization */
+	if (adapter->bus_recovery_info) {
+		struct i2c_bus_recovery_info *bri = adapter->bus_recovery_info;
+
+		if (!bri->recover_bus) {
+			dev_err(&adapter->dev, "No recover_bus() found, not using recovery\n");
+			adapter->bus_recovery_info = NULL;
+			goto exit_recovery;
+		}
+
+		/* Generic GPIO recovery */
+		if (bri->recover_bus == i2c_generic_gpio_recovery) {
+			if (!gpio_is_valid(bri->scl_gpio)) {
+				dev_err(&adapter->dev, "Invalid SCL gpio, not using recovery\n");
+				adapter->bus_recovery_info = NULL;
+				goto exit_recovery;
+			}
+
+			if (gpio_is_valid(bri->sda_gpio))
+				bri->get_sda = get_sda_gpio_value;
+			else
+				bri->get_sda = NULL;
+
+			bri->get_scl = get_scl_gpio_value;
+			bri->set_scl = set_scl_gpio_value;
+		} else if (!bri->set_scl || !bri->get_scl) {
+			/* Generic SCL recovery */
+			dev_err(&adapter->dev, "No {get|set}_gpio() found, not using recovery\n");
+			adapter->bus_recovery_info = NULL;
+		}
+	}
+exit_recovery:
+
 	/* populate children from any i2c device tables */
 	scan_boardinfo(adapter);
 
diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h
index 4696f43..04de8c9 100644
--- a/include/i2c/i2c.h
+++ b/include/i2c/i2c.h
@@ -19,6 +19,8 @@
 #include <driver.h>
 #include <linux/types.h>
 
+struct i2c_adapter;
+
 /*
  * struct i2c_platform_data - structure of platform data for MXC I2C driver
  * @param	bitrate	Bus speed measured in Hz
@@ -61,6 +63,44 @@ struct i2c_msg {
 	__u16			len;	/**< Number of data bytes in @buf being read from or written to the I2C slave address. */
 };
 
+/**
+ * struct i2c_bus_recovery_info - I2C bus recovery information
+ * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
+ *	i2c_generic_scl_recovery() or i2c_generic_gpio_recovery().
+ * @get_scl: This gets current value of SCL line. Mandatory for generic SCL
+ *	recovery. Used internally for generic GPIO recovery.
+ * @set_scl: This sets/clears SCL line. Mandatory for generic SCL recovery. Used
+ *	internally for generic GPIO recovery.
+ * @get_sda: This gets current value of SDA line. Optional for generic SCL
+ *	recovery. Used internally, if sda_gpio is a valid GPIO, for generic GPIO
+ *	recovery.
+ * @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
+ *	may configure padmux here for SDA/SCL line or something else they want.
+ * @scl_gpio: gpio number of the SCL line. Only required for GPIO recovery.
+ * @sda_gpio: gpio number of the SDA line. Only required for GPIO recovery.
+ */
+struct i2c_bus_recovery_info {
+	int (*recover_bus)(struct i2c_adapter *);
+
+	int (*get_scl)(struct i2c_adapter *);
+	void (*set_scl)(struct i2c_adapter *, int val);
+	int (*get_sda)(struct i2c_adapter *);
+
+	void (*prepare_recovery)(struct i2c_adapter *);
+	void (*unprepare_recovery)(struct i2c_adapter *);
+
+	/* gpio recovery */
+	int scl_gpio;
+	int sda_gpio;
+};
+
+int i2c_recover_bus(struct i2c_adapter *adap);
+
+/* Generic recovery routines */
+int i2c_generic_gpio_recovery(struct i2c_adapter *adap);
+int i2c_generic_scl_recovery(struct i2c_adapter *adap);
 
 /**
  * i2c_adapter is the structure used to identify a physical i2c bus
@@ -74,6 +114,8 @@ struct i2c_adapter {
 	struct list_head	list;
 	int			retries;
 	void 			*algo_data;
+
+	struct i2c_bus_recovery_info *bus_recovery_info;
 };
 
 
-- 
2.1.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/4] i2c: only use set_scl for recovery after calling prepare_recovery
  2015-07-08 13:29 i2c bus recovery support Jan Luebbe
  2015-07-08 13:29 ` [PATCH 1/4] i2c: add bus recovery infrastructure Jan Luebbe
@ 2015-07-08 13:29 ` Jan Luebbe
  2015-07-08 13:29 ` [PATCH 3/4] i2c-omap: clear ARDY twice Jan Luebbe
  2015-07-08 13:29 ` [PATCH 4/4] i2c-omap: add bus recovery support Jan Luebbe
  3 siblings, 0 replies; 6+ messages in thread
From: Jan Luebbe @ 2015-07-08 13:29 UTC (permalink / raw)
  To: barebox

set_scl may be ineffective before calling the driver specific
prepare_recovery callback.

Signed-off-by: Jan Luebbe <jluebbe@debian.org>
---
 drivers/i2c/i2c.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index e350ac5..39837fe 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -299,6 +299,9 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
 	if (bri->prepare_recovery)
 		bri->prepare_recovery(adap);
 
+	bri->set_scl(adap, val);
+	ndelay(RECOVERY_NDELAY);
+
 	/*
 	 * By this time SCL is high, as we need to give 9 falling-rising edges
 	 */
@@ -329,7 +332,6 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
 
 int i2c_generic_scl_recovery(struct i2c_adapter *adap)
 {
-	adap->bus_recovery_info->set_scl(adap, 1);
 	return i2c_generic_recovery(adap);
 }
 
-- 
2.1.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/4] i2c-omap: clear ARDY twice
  2015-07-08 13:29 i2c bus recovery support Jan Luebbe
  2015-07-08 13:29 ` [PATCH 1/4] i2c: add bus recovery infrastructure Jan Luebbe
  2015-07-08 13:29 ` [PATCH 2/4] i2c: only use set_scl for recovery after calling prepare_recovery Jan Luebbe
@ 2015-07-08 13:29 ` Jan Luebbe
  2015-07-08 13:29 ` [PATCH 4/4] i2c-omap: add bus recovery support Jan Luebbe
  3 siblings, 0 replies; 6+ messages in thread
From: Jan Luebbe @ 2015-07-08 13:29 UTC (permalink / raw)
  To: barebox

This implements the fix from the kernel commit
4cdbf7d346e7461c3b93a26707c852e2c9db3753.

Signed-off-by: Jan Luebbe <jluebbe@debian.org>
---
 drivers/i2c/busses/i2c-omap.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 330db98..58d2ec9 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -673,6 +673,10 @@ omap_i2c_isr(struct omap_i2c_struct *dev)
 		/*
 		 * ProDB0017052: Clear ARDY bit twice
 		 */
+		if (stat & OMAP_I2C_STAT_ARDY)
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ARDY);
+
+
 		if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
 					OMAP_I2C_STAT_AL)) {
 			omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY |
-- 
2.1.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/4] i2c-omap: add bus recovery support
  2015-07-08 13:29 i2c bus recovery support Jan Luebbe
                   ` (2 preceding siblings ...)
  2015-07-08 13:29 ` [PATCH 3/4] i2c-omap: clear ARDY twice Jan Luebbe
@ 2015-07-08 13:29 ` Jan Luebbe
  3 siblings, 0 replies; 6+ messages in thread
From: Jan Luebbe @ 2015-07-08 13:29 UTC (permalink / raw)
  To: barebox

This is based on commit 9dcb0e7b999db6c420c70fd32497a979a044fcdf from
the kernel with some additional fixes.

Signed-off-by: Jan Luebbe <jluebbe@debian.org>
---
 drivers/i2c/busses/i2c-omap.c | 91 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 88 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 58d2ec9..3abf05a 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -107,16 +107,20 @@
 #define OMAP_I2C_SCLH_HSSCLH	8
 
 /* I2C System Test Register (OMAP_I2C_SYSTEST): */
-#ifdef DEBUG
 #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 */
+/* Functional mode */
+#define OMAP_I2C_SYSTEST_SCL_I_FUNC	(1 << 8)	/* SCL line input value */
+#define OMAP_I2C_SYSTEST_SCL_O_FUNC	(1 << 7)	/* SCL line output value */
+#define OMAP_I2C_SYSTEST_SDA_I_FUNC	(1 << 6)	/* SDA line input value */
+#define OMAP_I2C_SYSTEST_SDA_O_FUNC	(1 << 5)	/* SDA line output value */
+/* SDA/SCL IO mode */
 #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 */
 #define OMAP_I2C_SYSTEST_SDA_O		(1 << 0)	/* SDA line drive out */
-#endif
 
 /* OCP_SYSSTATUS bit definitions */
 #define SYSS_RESETDONE_MASK		(1 << 0)
@@ -492,7 +496,7 @@ static int omap_i2c_wait_for_bb(struct i2c_adapter *adapter)
 	while (omap_i2c_read_reg(i2c_omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
 		if (is_timeout(start, MSECOND)) {
 			dev_warn(&adapter->dev, "timeout waiting for bus ready\n");
-			return -ETIMEDOUT;
+			return i2c_recover_bus(adapter);
 		}
 	}
 
@@ -990,6 +994,86 @@ out:
 #define OMAP_I2C_SCHEME_0		0
 #define OMAP_I2C_SCHEME_1		1
 
+static int omap_i2c_get_scl(struct i2c_adapter *adapter)
+{
+	struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter);
+	u32 reg;
+
+	reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG);
+
+	printf("%s %i\n", __func__, !!(reg & OMAP_I2C_SYSTEST_SCL_I_FUNC));
+	return reg & OMAP_I2C_SYSTEST_SCL_I_FUNC;
+}
+
+static int omap_i2c_get_sda(struct i2c_adapter *adapter)
+{
+	struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter);
+	u32 reg;
+
+	reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG);
+
+	printf("%s %i\n", __func__, !!(reg & OMAP_I2C_SYSTEST_SDA_I_FUNC));
+	return reg & OMAP_I2C_SYSTEST_SDA_I_FUNC;
+}
+
+static void omap_i2c_set_scl(struct i2c_adapter *adapter, int val)
+{
+	struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter);
+	u32 reg;
+
+	printf("%s %i\n", __func__, val);
+	reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG);
+	if (val)
+		reg |= OMAP_I2C_SYSTEST_SCL_O;
+	else
+		reg &= ~OMAP_I2C_SYSTEST_SCL_O;
+	omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSTEST_REG, reg);
+}
+
+static void omap_i2c_prepare_recovery(struct i2c_adapter *adapter)
+{
+	struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter);
+	u32 reg;
+
+	printf("%s\n", __func__);
+
+	reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG);
+	/* enable test mode */
+	reg |= OMAP_I2C_SYSTEST_ST_EN;
+	/* select SDA/SCL IO mode */
+	reg |= 3 << OMAP_I2C_SYSTEST_TMODE_SHIFT;
+	/* set SCL to high-impedance state (reset value is 0) */
+	reg |= OMAP_I2C_SYSTEST_SCL_O;
+	/* set SDA to high-impedance state (reset value is 0) */
+	reg |= OMAP_I2C_SYSTEST_SDA_O;
+	omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSTEST_REG, reg);
+}
+
+static void omap_i2c_unprepare_recovery(struct i2c_adapter *adapter)
+{
+	struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter);
+	u32 reg;
+
+	printf("%s\n", __func__);
+
+	reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG);
+	/* restore reset values */
+	reg &= ~OMAP_I2C_SYSTEST_ST_EN;
+	reg &= ~OMAP_I2C_SYSTEST_TMODE_MASK;
+	reg &= ~OMAP_I2C_SYSTEST_SCL_O;
+	reg &= ~OMAP_I2C_SYSTEST_SDA_O;
+	omap_i2c_write_reg(i2c_omap, 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 __init
 i2c_omap_probe(struct device_d *pdev)
 {
@@ -1101,6 +1185,7 @@ i2c_omap_probe(struct device_d *pdev)
 	i2c_omap->adapter.nr = pdev->id;
 	i2c_omap->adapter.dev.parent = pdev;
 	i2c_omap->adapter.dev.device_node = pdev->device_node;
+	i2c_omap->adapter.bus_recovery_info = &omap_i2c_bus_recovery_info;
 
 	/* i2c device drivers may be active on return from add_adapter() */
 	r = i2c_add_numbered_adapter(&i2c_omap->adapter);
-- 
2.1.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/4] i2c: add bus recovery infrastructure
  2015-07-08 13:29 ` [PATCH 1/4] i2c: add bus recovery infrastructure Jan Luebbe
@ 2015-07-13  6:47   ` Sascha Hauer
  0 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2015-07-13  6:47 UTC (permalink / raw)
  To: Jan Luebbe; +Cc: barebox

On Wed, Jul 08, 2015 at 03:29:15PM +0200, Jan Luebbe wrote:
> This is based on the code introduced to the kernel in
> 5f9296ba21b3c395e53dd84e7ff9578f97f24295.
> 
> Signed-off-by: Jan Luebbe <jluebbe@debian.org>
> ---
>  drivers/i2c/i2c.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/i2c/i2c.h |  42 ++++++++++++++
>  2 files changed, 203 insertions(+)
> 
> diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
> index ebc7e23..e350ac5 100644
> --- a/drivers/i2c/i2c.c
> +++ b/drivers/i2c/i2c.c
> @@ -23,6 +23,7 @@
>  #include <xfuncs.h>
>  #include <init.h>
>  #include <of.h>
> +#include <gpio.h>
>  
>  #include <i2c/i2c.h>
>  
> @@ -228,6 +229,133 @@ int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count)
>  }
>  EXPORT_SYMBOL(i2c_write_reg);
>  
> +/* i2c bus recovery routines */
> +static int get_scl_gpio_value(struct i2c_adapter *adap)
> +{
> +	gpio_direction_input(adap->bus_recovery_info->scl_gpio);
> +	return gpio_get_value(adap->bus_recovery_info->scl_gpio);
> +}
> +
> +static void set_scl_gpio_value(struct i2c_adapter *adap, int val)
> +{
> +	if (val)
> +		gpio_direction_input(adap->bus_recovery_info->scl_gpio);
> +	else
> +		gpio_direction_output(adap->bus_recovery_info->scl_gpio, 0);
> +}
> +
> +static int get_sda_gpio_value(struct i2c_adapter *adap)
> +{
> +	return gpio_get_value(adap->bus_recovery_info->sda_gpio);
> +}
> +
> +static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
> +{
> +	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
> +	struct device_d *dev = &adap->dev;
> +	int ret = 0;
> +
> +	ret = gpio_request_one(bri->scl_gpio, GPIOF_IN, "i2c-scl");
> +	if (ret) {
> +		dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio);
> +		return ret;
> +	}
> +
> +	if (bri->get_sda) {
> +		if (gpio_request_one(bri->sda_gpio, GPIOF_IN, "i2c-sda")) {
> +			/* work without SDA polling */
> +			dev_warn(dev, "Can't get SDA gpio: %d. Not using SDA polling\n",
> +					bri->sda_gpio);
> +			bri->get_sda = NULL;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap)
> +{
> +	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
> +
> +	if (bri->get_sda)
> +		gpio_free(bri->sda_gpio);
> +
> +	gpio_free(bri->scl_gpio);
> +}
> +
> +/*
> + * 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
> + * is: delay in ns = (10^6 / 100) / 2
> + */
> +#define RECOVERY_NDELAY		5000
> +#define RECOVERY_CLK_CNT	9
> +
> +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;
> +
> +	if (bri->prepare_recovery)
> +		bri->prepare_recovery(adap);
> +
> +	/*
> +	 * By this time SCL is high, as we need to give 9 falling-rising edges
> +	 */
> +	while (i++ < RECOVERY_CLK_CNT * 2) {
> +		if (val) {
> +			/* Break if SDA is high */
> +			if (bri->get_sda && bri->get_sda(adap))
> +					break;
> +			/* SCL shouldn't be low here */
> +			if (!bri->get_scl(adap)) {
> +				dev_err(&adap->dev,
> +					"SCL is stuck low, exit recovery\n");
> +				ret = -EBUSY;
> +				break;
> +			}
> +		}
> +
> +		val = !val;
> +		bri->set_scl(adap, val);
> +		ndelay(RECOVERY_NDELAY);
> +	}
> +
> +	if (bri->unprepare_recovery)
> +		bri->unprepare_recovery(adap);
> +
> +	return ret;
> +}
> +
> +int i2c_generic_scl_recovery(struct i2c_adapter *adap)
> +{
> +	adap->bus_recovery_info->set_scl(adap, 1);
> +	return i2c_generic_recovery(adap);
> +}
> +
> +int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
> +{
> +	int ret;
> +
> +	ret = i2c_get_gpios_for_recovery(adap);
> +	if (ret)
> +		return ret;
> +
> +	ret = i2c_generic_recovery(adap);
> +	i2c_put_gpios_for_recovery(adap);
> +
> +	return ret;
> +}
> +
> +int i2c_recover_bus(struct i2c_adapter *adap)
> +{
> +	if (!adap->bus_recovery_info)
> +		return -EOPNOTSUPP;
> +
> +	dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
> +	return adap->bus_recovery_info->recover_bus(adap);
> +}
> +
>  /**
>   * i2c_new_device - instantiate one new I2C device
>   *
> @@ -456,6 +584,39 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
>  
>  	list_add_tail(&adapter->list, &adapter_list);
>  
> +	/* bus recovery specific initialization */
> +	if (adapter->bus_recovery_info) {
> +		struct i2c_bus_recovery_info *bri = adapter->bus_recovery_info;
> +
> +		if (!bri->recover_bus) {
> +			dev_err(&adapter->dev, "No recover_bus() found, not using recovery\n");
> +			adapter->bus_recovery_info = NULL;
> +			goto exit_recovery;
> +		}
> +
> +		/* Generic GPIO recovery */
> +		if (bri->recover_bus == i2c_generic_gpio_recovery) {

This comparison to i2c_generic_gpio_recovery here forces the linker to
always compile in gpio recovery support, even when it's unused in the
compiled in drivers. I think we can do better here.

Also, could you put the bus recovery support into a separate function so
that we can call it wherever we need it? We might call it from other
places later aswell.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-07-13  6:47 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-08 13:29 i2c bus recovery support Jan Luebbe
2015-07-08 13:29 ` [PATCH 1/4] i2c: add bus recovery infrastructure Jan Luebbe
2015-07-13  6:47   ` Sascha Hauer
2015-07-08 13:29 ` [PATCH 2/4] i2c: only use set_scl for recovery after calling prepare_recovery Jan Luebbe
2015-07-08 13:29 ` [PATCH 3/4] i2c-omap: clear ARDY twice Jan Luebbe
2015-07-08 13:29 ` [PATCH 4/4] i2c-omap: add bus recovery support Jan Luebbe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox