16ec1b753SSimon Glass /*
26ec1b753SSimon Glass * Simulate an I2C eeprom
36ec1b753SSimon Glass *
46ec1b753SSimon Glass * Copyright (c) 2014 Google, Inc
56ec1b753SSimon Glass *
66ec1b753SSimon Glass * SPDX-License-Identifier: GPL-2.0+
76ec1b753SSimon Glass */
86ec1b753SSimon Glass
96ec1b753SSimon Glass #include <common.h>
106ec1b753SSimon Glass #include <dm.h>
1138068820SSimon Glass #include <errno.h>
126ec1b753SSimon Glass #include <i2c.h>
136ec1b753SSimon Glass #include <malloc.h>
146ec1b753SSimon Glass #include <asm/test.h>
156ec1b753SSimon Glass
166ec1b753SSimon Glass #ifdef DEBUG
176ec1b753SSimon Glass #define debug_buffer print_buffer
186ec1b753SSimon Glass #else
196ec1b753SSimon Glass #define debug_buffer(x, ...)
206ec1b753SSimon Glass #endif
216ec1b753SSimon Glass
226ec1b753SSimon Glass DECLARE_GLOBAL_DATA_PTR;
236ec1b753SSimon Glass
246ec1b753SSimon Glass struct sandbox_i2c_flash_plat_data {
256ec1b753SSimon Glass enum sandbox_i2c_eeprom_test_mode test_mode;
266ec1b753SSimon Glass const char *filename;
276ec1b753SSimon Glass int offset_len; /* Length of an offset in bytes */
286ec1b753SSimon Glass int size; /* Size of data buffer */
296ec1b753SSimon Glass };
306ec1b753SSimon Glass
316ec1b753SSimon Glass struct sandbox_i2c_flash {
326ec1b753SSimon Glass uint8_t *data;
336ec1b753SSimon Glass };
346ec1b753SSimon Glass
sandbox_i2c_eeprom_set_test_mode(struct udevice * dev,enum sandbox_i2c_eeprom_test_mode mode)356ec1b753SSimon Glass void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev,
366ec1b753SSimon Glass enum sandbox_i2c_eeprom_test_mode mode)
376ec1b753SSimon Glass {
386ec1b753SSimon Glass struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
396ec1b753SSimon Glass
406ec1b753SSimon Glass plat->test_mode = mode;
416ec1b753SSimon Glass }
426ec1b753SSimon Glass
sandbox_i2c_eeprom_set_offset_len(struct udevice * dev,int offset_len)436ec1b753SSimon Glass void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len)
446ec1b753SSimon Glass {
456ec1b753SSimon Glass struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
466ec1b753SSimon Glass
476ec1b753SSimon Glass plat->offset_len = offset_len;
486ec1b753SSimon Glass }
496ec1b753SSimon Glass
sandbox_i2c_eeprom_xfer(struct udevice * emul,struct i2c_msg * msg,int nmsgs)506ec1b753SSimon Glass static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg,
516ec1b753SSimon Glass int nmsgs)
526ec1b753SSimon Glass {
536ec1b753SSimon Glass struct sandbox_i2c_flash *priv = dev_get_priv(emul);
546ec1b753SSimon Glass uint offset = 0;
556ec1b753SSimon Glass
566ec1b753SSimon Glass debug("\n%s\n", __func__);
576ec1b753SSimon Glass debug_buffer(0, priv->data, 1, 16, 0);
586ec1b753SSimon Glass for (; nmsgs > 0; nmsgs--, msg++) {
596ec1b753SSimon Glass struct sandbox_i2c_flash_plat_data *plat =
606ec1b753SSimon Glass dev_get_platdata(emul);
616ec1b753SSimon Glass int len;
626ec1b753SSimon Glass u8 *ptr;
636ec1b753SSimon Glass
646ec1b753SSimon Glass if (!plat->size)
656ec1b753SSimon Glass return -ENODEV;
666ec1b753SSimon Glass if (msg->addr + msg->len > plat->size) {
676ec1b753SSimon Glass debug("%s: Address %x, len %x is outside range 0..%x\n",
686ec1b753SSimon Glass __func__, msg->addr, msg->len, plat->size);
696ec1b753SSimon Glass return -EINVAL;
706ec1b753SSimon Glass }
716ec1b753SSimon Glass len = msg->len;
726ec1b753SSimon Glass debug(" %s: msg->len=%d",
736ec1b753SSimon Glass msg->flags & I2C_M_RD ? "read" : "write",
746ec1b753SSimon Glass msg->len);
756ec1b753SSimon Glass if (msg->flags & I2C_M_RD) {
766ec1b753SSimon Glass if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE)
776ec1b753SSimon Glass len = 1;
786ec1b753SSimon Glass debug(", offset %x, len %x: ", offset, len);
796ec1b753SSimon Glass memcpy(msg->buf, priv->data + offset, len);
806ec1b753SSimon Glass memset(msg->buf + len, '\xff', msg->len - len);
816ec1b753SSimon Glass debug_buffer(0, msg->buf, 1, msg->len, 0);
826ec1b753SSimon Glass } else if (len >= plat->offset_len) {
836ec1b753SSimon Glass int i;
846ec1b753SSimon Glass
856ec1b753SSimon Glass ptr = msg->buf;
866ec1b753SSimon Glass for (i = 0; i < plat->offset_len; i++, len--)
876ec1b753SSimon Glass offset = (offset << 8) | *ptr++;
886ec1b753SSimon Glass debug(", set offset %x: ", offset);
896ec1b753SSimon Glass debug_buffer(0, msg->buf, 1, msg->len, 0);
906ec1b753SSimon Glass if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE)
916ec1b753SSimon Glass len = min(len, 1);
926ec1b753SSimon Glass
936ec1b753SSimon Glass /* For testing, map offsets into our limited buffer */
946ec1b753SSimon Glass for (i = 24; i > 0; i -= 8) {
956ec1b753SSimon Glass if (offset > (1 << i)) {
966ec1b753SSimon Glass offset = (offset >> i) |
976ec1b753SSimon Glass (offset & ((1 << i) - 1));
986ec1b753SSimon Glass offset += i;
996ec1b753SSimon Glass }
1006ec1b753SSimon Glass }
1016ec1b753SSimon Glass memcpy(priv->data + offset, ptr, len);
1026ec1b753SSimon Glass }
1036ec1b753SSimon Glass }
1046ec1b753SSimon Glass debug_buffer(0, priv->data, 1, 16, 0);
1056ec1b753SSimon Glass
1066ec1b753SSimon Glass return 0;
1076ec1b753SSimon Glass }
1086ec1b753SSimon Glass
1096ec1b753SSimon Glass struct dm_i2c_ops sandbox_i2c_emul_ops = {
1106ec1b753SSimon Glass .xfer = sandbox_i2c_eeprom_xfer,
1116ec1b753SSimon Glass };
1126ec1b753SSimon Glass
sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice * dev)1136ec1b753SSimon Glass static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev)
1146ec1b753SSimon Glass {
1156ec1b753SSimon Glass struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
1166ec1b753SSimon Glass
117*2dd57f5eSSimon Glass plat->size = dev_read_u32_default(dev, "sandbox,size", 32);
118*2dd57f5eSSimon Glass plat->filename = dev_read_string(dev, "sandbox,filename");
1196ec1b753SSimon Glass if (!plat->filename) {
1206ec1b753SSimon Glass debug("%s: No filename for device '%s'\n", __func__,
1216ec1b753SSimon Glass dev->name);
1226ec1b753SSimon Glass return -EINVAL;
1236ec1b753SSimon Glass }
1246ec1b753SSimon Glass plat->test_mode = SIE_TEST_MODE_NONE;
1256ec1b753SSimon Glass plat->offset_len = 1;
1266ec1b753SSimon Glass
1276ec1b753SSimon Glass return 0;
1286ec1b753SSimon Glass }
1296ec1b753SSimon Glass
sandbox_i2c_eeprom_probe(struct udevice * dev)1306ec1b753SSimon Glass static int sandbox_i2c_eeprom_probe(struct udevice *dev)
1316ec1b753SSimon Glass {
1326ec1b753SSimon Glass struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
1336ec1b753SSimon Glass struct sandbox_i2c_flash *priv = dev_get_priv(dev);
1346ec1b753SSimon Glass
1356ec1b753SSimon Glass priv->data = calloc(1, plat->size);
1366ec1b753SSimon Glass if (!priv->data)
1376ec1b753SSimon Glass return -ENOMEM;
1386ec1b753SSimon Glass
1396ec1b753SSimon Glass return 0;
1406ec1b753SSimon Glass }
1416ec1b753SSimon Glass
sandbox_i2c_eeprom_remove(struct udevice * dev)1426ec1b753SSimon Glass static int sandbox_i2c_eeprom_remove(struct udevice *dev)
1436ec1b753SSimon Glass {
1446ec1b753SSimon Glass struct sandbox_i2c_flash *priv = dev_get_priv(dev);
1456ec1b753SSimon Glass
1466ec1b753SSimon Glass free(priv->data);
1476ec1b753SSimon Glass
1486ec1b753SSimon Glass return 0;
1496ec1b753SSimon Glass }
1506ec1b753SSimon Glass
1516ec1b753SSimon Glass static const struct udevice_id sandbox_i2c_ids[] = {
1526ec1b753SSimon Glass { .compatible = "sandbox,i2c-eeprom" },
1536ec1b753SSimon Glass { }
1546ec1b753SSimon Glass };
1556ec1b753SSimon Glass
1566ec1b753SSimon Glass U_BOOT_DRIVER(sandbox_i2c_emul) = {
1576ec1b753SSimon Glass .name = "sandbox_i2c_eeprom_emul",
1586ec1b753SSimon Glass .id = UCLASS_I2C_EMUL,
1596ec1b753SSimon Glass .of_match = sandbox_i2c_ids,
1606ec1b753SSimon Glass .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata,
1616ec1b753SSimon Glass .probe = sandbox_i2c_eeprom_probe,
1626ec1b753SSimon Glass .remove = sandbox_i2c_eeprom_remove,
1636ec1b753SSimon Glass .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash),
1646ec1b753SSimon Glass .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data),
1656ec1b753SSimon Glass .ops = &sandbox_i2c_emul_ops,
1666ec1b753SSimon Glass };
167