aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2007-05-23 00:03:59 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2007-05-23 00:03:59 +0000
commit0ff596d02fd9d876a31d038255a6d4f89da9dfed (patch)
tree754c9dfaee7006ab91d80333141835d9213a6d06 /hw
parentc6fdf5fca0149fbc2d05d8d5ad43e3686c37514e (diff)
I2C/SMBus framework.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2845 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi.c57
-rw-r--r--hw/i2c.c117
-rw-r--r--hw/i2c.h49
-rw-r--r--hw/pc.c9
-rw-r--r--hw/smbus.c303
-rw-r--r--hw/smbus.h46
-rw-r--r--hw/smbus_eeprom.c46
7 files changed, 561 insertions, 66 deletions
diff --git a/hw/acpi.c b/hw/acpi.c
index 5310570ecf..de4002e92f 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -35,7 +35,7 @@ typedef struct PIIX4PMState {
uint8_t apms;
QEMUTimer *tmr_timer;
int64_t tmr_overflow_time;
- SMBusDevice *smb_dev[128];
+ i2c_bus *smbus;
uint8_t smb_stat;
uint8_t smb_ctl;
uint8_t smb_cmd;
@@ -63,9 +63,6 @@ typedef struct PIIX4PMState {
#define SMBHSTDAT1 0x06
#define SMBBLKDAT 0x07
-/* Note: only used for piix4_smbus_register_device */
-static PIIX4PMState *piix4_pm_state;
-
static uint32_t get_pmtmr(PIIX4PMState *s)
{
uint32_t d;
@@ -258,59 +255,44 @@ static void smb_transaction(PIIX4PMState *s)
uint8_t read = s->smb_addr & 0x01;
uint8_t cmd = s->smb_cmd;
uint8_t addr = s->smb_addr >> 1;
- SMBusDevice *dev = s->smb_dev[addr];
+ i2c_bus *bus = s->smbus;
#ifdef DEBUG
printf("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
#endif
- if (!dev) goto error;
-
switch(prot) {
case 0x0:
- if (!dev->quick_cmd) goto error;
- (*dev->quick_cmd)(dev, read);
+ smbus_quick_command(bus, addr, read);
break;
case 0x1:
if (read) {
- if (!dev->receive_byte) goto error;
- s->smb_data0 = (*dev->receive_byte)(dev);
- }
- else {
- if (!dev->send_byte) goto error;
- (*dev->send_byte)(dev, cmd);
+ s->smb_data0 = smbus_receive_byte(bus, addr);
+ } else {
+ smbus_send_byte(bus, addr, cmd);
}
break;
case 0x2:
if (read) {
- if (!dev->read_byte) goto error;
- s->smb_data0 = (*dev->read_byte)(dev, cmd);
- }
- else {
- if (!dev->write_byte) goto error;
- (*dev->write_byte)(dev, cmd, s->smb_data0);
+ s->smb_data0 = smbus_read_byte(bus, addr, cmd);
+ } else {
+ smbus_write_byte(bus, addr, cmd, s->smb_data0);
}
break;
case 0x3:
if (read) {
uint16_t val;
- if (!dev->read_word) goto error;
- val = (*dev->read_word)(dev, cmd);
+ val = smbus_read_word(bus, addr, cmd);
s->smb_data0 = val;
s->smb_data1 = val >> 8;
- }
- else {
- if (!dev->write_word) goto error;
- (*dev->write_word)(dev, cmd, (s->smb_data1 << 8) | s->smb_data0);
+ } else {
+ smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
}
break;
case 0x5:
if (read) {
- if (!dev->read_block) goto error;
- s->smb_data0 = (*dev->read_block)(dev, cmd, s->smb_data);
- }
- else {
- if (!dev->write_block) goto error;
- (*dev->write_block)(dev, cmd, s->smb_data0, s->smb_data);
+ s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data);
+ } else {
+ smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
}
break;
default:
@@ -469,7 +451,7 @@ static int pm_load(QEMUFile* f,void* opaque,int version_id)
return 0;
}
-void piix4_pm_init(PCIBus *bus, int devfn)
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn)
{
PIIX4PMState *s;
uint8_t *pci_conf;
@@ -514,10 +496,7 @@ void piix4_pm_init(PCIBus *bus, int devfn)
s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s);
register_savevm("piix4_pm", 0, 1, pm_save, pm_load, s);
- piix4_pm_state = s;
-}
-void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr)
-{
- piix4_pm_state->smb_dev[addr] = dev;
+ s->smbus = i2c_init_bus();
+ return s->smbus;
}
diff --git a/hw/i2c.c b/hw/i2c.c
new file mode 100644
index 0000000000..5d9319da4c
--- /dev/null
+++ b/hw/i2c.c
@@ -0,0 +1,117 @@
+/*
+ * QEMU I2C bus interface.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "vl.h"
+
+struct i2c_bus
+{
+ i2c_slave *current_dev;
+ i2c_slave *dev;
+};
+
+/* Create a new I2C bus. */
+i2c_bus *i2c_init_bus(void)
+{
+ i2c_bus *bus;
+
+ bus = (i2c_bus *)qemu_mallocz(sizeof(i2c_bus));
+ return bus;
+}
+
+/* Create a new slave device. */
+i2c_slave *i2c_slave_init(i2c_bus *bus, int address, int size)
+{
+ i2c_slave *dev;
+
+ if (size < sizeof(i2c_slave))
+ cpu_abort(cpu_single_env, "I2C struct too small");
+
+ dev = (i2c_slave *)qemu_mallocz(size);
+ dev->address = address;
+ dev->next = bus->dev;
+ bus->dev = dev;
+
+ return dev;
+}
+
+void i2c_set_slave_address(i2c_slave *dev, int address)
+{
+ dev->address = address;
+}
+
+/* Return nonzero if bus is busy. */
+int i2c_bus_busy(i2c_bus *bus)
+{
+ return bus->current_dev != NULL;
+}
+
+/* Returns nonzero if the bus is already busy, or is the address is not
+ valid. */
+/* TODO: Make this handle multiple masters. */
+int i2c_start_transfer(i2c_bus *bus, int address, int recv)
+{
+ i2c_slave *dev;
+
+ for (dev = bus->dev; dev; dev = dev->next) {
+ if (dev->address == address)
+ break;
+ }
+
+ if (!dev)
+ return 1;
+
+ /* If the bus is already busy, assume this is a repeated
+ start condition. */
+ bus->current_dev = dev;
+ dev->event(dev, recv ? I2C_START_RECV : I2C_START_SEND);
+ return 0;
+}
+
+void i2c_end_transfer(i2c_bus *bus)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return;
+
+ dev->event(dev, I2C_FINISH);
+
+ bus->current_dev = NULL;
+}
+
+int i2c_send(i2c_bus *bus, uint8_t data)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return -1;
+
+ return dev->send(dev, data);
+}
+
+int i2c_recv(i2c_bus *bus)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return -1;
+
+ return dev->recv(dev);
+}
+
+void i2c_nack(i2c_bus *bus)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return;
+
+ dev->event(dev, I2C_NACK);
+}
+
diff --git a/hw/i2c.h b/hw/i2c.h
new file mode 100644
index 0000000000..a3c4de3cac
--- /dev/null
+++ b/hw/i2c.h
@@ -0,0 +1,49 @@
+#ifndef QEMU_I2C_H
+#define QEMU_I2C_H
+
+/* The QEMU I2C implementation only supports simple transfers that complete
+ immediately. It does not support slave devices that need to be able to
+ defer their response (eg. CPU slave interfaces where the data is supplied
+ by the device driver in response to an interrupt). */
+
+enum i2c_event {
+ I2C_START_RECV,
+ I2C_START_SEND,
+ I2C_FINISH,
+ I2C_NACK /* Masker NACKed a recieve byte. */
+};
+
+typedef struct i2c_slave i2c_slave;
+
+/* Master to slave. */
+typedef int (*i2c_send_cb)(i2c_slave *s, uint8_t data);
+/* Slave to master. */
+typedef int (*i2c_recv_cb)(i2c_slave *s);
+/* Notify the slave of a bus state change. */
+typedef void (*i2c_event_cb)(i2c_slave *s, enum i2c_event event);
+
+struct i2c_slave
+{
+ /* Callbacks to be set by the device. */
+ i2c_event_cb event;
+ i2c_recv_cb recv;
+ i2c_send_cb send;
+
+ /* Remaining fields for internal use by the I2C code. */
+ int address;
+ void *next;
+};
+
+typedef struct i2c_bus i2c_bus;
+
+i2c_bus *i2c_init_bus(void);
+i2c_slave *i2c_slave_init(i2c_bus *bus, int address, int size);
+void i2c_set_slave_address(i2c_slave *dev, int address);
+int i2c_bus_busy(i2c_bus *bus);
+int i2c_start_transfer(i2c_bus *bus, int address, int recv);
+void i2c_end_transfer(i2c_bus *bus);
+void i2c_nack(i2c_bus *bus);
+int i2c_send(i2c_bus *bus, uint8_t data);
+int i2c_recv(i2c_bus *bus);
+
+#endif
diff --git a/hw/pc.c b/hw/pc.c
index a311c42065..a698ba546b 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -897,11 +897,12 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device,
if (pci_enabled && acpi_enabled) {
uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
- piix4_pm_init(pci_bus, piix3_devfn + 3);
+ i2c_bus *smbus;
+
+ /* TODO: Populate SPD eeprom data. */
+ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3);
for (i = 0; i < 8; i++) {
- SMBusDevice *eeprom = smbus_eeprom_device_init(0x50 + i,
- eeprom_buf + (i * 256));
- piix4_smbus_register_device(eeprom, 0x50 + i);
+ smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256));
}
}
diff --git a/hw/smbus.c b/hw/smbus.c
new file mode 100644
index 0000000000..651a7a0b97
--- /dev/null
+++ b/hw/smbus.c
@@ -0,0 +1,303 @@
+/*
+ * QEMU SMBus device emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+/* TODO: Implement PEC. */
+
+#include "vl.h"
+
+//#define DEBUG_SMBUS 1
+
+#ifdef DEBUG_SMBUS
+#define DPRINTF(fmt, args...) \
+do { printf("smbus(%02x): " fmt , dev->i2c.address, ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "smbus: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "smbus: error: " fmt , ##args);} while (0)
+#endif
+
+enum {
+ SMBUS_IDLE,
+ SMBUS_WRITE_DATA,
+ SMBUS_RECV_BYTE,
+ SMBUS_READ_DATA,
+ SMBUS_DONE,
+ SMBUS_CONFUSED = -1
+};
+
+static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
+{
+ DPRINTF("Quick Command %d\n", recv);
+ if (dev->quick_cmd)
+ dev->quick_cmd(dev, recv);
+}
+
+static void smbus_do_write(SMBusDevice *dev)
+{
+ if (dev->data_len == 0) {
+ smbus_do_quick_cmd(dev, 0);
+ } else if (dev->data_len == 1) {
+ DPRINTF("Send Byte\n");
+ if (dev->send_byte) {
+ dev->send_byte(dev, dev->data_buf[0]);
+ }
+ } else {
+ dev->command = dev->data_buf[0];
+ DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
+ if (dev->write_data) {
+ dev->write_data(dev, dev->command, dev->data_buf + 1,
+ dev->data_len - 1);
+ }
+ }
+}
+
+void smbus_i2c_event(i2c_slave *s, enum i2c_event event)
+{
+ SMBusDevice *dev = (SMBusDevice *)s;
+ switch (event) {
+ case I2C_START_SEND:
+ switch (dev->mode) {
+ case SMBUS_IDLE:
+ DPRINTF("Incoming data\n");
+ dev->mode = SMBUS_WRITE_DATA;
+ break;
+ default:
+ BADF("Unexpected send start condition in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ break;
+ }
+ break;
+
+ case I2C_START_RECV:
+ switch (dev->mode) {
+ case SMBUS_IDLE:
+ DPRINTF("Read mode\n");
+ dev->mode = SMBUS_RECV_BYTE;
+ break;
+ case SMBUS_WRITE_DATA:
+ if (dev->data_len == 0) {
+ BADF("Read after write with no data\n");
+ dev->mode = SMBUS_CONFUSED;
+ } else {
+ if (dev->data_len > 1) {
+ smbus_do_write(dev);
+ } else {
+ dev->command = dev->data_buf[0];
+ DPRINTF("%02x: Command %d\n", dev->i2c.address,
+ dev->command);
+ }
+ DPRINTF("Read mode\n");
+ dev->data_len = 0;
+ dev->mode = SMBUS_READ_DATA;
+ }
+ break;
+ default:
+ BADF("Unexpected recv start condition in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ break;
+ }
+ break;
+
+ case I2C_FINISH:
+ switch (dev->mode) {
+ case SMBUS_WRITE_DATA:
+ smbus_do_write(dev);
+ break;
+ case SMBUS_RECV_BYTE:
+ smbus_do_quick_cmd(dev, 1);
+ break;
+ case SMBUS_READ_DATA:
+ BADF("Unexpected stop during receive\n");
+ break;
+ default:
+ /* Nothing to do. */
+ break;
+ }
+ dev->mode = SMBUS_IDLE;
+ dev->data_len = 0;
+ break;
+
+ case I2C_NACK:
+ switch (dev->mode) {
+ case SMBUS_DONE:
+ /* Nothing to do. */
+ break;
+ case SMBUS_READ_DATA:
+ dev->mode = SMBUS_DONE;
+ break;
+ default:
+ BADF("Unexpected NACK in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ break;
+ }
+ }
+}
+
+static int smbus_i2c_recv(i2c_slave *s)
+{
+ SMBusDevice *dev = (SMBusDevice *)s;
+ int ret;
+
+ switch (dev->mode) {
+ case SMBUS_RECV_BYTE:
+ if (dev->receive_byte) {
+ ret = dev->receive_byte(dev);
+ } else {
+ ret = 0;
+ }
+ DPRINTF("Receive Byte %02x\n", ret);
+ dev->mode = SMBUS_DONE;
+ break;
+ case SMBUS_READ_DATA:
+ if (dev->read_data) {
+ ret = dev->read_data(dev, dev->command, dev->data_len);
+ dev->data_len++;
+ } else {
+ ret = 0;
+ }
+ DPRINTF("Read data %02x\n", ret);
+ break;
+ default:
+ BADF("Unexpected read in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int smbus_i2c_send(i2c_slave *s, uint8_t data)
+{
+ SMBusDevice *dev = (SMBusDevice *)s;
+ switch (dev->mode) {
+ case SMBUS_WRITE_DATA:
+ DPRINTF("Write data %02x\n", data);
+ dev->data_buf[dev->data_len++] = data;
+ break;
+ default:
+ BADF("Unexpected write in state %d\n", dev->mode);
+ break;
+ }
+ return 0;
+}
+
+SMBusDevice *smbus_device_init(i2c_bus *bus, int address, int size)
+{
+ SMBusDevice *dev;
+
+ dev = (SMBusDevice *)i2c_slave_init(bus, address, size);
+ dev->i2c.event = smbus_i2c_event;
+ dev->i2c.recv = smbus_i2c_recv;
+ dev->i2c.send = smbus_i2c_send;
+
+ return dev;
+}
+
+/* Master device commands. */
+void smbus_quick_command(i2c_bus *bus, int addr, int read)
+{
+ i2c_start_transfer(bus, addr, read);
+ i2c_end_transfer(bus);
+}
+
+uint8_t smbus_receive_byte(i2c_bus *bus, int addr)
+{
+ uint8_t data;
+
+ i2c_start_transfer(bus, addr, 1);
+ data = i2c_recv(bus);
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return data;
+}
+
+void smbus_send_byte(i2c_bus *bus, int addr, uint8_t data)
+{
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, data);
+ i2c_end_transfer(bus);
+}
+
+uint8_t smbus_read_byte(i2c_bus *bus, int addr, uint8_t command)
+{
+ uint8_t data;
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_start_transfer(bus, addr, 1);
+ data = i2c_recv(bus);
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return data;
+}
+
+void smbus_write_byte(i2c_bus *bus, int addr, uint8_t command, uint8_t data)
+{
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_send(bus, data);
+ i2c_end_transfer(bus);
+}
+
+uint16_t smbus_read_word(i2c_bus *bus, int addr, uint8_t command)
+{
+ uint16_t data;
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_start_transfer(bus, addr, 1);
+ data = i2c_recv(bus);
+ data |= i2c_recv(bus) << 8;
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return data;
+}
+
+void smbus_write_word(i2c_bus *bus, int addr, uint8_t command, uint16_t data)
+{
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_send(bus, data & 0xff);
+ i2c_send(bus, data >> 8);
+ i2c_end_transfer(bus);
+}
+
+int smbus_read_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data)
+{
+ int len;
+ int i;
+
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_start_transfer(bus, addr, 1);
+ len = i2c_recv(bus);
+ if (len > 32)
+ len = 0;
+ for (i = 0; i < len; i++)
+ data[i] = i2c_recv(bus);
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return len;
+}
+
+void smbus_write_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data,
+ int len)
+{
+ int i;
+
+ if (len > 32)
+ len = 32;
+
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_send(bus, len);
+ for (i = 0; i < len; i++)
+ i2c_send(bus, data[i]);
+ i2c_end_transfer(bus);
+}
diff --git a/hw/smbus.h b/hw/smbus.h
index 76fc3c8395..9125896cc9 100644
--- a/hw/smbus.h
+++ b/hw/smbus.h
@@ -25,14 +25,46 @@
typedef struct SMBusDevice SMBusDevice;
struct SMBusDevice {
- uint8_t addr;
+ /* The SMBus protocol is implemented on top of I2C. */
+ i2c_slave i2c;
+
+ /* Callbacks set by the device. */
void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
void (*send_byte)(SMBusDevice *dev, uint8_t val);
uint8_t (*receive_byte)(SMBusDevice *dev);
- void (*write_byte)(SMBusDevice *dev, uint8_t cmd, uint8_t val);
- uint8_t (*read_byte)(SMBusDevice *dev, uint8_t cmd);
- void (*write_word)(SMBusDevice *dev, uint8_t cmd, uint16_t val);
- uint16_t (*read_word)(SMBusDevice *dev, uint8_t cmd);
- void (*write_block)(SMBusDevice *dev, uint8_t cmd, uint8_t len, uint8_t *buf);
- uint8_t (*read_block)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf);
+ /* We can't distinguish between a word write and a block write with
+ length 1, so pass the whole data block including the length byte
+ (if present). The device is responsible figuring out what type of
+ command this is. */
+ void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len);
+ /* Likewise we can't distinguish between defferent reads, or even know
+ the length of the read until the read is complete, so read data a
+ byte at a time. The device is responsible for adding the length
+ byte on block reads. */
+ uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n);
+
+ /* Remaining fields for internal use only. */
+ int mode;
+ int data_len;
+ uint8_t data_buf[34]; /* command + len + 32 bytes of data. */
+ uint8_t command;
};
+
+/* Create a slave device. */
+SMBusDevice *smbus_device_init(i2c_bus *bus, int address, int size);
+
+/* Master device commands. */
+void smbus_quick_command(i2c_bus *bus, int addr, int read);
+uint8_t smbus_receive_byte(i2c_bus *bus, int addr);
+void smbus_send_byte(i2c_bus *bus, int addr, uint8_t data);
+uint8_t smbus_read_byte(i2c_bus *bus, int addr, uint8_t command);
+void smbus_write_byte(i2c_bus *bus, int addr, uint8_t command, uint8_t data);
+uint16_t smbus_read_word(i2c_bus *bus, int addr, uint8_t command);
+void smbus_write_word(i2c_bus *bus, int addr, uint8_t command, uint16_t data);
+int smbus_read_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data);
+void smbus_write_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data,
+ int len);
+
+/* smbus_eeprom.c */
+void smbus_eeprom_device_init(i2c_bus *bus, uint8_t addr, uint8_t *buf);
+
diff --git a/hw/smbus_eeprom.c b/hw/smbus_eeprom.c
index d401b17565..699bd54175 100644
--- a/hw/smbus_eeprom.c
+++ b/hw/smbus_eeprom.c
@@ -58,37 +58,51 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev)
return val;
}
-static void eeprom_write_byte(SMBusDevice *dev, uint8_t cmd, uint8_t val)
+static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+ int n;
#ifdef DEBUG
printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", dev->addr,
- cmd, val);
+ cmd, buf[0]);
#endif
- eeprom->data[cmd] = val;
+ /* An page write operation is not a valid SMBus command.
+ It is a block write without a length byte. Fortunately we
+ get the full block anyway. */
+ /* TODO: Should this set the current location? */
+ if (cmd + len > 256)
+ n = 256 - cmd;
+ else
+ n = len;
+ memcpy(eeprom->data + cmd, buf, n);
+ len -= n;
+ if (len)
+ memcpy(eeprom->data, buf + n, len);
}
-static uint8_t eeprom_read_byte(SMBusDevice *dev, uint8_t cmd)
+static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
- uint8_t val = eeprom->data[cmd];
-#ifdef DEBUG
- printf("eeprom_read_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", dev->addr,
- cmd, val);
-#endif
- return val;
+ /* If this is the first byte then set the current position. */
+ if (n == 0)
+ eeprom->offset = cmd;
+ /* As with writes, we implement block reads without the
+ SMBus length byte. */
+ return eeprom_receive_byte(dev);
}
-SMBusDevice *smbus_eeprom_device_init(uint8_t addr, uint8_t *buf)
+void smbus_eeprom_device_init(i2c_bus *bus, uint8_t addr, uint8_t *buf)
{
- SMBusEEPROMDevice *eeprom = qemu_mallocz(sizeof(SMBusEEPROMDevice));
- eeprom->dev.addr = addr;
+ SMBusEEPROMDevice *eeprom;
+
+ eeprom = (SMBusEEPROMDevice *)smbus_device_init(bus, addr,
+ sizeof(SMBusEEPROMDevice));
+
eeprom->dev.quick_cmd = eeprom_quick_cmd;
eeprom->dev.send_byte = eeprom_send_byte;
eeprom->dev.receive_byte = eeprom_receive_byte;
- eeprom->dev.write_byte = eeprom_write_byte;
- eeprom->dev.read_byte = eeprom_read_byte;
+ eeprom->dev.write_data = eeprom_write_data;
+ eeprom->dev.read_data = eeprom_read_data;
eeprom->data = buf;
eeprom->offset = 0;
- return (SMBusDevice *) eeprom;
}