1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Simulate an I2C eeprom 4 * 5 * Copyright (c) 2014 Google, Inc 6 */ 7 8 #include <common.h> 9 #include <dm.h> 10 #include <errno.h> 11 #include <i2c.h> 12 #include <malloc.h> 13 #include <asm/test.h> 14 15 #ifdef DEBUG 16 #define debug_buffer print_buffer 17 #else 18 #define debug_buffer(x, ...) 19 #endif 20 21 struct sandbox_i2c_flash_plat_data { 22 enum sandbox_i2c_eeprom_test_mode test_mode; 23 const char *filename; 24 int offset_len; /* Length of an offset in bytes */ 25 int size; /* Size of data buffer */ 26 }; 27 28 struct sandbox_i2c_flash { 29 uint8_t *data; 30 }; 31 32 void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, 33 enum sandbox_i2c_eeprom_test_mode mode) 34 { 35 struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); 36 37 plat->test_mode = mode; 38 } 39 40 void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) 41 { 42 struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); 43 44 plat->offset_len = offset_len; 45 } 46 47 static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, 48 int nmsgs) 49 { 50 struct sandbox_i2c_flash *priv = dev_get_priv(emul); 51 uint offset = 0; 52 53 debug("\n%s\n", __func__); 54 debug_buffer(0, priv->data, 1, 16, 0); 55 for (; nmsgs > 0; nmsgs--, msg++) { 56 struct sandbox_i2c_flash_plat_data *plat = 57 dev_get_platdata(emul); 58 int len; 59 u8 *ptr; 60 61 if (!plat->size) 62 return -ENODEV; 63 if (msg->addr + msg->len > plat->size) { 64 debug("%s: Address %x, len %x is outside range 0..%x\n", 65 __func__, msg->addr, msg->len, plat->size); 66 return -EINVAL; 67 } 68 len = msg->len; 69 debug(" %s: msg->len=%d", 70 msg->flags & I2C_M_RD ? "read" : "write", 71 msg->len); 72 if (msg->flags & I2C_M_RD) { 73 if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) 74 len = 1; 75 debug(", offset %x, len %x: ", offset, len); 76 memcpy(msg->buf, priv->data + offset, len); 77 memset(msg->buf + len, '\xff', msg->len - len); 78 debug_buffer(0, msg->buf, 1, msg->len, 0); 79 } else if (len >= plat->offset_len) { 80 int i; 81 82 ptr = msg->buf; 83 for (i = 0; i < plat->offset_len; i++, len--) 84 offset = (offset << 8) | *ptr++; 85 debug(", set offset %x: ", offset); 86 debug_buffer(0, msg->buf, 1, msg->len, 0); 87 if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) 88 len = min(len, 1); 89 90 /* For testing, map offsets into our limited buffer */ 91 for (i = 24; i > 0; i -= 8) { 92 if (offset > (1 << i)) { 93 offset = (offset >> i) | 94 (offset & ((1 << i) - 1)); 95 offset += i; 96 } 97 } 98 memcpy(priv->data + offset, ptr, len); 99 } 100 } 101 debug_buffer(0, priv->data, 1, 16, 0); 102 103 return 0; 104 } 105 106 struct dm_i2c_ops sandbox_i2c_emul_ops = { 107 .xfer = sandbox_i2c_eeprom_xfer, 108 }; 109 110 static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev) 111 { 112 struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); 113 114 plat->size = dev_read_u32_default(dev, "sandbox,size", 32); 115 plat->filename = dev_read_string(dev, "sandbox,filename"); 116 if (!plat->filename) { 117 debug("%s: No filename for device '%s'\n", __func__, 118 dev->name); 119 return -EINVAL; 120 } 121 plat->test_mode = SIE_TEST_MODE_NONE; 122 plat->offset_len = 1; 123 124 return 0; 125 } 126 127 static int sandbox_i2c_eeprom_probe(struct udevice *dev) 128 { 129 struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); 130 struct sandbox_i2c_flash *priv = dev_get_priv(dev); 131 132 priv->data = calloc(1, plat->size); 133 if (!priv->data) 134 return -ENOMEM; 135 136 return 0; 137 } 138 139 static int sandbox_i2c_eeprom_remove(struct udevice *dev) 140 { 141 struct sandbox_i2c_flash *priv = dev_get_priv(dev); 142 143 free(priv->data); 144 145 return 0; 146 } 147 148 static const struct udevice_id sandbox_i2c_ids[] = { 149 { .compatible = "sandbox,i2c-eeprom" }, 150 { } 151 }; 152 153 U_BOOT_DRIVER(sandbox_i2c_emul) = { 154 .name = "sandbox_i2c_eeprom_emul", 155 .id = UCLASS_I2C_EMUL, 156 .of_match = sandbox_i2c_ids, 157 .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata, 158 .probe = sandbox_i2c_eeprom_probe, 159 .remove = sandbox_i2c_eeprom_remove, 160 .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash), 161 .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data), 162 .ops = &sandbox_i2c_emul_ops, 163 }; 164