From 85968522b928e19a27aa79f0cb186c80b8b82e47 Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Mon, 28 Oct 2019 17:44:57 +0000 Subject: [PATCH 1/5] 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 Signed-off-by: Ian Ray Reviewed-by: Heiko Schocher --- drivers/i2c/i2c-uclass.c | 34 +++++++++++++++++++++++++++------- include/i2c.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 7 deletions(-) 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 * From 22e93511be2e012d3f807641b07e09b649a71ef6 Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Mon, 28 Oct 2019 17:44:58 +0000 Subject: [PATCH 2/5] dm: i2c: EEPROM simulator allow tests visibility of addr and offset Improve i2c EEPROM simulator testing by providing access functions to check the previous chip addr and offset. Given that we can now directly test the offsets, also simplified the offset mapping and allow for wrapping acceses. Signed-off-by: Robert Beckett Reviewed-by: Heiko Schocher --- arch/sandbox/include/asm/test.h | 4 +++ drivers/misc/i2c_eeprom_emul.c | 61 +++++++++++++++++++++++++-------- test/dm/i2c.c | 50 +++++++++++++++++++-------- 3 files changed, 86 insertions(+), 29 deletions(-) diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index b885e1a14f..f3a8b455d5 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -72,6 +72,10 @@ void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len); +uint sanbox_i2c_eeprom_get_prev_addr(struct udevice *dev); + +uint sanbox_i2c_eeprom_get_prev_offset(struct udevice *dev); + /** * sandbox_i2c_rtc_set_offset() - set the time offset from system/base time * diff --git a/drivers/misc/i2c_eeprom_emul.c b/drivers/misc/i2c_eeprom_emul.c index 29ed45923f..284267f2ea 100644 --- a/drivers/misc/i2c_eeprom_emul.c +++ b/drivers/misc/i2c_eeprom_emul.c @@ -27,6 +27,8 @@ struct sandbox_i2c_flash_plat_data { struct sandbox_i2c_flash { uint8_t *data; + uint prev_addr; /* slave address of previous access */ + uint prev_offset; /* offset of previous access */ }; void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, @@ -44,6 +46,20 @@ void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) plat->offset_len = offset_len; } +uint sanbox_i2c_eeprom_get_prev_addr(struct udevice *dev) +{ + struct sandbox_i2c_flash *priv = dev_get_priv(dev); + + return priv->prev_addr; +} + +uint sanbox_i2c_eeprom_get_prev_offset(struct udevice *dev) +{ + struct sandbox_i2c_flash *priv = dev_get_priv(dev); + + return priv->prev_offset; +} + static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, int nmsgs) { @@ -52,6 +68,10 @@ static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, debug("\n%s\n", __func__); debug_buffer(0, priv->data, 1, 16, 0); + + /* store addr for testing visibity */ + priv->prev_addr = msg->addr; + for (; nmsgs > 0; nmsgs--, msg++) { struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(emul); @@ -60,11 +80,6 @@ static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, if (!plat->size) return -ENODEV; - if (msg->addr + msg->len > plat->size) { - debug("%s: Address %x, len %x is outside range 0..%x\n", - __func__, msg->addr, msg->len, plat->size); - return -EINVAL; - } len = msg->len; debug(" %s: msg->len=%d", msg->flags & I2C_M_RD ? "read" : "write", @@ -73,7 +88,16 @@ static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) len = 1; debug(", offset %x, len %x: ", offset, len); - memcpy(msg->buf, priv->data + offset, len); + if (offset + len > plat->size) { + int overflow = offset + len - plat->size; + int initial = len - overflow; + + memcpy(msg->buf, priv->data + offset, initial); + memcpy(msg->buf + initial, priv->data, + overflow); + } else { + memcpy(msg->buf, priv->data + offset, len); + } memset(msg->buf + len, '\xff', msg->len - len); debug_buffer(0, msg->buf, 1, msg->len, 0); } else if (len >= plat->offset_len) { @@ -87,15 +111,24 @@ static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) len = min(len, 1); - /* For testing, map offsets into our limited buffer */ - for (i = 24; i > 0; i -= 8) { - if (offset > (1 << i)) { - offset = (offset >> i) | - (offset & ((1 << i) - 1)); - offset += i; - } + /* store offset for testing visibility */ + priv->prev_offset = offset; + + /* For testing, map offsets into our limited buffer. + * offset wraps every 256 bytes + */ + offset &= 0xff; + debug("mapped offset to %x\n", offset); + + if (offset + len > plat->size) { + int overflow = offset + len - plat->size; + int initial = len - overflow; + + memcpy(priv->data + offset, ptr, initial); + memcpy(priv->data, ptr + initial, overflow); + } else { + memcpy(priv->data + offset, ptr, len); } - memcpy(priv->data + offset, ptr, len); } } debug_buffer(0, priv->data, 1, 16, 0); diff --git a/test/dm/i2c.c b/test/dm/i2c.c index cbbd4aa29a..bf5597b7a6 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -15,6 +15,7 @@ #include #include #include +#include #include static const int busnum; @@ -185,35 +186,54 @@ static int dm_test_i2c_offset(struct unit_test_state *uts) ut_assertok(i2c_set_chip_offset_len(dev, 0)); ut_assertok(dm_i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); - ut_assertok(memcmp(buf, "AB\0\0\0\0", sizeof(buf))); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0, sanbox_i2c_eeprom_get_prev_offset(eeprom)); /* Offset length 1 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 1); ut_assertok(i2c_set_chip_offset_len(dev, 1)); ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_asserteq(2, sanbox_i2c_eeprom_get_prev_offset(eeprom)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); - ut_assertok(memcmp(buf, "ABAB\0", sizeof(buf))); + ut_asserteq_mem("ABAB\0", buf, sizeof(buf)); + ut_asserteq(0, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Offset length 2 boundary - check model wrapping */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 2); + ut_assertok(i2c_set_chip_offset_len(dev, 2)); + ut_assertok(dm_i2c_write(dev, 0xFF, (uint8_t *)"A", 1)); + ut_asserteq(0xFF, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_write(dev, 0x100, (uint8_t *)"B", 1)); + ut_asserteq(0x100, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_write(dev, 0x101, (uint8_t *)"C", 1)); + ut_asserteq(0x101, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_read(dev, 0xFF, buf, 5)); + ut_asserteq_mem("ABCAB", buf, sizeof(buf)); + ut_asserteq(0xFF, sanbox_i2c_eeprom_get_prev_offset(eeprom)); /* Offset length 2 */ sandbox_i2c_eeprom_set_offset_len(eeprom, 2); ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(dm_i2c_write(dev, 0x210, (uint8_t *)"AB", 2)); - ut_assertok(dm_i2c_read(dev, 0x210, buf, 5)); - ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); + ut_assertok(dm_i2c_write(dev, 0x2020, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x2020, buf, 5)); + ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x2020, sanbox_i2c_eeprom_get_prev_offset(eeprom)); /* Offset length 3 */ - sandbox_i2c_eeprom_set_offset_len(eeprom, 2); - ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(dm_i2c_write(dev, 0x410, (uint8_t *)"AB", 2)); - ut_assertok(dm_i2c_read(dev, 0x410, buf, 5)); - ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); + sandbox_i2c_eeprom_set_offset_len(eeprom, 3); + ut_assertok(i2c_set_chip_offset_len(dev, 3)); + ut_assertok(dm_i2c_write(dev, 0x303030, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x303030, buf, 5)); + ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x303030, sanbox_i2c_eeprom_get_prev_offset(eeprom)); /* Offset length 4 */ - sandbox_i2c_eeprom_set_offset_len(eeprom, 2); - ut_assertok(i2c_set_chip_offset_len(dev, 2)); - ut_assertok(dm_i2c_write(dev, 0x420, (uint8_t *)"AB", 2)); - ut_assertok(dm_i2c_read(dev, 0x420, buf, 5)); - ut_assertok(memcmp(buf, "AB\0\0\0", sizeof(buf))); + sandbox_i2c_eeprom_set_offset_len(eeprom, 4); + ut_assertok(i2c_set_chip_offset_len(dev, 4)); + ut_assertok(dm_i2c_write(dev, 0x40404040, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x40404040, buf, 5)); + ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x40404040, sanbox_i2c_eeprom_get_prev_offset(eeprom)); /* Restore defaults */ sandbox_i2c_eeprom_set_offset_len(eeprom, 1); From 951674ac7e3db23eb3b2b87c27387364a9e38ca2 Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Mon, 28 Oct 2019 17:44:59 +0000 Subject: [PATCH 3/5] dm: i2c: EEPROM simulator add tests for addr offset mask Add support for setting the chip address offset mask to EEPROM sumulator and add tests to test it. Signed-off-by: Robert Beckett Reviewed-by: Heiko Schocher --- arch/sandbox/include/asm/test.h | 3 ++ drivers/misc/i2c_eeprom_emul.c | 19 +++++++--- test/dm/i2c.c | 65 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index f3a8b455d5..7dd9aa58c5 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -72,6 +72,9 @@ void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len); +void sandbox_i2c_eeprom_set_chip_addr_offset_mask(struct udevice *dev, + uint mask); + uint sanbox_i2c_eeprom_get_prev_addr(struct udevice *dev); uint sanbox_i2c_eeprom_get_prev_offset(struct udevice *dev); diff --git a/drivers/misc/i2c_eeprom_emul.c b/drivers/misc/i2c_eeprom_emul.c index 284267f2ea..16758a0ef9 100644 --- a/drivers/misc/i2c_eeprom_emul.c +++ b/drivers/misc/i2c_eeprom_emul.c @@ -23,6 +23,7 @@ struct sandbox_i2c_flash_plat_data { const char *filename; int offset_len; /* Length of an offset in bytes */ int size; /* Size of data buffer */ + uint chip_addr_offset_mask; /* mask of addr bits used for offset */ }; struct sandbox_i2c_flash { @@ -46,6 +47,14 @@ void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) plat->offset_len = offset_len; } +void sandbox_i2c_eeprom_set_chip_addr_offset_mask(struct udevice *dev, + uint mask) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); + + plat->chip_addr_offset_mask = mask; +} + uint sanbox_i2c_eeprom_get_prev_addr(struct udevice *dev) { struct sandbox_i2c_flash *priv = dev_get_priv(dev); @@ -64,7 +73,8 @@ static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, int nmsgs) { struct sandbox_i2c_flash *priv = dev_get_priv(emul); - uint offset = 0; + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(emul); + uint offset = msg->addr & plat->chip_addr_offset_mask; debug("\n%s\n", __func__); debug_buffer(0, priv->data, 1, 16, 0); @@ -73,17 +83,15 @@ static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, priv->prev_addr = msg->addr; for (; nmsgs > 0; nmsgs--, msg++) { - struct sandbox_i2c_flash_plat_data *plat = - dev_get_platdata(emul); int len; u8 *ptr; if (!plat->size) return -ENODEV; len = msg->len; - debug(" %s: msg->len=%d", + debug(" %s: msg->addr=%x msg->len=%d", msg->flags & I2C_M_RD ? "read" : "write", - msg->len); + msg->addr, msg->len); if (msg->flags & I2C_M_RD) { if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) len = 1; @@ -153,6 +161,7 @@ static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev) } plat->test_mode = SIE_TEST_MODE_NONE; plat->offset_len = 1; + plat->chip_addr_offset_mask = 0; return 0; } diff --git a/test/dm/i2c.c b/test/dm/i2c.c index bf5597b7a6..cadbb43b9e 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -241,3 +241,68 @@ static int dm_test_i2c_offset(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_i2c_offset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_i2c_addr_offset(struct unit_test_state *uts) +{ + struct udevice *eeprom; + struct udevice *dev; + u8 buf[5]; + + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); + + /* Do a transfer so we can find the emulator */ + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); + + /* Offset length 0 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 0); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 0)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x3, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x3, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x3, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Offset length 1 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 1); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 1)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x310, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x310, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x310, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Offset length 2 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 2); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 2)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x32020, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x32020, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x32020, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Offset length 3 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 3); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 3)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x3303030, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x3303030, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x3303030, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Restore defaults */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 1); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0); + + return 0; +} + +DM_TEST(dm_test_i2c_addr_offset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From 1a59cb426d50082821df941146a9ec20decd118f Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Mon, 28 Oct 2019 18:29:05 +0000 Subject: [PATCH 4/5] misc: i2c_eeprom: add fixed partitions support Add ability to partition eeprom via devicetree bindings Signed-off-by: Robert Beckett Reviewed-by: Heiko Schocher --- drivers/misc/i2c_eeprom.c | 98 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index 3755dbf74b..741bae2727 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/drivers/misc/i2c_eeprom.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,29 @@ static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev) return 0; } +static int i2c_eeprom_std_bind(struct udevice *dev) +{ + ofnode partitions = ofnode_find_subnode(dev_ofnode(dev), "partitions"); + ofnode partition; + const char *name; + + if (!ofnode_valid(partitions)) + return 0; + if (!ofnode_device_is_compatible(partitions, "fixed-partitions")) + return -ENOTSUPP; + + ofnode_for_each_subnode(partition, partitions) { + name = ofnode_get_name(partition); + if (!name) + continue; + + device_bind_ofnode(dev, DM_GET_DRIVER(i2c_eeprom_partition), + name, NULL, partition, NULL); + } + + return 0; +} + static int i2c_eeprom_std_probe(struct udevice *dev) { u8 test_byte; @@ -118,12 +142,86 @@ U_BOOT_DRIVER(i2c_eeprom_std) = { .name = "i2c_eeprom", .id = UCLASS_I2C_EEPROM, .of_match = i2c_eeprom_std_ids, + .bind = i2c_eeprom_std_bind, .probe = i2c_eeprom_std_probe, .ofdata_to_platdata = i2c_eeprom_std_ofdata_to_platdata, .priv_auto_alloc_size = sizeof(struct i2c_eeprom), .ops = &i2c_eeprom_std_ops, }; +struct i2c_eeprom_partition { + u32 offset; + u32 size; +}; + +static int i2c_eeprom_partition_probe(struct udevice *dev) +{ + return 0; +} + +static int i2c_eeprom_partition_ofdata_to_platdata(struct udevice *dev) +{ + struct i2c_eeprom_partition *priv = dev_get_priv(dev); + u32 offset, size; + int ret; + + ret = dev_read_u32(dev, "offset", &offset); + if (ret) + return ret; + + ret = dev_read_u32(dev, "size", &size); + if (ret) + return ret; + + priv->offset = offset; + priv->size = size; + + return 0; +} + +static int i2c_eeprom_partition_read(struct udevice *dev, int offset, + u8 *buf, int size) +{ + struct i2c_eeprom_partition *priv = dev_get_priv(dev); + struct udevice *parent = dev_get_parent(dev); + + if (!parent) + return -ENODEV; + if (offset + size > priv->size) + return -EINVAL; + + return i2c_eeprom_read(parent, offset + priv->offset, buf, size); +} + +static int i2c_eeprom_partition_write(struct udevice *dev, int offset, + const u8 *buf, int size) +{ + struct i2c_eeprom_partition *priv = dev_get_priv(dev); + struct udevice *parent = dev_get_parent(dev); + + if (!parent) + return -ENODEV; + if (offset + size > priv->size) + return -EINVAL; + + return i2c_eeprom_write(parent, offset + priv->offset, (uint8_t *)buf, + size); +} + +static const struct i2c_eeprom_ops i2c_eeprom_partition_ops = { + .read = i2c_eeprom_partition_read, + .write = i2c_eeprom_partition_write, +}; + +U_BOOT_DRIVER(i2c_eeprom_partition) = { + .name = "i2c_eeprom_partition", + .id = UCLASS_I2C_EEPROM, + .probe = i2c_eeprom_partition_probe, + .ofdata_to_platdata = i2c_eeprom_partition_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct i2c_eeprom_partition), + .ops = &i2c_eeprom_partition_ops, +}; + UCLASS_DRIVER(i2c_eeprom) = { .id = UCLASS_I2C_EEPROM, .name = "i2c_eeprom", From 033e18b47bd020610f84ca88c5ce3b88215d563a Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Mon, 28 Oct 2019 18:29:06 +0000 Subject: [PATCH 5/5] misc: i2c_eeprom: add size query Add ability to query size of eeprom device and partitions Signed-off-by: Robert Beckett Reviewed-by: Heiko Schocher --- drivers/misc/i2c_eeprom.c | 145 +++++++++++++++++++++++++++++++++----- include/i2c_eeprom.h | 12 ++++ 2 files changed, 138 insertions(+), 19 deletions(-) diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index 741bae2727..934f82074d 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/drivers/misc/i2c_eeprom.c @@ -12,6 +12,11 @@ #include #include +struct i2c_eeprom_drv_data { + u32 size; /* size in bytes */ + u32 pagewidth; /* pagesize = 2^pagewidth */ +}; + int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size) { const struct i2c_eeprom_ops *ops = device_get_ops(dev); @@ -32,6 +37,16 @@ int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size) return ops->write(dev, offset, buf, size); } +int i2c_eeprom_size(struct udevice *dev) +{ + const struct i2c_eeprom_ops *ops = device_get_ops(dev); + + if (!ops->size) + return -ENOSYS; + + return ops->size(dev); +} + static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf, int size) { @@ -61,25 +76,39 @@ static int i2c_eeprom_std_write(struct udevice *dev, int offset, return 0; } +static int i2c_eeprom_std_size(struct udevice *dev) +{ + struct i2c_eeprom *priv = dev_get_priv(dev); + + return priv->size; +} + static const struct i2c_eeprom_ops i2c_eeprom_std_ops = { .read = i2c_eeprom_std_read, .write = i2c_eeprom_std_write, + .size = i2c_eeprom_std_size, }; static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev) { struct i2c_eeprom *priv = dev_get_priv(dev); - u64 data = dev_get_driver_data(dev); + struct i2c_eeprom_drv_data *data = + (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev); u32 pagesize; + u32 size; if (dev_read_u32(dev, "pagesize", &pagesize) == 0) { priv->pagesize = pagesize; - return 0; + } else { + /* 6 bit -> page size of up to 2^63 (should be sufficient) */ + priv->pagewidth = data->pagewidth; + priv->pagesize = (1 << priv->pagewidth); } - /* 6 bit -> page size of up to 2^63 (should be sufficient) */ - priv->pagewidth = data & 0x3F; - priv->pagesize = (1 << priv->pagewidth); + if (dev_read_u32(dev, "size", &size) == 0) + priv->size = size; + else + priv->size = data->size; return 0; } @@ -120,21 +149,91 @@ static int i2c_eeprom_std_probe(struct udevice *dev) return 0; } +static const struct i2c_eeprom_drv_data eeprom_data = { + .size = 0, + .pagewidth = 0, +}; + +static const struct i2c_eeprom_drv_data mc24aa02e48_data = { + .size = 256, + .pagewidth = 3, +}; + +static const struct i2c_eeprom_drv_data atmel24c01a_data = { + .size = 128, + .pagewidth = 3, +}; + +static const struct i2c_eeprom_drv_data atmel24c02_data = { + .size = 256, + .pagewidth = 3, +}; + +static const struct i2c_eeprom_drv_data atmel24c04_data = { + .size = 512, + .pagewidth = 4, +}; + +static const struct i2c_eeprom_drv_data atmel24c08_data = { + .size = 1024, + .pagewidth = 4, +}; + +static const struct i2c_eeprom_drv_data atmel24c08a_data = { + .size = 1024, + .pagewidth = 4, +}; + +static const struct i2c_eeprom_drv_data atmel24c16a_data = { + .size = 2048, + .pagewidth = 4, +}; + +static const struct i2c_eeprom_drv_data atmel24mac402_data = { + .size = 256, + .pagewidth = 4, +}; + +static const struct i2c_eeprom_drv_data atmel24c32_data = { + .size = 4096, + .pagewidth = 5, +}; + +static const struct i2c_eeprom_drv_data atmel24c64_data = { + .size = 8192, + .pagewidth = 5, +}; + +static const struct i2c_eeprom_drv_data atmel24c128_data = { + .size = 16384, + .pagewidth = 6, +}; + +static const struct i2c_eeprom_drv_data atmel24c256_data = { + .size = 32768, + .pagewidth = 6, +}; + +static const struct i2c_eeprom_drv_data atmel24c512_data = { + .size = 65536, + .pagewidth = 6, +}; + static const struct udevice_id i2c_eeprom_std_ids[] = { - { .compatible = "i2c-eeprom", .data = 0 }, - { .compatible = "microchip,24aa02e48", .data = 3 }, - { .compatible = "atmel,24c01a", .data = 3 }, - { .compatible = "atmel,24c02", .data = 3 }, - { .compatible = "atmel,24c04", .data = 4 }, - { .compatible = "atmel,24c08", .data = 4 }, - { .compatible = "atmel,24c08a", .data = 4 }, - { .compatible = "atmel,24c16a", .data = 4 }, - { .compatible = "atmel,24mac402", .data = 4 }, - { .compatible = "atmel,24c32", .data = 5 }, - { .compatible = "atmel,24c64", .data = 5 }, - { .compatible = "atmel,24c128", .data = 6 }, - { .compatible = "atmel,24c256", .data = 6 }, - { .compatible = "atmel,24c512", .data = 6 }, + { .compatible = "i2c-eeprom", (ulong)&eeprom_data }, + { .compatible = "microchip,24aa02e48", (ulong)&mc24aa02e48_data }, + { .compatible = "atmel,24c01a", (ulong)&atmel24c01a_data }, + { .compatible = "atmel,24c02", (ulong)&atmel24c02_data }, + { .compatible = "atmel,24c04", (ulong)&atmel24c04_data }, + { .compatible = "atmel,24c08", (ulong)&atmel24c08_data }, + { .compatible = "atmel,24c08a", (ulong)&atmel24c08a_data }, + { .compatible = "atmel,24c16a", (ulong)&atmel24c16a_data }, + { .compatible = "atmel,24mac402", (ulong)&atmel24mac402_data }, + { .compatible = "atmel,24c32", (ulong)&atmel24c32_data }, + { .compatible = "atmel,24c64", (ulong)&atmel24c64_data }, + { .compatible = "atmel,24c128", (ulong)&atmel24c128_data }, + { .compatible = "atmel,24c256", (ulong)&atmel24c256_data }, + { .compatible = "atmel,24c512", (ulong)&atmel24c512_data }, { } }; @@ -208,9 +307,17 @@ static int i2c_eeprom_partition_write(struct udevice *dev, int offset, size); } +static int i2c_eeprom_partition_size(struct udevice *dev) +{ + struct i2c_eeprom_partition *priv = dev_get_priv(dev); + + return priv->size; +} + static const struct i2c_eeprom_ops i2c_eeprom_partition_ops = { .read = i2c_eeprom_partition_read, .write = i2c_eeprom_partition_write, + .size = i2c_eeprom_partition_size, }; U_BOOT_DRIVER(i2c_eeprom_partition) = { diff --git a/include/i2c_eeprom.h b/include/i2c_eeprom.h index 0fcdf3831b..b96254ae79 100644 --- a/include/i2c_eeprom.h +++ b/include/i2c_eeprom.h @@ -10,6 +10,7 @@ struct i2c_eeprom_ops { int (*read)(struct udevice *dev, int offset, uint8_t *buf, int size); int (*write)(struct udevice *dev, int offset, const uint8_t *buf, int size); + int (*size)(struct udevice *dev); }; struct i2c_eeprom { @@ -17,6 +18,8 @@ struct i2c_eeprom { unsigned long pagesize; /* The EEPROM's page width in bits (pagesize = 2^pagewidth) */ unsigned pagewidth; + /* The EEPROM's capacity in bytes */ + unsigned long size; }; /* @@ -43,4 +46,13 @@ int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size); */ int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size); +/* + * i2c_eeprom_size() - get size of I2C EEPROM chip + * + * @dev: Chip to query + * + * @return +ve size in bytes on success, -ve on failure + */ +int i2c_eeprom_size(struct udevice *dev); + #endif