qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] Maxim MAX7310 GPIO expander. I2C bus support.
@ 2007-03-16 20:46 andrzej zaborowski
  0 siblings, 0 replies; only message in thread
From: andrzej zaborowski @ 2007-03-16 20:46 UTC (permalink / raw)
  To: qemu-devel

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

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

Cheers,
Andrew

[-- Attachment #2: 0002-Maxim-MAX7310-GPIO-expander.-I2C-bus-support.txt --]
[-- Type: text/plain, Size: 9745 bytes --]

From 0babece5fd378a12a71c43791c76308751c6dc96 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <balrog@zabor.org>
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 <balrog@zabor.org>
+ *
+ * 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 <balrog@zabor.org>
+ *
+ * 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


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2007-03-16 20:48 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-16 20:46 [Qemu-devel] [PATCH] Maxim MAX7310 GPIO expander. I2C bus support andrzej zaborowski

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).