diff mbox series

[U-Boot,1/3] i2c: add support for offset overflow in to address

Message ID 20191028174459.7019-2-bob.beckett@collabora.com
State Accepted
Commit 85968522b928e19a27aa79f0cb186c80b8b82e47
Delegated to: Heiko Schocher
Headers show
Series i2c: add support for offset overflow in to chip address | expand

Commit Message

Robert Beckett Oct. 28, 2019, 5:44 p.m. UTC
Some devices (2 wire eeproms for example) use some bits from the chip
address to represent the high bits of the offset instead of or as well
as using multiple bytes for the offset, effectively stealing chip
addresses on the bus.

Add a chip offset mask that can be set for any i2c chip which gets
filled with the offset overflow during offset setup.

Signed-off-by: Robert Beckett <bob.beckett@collabora.com>
Signed-off-by: Ian Ray <ian.ray@ge.com>
---

 drivers/i2c/i2c-uclass.c | 34 +++++++++++++++++++++++++++-------
 include/i2c.h            | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+), 7 deletions(-)

Comments

Heiko Schocher Nov. 20, 2019, 5:43 a.m. UTC | #1
Hello Robert

Am 28.10.2019 um 18:44 schrieb Robert Beckett:
> Some devices (2 wire eeproms for example) use some bits from the chip
> address to represent the high bits of the offset instead of or as well
> as using multiple bytes for the offset, effectively stealing chip
> addresses on the bus.
> 
> Add a chip offset mask that can be set for any i2c chip which gets
> filled with the offset overflow during offset setup.
> 
> Signed-off-by: Robert Beckett <bob.beckett@collabora.com>
> Signed-off-by: Ian Ray <ian.ray@ge.com>
> ---
> 
>   drivers/i2c/i2c-uclass.c | 34 +++++++++++++++++++++++++++-------
>   include/i2c.h            | 33 +++++++++++++++++++++++++++++++++
>   2 files changed, 60 insertions(+), 7 deletions(-)

Reviewed-by: Heiko Schocher <hs@denx.de>

Thanks!

bye,
Heiko
Heiko Schocher Dec. 17, 2019, 11:57 a.m. UTC | #2
Hello Robert,

Am 28.10.2019 um 18:44 schrieb Robert Beckett:
> Some devices (2 wire eeproms for example) use some bits from the chip
> address to represent the high bits of the offset instead of or as well
> as using multiple bytes for the offset, effectively stealing chip
> addresses on the bus.
> 
> Add a chip offset mask that can be set for any i2c chip which gets
> filled with the offset overflow during offset setup.
> 
> Signed-off-by: Robert Beckett <bob.beckett@collabora.com>
> Signed-off-by: Ian Ray <ian.ray@ge.com>
> ---
> 
>   drivers/i2c/i2c-uclass.c | 34 +++++++++++++++++++++++++++-------
>   include/i2c.h            | 33 +++++++++++++++++++++++++++++++++
>   2 files changed, 60 insertions(+), 7 deletions(-)

Applied to u-boot-i2c next branch.

Thanks!

bye,
Heiko
diff mbox series

Patch

diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index e47abf1833..44aace3a36 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -52,16 +52,19 @@  void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs)
 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;
+	if (chip->chip_addr_offset_mask)
+		msg->addr |= (offset >> (8 * offset_len)) &
+			chip->chip_addr_offset_mask;
 	msg->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
 	msg->len = chip->offset_len;
 	msg->buf = offset_buf;
-	if (!chip->offset_len)
+	if (!offset_len)
 		return -EADDRNOTAVAIL;
-	assert(chip->offset_len <= I2C_MAX_OFFSET_LEN);
-	offset_len = chip->offset_len;
+	assert(offset_len <= I2C_MAX_OFFSET_LEN);
+
 	while (offset_len--)
 		*offset_buf++ = offset >> (8 * offset_len);
 
@@ -83,7 +86,7 @@  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 = msg->addr;
 		ptr->flags = msg->flags | I2C_M_RD;
 		ptr->len = 1;
 		ptr->buf = &buffer[i];
@@ -139,7 +142,7 @@  int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len)
 		ptr++;
 
 	if (len) {
-		ptr->addr = chip->chip_addr;
+		ptr->addr = msg->addr;
 		ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
 		ptr->flags |= I2C_M_RD;
 		ptr->len = len;
@@ -323,7 +326,8 @@  int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len,
 		struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
 		int ret;
 
-		if (chip->chip_addr == chip_addr) {
+		if (chip->chip_addr == (chip_addr &
+					~chip->chip_addr_offset_mask)) {
 			ret = device_probe(dev);
 			debug("found, ret=%d\n", ret);
 			if (ret)
@@ -465,6 +469,22 @@  int i2c_get_chip_offset_len(struct udevice *dev)
 	return chip->offset_len;
 }
 
+int i2c_set_chip_addr_offset_mask(struct udevice *dev, uint mask)
+{
+	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+
+	chip->chip_addr_offset_mask = mask;
+
+	return 0;
+}
+
+uint i2c_get_chip_addr_offset_mask(struct udevice *dev)
+{
+	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+
+	return chip->chip_addr_offset_mask;
+}
+
 #ifdef CONFIG_DM_GPIO
 static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit)
 {
diff --git a/include/i2c.h b/include/i2c.h
index 33570f5404..72e2e8e426 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -45,12 +45,26 @@  struct udevice;
  *		represent up to 256 bytes. A value larger than 1 may be
  *		needed for larger devices.
  * @flags:	Flags for this chip (dm_i2c_chip_flags)
+ * @chip_addr_offset_mask: Mask of offset bits within chip_addr. Used for
+ *			   devices which steal addresses as part of offset.
+ *			   If offset_len is zero, then the offset is encoded
+ *			   completely within the chip address itself.
+ *			   e.g. a devce with chip address of 0x2c with 512
+ *			   registers might use the bottom bit of the address
+ *			   to indicate which half of the address space is being
+ *			   accessed while still only using 1 byte offset.
+ *			   This means it will respond to  chip address 0x2c and
+ *			   0x2d.
+ *			   A real world example is the Atmel AT24C04. It's
+ *			   datasheet explains it's usage of this addressing
+ *			   mode.
  * @emul: Emulator for this chip address (only used for emulation)
  */
 struct dm_i2c_chip {
 	uint chip_addr;
 	uint offset_len;
 	uint flags;
+	uint chip_addr_offset_mask;
 #ifdef CONFIG_SANDBOX
 	struct udevice *emul;
 	bool test_mode;
@@ -261,6 +275,25 @@  int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len);
  */
 int i2c_get_chip_offset_len(struct udevice *dev);
 
+/**
+ * i2c_set_chip_addr_offset_mask() - set mask of address bits usable by offset
+ *
+ * Some devices listen on multiple chip addresses to achieve larger offsets
+ * than their single or multiple byte offsets would allow for. You can use this
+ * function to set the bits that are valid to be used for offset overflow.
+ *
+ * @mask: The mask to be used for high offset bits within address
+ * @return 0 if OK, other -ve value on error
+ */
+int i2c_set_chip_addr_offset_mask(struct udevice *dev, uint mask);
+
+/*
+ * i2c_get_chip_addr_offset_mask() - get mask of address bits usable by offset
+ *
+ * @return current chip addr offset mask
+ */
+uint i2c_get_chip_addr_offset_mask(struct udevice *dev);
+
 /**
  * i2c_deblock() - recover a bus that is in an unknown state
  *