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