[U-Boot] [PATCH] dm: i2c: implement gpio-based I2C deblock

Alexander Kochetkov al.kochet at gmail.com
Fri Mar 2 13:37:04 UTC 2018


The commit extract gpio description from device tree,
setup pins and toggle them until I2C slave device
release SDA.

Any comments? Ideas?

Could someone review the patch and tell that should
I do with it in order to bring the patch to u-boot?

Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>
---
 drivers/i2c/i2c-uclass.c |   95 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 93 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index 920811a..a451e41 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -11,6 +11,8 @@
 #include <malloc.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <asm/gpio.h>
 
 #define I2C_MAX_OFFSET_LEN	4
 
@@ -445,9 +447,96 @@ int i2c_get_chip_offset_len(struct udevice *dev)
 	return chip->offset_len;
 }
 
+static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit)
+{
+	if (bit)
+		dm_gpio_set_dir_flags(pin, GPIOD_IS_IN);
+	else
+		dm_gpio_set_dir_flags(pin, GPIOD_IS_OUT |
+					   GPIOD_ACTIVE_LOW |
+					   GPIOD_IS_OUT_ACTIVE);
+}
+
+static int i2c_gpio_get_pin(struct gpio_desc *pin)
+{
+	return dm_gpio_get_value(pin);
+}
+
+static void i2c_deblock_gpio_run(struct gpio_desc *sda_pin, struct gpio_desc *scl_pin)
+{
+	int counter = 16;
+
+	i2c_gpio_set_pin(sda_pin, 1);
+	i2c_gpio_set_pin(scl_pin, 1);
+	udelay(5);
+
+	while (counter-- >= 0) {
+		i2c_gpio_set_pin(scl_pin, 1);
+		udelay(5);
+		i2c_gpio_set_pin(scl_pin, 0);
+		udelay(5);
+		if (i2c_gpio_get_pin(sda_pin))
+			break;
+	}
+
+	i2c_gpio_set_pin(sda_pin, 0);
+	udelay(5);
+
+	i2c_gpio_set_pin(scl_pin, 1);
+	udelay(5);
+
+	i2c_gpio_set_pin(sda_pin, 1);
+	udelay(5);
+}
+
+enum {
+	PIN_SDA = 0,
+	PIN_SCL,
+	PIN_COUNT,
+};
+
+static int i2c_deblock_gpio(struct udevice *bus)
+{
+	struct gpio_desc gpios[PIN_COUNT];
+	int ret;
+
+	ret = gpio_request_list_by_name(bus, "gpios", gpios,
+					ARRAY_SIZE(gpios), GPIOD_IS_IN);
+	if (ret != ARRAY_SIZE(gpios)) {
+		debug("%s: I2C Node '%s' has no 'gpios' property %s\n", __func__,
+		      dev_read_name(bus), bus->name);
+		if (ret >= 0) {
+			gpio_free_list(bus, gpios, ret);
+			ret = -ENOENT;
+		}
+		goto out;
+	}
+
+	ret = pinctrl_select_state(bus, "gpio");
+	if (ret) {
+		debug("%s: I2C Node '%s' has no 'gpio' pinctrl state. %s\n", __func__,
+		      dev_read_name(bus), bus->name);
+		goto out_no_pinctrl;
+	}
+
+	i2c_deblock_gpio_run(&gpios[PIN_SDA], &gpios[PIN_SCL]);
+
+	ret = pinctrl_select_state(bus, "default");
+	if (ret) {
+		debug("%s: I2C Node '%s' has no 'default' pinctrl state. %s\n", __func__,
+		      dev_read_name(bus), bus->name);
+	}
+
+out_no_pinctrl:
+	gpio_free_list(bus, gpios, ARRAY_SIZE(gpios));
+out:
+	return ret;
+}
+
 int i2c_deblock(struct udevice *bus)
 {
 	struct dm_i2c_ops *ops = i2c_get_ops(bus);
+	int ret;
 
 	/*
 	 * We could implement a software deblocking here if we could get
@@ -457,8 +546,10 @@ int i2c_deblock(struct udevice *bus)
 	 *
 	 * See https://patchwork.ozlabs.org/patch/399040/
 	 */
-	if (!ops->deblock)
-		return -ENOSYS;
+	if (!ops->deblock) {
+		ret = i2c_deblock_gpio(bus);
+		return ret ? -ENOSYS : 0;
+	}
 
 	return ops->deblock(bus);
 }
-- 
1.7.9.5



More information about the U-Boot mailing list