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

Lukasz Majewski lukma at denx.de
Thu Apr 4 10:35:34 UTC 2019


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 at 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 at 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;
 };
 
 /*
-- 
2.11.0



More information about the U-Boot mailing list