xref: /qemu/hw/nvram/eeprom_at24c.c (revision 5d8424dbd3e8335ea3d57f64eaa603c8fc80706f)
1*5d8424dbSMichael Davidsaver /*
2*5d8424dbSMichael Davidsaver  * *AT24C* series I2C EEPROM
3*5d8424dbSMichael Davidsaver  *
4*5d8424dbSMichael Davidsaver  * Copyright (c) 2015 Michael Davidsaver
5*5d8424dbSMichael Davidsaver  *
6*5d8424dbSMichael Davidsaver  * This work is licensed under the terms of the GNU GPL, version 2.  See
7*5d8424dbSMichael Davidsaver  * the LICENSE file in the top-level directory.
8*5d8424dbSMichael Davidsaver  */
9*5d8424dbSMichael Davidsaver 
10*5d8424dbSMichael Davidsaver #include <string.h>
11*5d8424dbSMichael Davidsaver 
12*5d8424dbSMichael Davidsaver #include "qemu/osdep.h"
13*5d8424dbSMichael Davidsaver #include "qapi/error.h"
14*5d8424dbSMichael Davidsaver #include "hw/hw.h"
15*5d8424dbSMichael Davidsaver #include "hw/i2c/i2c.h"
16*5d8424dbSMichael Davidsaver #include "sysemu/block-backend.h"
17*5d8424dbSMichael Davidsaver 
18*5d8424dbSMichael Davidsaver /* #define DEBUG_AT24C */
19*5d8424dbSMichael Davidsaver 
20*5d8424dbSMichael Davidsaver #ifdef DEBUG_AT24C
21*5d8424dbSMichael Davidsaver #define DPRINTK(FMT, ...) printf(TYPE_AT24C_EE " : " FMT, ## __VA_ARGS__)
22*5d8424dbSMichael Davidsaver #else
23*5d8424dbSMichael Davidsaver #define DPRINTK(FMT, ...) do {} while (0)
24*5d8424dbSMichael Davidsaver #endif
25*5d8424dbSMichael Davidsaver 
26*5d8424dbSMichael Davidsaver #define ERR(FMT, ...) fprintf(stderr, TYPE_AT24C_EE " : " FMT, \
27*5d8424dbSMichael Davidsaver                             ## __VA_ARGS__)
28*5d8424dbSMichael Davidsaver 
29*5d8424dbSMichael Davidsaver #define TYPE_AT24C_EE "at24c-eeprom"
30*5d8424dbSMichael Davidsaver #define AT24C_EE(obj) OBJECT_CHECK(EEPROMState, (obj), TYPE_AT24C_EE)
31*5d8424dbSMichael Davidsaver 
32*5d8424dbSMichael Davidsaver typedef struct EEPROMState {
33*5d8424dbSMichael Davidsaver     I2CSlave parent_obj;
34*5d8424dbSMichael Davidsaver 
35*5d8424dbSMichael Davidsaver     /* address counter */
36*5d8424dbSMichael Davidsaver     uint16_t cur;
37*5d8424dbSMichael Davidsaver     /* total size in bytes */
38*5d8424dbSMichael Davidsaver     uint32_t rsize;
39*5d8424dbSMichael Davidsaver     bool writable;
40*5d8424dbSMichael Davidsaver     /* cells changed since last START? */
41*5d8424dbSMichael Davidsaver     bool changed;
42*5d8424dbSMichael Davidsaver     /* during WRITE, # of address bytes transfered */
43*5d8424dbSMichael Davidsaver     uint8_t haveaddr;
44*5d8424dbSMichael Davidsaver 
45*5d8424dbSMichael Davidsaver     uint8_t *mem;
46*5d8424dbSMichael Davidsaver 
47*5d8424dbSMichael Davidsaver     BlockBackend *blk;
48*5d8424dbSMichael Davidsaver } EEPROMState;
49*5d8424dbSMichael Davidsaver 
50*5d8424dbSMichael Davidsaver static
51*5d8424dbSMichael Davidsaver int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
52*5d8424dbSMichael Davidsaver {
53*5d8424dbSMichael Davidsaver     EEPROMState *ee = container_of(s, EEPROMState, parent_obj);
54*5d8424dbSMichael Davidsaver 
55*5d8424dbSMichael Davidsaver     switch (event) {
56*5d8424dbSMichael Davidsaver     case I2C_START_SEND:
57*5d8424dbSMichael Davidsaver     case I2C_START_RECV:
58*5d8424dbSMichael Davidsaver     case I2C_FINISH:
59*5d8424dbSMichael Davidsaver         ee->haveaddr = 0;
60*5d8424dbSMichael Davidsaver         DPRINTK("clear\n");
61*5d8424dbSMichael Davidsaver         if (ee->blk && ee->changed) {
62*5d8424dbSMichael Davidsaver             int len = blk_pwrite(ee->blk, 0, ee->mem, ee->rsize, 0);
63*5d8424dbSMichael Davidsaver             if (len != ee->rsize) {
64*5d8424dbSMichael Davidsaver                 ERR(TYPE_AT24C_EE
65*5d8424dbSMichael Davidsaver                         " : failed to write backing file\n");
66*5d8424dbSMichael Davidsaver             }
67*5d8424dbSMichael Davidsaver             DPRINTK("Wrote to backing file\n");
68*5d8424dbSMichael Davidsaver         }
69*5d8424dbSMichael Davidsaver         ee->changed = false;
70*5d8424dbSMichael Davidsaver         break;
71*5d8424dbSMichael Davidsaver     case I2C_NACK:
72*5d8424dbSMichael Davidsaver         break;
73*5d8424dbSMichael Davidsaver     }
74*5d8424dbSMichael Davidsaver     return 0;
75*5d8424dbSMichael Davidsaver }
76*5d8424dbSMichael Davidsaver 
77*5d8424dbSMichael Davidsaver static
78*5d8424dbSMichael Davidsaver int at24c_eeprom_recv(I2CSlave *s)
79*5d8424dbSMichael Davidsaver {
80*5d8424dbSMichael Davidsaver     EEPROMState *ee = AT24C_EE(s);
81*5d8424dbSMichael Davidsaver     int ret;
82*5d8424dbSMichael Davidsaver 
83*5d8424dbSMichael Davidsaver     ret = ee->mem[ee->cur];
84*5d8424dbSMichael Davidsaver 
85*5d8424dbSMichael Davidsaver     ee->cur = (ee->cur + 1u) % ee->rsize;
86*5d8424dbSMichael Davidsaver     DPRINTK("Recv %02x %c\n", ret, ret);
87*5d8424dbSMichael Davidsaver 
88*5d8424dbSMichael Davidsaver     return ret;
89*5d8424dbSMichael Davidsaver }
90*5d8424dbSMichael Davidsaver 
91*5d8424dbSMichael Davidsaver static
92*5d8424dbSMichael Davidsaver int at24c_eeprom_send(I2CSlave *s, uint8_t data)
93*5d8424dbSMichael Davidsaver {
94*5d8424dbSMichael Davidsaver     EEPROMState *ee = AT24C_EE(s);
95*5d8424dbSMichael Davidsaver 
96*5d8424dbSMichael Davidsaver     if (ee->haveaddr < 2) {
97*5d8424dbSMichael Davidsaver         ee->cur <<= 8;
98*5d8424dbSMichael Davidsaver         ee->cur |= data;
99*5d8424dbSMichael Davidsaver         ee->haveaddr++;
100*5d8424dbSMichael Davidsaver         if (ee->haveaddr == 2) {
101*5d8424dbSMichael Davidsaver             ee->cur %= ee->rsize;
102*5d8424dbSMichael Davidsaver             DPRINTK("Set pointer %04x\n", ee->cur);
103*5d8424dbSMichael Davidsaver         }
104*5d8424dbSMichael Davidsaver 
105*5d8424dbSMichael Davidsaver     } else {
106*5d8424dbSMichael Davidsaver         if (ee->writable) {
107*5d8424dbSMichael Davidsaver             DPRINTK("Send %02x\n", data);
108*5d8424dbSMichael Davidsaver             ee->mem[ee->cur] = data;
109*5d8424dbSMichael Davidsaver             ee->changed = true;
110*5d8424dbSMichael Davidsaver         } else {
111*5d8424dbSMichael Davidsaver             DPRINTK("Send error %02x read-only\n", data);
112*5d8424dbSMichael Davidsaver         }
113*5d8424dbSMichael Davidsaver         ee->cur = (ee->cur + 1u) % ee->rsize;
114*5d8424dbSMichael Davidsaver 
115*5d8424dbSMichael Davidsaver     }
116*5d8424dbSMichael Davidsaver 
117*5d8424dbSMichael Davidsaver     return 0;
118*5d8424dbSMichael Davidsaver }
119*5d8424dbSMichael Davidsaver 
120*5d8424dbSMichael Davidsaver static
121*5d8424dbSMichael Davidsaver int at24c_eeprom_init(I2CSlave *i2c)
122*5d8424dbSMichael Davidsaver {
123*5d8424dbSMichael Davidsaver     EEPROMState *ee = AT24C_EE(i2c);
124*5d8424dbSMichael Davidsaver 
125*5d8424dbSMichael Davidsaver     ee->mem = g_malloc0(ee->rsize);
126*5d8424dbSMichael Davidsaver 
127*5d8424dbSMichael Davidsaver     if (ee->blk) {
128*5d8424dbSMichael Davidsaver         int64_t len = blk_getlength(ee->blk);
129*5d8424dbSMichael Davidsaver 
130*5d8424dbSMichael Davidsaver         if (len != ee->rsize) {
131*5d8424dbSMichael Davidsaver             ERR(TYPE_AT24C_EE " : Backing file size %lu != %u\n",
132*5d8424dbSMichael Davidsaver                     (unsigned long)len, (unsigned)ee->rsize);
133*5d8424dbSMichael Davidsaver             exit(1);
134*5d8424dbSMichael Davidsaver         }
135*5d8424dbSMichael Davidsaver 
136*5d8424dbSMichael Davidsaver         if (blk_set_perm(ee->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
137*5d8424dbSMichael Davidsaver                          BLK_PERM_ALL, &error_fatal) < 0)
138*5d8424dbSMichael Davidsaver         {
139*5d8424dbSMichael Davidsaver             ERR(TYPE_AT24C_EE
140*5d8424dbSMichael Davidsaver                     " : Backing file incorrect permission\n");
141*5d8424dbSMichael Davidsaver             exit(1);
142*5d8424dbSMichael Davidsaver         }
143*5d8424dbSMichael Davidsaver     }
144*5d8424dbSMichael Davidsaver     return 0;
145*5d8424dbSMichael Davidsaver }
146*5d8424dbSMichael Davidsaver 
147*5d8424dbSMichael Davidsaver static
148*5d8424dbSMichael Davidsaver void at24c_eeprom_reset(DeviceState *state)
149*5d8424dbSMichael Davidsaver {
150*5d8424dbSMichael Davidsaver     EEPROMState *ee = AT24C_EE(state);
151*5d8424dbSMichael Davidsaver 
152*5d8424dbSMichael Davidsaver     ee->changed = false;
153*5d8424dbSMichael Davidsaver     ee->cur = 0;
154*5d8424dbSMichael Davidsaver     ee->haveaddr = 0;
155*5d8424dbSMichael Davidsaver 
156*5d8424dbSMichael Davidsaver     memset(ee->mem, 0, ee->rsize);
157*5d8424dbSMichael Davidsaver 
158*5d8424dbSMichael Davidsaver     if (ee->blk) {
159*5d8424dbSMichael Davidsaver         int len = blk_pread(ee->blk, 0, ee->mem, ee->rsize);
160*5d8424dbSMichael Davidsaver 
161*5d8424dbSMichael Davidsaver         if (len != ee->rsize) {
162*5d8424dbSMichael Davidsaver             ERR(TYPE_AT24C_EE
163*5d8424dbSMichael Davidsaver                     " : Failed initial sync with backing file\n");
164*5d8424dbSMichael Davidsaver         }
165*5d8424dbSMichael Davidsaver         DPRINTK("Reset read backing file\n");
166*5d8424dbSMichael Davidsaver     }
167*5d8424dbSMichael Davidsaver }
168*5d8424dbSMichael Davidsaver 
169*5d8424dbSMichael Davidsaver static Property at24c_eeprom_props[] = {
170*5d8424dbSMichael Davidsaver     DEFINE_PROP_UINT32("rom-size", EEPROMState, rsize, 0),
171*5d8424dbSMichael Davidsaver     DEFINE_PROP_BOOL("writable", EEPROMState, writable, true),
172*5d8424dbSMichael Davidsaver     DEFINE_PROP_DRIVE("drive", EEPROMState, blk),
173*5d8424dbSMichael Davidsaver     DEFINE_PROP_END_OF_LIST()
174*5d8424dbSMichael Davidsaver };
175*5d8424dbSMichael Davidsaver 
176*5d8424dbSMichael Davidsaver static
177*5d8424dbSMichael Davidsaver void at24c_eeprom_class_init(ObjectClass *klass, void *data)
178*5d8424dbSMichael Davidsaver {
179*5d8424dbSMichael Davidsaver     DeviceClass *dc = DEVICE_CLASS(klass);
180*5d8424dbSMichael Davidsaver     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
181*5d8424dbSMichael Davidsaver 
182*5d8424dbSMichael Davidsaver     k->init = &at24c_eeprom_init;
183*5d8424dbSMichael Davidsaver     k->event = &at24c_eeprom_event;
184*5d8424dbSMichael Davidsaver     k->recv = &at24c_eeprom_recv;
185*5d8424dbSMichael Davidsaver     k->send = &at24c_eeprom_send;
186*5d8424dbSMichael Davidsaver 
187*5d8424dbSMichael Davidsaver     dc->props = at24c_eeprom_props;
188*5d8424dbSMichael Davidsaver     dc->reset = at24c_eeprom_reset;
189*5d8424dbSMichael Davidsaver }
190*5d8424dbSMichael Davidsaver 
191*5d8424dbSMichael Davidsaver static
192*5d8424dbSMichael Davidsaver const TypeInfo at24c_eeprom_type = {
193*5d8424dbSMichael Davidsaver     .name = TYPE_AT24C_EE,
194*5d8424dbSMichael Davidsaver     .parent = TYPE_I2C_SLAVE,
195*5d8424dbSMichael Davidsaver     .instance_size = sizeof(EEPROMState),
196*5d8424dbSMichael Davidsaver     .class_size = sizeof(I2CSlaveClass),
197*5d8424dbSMichael Davidsaver     .class_init = at24c_eeprom_class_init,
198*5d8424dbSMichael Davidsaver };
199*5d8424dbSMichael Davidsaver 
200*5d8424dbSMichael Davidsaver static void at24c_eeprom_register(void)
201*5d8424dbSMichael Davidsaver {
202*5d8424dbSMichael Davidsaver     type_register_static(&at24c_eeprom_type);
203*5d8424dbSMichael Davidsaver }
204*5d8424dbSMichael Davidsaver 
205*5d8424dbSMichael Davidsaver type_init(at24c_eeprom_register)
206