Commit 85968522b928e19a27aa79f0cb186c80b8b82e47

Authored by Robert Beckett
Committed by Heiko Schocher
1 parent 553cb06887

i2c: add support for offset overflow in to address

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>
Reviewed-by: Heiko Schocher <hs@denx.de>

Showing 2 changed files with 60 additions and 7 deletions Side-by-side Diff

drivers/i2c/i2c-uclass.c
... ... @@ -52,16 +52,19 @@
52 52 static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset,
53 53 uint8_t offset_buf[], struct i2c_msg *msg)
54 54 {
55   - int offset_len;
  55 + int offset_len = chip->offset_len;
56 56  
57 57 msg->addr = chip->chip_addr;
  58 + if (chip->chip_addr_offset_mask)
  59 + msg->addr |= (offset >> (8 * offset_len)) &
  60 + chip->chip_addr_offset_mask;
58 61 msg->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
59 62 msg->len = chip->offset_len;
60 63 msg->buf = offset_buf;
61   - if (!chip->offset_len)
  64 + if (!offset_len)
62 65 return -EADDRNOTAVAIL;
63   - assert(chip->offset_len <= I2C_MAX_OFFSET_LEN);
64   - offset_len = chip->offset_len;
  66 + assert(offset_len <= I2C_MAX_OFFSET_LEN);
  67 +
65 68 while (offset_len--)
66 69 *offset_buf++ = offset >> (8 * offset_len);
67 70  
... ... @@ -83,7 +86,7 @@
83 86 if (i2c_setup_offset(chip, offset + i, offset_buf, msg))
84 87 return -EINVAL;
85 88 ptr = msg + 1;
86   - ptr->addr = chip->chip_addr;
  89 + ptr->addr = msg->addr;
87 90 ptr->flags = msg->flags | I2C_M_RD;
88 91 ptr->len = 1;
89 92 ptr->buf = &buffer[i];
... ... @@ -139,7 +142,7 @@
139 142 ptr++;
140 143  
141 144 if (len) {
142   - ptr->addr = chip->chip_addr;
  145 + ptr->addr = msg->addr;
143 146 ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
144 147 ptr->flags |= I2C_M_RD;
145 148 ptr->len = len;
... ... @@ -323,7 +326,8 @@
323 326 struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
324 327 int ret;
325 328  
326   - if (chip->chip_addr == chip_addr) {
  329 + if (chip->chip_addr == (chip_addr &
  330 + ~chip->chip_addr_offset_mask)) {
327 331 ret = device_probe(dev);
328 332 debug("found, ret=%d\n", ret);
329 333 if (ret)
... ... @@ -463,6 +467,22 @@
463 467 struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
464 468  
465 469 return chip->offset_len;
  470 +}
  471 +
  472 +int i2c_set_chip_addr_offset_mask(struct udevice *dev, uint mask)
  473 +{
  474 + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
  475 +
  476 + chip->chip_addr_offset_mask = mask;
  477 +
  478 + return 0;
  479 +}
  480 +
  481 +uint i2c_get_chip_addr_offset_mask(struct udevice *dev)
  482 +{
  483 + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
  484 +
  485 + return chip->chip_addr_offset_mask;
466 486 }
467 487  
468 488 #ifdef CONFIG_DM_GPIO
... ... @@ -45,12 +45,26 @@
45 45 * represent up to 256 bytes. A value larger than 1 may be
46 46 * needed for larger devices.
47 47 * @flags: Flags for this chip (dm_i2c_chip_flags)
  48 + * @chip_addr_offset_mask: Mask of offset bits within chip_addr. Used for
  49 + * devices which steal addresses as part of offset.
  50 + * If offset_len is zero, then the offset is encoded
  51 + * completely within the chip address itself.
  52 + * e.g. a devce with chip address of 0x2c with 512
  53 + * registers might use the bottom bit of the address
  54 + * to indicate which half of the address space is being
  55 + * accessed while still only using 1 byte offset.
  56 + * This means it will respond to chip address 0x2c and
  57 + * 0x2d.
  58 + * A real world example is the Atmel AT24C04. It's
  59 + * datasheet explains it's usage of this addressing
  60 + * mode.
48 61 * @emul: Emulator for this chip address (only used for emulation)
49 62 */
50 63 struct dm_i2c_chip {
51 64 uint chip_addr;
52 65 uint offset_len;
53 66 uint flags;
  67 + uint chip_addr_offset_mask;
54 68 #ifdef CONFIG_SANDBOX
55 69 struct udevice *emul;
56 70 bool test_mode;
... ... @@ -260,6 +274,25 @@
260 274 * @return: Current offset length value (typically 1 or 2)
261 275 */
262 276 int i2c_get_chip_offset_len(struct udevice *dev);
  277 +
  278 +/**
  279 + * i2c_set_chip_addr_offset_mask() - set mask of address bits usable by offset
  280 + *
  281 + * Some devices listen on multiple chip addresses to achieve larger offsets
  282 + * than their single or multiple byte offsets would allow for. You can use this
  283 + * function to set the bits that are valid to be used for offset overflow.
  284 + *
  285 + * @mask: The mask to be used for high offset bits within address
  286 + * @return 0 if OK, other -ve value on error
  287 + */
  288 +int i2c_set_chip_addr_offset_mask(struct udevice *dev, uint mask);
  289 +
  290 +/*
  291 + * i2c_get_chip_addr_offset_mask() - get mask of address bits usable by offset
  292 + *
  293 + * @return current chip addr offset mask
  294 + */
  295 +uint i2c_get_chip_addr_offset_mask(struct udevice *dev);
263 296  
264 297 /**
265 298 * i2c_deblock() - recover a bus that is in an unknown state