qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] GPIO model for Zynq SoC
@ 2014-12-30 13:13 Colin Leitner
  2014-12-30 13:13 ` [Qemu-devel] [PATCH 1/2] zynq_gpio: " Colin Leitner
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: Colin Leitner @ 2014-12-30 13:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Hello everyone,

I wrote the Zynq GPIO model a while ago and it proved useful in an internal
project, so maybe others will find it useful too.

Cheers,
    Colin

Colin Leitner (2):
  zynq_gpio: GPIO model for Zynq SoC
  xilinx_zynq: Add zynq_gpio to the machine

 hw/arm/xilinx_zynq.c  |    2 +
 hw/gpio/Makefile.objs |    1 +
 hw/gpio/zynq_gpio.c   |  441 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 444 insertions(+)
 create mode 100644 hw/gpio/zynq_gpio.c

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH 1/2] zynq_gpio: GPIO model for Zynq SoC
  2014-12-30 13:13 [Qemu-devel] [PATCH 0/2] GPIO model for Zynq SoC Colin Leitner
@ 2014-12-30 13:13 ` Colin Leitner
  2014-12-30 23:24   ` Peter Crosthwaite
  2014-12-30 13:13 ` [Qemu-devel] [PATCH 2/2] xilinx_zynq: Add zynq_gpio to the machine Colin Leitner
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 18+ messages in thread
From: Colin Leitner @ 2014-12-30 13:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Based on the pl061 model. This model implements all four banks with 32 I/Os
each.

The I/Os are placed in four named groups:

 * mio_in/out[0..63], where mio_in/out[0..31] map to bank 0 and the rest to
   bank 1
 * emio_in/out[0..63], where emio_in/out[0..31] map to bank 2 and the rest to
   bank 3

Basic I/O tested with the Zynq GPIO driver in Linux 3.12.

Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/gpio/Makefile.objs |    1 +
 hw/gpio/zynq_gpio.c   |  441 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 442 insertions(+)
 create mode 100644 hw/gpio/zynq_gpio.c

diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 1abcf17..32b99e0 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
 common-obj-$(CONFIG_E500) += mpc8xxx.o
 
 obj-$(CONFIG_OMAP) += omap_gpio.o
+obj-$(CONFIG_ZYNQ) += zynq_gpio.o
diff --git a/hw/gpio/zynq_gpio.c b/hw/gpio/zynq_gpio.c
new file mode 100644
index 0000000..2119561
--- /dev/null
+++ b/hw/gpio/zynq_gpio.c
@@ -0,0 +1,441 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * Based on the PL061 model:
+ *   Copyright (c) 2007 CodeSourcery.
+ *   Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/*
+ * We model all banks as if they were fully populated. MIO pins are usually
+ * limited to 54 pins, but this is probably device dependent and shouldn't
+ * cause too much trouble. One noticable difference is the reset value of
+ * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
+ *
+ * The output enable pins are not modeled.
+ */
+
+#include "hw/sysbus.h"
+
+//#define DEBUG_ZYNQ_GPIO 1
+
+#ifdef DEBUG_ZYNQ_GPIO
+#define DPRINTF(fmt, ...) \
+do { printf("zynq-gpio: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "zynq-gpio: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "zynq-gpio: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define TYPE_ZYNQ_GPIO "zynq-gpio"
+#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
+
+typedef struct {
+    uint32_t mask_data;
+    uint32_t out_data;
+    uint32_t old_out_data;
+    uint32_t in_data;
+    uint32_t old_in_data;
+    uint32_t dir;
+    uint32_t oen;
+    uint32_t imask;
+    uint32_t istat;
+    uint32_t itype;
+    uint32_t ipolarity;
+    uint32_t iany;
+
+    qemu_irq *out;
+} GPIOBank;
+
+typedef struct ZynqGPIOState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    GPIOBank banks[4];
+    qemu_irq mio_out[64];
+    qemu_irq emio_out[64];
+    qemu_irq irq;
+} ZynqGPIOState;
+
+static void zynq_gpio_update_out(GPIOBank *b)
+{
+    uint32_t changed;
+    uint32_t mask;
+    uint32_t out;
+    int i;
+
+    DPRINTF("dir = %d, data = %d\n", b->dir, b->out_data);
+
+    /* Outputs float high.  */
+    /* FIXME: This is board dependent.  */
+    out = (b->out_data & b->dir) | ~b->dir;
+    changed = b->old_out_data ^ out;
+    if (changed) {
+        b->old_out_data = out;
+        for (i = 0; i < 32; i++) {
+            mask = 1 << i;
+            if (changed & mask) {
+                DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
+                qemu_set_irq(b->out[i], (out & mask) != 0);
+            }
+        }
+    }
+}
+
+static void zynq_gpio_update_in(GPIOBank *b)
+{
+    uint32_t changed;
+    uint32_t mask;
+    int i;
+
+    changed = b->old_in_data ^ b->in_data;
+    if (changed) {
+        b->old_in_data = b->in_data;
+        for (i = 0; i < 32; i++) {
+            mask = 1 << i;
+            if (changed & mask) {
+                DPRINTF("Changed input %d = %d\n", i, (b->in_data & mask) != 0);
+
+                if (b->itype & mask) {
+                    /* Edge interrupt */
+                    if (b->iany & mask) {
+                        /* Any edge triggers the interrupt */
+                        b->istat |= mask;
+                    } else {
+                        /* Edge is selected by INT_POLARITY */
+                        b->istat |= ~(b->in_data ^ b->ipolarity) & mask;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Level interrupt */
+    b->istat |= ~(b->in_data ^ b->ipolarity) & ~b->itype;
+
+    DPRINTF("istat = %08X\n", b->istat);
+}
+
+static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
+{
+    int b;
+    uint32_t istat = 0;
+
+    for (b = 0; b < 4; b++) {
+        istat |= s->banks[b].istat & ~s->banks[b].imask;
+    }
+
+    DPRINTF("IRQ = %d\n", istat != 0);
+
+    qemu_set_irq(s->irq, istat != 0);
+}
+
+static void zynq_gpio_update(ZynqGPIOState *s)
+{
+    int b;
+
+    for (b = 0; b < 4; b++) {
+        zynq_gpio_update_out(&s->banks[b]);
+        zynq_gpio_update_in(&s->banks[b]);
+    }
+
+    zynq_gpio_set_in_irq(s);
+}
+
+static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
+                               unsigned size)
+{
+    ZynqGPIOState *s = (ZynqGPIOState *)opaque;
+    int b;
+    GPIOBank *bank;
+
+    switch (offset) {
+    case 0x000: /* MASK_DATA_0_LSW */
+        return (s->banks[0].mask_data >> 0) & 0xffff;
+    case 0x004: /* MASK_DATA_0_MSW */
+        return (s->banks[0].mask_data >> 16) & 0xffff;
+    case 0x008: /* MASK_DATA_1_LSW */
+        return (s->banks[1].mask_data >> 0) & 0xffff;
+    case 0x00C: /* MASK_DATA_1_MSW */
+        return (s->banks[1].mask_data >> 16) & 0xffff;
+    case 0x010: /* MASK_DATA_2_LSW */
+        return (s->banks[2].mask_data >> 0) & 0xffff;
+    case 0x014: /* MASK_DATA_2_MSW */
+        return (s->banks[2].mask_data >> 16) & 0xffff;
+    case 0x018: /* MASK_DATA_3_LSW */
+        return (s->banks[3].mask_data >> 0) & 0xffff;
+    case 0x01C: /* MASK_DATA_3_MSW */
+        return (s->banks[3].mask_data >> 16) & 0xffff;
+
+    case 0x040: /* DATA_0 */
+        return s->banks[0].out_data;
+    case 0x044: /* DATA_1 */
+        return s->banks[1].out_data;
+    case 0x048: /* DATA_2 */
+        return s->banks[2].out_data;
+    case 0x04C: /* DATA_3 */
+        return s->banks[3].out_data;
+
+    case 0x060: /* DATA_0_RO */
+        return s->banks[0].in_data;
+    case 0x064: /* DATA_1_RO */
+        return s->banks[1].in_data;
+    case 0x068: /* DATA_2_RO */
+        return s->banks[2].in_data;
+    case 0x06C: /* DATA_3_RO */
+        return s->banks[3].in_data;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - 0x200 - b * 0x40) {
+    case 0x04: /* DIRM_x */
+        return bank->dir;
+    case 0x08: /* OEN_x */
+        return bank->oen;
+    case 0x0C: /* INT_MASK_x */
+        return bank->imask;
+    case 0x10: /* INT_EN_x */
+        return 0;
+    case 0x14: /* INT_DIS_x */
+        return 0;
+    case 0x18: /* INT_STAT_x */
+        return bank->istat;
+    case 0x1C: /* INT_TYPE_x */
+        return bank->itype;
+    case 0x20: /* INT_POLARITY_x */
+        return bank->ipolarity;
+    case 0x24: /* INT_ANY_x */
+        return bank->iany;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void zynq_gpio_mask_data(GPIOBank *bank, int bit_offset,
+                                uint32_t mask_data)
+{
+    DPRINTF("mask data offset = %d, mask_data = %08X\n", bit_offset, mask_data);
+
+    /* MASK_DATA registers are R/W on their data part */
+    bank->mask_data = (bank->mask_data & ~(0xffff << bit_offset)) |
+                     ((mask_data & 0xffff) << bit_offset);
+    bank->out_data = (bank->out_data & ~((~mask_data >> 16) << bit_offset)) |
+                     ((mask_data & 0xffff) << bit_offset);
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_data(GPIOBank *bank, uint32_t data)
+{
+    bank->out_data = data;
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_write(void *opaque, hwaddr offset,
+                            uint64_t value, unsigned size)
+{
+    ZynqGPIOState *s = (ZynqGPIOState *)opaque;
+    int b;
+    GPIOBank *bank;
+
+    switch (offset) {
+    case 0x000: /* MASK_DATA_0_LSW */
+        zynq_gpio_mask_data(&s->banks[0], 0, value);
+        return;
+    case 0x004: /* MASK_DATA_0_MSW */
+        zynq_gpio_mask_data(&s->banks[0], 16, value);
+        return;
+    case 0x008: /* MASK_DATA_1_LSW */
+        zynq_gpio_mask_data(&s->banks[1], 0, value);
+        return;
+    case 0x00C: /* MASK_DATA_1_MSW */
+        zynq_gpio_mask_data(&s->banks[1], 16, value);
+        return;
+    case 0x010: /* MASK_DATA_2_LSW */
+        zynq_gpio_mask_data(&s->banks[2], 0, value);
+        return;
+    case 0x014: /* MASK_DATA_2_MSW */
+        zynq_gpio_mask_data(&s->banks[2], 16, value);
+        return;
+    case 0x018: /* MASK_DATA_3_LSW */
+        zynq_gpio_mask_data(&s->banks[3], 0, value);
+        return;
+    case 0x01C: /* MASK_DATA_3_MSW */
+        zynq_gpio_mask_data(&s->banks[3], 16, value);
+        return;
+
+    case 0x040: /* DATA_0 */
+        zynq_gpio_data(&s->banks[0], value);
+        return;
+    case 0x044: /* DATA_1 */
+        zynq_gpio_data(&s->banks[1], value);
+        return;
+    case 0x048: /* DATA_2 */
+        zynq_gpio_data(&s->banks[2], value);
+        return;
+    case 0x04C: /* DATA_3 */
+        zynq_gpio_data(&s->banks[3], value);
+        return;
+
+    case 0x060: /* DATA_0_RO */
+    case 0x064: /* DATA_1_RO */
+    case 0x068: /* DATA_2_RO */
+    case 0x06C: /* DATA_3_RO */
+        return;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %x\n", (int)offset);
+        return;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - 0x200 - b * 0x40) {
+    case 0x04: /* DIRM_x */
+        bank->dir = value;
+        break;
+    case 0x08: /* OEN_x */
+        bank->oen = value;
+        break;
+    case 0x0C: /* INT_MASK_x */
+        return;
+    case 0x10: /* INT_EN_x */
+        bank->imask &= ~value;
+        break;
+    case 0x14: /* INT_DIS_x */
+        bank->imask |= value;
+        break;
+    case 0x18: /* INT_STAT_x */
+        bank->istat &= ~value;
+        break;
+    case 0x1C: /* INT_TYPE_x */
+        bank->itype = value;
+        break;
+    case 0x20: /* INT_POLARITY_x */
+        bank->ipolarity = value;
+        break;
+    case 0x24: /* INT_ANY_x */
+        bank->iany = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %x\n", (int)offset);
+        return;
+    }
+
+    zynq_gpio_update(s);
+}
+
+static void zynq_gpio_reset(ZynqGPIOState *s)
+{
+    int b;
+
+    for (b = 0; b < 4; b++) {
+        s->banks[b].mask_data = 0x00000000;
+        s->banks[b].out_data = 0x00000000;
+        s->banks[b].dir = 0x00000000;
+        s->banks[b].oen = 0x00000000;
+        s->banks[b].imask = 0x00000000;
+        s->banks[b].istat = 0x00000000;
+        s->banks[b].itype = 0xffffffff;
+        s->banks[b].ipolarity = 0x00000000;
+        s->banks[b].iany = 0x00000000;
+    }
+}
+
+static void zynq_gpio_set_irq(void * opaque, int irq, int level)
+{
+    ZynqGPIOState *s = (ZynqGPIOState *)opaque;
+
+    GPIOBank *bank = &s->banks[irq / 32];
+    uint32_t mask = 1 << (irq % 32);
+
+    bank->in_data &= ~mask;
+    if (level)
+        bank->in_data |= mask;
+
+    zynq_gpio_update_in(bank);
+
+    zynq_gpio_set_in_irq(s);
+}
+
+static void zynq_gpio_set_mio_irq(void * opaque, int irq, int level)
+{
+    zynq_gpio_set_irq(opaque, irq + 0, level);
+}
+
+static void zynq_gpio_set_emio_irq(void * opaque, int irq, int level)
+{
+    zynq_gpio_set_irq(opaque, irq + 64, level);
+}
+
+static const MemoryRegionOps zynq_gpio_ops = {
+    .read = zynq_gpio_read,
+    .write = zynq_gpio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int zynq_gpio_initfn(SysBusDevice *sbd)
+{
+    DeviceState *dev = DEVICE(sbd);
+    ZynqGPIOState *s = ZYNQ_GPIO(dev);
+
+    s->banks[0].out = &s->mio_out[0];
+    s->banks[1].out = &s->mio_out[32];
+    s->banks[2].out = &s->emio_out[0];
+    s->banks[3].out = &s->emio_out[32];
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &zynq_gpio_ops, s, "zynq_gpio", 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_mio_irq, "mio_in", 64);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_emio_irq, "emio_in", 64);
+
+    qdev_init_gpio_out_named(dev, s->mio_out, "mio_out", 64);
+    qdev_init_gpio_out_named(dev, s->emio_out, "emio_out", 64);
+
+    zynq_gpio_reset(s);
+
+    return 0;
+}
+
+static void zynq_gpio_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = zynq_gpio_initfn;
+}
+
+static const TypeInfo zynq_gpio_info = {
+    .name          = TYPE_ZYNQ_GPIO,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ZynqGPIOState),
+    .class_init    = zynq_gpio_class_init,
+};
+
+static void zynq_gpio_register_type(void)
+{
+    type_register_static(&zynq_gpio_info);
+}
+
+type_init(zynq_gpio_register_type)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH 2/2] xilinx_zynq: Add zynq_gpio to the machine
  2014-12-30 13:13 [Qemu-devel] [PATCH 0/2] GPIO model for Zynq SoC Colin Leitner
  2014-12-30 13:13 ` [Qemu-devel] [PATCH 1/2] zynq_gpio: " Colin Leitner
@ 2014-12-30 13:13 ` Colin Leitner
  2014-12-31 14:06 ` [Qemu-devel] [PATCH v2 0/2] Reworked Zynq GPIO model Colin Leitner
  2015-01-06 22:26 ` [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model Colin Leitner
  3 siblings, 0 replies; 18+ messages in thread
From: Colin Leitner @ 2014-12-30 13:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/arm/xilinx_zynq.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 06e6e24..6d8c0d9 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -202,6 +202,8 @@ static void zynq_init(MachineState *machine)
     zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
     zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
 
+    sysbus_create_simple("zynq-gpio", 0xE000A000, pic[52-IRQ_OFFSET]);
+
     sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
     sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PATCH 1/2] zynq_gpio: GPIO model for Zynq SoC
  2014-12-30 13:13 ` [Qemu-devel] [PATCH 1/2] zynq_gpio: " Colin Leitner
@ 2014-12-30 23:24   ` Peter Crosthwaite
  2014-12-31  1:03     ` Colin Leitner
  0 siblings, 1 reply; 18+ messages in thread
From: Peter Crosthwaite @ 2014-12-30 23:24 UTC (permalink / raw)
  To: Colin Leitner; +Cc: Colin Leitner, qemu-devel@nongnu.org Developers

On Tue, Dec 30, 2014 at 5:13 AM, Colin Leitner
<colin.leitner@googlemail.com> wrote:
> Based on the pl061 model. This model implements all four banks with 32 I/Os
> each.
>
> The I/Os are placed in four named groups:
>
>  * mio_in/out[0..63], where mio_in/out[0..31] map to bank 0 and the rest to
>    bank 1
>  * emio_in/out[0..63], where emio_in/out[0..31] map to bank 2 and the rest to
>    bank 3
>
> Basic I/O tested with the Zynq GPIO driver in Linux 3.12.
>
> Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
> ---
>  hw/gpio/Makefile.objs |    1 +
>  hw/gpio/zynq_gpio.c   |  441 +++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 442 insertions(+)
>  create mode 100644 hw/gpio/zynq_gpio.c
>
> diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
> index 1abcf17..32b99e0 100644
> --- a/hw/gpio/Makefile.objs
> +++ b/hw/gpio/Makefile.objs
> @@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
>  common-obj-$(CONFIG_E500) += mpc8xxx.o
>
>  obj-$(CONFIG_OMAP) += omap_gpio.o
> +obj-$(CONFIG_ZYNQ) += zynq_gpio.o

I think we are trying to slowly covert filenames to use - separators.
Should be followed for new files.

> diff --git a/hw/gpio/zynq_gpio.c b/hw/gpio/zynq_gpio.c
> new file mode 100644
> index 0000000..2119561
> --- /dev/null
> +++ b/hw/gpio/zynq_gpio.c
> @@ -0,0 +1,441 @@
> +/*
> + * Zynq General Purpose IO
> + *
> + * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
> + *
> + * Based on the PL061 model:
> + *   Copyright (c) 2007 CodeSourcery.
> + *   Written by Paul Brook
> + *
> + * This code is licensed under the GPL.
> + */
> +
> +/*
> + * We model all banks as if they were fully populated. MIO pins are usually
> + * limited to 54 pins, but this is probably device dependent and shouldn't
> + * cause too much trouble. One noticable difference is the reset value of

"noticeable"

> + * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
> + *
> + * The output enable pins are not modeled.
> + */
> +
> +#include "hw/sysbus.h"
> +
> +//#define DEBUG_ZYNQ_GPIO 1

Don't worry about commented out debug switches.

> +
> +#ifdef DEBUG_ZYNQ_GPIO
> +#define DPRINTF(fmt, ...) \
> +do { printf("zynq-gpio: " fmt , ## __VA_ARGS__); } while (0)

use qemu_log.

> +#define BADF(fmt, ...) \

BADF is unused.

> +do { fprintf(stderr, "zynq-gpio: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)

and exit(1) is probably not a good semantic for an error condition.
Such conditions should just be asserts. You should just drop BADF
completely.

> +#else
> +#define DPRINTF(fmt, ...) do {} while(0)
> +#define BADF(fmt, ...) \
> +do { fprintf(stderr, "zynq-gpio: error: " fmt , ## __VA_ARGS__);} while (0)
> +#endif
> +

It's better to use a regular if for debug instrumentation. check
hw/dma/pl330.c for an example of this. The reason is to always compile
test the contents of printfs.

> +#define TYPE_ZYNQ_GPIO "zynq-gpio"
> +#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
> +
> +typedef struct {

Modern device-model conventions require the type struct, typename and
cast macros and the register offset #defines to be in a header file
specific to the device. These bits should be in zynq-gpio.h

> +    uint32_t mask_data;
> +    uint32_t out_data;
> +    uint32_t old_out_data;
> +    uint32_t in_data;
> +    uint32_t old_in_data;
> +    uint32_t dir;
> +    uint32_t oen;
> +    uint32_t imask;
> +    uint32_t istat;
> +    uint32_t itype;
> +    uint32_t ipolarity;
> +    uint32_t iany;
> +
> +    qemu_irq *out;
> +} GPIOBank;

Preface the struct name with "Zynq" for consistency with other
identifiers defined.

> +
> +typedef struct ZynqGPIOState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion iomem;
> +    GPIOBank banks[4];
> +    qemu_irq mio_out[64];
> +    qemu_irq emio_out[64];

Is it better to just model the GPIO controller as a standalone GPIO,
and leave the mio vs emio distinction to the SoC/Board level?

This would mean the bank GPIOs are on the top level entity, and the
core would then have no EMIO/MIO awareness. This also makes QEMU a
little less awkward considering there is no sense of MIO and EMIO in
QEMU to date.

> +    qemu_irq irq;
> +} ZynqGPIOState;
> +
> +static void zynq_gpio_update_out(GPIOBank *b)
> +{
> +    uint32_t changed;
> +    uint32_t mask;
> +    uint32_t out;
> +    int i;
> +
> +    DPRINTF("dir = %d, data = %d\n", b->dir, b->out_data);
> +
> +    /* Outputs float high.  */
> +    /* FIXME: This is board dependent.  */

How so? Looks pretty generic to me (not sure what needs fixing here).
Are you saying that the IO width should truncate based on Zynq
specifics?

> +    out = (b->out_data & b->dir) | ~b->dir;
> +    changed = b->old_out_data ^ out;
> +    if (changed) {

This if doesn't save much in optimization, as the expensive part (the
qemu_set_irq) is already change-guarded per-bit below anyway. Just
drop the if.

> +        b->old_out_data = out;
> +        for (i = 0; i < 32; i++) {

Macroify hardcoded constant 32.

> +            mask = 1 << i;
> +            if (changed & mask) {
> +                DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
> +                qemu_set_irq(b->out[i], (out & mask) != 0);
> +            }
> +        }
> +    }
> +}
> +
> +static void zynq_gpio_update_in(GPIOBank *b)
> +{
> +    uint32_t changed;
> +    uint32_t mask;
> +    int i;
> +
> +    changed = b->old_in_data ^ b->in_data;
> +    if (changed) {

Same as before. I don't think this is needed.

> +        b->old_in_data = b->in_data;
> +        for (i = 0; i < 32; i++) {
> +            mask = 1 << i;
> +            if (changed & mask) {
> +                DPRINTF("Changed input %d = %d\n", i, (b->in_data & mask) != 0);
> +
> +                if (b->itype & mask) {
> +                    /* Edge interrupt */
> +                    if (b->iany & mask) {
> +                        /* Any edge triggers the interrupt */
> +                        b->istat |= mask;
> +                    } else {
> +                        /* Edge is selected by INT_POLARITY */
> +                        b->istat |= ~(b->in_data ^ b->ipolarity) & mask;
> +                    }
> +                }
> +            }
> +        }
> +    }
> +
> +    /* Level interrupt */
> +    b->istat |= ~(b->in_data ^ b->ipolarity) & ~b->itype;
> +
> +    DPRINTF("istat = %08X\n", b->istat);
> +}
> +
> +static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
> +{
> +    int b;
> +    uint32_t istat = 0;
> +
> +    for (b = 0; b < 4; b++) {
> +        istat |= s->banks[b].istat & ~s->banks[b].imask;
> +    }
> +
> +    DPRINTF("IRQ = %d\n", istat != 0);
> +
> +    qemu_set_irq(s->irq, istat != 0);
> +}
> +
> +static void zynq_gpio_update(ZynqGPIOState *s)
> +{
> +    int b;
> +
> +    for (b = 0; b < 4; b++) {
> +        zynq_gpio_update_out(&s->banks[b]);
> +        zynq_gpio_update_in(&s->banks[b]);
> +    }
> +
> +    zynq_gpio_set_in_irq(s);
> +}
> +
> +static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
> +                               unsigned size)
> +{
> +    ZynqGPIOState *s = (ZynqGPIOState *)opaque;
> +    int b;
> +    GPIOBank *bank;
> +
> +    switch (offset) {
> +    case 0x000: /* MASK_DATA_0_LSW */

Define these register offsets as macros as use them for the case labels.

> +        return (s->banks[0].mask_data >> 0) & 0xffff;

Use extract32 to get field values rather than >> & logic.

> +    case 0x004: /* MASK_DATA_0_MSW */
> +        return (s->banks[0].mask_data >> 16) & 0xffff;
> +    case 0x008: /* MASK_DATA_1_LSW */
> +        return (s->banks[1].mask_data >> 0) & 0xffff;
> +    case 0x00C: /* MASK_DATA_1_MSW */
> +        return (s->banks[1].mask_data >> 16) & 0xffff;
> +    case 0x010: /* MASK_DATA_2_LSW */
> +        return (s->banks[2].mask_data >> 0) & 0xffff;
> +    case 0x014: /* MASK_DATA_2_MSW */
> +        return (s->banks[2].mask_data >> 16) & 0xffff;
> +    case 0x018: /* MASK_DATA_3_LSW */
> +        return (s->banks[3].mask_data >> 0) & 0xffff;
> +    case 0x01C: /* MASK_DATA_3_MSW */
> +        return (s->banks[3].mask_data >> 16) & 0xffff;

I would look into doing this shorthand though with:

case MASK_DATA_0_LSW ... MASK_DATA_3_MSW:
    bank = extract32(offset, 3, 2);
    shift = offset & 0x8 ? 16 : 0;
    return extract32(s->banks[bank].mask_data, shift, 16);


> +
> +    case 0x040: /* DATA_0 */
> +        return s->banks[0].out_data;
> +    case 0x044: /* DATA_1 */
> +        return s->banks[1].out_data;
> +    case 0x048: /* DATA_2 */
> +        return s->banks[2].out_data;
> +    case 0x04C: /* DATA_3 */
> +        return s->banks[3].out_data;
> +
> +    case 0x060: /* DATA_0_RO */
> +        return s->banks[0].in_data;
> +    case 0x064: /* DATA_1_RO */
> +        return s->banks[1].in_data;
> +    case 0x068: /* DATA_2_RO */
> +        return s->banks[2].in_data;
> +    case 0x06C: /* DATA_3_RO */
> +        return s->banks[3].in_data;

Similar here (slightly simpler logic).

> +    }
> +
> +    if (offset < 0x204 || offset > 0x2e4) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_read: Bad offset %x\n", (int)offset);
> +        return 0;
> +    }
> +
> +    b = (offset - 0x200) / 0x40;
> +    bank = &s->banks[b];
> +
> +    switch (offset - 0x200 - b * 0x40) {
> +    case 0x04: /* DIRM_x */

You can macroify theses offset labels. Probably with exactly the names
you have already used in the comments.

> +        return bank->dir;
> +    case 0x08: /* OEN_x */
> +        return bank->oen;
> +    case 0x0C: /* INT_MASK_x */
> +        return bank->imask;
> +    case 0x10: /* INT_EN_x */
> +        return 0;
> +    case 0x14: /* INT_DIS_x */
> +        return 0;
> +    case 0x18: /* INT_STAT_x */
> +        return bank->istat;
> +    case 0x1C: /* INT_TYPE_x */
> +        return bank->itype;
> +    case 0x20: /* INT_POLARITY_x */
> +        return bank->ipolarity;
> +    case 0x24: /* INT_ANY_x */
> +        return bank->iany;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_read: Bad offset %x\n", (int)offset);
> +        return 0;
> +    }
> +}
> +
> +static void zynq_gpio_mask_data(GPIOBank *bank, int bit_offset,
> +                                uint32_t mask_data)
> +{
> +    DPRINTF("mask data offset = %d, mask_data = %08X\n", bit_offset, mask_data);
> +
> +    /* MASK_DATA registers are R/W on their data part */
> +    bank->mask_data = (bank->mask_data & ~(0xffff << bit_offset)) |
> +                     ((mask_data & 0xffff) << bit_offset);

You can use deposit32 here.

> +    bank->out_data = (bank->out_data & ~((~mask_data >> 16) << bit_offset)) |
> +                     ((mask_data & 0xffff) << bit_offset);
> +    zynq_gpio_update_out(bank);
> +}
> +
> +static void zynq_gpio_data(GPIOBank *bank, uint32_t data)
> +{
> +    bank->out_data = data;
> +    zynq_gpio_update_out(bank);
> +}
> +
> +static void zynq_gpio_write(void *opaque, hwaddr offset,
> +                            uint64_t value, unsigned size)
> +{
> +    ZynqGPIOState *s = (ZynqGPIOState *)opaque;

cast un-needed.

> +    int b;
> +    GPIOBank *bank;
> +
> +    switch (offset) {
> +    case 0x000: /* MASK_DATA_0_LSW */
> +        zynq_gpio_mask_data(&s->banks[0], 0, value);
> +        return;
> +    case 0x004: /* MASK_DATA_0_MSW */
> +        zynq_gpio_mask_data(&s->banks[0], 16, value);
> +        return;
> +    case 0x008: /* MASK_DATA_1_LSW */
> +        zynq_gpio_mask_data(&s->banks[1], 0, value);
> +        return;
> +    case 0x00C: /* MASK_DATA_1_MSW */
> +        zynq_gpio_mask_data(&s->banks[1], 16, value);
> +        return;
> +    case 0x010: /* MASK_DATA_2_LSW */
> +        zynq_gpio_mask_data(&s->banks[2], 0, value);
> +        return;
> +    case 0x014: /* MASK_DATA_2_MSW */
> +        zynq_gpio_mask_data(&s->banks[2], 16, value);
> +        return;
> +    case 0x018: /* MASK_DATA_3_LSW */
> +        zynq_gpio_mask_data(&s->banks[3], 0, value);
> +        return;
> +    case 0x01C: /* MASK_DATA_3_MSW */
> +        zynq_gpio_mask_data(&s->banks[3], 16, value);
> +        return;
> +
> +    case 0x040: /* DATA_0 */
> +        zynq_gpio_data(&s->banks[0], value);
> +        return;
> +    case 0x044: /* DATA_1 */
> +        zynq_gpio_data(&s->banks[1], value);
> +        return;
> +    case 0x048: /* DATA_2 */
> +        zynq_gpio_data(&s->banks[2], value);
> +        return;
> +    case 0x04C: /* DATA_3 */
> +        zynq_gpio_data(&s->banks[3], value);
> +        return;
> +
> +    case 0x060: /* DATA_0_RO */
> +    case 0x064: /* DATA_1_RO */
> +    case 0x068: /* DATA_2_RO */
> +    case 0x06C: /* DATA_3_RO */
> +        return;
> +    }
> +
> +    if (offset < 0x204 || offset > 0x2e4) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_write: Bad offset %x\n", (int)offset);
> +        return;
> +    }
> +
> +    b = (offset - 0x200) / 0x40;
> +    bank = &s->banks[b];
> +
> +    switch (offset - 0x200 - b * 0x40) {
> +    case 0x04: /* DIRM_x */
> +        bank->dir = value;
> +        break;
> +    case 0x08: /* OEN_x */
> +        bank->oen = value;

Probably worth a LOG_UNIMP.

> +        break;
> +    case 0x0C: /* INT_MASK_x */
> +        return;
> +    case 0x10: /* INT_EN_x */
> +        bank->imask &= ~value;
> +        break;
> +    case 0x14: /* INT_DIS_x */
> +        bank->imask |= value;
> +        break;
> +    case 0x18: /* INT_STAT_x */
> +        bank->istat &= ~value;
> +        break;
> +    case 0x1C: /* INT_TYPE_x */
> +        bank->itype = value;
> +        break;
> +    case 0x20: /* INT_POLARITY_x */
> +        bank->ipolarity = value;
> +        break;
> +    case 0x24: /* INT_ANY_x */
> +        bank->iany = value;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_write: Bad offset %x\n", (int)offset);
> +        return;
> +    }
> +
> +    zynq_gpio_update(s);
> +}
> +
> +static void zynq_gpio_reset(ZynqGPIOState *s)
> +{
> +    int b;
> +
> +    for (b = 0; b < 4; b++) {
> +        s->banks[b].mask_data = 0x00000000;
> +        s->banks[b].out_data = 0x00000000;
> +        s->banks[b].dir = 0x00000000;
> +        s->banks[b].oen = 0x00000000;
> +        s->banks[b].imask = 0x00000000;
> +        s->banks[b].istat = 0x00000000;
> +        s->banks[b].itype = 0xffffffff;
> +        s->banks[b].ipolarity = 0x00000000;
> +        s->banks[b].iany = 0x00000000;
> +    }
> +}
> +
> +static void zynq_gpio_set_irq(void * opaque, int irq, int level)
> +{
> +    ZynqGPIOState *s = (ZynqGPIOState *)opaque;
> +
> +    GPIOBank *bank = &s->banks[irq / 32];
> +    uint32_t mask = 1 << (irq % 32);
> +
> +    bank->in_data &= ~mask;
> +    if (level)
> +        bank->in_data |= mask;
> +
> +    zynq_gpio_update_in(bank);
> +
> +    zynq_gpio_set_in_irq(s);
> +}
> +
> +static void zynq_gpio_set_mio_irq(void * opaque, int irq, int level)
> +{
> +    zynq_gpio_set_irq(opaque, irq + 0, level);
> +}
> +
> +static void zynq_gpio_set_emio_irq(void * opaque, int irq, int level)
> +{
> +    zynq_gpio_set_irq(opaque, irq + 64, level);
> +}

You shouldn't need to re-linearise the IRQs as a set of 128 like this.
Following on from my comment about getting rid of MIO vs EMIO, what
you can probably do is qdev_init_gpio_in_named() the 4 banks in a 4x
loop and pass the bank pointer as opaque data instead. add the
containing ZynqGPIOState as a *parent field to the bank struct. Then
you do not need the /32 logic to do bank identification.

> +
> +static const MemoryRegionOps zynq_gpio_ops = {
> +    .read = zynq_gpio_read,
> +    .write = zynq_gpio_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int zynq_gpio_initfn(SysBusDevice *sbd)
> +{
> +    DeviceState *dev = DEVICE(sbd);
> +    ZynqGPIOState *s = ZYNQ_GPIO(dev);
> +
> +    s->banks[0].out = &s->mio_out[0];
> +    s->banks[1].out = &s->mio_out[32];
> +    s->banks[2].out = &s->emio_out[0];
> +    s->banks[3].out = &s->emio_out[32];

This would disappear with getting rid of MIO/EMIO.

> +
> +    memory_region_init_io(&s->iomem, OBJECT(s), &zynq_gpio_ops, s, "zynq_gpio", 0x1000);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    sysbus_init_irq(sbd, &s->irq);
> +
> +    qdev_init_gpio_in_named(dev, zynq_gpio_set_mio_irq, "mio_in", 64);
> +    qdev_init_gpio_in_named(dev, zynq_gpio_set_emio_irq, "emio_in", 64);
> +
> +    qdev_init_gpio_out_named(dev, s->mio_out, "mio_out", 64);
> +    qdev_init_gpio_out_named(dev, s->emio_out, "emio_out", 64);
> +
> +    zynq_gpio_reset(s);

Don't reset in init fns. You shuold use a device-reset function ...

> +
> +    return 0;
> +}
> +
> +static void zynq_gpio_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = zynq_gpio_initfn;

... like this:

k->reset = zynq_gpio_reset;

Regards,
Peter

> +}
> +
> +static const TypeInfo zynq_gpio_info = {
> +    .name          = TYPE_ZYNQ_GPIO,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(ZynqGPIOState),
> +    .class_init    = zynq_gpio_class_init,
> +};
> +
> +static void zynq_gpio_register_type(void)
> +{
> +    type_register_static(&zynq_gpio_info);
> +}
> +
> +type_init(zynq_gpio_register_type)
> --
> 1.7.10.4
>
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PATCH 1/2] zynq_gpio: GPIO model for Zynq SoC
  2014-12-30 23:24   ` Peter Crosthwaite
@ 2014-12-31  1:03     ` Colin Leitner
  0 siblings, 0 replies; 18+ messages in thread
From: Colin Leitner @ 2014-12-31  1:03 UTC (permalink / raw)
  To: Peter Crosthwaite, Colin Leitner; +Cc: qemu-devel@nongnu.org Developers

Hi Peter,

thanks for the review! I'll rework the patch ASAP.

> Is it better to just model the GPIO controller as a standalone GPIO,
> and leave the mio vs emio distinction to the SoC/Board level?
> 
> This would mean the bank GPIOs are on the top level entity, and the
> core would then have no EMIO/MIO awareness. This also makes QEMU a
> little less awkward considering there is no sense of MIO and EMIO in
> QEMU to date.

The reason for chosing the MIO/EMIO names was simply for easier mapping
to real hardware where I've been usually confronted with MIO/EMIO.
Changing this to the banks makes sense of course if Xilinx choses to
reuse that IP again.

>> +    /* Outputs float high.  */
>> +    /* FIXME: This is board dependent.  */
> 
> How so? Looks pretty generic to me (not sure what needs fixing here).
> Are you saying that the IO width should truncate based on Zynq
> specifics?

This is a fragment from pl061. If we don't explicitly drive a output
line through the direction register, we assume it floats high. We
still have to drive the qemu IRQ line to some state.

I'll write a better comment.

>> +    zynq_gpio_reset(s);
> 
> Don't reset in init fns. You shuold use a device-reset function ...

Another 1:1 copy from pl061. I'll take the time to read up how the
device model is meant to be implemented correctly.

Thanks again for the review and you'll hear from me shortly.

Regards,
	Colin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v2 0/2] Reworked Zynq GPIO model
  2014-12-30 13:13 [Qemu-devel] [PATCH 0/2] GPIO model for Zynq SoC Colin Leitner
  2014-12-30 13:13 ` [Qemu-devel] [PATCH 1/2] zynq_gpio: " Colin Leitner
  2014-12-30 13:13 ` [Qemu-devel] [PATCH 2/2] xilinx_zynq: Add zynq_gpio to the machine Colin Leitner
@ 2014-12-31 14:06 ` Colin Leitner
  2014-12-31 14:06   ` [Qemu-devel] [PATCH v2 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
  2014-12-31 14:06   ` [Qemu-devel] [PATCH v2 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
  2015-01-06 22:26 ` [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model Colin Leitner
  3 siblings, 2 replies; 18+ messages in thread
From: Colin Leitner @ 2014-12-31 14:06 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Hello everyone,

this is the second version with most of the review points addressed.

Two notable exceptions:

 * The bank registers are handled like in the original patch
 * I couldn't get rid of the intermediate set_irq callbacks because
   qdev_init_gpio_in_named won't allow me to set the opaque data to another
   value.

I retested basic I/O on different banks and IRQ handling. Both with the stock
Linux 3.12 driver.

Cheers,
	Colin

Colin Leitner (2):
  zynq_gpio: GPIO model for Zynq SoC
  xilinx_zynq: Add zynq-gpio to the machine

 hw/arm/xilinx_zynq.c  |    2 +
 hw/gpio/Makefile.objs |    1 +
 hw/gpio/zynq-gpio.c   |  386 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/gpio/zynq-gpio.h   |   79 ++++++++++
 4 files changed, 468 insertions(+)
 create mode 100644 hw/gpio/zynq-gpio.c
 create mode 100644 hw/gpio/zynq-gpio.h

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v2 1/2] zynq_gpio: GPIO model for Zynq SoC
  2014-12-31 14:06 ` [Qemu-devel] [PATCH v2 0/2] Reworked Zynq GPIO model Colin Leitner
@ 2014-12-31 14:06   ` Colin Leitner
  2014-12-31 14:06   ` [Qemu-devel] [PATCH v2 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
  1 sibling, 0 replies; 18+ messages in thread
From: Colin Leitner @ 2014-12-31 14:06 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Based on the pl061 model. This model implements all four banks with 32 I/Os
each.

The I/Os are placed in named groups:

 * bankX_in for the 32 inputs of each bank
 * bankX_out for the 32 outputs of each bank

Basic I/O and IRQ support tested with the Zynq GPIO driver in Linux 3.12.

Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/gpio/Makefile.objs |    1 +
 hw/gpio/zynq-gpio.c   |  386 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/gpio/zynq-gpio.h   |   79 ++++++++++
 3 files changed, 466 insertions(+)
 create mode 100644 hw/gpio/zynq-gpio.c
 create mode 100644 hw/gpio/zynq-gpio.h

diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 1abcf17..c927c66 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
 common-obj-$(CONFIG_E500) += mpc8xxx.o
 
 obj-$(CONFIG_OMAP) += omap_gpio.o
+obj-$(CONFIG_ZYNQ) += zynq-gpio.o
diff --git a/hw/gpio/zynq-gpio.c b/hw/gpio/zynq-gpio.c
new file mode 100644
index 0000000..2fd1712
--- /dev/null
+++ b/hw/gpio/zynq-gpio.c
@@ -0,0 +1,386 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * Based on the PL061 model:
+ *   Copyright (c) 2007 CodeSourcery.
+ *   Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/*
+ * We model all banks as if they were fully populated. MIO pins are usually
+ * limited to 54 pins, but this is probably device dependent and shouldn't
+ * cause too much trouble. One noticeable difference is the reset value of
+ * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
+ *
+ * The output enable pins are not modeled.
+ */
+
+#include "qemu/bitops.h"
+
+#include "zynq-gpio.h"
+
+#ifndef ZYNQ_GPIO_ERR_DEBUG
+#define ZYNQ_GPIO_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do {\
+    if (ZYNQ_GPIO_ERR_DEBUG >= lvl) {\
+        qemu_log("zynq-gpio: %s:" fmt, __func__, ## args);\
+    } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+static void zynq_gpio_update_out(ZynqGPIOBank *b)
+{
+    uint32_t changed;
+    uint32_t mask;
+    uint32_t out;
+    int i;
+
+    DB_PRINT("dir = %d, data = %d\n", b->dir, b->out_data);
+
+    /*
+     * We assume non-driven (DIRM = 0) outputs float high. On real hardware this
+     * could be different, but here we have to decide which value to set the
+     * output IRQ to if the direction register switches the I/O to an input.
+     */
+    /* FIXME: This is board dependent. */
+    out = (b->out_data & b->dir) | ~b->dir;
+
+    changed = b->old_out_data ^ out;
+    b->old_out_data = out;
+
+    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DB_PRINT("Set output %d = %d\n", i, (out & mask) != 0);
+            qemu_set_irq(b->out[i], (out & mask) != 0);
+        }
+    }
+}
+
+static void zynq_gpio_update_in(ZynqGPIOBank *b)
+{
+    uint32_t changed;
+    uint32_t mask;
+    int i;
+
+    changed = b->old_in_data ^ b->in_data;
+    b->old_in_data = b->in_data;
+
+    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DB_PRINT("Changed input %d = %d\n", i, (b->in_data & mask) != 0);
+
+            if (b->itype & mask) {
+                /* Edge interrupt */
+                if (b->iany & mask) {
+                    /* Any edge triggers the interrupt */
+                    b->istat |= mask;
+                } else {
+                    /* Edge is selected by INT_POLARITY */
+                    b->istat |= ~(b->in_data ^ b->ipolarity) & mask;
+                }
+            }
+        }
+    }
+
+    /* Level interrupt */
+    b->istat |= ~(b->in_data ^ b->ipolarity) & ~b->itype;
+
+    DB_PRINT("istat = %08X\n", b->istat);
+}
+
+static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
+{
+    int b;
+    uint32_t istat = 0;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        istat |= s->banks[b].istat & ~s->banks[b].imask;
+    }
+
+    DB_PRINT("IRQ = %d\n", istat != 0);
+
+    qemu_set_irq(s->irq, istat != 0);
+}
+
+static void zynq_gpio_update(ZynqGPIOState *s)
+{
+    int b;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        zynq_gpio_update_out(&s->banks[b]);
+        zynq_gpio_update_in(&s->banks[b]);
+    }
+
+    zynq_gpio_set_in_irq(s);
+}
+
+static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
+                               unsigned size)
+{
+    ZynqGPIOState *s = opaque;
+    int b;
+    int shift;
+    ZynqGPIOBank *bank;
+
+    switch (offset) {
+    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW...ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
+        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
+        shift = (offset & 0x8) ? 16 : 0;
+        return extract32(s->banks[b].mask_data, shift, 16);
+
+    case ZYNQ_GPIO_REG_DATA_0...ZYNQ_GPIO_REG_DATA_3:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
+        return s->banks[b].out_data;
+
+    case ZYNQ_GPIO_REG_DATA_0_RO...ZYNQ_GPIO_REG_DATA_3_RO:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0_RO) / 4;
+        return s->banks[b].in_data;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
+    case ZYNQ_GPIO_BANK_REG_DIRM:
+        return bank->dir;
+    case ZYNQ_GPIO_BANK_REG_OEN:
+        return bank->oen;
+    case ZYNQ_GPIO_BANK_REG_INT_MASK:
+        return bank->imask;
+    case ZYNQ_GPIO_BANK_REG_INT_EN:
+        return 0;
+    case ZYNQ_GPIO_BANK_REG_INT_DIS:
+        return 0;
+    case ZYNQ_GPIO_BANK_REG_INT_STAT:
+        return bank->istat;
+    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
+        return bank->itype;
+    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
+        return bank->ipolarity;
+    case ZYNQ_GPIO_BANK_REG_INT_ANY:
+        return bank->iany;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void zynq_gpio_mask_data(ZynqGPIOBank *bank, int bit_offset,
+                                uint32_t mask_data)
+{
+    DB_PRINT("mask data offset = %d, mask_data = %08X\n", bit_offset, mask_data);
+
+    /* MASK_DATA registers are R/W on their data part */
+    bank->mask_data = deposit32(bank->mask_data, bit_offset, 16, mask_data);
+    bank->out_data = deposit32(bank->out_data, bit_offset, 16, mask_data);
+
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_data(ZynqGPIOBank *bank, uint32_t data)
+{
+    bank->out_data = data;
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_write(void *opaque, hwaddr offset,
+                            uint64_t value, unsigned size)
+{
+    ZynqGPIOState *s = opaque;
+    int b;
+    int shift;
+    ZynqGPIOBank *bank;
+
+    switch (offset) {
+    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW...ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
+        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
+        shift = (offset & 0x8) ? 16 : 0;
+        zynq_gpio_mask_data(&s->banks[b], shift, value);
+        return;
+
+    case ZYNQ_GPIO_REG_DATA_0...ZYNQ_GPIO_REG_DATA_3:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
+        zynq_gpio_data(&s->banks[b], value);
+        return;
+
+    case ZYNQ_GPIO_REG_DATA_0_RO...ZYNQ_GPIO_REG_DATA_3_RO:
+        return;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %x\n", (int)offset);
+        return;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
+    case ZYNQ_GPIO_BANK_REG_DIRM:
+        bank->dir = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_OEN:
+        bank->oen = value;
+        qemu_log_mask(LOG_UNIMP,
+                     "zynq_gpio_write: Output enable lines not implemented\n");
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_MASK:
+        return;
+    case ZYNQ_GPIO_BANK_REG_INT_EN:
+        bank->imask &= ~value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_DIS:
+        bank->imask |= value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_STAT:
+        bank->istat &= ~value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
+        bank->itype = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
+        bank->ipolarity = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_ANY:
+        bank->iany = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %x\n", (int)offset);
+        return;
+    }
+
+    zynq_gpio_update(s);
+}
+
+static void zynq_gpio_reset(DeviceState *d)
+{
+    ZynqGPIOState *s = ZYNQ_GPIO(d);
+    int b;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        s->banks[b].mask_data = 0x00000000;
+        s->banks[b].out_data = 0x00000000;
+        s->banks[b].dir = 0x00000000;
+        s->banks[b].oen = 0x00000000;
+        s->banks[b].imask = 0x00000000;
+        s->banks[b].istat = 0x00000000;
+        s->banks[b].itype = 0xffffffff;
+        s->banks[b].ipolarity = 0x00000000;
+        s->banks[b].iany = 0x00000000;
+    }
+}
+
+static void zynq_gpio_set_irq(ZynqGPIOBank *bank, int irq, int level)
+{
+    uint32_t mask = 1 << irq;
+
+    bank->in_data &= ~mask;
+    if (level)
+        bank->in_data |= mask;
+
+    zynq_gpio_update_in(bank);
+
+    zynq_gpio_set_in_irq(bank->parent);
+}
+
+static void zynq_gpio_set_bank0_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[0], irq, level);
+}
+
+static void zynq_gpio_set_bank1_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[1], irq, level);
+}
+
+static void zynq_gpio_set_bank2_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[2], irq, level);
+}
+
+static void zynq_gpio_set_bank3_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[3], irq, level);
+}
+
+static const MemoryRegionOps zynq_gpio_ops = {
+    .read = zynq_gpio_read,
+    .write = zynq_gpio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void zynq_gpio_realize(DeviceState *dev, Error **errp)
+{
+    ZynqGPIOState *s = ZYNQ_GPIO(dev);
+    int b;
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &zynq_gpio_ops, s, "zynq-gpio", 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        char name[16];
+
+        s->banks[b].parent = s;
+
+        snprintf(name, sizeof(name), "bank%d_out", b);
+        qdev_init_gpio_out_named(dev, s->banks[b].out, name, ZYNQ_GPIO_IOS_PER_BANK);
+        /*
+         * TODO: it would be nice if we could pass the bank to the handler. This
+         * would allow us to remove the 4 callbacks and use zynq_gpio_set_irq
+         * directly.
+         */
+#if 0
+        snprintf(name, sizeof(name), "bank%d_in", b);
+        qdev_init_gpio_in_named(dev, zynq_gpio_set_irq, name, ZYNQ_GPIO_IOS_PER_BANK, &s->banks[b]);
+#endif
+    }
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank0_irq, "bank0_in", ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank1_irq, "bank1_in", ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank2_irq, "bank2_in", ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank3_irq, "bank3_in", ZYNQ_GPIO_IOS_PER_BANK);
+}
+
+static void zynq_gpio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = zynq_gpio_realize;
+    dc->reset = zynq_gpio_reset;
+}
+
+static const TypeInfo zynq_gpio_info = {
+    .name          = TYPE_ZYNQ_GPIO,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ZynqGPIOState),
+    .class_init    = zynq_gpio_class_init,
+};
+
+static void zynq_gpio_register_type(void)
+{
+    type_register_static(&zynq_gpio_info);
+}
+
+type_init(zynq_gpio_register_type)
diff --git a/hw/gpio/zynq-gpio.h b/hw/gpio/zynq-gpio.h
new file mode 100644
index 0000000..8146c1f
--- /dev/null
+++ b/hw/gpio/zynq-gpio.h
@@ -0,0 +1,79 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#ifndef HW_ZYNQ_GPIO_H
+#define HW_ZYNQ_GPIO_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ZYNQ_GPIO "zynq-gpio"
+#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
+
+#define ZYNQ_GPIO_BANKS         4
+#define ZYNQ_GPIO_IOS_PER_BANK  32
+
+typedef struct {
+    struct ZynqGPIOState *parent;
+
+    uint32_t mask_data;
+    uint32_t out_data;
+    uint32_t old_out_data;
+    uint32_t in_data;
+    uint32_t old_in_data;
+    uint32_t dir;
+    uint32_t oen;
+    uint32_t imask;
+    uint32_t istat;
+    uint32_t itype;
+    uint32_t ipolarity;
+    uint32_t iany;
+
+    qemu_irq out[ZYNQ_GPIO_IOS_PER_BANK];
+} ZynqGPIOBank;
+
+typedef struct ZynqGPIOState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    ZynqGPIOBank banks[ZYNQ_GPIO_BANKS];
+    qemu_irq irq;
+} ZynqGPIOState;
+
+#define ZYNQ_GPIO_REG_MASK_DATA_0_LSW   0x000
+#define ZYNQ_GPIO_REG_MASK_DATA_0_MSW   0x004
+#define ZYNQ_GPIO_REG_MASK_DATA_1_LSW   0x008
+#define ZYNQ_GPIO_REG_MASK_DATA_1_MSW   0x00C
+#define ZYNQ_GPIO_REG_MASK_DATA_2_LSW   0x010
+#define ZYNQ_GPIO_REG_MASK_DATA_2_MSW   0x014
+#define ZYNQ_GPIO_REG_MASK_DATA_3_LSW   0x018
+#define ZYNQ_GPIO_REG_MASK_DATA_3_MSW   0x01C
+#define ZYNQ_GPIO_REG_DATA_0            0x040
+#define ZYNQ_GPIO_REG_DATA_1            0x044
+#define ZYNQ_GPIO_REG_DATA_2            0x048
+#define ZYNQ_GPIO_REG_DATA_3            0x04C
+#define ZYNQ_GPIO_REG_DATA_0_RO         0x060
+#define ZYNQ_GPIO_REG_DATA_1_RO         0x064
+#define ZYNQ_GPIO_REG_DATA_2_RO         0x068
+#define ZYNQ_GPIO_REG_DATA_3_RO         0x06C
+
+/*
+ * Oddly enough these registers are neatly grouped per bank and not interleaved
+ * like the data registers
+ */
+#define ZYNQ_GPIO_BANK_OFFSET(bank)     (0x200 + 0x40 * (bank))
+#define ZYNQ_GPIO_BANK_REG_DIRM         0x04
+#define ZYNQ_GPIO_BANK_REG_OEN          0x08
+#define ZYNQ_GPIO_BANK_REG_INT_MASK     0x0C
+#define ZYNQ_GPIO_BANK_REG_INT_EN       0x10
+#define ZYNQ_GPIO_BANK_REG_INT_DIS      0x14
+#define ZYNQ_GPIO_BANK_REG_INT_STAT     0x18
+#define ZYNQ_GPIO_BANK_REG_INT_TYPE     0x1C
+#define ZYNQ_GPIO_BANK_REG_INT_POLARITY 0x20
+#define ZYNQ_GPIO_BANK_REG_INT_ANY      0x24
+
+#endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v2 2/2] xilinx_zynq: Add zynq-gpio to the machine
  2014-12-31 14:06 ` [Qemu-devel] [PATCH v2 0/2] Reworked Zynq GPIO model Colin Leitner
  2014-12-31 14:06   ` [Qemu-devel] [PATCH v2 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
@ 2014-12-31 14:06   ` Colin Leitner
  1 sibling, 0 replies; 18+ messages in thread
From: Colin Leitner @ 2014-12-31 14:06 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/arm/xilinx_zynq.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 06e6e24..6d8c0d9 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -202,6 +202,8 @@ static void zynq_init(MachineState *machine)
     zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
     zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
 
+    sysbus_create_simple("zynq-gpio", 0xE000A000, pic[52-IRQ_OFFSET]);
+
     sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
     sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model
  2014-12-30 13:13 [Qemu-devel] [PATCH 0/2] GPIO model for Zynq SoC Colin Leitner
                   ` (2 preceding siblings ...)
  2014-12-31 14:06 ` [Qemu-devel] [PATCH v2 0/2] Reworked Zynq GPIO model Colin Leitner
@ 2015-01-06 22:26 ` Colin Leitner
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
                     ` (2 more replies)
  3 siblings, 3 replies; 18+ messages in thread
From: Colin Leitner @ 2015-01-06 22:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Hello everyone,

this is the third version of the Zynq GPIO model patch. It includes
 * mostly code cleanup (variable naming, removed unneeded casts, added some
   local vars for better readability)
 * moved zynq-gpio.h to include/hw/gpio
 * enhancement in the reset/init logic to ensure that reset sets the IRQ output
   lines to 0.

Regards,
	Colin

Colin Leitner (2):
  zynq_gpio: GPIO model for Zynq SoC
  xilinx_zynq: Add zynq-gpio to the machine

 hw/arm/xilinx_zynq.c        |    2 +
 hw/gpio/Makefile.objs       |    1 +
 hw/gpio/zynq-gpio.c         |  402 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/gpio/zynq-gpio.h |   79 +++++++++
 4 files changed, 484 insertions(+)
 create mode 100644 hw/gpio/zynq-gpio.c
 create mode 100644 include/hw/gpio/zynq-gpio.h

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC
  2015-01-06 22:26 ` [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model Colin Leitner
@ 2015-01-06 22:26   ` Colin Leitner
  2015-01-20 22:05     ` Peter Crosthwaite
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
  2015-01-25 19:51   ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Colin Leitner
  2 siblings, 1 reply; 18+ messages in thread
From: Colin Leitner @ 2015-01-06 22:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Based on the pl061 model. This model implements all four banks with 32 I/Os
each.

The I/Os are placed in named groups:

 * bankX_in for the 32 inputs of each bank
 * bankX_out for the 32 outputs of each bank

Basic I/O and IRQ support tested with the Zynq GPIO driver in Linux 3.12.

Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/gpio/Makefile.objs       |    1 +
 hw/gpio/zynq-gpio.c         |  402 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/gpio/zynq-gpio.h |   79 +++++++++
 3 files changed, 482 insertions(+)
 create mode 100644 hw/gpio/zynq-gpio.c
 create mode 100644 include/hw/gpio/zynq-gpio.h

diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 1abcf17..c927c66 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
 common-obj-$(CONFIG_E500) += mpc8xxx.o
 
 obj-$(CONFIG_OMAP) += omap_gpio.o
+obj-$(CONFIG_ZYNQ) += zynq-gpio.o
diff --git a/hw/gpio/zynq-gpio.c b/hw/gpio/zynq-gpio.c
new file mode 100644
index 0000000..8cf4262
--- /dev/null
+++ b/hw/gpio/zynq-gpio.c
@@ -0,0 +1,402 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * Based on the PL061 model:
+ *   Copyright (c) 2007 CodeSourcery.
+ *   Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/*
+ * We model all banks as if they were fully populated. MIO pins are usually
+ * limited to 54 pins, but this is probably device dependent and shouldn't
+ * cause too much trouble. One noticeable difference is the reset value of
+ * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
+ *
+ * The output enable pins are not modeled.
+ */
+
+#include "hw/gpio/zynq-gpio.h"
+#include "qemu/bitops.h"
+
+#ifndef ZYNQ_GPIO_ERR_DEBUG
+#define ZYNQ_GPIO_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do {\
+    if (ZYNQ_GPIO_ERR_DEBUG >= lvl) {\
+        qemu_log("zynq-gpio: %s:" fmt, __func__, ## args);\
+    } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+static void zynq_gpio_update_out(ZynqGPIOBank *bank)
+{
+    uint32_t changed;
+    uint32_t mask;
+    uint32_t out;
+    int i;
+
+    DB_PRINT("dir = %d, data = %d\n", bank->dir, bank->out_data);
+
+    /*
+     * We assume non-driven (DIRM = 0) outputs float high. On real hardware this
+     * could be different, but here we have to decide which value to set the
+     * output IRQ to if the direction register switches the I/O to an input.
+     */
+    /* FIXME: This is board dependent. */
+    out = (bank->out_data & bank->dir) | ~bank->dir;
+
+    changed = bank->old_out_data ^ out;
+    bank->old_out_data = out;
+
+    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DB_PRINT("Set output %d = %d\n", i, (out & mask) != 0);
+            qemu_set_irq(bank->out[i], (out & mask) != 0);
+        }
+    }
+}
+
+static void zynq_gpio_update_in(ZynqGPIOBank *bank)
+{
+    uint32_t changed;
+    uint32_t mask;
+    int i;
+
+    changed = bank->old_in_data ^ bank->in_data;
+    bank->old_in_data = bank->in_data;
+
+    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DB_PRINT("Changed input %d = %d\n", i, (bank->in_data & mask) != 0);
+
+            if (bank->itype & mask) {
+                /* Edge interrupt */
+                if (bank->iany & mask) {
+                    /* Any edge triggers the interrupt */
+                    bank->istat |= mask;
+                } else {
+                    /* Edge is selected by INT_POLARITY */
+                    bank->istat |= ~(bank->in_data ^ bank->ipolarity) & mask;
+                }
+            }
+        }
+    }
+
+    /* Level interrupt */
+    bank->istat |= ~(bank->in_data ^ bank->ipolarity) & ~bank->itype;
+
+    DB_PRINT("istat = %08X\n", bank->istat);
+}
+
+static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
+{
+    int b;
+    uint32_t istat = 0;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+
+        istat |= bank->istat & ~bank->imask;
+    }
+
+    DB_PRINT("IRQ = %d\n", istat != 0);
+
+    qemu_set_irq(s->irq, istat != 0);
+}
+
+static void zynq_gpio_update(ZynqGPIOState *s)
+{
+    int b;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+
+        zynq_gpio_update_out(bank);
+        zynq_gpio_update_in(bank);
+    }
+
+    zynq_gpio_set_in_irq(s);
+}
+
+static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
+                               unsigned int size)
+{
+    ZynqGPIOState *s = opaque;
+    int b;
+    int shift;
+    ZynqGPIOBank *bank;
+
+    switch (offset) {
+    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
+        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
+        shift = (offset & 0x8) ? 16 : 0;
+        return extract32(s->banks[b].mask_data, shift, 16);
+
+    case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
+        return s->banks[b].out_data;
+
+    case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0_RO) / 4;
+        return s->banks[b].in_data;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n", offset);
+        return 0;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
+    case ZYNQ_GPIO_BANK_REG_DIRM:
+        return bank->dir;
+    case ZYNQ_GPIO_BANK_REG_OEN:
+        return bank->oen;
+    case ZYNQ_GPIO_BANK_REG_INT_MASK:
+        return bank->imask;
+    case ZYNQ_GPIO_BANK_REG_INT_EN:
+        return 0;
+    case ZYNQ_GPIO_BANK_REG_INT_DIS:
+        return 0;
+    case ZYNQ_GPIO_BANK_REG_INT_STAT:
+        return bank->istat;
+    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
+        return bank->itype;
+    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
+        return bank->ipolarity;
+    case ZYNQ_GPIO_BANK_REG_INT_ANY:
+        return bank->iany;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n", offset);
+        return 0;
+    }
+}
+
+static void zynq_gpio_mask_data(ZynqGPIOBank *bank, int bit_offset,
+                                uint32_t mask_data)
+{
+    DB_PRINT("mask data offset = %d, mask_data = %08X\n", bit_offset, mask_data);
+
+    /* MASK_DATA registers are R/W on their data part */
+    bank->mask_data = deposit32(bank->mask_data, bit_offset, 16, mask_data);
+    bank->out_data = deposit32(bank->out_data, bit_offset, 16, mask_data);
+
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_data(ZynqGPIOBank *bank, uint32_t data)
+{
+    bank->out_data = data;
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_write(void *opaque, hwaddr offset,
+                            uint64_t value, unsigned int size)
+{
+    ZynqGPIOState *s = opaque;
+    int b;
+    int shift;
+    ZynqGPIOBank *bank;
+
+    switch (offset) {
+    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
+        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
+        shift = (offset & 0x8) ? 16 : 0;
+        zynq_gpio_mask_data(&s->banks[b], shift, value);
+        return;
+
+    case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
+        zynq_gpio_data(&s->banks[b], value);
+        return;
+
+    case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
+        return;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n", offset);
+        return;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
+    case ZYNQ_GPIO_BANK_REG_DIRM:
+        bank->dir = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_OEN:
+        bank->oen = value;
+        qemu_log_mask(LOG_UNIMP,
+                     "zynq_gpio_write: Output enable lines not implemented\n");
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_MASK:
+        return;
+    case ZYNQ_GPIO_BANK_REG_INT_EN:
+        bank->imask &= ~value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_DIS:
+        bank->imask |= value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_STAT:
+        bank->istat &= ~value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
+        bank->itype = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
+        bank->ipolarity = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_ANY:
+        bank->iany = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n", offset);
+        return;
+    }
+
+    zynq_gpio_update(s);
+}
+
+static void zynq_gpio_reset(DeviceState *dev)
+{
+    ZynqGPIOState *s = ZYNQ_GPIO(dev);
+    int b;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+        int i;
+
+        bank->mask_data = 0x00000000;
+        bank->dir = 0x00000000;
+        bank->oen = 0x00000000;
+        bank->imask = 0x00000000;
+        bank->istat = 0x00000000;
+        bank->itype = 0xffffffff;
+        bank->ipolarity = 0x00000000;
+        bank->iany = 0x00000000;
+
+        bank->out_data = 0x00000000;
+        bank->old_out_data = 0x00000000;
+        for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+            qemu_set_irq(bank->out[i], 0);
+        }
+
+        bank->old_in_data = bank->in_data;
+    }
+}
+
+static void zynq_gpio_set_irq(ZynqGPIOBank *bank, int irq, int level)
+{
+    uint32_t mask = 1 << irq;
+
+    bank->in_data &= ~mask;
+    if (level)
+        bank->in_data |= mask;
+
+    zynq_gpio_update_in(bank);
+
+    zynq_gpio_set_in_irq(bank->parent);
+}
+
+static void zynq_gpio_set_bank0_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[0], irq, level);
+}
+
+static void zynq_gpio_set_bank1_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[1], irq, level);
+}
+
+static void zynq_gpio_set_bank2_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[2], irq, level);
+}
+
+static void zynq_gpio_set_bank3_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+    zynq_gpio_set_irq(&s->banks[3], irq, level);
+}
+
+static const MemoryRegionOps zynq_gpio_ops = {
+    .read = zynq_gpio_read,
+    .write = zynq_gpio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void zynq_gpio_realize(DeviceState *dev, Error **errp)
+{
+    ZynqGPIOState *s = ZYNQ_GPIO(dev);
+    int b;
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &zynq_gpio_ops, s, "zynq-gpio", 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+        char name[16];
+
+        memset(bank, 0, sizeof(*bank));
+
+        bank->parent = s;
+
+        snprintf(name, sizeof(name), "bank%d_out", b);
+        qdev_init_gpio_out_named(dev, bank->out, name, ZYNQ_GPIO_IOS_PER_BANK);
+        /*
+         * TODO: it would be nice if we could pass the bank to the handler. This
+         * would allow us to remove the 4 callbacks and use zynq_gpio_set_irq
+         * directly.
+         */
+#if 0
+        snprintf(name, sizeof(name), "bank%d_in", b);
+        qdev_init_gpio_in_named(dev, zynq_gpio_set_irq, name, ZYNQ_GPIO_IOS_PER_BANK, bank);
+#endif
+    }
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank0_irq, "bank0_in", ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank1_irq, "bank1_in", ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank2_irq, "bank2_in", ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank3_irq, "bank3_in", ZYNQ_GPIO_IOS_PER_BANK);
+}
+
+static void zynq_gpio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = zynq_gpio_realize;
+    dc->reset = zynq_gpio_reset;
+}
+
+static const TypeInfo zynq_gpio_info = {
+    .name          = TYPE_ZYNQ_GPIO,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ZynqGPIOState),
+    .class_init    = zynq_gpio_class_init,
+};
+
+static void zynq_gpio_register_type(void)
+{
+    type_register_static(&zynq_gpio_info);
+}
+
+type_init(zynq_gpio_register_type)
diff --git a/include/hw/gpio/zynq-gpio.h b/include/hw/gpio/zynq-gpio.h
new file mode 100644
index 0000000..8146c1f
--- /dev/null
+++ b/include/hw/gpio/zynq-gpio.h
@@ -0,0 +1,79 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#ifndef HW_ZYNQ_GPIO_H
+#define HW_ZYNQ_GPIO_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ZYNQ_GPIO "zynq-gpio"
+#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
+
+#define ZYNQ_GPIO_BANKS         4
+#define ZYNQ_GPIO_IOS_PER_BANK  32
+
+typedef struct {
+    struct ZynqGPIOState *parent;
+
+    uint32_t mask_data;
+    uint32_t out_data;
+    uint32_t old_out_data;
+    uint32_t in_data;
+    uint32_t old_in_data;
+    uint32_t dir;
+    uint32_t oen;
+    uint32_t imask;
+    uint32_t istat;
+    uint32_t itype;
+    uint32_t ipolarity;
+    uint32_t iany;
+
+    qemu_irq out[ZYNQ_GPIO_IOS_PER_BANK];
+} ZynqGPIOBank;
+
+typedef struct ZynqGPIOState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    ZynqGPIOBank banks[ZYNQ_GPIO_BANKS];
+    qemu_irq irq;
+} ZynqGPIOState;
+
+#define ZYNQ_GPIO_REG_MASK_DATA_0_LSW   0x000
+#define ZYNQ_GPIO_REG_MASK_DATA_0_MSW   0x004
+#define ZYNQ_GPIO_REG_MASK_DATA_1_LSW   0x008
+#define ZYNQ_GPIO_REG_MASK_DATA_1_MSW   0x00C
+#define ZYNQ_GPIO_REG_MASK_DATA_2_LSW   0x010
+#define ZYNQ_GPIO_REG_MASK_DATA_2_MSW   0x014
+#define ZYNQ_GPIO_REG_MASK_DATA_3_LSW   0x018
+#define ZYNQ_GPIO_REG_MASK_DATA_3_MSW   0x01C
+#define ZYNQ_GPIO_REG_DATA_0            0x040
+#define ZYNQ_GPIO_REG_DATA_1            0x044
+#define ZYNQ_GPIO_REG_DATA_2            0x048
+#define ZYNQ_GPIO_REG_DATA_3            0x04C
+#define ZYNQ_GPIO_REG_DATA_0_RO         0x060
+#define ZYNQ_GPIO_REG_DATA_1_RO         0x064
+#define ZYNQ_GPIO_REG_DATA_2_RO         0x068
+#define ZYNQ_GPIO_REG_DATA_3_RO         0x06C
+
+/*
+ * Oddly enough these registers are neatly grouped per bank and not interleaved
+ * like the data registers
+ */
+#define ZYNQ_GPIO_BANK_OFFSET(bank)     (0x200 + 0x40 * (bank))
+#define ZYNQ_GPIO_BANK_REG_DIRM         0x04
+#define ZYNQ_GPIO_BANK_REG_OEN          0x08
+#define ZYNQ_GPIO_BANK_REG_INT_MASK     0x0C
+#define ZYNQ_GPIO_BANK_REG_INT_EN       0x10
+#define ZYNQ_GPIO_BANK_REG_INT_DIS      0x14
+#define ZYNQ_GPIO_BANK_REG_INT_STAT     0x18
+#define ZYNQ_GPIO_BANK_REG_INT_TYPE     0x1C
+#define ZYNQ_GPIO_BANK_REG_INT_POLARITY 0x20
+#define ZYNQ_GPIO_BANK_REG_INT_ANY      0x24
+
+#endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v3 2/2] xilinx_zynq: Add zynq-gpio to the machine
  2015-01-06 22:26 ` [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model Colin Leitner
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
@ 2015-01-06 22:26   ` Colin Leitner
  2015-01-20 22:06     ` Peter Crosthwaite
  2015-01-25 19:51   ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Colin Leitner
  2 siblings, 1 reply; 18+ messages in thread
From: Colin Leitner @ 2015-01-06 22:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/arm/xilinx_zynq.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 06e6e24..6d8c0d9 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -202,6 +202,8 @@ static void zynq_init(MachineState *machine)
     zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
     zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
 
+    sysbus_create_simple("zynq-gpio", 0xE000A000, pic[52-IRQ_OFFSET]);
+
     sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
     sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
@ 2015-01-20 22:05     ` Peter Crosthwaite
  0 siblings, 0 replies; 18+ messages in thread
From: Peter Crosthwaite @ 2015-01-20 22:05 UTC (permalink / raw)
  To: Colin Leitner; +Cc: Colin Leitner, qemu-devel@nongnu.org Developers

Hi Colin,


Sorry about the delay, review below.

On Tue, Jan 6, 2015 at 2:26 PM, Colin Leitner
<colin.leitner@googlemail.com> wrote:
> Based on the pl061 model. This model implements all four banks with 32 I/Os
> each.
>
> The I/Os are placed in named groups:
>
>  * bankX_in for the 32 inputs of each bank
>  * bankX_out for the 32 outputs of each bank
>
> Basic I/O and IRQ support tested with the Zynq GPIO driver in Linux 3.12.
>
> Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
> ---
>  hw/gpio/Makefile.objs       |    1 +
>  hw/gpio/zynq-gpio.c         |  402 +++++++++++++++++++++++++++++++++++++++++++
>  include/hw/gpio/zynq-gpio.h |   79 +++++++++
>  3 files changed, 482 insertions(+)
>  create mode 100644 hw/gpio/zynq-gpio.c
>  create mode 100644 include/hw/gpio/zynq-gpio.h
>
> diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
> index 1abcf17..c927c66 100644
> --- a/hw/gpio/Makefile.objs
> +++ b/hw/gpio/Makefile.objs
> @@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
>  common-obj-$(CONFIG_E500) += mpc8xxx.o
>
>  obj-$(CONFIG_OMAP) += omap_gpio.o
> +obj-$(CONFIG_ZYNQ) += zynq-gpio.o
> diff --git a/hw/gpio/zynq-gpio.c b/hw/gpio/zynq-gpio.c
> new file mode 100644
> index 0000000..8cf4262
> --- /dev/null
> +++ b/hw/gpio/zynq-gpio.c
> @@ -0,0 +1,402 @@
> +/*
> + * Zynq General Purpose IO
> + *
> + * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
> + *
> + * Based on the PL061 model:
> + *   Copyright (c) 2007 CodeSourcery.
> + *   Written by Paul Brook
> + *
> + * This code is licensed under the GPL.
> + */
> +
> +/*
> + * We model all banks as if they were fully populated. MIO pins are usually
> + * limited to 54 pins, but this is probably device dependent and shouldn't
> + * cause too much trouble. One noticeable difference is the reset value of
> + * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
> + *
> + * The output enable pins are not modeled.
> + */
> +
> +#include "hw/gpio/zynq-gpio.h"
> +#include "qemu/bitops.h"
> +
> +#ifndef ZYNQ_GPIO_ERR_DEBUG
> +#define ZYNQ_GPIO_ERR_DEBUG 0
> +#endif
> +
> +#define DB_PRINT_L(lvl, fmt, args...) do {\
> +    if (ZYNQ_GPIO_ERR_DEBUG >= lvl) {\
> +        qemu_log("zynq-gpio: %s:" fmt, __func__, ## args);\
> +    } \
> +} while (0);
> +
> +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
> +
> +static void zynq_gpio_update_out(ZynqGPIOBank *bank)
> +{
> +    uint32_t changed;
> +    uint32_t mask;
> +    uint32_t out;
> +    int i;
> +
> +    DB_PRINT("dir = %d, data = %d\n", bank->dir, bank->out_data);
> +
> +    /*
> +     * We assume non-driven (DIRM = 0) outputs float high. On real hardware this
> +     * could be different, but here we have to decide which value to set the
> +     * output IRQ to if the direction register switches the I/O to an input.
> +     */
> +    /* FIXME: This is board dependent. */
> +    out = (bank->out_data & bank->dir) | ~bank->dir;
> +
> +    changed = bank->old_out_data ^ out;
> +    bank->old_out_data = out;
> +
> +    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
> +        mask = 1 << i;
> +        if (changed & mask) {
> +            DB_PRINT("Set output %d = %d\n", i, (out & mask) != 0);
> +            qemu_set_irq(bank->out[i], (out & mask) != 0);
> +        }
> +    }
> +}
> +
> +static void zynq_gpio_update_in(ZynqGPIOBank *bank)
> +{
> +    uint32_t changed;
> +    uint32_t mask;
> +    int i;
> +
> +    changed = bank->old_in_data ^ bank->in_data;
> +    bank->old_in_data = bank->in_data;
> +
> +    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
> +        mask = 1 << i;
> +        if (changed & mask) {
> +            DB_PRINT("Changed input %d = %d\n", i, (bank->in_data & mask) != 0);
> +
> +            if (bank->itype & mask) {
> +                /* Edge interrupt */
> +                if (bank->iany & mask) {
> +                    /* Any edge triggers the interrupt */
> +                    bank->istat |= mask;
> +                } else {
> +                    /* Edge is selected by INT_POLARITY */
> +                    bank->istat |= ~(bank->in_data ^ bank->ipolarity) & mask;
> +                }
> +            }
> +        }
> +    }
> +
> +    /* Level interrupt */
> +    bank->istat |= ~(bank->in_data ^ bank->ipolarity) & ~bank->itype;
> +
> +    DB_PRINT("istat = %08X\n", bank->istat);
> +}
> +
> +static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
> +{
> +    int b;
> +    uint32_t istat = 0;
> +
> +    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> +        ZynqGPIOBank *bank = &s->banks[b];
> +
> +        istat |= bank->istat & ~bank->imask;
> +    }
> +
> +    DB_PRINT("IRQ = %d\n", istat != 0);
> +
> +    qemu_set_irq(s->irq, istat != 0);
> +}
> +
> +static void zynq_gpio_update(ZynqGPIOState *s)
> +{
> +    int b;
> +
> +    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> +        ZynqGPIOBank *bank = &s->banks[b];
> +
> +        zynq_gpio_update_out(bank);
> +        zynq_gpio_update_in(bank);
> +    }
> +
> +    zynq_gpio_set_in_irq(s);
> +}
> +
> +static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
> +                               unsigned int size)
> +{
> +    ZynqGPIOState *s = opaque;
> +    int b;
> +    int shift;
> +    ZynqGPIOBank *bank;
> +
> +    switch (offset) {
> +    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
> +        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
> +        shift = (offset & 0x8) ? 16 : 0;
> +        return extract32(s->banks[b].mask_data, shift, 16);
> +
> +    case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
> +        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
> +        return s->banks[b].out_data;
> +
> +    case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
> +        b = (offset - ZYNQ_GPIO_REG_DATA_0_RO) / 4;
> +        return s->banks[b].in_data;
> +    }
> +
> +    if (offset < 0x204 || offset > 0x2e4) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n", offset);
> +        return 0;
> +    }
> +
> +    b = (offset - 0x200) / 0x40;
> +    bank = &s->banks[b];
> +
> +    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
> +    case ZYNQ_GPIO_BANK_REG_DIRM:
> +        return bank->dir;
> +    case ZYNQ_GPIO_BANK_REG_OEN:
> +        return bank->oen;
> +    case ZYNQ_GPIO_BANK_REG_INT_MASK:
> +        return bank->imask;
> +    case ZYNQ_GPIO_BANK_REG_INT_EN:
> +        return 0;
> +    case ZYNQ_GPIO_BANK_REG_INT_DIS:
> +        return 0;
> +    case ZYNQ_GPIO_BANK_REG_INT_STAT:
> +        return bank->istat;
> +    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
> +        return bank->itype;
> +    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
> +        return bank->ipolarity;
> +    case ZYNQ_GPIO_BANK_REG_INT_ANY:
> +        return bank->iany;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n", offset);
> +        return 0;
> +    }
> +}
> +
> +static void zynq_gpio_mask_data(ZynqGPIOBank *bank, int bit_offset,
> +                                uint32_t mask_data)
> +{
> +    DB_PRINT("mask data offset = %d, mask_data = %08X\n", bit_offset, mask_data);
> +
> +    /* MASK_DATA registers are R/W on their data part */
> +    bank->mask_data = deposit32(bank->mask_data, bit_offset, 16, mask_data);
> +    bank->out_data = deposit32(bank->out_data, bit_offset, 16, mask_data);
> +
> +    zynq_gpio_update_out(bank);
> +}
> +
> +static void zynq_gpio_data(ZynqGPIOBank *bank, uint32_t data)
> +{
> +    bank->out_data = data;
> +    zynq_gpio_update_out(bank);
> +}
> +
> +static void zynq_gpio_write(void *opaque, hwaddr offset,
> +                            uint64_t value, unsigned int size)
> +{
> +    ZynqGPIOState *s = opaque;
> +    int b;
> +    int shift;
> +    ZynqGPIOBank *bank;
> +
> +    switch (offset) {
> +    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
> +        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
> +        shift = (offset & 0x8) ? 16 : 0;
> +        zynq_gpio_mask_data(&s->banks[b], shift, value);
> +        return;
> +
> +    case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
> +        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
> +        zynq_gpio_data(&s->banks[b], value);
> +        return;
> +
> +    case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
> +        return;
> +    }
> +
> +    if (offset < 0x204 || offset > 0x2e4) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n", offset);
> +        return;
> +    }
> +
> +    b = (offset - 0x200) / 0x40;
> +    bank = &s->banks[b];
> +
> +    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
> +    case ZYNQ_GPIO_BANK_REG_DIRM:
> +        bank->dir = value;
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_OEN:
> +        bank->oen = value;
> +        qemu_log_mask(LOG_UNIMP,
> +                     "zynq_gpio_write: Output enable lines not implemented\n");
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_INT_MASK:
> +        return;
> +    case ZYNQ_GPIO_BANK_REG_INT_EN:
> +        bank->imask &= ~value;
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_INT_DIS:
> +        bank->imask |= value;
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_INT_STAT:
> +        bank->istat &= ~value;
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
> +        bank->itype = value;
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
> +        bank->ipolarity = value;
> +        break;
> +    case ZYNQ_GPIO_BANK_REG_INT_ANY:
> +        bank->iany = value;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                     "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n", offset);
> +        return;
> +    }
> +
> +    zynq_gpio_update(s);
> +}
> +
> +static void zynq_gpio_reset(DeviceState *dev)
> +{
> +    ZynqGPIOState *s = ZYNQ_GPIO(dev);
> +    int b;
> +
> +    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> +        ZynqGPIOBank *bank = &s->banks[b];
> +        int i;
> +
> +        bank->mask_data = 0x00000000;
> +        bank->dir = 0x00000000;
> +        bank->oen = 0x00000000;
> +        bank->imask = 0x00000000;
> +        bank->istat = 0x00000000;
> +        bank->itype = 0xffffffff;
> +        bank->ipolarity = 0x00000000;
> +        bank->iany = 0x00000000;
> +
> +        bank->out_data = 0x00000000;
> +        bank->old_out_data = 0x00000000;
> +        for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
> +            qemu_set_irq(bank->out[i], 0);
> +        }
> +
> +        bank->old_in_data = bank->in_data;
> +    }
> +}
> +
> +static void zynq_gpio_set_irq(ZynqGPIOBank *bank, int irq, int level)
> +{
> +    uint32_t mask = 1 << irq;
> +
> +    bank->in_data &= ~mask;
> +    if (level)
> +        bank->in_data |= mask;
> +

You need braces {} around the if body. Checkpatch should catch this.

> +    zynq_gpio_update_in(bank);
> +
> +    zynq_gpio_set_in_irq(bank->parent);
> +}
> +
> +static void zynq_gpio_set_bank0_irq(void *opaque, int irq, int level)
> +{
> +    ZynqGPIOState *s = opaque;


Blank line here between declarations and function body.

> +    zynq_gpio_set_irq(&s->banks[0], irq, level);
> +}
> +
> +static void zynq_gpio_set_bank1_irq(void *opaque, int irq, int level)
> +{
> +    ZynqGPIOState *s = opaque;
> +    zynq_gpio_set_irq(&s->banks[1], irq, level);
> +}
> +
> +static void zynq_gpio_set_bank2_irq(void *opaque, int irq, int level)
> +{
> +    ZynqGPIOState *s = opaque;
> +    zynq_gpio_set_irq(&s->banks[2], irq, level);
> +}
> +
> +static void zynq_gpio_set_bank3_irq(void *opaque, int irq, int level)
> +{
> +    ZynqGPIOState *s = opaque;
> +    zynq_gpio_set_irq(&s->banks[3], irq, level);
> +}
> +
> +static const MemoryRegionOps zynq_gpio_ops = {
> +    .read = zynq_gpio_read,
> +    .write = zynq_gpio_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void zynq_gpio_realize(DeviceState *dev, Error **errp)
> +{
> +    ZynqGPIOState *s = ZYNQ_GPIO(dev);
> +    int b;
> +
> +    memory_region_init_io(&s->iomem, OBJECT(s), &zynq_gpio_ops, s, "zynq-gpio", 0x1000);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
> +    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
> +
> +    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> +        ZynqGPIOBank *bank = &s->banks[b];
> +        char name[16];
> +
> +        memset(bank, 0, sizeof(*bank));
> +

Is this needed? QOM should init struct fields to 0 for you.

> +        bank->parent = s;
> +
> +        snprintf(name, sizeof(name), "bank%d_out", b);
> +        qdev_init_gpio_out_named(dev, bank->out, name, ZYNQ_GPIO_IOS_PER_BANK);
> +        /*
> +         * TODO: it would be nice if we could pass the bank to the handler. This
> +         * would allow us to remove the 4 callbacks and use zynq_gpio_set_irq
> +         * directly.
> +         */
> +#if 0
> +        snprintf(name, sizeof(name), "bank%d_in", b);
> +        qdev_init_gpio_in_named(dev, zynq_gpio_set_irq, name, ZYNQ_GPIO_IOS_PER_BANK, bank);
> +#endif

Yes I am seeing the problem here now. But this is a better solution
than V1 so I think we are good here, pending a rethink of the GPIO API
WRT this problem.

With the small style issues fixed and a checkpatch pass, please add

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

To the next rev.

Regards,
Peter

> +    }
> +    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank0_irq, "bank0_in", ZYNQ_GPIO_IOS_PER_BANK);
> +    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank1_irq, "bank1_in", ZYNQ_GPIO_IOS_PER_BANK);
> +    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank2_irq, "bank2_in", ZYNQ_GPIO_IOS_PER_BANK);
> +    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank3_irq, "bank3_in", ZYNQ_GPIO_IOS_PER_BANK);
> +}
> +
> +static void zynq_gpio_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = zynq_gpio_realize;
> +    dc->reset = zynq_gpio_reset;
> +}
> +
> +static const TypeInfo zynq_gpio_info = {
> +    .name          = TYPE_ZYNQ_GPIO,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(ZynqGPIOState),
> +    .class_init    = zynq_gpio_class_init,
> +};
> +
> +static void zynq_gpio_register_type(void)
> +{
> +    type_register_static(&zynq_gpio_info);
> +}
> +
> +type_init(zynq_gpio_register_type)
> diff --git a/include/hw/gpio/zynq-gpio.h b/include/hw/gpio/zynq-gpio.h
> new file mode 100644
> index 0000000..8146c1f
> --- /dev/null
> +++ b/include/hw/gpio/zynq-gpio.h
> @@ -0,0 +1,79 @@
> +/*
> + * Zynq General Purpose IO
> + *
> + * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
> + *
> + * This code is licensed under the GPL.
> + */
> +
> +#ifndef HW_ZYNQ_GPIO_H
> +#define HW_ZYNQ_GPIO_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_ZYNQ_GPIO "zynq-gpio"
> +#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
> +
> +#define ZYNQ_GPIO_BANKS         4
> +#define ZYNQ_GPIO_IOS_PER_BANK  32
> +
> +typedef struct {
> +    struct ZynqGPIOState *parent;
> +
> +    uint32_t mask_data;
> +    uint32_t out_data;
> +    uint32_t old_out_data;
> +    uint32_t in_data;
> +    uint32_t old_in_data;
> +    uint32_t dir;
> +    uint32_t oen;
> +    uint32_t imask;
> +    uint32_t istat;
> +    uint32_t itype;
> +    uint32_t ipolarity;
> +    uint32_t iany;
> +
> +    qemu_irq out[ZYNQ_GPIO_IOS_PER_BANK];
> +} ZynqGPIOBank;
> +
> +typedef struct ZynqGPIOState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion iomem;
> +    ZynqGPIOBank banks[ZYNQ_GPIO_BANKS];
> +    qemu_irq irq;
> +} ZynqGPIOState;
> +
> +#define ZYNQ_GPIO_REG_MASK_DATA_0_LSW   0x000
> +#define ZYNQ_GPIO_REG_MASK_DATA_0_MSW   0x004
> +#define ZYNQ_GPIO_REG_MASK_DATA_1_LSW   0x008
> +#define ZYNQ_GPIO_REG_MASK_DATA_1_MSW   0x00C
> +#define ZYNQ_GPIO_REG_MASK_DATA_2_LSW   0x010
> +#define ZYNQ_GPIO_REG_MASK_DATA_2_MSW   0x014
> +#define ZYNQ_GPIO_REG_MASK_DATA_3_LSW   0x018
> +#define ZYNQ_GPIO_REG_MASK_DATA_3_MSW   0x01C
> +#define ZYNQ_GPIO_REG_DATA_0            0x040
> +#define ZYNQ_GPIO_REG_DATA_1            0x044
> +#define ZYNQ_GPIO_REG_DATA_2            0x048
> +#define ZYNQ_GPIO_REG_DATA_3            0x04C
> +#define ZYNQ_GPIO_REG_DATA_0_RO         0x060
> +#define ZYNQ_GPIO_REG_DATA_1_RO         0x064
> +#define ZYNQ_GPIO_REG_DATA_2_RO         0x068
> +#define ZYNQ_GPIO_REG_DATA_3_RO         0x06C
> +
> +/*
> + * Oddly enough these registers are neatly grouped per bank and not interleaved
> + * like the data registers
> + */
> +#define ZYNQ_GPIO_BANK_OFFSET(bank)     (0x200 + 0x40 * (bank))
> +#define ZYNQ_GPIO_BANK_REG_DIRM         0x04
> +#define ZYNQ_GPIO_BANK_REG_OEN          0x08
> +#define ZYNQ_GPIO_BANK_REG_INT_MASK     0x0C
> +#define ZYNQ_GPIO_BANK_REG_INT_EN       0x10
> +#define ZYNQ_GPIO_BANK_REG_INT_DIS      0x14
> +#define ZYNQ_GPIO_BANK_REG_INT_STAT     0x18
> +#define ZYNQ_GPIO_BANK_REG_INT_TYPE     0x1C
> +#define ZYNQ_GPIO_BANK_REG_INT_POLARITY 0x20
> +#define ZYNQ_GPIO_BANK_REG_INT_ANY      0x24
> +
> +#endif
> --
> 1.7.10.4
>
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PATCH v3 2/2] xilinx_zynq: Add zynq-gpio to the machine
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
@ 2015-01-20 22:06     ` Peter Crosthwaite
  0 siblings, 0 replies; 18+ messages in thread
From: Peter Crosthwaite @ 2015-01-20 22:06 UTC (permalink / raw)
  To: Colin Leitner; +Cc: Colin Leitner, qemu-devel@nongnu.org Developers

On Tue, Jan 6, 2015 at 2:26 PM, Colin Leitner
<colin.leitner@googlemail.com> wrote:
> Signed-off-by: Colin Leitner <colin.leitner@gmail.com>

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

> ---
>  hw/arm/xilinx_zynq.c |    2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
> index 06e6e24..6d8c0d9 100644
> --- a/hw/arm/xilinx_zynq.c
> +++ b/hw/arm/xilinx_zynq.c
> @@ -202,6 +202,8 @@ static void zynq_init(MachineState *machine)
>      zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
>      zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
>
> +    sysbus_create_simple("zynq-gpio", 0xE000A000, pic[52-IRQ_OFFSET]);
> +
>      sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
>      sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
>
> --
> 1.7.10.4
>
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model
  2015-01-06 22:26 ` [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model Colin Leitner
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
  2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
@ 2015-01-25 19:51   ` Colin Leitner
  2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
                       ` (2 more replies)
  2 siblings, 3 replies; 18+ messages in thread
From: Colin Leitner @ 2015-01-25 19:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Hello everyone,

this is the fourth version of the Zynq GPIO model patch. It includes

 * removal of unneeded memset in zynq_gpio_realize
 * some minor code cleanup
 * fixes for all remaining checkpatch warnings (lines too long)

Regards,
	Colin

Colin Leitner (2):
  zynq_gpio: GPIO model for Zynq SoC
  xilinx_zynq: Add zynq-gpio to the machine

 hw/arm/xilinx_zynq.c        |    2 +
 hw/gpio/Makefile.objs       |    1 +
 hw/gpio/zynq-gpio.c         |  413 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/gpio/zynq-gpio.h |   79 +++++++++
 4 files changed, 495 insertions(+)
 create mode 100644 hw/gpio/zynq-gpio.c
 create mode 100644 include/hw/gpio/zynq-gpio.h

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v4 1/2] zynq_gpio: GPIO model for Zynq SoC
  2015-01-25 19:51   ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Colin Leitner
@ 2015-01-25 19:51     ` Colin Leitner
  2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
  2015-02-10  6:06     ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Peter Crosthwaite
  2 siblings, 0 replies; 18+ messages in thread
From: Colin Leitner @ 2015-01-25 19:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Based on the pl061 model. This model implements all four banks with 32 I/Os
each.

The I/Os are placed in named groups:

 * bankX_in for the 32 inputs of each bank
 * bankX_out for the 32 outputs of each bank

Basic I/O and IRQ support tested with the Zynq GPIO driver in Linux 3.12.

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/gpio/Makefile.objs       |    1 +
 hw/gpio/zynq-gpio.c         |  413 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/gpio/zynq-gpio.h |   79 +++++++++
 3 files changed, 493 insertions(+)
 create mode 100644 hw/gpio/zynq-gpio.c
 create mode 100644 include/hw/gpio/zynq-gpio.h

diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 1abcf17..c927c66 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
 common-obj-$(CONFIG_E500) += mpc8xxx.o
 
 obj-$(CONFIG_OMAP) += omap_gpio.o
+obj-$(CONFIG_ZYNQ) += zynq-gpio.o
diff --git a/hw/gpio/zynq-gpio.c b/hw/gpio/zynq-gpio.c
new file mode 100644
index 0000000..c740f8f
--- /dev/null
+++ b/hw/gpio/zynq-gpio.c
@@ -0,0 +1,413 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * Based on the PL061 model:
+ *   Copyright (c) 2007 CodeSourcery.
+ *   Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/*
+ * We model all banks as if they were fully populated. MIO pins are usually
+ * limited to 54 pins, but this is probably device dependent and shouldn't
+ * cause too much trouble. One noticeable difference is the reset value of
+ * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
+ *
+ * The output enable pins are not modeled.
+ */
+
+#include "hw/gpio/zynq-gpio.h"
+#include "qemu/bitops.h"
+
+#ifndef ZYNQ_GPIO_ERR_DEBUG
+#define ZYNQ_GPIO_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do {\
+    if (ZYNQ_GPIO_ERR_DEBUG >= lvl) {\
+        qemu_log("zynq-gpio: %s:" fmt, __func__, ## args);\
+    } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+static void zynq_gpio_update_out(ZynqGPIOBank *bank)
+{
+    uint32_t changed;
+    uint32_t mask;
+    uint32_t out;
+    int i;
+
+    DB_PRINT("dir = %d, data = %d\n", bank->dir, bank->out_data);
+
+    /*
+     * We assume non-driven (DIRM = 0) outputs float high. On real hardware
+     * this could be different, but here we have to decide which value to set
+     * the output IRQ to if the direction register switches the I/O to an
+     * input.
+     */
+    /* FIXME: This is board dependent. */
+    out = (bank->out_data & bank->dir) | ~bank->dir;
+
+    changed = bank->old_out_data ^ out;
+    bank->old_out_data = out;
+
+    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DB_PRINT("Set output %d = %d\n", i, (out & mask) != 0);
+            qemu_set_irq(bank->out[i], (out & mask) != 0);
+        }
+    }
+}
+
+static void zynq_gpio_update_in(ZynqGPIOBank *bank)
+{
+    uint32_t changed;
+    uint32_t mask;
+    int i;
+
+    changed = bank->old_in_data ^ bank->in_data;
+    bank->old_in_data = bank->in_data;
+
+    for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DB_PRINT("Changed input %d = %d\n", i, (bank->in_data & mask) != 0);
+
+            if (bank->itype & mask) {
+                /* Edge interrupt */
+                if (bank->iany & mask) {
+                    /* Any edge triggers the interrupt */
+                    bank->istat |= mask;
+                } else {
+                    /* Edge is selected by INT_POLARITY */
+                    bank->istat |= ~(bank->in_data ^ bank->ipolarity) & mask;
+                }
+            }
+        }
+    }
+
+    /* Level interrupt */
+    bank->istat |= ~(bank->in_data ^ bank->ipolarity) & ~bank->itype;
+
+    DB_PRINT("istat = %08X\n", bank->istat);
+}
+
+static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
+{
+    int b;
+    uint32_t istat = 0;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+
+        istat |= bank->istat & ~bank->imask;
+    }
+
+    DB_PRINT("IRQ = %d\n", istat != 0);
+
+    qemu_set_irq(s->irq, istat != 0);
+}
+
+static void zynq_gpio_update(ZynqGPIOState *s)
+{
+    int b;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+
+        zynq_gpio_update_out(bank);
+        zynq_gpio_update_in(bank);
+    }
+
+    zynq_gpio_set_in_irq(s);
+}
+
+static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
+                               unsigned int size)
+{
+    ZynqGPIOState *s = opaque;
+    int b;
+    int shift;
+    ZynqGPIOBank *bank;
+
+    switch (offset) {
+    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
+        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
+        shift = (offset & 0x8) ? 16 : 0;
+        return extract32(s->banks[b].mask_data, shift, 16);
+
+    case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
+        return s->banks[b].out_data;
+
+    case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0_RO) / 4;
+        return s->banks[b].in_data;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n", offset);
+        return 0;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
+    case ZYNQ_GPIO_BANK_REG_DIRM:
+        return bank->dir;
+    case ZYNQ_GPIO_BANK_REG_OEN:
+        return bank->oen;
+    case ZYNQ_GPIO_BANK_REG_INT_MASK:
+        return bank->imask;
+    case ZYNQ_GPIO_BANK_REG_INT_EN:
+        return 0;
+    case ZYNQ_GPIO_BANK_REG_INT_DIS:
+        return 0;
+    case ZYNQ_GPIO_BANK_REG_INT_STAT:
+        return bank->istat;
+    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
+        return bank->itype;
+    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
+        return bank->ipolarity;
+    case ZYNQ_GPIO_BANK_REG_INT_ANY:
+        return bank->iany;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n", offset);
+        return 0;
+    }
+}
+
+static void zynq_gpio_mask_data(ZynqGPIOBank *bank, int bit_offset,
+                                uint32_t mask_data)
+{
+    DB_PRINT("mask data offset = %d, mask_data = %08X\n",
+            bit_offset, mask_data);
+
+    /* MASK_DATA registers are R/W on their data part */
+    bank->mask_data = deposit32(bank->mask_data, bit_offset, 16, mask_data);
+    bank->out_data = deposit32(bank->out_data, bit_offset, 16, mask_data);
+
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_data(ZynqGPIOBank *bank, uint32_t data)
+{
+    bank->out_data = data;
+    zynq_gpio_update_out(bank);
+}
+
+static void zynq_gpio_write(void *opaque, hwaddr offset,
+                            uint64_t value, unsigned int size)
+{
+    ZynqGPIOState *s = opaque;
+    int b;
+    int shift;
+    ZynqGPIOBank *bank;
+
+    switch (offset) {
+    case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
+        b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
+        shift = (offset & 0x8) ? 16 : 0;
+        zynq_gpio_mask_data(&s->banks[b], shift, value);
+        return;
+
+    case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
+        b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
+        zynq_gpio_data(&s->banks[b], value);
+        return;
+
+    case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
+        return;
+    }
+
+    if (offset < 0x204 || offset > 0x2e4) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n", offset);
+        return;
+    }
+
+    b = (offset - 0x200) / 0x40;
+    bank = &s->banks[b];
+
+    switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
+    case ZYNQ_GPIO_BANK_REG_DIRM:
+        bank->dir = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_OEN:
+        bank->oen = value;
+        qemu_log_mask(LOG_UNIMP,
+                     "zynq_gpio_write: Output enable lines not implemented\n");
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_MASK:
+        return;
+    case ZYNQ_GPIO_BANK_REG_INT_EN:
+        bank->imask &= ~value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_DIS:
+        bank->imask |= value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_STAT:
+        bank->istat &= ~value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_TYPE:
+        bank->itype = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
+        bank->ipolarity = value;
+        break;
+    case ZYNQ_GPIO_BANK_REG_INT_ANY:
+        bank->iany = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n", offset);
+        return;
+    }
+
+    zynq_gpio_update(s);
+}
+
+static void zynq_gpio_reset(DeviceState *dev)
+{
+    ZynqGPIOState *s = ZYNQ_GPIO(dev);
+    int b;
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+        int i;
+
+        bank->mask_data = 0x00000000;
+        bank->dir = 0x00000000;
+        bank->oen = 0x00000000;
+        bank->imask = 0x00000000;
+        bank->istat = 0x00000000;
+        bank->itype = 0xffffffff;
+        bank->ipolarity = 0x00000000;
+        bank->iany = 0x00000000;
+
+        bank->out_data = 0x00000000;
+        bank->old_out_data = 0x00000000;
+        for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
+            qemu_set_irq(bank->out[i], 0);
+        }
+
+        bank->old_in_data = bank->in_data;
+    }
+}
+
+static void zynq_gpio_set_irq(ZynqGPIOBank *bank, int irq, int level)
+{
+    uint32_t mask = 1 << irq;
+
+    bank->in_data &= ~mask;
+    if (level) {
+        bank->in_data |= mask;
+    }
+
+    zynq_gpio_update_in(bank);
+
+    zynq_gpio_set_in_irq(bank->parent);
+}
+
+static void zynq_gpio_set_bank0_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+
+    zynq_gpio_set_irq(&s->banks[0], irq, level);
+}
+
+static void zynq_gpio_set_bank1_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+
+    zynq_gpio_set_irq(&s->banks[1], irq, level);
+}
+
+static void zynq_gpio_set_bank2_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+
+    zynq_gpio_set_irq(&s->banks[2], irq, level);
+}
+
+static void zynq_gpio_set_bank3_irq(void *opaque, int irq, int level)
+{
+    ZynqGPIOState *s = opaque;
+
+    zynq_gpio_set_irq(&s->banks[3], irq, level);
+}
+
+static const MemoryRegionOps zynq_gpio_ops = {
+    .read = zynq_gpio_read,
+    .write = zynq_gpio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void zynq_gpio_realize(DeviceState *dev, Error **errp)
+{
+    ZynqGPIOState *s = ZYNQ_GPIO(dev);
+    int b;
+
+    memory_region_init_io(&s->iomem, OBJECT(s),
+                         &zynq_gpio_ops, s, "zynq-gpio", 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+    for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
+        ZynqGPIOBank *bank = &s->banks[b];
+        char name[16];
+
+        bank->parent = s;
+
+        snprintf(name, sizeof(name), "bank%d_out", b);
+        qdev_init_gpio_out_named(dev, bank->out, name, ZYNQ_GPIO_IOS_PER_BANK);
+        /*
+         * TODO: it would be nice if we could pass the bank to the handler.
+         * This would allow us to remove the 4 callbacks and use
+         * zynq_gpio_set_irq directly.
+         */
+#if 0
+        snprintf(name, sizeof(name), "bank%d_in", b);
+        qdev_init_gpio_in_named(dev, zynq_gpio_set_irq, name,
+                               ZYNQ_GPIO_IOS_PER_BANK, bank);
+#endif
+    }
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank0_irq, "bank0_in",
+                           ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank1_irq, "bank1_in",
+                           ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank2_irq, "bank2_in",
+                           ZYNQ_GPIO_IOS_PER_BANK);
+    qdev_init_gpio_in_named(dev, zynq_gpio_set_bank3_irq, "bank3_in",
+                           ZYNQ_GPIO_IOS_PER_BANK);
+}
+
+static void zynq_gpio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = zynq_gpio_realize;
+    dc->reset = zynq_gpio_reset;
+}
+
+static const TypeInfo zynq_gpio_info = {
+    .name          = TYPE_ZYNQ_GPIO,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ZynqGPIOState),
+    .class_init    = zynq_gpio_class_init,
+};
+
+static void zynq_gpio_register_type(void)
+{
+    type_register_static(&zynq_gpio_info);
+}
+
+type_init(zynq_gpio_register_type)
diff --git a/include/hw/gpio/zynq-gpio.h b/include/hw/gpio/zynq-gpio.h
new file mode 100644
index 0000000..8146c1f
--- /dev/null
+++ b/include/hw/gpio/zynq-gpio.h
@@ -0,0 +1,79 @@
+/*
+ * Zynq General Purpose IO
+ *
+ * Copyright (C) 2014 Colin Leitner <colin.leitner@gmail.com>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#ifndef HW_ZYNQ_GPIO_H
+#define HW_ZYNQ_GPIO_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ZYNQ_GPIO "zynq-gpio"
+#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
+
+#define ZYNQ_GPIO_BANKS         4
+#define ZYNQ_GPIO_IOS_PER_BANK  32
+
+typedef struct {
+    struct ZynqGPIOState *parent;
+
+    uint32_t mask_data;
+    uint32_t out_data;
+    uint32_t old_out_data;
+    uint32_t in_data;
+    uint32_t old_in_data;
+    uint32_t dir;
+    uint32_t oen;
+    uint32_t imask;
+    uint32_t istat;
+    uint32_t itype;
+    uint32_t ipolarity;
+    uint32_t iany;
+
+    qemu_irq out[ZYNQ_GPIO_IOS_PER_BANK];
+} ZynqGPIOBank;
+
+typedef struct ZynqGPIOState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    ZynqGPIOBank banks[ZYNQ_GPIO_BANKS];
+    qemu_irq irq;
+} ZynqGPIOState;
+
+#define ZYNQ_GPIO_REG_MASK_DATA_0_LSW   0x000
+#define ZYNQ_GPIO_REG_MASK_DATA_0_MSW   0x004
+#define ZYNQ_GPIO_REG_MASK_DATA_1_LSW   0x008
+#define ZYNQ_GPIO_REG_MASK_DATA_1_MSW   0x00C
+#define ZYNQ_GPIO_REG_MASK_DATA_2_LSW   0x010
+#define ZYNQ_GPIO_REG_MASK_DATA_2_MSW   0x014
+#define ZYNQ_GPIO_REG_MASK_DATA_3_LSW   0x018
+#define ZYNQ_GPIO_REG_MASK_DATA_3_MSW   0x01C
+#define ZYNQ_GPIO_REG_DATA_0            0x040
+#define ZYNQ_GPIO_REG_DATA_1            0x044
+#define ZYNQ_GPIO_REG_DATA_2            0x048
+#define ZYNQ_GPIO_REG_DATA_3            0x04C
+#define ZYNQ_GPIO_REG_DATA_0_RO         0x060
+#define ZYNQ_GPIO_REG_DATA_1_RO         0x064
+#define ZYNQ_GPIO_REG_DATA_2_RO         0x068
+#define ZYNQ_GPIO_REG_DATA_3_RO         0x06C
+
+/*
+ * Oddly enough these registers are neatly grouped per bank and not interleaved
+ * like the data registers
+ */
+#define ZYNQ_GPIO_BANK_OFFSET(bank)     (0x200 + 0x40 * (bank))
+#define ZYNQ_GPIO_BANK_REG_DIRM         0x04
+#define ZYNQ_GPIO_BANK_REG_OEN          0x08
+#define ZYNQ_GPIO_BANK_REG_INT_MASK     0x0C
+#define ZYNQ_GPIO_BANK_REG_INT_EN       0x10
+#define ZYNQ_GPIO_BANK_REG_INT_DIS      0x14
+#define ZYNQ_GPIO_BANK_REG_INT_STAT     0x18
+#define ZYNQ_GPIO_BANK_REG_INT_TYPE     0x1C
+#define ZYNQ_GPIO_BANK_REG_INT_POLARITY 0x20
+#define ZYNQ_GPIO_BANK_REG_INT_ANY      0x24
+
+#endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [Qemu-devel] [PATCH v4 2/2] xilinx_zynq: Add zynq-gpio to the machine
  2015-01-25 19:51   ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Colin Leitner
  2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
@ 2015-01-25 19:51     ` Colin Leitner
  2015-02-10  6:06     ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Peter Crosthwaite
  2 siblings, 0 replies; 18+ messages in thread
From: Colin Leitner @ 2015-01-25 19:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.crosthwaite, Colin Leitner

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
---
 hw/arm/xilinx_zynq.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 06e6e24..6d8c0d9 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -202,6 +202,8 @@ static void zynq_init(MachineState *machine)
     zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
     zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
 
+    sysbus_create_simple("zynq-gpio", 0xE000A000, pic[52-IRQ_OFFSET]);
+
     sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
     sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model
  2015-01-25 19:51   ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Colin Leitner
  2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
  2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
@ 2015-02-10  6:06     ` Peter Crosthwaite
  2015-02-21 17:59       ` Peter Crosthwaite
  2 siblings, 1 reply; 18+ messages in thread
From: Peter Crosthwaite @ 2015-02-10  6:06 UTC (permalink / raw)
  To: Colin Leitner, Peter Maydell
  Cc: Colin Leitner, qemu-devel@nongnu.org Developers

On Sun, Jan 25, 2015 at 11:51 AM, Colin Leitner
<colin.leitner@googlemail.com> wrote:
> Hello everyone,
>
> this is the fourth version of the Zynq GPIO model patch. It includes
>
>  * removal of unneeded memset in zynq_gpio_realize
>  * some minor code cleanup
>  * fixes for all remaining checkpatch warnings (lines too long)
>
> Regards,
>         Colin
>

CC Peter for target arm.

Regards,
Peter

> Colin Leitner (2):
>   zynq_gpio: GPIO model for Zynq SoC
>   xilinx_zynq: Add zynq-gpio to the machine
>
>  hw/arm/xilinx_zynq.c        |    2 +
>  hw/gpio/Makefile.objs       |    1 +
>  hw/gpio/zynq-gpio.c         |  413 +++++++++++++++++++++++++++++++++++++++++++
>  include/hw/gpio/zynq-gpio.h |   79 +++++++++
>  4 files changed, 495 insertions(+)
>  create mode 100644 hw/gpio/zynq-gpio.c
>  create mode 100644 include/hw/gpio/zynq-gpio.h
>
> --
> 1.7.10.4
>
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model
  2015-02-10  6:06     ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Peter Crosthwaite
@ 2015-02-21 17:59       ` Peter Crosthwaite
  0 siblings, 0 replies; 18+ messages in thread
From: Peter Crosthwaite @ 2015-02-21 17:59 UTC (permalink / raw)
  To: Colin Leitner, Peter Maydell
  Cc: Colin Leitner, qemu-devel@nongnu.org Developers

On Mon, Feb 9, 2015 at 10:06 PM, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sun, Jan 25, 2015 at 11:51 AM, Colin Leitner
> <colin.leitner@googlemail.com> wrote:
>> Hello everyone,
>>
>> this is the fourth version of the Zynq GPIO model patch. It includes
>>
>>  * removal of unneeded memset in zynq_gpio_realize
>>  * some minor code cleanup
>>  * fixes for all remaining checkpatch warnings (lines too long)
>>
>> Regards,
>>         Colin
>>
>
> CC Peter for target arm.
>

Hi Peter,

Do you need a [PULL] from me for this one?

Regards,
Peter

> Regards,
> Peter
>
>> Colin Leitner (2):
>>   zynq_gpio: GPIO model for Zynq SoC
>>   xilinx_zynq: Add zynq-gpio to the machine
>>
>>  hw/arm/xilinx_zynq.c        |    2 +
>>  hw/gpio/Makefile.objs       |    1 +
>>  hw/gpio/zynq-gpio.c         |  413 +++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/gpio/zynq-gpio.h |   79 +++++++++
>>  4 files changed, 495 insertions(+)
>>  create mode 100644 hw/gpio/zynq-gpio.c
>>  create mode 100644 include/hw/gpio/zynq-gpio.h
>>
>> --
>> 1.7.10.4
>>
>>

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2015-02-21 17:59 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-30 13:13 [Qemu-devel] [PATCH 0/2] GPIO model for Zynq SoC Colin Leitner
2014-12-30 13:13 ` [Qemu-devel] [PATCH 1/2] zynq_gpio: " Colin Leitner
2014-12-30 23:24   ` Peter Crosthwaite
2014-12-31  1:03     ` Colin Leitner
2014-12-30 13:13 ` [Qemu-devel] [PATCH 2/2] xilinx_zynq: Add zynq_gpio to the machine Colin Leitner
2014-12-31 14:06 ` [Qemu-devel] [PATCH v2 0/2] Reworked Zynq GPIO model Colin Leitner
2014-12-31 14:06   ` [Qemu-devel] [PATCH v2 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
2014-12-31 14:06   ` [Qemu-devel] [PATCH v2 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
2015-01-06 22:26 ` [Qemu-devel] [PATCH v3 0/2] Reworked Zynq GPIO model Colin Leitner
2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
2015-01-20 22:05     ` Peter Crosthwaite
2015-01-06 22:26   ` [Qemu-devel] [PATCH v3 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
2015-01-20 22:06     ` Peter Crosthwaite
2015-01-25 19:51   ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Colin Leitner
2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 1/2] zynq_gpio: GPIO model for Zynq SoC Colin Leitner
2015-01-25 19:51     ` [Qemu-devel] [PATCH v4 2/2] xilinx_zynq: Add zynq-gpio to the machine Colin Leitner
2015-02-10  6:06     ` [Qemu-devel] [PATCH v4 0/2] Reworked Zynq GPIO model Peter Crosthwaite
2015-02-21 17:59       ` Peter Crosthwaite

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