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 From 4cdf4e0655823fbb0c937cab939e54a1527fa01e Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 9 Apr 2019 08:57:41 +0200 Subject: i2c: muxes: pca954x: update list of supported devices The Kconfig help has not been updated while adding PCA9547 and PCA9646. Signed-off-by: Luca Ceresoli Acked-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/muxes/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index b0da67ce2c6..4a601c88b78 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -29,11 +29,11 @@ config I2C_MUX_PCA954x tristate "TI PCA954x I2C Mux/switches" depends on I2C_MUX help - If you say yes here you get support for the TI PCA954x - I2C mux/switch devices. It is x width I2C multiplexer which enables to - partitioning I2C bus and connect multiple devices with the same address - to the same I2C controller where driver handles proper routing to - target i2c device. PCA9544 and PCA9548 are supported. + If you say yes here you get support for the TI PCA954x I2C mux/switch + devices. It is x width I2C multiplexer which enables to partitioning + I2C bus and connect multiple devices with the same address to the same + I2C controller where driver handles proper routing to target i2c + device. Supported chips are PCA9544, PCA9547, PCA9548 and PCA9646. config I2C_MUX_GPIO tristate "GPIO-based I2C multiplexer" -- cgit v1.2.3 From 5995cdb167a4e8d3049cc25ca37c13ab85a63ac0 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 9 Apr 2019 08:57:42 +0200 Subject: i2c: muxes: pca954x: clarify enable field The chip_desc.enable field is used only for muxes, not for switches. Document it and remove the unused values. Signed-off-by: Luca Ceresoli Reviewed-by: Heiko Schocher Acked-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/muxes/pca954x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c index bd4e9abe5f3..5669753ce14 100644 --- a/drivers/i2c/muxes/pca954x.c +++ b/drivers/i2c/muxes/pca954x.c @@ -22,7 +22,7 @@ enum pca_type { }; struct chip_desc { - u8 enable; + u8 enable; /* Enable mask in ctl register (used for muxes only) */ enum muxtype { pca954x_ismux = 0, pca954x_isswi, @@ -48,12 +48,10 @@ static const struct chip_desc chips[] = { .width = 8, }, [PCA9548] = { - .enable = 0x8, .muxtype = pca954x_isswi, .width = 8, }, [PCA9646] = { - .enable = 0x0, .muxtype = pca954x_isswi, .width = 4, }, -- cgit v1.2.3 From 9985b74bf6d6550b83370e28057f11eab2a647d7 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 9 Apr 2019 08:57:43 +0200 Subject: i2c: muxes: pca954x: support PCA9543 I2C switch The PCA9543 is a 2-channel I2C switch. Signed-off-by: Luca Ceresoli Reviewed-by: Heiko Schocher Acked-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/muxes/Kconfig | 3 ++- drivers/i2c/muxes/pca954x.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 4a601c88b78..68f15261be7 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -33,7 +33,8 @@ config I2C_MUX_PCA954x devices. It is x width I2C multiplexer which enables to partitioning I2C bus and connect multiple devices with the same address to the same I2C controller where driver handles proper routing to target i2c - device. Supported chips are PCA9544, PCA9547, PCA9548 and PCA9646. + device. Supported chips are PCA9543, PCA9544, PCA9547, PCA9548 and + PCA9646. config I2C_MUX_GPIO tristate "GPIO-based I2C multiplexer" diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c index 5669753ce14..a630ce991d0 100644 --- a/drivers/i2c/muxes/pca954x.c +++ b/drivers/i2c/muxes/pca954x.c @@ -15,6 +15,7 @@ DECLARE_GLOBAL_DATA_PTR; enum pca_type { + PCA9543, PCA9544, PCA9547, PCA9548, @@ -37,6 +38,10 @@ struct pca954x_priv { }; static const struct chip_desc chips[] = { + [PCA9543] = { + .muxtype = pca954x_isswi, + .width = 2, + }, [PCA9544] = { .enable = 0x4, .muxtype = pca954x_ismux, @@ -87,6 +92,7 @@ static const struct i2c_mux_ops pca954x_ops = { }; static const struct udevice_id pca954x_ids[] = { + { .compatible = "nxp,pca9543", .data = PCA9543 }, { .compatible = "nxp,pca9544", .data = PCA9544 }, { .compatible = "nxp,pca9547", .data = PCA9547 }, { .compatible = "nxp,pca9548", .data = PCA9548 }, -- cgit v1.2.3 From a29034d1e6d1c1535055f455b5af5906a7edbc04 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 7 Apr 2019 12:38:48 +0300 Subject: misc: i2c_eeprom: support DT pagesize property Read the page size from DT when available. Signed-off-by: Baruch Siach Reviewed-by: Heiko Schocher --- drivers/misc/i2c_eeprom.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index 29ad87c1d7b..ce2cad44d8d 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/drivers/misc/i2c_eeprom.c @@ -50,6 +50,12 @@ static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev) { struct i2c_eeprom *priv = dev_get_priv(dev); u64 data = dev_get_driver_data(dev); + u32 pagesize; + + if (dev_read_u32(dev, "pagesize", &pagesize) == 0) { + priv->pagesize = pagesize; + return 0; + } /* 6 bit -> page size of up to 2^63 (should be sufficient) */ priv->pagewidth = data & 0x3F; -- cgit v1.2.3 From 84c80c63d53bc8a7779b1e7e7084ee3b2d20e768 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 7 Apr 2019 12:38:49 +0300 Subject: misc: i2c_eeprom: add eeprom write support Write up to page size in each i2c transfer. Signed-off-by: Baruch Siach Reviewed-by: Heiko Schocher --- drivers/misc/i2c_eeprom.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index ce2cad44d8d..f25d0540075 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/drivers/misc/i2c_eeprom.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -38,7 +39,24 @@ static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf, static int i2c_eeprom_std_write(struct udevice *dev, int offset, const uint8_t *buf, int size) { - return -ENODEV; + struct i2c_eeprom *priv = dev_get_priv(dev); + int ret; + + while (size > 0) { + int write_size = min_t(int, size, priv->pagesize); + + ret = dm_i2c_write(dev, offset, buf, write_size); + if (ret) + return ret; + + offset += write_size; + buf += write_size; + size -= write_size; + + udelay(10000); + } + + return 0; } static const struct i2c_eeprom_ops i2c_eeprom_std_ops = { -- cgit v1.2.3