2 * *AT24C* series I2C EEPROM
4 * Copyright (c) 2015 Michael Davidsaver
6 * This work is licensed under the terms of the GNU GPL, version 2. See
7 * the LICENSE file in the top-level directory.
10 #include "qemu/osdep.h"
12 #include "qapi/error.h"
13 #include "qemu/module.h"
14 #include "hw/i2c/i2c.h"
15 #include "hw/nvram/eeprom_at24c.h"
16 #include "hw/qdev-properties.h"
17 #include "hw/qdev-properties-system.h"
18 #include "sysemu/block-backend.h"
19 #include "qom/object.h"
21 /* #define DEBUG_AT24C */
24 #define DPRINTK(FMT, ...) printf(TYPE_AT24C_EE " : " FMT, ## __VA_ARGS__)
26 #define DPRINTK(FMT, ...) do {} while (0)
29 #define ERR(FMT, ...) fprintf(stderr, TYPE_AT24C_EE " : " FMT, \
32 #define TYPE_AT24C_EE "at24c-eeprom"
33 typedef struct EEPROMState EEPROMState
;
34 DECLARE_INSTANCE_CHECKER(EEPROMState
, AT24C_EE
,
42 /* total size in bytes */
46 * for 24c01, 24c02 size <= 256 byte, use only 1 byte
47 * otherwise size > 256, use 2 byte
52 /* cells changed since last START? */
54 /* during WRITE, # of address bytes transferred */
61 const uint8_t *init_rom
;
62 uint32_t init_rom_size
;
66 int at24c_eeprom_event(I2CSlave
*s
, enum i2c_event event
)
68 EEPROMState
*ee
= AT24C_EE(s
);
77 if (ee
->blk
&& ee
->changed
) {
78 int ret
= blk_pwrite(ee
->blk
, 0, ee
->rsize
, ee
->mem
, 0);
81 " : failed to write backing file\n");
83 DPRINTK("Wrote to backing file\n");
96 uint8_t at24c_eeprom_recv(I2CSlave
*s
)
98 EEPROMState
*ee
= AT24C_EE(s
);
102 * If got the byte address but not completely with address size
103 * will return the invalid value
105 if (ee
->haveaddr
> 0 && ee
->haveaddr
< ee
->asize
) {
109 ret
= ee
->mem
[ee
->cur
];
111 ee
->cur
= (ee
->cur
+ 1u) % ee
->rsize
;
112 DPRINTK("Recv %02x %c\n", ret
, ret
);
118 int at24c_eeprom_send(I2CSlave
*s
, uint8_t data
)
120 EEPROMState
*ee
= AT24C_EE(s
);
122 if (ee
->haveaddr
< ee
->asize
) {
126 if (ee
->haveaddr
== ee
->asize
) {
127 ee
->cur
%= ee
->rsize
;
128 DPRINTK("Set pointer %04x\n", ee
->cur
);
133 DPRINTK("Send %02x\n", data
);
134 ee
->mem
[ee
->cur
] = data
;
137 DPRINTK("Send error %02x read-only\n", data
);
139 ee
->cur
= (ee
->cur
+ 1u) % ee
->rsize
;
146 I2CSlave
*at24c_eeprom_init(I2CBus
*bus
, uint8_t address
, uint32_t rom_size
)
148 return at24c_eeprom_init_rom(bus
, address
, rom_size
, NULL
, 0);
151 I2CSlave
*at24c_eeprom_init_rom(I2CBus
*bus
, uint8_t address
, uint32_t rom_size
,
152 const uint8_t *init_rom
, uint32_t init_rom_size
)
156 s
= AT24C_EE(i2c_slave_new(TYPE_AT24C_EE
, address
));
158 qdev_prop_set_uint32(DEVICE(s
), "rom-size", rom_size
);
160 /* TODO: Model init_rom with QOM properties. */
161 s
->init_rom
= init_rom
;
162 s
->init_rom_size
= init_rom_size
;
164 i2c_slave_realize_and_unref(I2C_SLAVE(s
), bus
, &error_abort
);
169 static void at24c_eeprom_realize(DeviceState
*dev
, Error
**errp
)
171 EEPROMState
*ee
= AT24C_EE(dev
);
173 if (ee
->init_rom_size
> ee
->rsize
) {
174 error_setg(errp
, "%s: init rom is larger than rom: %u > %u",
175 TYPE_AT24C_EE
, ee
->init_rom_size
, ee
->rsize
);
180 int64_t len
= blk_getlength(ee
->blk
);
182 if (len
!= ee
->rsize
) {
183 error_setg(errp
, "%s: Backing file size %" PRId64
" != %u",
184 TYPE_AT24C_EE
, len
, ee
->rsize
);
188 if (blk_set_perm(ee
->blk
, BLK_PERM_CONSISTENT_READ
| BLK_PERM_WRITE
,
189 BLK_PERM_ALL
, &error_fatal
) < 0)
191 error_setg(errp
, "%s: Backing file incorrect permission",
197 ee
->mem
= g_malloc0(ee
->rsize
);
198 memset(ee
->mem
, 0, ee
->rsize
);
201 memcpy(ee
->mem
, ee
->init_rom
, MIN(ee
->init_rom_size
, ee
->rsize
));
205 int ret
= blk_pread(ee
->blk
, 0, ee
->rsize
, ee
->mem
, 0);
209 " : Failed initial sync with backing file\n");
211 DPRINTK("Reset read backing file\n");
215 * If address size didn't define with property set
216 * value is 0 as default, setting it by Rom size detecting.
218 if (ee
->asize
== 0) {
219 if (ee
->rsize
<= 256) {
228 void at24c_eeprom_reset(DeviceState
*state
)
230 EEPROMState
*ee
= AT24C_EE(state
);
237 static Property at24c_eeprom_props
[] = {
238 DEFINE_PROP_UINT32("rom-size", EEPROMState
, rsize
, 0),
239 DEFINE_PROP_UINT8("address-size", EEPROMState
, asize
, 0),
240 DEFINE_PROP_BOOL("writable", EEPROMState
, writable
, true),
241 DEFINE_PROP_DRIVE("drive", EEPROMState
, blk
),
242 DEFINE_PROP_END_OF_LIST()
246 void at24c_eeprom_class_init(ObjectClass
*klass
, void *data
)
248 DeviceClass
*dc
= DEVICE_CLASS(klass
);
249 I2CSlaveClass
*k
= I2C_SLAVE_CLASS(klass
);
251 dc
->realize
= &at24c_eeprom_realize
;
252 k
->event
= &at24c_eeprom_event
;
253 k
->recv
= &at24c_eeprom_recv
;
254 k
->send
= &at24c_eeprom_send
;
256 device_class_set_props(dc
, at24c_eeprom_props
);
257 dc
->reset
= at24c_eeprom_reset
;
261 const TypeInfo at24c_eeprom_type
= {
262 .name
= TYPE_AT24C_EE
,
263 .parent
= TYPE_I2C_SLAVE
,
264 .instance_size
= sizeof(EEPROMState
),
265 .class_size
= sizeof(I2CSlaveClass
),
266 .class_init
= at24c_eeprom_class_init
,
269 static void at24c_eeprom_register(void)
271 type_register_static(&at24c_eeprom_type
);
274 type_init(at24c_eeprom_register
)