* [Qemu-devel] [PATCH v3 0/2] i2c: Add AT24Cxx EEPROM model
@ 2013-04-26 9:19 Jan Kiszka
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 1/2] i2c: Introduce device address mask Jan Kiszka
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model Jan Kiszka
0 siblings, 2 replies; 5+ messages in thread
From: Jan Kiszka @ 2013-04-26 9:19 UTC (permalink / raw)
To: qemu-devel, Anthony Liguori; +Cc: Peter Crosthwaite, Andreas Faerber
Changes in v3:
- rebased over current master
- addressed review comments by Andreas Färber and Peter Crosthwaite
- better QOM compliance
- table-driven device parametrization
I also briefly looked into a libqos-based test case, but the omap
wrapper just gives me
libi2c-omap.c:79:omap_i2c_send: assertion failed: ((data & OMAP_I2C_CON_STP) != 0)
when sending an address word to the EEPROM.
Jan Kiszka (2):
i2c: Introduce device address mask
Add AT24Cxx I2C EEPROM device model
hw/arm/pxa2xx.c | 3 +-
hw/arm/tosa.c | 2 +-
hw/arm/z2.c | 2 +-
hw/audio/wm8750.c | 2 +-
hw/display/ssd0303.c | 2 +-
hw/gpio/max7310.c | 2 +-
hw/i2c/core.c | 14 ++-
hw/i2c/smbus.c | 2 +-
hw/input/lm832x.c | 2 +-
hw/misc/tmp105.c | 2 +-
hw/nvram/Makefile.objs | 2 +-
hw/nvram/at24.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++
hw/timer/ds1338.c | 2 +-
hw/timer/twl92230.c | 2 +-
include/hw/i2c/i2c.h | 4 +-
15 files changed, 373 insertions(+), 17 deletions(-)
create mode 100644 hw/nvram/at24.c
--
1.7.3.4
^ permalink raw reply [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH v3 1/2] i2c: Introduce device address mask
2013-04-26 9:19 [Qemu-devel] [PATCH v3 0/2] i2c: Add AT24Cxx EEPROM model Jan Kiszka
@ 2013-04-26 9:19 ` Jan Kiszka
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model Jan Kiszka
1 sibling, 0 replies; 5+ messages in thread
From: Jan Kiszka @ 2013-04-26 9:19 UTC (permalink / raw)
To: qemu-devel, Anthony Liguori; +Cc: Peter Crosthwaite, Andreas Faerber
Some devices react on multiple addresses. To emulate this, we could
register them multiple times, but that is cumbersome. The AT24C16, e.g.
listens on 8 different addresses.
Instead, introduce a device address mask that is applied on the
transmitted address before matching it against the stored one. Moreover,
the transmitted address is passed as additional parameter to the event
callback of the device.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
hw/arm/pxa2xx.c | 3 ++-
hw/arm/tosa.c | 2 +-
hw/arm/z2.c | 2 +-
hw/audio/wm8750.c | 2 +-
hw/display/ssd0303.c | 2 +-
hw/gpio/max7310.c | 2 +-
hw/i2c/core.c | 14 ++++++++++----
hw/i2c/smbus.c | 2 +-
hw/input/lm832x.c | 2 +-
hw/misc/tmp105.c | 2 +-
hw/timer/ds1338.c | 2 +-
hw/timer/twl92230.c | 2 +-
include/hw/i2c/i2c.h | 4 +++-
13 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 24b03a0..7d79805 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1240,7 +1240,8 @@ static void pxa2xx_i2c_update(PXA2xxI2CState *s)
}
/* These are only stubs now. */
-static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
+static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event,
+ uint8_t param)
{
PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
PXA2xxI2CState *s = slave->host;
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 47818a5..8a89557 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -157,7 +157,7 @@ static int tosa_dac_send(I2CSlave *i2c, uint8_t data)
return 0;
}
-static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
+static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
TosaDACState *s = FROM_I2C_SLAVE(TosaDACState, i2c);
s->len = 0;
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index 07a127b..b42d330 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -216,7 +216,7 @@ static int aer915_send(I2CSlave *i2c, uint8_t data)
return 0;
}
-static void aer915_event(I2CSlave *i2c, enum i2c_event event)
+static void aer915_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
AER915State *s = FROM_I2C_SLAVE(AER915State, i2c);
switch (event) {
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
index 6b5a349..6181de7 100644
--- a/hw/audio/wm8750.c
+++ b/hw/audio/wm8750.c
@@ -297,7 +297,7 @@ static void wm8750_reset(I2CSlave *i2c)
s->i2c_len = 0;
}
-static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
+static void wm8750_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
WM8750State *s = (WM8750State *) i2c;
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
index 3d7ebbe..9c09357 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -173,7 +173,7 @@ static int ssd0303_send(I2CSlave *i2c, uint8_t data)
return 0;
}
-static void ssd0303_event(I2CSlave *i2c, enum i2c_event event)
+static void ssd0303_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
ssd0303_state *s = (ssd0303_state *)i2c;
switch (event) {
diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c
index 59b2877..6574436 100644
--- a/hw/gpio/max7310.c
+++ b/hw/gpio/max7310.c
@@ -123,7 +123,7 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data)
return 0;
}
-static void max7310_event(I2CSlave *i2c, enum i2c_event event)
+static void max7310_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
MAX7310State *s = (MAX7310State *) i2c;
s->len = 0;
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index 0c4fc1d..bb604e9 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -76,6 +76,11 @@ void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
dev->address = address;
}
+void i2c_set_slave_address_mask(I2CSlave *dev, uint8_t mask)
+{
+ dev->address_mask = mask;
+}
+
/* Return nonzero if bus is busy. */
int i2c_bus_busy(i2c_bus *bus)
{
@@ -93,7 +98,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
DeviceState *qdev = kid->child;
I2CSlave *candidate = I2C_SLAVE(qdev);
- if (candidate->address == address) {
+ if (candidate->address == (address & candidate->address_mask)) {
slave = candidate;
break;
}
@@ -108,7 +113,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
start condition. */
bus->current_dev = slave;
if (sc->event) {
- sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND);
+ sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND, address);
}
return 0;
}
@@ -124,7 +129,7 @@ void i2c_end_transfer(i2c_bus *bus)
sc = I2C_SLAVE_GET_CLASS(dev);
if (sc->event) {
- sc->event(dev, I2C_FINISH);
+ sc->event(dev, I2C_FINISH, 0);
}
bus->current_dev = NULL;
@@ -175,7 +180,7 @@ void i2c_nack(i2c_bus *bus)
sc = I2C_SLAVE_GET_CLASS(dev);
if (sc->event) {
- sc->event(dev, I2C_NACK);
+ sc->event(dev, I2C_NACK, 0);
}
}
@@ -207,6 +212,7 @@ static int i2c_slave_qdev_init(DeviceState *dev)
I2CSlave *s = I2C_SLAVE(dev);
I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s);
+ s->address_mask = 0x7f;
return sc->init(s);
}
diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c
index 25d2d04..c9e1261 100644
--- a/hw/i2c/smbus.c
+++ b/hw/i2c/smbus.c
@@ -66,7 +66,7 @@ static void smbus_do_write(SMBusDevice *dev)
}
}
-static void smbus_i2c_event(I2CSlave *s, enum i2c_event event)
+static void smbus_i2c_event(I2CSlave *s, enum i2c_event event, uint8_t param)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c
index bacbeb2..a545e88 100644
--- a/hw/input/lm832x.c
+++ b/hw/input/lm832x.c
@@ -378,7 +378,7 @@ static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value)
}
}
-static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
+static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c
index 155e03d..ce73288 100644
--- a/hw/misc/tmp105.c
+++ b/hw/misc/tmp105.c
@@ -170,7 +170,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
return 0;
}
-static void tmp105_event(I2CSlave *i2c, enum i2c_event event)
+static void tmp105_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
TMP105State *s = TMP105(i2c);
diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c
index 8987cdc..51b3682 100644
--- a/hw/timer/ds1338.c
+++ b/hw/timer/ds1338.c
@@ -88,7 +88,7 @@ static void inc_regptr(DS1338State *s)
}
}
-static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
+static void ds1338_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c
index b730d85..be8d3a1 100644
--- a/hw/timer/twl92230.c
+++ b/hw/timer/twl92230.c
@@ -706,7 +706,7 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
}
}
-static void menelaus_event(I2CSlave *i2c, enum i2c_event event)
+static void menelaus_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
{
MenelausState *s = (MenelausState *) i2c;
diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h
index 461392f..c0f38b8 100644
--- a/include/hw/i2c/i2c.h
+++ b/include/hw/i2c/i2c.h
@@ -39,7 +39,7 @@ typedef struct I2CSlaveClass
int (*recv)(I2CSlave *s);
/* Notify the slave of a bus state change. */
- void (*event)(I2CSlave *s, enum i2c_event event);
+ void (*event)(I2CSlave *s, enum i2c_event event, uint8_t param);
} I2CSlaveClass;
struct I2CSlave
@@ -48,10 +48,12 @@ struct I2CSlave
/* Remaining fields for internal use by the I2C code. */
uint8_t address;
+ uint8_t address_mask;
};
i2c_bus *i2c_init_bus(DeviceState *parent, const char *name);
void i2c_set_slave_address(I2CSlave *dev, uint8_t address);
+void i2c_set_slave_address_mask(I2CSlave *dev, uint8_t mask);
int i2c_bus_busy(i2c_bus *bus);
int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv);
void i2c_end_transfer(i2c_bus *bus);
--
1.7.3.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model
2013-04-26 9:19 [Qemu-devel] [PATCH v3 0/2] i2c: Add AT24Cxx EEPROM model Jan Kiszka
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 1/2] i2c: Introduce device address mask Jan Kiszka
@ 2013-04-26 9:19 ` Jan Kiszka
2013-04-26 12:16 ` Peter Crosthwaite
1 sibling, 1 reply; 5+ messages in thread
From: Jan Kiszka @ 2013-04-26 9:19 UTC (permalink / raw)
To: qemu-devel, Anthony Liguori; +Cc: Peter Crosthwaite, Andreas Faerber
This implements I2C EEPROMs of the AT24Cxx series. Sizes from 1Kbit to
1024Kbit are supported. Each EEPROM is backed by a block device. Its
size can be explicitly specified by the "size" property (required for
sizes < 512, the blockdev sector size) or is derived from the size of
the backing block device. Device addresses are built from the device
number property. Write protection can be configured by declaring the
block device read-only.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
hw/nvram/Makefile.objs | 2 +-
hw/nvram/at24.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 348 insertions(+), 1 deletions(-)
create mode 100644 hw/nvram/at24.c
diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs
index e9a6694..8271cd6 100644
--- a/hw/nvram/Makefile.objs
+++ b/hw/nvram/Makefile.objs
@@ -1,5 +1,5 @@
common-obj-$(CONFIG_DS1225Y) += ds1225y.o
-common-obj-y += eeprom93xx.o
+common-obj-y += eeprom93xx.o at24.o
common-obj-y += fw_cfg.o
common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
obj-$(CONFIG_PSERIES) += spapr_nvram.o
diff --git a/hw/nvram/at24.c b/hw/nvram/at24.c
new file mode 100644
index 0000000..91e58e0
--- /dev/null
+++ b/hw/nvram/at24.c
@@ -0,0 +1,347 @@
+/*
+ * AT24Cxx EEPROM emulation
+ *
+ * Copyright (c) Siemens AG, 2012, 2013
+ * Author: Jan Kiszka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "hw/block/block.h"
+#include "sysemu/blockdev.h"
+
+#define TYPE_AT24 "at24"
+#define AT24(obj) OBJECT_CHECK(AT24State, (obj), TYPE_AT24)
+
+#define AT24_BASE_ADDRESS 0x50
+#define AT24_MAX_PAGE_LEN 256
+
+typedef struct AT24Parameters {
+ unsigned int size;
+ unsigned int page_size;
+ unsigned int device_bits;
+ unsigned int hi_addr_bits;
+ bool addr16;
+} AT24Parameters;
+
+typedef enum AT24TransferState {
+ AT24_IDLE,
+ AT24_RD_ADDR,
+ AT24_WR_ADDR_HI,
+ AT24_WR_ADDR_LO,
+ AT24_RW_DATA0,
+ AT24_RD_DATA,
+ AT24_WR_DATA,
+} AT24TransferState;
+
+typedef struct AT24State {
+ I2CSlave parent_obj;
+
+ BlockConf block_conf;
+ BlockDriverState *bs;
+ uint32_t size;
+ bool wp;
+ unsigned int addr_mask;
+ unsigned int page_mask;
+ bool addr16;
+ unsigned int hi_addr_mask;
+ uint8_t device;
+ AT24TransferState transfer_state;
+ uint8_t sector_buffer[BDRV_SECTOR_SIZE];
+ int cached_sector;
+ bool cache_dirty;
+ uint32_t transfer_addr;
+} AT24State;
+
+static void at24_flush_transfer_buffer(AT24State *s)
+{
+ if (s->cached_sector < 0 || !s->cache_dirty) {
+ return;
+ }
+ bdrv_write(s->bs, s->cached_sector, s->sector_buffer, 1);
+ s->cache_dirty = false;
+}
+
+static void at24_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
+{
+ AT24State *s = AT24(i2c);
+
+ switch (event) {
+ case I2C_START_SEND:
+ switch (s->transfer_state) {
+ case AT24_IDLE:
+ if (s->addr16) {
+ s->transfer_addr = (param & s->hi_addr_mask) << 16;
+ s->transfer_state = AT24_WR_ADDR_HI;
+ } else {
+ s->transfer_addr = (param & s->hi_addr_mask) << 8;
+ s->transfer_state = AT24_WR_ADDR_LO;
+ }
+ break;
+ default:
+ s->transfer_state = AT24_IDLE;
+ break;
+ }
+ break;
+ case I2C_START_RECV:
+ switch (s->transfer_state) {
+ case AT24_IDLE:
+ s->transfer_state = AT24_RD_ADDR;
+ break;
+ case AT24_RW_DATA0:
+ s->transfer_state = AT24_RD_DATA;
+ break;
+ default:
+ s->transfer_state = AT24_IDLE;
+ break;
+ }
+ break;
+ case I2C_FINISH:
+ switch (s->transfer_state) {
+ case AT24_WR_DATA:
+ at24_flush_transfer_buffer(s);
+ /* fall through */
+ default:
+ s->transfer_state = AT24_IDLE;
+ break;
+ }
+ break;
+ default:
+ s->transfer_state = AT24_IDLE;
+ break;
+ }
+}
+
+static int at24_cache_sector(AT24State *s, int sector)
+{
+ int ret;
+
+ if (s->cached_sector == sector) {
+ return 0;
+ }
+ ret = bdrv_read(s->bs, sector, s->sector_buffer, 1);
+ if (ret < 0) {
+ s->cached_sector = -1;
+ return ret;
+ }
+ s->cached_sector = sector;
+ s->cache_dirty = false;
+ return 0;
+}
+
+static int at24_tx(I2CSlave *i2c, uint8_t data)
+{
+ AT24State *s = AT24(i2c);
+
+ switch (s->transfer_state) {
+ case AT24_WR_ADDR_HI:
+ s->transfer_addr |= (data << 8) & s->addr_mask;
+ s->transfer_state = AT24_WR_ADDR_LO;
+ break;
+ case AT24_WR_ADDR_LO:
+ s->transfer_addr |= data & s->addr_mask;
+ s->transfer_state = AT24_RW_DATA0;
+ break;
+ case AT24_RW_DATA0:
+ s->transfer_state = AT24_WR_DATA;
+ if (at24_cache_sector(s, s->transfer_addr >> BDRV_SECTOR_BITS) < 0) {
+ s->transfer_state = AT24_IDLE;
+ return -1;
+ }
+ /* fall through */
+ case AT24_WR_DATA:
+ if (!s->wp) {
+ s->sector_buffer[s->transfer_addr & ~BDRV_SECTOR_MASK] = data;
+ s->cache_dirty = true;
+ }
+ s->transfer_addr = (s->transfer_addr & s->page_mask) |
+ ((s->transfer_addr + 1) & ~s->page_mask);
+ break;
+ default:
+ s->transfer_state = AT24_IDLE;
+ return -1;
+ }
+ return 0;
+}
+
+static int at24_rx(I2CSlave *i2c)
+{
+ AT24State *s = AT24(i2c);
+ unsigned int sector, offset;
+ int result;
+
+ switch (s->transfer_state) {
+ case AT24_RD_ADDR:
+ s->transfer_state = AT24_IDLE;
+ result = s->transfer_addr;
+ break;
+ case AT24_RD_DATA:
+ sector = s->transfer_addr >> BDRV_SECTOR_BITS;
+ offset = s->transfer_addr & ~BDRV_SECTOR_MASK;
+ s->transfer_addr = (s->transfer_addr + 1) & s->addr_mask;
+ if (at24_cache_sector(s, sector) < 0) {
+ result = 0;
+ break;
+ }
+ result = s->sector_buffer[offset];
+ break;
+ default:
+ s->transfer_state = AT24_IDLE;
+ result = 0;
+ break;
+ }
+ return result;
+}
+
+static void at24_reset(DeviceState *d)
+{
+ AT24State *s = AT24(d);
+
+ s->transfer_state = AT24_IDLE;
+ s->cached_sector = -1;
+}
+
+static const AT24Parameters at24_parameters[] = {
+ { 1*1024, 8, 3, 0, false },
+ { 2*1024, 8, 3, 0, false },
+ { 4*1024, 16, 2, 1, false },
+ { 8*1024, 16, 1, 2, false },
+ { 16*1024, 16, 0, 3, false },
+ { 32*1024, 32, 3, 0, true },
+ { 64*1024, 32, 3, 0, true },
+ { 128*1024, 64, 2, 0, true },
+ { 256*1024, 64, 2, 0, true },
+ { 512*1024, 128, 2, 0, true },
+ { 1024*1024, 256, 1, 1, true },
+ { },
+};
+
+static void at24_realize(DeviceState *d, Error **errp)
+{
+ I2CSlave *i2c = I2C_SLAVE(d);
+ AT24State *s = AT24(d);
+ const AT24Parameters *params;
+ int64_t image_size;
+ int dev_no;
+
+ s->bs = s->block_conf.bs;
+ if (!s->bs) {
+ error_setg(errp, "drive property not set");
+ return;
+ }
+
+ s->wp = bdrv_is_read_only(s->bs);
+
+ image_size = bdrv_getlength(s->bs);
+ if (s->size == 0) {
+ s->size = image_size;
+ } else if (image_size < s->size) {
+ error_setg(errp, "image file smaller than specified EEPROM size");
+ return;
+ }
+
+ for (params = at24_parameters; params->size != 0; params++) {
+ if (s->size * 8 == params->size) {
+ break;
+ }
+ }
+ if (params->size == 0) {
+ error_setg(errp, "invalid EEPROM size, must be 2^(10..17)");
+ return;
+ }
+ s->addr_mask = s->size - 1;
+ s->page_mask = ~(params->page_size - 1);
+ s->hi_addr_mask = (1 << params->hi_addr_bits) - 1;
+ s->addr16 = params->addr16;
+
+ dev_no = (s->device & ((1 << params->device_bits) - 1)) <<
+ params->hi_addr_bits;
+ i2c_set_slave_address(i2c, AT24_BASE_ADDRESS | dev_no);
+ i2c_set_slave_address_mask(i2c, 0x7f & ~s->hi_addr_mask);
+}
+
+static void at24_pre_save(void *opaque)
+{
+ AT24State *s = opaque;
+
+ at24_flush_transfer_buffer(s);
+}
+
+static int at24_post_load(void *opaque, int version_id)
+{
+ AT24State *s = opaque;
+
+ s->cached_sector = -1;
+ return 0;
+}
+
+static const VMStateDescription vmstate_at24 = {
+ .name = "at24",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = at24_pre_save,
+ .post_load = at24_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(transfer_state, AT24State),
+ VMSTATE_UINT32(transfer_addr, AT24State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property at24_properties[] = {
+ DEFINE_BLOCK_PROPERTIES(AT24State, block_conf),
+ DEFINE_PROP_UINT8("device", AT24State, device, 0),
+ DEFINE_PROP_UINT32("size", AT24State, size, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void at24_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+ sc->event = at24_event;
+ sc->recv = at24_rx;
+ sc->send = at24_tx;
+ dc->desc = "AT24Cxx EEPROM";
+ dc->reset = at24_reset;
+ dc->realize = at24_realize;
+ dc->vmsd = &vmstate_at24;
+ dc->props = at24_properties;
+}
+
+static const TypeInfo at24_info = {
+ .name = "at24",
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(AT24State),
+ .class_init = at24_class_init,
+};
+
+static void at24_register_types(void)
+{
+ /* buffering is based on this, enforce it early */
+ assert(AT24_MAX_PAGE_LEN <= BDRV_SECTOR_SIZE);
+
+ type_register_static(&at24_info);
+}
+
+type_init(at24_register_types)
--
1.7.3.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model Jan Kiszka
@ 2013-04-26 12:16 ` Peter Crosthwaite
2013-04-26 12:28 ` Jan Kiszka
0 siblings, 1 reply; 5+ messages in thread
From: Peter Crosthwaite @ 2013-04-26 12:16 UTC (permalink / raw)
To: Jan Kiszka; +Cc: Anthony Liguori, qemu-devel, Andreas Faerber
Hi Jan,
On Fri, Apr 26, 2013 at 7:19 PM, Jan Kiszka <jan.kiszka@siemens.com> wrote:
> This implements I2C EEPROMs of the AT24Cxx series. Sizes from 1Kbit to
> 1024Kbit are supported. Each EEPROM is backed by a block device. Its
> size can be explicitly specified by the "size" property (required for
> sizes < 512, the blockdev sector size) or is derived from the size of
> the backing block device. Device addresses are built from the device
> number property. Write protection can be configured by declaring the
> block device read-only.
>
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> ---
> hw/nvram/Makefile.objs | 2 +-
> hw/nvram/at24.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 348 insertions(+), 1 deletions(-)
> create mode 100644 hw/nvram/at24.c
>
> diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs
> index e9a6694..8271cd6 100644
> --- a/hw/nvram/Makefile.objs
> +++ b/hw/nvram/Makefile.objs
> @@ -1,5 +1,5 @@
> common-obj-$(CONFIG_DS1225Y) += ds1225y.o
> -common-obj-y += eeprom93xx.o
> +common-obj-y += eeprom93xx.o at24.o
> common-obj-y += fw_cfg.o
> common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
> obj-$(CONFIG_PSERIES) += spapr_nvram.o
> diff --git a/hw/nvram/at24.c b/hw/nvram/at24.c
> new file mode 100644
> index 0000000..91e58e0
> --- /dev/null
> +++ b/hw/nvram/at24.c
> @@ -0,0 +1,347 @@
> +/*
> + * AT24Cxx EEPROM emulation
> + *
> + * Copyright (c) Siemens AG, 2012, 2013
> + * Author: Jan Kiszka
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/i2c/i2c.h"
> +#include "hw/block/block.h"
> +#include "sysemu/blockdev.h"
> +
> +#define TYPE_AT24 "at24"
> +#define AT24(obj) OBJECT_CHECK(AT24State, (obj), TYPE_AT24)
> +
> +#define AT24_BASE_ADDRESS 0x50
> +#define AT24_MAX_PAGE_LEN 256
> +
> +typedef struct AT24Parameters {
> + unsigned int size;
> + unsigned int page_size;
> + unsigned int device_bits;
> + unsigned int hi_addr_bits;
> + bool addr16;
> +} AT24Parameters;
> +
> +typedef enum AT24TransferState {
> + AT24_IDLE,
> + AT24_RD_ADDR,
> + AT24_WR_ADDR_HI,
> + AT24_WR_ADDR_LO,
> + AT24_RW_DATA0,
> + AT24_RD_DATA,
> + AT24_WR_DATA,
> +} AT24TransferState;
> +
> +typedef struct AT24State {
> + I2CSlave parent_obj;
> +
> + BlockConf block_conf;
> + BlockDriverState *bs;
> + uint32_t size;
> + bool wp;
> + unsigned int addr_mask;
> + unsigned int page_mask;
> + bool addr16;
> + unsigned int hi_addr_mask;
> + uint8_t device;
> + AT24TransferState transfer_state;
> + uint8_t sector_buffer[BDRV_SECTOR_SIZE];
> + int cached_sector;
> + bool cache_dirty;
> + uint32_t transfer_addr;
> +} AT24State;
> +
> +static void at24_flush_transfer_buffer(AT24State *s)
> +{
> + if (s->cached_sector < 0 || !s->cache_dirty) {
> + return;
> + }
> + bdrv_write(s->bs, s->cached_sector, s->sector_buffer, 1);
> + s->cache_dirty = false;
> +}
> +
> +static void at24_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
> +{
> + AT24State *s = AT24(i2c);
> +
> + switch (event) {
> + case I2C_START_SEND:
> + switch (s->transfer_state) {
> + case AT24_IDLE:
> + if (s->addr16) {
> + s->transfer_addr = (param & s->hi_addr_mask) << 16;
> + s->transfer_state = AT24_WR_ADDR_HI;
> + } else {
> + s->transfer_addr = (param & s->hi_addr_mask) << 8;
> + s->transfer_state = AT24_WR_ADDR_LO;
> + }
> + break;
> + default:
> + s->transfer_state = AT24_IDLE;
> + break;
> + }
> + break;
> + case I2C_START_RECV:
> + switch (s->transfer_state) {
> + case AT24_IDLE:
> + s->transfer_state = AT24_RD_ADDR;
> + break;
> + case AT24_RW_DATA0:
> + s->transfer_state = AT24_RD_DATA;
> + break;
> + default:
> + s->transfer_state = AT24_IDLE;
> + break;
> + }
> + break;
> + case I2C_FINISH:
> + switch (s->transfer_state) {
> + case AT24_WR_DATA:
> + at24_flush_transfer_buffer(s);
> + /* fall through */
> + default:
> + s->transfer_state = AT24_IDLE;
> + break;
> + }
> + break;
> + default:
> + s->transfer_state = AT24_IDLE;
> + break;
> + }
> +}
> +
> +static int at24_cache_sector(AT24State *s, int sector)
> +{
> + int ret;
> +
> + if (s->cached_sector == sector) {
> + return 0;
> + }
> + ret = bdrv_read(s->bs, sector, s->sector_buffer, 1);
> + if (ret < 0) {
> + s->cached_sector = -1;
> + return ret;
> + }
> + s->cached_sector = sector;
> + s->cache_dirty = false;
> + return 0;
> +}
> +
> +static int at24_tx(I2CSlave *i2c, uint8_t data)
> +{
> + AT24State *s = AT24(i2c);
> +
> + switch (s->transfer_state) {
> + case AT24_WR_ADDR_HI:
> + s->transfer_addr |= (data << 8) & s->addr_mask;
> + s->transfer_state = AT24_WR_ADDR_LO;
> + break;
> + case AT24_WR_ADDR_LO:
> + s->transfer_addr |= data & s->addr_mask;
> + s->transfer_state = AT24_RW_DATA0;
> + break;
> + case AT24_RW_DATA0:
> + s->transfer_state = AT24_WR_DATA;
> + if (at24_cache_sector(s, s->transfer_addr >> BDRV_SECTOR_BITS) < 0) {
> + s->transfer_state = AT24_IDLE;
> + return -1;
> + }
> + /* fall through */
> + case AT24_WR_DATA:
> + if (!s->wp) {
> + s->sector_buffer[s->transfer_addr & ~BDRV_SECTOR_MASK] = data;
> + s->cache_dirty = true;
> + }
> + s->transfer_addr = (s->transfer_addr & s->page_mask) |
> + ((s->transfer_addr + 1) & ~s->page_mask);
> + break;
> + default:
> + s->transfer_state = AT24_IDLE;
> + return -1;
> + }
> + return 0;
> +}
> +
> +static int at24_rx(I2CSlave *i2c)
> +{
> + AT24State *s = AT24(i2c);
> + unsigned int sector, offset;
> + int result;
> +
> + switch (s->transfer_state) {
> + case AT24_RD_ADDR:
> + s->transfer_state = AT24_IDLE;
> + result = s->transfer_addr;
> + break;
> + case AT24_RD_DATA:
> + sector = s->transfer_addr >> BDRV_SECTOR_BITS;
> + offset = s->transfer_addr & ~BDRV_SECTOR_MASK;
> + s->transfer_addr = (s->transfer_addr + 1) & s->addr_mask;
> + if (at24_cache_sector(s, sector) < 0) {
> + result = 0;
> + break;
> + }
> + result = s->sector_buffer[offset];
> + break;
> + default:
> + s->transfer_state = AT24_IDLE;
> + result = 0;
> + break;
> + }
> + return result;
> +}
> +
> +static void at24_reset(DeviceState *d)
> +{
> + AT24State *s = AT24(d);
> +
> + s->transfer_state = AT24_IDLE;
> + s->cached_sector = -1;
> +}
> +
> +static const AT24Parameters at24_parameters[] = {
> + { 1*1024, 8, 3, 0, false },
> + { 2*1024, 8, 3, 0, false },
> + { 4*1024, 16, 2, 1, false },
> + { 8*1024, 16, 1, 2, false },
> + { 16*1024, 16, 0, 3, false },
> + { 32*1024, 32, 3, 0, true },
> + { 64*1024, 32, 3, 0, true },
> + { 128*1024, 64, 2, 0, true },
> + { 256*1024, 64, 2, 0, true },
> + { 512*1024, 128, 2, 0, true },
> + { 1024*1024, 256, 1, 1, true },
> + { },
You could potentially add a string field to this array for the part
name. "at24c08" etc.
Then in the register types function you can iterate through the array
and instantiate a
type for each different part. This would allow you to go
qemu-system-foo -device at24c08
rather than
qemu-system-foo -device at24cXX,size=4096
etc.
See block/m25p80.c for a similar situation.
> +};
> +
> +static void at24_realize(DeviceState *d, Error **errp)
> +{
> + I2CSlave *i2c = I2C_SLAVE(d);
> + AT24State *s = AT24(d);
> + const AT24Parameters *params;
> + int64_t image_size;
> + int dev_no;
> +
> + s->bs = s->block_conf.bs;
> + if (!s->bs) {
> + error_setg(errp, "drive property not set");
> + return;
> + }
> +
> + s->wp = bdrv_is_read_only(s->bs);
> +
> + image_size = bdrv_getlength(s->bs);
> + if (s->size == 0) {
> + s->size = image_size;
> + } else if (image_size < s->size) {
> + error_setg(errp, "image file smaller than specified EEPROM size");
> + return;
> + }
> +
> + for (params = at24_parameters; params->size != 0; params++) {
> + if (s->size * 8 == params->size) {
> + break;
> + }
> + }
> + if (params->size == 0) {
> + error_setg(errp, "invalid EEPROM size, must be 2^(10..17)");
> + return;
> + }
> + s->addr_mask = s->size - 1;
> + s->page_mask = ~(params->page_size - 1);
> + s->hi_addr_mask = (1 << params->hi_addr_bits) - 1;
> + s->addr16 = params->addr16;
> +
> + dev_no = (s->device & ((1 << params->device_bits) - 1)) <<
> + params->hi_addr_bits;
I still think this device bits has a place in the I2C layer itself.
But i'm happy to send
cleanup patches myself after a merge. I need this for another I2C
device and rather
not copy paste.
Regards,
Peter
> + i2c_set_slave_address(i2c, AT24_BASE_ADDRESS | dev_no);
> + i2c_set_slave_address_mask(i2c, 0x7f & ~s->hi_addr_mask);
> +}
> +
> +static void at24_pre_save(void *opaque)
> +{
> + AT24State *s = opaque;
> +
> + at24_flush_transfer_buffer(s);
> +}
> +
> +static int at24_post_load(void *opaque, int version_id)
> +{
> + AT24State *s = opaque;
> +
> + s->cached_sector = -1;
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_at24 = {
> + .name = "at24",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .pre_save = at24_pre_save,
> + .post_load = at24_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(transfer_state, AT24State),
> + VMSTATE_UINT32(transfer_addr, AT24State),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static Property at24_properties[] = {
> + DEFINE_BLOCK_PROPERTIES(AT24State, block_conf),
> + DEFINE_PROP_UINT8("device", AT24State, device, 0),
> + DEFINE_PROP_UINT32("size", AT24State, size, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void at24_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
> +
> + sc->event = at24_event;
> + sc->recv = at24_rx;
> + sc->send = at24_tx;
> + dc->desc = "AT24Cxx EEPROM";
> + dc->reset = at24_reset;
> + dc->realize = at24_realize;
> + dc->vmsd = &vmstate_at24;
> + dc->props = at24_properties;
> +}
> +
> +static const TypeInfo at24_info = {
> + .name = "at24",
> + .parent = TYPE_I2C_SLAVE,
> + .instance_size = sizeof(AT24State),
> + .class_init = at24_class_init,
> +};
> +
> +static void at24_register_types(void)
> +{
> + /* buffering is based on this, enforce it early */
> + assert(AT24_MAX_PAGE_LEN <= BDRV_SECTOR_SIZE);
> +
> + type_register_static(&at24_info);
> +}
> +
> +type_init(at24_register_types)
> --
> 1.7.3.4
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model
2013-04-26 12:16 ` Peter Crosthwaite
@ 2013-04-26 12:28 ` Jan Kiszka
0 siblings, 0 replies; 5+ messages in thread
From: Jan Kiszka @ 2013-04-26 12:28 UTC (permalink / raw)
To: Peter Crosthwaite; +Cc: Anthony Liguori, qemu-devel, Andreas Faerber
On 2013-04-26 14:16, Peter Crosthwaite wrote:
> Hi Jan,
>
> On Fri, Apr 26, 2013 at 7:19 PM, Jan Kiszka <jan.kiszka@siemens.com> wrote:
>> This implements I2C EEPROMs of the AT24Cxx series. Sizes from 1Kbit to
>> 1024Kbit are supported. Each EEPROM is backed by a block device. Its
>> size can be explicitly specified by the "size" property (required for
>> sizes < 512, the blockdev sector size) or is derived from the size of
>> the backing block device. Device addresses are built from the device
>> number property. Write protection can be configured by declaring the
>> block device read-only.
>>
>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>> ---
>> hw/nvram/Makefile.objs | 2 +-
>> hw/nvram/at24.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 348 insertions(+), 1 deletions(-)
>> create mode 100644 hw/nvram/at24.c
>>
>> diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs
>> index e9a6694..8271cd6 100644
>> --- a/hw/nvram/Makefile.objs
>> +++ b/hw/nvram/Makefile.objs
>> @@ -1,5 +1,5 @@
>> common-obj-$(CONFIG_DS1225Y) += ds1225y.o
>> -common-obj-y += eeprom93xx.o
>> +common-obj-y += eeprom93xx.o at24.o
>> common-obj-y += fw_cfg.o
>> common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
>> obj-$(CONFIG_PSERIES) += spapr_nvram.o
>> diff --git a/hw/nvram/at24.c b/hw/nvram/at24.c
>> new file mode 100644
>> index 0000000..91e58e0
>> --- /dev/null
>> +++ b/hw/nvram/at24.c
>> @@ -0,0 +1,347 @@
>> +/*
>> + * AT24Cxx EEPROM emulation
>> + *
>> + * Copyright (c) Siemens AG, 2012, 2013
>> + * Author: Jan Kiszka
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "hw/hw.h"
>> +#include "hw/i2c/i2c.h"
>> +#include "hw/block/block.h"
>> +#include "sysemu/blockdev.h"
>> +
>> +#define TYPE_AT24 "at24"
>> +#define AT24(obj) OBJECT_CHECK(AT24State, (obj), TYPE_AT24)
>> +
>> +#define AT24_BASE_ADDRESS 0x50
>> +#define AT24_MAX_PAGE_LEN 256
>> +
>> +typedef struct AT24Parameters {
>> + unsigned int size;
>> + unsigned int page_size;
>> + unsigned int device_bits;
>> + unsigned int hi_addr_bits;
>> + bool addr16;
>> +} AT24Parameters;
>> +
>> +typedef enum AT24TransferState {
>> + AT24_IDLE,
>> + AT24_RD_ADDR,
>> + AT24_WR_ADDR_HI,
>> + AT24_WR_ADDR_LO,
>> + AT24_RW_DATA0,
>> + AT24_RD_DATA,
>> + AT24_WR_DATA,
>> +} AT24TransferState;
>> +
>> +typedef struct AT24State {
>> + I2CSlave parent_obj;
>> +
>> + BlockConf block_conf;
>> + BlockDriverState *bs;
>> + uint32_t size;
>> + bool wp;
>> + unsigned int addr_mask;
>> + unsigned int page_mask;
>> + bool addr16;
>> + unsigned int hi_addr_mask;
>> + uint8_t device;
>> + AT24TransferState transfer_state;
>> + uint8_t sector_buffer[BDRV_SECTOR_SIZE];
>> + int cached_sector;
>> + bool cache_dirty;
>> + uint32_t transfer_addr;
>> +} AT24State;
>> +
>> +static void at24_flush_transfer_buffer(AT24State *s)
>> +{
>> + if (s->cached_sector < 0 || !s->cache_dirty) {
>> + return;
>> + }
>> + bdrv_write(s->bs, s->cached_sector, s->sector_buffer, 1);
>> + s->cache_dirty = false;
>> +}
>> +
>> +static void at24_event(I2CSlave *i2c, enum i2c_event event, uint8_t param)
>> +{
>> + AT24State *s = AT24(i2c);
>> +
>> + switch (event) {
>> + case I2C_START_SEND:
>> + switch (s->transfer_state) {
>> + case AT24_IDLE:
>> + if (s->addr16) {
>> + s->transfer_addr = (param & s->hi_addr_mask) << 16;
>> + s->transfer_state = AT24_WR_ADDR_HI;
>> + } else {
>> + s->transfer_addr = (param & s->hi_addr_mask) << 8;
>> + s->transfer_state = AT24_WR_ADDR_LO;
>> + }
>> + break;
>> + default:
>> + s->transfer_state = AT24_IDLE;
>> + break;
>> + }
>> + break;
>> + case I2C_START_RECV:
>> + switch (s->transfer_state) {
>> + case AT24_IDLE:
>> + s->transfer_state = AT24_RD_ADDR;
>> + break;
>> + case AT24_RW_DATA0:
>> + s->transfer_state = AT24_RD_DATA;
>> + break;
>> + default:
>> + s->transfer_state = AT24_IDLE;
>> + break;
>> + }
>> + break;
>> + case I2C_FINISH:
>> + switch (s->transfer_state) {
>> + case AT24_WR_DATA:
>> + at24_flush_transfer_buffer(s);
>> + /* fall through */
>> + default:
>> + s->transfer_state = AT24_IDLE;
>> + break;
>> + }
>> + break;
>> + default:
>> + s->transfer_state = AT24_IDLE;
>> + break;
>> + }
>> +}
>> +
>> +static int at24_cache_sector(AT24State *s, int sector)
>> +{
>> + int ret;
>> +
>> + if (s->cached_sector == sector) {
>> + return 0;
>> + }
>> + ret = bdrv_read(s->bs, sector, s->sector_buffer, 1);
>> + if (ret < 0) {
>> + s->cached_sector = -1;
>> + return ret;
>> + }
>> + s->cached_sector = sector;
>> + s->cache_dirty = false;
>> + return 0;
>> +}
>> +
>> +static int at24_tx(I2CSlave *i2c, uint8_t data)
>> +{
>> + AT24State *s = AT24(i2c);
>> +
>> + switch (s->transfer_state) {
>> + case AT24_WR_ADDR_HI:
>> + s->transfer_addr |= (data << 8) & s->addr_mask;
>> + s->transfer_state = AT24_WR_ADDR_LO;
>> + break;
>> + case AT24_WR_ADDR_LO:
>> + s->transfer_addr |= data & s->addr_mask;
>> + s->transfer_state = AT24_RW_DATA0;
>> + break;
>> + case AT24_RW_DATA0:
>> + s->transfer_state = AT24_WR_DATA;
>> + if (at24_cache_sector(s, s->transfer_addr >> BDRV_SECTOR_BITS) < 0) {
>> + s->transfer_state = AT24_IDLE;
>> + return -1;
>> + }
>> + /* fall through */
>> + case AT24_WR_DATA:
>> + if (!s->wp) {
>> + s->sector_buffer[s->transfer_addr & ~BDRV_SECTOR_MASK] = data;
>> + s->cache_dirty = true;
>> + }
>> + s->transfer_addr = (s->transfer_addr & s->page_mask) |
>> + ((s->transfer_addr + 1) & ~s->page_mask);
>> + break;
>> + default:
>> + s->transfer_state = AT24_IDLE;
>> + return -1;
>> + }
>> + return 0;
>> +}
>> +
>> +static int at24_rx(I2CSlave *i2c)
>> +{
>> + AT24State *s = AT24(i2c);
>> + unsigned int sector, offset;
>> + int result;
>> +
>> + switch (s->transfer_state) {
>> + case AT24_RD_ADDR:
>> + s->transfer_state = AT24_IDLE;
>> + result = s->transfer_addr;
>> + break;
>> + case AT24_RD_DATA:
>> + sector = s->transfer_addr >> BDRV_SECTOR_BITS;
>> + offset = s->transfer_addr & ~BDRV_SECTOR_MASK;
>> + s->transfer_addr = (s->transfer_addr + 1) & s->addr_mask;
>> + if (at24_cache_sector(s, sector) < 0) {
>> + result = 0;
>> + break;
>> + }
>> + result = s->sector_buffer[offset];
>> + break;
>> + default:
>> + s->transfer_state = AT24_IDLE;
>> + result = 0;
>> + break;
>> + }
>> + return result;
>> +}
>> +
>> +static void at24_reset(DeviceState *d)
>> +{
>> + AT24State *s = AT24(d);
>> +
>> + s->transfer_state = AT24_IDLE;
>> + s->cached_sector = -1;
>> +}
>> +
>> +static const AT24Parameters at24_parameters[] = {
>> + { 1*1024, 8, 3, 0, false },
>> + { 2*1024, 8, 3, 0, false },
>> + { 4*1024, 16, 2, 1, false },
>> + { 8*1024, 16, 1, 2, false },
>> + { 16*1024, 16, 0, 3, false },
>> + { 32*1024, 32, 3, 0, true },
>> + { 64*1024, 32, 3, 0, true },
>> + { 128*1024, 64, 2, 0, true },
>> + { 256*1024, 64, 2, 0, true },
>> + { 512*1024, 128, 2, 0, true },
>> + { 1024*1024, 256, 1, 1, true },
>> + { },
>
> You could potentially add a string field to this array for the part
> name. "at24c08" etc.
> Then in the register types function you can iterate through the array
> and instantiate a
> type for each different part. This would allow you to go
>
> qemu-system-foo -device at24c08
>
> rather than
>
> qemu-system-foo -device at24cXX,size=4096
You only need the size property if your EEPROM image is larger than the
target type. But I could add explicit types, too.
>
> etc.
>
> See block/m25p80.c for a similar situation.
>
>> +};
>> +
>> +static void at24_realize(DeviceState *d, Error **errp)
>> +{
>> + I2CSlave *i2c = I2C_SLAVE(d);
>> + AT24State *s = AT24(d);
>> + const AT24Parameters *params;
>> + int64_t image_size;
>> + int dev_no;
>> +
>> + s->bs = s->block_conf.bs;
>> + if (!s->bs) {
>> + error_setg(errp, "drive property not set");
>> + return;
>> + }
>> +
>> + s->wp = bdrv_is_read_only(s->bs);
>> +
>> + image_size = bdrv_getlength(s->bs);
>> + if (s->size == 0) {
>> + s->size = image_size;
>> + } else if (image_size < s->size) {
>> + error_setg(errp, "image file smaller than specified EEPROM size");
>> + return;
>> + }
>> +
>> + for (params = at24_parameters; params->size != 0; params++) {
>> + if (s->size * 8 == params->size) {
>> + break;
>> + }
>> + }
>> + if (params->size == 0) {
>> + error_setg(errp, "invalid EEPROM size, must be 2^(10..17)");
>> + return;
>> + }
>> + s->addr_mask = s->size - 1;
>> + s->page_mask = ~(params->page_size - 1);
>> + s->hi_addr_mask = (1 << params->hi_addr_bits) - 1;
>> + s->addr16 = params->addr16;
>> +
>> + dev_no = (s->device & ((1 << params->device_bits) - 1)) <<
>> + params->hi_addr_bits;
>
> I still think this device bits has a place in the I2C layer itself.
> But i'm happy to send
> cleanup patches myself after a merge. I need this for another I2C
> device and rather
> not copy paste.
Perfect, thanks.
Jan
--
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2013-04-26 12:28 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-04-26 9:19 [Qemu-devel] [PATCH v3 0/2] i2c: Add AT24Cxx EEPROM model Jan Kiszka
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 1/2] i2c: Introduce device address mask Jan Kiszka
2013-04-26 9:19 ` [Qemu-devel] [PATCH v3 2/2] Add AT24Cxx I2C EEPROM device model Jan Kiszka
2013-04-26 12:16 ` Peter Crosthwaite
2013-04-26 12:28 ` Jan Kiszka
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).