From e643f91ccfa544b7e3153a7721fba66e0e494759 Mon Sep 17 00:00:00 2001 From: Jim Brennan <jbren...@impinj.com> Date: Wed, 13 Dec 2017 13:44:02 -0800 Subject: [PATCH] i2c: imx: bus arbitration fix when reading block data
Fixes arbitration failure on imx platform due to incorrect chip address use when reading a block of data. Add support for both reading or writing a block of data or any combination. Signed-off-by: Jim Brennan <jbren...@impinj.com> --- drivers/i2c/mxc_i2c.c | 65 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 205274e947..bd2a39ebe6 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -25,6 +25,7 @@ #include <dm.h> #include <dm/pinctrl.h> #include <fdtdec.h> +#include <stdbool.h> DECLARE_GLOBAL_DATA_PTR; @@ -275,7 +276,7 @@ static void i2c_imx_stop(struct mxc_i2c_bus *i2c_bus) * write register address */ static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip, - u32 addr, int alen) + u32 addr, int alen, bool read) { unsigned int temp; int ret; @@ -317,9 +318,17 @@ static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip, temp |= I2CR_MTX | I2CR_TX_NO_AK; writeb(temp, base + (I2CR << reg_shift)); - if (alen >= 0) { - /* write slave address */ - ret = tx_byte(i2c_bus, chip << 1); + /* write slave address */ + u8 slave_address = (chip << 1); + + if (read) + slave_address |= 1; + ret = tx_byte(i2c_bus, slave_address); + if (ret < 0) + return ret; + + while (alen--) { + ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff); if (ret < 0) return ret; @@ -413,7 +422,7 @@ exit: #endif static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip, - u32 addr, int alen) + u32 addr, int alen, bool read) { int retry; int ret; @@ -424,7 +433,7 @@ static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip, return -EINVAL; for (retry = 0; retry < 3; retry++) { - ret = i2c_init_transfer_(i2c_bus, chip, addr, alen); + ret = i2c_init_transfer_(i2c_bus, chip, addr, alen, read); if (ret >= 0) return 0; i2c_imx_stop(i2c_bus); @@ -536,7 +545,7 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT; ulong base = i2c_bus->base; - ret = i2c_init_transfer(i2c_bus, chip, addr, alen); + ret = i2c_init_transfer(i2c_bus, chip, addr, alen, false); if (ret < 0) return ret; @@ -566,7 +575,7 @@ static int bus_i2c_write(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, { int ret = 0; - ret = i2c_init_transfer(i2c_bus, chip, addr, alen); + ret = i2c_init_transfer(i2c_bus, chip, addr, alen, false); if (ret < 0) return ret; @@ -817,7 +826,7 @@ static int mxc_i2c_probe_chip(struct udevice *bus, u32 chip_addr, int ret; struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus); - ret = i2c_init_transfer(i2c_bus, chip_addr, 0, 0); + ret = i2c_init_transfer(i2c_bus, chip_addr, 0, 0, false); if (ret < 0) { debug("%s failed, ret = %d\n", __func__, ret); return ret; @@ -841,35 +850,43 @@ static int mxc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) * because here we only want to send out chip address. The register * address is wrapped in msg. */ - ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0); + + bool read = (msg->flags & I2C_M_RD) ? true : false; + + ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0, read); if (ret < 0) { debug("i2c_init_transfer error: %d\n", ret); return ret; } for (; nmsgs > 0; nmsgs--, msg++) { + bool current_is_read = (msg->flags & I2C_M_RD) ? true : false; bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); - if (msg->flags & I2C_M_RD) + if (current_is_read) ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, msg->len); else { ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, msg->len); - if (ret) + } + if (ret) + break; + + if (nmsgs > 1 && (current_is_read ^ next_is_read)) { + u8 addr = msg->addr << 1; + + /* Reuse ret */ + ret = readb(base + (I2CR << reg_shift)); + ret |= I2CR_RSTA; + writeb(ret, base + (I2CR << reg_shift)); + + if (next_is_read) + addr |= 1; + + ret = tx_byte(i2c_bus, addr); + if (ret < 0) break; - if (next_is_read) { - /* Reuse ret */ - ret = readb(base + (I2CR << reg_shift)); - ret |= I2CR_RSTA; - writeb(ret, base + (I2CR << reg_shift)); - - ret = tx_byte(i2c_bus, (msg->addr << 1) | 1); - if (ret < 0) { - i2c_imx_stop(i2c_bus); - break; - } - } } } -- 2.13.6 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot