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 <yamad...@jp.panasonic.com>
Cc: Heiko Schocher <h...@denx.de>
Cc: Simon Glass <s...@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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to