[PATCH] i2c: gpio: Add custom deblock sequence

Add custom deblock dequence for the I2C bus, needed on some devices. This sequence is issued once, when probing the driver, and is controlled by DT property, "i2c-gpio,deblock".
Signed-off-by: Marek Vasut marex@denx.de --- drivers/i2c/i2c-gpio.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index 4e8fa21473..46975ef5c8 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -39,6 +39,11 @@ static int i2c_gpio_sda_get(struct gpio_desc *sda) return dm_gpio_get_value(sda); }
+static int i2c_gpio_scl_get(struct gpio_desc *scl) +{ + return dm_gpio_get_value(scl); +} + static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit) { if (bit) @@ -305,6 +310,67 @@ static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz) return 0; }
+/* + * I2C is a synchronous protocol and resets of the processor in the middle + * of an access can block the I2C Bus until a powerdown of the full unit is + * done. This function toggles the SCL until the SCL and SCA line are + * released, but max. 16 times, after this a I2C start-sequence is sent. + * This I2C Deblocking mechanism was developed by Keymile in association + * with Anatech and Atmel in 1998. + */ +static void i2c_gpio_make_abort(struct udevice *dev) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + int scl_state = 0; + int sda_state = 0; + int i = 0; + int ret = 0; + +#define DELAY_ABORT_SEQ 62 /* @200kHz 9 clocks = 44us, 62us is ok */ + + i2c_gpio_scl_set(scl, 1); + + if (!i2c_gpio_sda_get(sda)) { + ret = -1; + for (i = 0; i < 16; i++) { + i2c_gpio_scl_set(scl, 0); + udelay(DELAY_ABORT_SEQ); + i2c_gpio_scl_set(scl, 1); + udelay(DELAY_ABORT_SEQ); + scl_state = i2c_gpio_scl_get(scl); + sda_state = i2c_gpio_sda_get(sda); + if (scl_state && sda_state) { + ret = 0; + break; + } + } + } + + if (!ret) { + for (i = 0; i < 5; i++) { + i2c_gpio_send_start(scl, sda, 2 * bus->udelay); + i2c_gpio_scl_set(scl, 0); + } + } + + /* respect stop setup time */ + udelay(DELAY_ABORT_SEQ); + i2c_gpio_scl_set(scl, 1); + udelay(DELAY_ABORT_SEQ); + i2c_gpio_sda_set(sda, 1); + i2c_gpio_sda_get(sda); +} + +static int i2c_gpio_drv_probe(struct udevice *dev) +{ + if (dev_read_bool(dev, "i2c-gpio,deblock")) + i2c_gpio_make_abort(dev); + + return 0; +} + static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) { struct i2c_gpio_bus *bus = dev_get_priv(dev); @@ -341,6 +407,7 @@ U_BOOT_DRIVER(i2c_gpio) = { .name = "i2c-gpio", .id = UCLASS_I2C, .of_match = i2c_gpio_ids, + .probe = i2c_gpio_drv_probe, .ofdata_to_platdata = i2c_gpio_ofdata_to_platdata, .priv_auto_alloc_size = sizeof(struct i2c_gpio_bus), .ops = &i2c_gpio_ops,

Hello Marek,
Am 24.01.2020 um 19:12 schrieb Marek Vasut:
Add custom deblock dequence for the I2C bus, needed on some devices. This sequence is issued once, when probing the driver, and is controlled by DT property, "i2c-gpio,deblock".
Signed-off-by: Marek Vasut marex@denx.de
drivers/i2c/i2c-gpio.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index 4e8fa21473..46975ef5c8 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -39,6 +39,11 @@ static int i2c_gpio_sda_get(struct gpio_desc *sda) return dm_gpio_get_value(sda); }
+static int i2c_gpio_scl_get(struct gpio_desc *scl) +{
- return dm_gpio_get_value(scl);
+}
- static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit) { if (bit)
@@ -305,6 +310,67 @@ static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz) return 0; }
+/*
- I2C is a synchronous protocol and resets of the processor in the middle
- of an access can block the I2C Bus until a powerdown of the full unit is
- done. This function toggles the SCL until the SCL and SCA line are
- released, but max. 16 times, after this a I2C start-sequence is sent.
- This I2C Deblocking mechanism was developed by Keymile in association
- with Anatech and Atmel in 1998.
- */
+static void i2c_gpio_make_abort(struct udevice *dev) +{
- struct i2c_gpio_bus *bus = dev_get_priv(dev);
- struct gpio_desc *scl = &bus->gpios[PIN_SCL];
- struct gpio_desc *sda = &bus->gpios[PIN_SDA];
- int scl_state = 0;
- int sda_state = 0;
- int i = 0;
- int ret = 0;
+#define DELAY_ABORT_SEQ 62 /* @200kHz 9 clocks = 44us, 62us is ok */
- i2c_gpio_scl_set(scl, 1);
- if (!i2c_gpio_sda_get(sda)) {
ret = -1;
for (i = 0; i < 16; i++) {
i2c_gpio_scl_set(scl, 0);
udelay(DELAY_ABORT_SEQ);
i2c_gpio_scl_set(scl, 1);
udelay(DELAY_ABORT_SEQ);
scl_state = i2c_gpio_scl_get(scl);
sda_state = i2c_gpio_sda_get(sda);
if (scl_state && sda_state) {
ret = 0;
break;
}
}
- }
- if (!ret) {
for (i = 0; i < 5; i++) {
i2c_gpio_send_start(scl, sda, 2 * bus->udelay);
i2c_gpio_scl_set(scl, 0);
}
- }
- /* respect stop setup time */
- udelay(DELAY_ABORT_SEQ);
- i2c_gpio_scl_set(scl, 1);
- udelay(DELAY_ABORT_SEQ);
- i2c_gpio_sda_set(sda, 1);
- i2c_gpio_sda_get(sda);
+}
+static int i2c_gpio_drv_probe(struct udevice *dev) +{
- if (dev_read_bool(dev, "i2c-gpio,deblock"))
i2c_gpio_make_abort(dev);
- return 0;
+}
- static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) { struct i2c_gpio_bus *bus = dev_get_priv(dev);
@@ -341,6 +407,7 @@ U_BOOT_DRIVER(i2c_gpio) = { .name = "i2c-gpio", .id = UCLASS_I2C, .of_match = i2c_gpio_ids,
- .probe = i2c_gpio_drv_probe, .ofdata_to_platdata = i2c_gpio_ofdata_to_platdata, .priv_auto_alloc_size = sizeof(struct i2c_gpio_bus), .ops = &i2c_gpio_ops,
Hmm.. why do you add this into the probe function and do not add it to:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L382
which is called from: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/i2c-uclass.c#L5...
Ok, i2c_deblock() currently is only used from do_i2c_reset(), but may it is worth to adapt drivers/i2c-core/i2c-uclass.c to call the deblock sequence also from i2c_pre_probe()?
May dependend on your proposed DT property "i2c-gpio,deblock" ?
But please update also the documentation in
u-boot:/doc/device-tree-bindings/i2c/i2c-gpio.txt
or if we go the more generic way:
u-boot:/doc/device-tree-bindings/i2c/i2c.txt
Thanks!
BTW: May it makes sense to adapt: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/i2c-uclass.c#L5...
too?
bye, Heiko

On 1/27/20 7:40 AM, Heiko Schocher wrote:
Hello Marek,
Am 24.01.2020 um 19:12 schrieb Marek Vasut:
Add custom deblock dequence for the I2C bus, needed on some devices. This sequence is issued once, when probing the driver, and is controlled by DT property, "i2c-gpio,deblock".
Signed-off-by: Marek Vasut marex@denx.de
drivers/i2c/i2c-gpio.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index 4e8fa21473..46975ef5c8 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -39,6 +39,11 @@ static int i2c_gpio_sda_get(struct gpio_desc *sda) return dm_gpio_get_value(sda); } +static int i2c_gpio_scl_get(struct gpio_desc *scl) +{ + return dm_gpio_get_value(scl); +}
static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit) { if (bit) @@ -305,6 +310,67 @@ static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz) return 0; } +/*
- I2C is a synchronous protocol and resets of the processor in the
middle
- of an access can block the I2C Bus until a powerdown of the full
unit is
- done. This function toggles the SCL until the SCL and SCA line are
- released, but max. 16 times, after this a I2C start-sequence is sent.
- This I2C Deblocking mechanism was developed by Keymile in association
- with Anatech and Atmel in 1998.
- */
+static void i2c_gpio_make_abort(struct udevice *dev) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + int scl_state = 0; + int sda_state = 0; + int i = 0; + int ret = 0;
+#define DELAY_ABORT_SEQ 62 /* @200kHz 9 clocks = 44us, 62us is ok */
+ i2c_gpio_scl_set(scl, 1);
+ if (!i2c_gpio_sda_get(sda)) { + ret = -1; + for (i = 0; i < 16; i++) { + i2c_gpio_scl_set(scl, 0); + udelay(DELAY_ABORT_SEQ); + i2c_gpio_scl_set(scl, 1); + udelay(DELAY_ABORT_SEQ); + scl_state = i2c_gpio_scl_get(scl); + sda_state = i2c_gpio_sda_get(sda); + if (scl_state && sda_state) { + ret = 0; + break; + } + } + }
+ if (!ret) { + for (i = 0; i < 5; i++) { + i2c_gpio_send_start(scl, sda, 2 * bus->udelay); + i2c_gpio_scl_set(scl, 0); + } + }
+ /* respect stop setup time */ + udelay(DELAY_ABORT_SEQ); + i2c_gpio_scl_set(scl, 1); + udelay(DELAY_ABORT_SEQ); + i2c_gpio_sda_set(sda, 1); + i2c_gpio_sda_get(sda); +}
+static int i2c_gpio_drv_probe(struct udevice *dev) +{ + if (dev_read_bool(dev, "i2c-gpio,deblock")) + i2c_gpio_make_abort(dev);
+ return 0; +}
static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) { struct i2c_gpio_bus *bus = dev_get_priv(dev); @@ -341,6 +407,7 @@ U_BOOT_DRIVER(i2c_gpio) = { .name = "i2c-gpio", .id = UCLASS_I2C, .of_match = i2c_gpio_ids, + .probe = i2c_gpio_drv_probe, .ofdata_to_platdata = i2c_gpio_ofdata_to_platdata, .priv_auto_alloc_size = sizeof(struct i2c_gpio_bus), .ops = &i2c_gpio_ops,
Hmm.. why do you add this into the probe function and do not add it to:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L382
Because I wasn't aware of this callback.
which is called from: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/i2c-uclass.c#L5...
Ok, i2c_deblock() currently is only used from do_i2c_reset(), but may it is worth to adapt drivers/i2c-core/i2c-uclass.c to call the deblock sequence also from i2c_pre_probe()?
May dependend on your proposed DT property "i2c-gpio,deblock" ?
But please update also the documentation in
u-boot:/doc/device-tree-bindings/i2c/i2c-gpio.txt
or if we go the more generic way:
u-boot:/doc/device-tree-bindings/i2c/i2c.txt
Thanks!
BTW: May it makes sense to adapt: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/i2c-uclass.c#L5...
I need to take a look at this one.
participants (2)
-
Heiko Schocher
-
Marek Vasut