qemu/hw/block/m24cxx.c
<<
>>
Prefs
   1/*
   2 * ST m24Cxx I2C EEPROMs
   3 *
   4 * Copyright (c) 2012 Xilinx Inc.
   5 * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
   6 *
   7 *  This program is free software; you can redistribute it and/or modify it
   8 *  under the terms of the GNU General Public License as published by the
   9 *  Free Software Foundation; either version 2 of the License, or
  10 *  (at your option) any later version.
  11 *
  12 *  This program is distributed in the hope that it will be useful, but WITHOUT
  13 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  15 *  for more details.
  16 *
  17 *  You should have received a copy of the GNU General Public License along
  18 *  with this program; if not, see <http://www.gnu.org/licenses/>.
  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    /* do nothing. Masters do not directly interact with the backing store,
  53     * only the working copy so no mutexing required.
  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    /* the device is so small, just sync the whole thing */
  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        /* should be impossible even with a degenerate guest */
  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        /* FIXME: Move to late init */
 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