X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fi2c%2Fi2c-uclass.c;h=4ac6ef84f58007ca45987b4e3713dc8e501d9027;hb=aa54192d4a87460e105264a8156f1d7b1d212b0b;hp=920811a075c68454908129497db1809d4bd9ec72;hpb=df8dcac8a380147e02582c25016406cfe6bba56e;p=u-boot diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 920811a075..4ac6ef84f5 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -11,9 +11,19 @@ #include #include #include +#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) { @@ -445,20 +455,110 @@ 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); }