X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fi2c%2Fi2c-uclass.c;h=5e58dd0916cbe423c9fa2c6dad35176ca666c00e;hb=2239690aca9e1dc9ddb89289b6f4b7060a86b3fa;hp=50b99ead3d9fb4179940d2c6566a698d954028f5;hpb=7c0e5d865ff0b86dfce492b656238919c659d756;p=u-boot diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 50b99ead3d..5e58dd0916 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -1,23 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2014 Google, Inc - * - * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include -#include #include #include #include #include -#include - -DECLARE_GLOBAL_DATA_PTR; +#include +#ifdef CONFIG_DM_GPIO +#include +#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) { @@ -449,71 +454,167 @@ int i2c_get_chip_offset_len(struct udevice *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) { +#if CONFIG_IS_ENABLED(OF_CONTROL) 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); -} - -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); +#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),