From a40fe217d19fdb9804fdc88342ce4bb0a0440c84 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 4 Apr 2019 12:35:34 +0200 Subject: DM: I2C: Introduce 'u-boot, i2c-transaction-bytes' property The 'u-boot,i2c-transaction-bytes' device tree property provides information regarding number of bytes transferred by a device in a single transaction. This change is necessary to avoid hanging devices after soft reset. One notable example is communication with MC34708 device: 1. Reset when communicating with MC34708 via I2C. 2. The u-boot (after reboot -f) tries to setup the I2C and then calls force_idle_bus. In the same time MC34708 still has some data to be sent (as it transfers data in 24 bits chunks). 3. The force_idle_bus() is not able to make the bus idle as 8 SCL clocks may be not enough to have the full transmission. 4. We end up with I2C inconsistency with MC34708. This PMIC device requires 24+ SCL cycles to make finish any pending I2C transmission. Signed-off-by: Lukasz Majewski --- drivers/i2c/i2c-uclass.c | 24 ++++++++++++++++++++++++ drivers/i2c/mxc_i2c.c | 19 +++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 49e23a0a4bf..e47abf18333 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -593,6 +593,29 @@ int i2c_chip_ofdata_to_platdata(struct udevice *dev, struct dm_i2c_chip *chip) } #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) && !CONFIG_IS_ENABLED(OF_PLATDATA) @@ -674,6 +697,7 @@ UCLASS_DRIVER(i2c) = { .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), diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 9999d9fe5e4..5420afbc8e0 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -354,9 +354,10 @@ int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) { struct udevice *bus = i2c_bus->bus; + struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); struct gpio_desc *scl_gpio = &i2c_bus->scl_gpio; struct gpio_desc *sda_gpio = &i2c_bus->sda_gpio; - int sda, scl; + int sda, scl, idle_sclks; int i, ret = 0; ulong elapsed, start_time; @@ -380,8 +381,22 @@ int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) if ((sda & scl) == 1) goto exit; /* Bus is idle already */ + /* + * In most cases it is just enough to generate 8 + 1 SCLK + * clocks to recover I2C slave device from 'stuck' state + * (when for example SW reset was performed, in the middle of + * I2C transmission). + * + * However, there are devices which send data in packets of + * N bytes (N > 1). In such case we do need N * 8 + 1 SCLK + * clocks. + */ + idle_sclks = 8 + 1; + + if (i2c->max_transaction_bytes > 0) + idle_sclks = i2c->max_transaction_bytes * 8 + 1; /* Send high and low on the SCL line */ - for (i = 0; i < 9; i++) { + for (i = 0; i < idle_sclks; i++) { dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_OUT); dm_gpio_set_value(scl_gpio, 0); udelay(50); -- cgit v1.2.3