All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paul Brook <paul@codesourcery.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] I2C/SMBus framework
Date: Sat, 12 May 2007 18:58:35 +0100	[thread overview]
Message-ID: <200705121858.37202.paul@codesourcery.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 446 bytes --]

The attached patch implements an I2C/SMBus framework for qemu.

It provides an API for connecting emulated I2C devices, and implements SMBus 
as a layer on top of that.

Currently the only i2c device in CVS is the piix eeprom. I've tested this as 
much as I can - linux successfully reads the eeprom contents.
I've also used the same framework for several different boards.

I will apply in a day or so if there are no serious objections.

Paul


[-- Attachment #2: patch.qemu_i2c --]
[-- Type: text/x-diff, Size: 23649 bytes --]

Index: Makefile.target
===================================================================
RCS file: /sources/qemu/qemu/Makefile.target,v
retrieving revision 1.169
diff -u -p -r1.169 Makefile.target
--- Makefile.target	8 May 2007 21:05:55 -0000	1.169
+++ Makefile.target	12 May 2007 17:50:21 -0000
@@ -401,6 +401,8 @@ SOUND_HW += fmopl.o adlib.o
 endif
 AUDIODRV+= wavcapture.o
 
+VL_OBJS += i2c.o smbus.o
+
 # SCSI layer
 VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
 
Index: vl.h
===================================================================
RCS file: /sources/qemu/qemu/vl.h,v
retrieving revision 1.234
diff -u -p -r1.234 vl.h
--- vl.h	9 May 2007 20:25:36 -0000	1.234
+++ vl.h	12 May 2007 17:50:21 -0000
@@ -1125,17 +1125,16 @@ int pit_get_out(PITState *pit, int chann
 void pcspk_init(PITState *);
 int pcspk_audio_init(AudioState *, qemu_irq *pic);
 
+#include "hw/i2c.h"
+
 #include "hw/smbus.h"
 
 /* acpi.c */
 extern int acpi_enabled;
-void piix4_pm_init(PCIBus *bus, int devfn);
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn);
 void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
 void acpi_bios_init(void);
 
-/* smbus_eeprom.c */
-SMBusDevice *smbus_eeprom_device_init(uint8_t addr, uint8_t *buf);
-
 /* pc.c */
 extern QEMUMachine pc_machine;
 extern QEMUMachine isapc_machine;
Index: hw/acpi.c
===================================================================
RCS file: /sources/qemu/qemu/hw/acpi.c,v
retrieving revision 1.10
diff -u -p -r1.10 acpi.c
--- hw/acpi.c	15 Apr 2007 23:54:20 -0000	1.10
+++ hw/acpi.c	12 May 2007 17:50:21 -0000
@@ -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
     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);
+            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* opa
     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 devf
     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;
 }
Index: hw/i2c.c
===================================================================
RCS file: hw/i2c.c
diff -N hw/i2c.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hw/i2c.c	12 May 2007 17:50:21 -0000
@@ -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);
+}
+
Index: hw/i2c.h
===================================================================
RCS file: hw/i2c.h
diff -N hw/i2c.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hw/i2c.h	12 May 2007 17:50:21 -0000
@@ -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
Index: hw/pc.c
===================================================================
RCS file: /sources/qemu/qemu/hw/pc.c,v
retrieving revision 1.74
diff -u -p -r1.74 pc.c
--- hw/pc.c	7 Apr 2007 18:14:41 -0000	1.74
+++ hw/pc.c	12 May 2007 17:50:22 -0000
@@ -758,11 +758,12 @@ static void pc_init1(int ram_size, int v
 
     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));
         }
     }
     
Index: hw/smbus.c
===================================================================
RCS file: hw/smbus.c
diff -N hw/smbus.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hw/smbus.c	12 May 2007 17:50:22 -0000
@@ -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);
+}
Index: hw/smbus.h
===================================================================
RCS file: /sources/qemu/qemu/hw/smbus.h,v
retrieving revision 1.1
diff -u -p -r1.1 smbus.h
--- hw/smbus.h	2 Feb 2007 03:13:18 -0000	1.1
+++ hw/smbus.h	12 May 2007 17:50:22 -0000
@@ -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);
+
Index: hw/smbus_eeprom.c
===================================================================
RCS file: /sources/qemu/qemu/hw/smbus_eeprom.c,v
retrieving revision 1.1
diff -u -p -r1.1 smbus_eeprom.c
--- hw/smbus_eeprom.c	2 Feb 2007 03:13:18 -0000	1.1
+++ hw/smbus_eeprom.c	12 May 2007 17:50:22 -0000
@@ -58,37 +58,51 @@ static uint8_t eeprom_receive_byte(SMBus
     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;
 }

             reply	other threads:[~2007-05-12 18:06 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-05-12 17:58 Paul Brook [this message]
2007-05-13 15:32 ` [Qemu-devel] I2C/SMBus framework Thiemo Seufer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200705121858.37202.paul@codesourcery.com \
    --to=paul@codesourcery.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.