From patchwork Fri Jan 24 18:12:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Vasut X-Patchwork-Id: 240074 List-Id: U-Boot discussion From: marex at denx.de (Marek Vasut) Date: Fri, 24 Jan 2020 19:12:14 +0100 Subject: [PATCH] i2c: gpio: Add custom deblock sequence Message-ID: <20200124181214.122950-1-marex@denx.de> Add custom deblock dequence for the I2C bus, needed on some devices. This sequence is issued once, when probing the driver, and is controlled by DT property, "i2c-gpio,deblock". Signed-off-by: Marek Vasut --- drivers/i2c/i2c-gpio.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index 4e8fa21473..46975ef5c8 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -39,6 +39,11 @@ static int i2c_gpio_sda_get(struct gpio_desc *sda) return dm_gpio_get_value(sda); } +static int i2c_gpio_scl_get(struct gpio_desc *scl) +{ + return dm_gpio_get_value(scl); +} + static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit) { if (bit) @@ -305,6 +310,67 @@ static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz) return 0; } +/* + * I2C is a synchronous protocol and resets of the processor in the middle + * of an access can block the I2C Bus until a powerdown of the full unit is + * done. This function toggles the SCL until the SCL and SCA line are + * released, but max. 16 times, after this a I2C start-sequence is sent. + * This I2C Deblocking mechanism was developed by Keymile in association + * with Anatech and Atmel in 1998. + */ +static void i2c_gpio_make_abort(struct udevice *dev) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + int scl_state = 0; + int sda_state = 0; + int i = 0; + int ret = 0; + +#define DELAY_ABORT_SEQ 62 /* @200kHz 9 clocks = 44us, 62us is ok */ + + i2c_gpio_scl_set(scl, 1); + + if (!i2c_gpio_sda_get(sda)) { + ret = -1; + for (i = 0; i < 16; i++) { + i2c_gpio_scl_set(scl, 0); + udelay(DELAY_ABORT_SEQ); + i2c_gpio_scl_set(scl, 1); + udelay(DELAY_ABORT_SEQ); + scl_state = i2c_gpio_scl_get(scl); + sda_state = i2c_gpio_sda_get(sda); + if (scl_state && sda_state) { + ret = 0; + break; + } + } + } + + if (!ret) { + for (i = 0; i < 5; i++) { + i2c_gpio_send_start(scl, sda, 2 * bus->udelay); + i2c_gpio_scl_set(scl, 0); + } + } + + /* respect stop setup time */ + udelay(DELAY_ABORT_SEQ); + i2c_gpio_scl_set(scl, 1); + udelay(DELAY_ABORT_SEQ); + i2c_gpio_sda_set(sda, 1); + i2c_gpio_sda_get(sda); +} + +static int i2c_gpio_drv_probe(struct udevice *dev) +{ + if (dev_read_bool(dev, "i2c-gpio,deblock")) + i2c_gpio_make_abort(dev); + + return 0; +} + static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) { struct i2c_gpio_bus *bus = dev_get_priv(dev); @@ -341,6 +407,7 @@ U_BOOT_DRIVER(i2c_gpio) = { .name = "i2c-gpio", .id = UCLASS_I2C, .of_match = i2c_gpio_ids, + .probe = i2c_gpio_drv_probe, .ofdata_to_platdata = i2c_gpio_ofdata_to_platdata, .priv_auto_alloc_size = sizeof(struct i2c_gpio_bus), .ops = &i2c_gpio_ops,