[U-Boot] [PATCH] i2c: imx: bus arbitration fix when reading block data

Jim Brennan jbrennan at impinj.com
Wed Dec 27 01:05:07 UTC 2017


From e643f91ccfa544b7e3153a7721fba66e0e494759 Mon Sep 17 00:00:00 2001
From: Jim Brennan <jbrennan at 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 <jbrennan at 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





More information about the U-Boot mailing list