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 <log.h> 13#include <malloc.h> 14#include <asm/test.h> 15 16#ifdef DEBUG 17#define debug_buffer print_buffer 18#else 19#define debug_buffer(x, ...) 20#endif 21 22struct sandbox_i2c_flash_plat_data { 23 enum sandbox_i2c_eeprom_test_mode test_mode; 24 const char *filename; 25 int offset_len; /* Length of an offset in bytes */ 26 int size; /* Size of data buffer */ 27 uint chip_addr_offset_mask; /* mask of addr bits used for offset */ 28}; 29 30struct sandbox_i2c_flash { 31 uint8_t *data; 32 uint prev_addr; /* slave address of previous access */ 33 uint prev_offset; /* offset of previous access */ 34}; 35 36void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, 37 enum sandbox_i2c_eeprom_test_mode mode) 38{ 39 struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); 40 41 plat->test_mode = mode; 42} 43 44void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) 45{ 46 struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); 47 48 plat->offset_len = offset_len; 49} 50 51void sandbox_i2c_eeprom_set_chip_addr_offset_mask(struct udevice *dev, 52 uint mask) 53{ 54 struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); 55 56 plat->chip_addr_offset_mask = mask; 57} 58 59uint sanbox_i2c_eeprom_get_prev_addr(struct udevice *dev) 60{ 61 struct sandbox_i2c_flash *priv = dev_get_priv(dev); 62 63 return priv->prev_addr; 64} 65 66uint sanbox_i2c_eeprom_get_prev_offset(struct udevice *dev) 67{ 68 struct sandbox_i2c_flash *priv = dev_get_priv(dev); 69 70 return priv->prev_offset; 71} 72 73static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, 74 int nmsgs) 75{ 76 struct sandbox_i2c_flash *priv = dev_get_priv(emul); 77 struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(emul); 78 uint offset = msg->addr & plat->chip_addr_offset_mask; 79 80 debug("\n%s\n", __func__); 81 debug_buffer(0, priv->data, 1, 16, 0); 82 83 /* store addr for testing visibity */ 84 priv->prev_addr = msg->addr; 85 86 for (; nmsgs > 0; nmsgs--, msg++) { 87 int len; 88 u8 *ptr; 89 90 if (!plat->size) 91 return -ENODEV; 92 len = msg->len; 93 debug(" %s: msg->addr=%x msg->len=%d", 94 msg->flags & I2C_M_RD ? "read" : "write", 95 msg->addr, msg->len); 96 if (msg->flags & I2C_M_RD) { 97 if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) 98 len = 1; 99 debug(", offset %x, len %x: ", offset, len); 100 if (offset + len > plat->size) { 101 int overflow = offset + len - plat->size; 102 int initial = len - overflow; 103 104 memcpy(msg->buf, priv->data + offset, initial); 105 memcpy(msg->buf + initial, priv->data, 106 overflow); 107 } else { 108 memcpy(msg->buf, priv->data + offset, len); 109 } 110 memset(msg->buf + len, '\xff', msg->len - len); 111 debug_buffer(0, msg->buf, 1, msg->len, 0); 112 } else if (len >= plat->offset_len) { 113 int i; 114 115 ptr = msg->buf; 116 for (i = 0; i < plat->offset_len; i++, len--) 117 offset = (offset << 8) | *ptr++; 118 debug(", set offset %x: ", offset); 119 debug_buffer(0, msg->buf, 1, msg->len, 0); 120 if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) 121 len = min(len, 1); 122 123 /* store offset for testing visibility */ 124 priv->prev_offset = offset; 125 126 /* For testing, map offsets into our limited buffer. 127 * offset wraps every 256 bytes 128 */ 129 offset &= 0xff; 130 debug("mapped offset to %x\n", offset); 131 132 if (offset + len > plat->size) { 133 int overflow = offset + len - plat->size; 134 int initial = len - overflow; 135 136 memcpy(priv->data + offset, ptr, initial); 137 memcpy(priv->data, ptr + initial, overflow); 138 } else { 139 memcpy(priv->data + offset, ptr, len); 140 } 141 } 142 } 143 debug_buffer(0, priv->data, 1, 16, 0); 144 145 return 0; 146} 147 148struct dm_i2c_ops sandbox_i2c_emul_ops = { 149 .xfer = sandbox_i2c_eeprom_xfer, 150}; 151 152static int sandbox_i2c_eeprom_of_to_plat(struct udevice *dev) 153{ 154 struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); 155 156 plat->size = dev_read_u32_default(dev, "sandbox,size", 32); 157 plat->filename = dev_read_string(dev, "sandbox,filename"); 158 if (!plat->filename) { 159 debug("%s: No filename for device '%s'\n", __func__, 160 dev->name); 161 return -EINVAL; 162 } 163 plat->test_mode = SIE_TEST_MODE_NONE; 164 plat->offset_len = 1; 165 plat->chip_addr_offset_mask = 0; 166 167 return 0; 168} 169 170static int sandbox_i2c_eeprom_probe(struct udevice *dev) 171{ 172 struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev); 173 struct sandbox_i2c_flash *priv = dev_get_priv(dev); 174 /* For eth3 */ 175 const u8 mac[] = { 0x02, 0x00, 0x11, 0x22, 0x33, 0x45 }; 176 177 priv->data = calloc(1, plat->size); 178 if (!priv->data) 179 return -ENOMEM; 180 181 memcpy(&priv->data[24], mac, sizeof(mac)); 182 183 return 0; 184} 185 186static int sandbox_i2c_eeprom_remove(struct udevice *dev) 187{ 188 struct sandbox_i2c_flash *priv = dev_get_priv(dev); 189 190 free(priv->data); 191 192 return 0; 193} 194 195static const struct udevice_id sandbox_i2c_ids[] = { 196 { .compatible = "sandbox,i2c-eeprom" }, 197 { } 198}; 199 200U_BOOT_DRIVER(sandbox_i2c_emul) = { 201 .name = "sandbox_i2c_eeprom_emul", 202 .id = UCLASS_I2C_EMUL, 203 .of_match = sandbox_i2c_ids, 204 .of_to_plat = sandbox_i2c_eeprom_of_to_plat, 205 .probe = sandbox_i2c_eeprom_probe, 206 .remove = sandbox_i2c_eeprom_remove, 207 .priv_auto = sizeof(struct sandbox_i2c_flash), 208 .plat_auto = sizeof(struct sandbox_i2c_flash_plat_data), 209 .ops = &sandbox_i2c_emul_ops, 210}; 211