]> git.sur5r.net Git - u-boot/blobdiff - drivers/i2c/i2c-uclass.c
SPDX: Convert all of our single license tags to Linux Kernel style
[u-boot] / drivers / i2c / i2c-uclass.c
index eafa457845df2371bff8a59ce8b20b7d2d32dd82..5e58dd0916cbe423c9fa2c6dad35176ca666c00e 100644 (file)
@@ -1,23 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (c) 2014 Google, Inc
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
-#include <fdtdec.h>
 #include <i2c.h>
 #include <malloc.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
-#include <dm/root.h>
-
-DECLARE_GLOBAL_DATA_PTR;
+#include <dm/pinctrl.h>
+#ifdef CONFIG_DM_GPIO
+#include <asm/gpio.h>
+#endif
 
 #define I2C_MAX_OFFSET_LEN     4
 
+enum {
+       PIN_SDA = 0,
+       PIN_SCL,
+       PIN_COUNT,
+};
+
+/* Useful debugging function */
+void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs)
+{
+       int i;
+
+       for (i = 0; i < nmsgs; i++) {
+               struct i2c_msg *m = &msg[i];
+
+               printf("   %s %x len=%x", m->flags & I2C_M_RD ? "R" : "W",
+                      msg->addr, msg->len);
+               if (!(m->flags & I2C_M_RD))
+                       printf(": %x", m->buf[0]);
+               printf("\n");
+       }
+}
+
 /**
  * i2c_setup_offset() - Set up a new message with a chip offset
  *
@@ -186,6 +207,36 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,
        }
 }
 
+int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct dm_i2c_ops *ops = i2c_get_ops(bus);
+
+       if (!ops->xfer)
+               return -ENOSYS;
+
+       return ops->xfer(bus, msg, nmsgs);
+}
+
+int dm_i2c_reg_read(struct udevice *dev, uint offset)
+{
+       uint8_t val;
+       int ret;
+
+       ret = dm_i2c_read(dev, offset, &val, 1);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value)
+{
+       uint8_t val = value;
+
+       return dm_i2c_write(dev, offset, &val, 1);
+}
+
 /**
  * i2c_probe_chip() - probe for a chip on a bus
  *
@@ -230,6 +281,8 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len,
 
        snprintf(name, sizeof(name), "generic_%x", chip_addr);
        str = strdup(name);
+       if (!str)
+               return -ENOMEM;
        ret = device_bind_driver(bus, "i2c_generic_chip_drv", str, &dev);
        debug("%s:  device_bind_driver: ret=%d\n", __func__, ret);
        if (ret)
@@ -325,10 +378,10 @@ int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags,
        return ret;
 }
 
-int i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+int dm_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 {
        struct dm_i2c_ops *ops = i2c_get_ops(bus);
-       struct dm_i2c_bus *i2c = bus->uclass_priv;
+       struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
        int ret;
 
        /*
@@ -346,15 +399,10 @@ int i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
        return 0;
 }
 
-/*
- * i2c_get_bus_speed:
- *
- *  Returns speed of selected I2C bus in Hz
- */
-int i2c_get_bus_speed(struct udevice *bus)
+int dm_i2c_get_bus_speed(struct udevice *bus)
 {
        struct dm_i2c_ops *ops = i2c_get_ops(bus);
-       struct dm_i2c_bus *i2c = bus->uclass_priv;
+       struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
 
        if (!ops->get_bus_speed)
                return i2c->speed_hz;
@@ -399,71 +447,174 @@ int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len)
        return 0;
 }
 
+int i2c_get_chip_offset_len(struct udevice *dev)
+{
+       struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+
+       return chip->offset_len;
+}
+
+#ifdef CONFIG_DM_GPIO
+static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit)
+{
+       if (bit)
+               dm_gpio_set_dir_flags(pin, GPIOD_IS_IN);
+       else
+               dm_gpio_set_dir_flags(pin, GPIOD_IS_OUT |
+                                          GPIOD_ACTIVE_LOW |
+                                          GPIOD_IS_OUT_ACTIVE);
+}
+
+static int i2c_gpio_get_pin(struct gpio_desc *pin)
+{
+       return dm_gpio_get_value(pin);
+}
+
+static int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin,
+                                struct gpio_desc *scl_pin)
+{
+       int counter = 9;
+       int ret = 0;
+
+       i2c_gpio_set_pin(sda_pin, 1);
+       i2c_gpio_set_pin(scl_pin, 1);
+       udelay(5);
+
+       /*  Toggle SCL until slave release SDA */
+       while (counter-- >= 0) {
+               i2c_gpio_set_pin(scl_pin, 1);
+               udelay(5);
+               i2c_gpio_set_pin(scl_pin, 0);
+               udelay(5);
+               if (i2c_gpio_get_pin(sda_pin))
+                       break;
+       }
+
+       /* Then, send I2C stop */
+       i2c_gpio_set_pin(sda_pin, 0);
+       udelay(5);
+
+       i2c_gpio_set_pin(scl_pin, 1);
+       udelay(5);
+
+       i2c_gpio_set_pin(sda_pin, 1);
+       udelay(5);
+
+       if (!i2c_gpio_get_pin(sda_pin) || !i2c_gpio_get_pin(scl_pin))
+               ret = -EREMOTEIO;
+
+       return ret;
+}
+
+static int i2c_deblock_gpio(struct udevice *bus)
+{
+       struct gpio_desc gpios[PIN_COUNT];
+       int ret, ret0;
+
+       ret = gpio_request_list_by_name(bus, "gpios", gpios,
+                                       ARRAY_SIZE(gpios), GPIOD_IS_IN);
+       if (ret != ARRAY_SIZE(gpios)) {
+               debug("%s: I2C Node '%s' has no 'gpios' property %s\n",
+                     __func__, dev_read_name(bus), bus->name);
+               if (ret >= 0) {
+                       gpio_free_list(bus, gpios, ret);
+                       ret = -ENOENT;
+               }
+               goto out;
+       }
+
+       ret = pinctrl_select_state(bus, "gpio");
+       if (ret) {
+               debug("%s: I2C Node '%s' has no 'gpio' pinctrl state. %s\n",
+                     __func__, dev_read_name(bus), bus->name);
+               goto out_no_pinctrl;
+       }
+
+       ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL]);
+
+       ret = pinctrl_select_state(bus, "default");
+       if (ret) {
+               debug("%s: I2C Node '%s' has no 'default' pinctrl state. %s\n",
+                     __func__, dev_read_name(bus), bus->name);
+       }
+
+       ret = !ret ? ret0 : ret;
+
+out_no_pinctrl:
+       gpio_free_list(bus, gpios, ARRAY_SIZE(gpios));
+out:
+       return ret;
+}
+#else
+static int i2c_deblock_gpio(struct udevice *bus)
+{
+       return -ENOSYS;
+}
+#endif // CONFIG_DM_GPIO
+
 int i2c_deblock(struct udevice *bus)
 {
        struct dm_i2c_ops *ops = i2c_get_ops(bus);
 
-       /*
-        * We could implement a software deblocking here if we could get
-        * access to the GPIOs used by I2C, and switch them to GPIO mode
-        * and then back to I2C. This is somewhat beyond our powers in
-        * driver model at present, so for now just fail.
-        *
-        * See https://patchwork.ozlabs.org/patch/399040/
-        */
        if (!ops->deblock)
-               return -ENOSYS;
+               return i2c_deblock_gpio(bus);
 
        return ops->deblock(bus);
 }
 
-int i2c_chip_ofdata_to_platdata(const void *blob, int node,
-                               struct dm_i2c_chip *chip)
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+int i2c_chip_ofdata_to_platdata(struct udevice *dev, struct dm_i2c_chip *chip)
 {
-       chip->offset_len = fdtdec_get_int(gd->fdt_blob, node,
-                                         "u-boot,i2c-offset-len", 1);
+       int addr;
+
+       chip->offset_len = dev_read_u32_default(dev, "u-boot,i2c-offset-len",
+                                               1);
        chip->flags = 0;
-       chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1);
-       if (chip->chip_addr == -1) {
-               debug("%s: I2C Node '%s' has no 'reg' property\n", __func__,
-                     fdt_get_name(blob, node, NULL));
+       addr = dev_read_u32_default(dev, "reg", -1);
+       if (addr == -1) {
+               debug("%s: I2C Node '%s' has no 'reg' property %s\n", __func__,
+                     dev_read_name(dev), dev->name);
                return -EINVAL;
        }
+       chip->chip_addr = addr;
 
        return 0;
 }
+#endif
 
 static int i2c_post_probe(struct udevice *dev)
 {
-       struct dm_i2c_bus *i2c = dev->uclass_priv;
-
-       i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
-                                    "clock-frequency", 100000);
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+       struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev);
 
-       return i2c_set_bus_speed(dev, i2c->speed_hz);
-}
+       i2c->speed_hz = dev_read_u32_default(dev, "clock-frequency", 100000);
 
-static int i2c_post_bind(struct udevice *dev)
-{
-       /* Scan the bus for devices */
-       return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+       return dm_i2c_set_bus_speed(dev, i2c->speed_hz);
+#else
+       return 0;
+#endif
 }
 
 static int i2c_child_post_bind(struct udevice *dev)
 {
+#if CONFIG_IS_ENABLED(OF_CONTROL)
        struct dm_i2c_chip *plat = dev_get_parent_platdata(dev);
 
-       if (dev->of_offset == -1)
+       if (!dev_of_valid(dev))
                return 0;
-
-       return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat);
+       return i2c_chip_ofdata_to_platdata(dev, plat);
+#else
+       return 0;
+#endif
 }
 
 UCLASS_DRIVER(i2c) = {
        .id             = UCLASS_I2C,
        .name           = "i2c",
        .flags          = DM_UC_FLAG_SEQ_ALIAS,
-       .post_bind      = i2c_post_bind,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+       .post_bind      = dm_scan_fdt_dev,
+#endif
        .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),