diff mbox series

i2c: gpio: Add custom deblock sequence

Message ID 20200124181214.122950-1-marex@denx.de
State New
Headers show
Series i2c: gpio: Add custom deblock sequence | expand

Commit Message

Marek Vasut Jan. 24, 2020, 6:12 p.m. UTC
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 at denx.de>
---
 drivers/i2c/i2c-gpio.c | 67 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

Comments

Heiko Schocher Jan. 27, 2020, 6:40 a.m. UTC | #1
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 at 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#L586


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#L504

too?

bye,
Heiko
Marek Vasut Jan. 27, 2020, 2:11 p.m. UTC | #2
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 at 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#L586
> 
> 
> 
> 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#L504

I need to take a look at this one.
diff mbox series

Patch

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,