[U-Boot] [RFC PATCH 2/2] dm: i2c: support 10bit addressing in I2C uclass layer

Masahiro Yamada yamada.m at jp.panasonic.com
Fri Dec 19 19:34:24 CET 2014


Master send to / receive from 10-bit addressed slave devices
can be supported by software layer without any hardware change
because the LSB 8bit of the slave address is treated as data part.

Master Send to a 10bit-addressed slave chip is performed like this:

 DIR    Format
 M->S   11110 + address[9:8] + R/W(0)
 M->S   address[7:0]
 M->S   data0
 M->S   data1
      ...

Master Receive from a 10bit-addressed slave chip is like this:

 DIR    Format
 M->S   11110 + address[9:8] + R/W(0)
 M->S   address[7:0]
        (Restart)
 M->S   111110 + address[9:8] + R/W(1)
 S->M   data0
 S->M   data1
      ...

Signed-off-by: Masahiro Yamada <yamada.m at jp.panasonic.com>
Cc: Heiko Schocher <hs at denx.de>
Cc: Simon Glass <sjg at chromium.org>
---

 drivers/i2c/i2c-uclass.c | 80 +++++++++++++++++++++++++++++++-----------------
 include/i2c.h            |  4 +++
 2 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index 005bf86..de9d92a 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -31,20 +31,28 @@ DECLARE_GLOBAL_DATA_PTR;
 static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset,
 			    uint8_t offset_buf[], struct i2c_msg *msg)
 {
-	int offset_len;
+	int offset_len = chip->offset_len;
 
-	msg->addr = chip->chip_addr;
-	msg->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
-	msg->len = chip->offset_len;
+	msg->flags = 0;
 	msg->buf = offset_buf;
-	if (!chip->offset_len)
-		return -EADDRNOTAVAIL;
-	assert(chip->offset_len <= I2C_MAX_OFFSET_LEN);
-	offset_len = chip->offset_len;
-	while (offset_len--)
+
+	if (chip->flags & DM_I2C_CHIP_10BIT) {
+		msg->addr = I2C_ADDR_TEN_HIGH(chip->chip_addr);
+		*offset_buf++ = I2C_ADDR_TEN_LOW(chip->chip_addr);
+		msg->len = 1;
+	} else {
+		msg->addr = chip->chip_addr;
+		msg->len = 0;
+	}
+
+	assert(offset_len <= I2C_MAX_OFFSET_LEN);
+
+	while (offset_len--) {
 		*offset_buf++ = offset >> (8 * offset_len);
+		msg->len++;
+	}
 
-	return 0;
+	return msg->len ? 0 : -EADDRNOTAVAIL;
 }
 
 static int i2c_read_bytewise(struct udevice *dev, uint offset,
@@ -54,7 +62,7 @@ static int i2c_read_bytewise(struct udevice *dev, uint offset,
 	struct udevice *bus = dev_get_parent(dev);
 	struct dm_i2c_ops *ops = i2c_get_ops(bus);
 	struct i2c_msg msg[2], *ptr;
-	uint8_t offset_buf[I2C_MAX_OFFSET_LEN];
+	uint8_t offset_buf[I2C_MAX_OFFSET_LEN + 1];
 	int ret;
 	int i;
 
@@ -62,7 +70,8 @@ static int i2c_read_bytewise(struct udevice *dev, uint offset,
 		if (i2c_setup_offset(chip, offset + i, offset_buf, msg))
 			return -EINVAL;
 		ptr = msg + 1;
-		ptr->addr = chip->chip_addr;
+		ptr->addr = chip->flags & DM_I2C_CHIP_10BIT ?
+			I2C_ADDR_TEN_HIGH(chip->chip_addr) : chip->chip_addr;
 		ptr->flags = msg->flags | I2C_M_RD;
 		ptr->len = 1;
 		ptr->buf = &buffer[i];
@@ -83,7 +92,7 @@ static int i2c_write_bytewise(struct udevice *dev, uint offset,
 	struct udevice *bus = dev_get_parent(dev);
 	struct dm_i2c_ops *ops = i2c_get_ops(bus);
 	struct i2c_msg msg[1];
-	uint8_t buf[I2C_MAX_OFFSET_LEN + 1];
+	uint8_t buf[I2C_MAX_OFFSET_LEN + 2];
 	int ret;
 	int i;
 
@@ -106,7 +115,7 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len)
 	struct udevice *bus = dev_get_parent(dev);
 	struct dm_i2c_ops *ops = i2c_get_ops(bus);
 	struct i2c_msg msg[2], *ptr;
-	uint8_t offset_buf[I2C_MAX_OFFSET_LEN];
+	uint8_t offset_buf[I2C_MAX_OFFSET_LEN + 1];
 	int msg_count;
 
 	if (!ops->xfer)
@@ -118,9 +127,9 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len)
 		ptr++;
 
 	if (len) {
-		ptr->addr = chip->chip_addr;
-		ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
-		ptr->flags |= I2C_M_RD;
+		ptr->addr = chip->flags & DM_I2C_CHIP_10BIT ?
+			I2C_ADDR_TEN_HIGH(chip->chip_addr) : chip->chip_addr;
+		ptr->flags = I2C_M_RD;
 		ptr->len = len;
 		ptr->buf = buffer;
 		ptr++;
@@ -136,6 +145,7 @@ int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len)
 	struct udevice *bus = dev_get_parent(dev);
 	struct dm_i2c_ops *ops = i2c_get_ops(bus);
 	struct i2c_msg msg[1];
+	int buf_len;
 
 	if (!ops->xfer)
 		return -ENOSYS;
@@ -157,27 +167,33 @@ int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len)
 	 * copying the message.
 	 *
 	 * Use the stack for small messages, malloc() for larger ones. We
-	 * need to allow space for the offset (up to 4 bytes) and the message
+	 * need to allow space for the offset (up to 4 bytes), the second
+	 * byte of the slave address (if 10bit addressing) and the message
 	 * itself.
 	 */
-	if (len < 64) {
-		uint8_t buf[I2C_MAX_OFFSET_LEN + len];
+
+	buf_len = I2C_MAX_OFFSET_LEN + len;
+	if (chip->flags & DM_I2C_CHIP_10BIT)
+		buf_len++;
+
+	if (buf_len <= 64) {
+		uint8_t buf[64];
 
 		i2c_setup_offset(chip, offset, buf, msg);
+		memcpy(buf + msg->len, buffer, len);
 		msg->len += len;
-		memcpy(buf + chip->offset_len, buffer, len);
 
 		return ops->xfer(bus, msg, 1);
 	} else {
 		uint8_t *buf;
 		int ret;
 
-		buf = malloc(I2C_MAX_OFFSET_LEN + len);
+		buf = malloc(buf_len);
 		if (!buf)
 			return -ENOMEM;
 		i2c_setup_offset(chip, offset, buf, msg);
+		memcpy(buf + msg->len, buffer, len);
 		msg->len += len;
-		memcpy(buf + chip->offset_len, buffer, len);
 
 		ret = ops->xfer(bus, msg, 1);
 		free(buf);
@@ -199,6 +215,7 @@ static int i2c_probe_chip(struct udevice *bus, uint chip_addr,
 {
 	struct dm_i2c_ops *ops = i2c_get_ops(bus);
 	struct i2c_msg msg[1];
+	u8 low_address;
 	int ret;
 
 	if (ops->probe_chip) {
@@ -210,11 +227,18 @@ static int i2c_probe_chip(struct udevice *bus, uint chip_addr,
 	if (!ops->xfer)
 		return -ENOSYS;
 
-	/* Probe with a zero-length message */
-	msg->addr = chip_addr;
-	msg->flags = chip_flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
-	msg->len = 0;
-	msg->buf = NULL;
+	if (chip_flags & DM_I2C_CHIP_10BIT) {
+		msg->addr = I2C_ADDR_TEN_HIGH(chip_addr);
+		low_address = I2C_ADDR_TEN_LOW(chip_addr);
+		msg->buf = &low_address;
+		msg->len = 1;
+	} else {
+		/* Probe with a zero-length message */
+		msg->addr = chip_addr;
+		msg->buf = NULL;
+		msg->len = 0;
+	}
+	msg->flags = 0;
 
 	return ops->xfer(bus, msg, 1);
 }
diff --git a/include/i2c.h b/include/i2c.h
index 9c6a60c..e616909 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -185,6 +185,10 @@ int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len);
  */
 int i2c_deblock(struct udevice *bus);
 
+/* return upper, lower byte of 10 bit addressing, respectively */
+#define I2C_ADDR_TEN_HIGH(a)	(0x78 | (((a) >> 8) & 0x3))
+#define I2C_ADDR_TEN_LOW(a)	((a) & 0xff)
+
 /*
  * Not all of these flags are implemented in the U-Boot API
  */
-- 
1.9.1



More information about the U-Boot mailing list