+// 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>
-
-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)
{
debug("Cannot find I2C bus %d\n", busnum);
return ret;
}
+
+ /* detect the presence of the chip on the bus */
+ ret = i2c_probe_chip(bus, chip_addr, 0);
+ debug("%s: bus='%s', address %02x, ret=%d\n", __func__, bus->name,
+ chip_addr, ret);
+ if (ret) {
+ debug("Cannot detect I2C chip %02x on bus %d\n", chip_addr,
+ busnum);
+ return ret;
+ }
+
ret = i2c_get_chip(bus, chip_addr, offset_len, devp);
if (ret) {
debug("Cannot find I2C chip %02x on bus %d\n", chip_addr,
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);
}
-#if CONFIG_IS_ENABLED(OF_CONTROL)
-int i2c_chip_ofdata_to_platdata(const void *blob, int node,
- struct dm_i2c_chip *chip)
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+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_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)
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev);
- i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
- "clock-frequency", 100000);
+ i2c->speed_hz = dev_read_u32_default(dev, "clock-frequency", 100000);
return dm_i2c_set_bus_speed(dev, i2c->speed_hz);
#else
#endif
}
-static int i2c_post_bind(struct udevice *dev)
+static int i2c_child_post_bind(struct udevice *dev)
{
-#if CONFIG_IS_ENABLED(OF_CONTROL)
- /* Scan the bus for devices */
- return dm_scan_fdt_dev(dev);
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct dm_i2c_chip *plat = dev_get_parent_platdata(dev);
+
+ if (!dev_of_valid(dev))
+ return 0;
+ return i2c_chip_ofdata_to_platdata(dev, plat);
#else
return 0;
#endif
}
-static int i2c_child_post_bind(struct udevice *dev)
+struct i2c_priv {
+ int max_id;
+};
+
+static int i2c_post_bind(struct udevice *dev)
{
-#if CONFIG_IS_ENABLED(OF_CONTROL)
- struct dm_i2c_chip *plat = dev_get_parent_platdata(dev);
+ struct uclass *class = dev->uclass;
+ struct i2c_priv *priv = class->priv;
+ int ret = 0;
- if (dev->of_offset == -1)
- return 0;
+ /* Just for sure */
+ if (!priv)
+ return -ENOMEM;
+
+ debug("%s: %s, req_seq=%d\n", __func__, dev->name, dev->req_seq);
+
+ /* if there is no alias ID, use the first free */
+ if (dev->req_seq == -1)
+ dev->req_seq = ++priv->max_id;
- return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat);
+ debug("%s: %s, new req_seq=%d\n", __func__, dev->name, dev->req_seq);
+
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+ ret = dm_scan_fdt_dev(dev);
+#endif
+ return ret;
+}
+
+int i2c_uclass_init(struct uclass *class)
+{
+ struct i2c_priv *priv = class->priv;
+
+ /* Just for sure */
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get the last allocated alias. */
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ priv->max_id = dev_read_alias_highest_id("i2c");
#else
- return 0;
+ priv->max_id = -1;
#endif
+
+ debug("%s: highest alias id is %d\n", __func__, priv->max_id);
+
+ return 0;
}
UCLASS_DRIVER(i2c) = {
.name = "i2c",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.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),