]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
DM: I2C: Introduce 'u-boot, i2c-transaction-bytes' property
authorLukasz Majewski <lukma@denx.de>
Thu, 4 Apr 2019 10:35:34 +0000 (12:35 +0200)
committerHeiko Schocher <hs@denx.de>
Thu, 11 Apr 2019 13:21:33 +0000 (15:21 +0200)
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
drivers/i2c/i2c-uclass.c
drivers/i2c/mxc_i2c.c
include/i2c.h

index de818d4713f0c6f77f6a63ed0eb315ec5e66e746..9698e4899b54c8135a863e78ef1cdca74a2bb9b5 100644 (file)
@@ -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>;
        };
 };
index 49e23a0a4bf20d1662abfecf30cfe90c843bed59..e47abf18333f4452b31c8662e4160358ab4b575f 100644 (file)
@@ -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),
index 9999d9fe5e4bd01fd5155a307b91017f0328eabe..5420afbc8e0de91b7f7529a94217ca1c2643dd52 100644 (file)
@@ -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);
index ccffc195527547c7d46ae8809b373212eb75f181..a5c760c711edcca9e278e478cd0c4babbef80b80 100644 (file)
@@ -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;
 };
 
 /*