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