From 6ec1b7535873dee4c73895e430494e12be4e3e4d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 10 Dec 2014 08:55:51 -0700 Subject: dm: i2c: Add an I2C EEPROM simulator To enable testing of I2C, add a simple I2C EEPROM simulator for sandbox. It supports reading and writing from a small data store. Signed-off-by: Simon Glass Acked-by: Heiko Schocher Reviewed-by: Masahiro Yamada diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h new file mode 100644 index 0000000..25a0c85 --- /dev/null +++ b/arch/sandbox/include/asm/test.h @@ -0,0 +1,26 @@ +/* + * Test-related constants for sandbox + * + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_TEST_H +#define __ASM_TEST_H + +/* The sandbox driver always permits an I2C device with this address */ +#define SANDBOX_I2C_TEST_ADDR 0x59 + +enum sandbox_i2c_eeprom_test_mode { + SIE_TEST_MODE_NONE, + /* Permits read/write of only one byte per I2C transaction */ + SIE_TEST_MODE_SINGLE_BYTE, +}; + +void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, + enum sandbox_i2c_eeprom_test_mode mode); + +void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len); + +#endif diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2f2e48f..ff02184 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,6 +20,9 @@ obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NS87308) += ns87308.o obj-$(CONFIG_PDSP188x) += pdsp188x.o +ifdef CONFIG_DM_I2C +obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o +endif obj-$(CONFIG_STATUS_LED) += status_led.o obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o diff --git a/drivers/misc/i2c_eeprom_emul.c b/drivers/misc/i2c_eeprom_emul.c new file mode 100644 index 0000000..7343445 --- /dev/null +++ b/drivers/misc/i2c_eeprom_emul.c @@ -0,0 +1,168 @@ +/* + * Simulate an I2C eeprom + * + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define debug_buffer print_buffer +#else +#define debug_buffer(x, ...) +#endif + +DECLARE_GLOBAL_DATA_PTR; + +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 */ +}; + +struct sandbox_i2c_flash { + uint8_t *data; +}; + +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_platdata(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_platdata(dev); + + plat->offset_len = offset_len; +} + +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; + + debug("\n%s\n", __func__); + debug_buffer(0, priv->data, 1, 16, 0); + 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; + 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", + 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); + 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); + + /* 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; + } + } + 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_ofdata_to_platdata(struct udevice *dev) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); + + plat->size = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "sandbox,size", 32); + plat->filename = fdt_getprop(gd->fdt_blob, dev->of_offset, + "sandbox,filename", NULL); + 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; + + return 0; +} + +static int sandbox_i2c_eeprom_probe(struct udevice *dev) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(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, + .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata, + .probe = sandbox_i2c_eeprom_probe, + .remove = sandbox_i2c_eeprom_remove, + .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash), + .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data), + .ops = &sandbox_i2c_emul_ops, +}; -- cgit v0.10.2