1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include "qemu/osdep.h"
22#include "hw/i2c/i2c.h"
23#include "hw/hw.h"
24#include "sysemu/blockdev.h"
25#include "sysemu/block-backend.h"
26#include "qemu/log.h"
27#include "qapi/error.h"
28#include "hw/block/m24cxx.h"
29#include "migration/vmstate.h"
30#include "hw/qdev-properties.h"
31#include "hw/qdev-properties-system.h"
32
33#ifndef M24CXX_DEBUG
34#define M24CXX_DEBUG 0
35#endif
36#define DB_PRINT(fmt, args...) do {\
37 if (M24CXX_DEBUG) {\
38 fprintf(stderr, "M24CXX: %s:" fmt, __func__, ## args);\
39 } \
40} while (0);
41
42const char *m24cxx_state_names[] = {
43 [STOPPED] = "STOPPED",
44 [ADDRESSING] = "ADDRESSING",
45 [READING] = "READING",
46 [WRITING] = "WRITING",
47};
48
49static void m24cxx_sync_complete(void *opaque, int ret)
50{
51 QEMUIOVector *iov = opaque;
52
53 qemu_iovec_destroy(iov);
54 g_free(iov);
55
56
57
58}
59
60static inline bool m24cxx_uses_i2c_addr(M24CXXState *s)
61{
62 return (s->size >> 8) && !(s->size >> 11);
63}
64
65static void m24cxx_sync(I2CSlave *i2c)
66{
67 M24CXXState *s = M24CXX(i2c);
68 int64_t nb_sectors;
69 QEMUIOVector *iov;
70
71 if (!s->blk) {
72 return;
73 }
74
75 iov = g_new(QEMUIOVector, 1);
76 nb_sectors = DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE);
77
78
79 qemu_iovec_init(iov, 1);
80 qemu_iovec_add(iov, s->storage, nb_sectors * BDRV_SECTOR_SIZE);
81 blk_aio_pwritev(s->blk, nb_sectors * BDRV_SECTOR_SIZE, iov, 0, m24cxx_sync_complete, iov);
82}
83
84static void m24cxx_reset(DeviceState *dev)
85{
86 M24CXXState *s = M24CXX(dev);
87
88 m24cxx_sync(I2C_SLAVE(s));
89 s->state = STOPPED;
90 s->cur_addr = 0;
91}
92
93static uint8_t m24cxx_recv(I2CSlave *i2c)
94{
95 M24CXXState *s = M24CXX(i2c);
96 int ret = 0;
97
98 if (s->state == READING) {
99 ret = s->storage[s->cur_addr++];
100 DB_PRINT("storage %x <-> %x\n", s->cur_addr - 1, ret);
101 s->cur_addr %= s->size;
102 } else {
103
104 qemu_log_mask(LOG_GUEST_ERROR, "read from m24cxx not in read state");
105 }
106 DB_PRINT("data: %02x\n", ret);
107 return ret;
108}
109
110static int m24cxx_send(I2CSlave *i2c, uint8_t data)
111{
112 M24CXXState *s = M24CXX(i2c);
113
114 switch (s->state) {
115 case (ADDRESSING):
116 if (!s->addr_count) {
117 s->cur_addr = 0;
118 }
119 s->cur_addr = deposit32(s->cur_addr,
120 (s->num_addr_bytes - s->addr_count - 1) * 8,
121 8, data);
122 s->addr_count++;
123 if (s->addr_count == s->num_addr_bytes) {
124 s->state = WRITING;
125 s->addr_count = 0;
126 }
127 return 0;
128 case (WRITING):
129 DB_PRINT("storage %x <-> %x\n", s->cur_addr, data);
130 s->storage[s->cur_addr++] = data;
131 s->cur_addr %= s->size;
132 return 0;
133 default:
134 DB_PRINT("write to m24cxx not in writable state\n");
135 qemu_log_mask(LOG_GUEST_ERROR, "write to m24cxx not in writable state");
136 return 1;
137 }
138}
139
140static int m24cxx_event(I2CSlave *i2c, enum i2c_event event)
141{
142 M24CXXState *s = M24CXX(i2c);
143
144 switch (event) {
145 case I2C_START_SEND:
146 s->state = ADDRESSING;
147 break;
148 case I2C_START_RECV:
149 s->state = READING;
150 break;
151 case I2C_FINISH:
152 m24cxx_sync(i2c);
153 s->state = STOPPED;
154 break;
155 case I2C_NACK:
156 DB_PRINT("NACKED\n");
157 break;
158 default:
159 return -1;
160 }
161
162 DB_PRINT("transitioning to state %s\n", m24cxx_state_names[s->state]);
163
164 return 0;
165}
166
167static int m24cxx_decode_address(I2CSlave *i2c, uint8_t address)
168{
169 M24CXXState *s = M24CXX(i2c);
170
171 if (m24cxx_uses_i2c_addr(s)) {
172 s->cur_addr &= ~(0x0700);
173 deposit32(s->cur_addr, 0, 3, ((s->size >> 8) - 1) & address);
174 }
175 return 0;
176}
177
178static void m24cxx_realize(DeviceState *dev, Error **errp)
179{
180 M24CXXState *s = M24CXX(dev);
181 I2CSlave *i2c = I2C_SLAVE(dev);
182
183 i2c->address_range = m24cxx_uses_i2c_addr(s) ? s->size >> 8 : 1;
184 s->num_addr_bytes = s->size >> 11 ? 2 : 1;
185 s->storage = g_new0(uint8_t, DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE) *
186 BDRV_SECTOR_SIZE);
187
188 if (s->blk) {
189
190 if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) {
191 error_setg(errp, "Failed to initialize I2C EEPROM!\n");
192 return;
193 }
194 } else {
195 memset(s->storage, 0xFF, s->size);
196 }
197}
198
199static int m24cxx_pre_save(void *opaque)
200{
201 m24cxx_sync((I2CSlave *)opaque);
202
203 return 0;
204}
205
206static const VMStateDescription vmstate_m24cxx = {
207 .name = "m24cxx",
208 .version_id = 1,
209 .minimum_version_id = 1,
210 .pre_save = m24cxx_pre_save,
211 .fields = (VMStateField[]) {
212 VMSTATE_I2C_SLAVE(i2c, M24CXXState),
213 VMSTATE_UINT8(state, M24CXXState),
214 VMSTATE_UINT16(cur_addr, M24CXXState),
215 VMSTATE_END_OF_LIST()
216 }
217};
218
219static Property m24cxx_properties[] = {
220 DEFINE_PROP_UINT16("size", M24CXXState, size, 1024),
221 DEFINE_PROP_DRIVE("drive", M24CXXState, blk),
222 DEFINE_PROP_END_OF_LIST(),
223};
224
225static void m24cxx_class_init(ObjectClass *klass, void *data)
226{
227 DeviceClass *dc = DEVICE_CLASS(klass);
228 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
229
230 k->event = m24cxx_event;
231 k->recv = m24cxx_recv;
232 k->send = m24cxx_send;
233 k->decode_address = m24cxx_decode_address;
234
235 dc->realize = m24cxx_realize;
236 dc->reset = m24cxx_reset;
237 dc->vmsd = &vmstate_m24cxx;
238 device_class_set_props(dc, m24cxx_properties);
239}
240
241static TypeInfo m24cxx_info = {
242 .name = TYPE_M24CXX,
243 .parent = TYPE_I2C_SLAVE,
244 .instance_size = sizeof(M24CXXState),
245 .class_init = m24cxx_class_init,
246};
247
248static const TypeInfo m24cxx_qom_aliases[] = {
249 { .name = "at.24c08", .parent = TYPE_M24CXX },
250 { .name = "at.24c16", .parent = TYPE_M24CXX },
251 { .name = "at.24c32", .parent = TYPE_M24CXX },
252 { .name = "at.24c64", .parent = TYPE_M24CXX },
253};
254
255static void m24cxx_register_types(void)
256{
257 int i;
258
259 type_register_static(&m24cxx_info);
260 for (i = 0; i < ARRAY_SIZE(m24cxx_qom_aliases); ++i) {
261 type_register_static(&m24cxx_qom_aliases[i]);
262 }
263}
264
265type_init(m24cxx_register_types)
266