Adds an 8-port GPIO expansion chip that connects over I2C and a simple
I2C bus api.

Cheers,
Andrew
From 0babece5fd378a12a71c43791c76308751c6dc96 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <[EMAIL PROTECTED]>
Date: Fri, 16 Mar 2007 15:11:23 +0100
Subject: [PATCH] Maxim MAX7310 GPIO expander. I2C bus support.

---
 hw/i2c.h     |  160 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/max7310.c |  176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vl.h         |    4 +
 3 files changed, 340 insertions(+), 0 deletions(-)

diff --git a/hw/i2c.h b/hw/i2c.h
new file mode 100644
index 0000000..d866f28
--- /dev/null
+++ b/hw/i2c.h
@@ -0,0 +1,160 @@
+/*
+ * Simplified I2C(tm) bus / SMBus(tm).
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <[EMAIL PROTECTED]>
+ *
+ * This file is licensed under GNU GPL v2.
+ */
+
+#define I2C_MAX_MSG    4096
+
+struct i2c_slave_s {
+    uint8_t address;
+    int (*tx)(void *opaque, uint8_t *data, int len);
+    void (*start)(void *opaque, int dir);
+    void (*stop)(void *opaque);
+    void *opaque;
+};
+
+struct i2c_bus_s {
+    struct i2c_slave_s *slave[0x80];
+    uint8_t current;
+    int dir;
+};
+
+/* I2C master - drives the clock signal on a bus.  There can be multiple
+ * masters on one bus.  */
+struct i2c_master_s {
+    struct i2c_bus_s *bus;
+    uint8_t message[I2C_MAX_MSG];
+    int message_len;
+    int ack;
+
+    uint8_t data;
+};
+
+static inline int i2c_bus_start(struct i2c_bus_s *bus, uint8_t byte)
+{
+    struct i2c_slave_s *slave;
+
+    bus->current = byte >> 1;
+    bus->dir = byte & 1;
+    slave = bus->slave[bus->current];
+
+    return !slave;
+}
+
+static inline int i2c_start_submit(struct i2c_bus_s *bus)
+{
+    struct i2c_slave_s *slave = bus->slave[bus->current];
+    if (!slave)
+        return 1;
+
+    if (slave->start)
+        slave->start(slave->opaque, bus->dir);
+    return 0;
+}
+
+static inline int i2c_stop_submit(struct i2c_bus_s *bus)
+{
+    struct i2c_slave_s *slave = bus->slave[bus->current];
+    if (!slave)
+        return 1;
+
+    if (slave->stop)
+        slave->stop(slave->opaque);
+    return 0;
+}
+
+static inline int i2c_msg_submit(struct i2c_bus_s *bus,
+                uint8_t message[], int len)
+{
+    struct i2c_slave_s *slave = bus->slave[bus->current];
+    if (!slave)
+        return 1;
+
+    return slave->tx ? slave->tx(slave->opaque, message, len) : 1;
+}
+
+static inline void i2c_master_submit(struct i2c_master_s *master,
+                int start, int stop)
+{
+    int ret = 0;
+
+    if (!master->bus) {
+        master->ack = 0;
+        return;
+    }
+
+    if (start) {
+        if (master->message_len)
+            if (!master->bus->dir) {   /* Master --> Slave */
+                i2c_start_submit(master->bus);
+                ret = i2c_msg_submit(master->bus,
+                                master->message, master->message_len);
+                master->message_len = 0;
+            }
+
+        ret = i2c_bus_start(master->bus, master->data);
+        master->message_len = 0;
+
+        if (master->bus->dir) {                /* Master <-- Slave */
+            i2c_start_submit(master->bus);
+            master->message_len = 1;
+            if (stop)
+                i2c_msg_submit(master->bus, master->message, 0);
+        }
+    } else if (stop < 2) {
+        if (!master->bus->dir) {       /* Master --> Slave */
+            if (master->message_len < I2C_MAX_MSG)
+                master->message[master->message_len ++] = master->data;
+        } else {                       /* Master <-- Slave */
+            ret = i2c_msg_submit(master->bus,
+                            master->message, master->message_len);
+            master->data = master->message[0];
+        }
+    }
+
+    if (stop) {
+        if (!master->bus->dir) {       /* Master --> Slave */
+            i2c_start_submit(master->bus);
+            ret = i2c_msg_submit(master->bus,
+                            master->message, master->message_len);
+            master->message_len = 0;
+        }
+
+        i2c_stop_submit(master->bus);
+    }
+
+    master->ack = !ret;
+}
+
+/* Call with zero `addr' to detach.  */
+static inline void i2c_slave_attach(struct i2c_bus_s *bus, uint8_t addr,
+                struct i2c_slave_s *dev)
+{
+    if (addr >= 0x80)
+        cpu_abort(cpu_single_env, "bad I2C address");
+
+    if (dev->address)
+        bus->slave[dev->address] = 0;
+
+    dev->address = addr;
+
+    if (dev->address)
+        bus->slave[dev->address] = dev;
+}
+
+static inline void i2c_master_attach(struct i2c_bus_s *bus,
+                struct i2c_master_s *dev)
+{
+    dev->bus = bus;
+}
+
+/* max7310.c */
+struct i2c_slave_s *max7310_init(void);
+void max7310_reset(struct i2c_slave_s *i2c);
+void max7310_gpio_set(struct i2c_slave_s *i2c, int line, int level);
+void max7310_gpio_handler_set(struct i2c_slave_s *i2c, int line,
+                gpio_handler_t handler, void *opaque);
diff --git a/hw/max7310.c b/hw/max7310.c
new file mode 100644
index 0000000..7e8813d
--- /dev/null
+++ b/hw/max7310.c
@@ -0,0 +1,176 @@
+/*
+ * MAX7310 8-port GPIO expansion chip.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <[EMAIL PROTECTED]>
+ *
+ * This file is licensed under GNU GPL v2.
+ */
+
+#include "vl.h"
+
+struct max7310_s {
+    uint8_t level;
+    uint8_t direction;
+    uint8_t polarity;
+    uint8_t status;
+    uint8_t command;
+    int i2c_dir;
+    struct i2c_slave_s i2c;
+    struct {
+        gpio_handler_t fn;
+        void *opaque;
+    } handler[8];
+};
+
+void max7310_reset(struct i2c_slave_s *i2c)
+{
+    struct max7310_s *s = (struct max7310_s *) i2c->opaque;
+    s->level &= s->direction;
+    s->direction = 0xff;
+    s->polarity = 0xf0;
+    s->status = 0x01;
+    s->command = 0x00;
+}
+
+static void max7310_start(void *opaque, int dir)
+{
+    struct max7310_s *s = (struct max7310_s *) opaque;
+    s->i2c_dir = dir;
+}
+
+static int max7310_read(void *opaque, uint8_t *data, int len)
+{
+    struct max7310_s *s = (struct max7310_s *) opaque;
+
+    switch (s->command) {
+    case 0x00: /* Input port */
+        memset(data, s->level ^ s->polarity, len);
+        break;
+
+    case 0x01: /* Output port */
+        memset(data, s->level & ~s->direction, len);
+        break;
+
+    case 0x02: /* Polarity inversion */
+        memset(data, s->polarity, len);
+        break;
+
+    case 0x03: /* Configuration */
+        memset(data, s->direction, len);
+        break;
+
+    case 0x04: /* Timeout */
+        memset(data, s->status, len);
+        break;
+
+    case 0xff: /* Reserved */
+        memset(data, 0xff, len);
+        break;
+
+    default:
+#ifdef VERBOSE
+        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
+#endif
+        return 1;
+    }
+    return 0;
+}
+
+static int max7310_write(void *opaque, uint8_t *data, int len)
+{
+    struct max7310_s *s = (struct max7310_s *) opaque;
+    uint8_t diff;
+    int line;
+
+    if (len >= 1)
+        s->command = data[0];
+
+    if (len < 2) {
+#ifdef VERBOSE
+        printf("%s: message too short (%i bytes)\n", __FUNCTION__, len);
+#endif
+        return 0;
+    }
+
+    switch (s->command) {
+    case 0x01: /* Output port */
+        for (diff = (data[1] ^ s->level) & ~s->direction; diff;
+                        diff &= ~(1 << line)) {
+            line = ffs(diff) - 1;
+            if (s->handler[line].fn)
+                s->handler[line].fn(line, (data[1] >> line) & 1,
+                                s->handler[line].opaque);
+        }
+        s->level = (s->level & s->direction) | (data[1] & ~s->direction);
+        break;
+
+    case 0x02: /* Polarity inversion */
+        s->polarity = data[1];
+        break;
+
+    case 0x03: /* Configuration */
+        s->level &= ~(s->direction ^ data[1]);
+        s->direction = data[1];
+        break;
+
+    case 0x04: /* Timeout */
+        s->status = data[1];
+        break;
+
+    default:
+#ifdef VERBOSE
+        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
+#endif
+        return 1;
+    }
+
+    return 0;
+}
+
+static int max7310_tx(void *opaque, uint8_t *data, int len)
+{
+    struct max7310_s *s = (struct max7310_s *) opaque;
+    if (len) {
+        if (s->i2c_dir)
+            return max7310_write(opaque, data, len);
+        else
+            return max7310_read(opaque, data, len);
+    }
+
+    return 0;
+}
+
+struct i2c_slave_s *max7310_init(void)
+{
+    struct max7310_s *s = qemu_mallocz(sizeof(struct max7310_s));
+    s->i2c.opaque = s;
+    s->i2c.tx = max7310_tx;
+    s->i2c.start = max7310_start;
+
+    max7310_reset(&s->i2c);
+    return &s->i2c;
+}
+
+void max7310_gpio_set(struct i2c_slave_s *i2c, int line, int level)
+{
+    struct max7310_s *s = (struct max7310_s *) i2c->opaque;
+    if (line >= sizeof(s->handler) / sizeof(*s->handler) || line  < 0)
+        cpu_abort(cpu_single_env, "bad GPIO line");
+
+    if (level)
+        s->level |= s->direction & (1 << line);
+    else
+        s->level &= ~(s->direction & (1 << line));
+}
+
+void max7310_gpio_handler_set(struct i2c_slave_s *i2c, int line,
+                gpio_handler_t handler, void *opaque)
+{
+    struct max7310_s *s = (struct max7310_s *) i2c->opaque;
+    if (line >= sizeof(s->handler) / sizeof(*s->handler) || line  < 0)
+        cpu_abort(cpu_single_env, "bad GPIO line");
+
+    s->handler[line].fn = handler;
+    s->handler[line].opaque = opaque;
+}
diff --git a/vl.h b/vl.h
index 95f3139..b30fb87 100644
--- a/vl.h
+++ b/vl.h
@@ -1438,6 +1438,10 @@ struct pcmcia_card_s {
 /* dscm1xxxx.c */
 struct pcmcia_card_s *dscm1xxxx_init(BlockDriverState *bdrv);
 
+typedef void (*gpio_handler_t)(int line, int level, void *opaque);
+
+#include "hw/i2c.h"
+
 #include "gdbstub.h"
 
 #endif /* defined(QEMU_TOOL) */
-- 
1.4.4.3

_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to