[U-Boot] [PATCH 1/2] DM: I2C: Introduce 'u-boot, i2c-transaction-bytes' property

The 'u-boot,i2c-transaction-bytes' device tree property provides information regarding number of bytes transferred by a device in a single transaction.
This change is necessary to avoid hanging devices after soft reset. One notable example is communication with MC34708 device:
1. Reset when communicating with MC34708 via I2C.
2. The u-boot (after reboot -f) tries to setup the I2C and then calls force_idle_bus. In the same time MC34708 still has some data to be sent (as it transfers data in 24 bits chunks).
3. The force_idle_bus() is not able to make the bus idle as 8 SCL clocks may be not enough to have the full transmission.
4. We end up with I2C inconsistency with MC34708.
This PMIC device requires 24+ SCL cycles to make finish any pending I2C transmission.
Signed-off-by: Lukasz Majewski lukma@denx.de
---
doc/device-tree-bindings/i2c/i2c.txt | 5 +++++ drivers/i2c/i2c-uclass.c | 24 ++++++++++++++++++++++++ drivers/i2c/mxc_i2c.c | 19 +++++++++++++++++-- include/i2c.h | 2 ++ 4 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/doc/device-tree-bindings/i2c/i2c.txt b/doc/device-tree-bindings/i2c/i2c.txt index de818d4713..9698e4899b 100644 --- a/doc/device-tree-bindings/i2c/i2c.txt +++ b/doc/device-tree-bindings/i2c/i2c.txt @@ -12,6 +12,10 @@ property which allows the chip offset length to be selected. Optional properties: - u-boot,i2c-offset-len - length of chip offset in bytes. If omitted the default value of 1 is used. +- u-boot,i2c-transaction-bytes - the length of single I2C transaction on + the bus. Some devices require more than single byte transmission + (e.g. mc34708 mfd). This information is necessary to correctly + initialize (put into idle state) I2C bus after soft reset. - gpios = <sda ...>, <scl ...>; pinctrl-names = "default", "gpio"; pinctrl-0 = <&i2c_xfer>; @@ -28,6 +32,7 @@ i2c4: i2c@12ca0000 { compatible = "google,cros-ec"; i2c-max-frequency = <100000>; u-boot,i2c-offset-len = <0>; + u-boot,i2c-transaction-bytes = <3>; ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; }; }; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 49e23a0a4b..e47abf1833 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -593,6 +593,29 @@ int i2c_chip_ofdata_to_platdata(struct udevice *dev, struct dm_i2c_chip *chip) } #endif
+static int i2c_pre_probe(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev); + unsigned int max = 0; + ofnode node; + int ret; + + i2c->max_transaction_bytes = 0; + dev_for_each_subnode(node, dev) { + ret = ofnode_read_u32(node, + "u-boot,i2c-transaction-bytes", + &max); + if (!ret && max > i2c->max_transaction_bytes) + i2c->max_transaction_bytes = max; + } + + debug("%s: I2C bus: %s max transaction bytes: %d\n", __func__, + dev->name, i2c->max_transaction_bytes); +#endif + return 0; +} + static int i2c_post_probe(struct udevice *dev) { #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) @@ -674,6 +697,7 @@ UCLASS_DRIVER(i2c) = { .post_bind = i2c_post_bind, .init = i2c_uclass_init, .priv_auto_alloc_size = sizeof(struct i2c_priv), + .pre_probe = i2c_pre_probe, .post_probe = i2c_post_probe, .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), .per_child_platdata_auto_alloc_size = sizeof(struct dm_i2c_chip), diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 9999d9fe5e..5420afbc8e 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -354,9 +354,10 @@ int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) { struct udevice *bus = i2c_bus->bus; + struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); struct gpio_desc *scl_gpio = &i2c_bus->scl_gpio; struct gpio_desc *sda_gpio = &i2c_bus->sda_gpio; - int sda, scl; + int sda, scl, idle_sclks; int i, ret = 0; ulong elapsed, start_time;
@@ -380,8 +381,22 @@ int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) if ((sda & scl) == 1) goto exit; /* Bus is idle already */
+ /* + * In most cases it is just enough to generate 8 + 1 SCLK + * clocks to recover I2C slave device from 'stuck' state + * (when for example SW reset was performed, in the middle of + * I2C transmission). + * + * However, there are devices which send data in packets of + * N bytes (N > 1). In such case we do need N * 8 + 1 SCLK + * clocks. + */ + idle_sclks = 8 + 1; + + if (i2c->max_transaction_bytes > 0) + idle_sclks = i2c->max_transaction_bytes * 8 + 1; /* Send high and low on the SCL line */ - for (i = 0; i < 9; i++) { + for (i = 0; i < idle_sclks; i++) { dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_OUT); dm_gpio_set_value(scl_gpio, 0); udelay(50); diff --git a/include/i2c.h b/include/i2c.h index ccffc19552..a5c760c711 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -68,9 +68,11 @@ struct dm_i2c_chip { * I2C bus udevice. * * @speed_hz: Bus speed in hertz (typically 100000) + * @max_transaction_bytes: Maximal size of single I2C transfer */ struct dm_i2c_bus { int speed_hz; + int max_transaction_bytes; };
/*

This file adds the "u-boot,i2c-transaction-bytes" to mc34708 PMIC.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
arch/arm/dts/imx53-kp-u-boot.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 arch/arm/dts/imx53-kp-u-boot.dtsi
diff --git a/arch/arm/dts/imx53-kp-u-boot.dtsi b/arch/arm/dts/imx53-kp-u-boot.dtsi new file mode 100644 index 0000000000..acab9b3657 --- /dev/null +++ b/arch/arm/dts/imx53-kp-u-boot.dtsi @@ -0,0 +1,10 @@ +/* + * Copyright 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ or X11 + */ + +&pmic { + u-boot,i2c-transaction-bytes = <3>; +};

Hello Lukasz,
Am 04.04.2019 um 12:35 schrieb Lukasz Majewski:
This file adds the "u-boot,i2c-transaction-bytes" to mc34708 PMIC.
Signed-off-by: Lukasz Majewski lukma@denx.de
arch/arm/dts/imx53-kp-u-boot.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 arch/arm/dts/imx53-kp-u-boot.dtsi
Reviewed-by: Heiko Schocherhs@denx.de
bye, Heiko

Hello Lukasz,
Am 04.04.2019 um 12:35 schrieb Lukasz Majewski:
This file adds the "u-boot,i2c-transaction-bytes" to mc34708 PMIC.
Signed-off-by: Lukasz Majewski lukma@denx.de
arch/arm/dts/imx53-kp-u-boot.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 arch/arm/dts/imx53-kp-u-boot.dtsi
Applied to u-boot-i2c.git master
Thanks!
bye, Heiko

Hello Lukasz,
Am 04.04.2019 um 12:35 schrieb Lukasz Majewski:
The 'u-boot,i2c-transaction-bytes' device tree property provides information regarding number of bytes transferred by a device in a single transaction.
This change is necessary to avoid hanging devices after soft reset. One notable example is communication with MC34708 device:
Reset when communicating with MC34708 via I2C.
The u-boot (after reboot -f) tries to setup the I2C and then calls
force_idle_bus. In the same time MC34708 still has some data to be sent (as it transfers data in 24 bits chunks).
- The force_idle_bus() is not able to make the bus idle as 8 SCL
clocks may be not enough to have the full transmission.
- We end up with I2C inconsistency with MC34708.
This PMIC device requires 24+ SCL cycles to make finish any pending I2C transmission.
Signed-off-by: Lukasz Majewski lukma@denx.de
doc/device-tree-bindings/i2c/i2c.txt | 5 +++++ drivers/i2c/i2c-uclass.c | 24 ++++++++++++++++++++++++ drivers/i2c/mxc_i2c.c | 19 +++++++++++++++++-- include/i2c.h | 2 ++ 4 files changed, 48 insertions(+), 2 deletions(-)
Reviewed-by: Heiko Schocherhs@denx.de
bye, Heiko

Hello Lukasz,
Am 04.04.2019 um 12:35 schrieb Lukasz Majewski:
The 'u-boot,i2c-transaction-bytes' device tree property provides information regarding number of bytes transferred by a device in a single transaction.
This change is necessary to avoid hanging devices after soft reset. One notable example is communication with MC34708 device:
Reset when communicating with MC34708 via I2C.
The u-boot (after reboot -f) tries to setup the I2C and then calls
force_idle_bus. In the same time MC34708 still has some data to be sent (as it transfers data in 24 bits chunks).
- The force_idle_bus() is not able to make the bus idle as 8 SCL
clocks may be not enough to have the full transmission.
- We end up with I2C inconsistency with MC34708.
This PMIC device requires 24+ SCL cycles to make finish any pending I2C transmission.
Signed-off-by: Lukasz Majewski lukma@denx.de
doc/device-tree-bindings/i2c/i2c.txt | 5 +++++ drivers/i2c/i2c-uclass.c | 24 ++++++++++++++++++++++++ drivers/i2c/mxc_i2c.c | 19 +++++++++++++++++-- include/i2c.h | 2 ++ 4 files changed, 48 insertions(+), 2 deletions(-)
Applied to u-boot-i2c.git master
Thanks!
bye, Heiko
participants (2)
-
Heiko Schocher
-
Lukasz Majewski