mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-17 12:41:32 +00:00
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 <lukma@denx.de>
This commit is contained in:
parent
3c99166441
commit
a40fe217d1
4 changed files with 48 additions and 2 deletions
|
@ -12,6 +12,10 @@ property which allows the chip offset length to be selected.
|
|||
Optional properties:
|
||||
- u-boot,i2c-offset-len - length of chip offset in bytes. If omitted the
|
||||
default value of 1 is used.
|
||||
- u-boot,i2c-transaction-bytes - the length of single I2C transaction on
|
||||
the bus. Some devices require more than single byte transmission
|
||||
(e.g. mc34708 mfd). This information is necessary to correctly
|
||||
initialize (put into idle state) I2C bus after soft reset.
|
||||
- gpios = <sda ...>, <scl ...>;
|
||||
pinctrl-names = "default", "gpio";
|
||||
pinctrl-0 = <&i2c_xfer>;
|
||||
|
@ -28,6 +32,7 @@ i2c4: i2c@12ca0000 {
|
|||
compatible = "google,cros-ec";
|
||||
i2c-max-frequency = <100000>;
|
||||
u-boot,i2c-offset-len = <0>;
|
||||
u-boot,i2c-transaction-bytes = <3>;
|
||||
ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -68,9 +68,11 @@ struct dm_i2c_chip {
|
|||
* I2C bus udevice.
|
||||
*
|
||||
* @speed_hz: Bus speed in hertz (typically 100000)
|
||||
* @max_transaction_bytes: Maximal size of single I2C transfer
|
||||
*/
|
||||
struct dm_i2c_bus {
|
||||
int speed_hz;
|
||||
int max_transaction_bytes;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Reference in a new issue