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