]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6ec1b753 SG |
2 | /* |
3 | * Simulate an I2C eeprom | |
4 | * | |
5 | * Copyright (c) 2014 Google, Inc | |
6ec1b753 SG |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <dm.h> | |
38068820 | 10 | #include <errno.h> |
6ec1b753 | 11 | #include <i2c.h> |
f7ae49fc | 12 | #include <log.h> |
6ec1b753 SG |
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 | ||
6ec1b753 SG |
22 | struct 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 */ | |
951674ac | 27 | uint chip_addr_offset_mask; /* mask of addr bits used for offset */ |
6ec1b753 SG |
28 | }; |
29 | ||
30 | struct sandbox_i2c_flash { | |
31 | uint8_t *data; | |
22e93511 RB |
32 | uint prev_addr; /* slave address of previous access */ |
33 | uint prev_offset; /* offset of previous access */ | |
6ec1b753 SG |
34 | }; |
35 | ||
36 | void 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_platdata(dev); | |
40 | ||
41 | plat->test_mode = mode; | |
42 | } | |
43 | ||
44 | void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) | |
45 | { | |
46 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
47 | ||
48 | plat->offset_len = offset_len; | |
49 | } | |
50 | ||
951674ac RB |
51 | void 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_platdata(dev); | |
55 | ||
56 | plat->chip_addr_offset_mask = mask; | |
57 | } | |
58 | ||
22e93511 RB |
59 | uint 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 | ||
66 | uint 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 | ||
6ec1b753 SG |
73 | static 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); | |
951674ac RB |
77 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(emul); |
78 | uint offset = msg->addr & plat->chip_addr_offset_mask; | |
6ec1b753 SG |
79 | |
80 | debug("\n%s\n", __func__); | |
81 | debug_buffer(0, priv->data, 1, 16, 0); | |
22e93511 RB |
82 | |
83 | /* store addr for testing visibity */ | |
84 | priv->prev_addr = msg->addr; | |
85 | ||
6ec1b753 | 86 | for (; nmsgs > 0; nmsgs--, msg++) { |
6ec1b753 SG |
87 | int len; |
88 | u8 *ptr; | |
89 | ||
90 | if (!plat->size) | |
91 | return -ENODEV; | |
6ec1b753 | 92 | len = msg->len; |
951674ac | 93 | debug(" %s: msg->addr=%x msg->len=%d", |
6ec1b753 | 94 | msg->flags & I2C_M_RD ? "read" : "write", |
951674ac | 95 | msg->addr, msg->len); |
6ec1b753 SG |
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); | |
22e93511 RB |
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 | } | |
6ec1b753 SG |
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 | ||
22e93511 RB |
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); | |
6ec1b753 | 140 | } |
6ec1b753 SG |
141 | } |
142 | } | |
143 | debug_buffer(0, priv->data, 1, 16, 0); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | struct dm_i2c_ops sandbox_i2c_emul_ops = { | |
149 | .xfer = sandbox_i2c_eeprom_xfer, | |
150 | }; | |
151 | ||
152 | static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev) | |
153 | { | |
154 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
155 | ||
2dd57f5e SG |
156 | plat->size = dev_read_u32_default(dev, "sandbox,size", 32); |
157 | plat->filename = dev_read_string(dev, "sandbox,filename"); | |
6ec1b753 SG |
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; | |
951674ac | 165 | plat->chip_addr_offset_mask = 0; |
6ec1b753 SG |
166 | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static int sandbox_i2c_eeprom_probe(struct udevice *dev) | |
171 | { | |
172 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
173 | struct sandbox_i2c_flash *priv = dev_get_priv(dev); | |
174 | ||
175 | priv->data = calloc(1, plat->size); | |
176 | if (!priv->data) | |
177 | return -ENOMEM; | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | static int sandbox_i2c_eeprom_remove(struct udevice *dev) | |
183 | { | |
184 | struct sandbox_i2c_flash *priv = dev_get_priv(dev); | |
185 | ||
186 | free(priv->data); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static const struct udevice_id sandbox_i2c_ids[] = { | |
192 | { .compatible = "sandbox,i2c-eeprom" }, | |
193 | { } | |
194 | }; | |
195 | ||
196 | U_BOOT_DRIVER(sandbox_i2c_emul) = { | |
197 | .name = "sandbox_i2c_eeprom_emul", | |
198 | .id = UCLASS_I2C_EMUL, | |
199 | .of_match = sandbox_i2c_ids, | |
200 | .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata, | |
201 | .probe = sandbox_i2c_eeprom_probe, | |
202 | .remove = sandbox_i2c_eeprom_remove, | |
203 | .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash), | |
204 | .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data), | |
205 | .ops = &sandbox_i2c_emul_ops, | |
206 | }; |