From patchwork Wed Feb 20 05:29:57 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Crosthwaite X-Patchwork-Id: 221951 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id ABEDB2C007E for ; Wed, 20 Feb 2013 16:31:32 +1100 (EST) Received: from localhost ([::1]:53423 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U82HC-0006Su-TI for incoming@patchwork.ozlabs.org; Wed, 20 Feb 2013 00:31:30 -0500 Received: from eggs.gnu.org ([208.118.235.92]:35429) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U82Ga-0005Ok-Gy for qemu-devel@nongnu.org; Wed, 20 Feb 2013 00:30:54 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U82GU-0004Lg-SI for qemu-devel@nongnu.org; Wed, 20 Feb 2013 00:30:52 -0500 Received: from mail-pa0-f42.google.com ([209.85.220.42]:46337) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U82GU-0004LJ-KV for qemu-devel@nongnu.org; Wed, 20 Feb 2013 00:30:46 -0500 Received: by mail-pa0-f42.google.com with SMTP id kq12so3820643pab.1 for ; Tue, 19 Feb 2013 21:30:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:sender:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:in-reply-to:references:x-gm-message-state; bh=h2EYPG8XX0fN/zuLNal4jnvaa5j7WYevJTdJNfVrwuY=; b=F6bKvwraHZ2+VQ7EddZmrWxRiBVRavzFvanC5UrFGxZFvgsvmw7sEd8p9wta3XP9fV FszBDv4TnyYG8G6dEU2jPKvS0BYptnQpFjxUe/eIsn74MBNFkCD3hmS9n/jvzfcGFEh7 UOdgxI/x2y2oqKbFi1dRrfL4yy19ucSwJULElePRQwJ/u9NfwCTD15jBiwgn2dATfXkl 6O2Vh21QaLqGAuzwQaNCkKgf1m1fXbMk+Zeuwz32R48Zdl253lBzzMDUCgxXGlZQVdsI lnzjez86v2h3UWx4ufXjdK+k38vEoMWRd5d3+h3HQWx9qqVfoCkufbsNfrZMH36pV78p dayA== X-Received: by 10.66.159.201 with SMTP id xe9mr554090pab.110.1361338245923; Tue, 19 Feb 2013 21:30:45 -0800 (PST) Received: from localhost ([1.128.17.184]) by mx.google.com with ESMTPS id 1sm20411265pbg.18.2013.02.19.21.30.40 (version=TLSv1.1 cipher=RC4-SHA bits=128/128); Tue, 19 Feb 2013 21:30:45 -0800 (PST) From: Peter Crosthwaite To: qemu-devel@nongnu.org Date: Wed, 20 Feb 2013 15:29:57 +1000 Message-Id: X-Mailer: git-send-email 1.7.0.4 In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQlSc19UWJnKx9g1GKmZNSdOulUwhfQONRNcNy+t/Ztrsdngw3K05q9wM1LK9008u1XeG2PP X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.220.42 Cc: edgar.iglesias@gmail.com, Peter Crosthwaite , peter.maydell@linaro.org Subject: [Qemu-devel] [PATCH v1 4/6] hw: M24Cxx I2C EEPROM device model X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Device model for the ST M24Cxx I2C EEPROM devices. Device can optionally be backed onto a file for persistent storage (using -mtd-block). Signed-off-by: Peter Crosthwaite --- default-configs/arm-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/m24cxx.c | 243 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+), 0 deletions(-) create mode 100644 hw/m24cxx.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 2f1a5c9..9114382 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -26,6 +26,7 @@ CONFIG_SSI_M25P80=y CONFIG_LAN9118=y CONFIG_SMC91C111=y CONFIG_DS1338=y +CONFIG_M24CXX=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 6b278cc..dc75c9f 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -178,6 +178,7 @@ common-obj-$(CONFIG_ADS7846) += ads7846.o common-obj-$(CONFIG_MAX111X) += max111x.o common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-y += i2c.o smbus.o smbus_eeprom.o +common-obj-$(CONFIG_M24CXX) += m24cxx.o common-obj-y += eeprom93xx.o common-obj-y += scsi-disk.o cdrom.o hd-geometry.o block-common.o common-obj-y += scsi-generic.o scsi-bus.o diff --git a/hw/m24cxx.c b/hw/m24cxx.c new file mode 100644 index 0000000..567b820 --- /dev/null +++ b/hw/m24cxx.c @@ -0,0 +1,243 @@ +/* + * ST m24Cxx I2C EEPROMs + * + * Copyright (c) 2012 Xilinx Inc. + * Copyright (c) 2012 Peter Crosthwaite + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "i2c.h" +#include "hw.h" +#include "sysemu/blockdev.h" + +#ifndef M24CXX_DEBUG +#define M24CXX_DEBUG 0 +#endif +#define DB_PRINT(fmt, args...) do {\ + if (M24CXX_DEBUG) {\ + fprintf(stderr, "M24CXX: %s:" fmt, __func__, ## args);\ + } \ +} while (0); + +typedef enum { + STOPPED, + ADDRESSING, + READING, + WRITING, +} M24CXXXferState; + +const char *m24cxx_state_names[] = { + [STOPPED] = "STOPPED", + [ADDRESSING] = "ADDRESSING", + [READING] = "READING", + [WRITING] = "WRITING", +}; + +typedef struct { + I2CSlave i2c; + uint16_t cur_addr; + uint8_t state; + + BlockDriverState *bdrv; + uint16_t size; + + uint8_t *storage; +} M24CXXState; + +#define TYPE_M24CXX "m24cxx" + +#define M24CXX(obj) \ + OBJECT_CHECK(M24CXXState, (obj), TYPE_M24CXX) + +static void m24cxx_sync_complete(void *opaque, int ret) +{ + /* do nothing. Masters do not directly interact with the backing store, + * only the working copy so no mutexing required. + */ +} + +static void m24cxx_sync(I2CSlave *i2c) +{ + M24CXXState *s = M24CXX(i2c); + int64_t nb_sectors; + QEMUIOVector iov; + + if (!s->bdrv) { + return; + } + + /* the device is so small, just sync the whole thing */ + nb_sectors = DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE); + qemu_iovec_init(&iov, 1); + qemu_iovec_add(&iov, s->storage, nb_sectors * BDRV_SECTOR_SIZE); + bdrv_aio_writev(s->bdrv, 0, &iov, nb_sectors, m24cxx_sync_complete, NULL); +} + +static void m24cxx_reset(DeviceState *dev) +{ + M24CXXState *s = M24CXX(dev); + + m24cxx_sync(I2C_SLAVE(s)); + s->state = STOPPED; + s->cur_addr = 0; +} + +static int m24cxx_recv(I2CSlave *i2c) +{ + M24CXXState *s = M24CXX(i2c); + int ret = 0; + + if (s->state == READING) { + ret = s->storage[s->cur_addr++]; + DB_PRINT("storage %x <-> %x\n", s->cur_addr-1, ret); + s->cur_addr %= s->size; + } else { + /* should be impossible even with a degenerate guest */ + qemu_log_mask(LOG_GUEST_ERROR, "read from m24cxx not in read state"); + } + DB_PRINT("data: %02x\n", ret); + return ret; +} + +static int m24cxx_send(I2CSlave *i2c, uint8_t data) +{ + M24CXXState *s = M24CXX(i2c); + + switch (s->state) { + case (ADDRESSING): + s->cur_addr &= ~0xFF; + s->cur_addr |= data; + DB_PRINT("setting address to %x\n", s->cur_addr); + s->state = WRITING; + return 0; + case (WRITING): + DB_PRINT("storage %x <-> %x\n", s->cur_addr, data); + s->storage[s->cur_addr++] = data; + s->cur_addr %= s->size; + return 0; + default: + DB_PRINT("write to m24cxx not in writable state\n"); + qemu_log_mask(LOG_GUEST_ERROR, "write to m24cxx not in writable state"); + return 1; + } +} + +static void m24cxx_event(I2CSlave *i2c, enum i2c_event event) +{ + M24CXXState *s = M24CXX(i2c); + + switch (event) { + case I2C_START_SEND: + s->state = ADDRESSING; + break; + case I2C_START_RECV: + s->state = READING; + break; + case I2C_FINISH: + m24cxx_sync(i2c); + s->state = STOPPED; + break; + case I2C_NACK: + DB_PRINT("NACKED\n"); + break; + } + + DB_PRINT("transitioning to state %s\n", m24cxx_state_names[s->state]); +} + +static void m24cxx_decode_address(I2CSlave *i2c, uint8_t address) +{ + M24CXXState *s = M24CXX(i2c); + + s->cur_addr &= ~(0x0300); + s->cur_addr |= (address & ((s->size - 1) >> 8)) << 8; +} + +static void m24cxx_realize(DeviceState *dev, Error **errp) +{ + M24CXXState *s = M24CXX(dev); + I2CSlave *i2c = I2C_SLAVE(dev); + DriveInfo *dinfo = drive_get_next(IF_MTD); + + i2c->address_range = s->size >> 8 ? s->size >> 8 : 1; + s->storage = g_new0(uint8_t, DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE) * + BDRV_SECTOR_SIZE); + + if (dinfo && dinfo->bdrv) { + s->bdrv = dinfo->bdrv; + /* FIXME: Move to late init */ + if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size, + BDRV_SECTOR_SIZE))) { + error_setg(errp, "Failed to initialize I2C EEPROM!\n"); + return; + } + } else { + memset(s->storage, 0xFF, s->size); + } +} + +static void m24cxx_pre_save(void *opaque) +{ + m24cxx_sync((I2CSlave *)opaque); +} + +static const VMStateDescription vmstate_m24cxx = { + .name = "m24cxx", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = m24cxx_pre_save, + .fields = (VMStateField[]) { + VMSTATE_I2C_SLAVE(i2c, M24CXXState), + VMSTATE_UINT8(state, M24CXXState), + VMSTATE_UINT16(cur_addr, M24CXXState), + VMSTATE_END_OF_LIST() + } +}; + +static Property m24cxx_properties[] = { + DEFINE_PROP_UINT16("size", M24CXXState, size, 1024), + DEFINE_PROP_END_OF_LIST(), +}; + +static void m24cxx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->event = m24cxx_event; + k->recv = m24cxx_recv; + k->send = m24cxx_send; + k->decode_address = m24cxx_decode_address; + + dc->realize = m24cxx_realize; + dc->reset = m24cxx_reset; + dc->vmsd = &vmstate_m24cxx; + dc->props = m24cxx_properties; +} + +static TypeInfo m24cxx_info = { + .name = TYPE_M24CXX, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(M24CXXState), + .class_init = m24cxx_class_init, +}; + +static void m24cxx_register_types(void) +{ + type_register_static(&m24cxx_info); +} + +type_init(m24cxx_register_types)