diff options
Diffstat (limited to 'roms/u-boot/drivers/misc/i2c_eeprom_emul.c')
-rw-r--r-- | roms/u-boot/drivers/misc/i2c_eeprom_emul.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/misc/i2c_eeprom_emul.c b/roms/u-boot/drivers/misc/i2c_eeprom_emul.c new file mode 100644 index 000000000..85b127c40 --- /dev/null +++ b/roms/u-boot/drivers/misc/i2c_eeprom_emul.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Simulate an I2C eeprom + * + * Copyright (c) 2014 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <asm/test.h> + +#ifdef DEBUG +#define debug_buffer print_buffer +#else +#define debug_buffer(x, ...) +#endif + +struct sandbox_i2c_flash_plat_data { + enum sandbox_i2c_eeprom_test_mode test_mode; + 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 { + 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, + enum sandbox_i2c_eeprom_test_mode mode) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); + + plat->test_mode = mode; +} + +void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); + + 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_plat(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); + + 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) +{ + struct sandbox_i2c_flash *priv = dev_get_priv(emul); + struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(emul); + uint offset = msg->addr & plat->chip_addr_offset_mask; + + 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++) { + int len; + u8 *ptr; + + if (!plat->size) + return -ENODEV; + len = msg->len; + debug(" %s: msg->addr=%x msg->len=%d", + msg->flags & I2C_M_RD ? "read" : "write", + msg->addr, msg->len); + if (msg->flags & I2C_M_RD) { + if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) + len = 1; + debug(", offset %x, len %x: ", 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) { + int i; + + ptr = msg->buf; + for (i = 0; i < plat->offset_len; i++, len--) + offset = (offset << 8) | *ptr++; + debug(", set offset %x: ", offset); + debug_buffer(0, msg->buf, 1, msg->len, 0); + if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) + len = min(len, 1); + + /* 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); + } + } + } + debug_buffer(0, priv->data, 1, 16, 0); + + return 0; +} + +struct dm_i2c_ops sandbox_i2c_emul_ops = { + .xfer = sandbox_i2c_eeprom_xfer, +}; + +static int sandbox_i2c_eeprom_of_to_plat(struct udevice *dev) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); + + plat->size = dev_read_u32_default(dev, "sandbox,size", 32); + plat->filename = dev_read_string(dev, "sandbox,filename"); + if (!plat->filename) { + debug("%s: No filename for device '%s'\n", __func__, + dev->name); + return -EINVAL; + } + plat->test_mode = SIE_TEST_MODE_NONE; + plat->offset_len = 1; + plat->chip_addr_offset_mask = 0; + + return 0; +} + +static int sandbox_i2c_eeprom_probe(struct udevice *dev) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); + struct sandbox_i2c_flash *priv = dev_get_priv(dev); + + priv->data = calloc(1, plat->size); + if (!priv->data) + return -ENOMEM; + + return 0; +} + +static int sandbox_i2c_eeprom_remove(struct udevice *dev) +{ + struct sandbox_i2c_flash *priv = dev_get_priv(dev); + + free(priv->data); + + return 0; +} + +static const struct udevice_id sandbox_i2c_ids[] = { + { .compatible = "sandbox,i2c-eeprom" }, + { } +}; + +U_BOOT_DRIVER(sandbox_i2c_emul) = { + .name = "sandbox_i2c_eeprom_emul", + .id = UCLASS_I2C_EMUL, + .of_match = sandbox_i2c_ids, + .of_to_plat = sandbox_i2c_eeprom_of_to_plat, + .probe = sandbox_i2c_eeprom_probe, + .remove = sandbox_i2c_eeprom_remove, + .priv_auto = sizeof(struct sandbox_i2c_flash), + .plat_auto = sizeof(struct sandbox_i2c_flash_plat_data), + .ops = &sandbox_i2c_emul_ops, +}; |