diff mbox

[5/9] Create a Versatile I2C device using the I2C bitbanging module

Message ID 1250281397-7660-6-git-send-email-benoit.canet@gmail.com
State Superseded
Headers show

Commit Message

Benoit Canet Aug. 14, 2009, 8:23 p.m. UTC
Signed-off-by: Benoit Canet <benoit.canet@gmail.com>
---
 Makefile.target    |    2 +-
 hw/versatile_i2c.c |  119 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 1 deletions(-)
 create mode 100644 hw/versatile_i2c.c
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index 6b28931..6a78ace 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -578,7 +578,7 @@  endif
 
 obj-arm-y = integratorcp.o versatilepb.o smc91c111.o arm_pic.o arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
-obj-arm-y += versatile_pci.o
+obj-arm-y += versatile_pci.o versatile_i2c.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o mpcore.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
 obj-arm-y += pl061.o
diff --git a/hw/versatile_i2c.c b/hw/versatile_i2c.c
new file mode 100644
index 0000000..c98cef2
--- /dev/null
+++ b/hw/versatile_i2c.c
@@ -0,0 +1,119 @@ 
+/*
+ * Versatile i2c interface
+ *
+ * Copyright (c) 2009 BenoƮt Canet <benoit.canet@gmail.com>
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "sysbus.h"
+
+#define I2C_CONTROL   0x0
+#define I2C_CONTROLS  0x0
+#define I2C_CONTROLC  0x4
+
+#define DATA    1<<1
+#define CLOCK   1
+
+typedef struct versatile_i2c_state {
+    SysBusDevice busdev;
+    int         last_clock;
+    int         last_data;
+    int         read_data;  /* data from decoder   */
+    qemu_irq    out[2];     /* 0 = data, 1 = clock */
+} versatile_i2c_state;
+
+static void versatile_i2c_gpio_set(void *opaque, int irq, int level)
+{
+    versatile_i2c_state *s = (versatile_i2c_state *) opaque;
+
+    if (irq == 0)
+        s->read_data = level;
+}
+
+static uint32_t versatile_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+    versatile_i2c_state *s = (versatile_i2c_state *) opaque;
+
+    switch (offset) {
+    case I2C_CONTROL:
+        return (s->read_data << 1) | s->last_clock;
+    default:
+        hw_error("versatile_i2c_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void versatile_i2c_write(void *opaque, target_phys_addr_t offset,
+                        uint32_t value)
+{
+    versatile_i2c_state *s = (versatile_i2c_state *) opaque;
+    int data  = s->last_data;
+    int clock = s->last_clock;
+
+    switch (offset) {
+    case I2C_CONTROLS:
+        if (value & DATA)
+            data = 1;
+        if (value & CLOCK)
+            clock = 1;
+        break;
+    case I2C_CONTROLC:
+        if (value & DATA)
+            data = 0;
+        if (value & CLOCK)
+            clock = 0;
+        break;
+    default:
+        hw_error("versatile_i2c_write: Bad offset %x\n", (int)offset);
+    }
+
+    s->last_clock = clock;
+    s->last_data  = data;
+
+    qemu_set_irq(s->out[0], data);
+    qemu_set_irq(s->out[1], clock);
+}
+
+static CPUReadMemoryFunc *versatile_i2c_readfn[] = {
+   versatile_i2c_read,
+   versatile_i2c_read,
+   versatile_i2c_read
+};
+
+static CPUWriteMemoryFunc *versatile_i2c_writefn[] = {
+   versatile_i2c_write,
+   versatile_i2c_write,
+   versatile_i2c_write
+};
+
+static void versatile_i2c_reset(versatile_i2c_state *s)
+{
+    s->last_clock = 1;
+    s->last_data  = 1;
+    qemu_set_irq(s->out[0], s->last_data);
+    qemu_set_irq(s->out[1], s->last_clock);
+}
+
+static void versatile_i2c_init(SysBusDevice *dev)
+{
+    versatile_i2c_state *s = FROM_SYSBUS(versatile_i2c_state, dev);
+    int iomemtype;
+
+    versatile_i2c_reset(s);
+
+    iomemtype = cpu_register_io_memory(versatile_i2c_readfn,
+                                       versatile_i2c_writefn, s);
+    sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+    qdev_init_gpio_in(&dev->qdev, versatile_i2c_gpio_set, 1);
+    qdev_init_gpio_out(&dev->qdev, s->out, 2);
+}
+
+static void versatile_i2c_register_devices(void)
+{
+    sysbus_register_dev("versatile,i2c", sizeof(versatile_i2c_state),
+                        versatile_i2c_init);
+}
+
+device_init(versatile_i2c_register_devices)