diff mbox

[05/13] mxs/imx23: Add the interrupt collector

Message ID CAEgOgz4Hb_22As8hbUMMNFg3KxtDbCWvWSgUuH1NgP9ux1YT0A@mail.gmail.com
State New
Headers show

Commit Message

Peter Crosthwaite Jan. 11, 2014, 8:29 a.m. UTC
---------- Forwarded message ----------
From: Michel Pollet <buserror@gmail.com>
Date: Wed, Dec 11, 2013 at 11:56 PM
Subject: [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector
To: qemu-devel@nongnu.org
Cc: Michel Pollet <buserror@gmail.com>


Implements the interrupt collector IO block

Signed-off-by: Michel Pollet <buserror@gmail.com>
---
 hw/intc/Makefile.objs |   1 +
 hw/intc/mxs_icoll.c   | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+)
 create mode 100644 hw/intc/mxs_icoll.c


Regards,
Peter

+            if (irqval & 0x10) // ENFIQ
+                s->fiq[irqi / 32] |= (1 << (irqi % 32));
+            else
+                s->fiq[irqi / 32] &= ~(1 << (irqi % 32));
+            if (irqval & 0x8) // SOFTIRQ
+                mxs_icoll_set_irq(s, irqi, 1);
+            break;
+    }
+
+    mxs_icoll_update(s);
+}
+
+static const MemoryRegionOps mxs_icoll_ops = {
+    .read = mxs_icoll_read,
+    .write = mxs_icoll_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_icoll_init(SysBusDevice *dev)
+{
+    mxs_icoll_state *s = OBJECT_CHECK(mxs_icoll_state, dev, "mxs_icoll");
+    DeviceState *qdev = DEVICE(dev);
+
+    qdev_init_gpio_in(qdev, mxs_icoll_set_irq, 128);
+    sysbus_init_irq(dev, &s->parent_irq);
+    sysbus_init_irq(dev, &s->parent_fiq);
+    memory_region_init_io(&s->iomem, OBJECT(s), &mxs_icoll_ops, s,
+            "mxs_icoll", 0x2000);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static void mxs_icoll_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = mxs_icoll_init;
+}
+
+static TypeInfo icoll_info = {
+    .name          = "mxs_icoll",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(mxs_icoll_state),
+    .class_init    = mxs_icoll_class_init,
+};
+
+static void mxs_icoll_register(void)
+{
+    type_register_static(&icoll_info);
+}
+
+type_init(mxs_icoll_register)
--
1.8.5.1
diff mbox

Patch

diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 47ac442..e934b3c 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -24,3 +24,4 @@  obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
 obj-$(CONFIG_SH4) += sh_intc.o
 obj-$(CONFIG_XICS) += xics.o
 obj-$(CONFIG_XICS_KVM) += xics_kvm.o
+obj-$(CONFIG_MXS) += mxs_icoll.o
diff --git a/hw/intc/mxs_icoll.c b/hw/intc/mxs_icoll.c
new file mode 100644
index 0000000..a1fd7d9
--- /dev/null
+++ b/hw/intc/mxs_icoll.c
@@ -0,0 +1,200 @@ 
+/*
+ * mxs_icoll.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * This block implements the interrupt collector of the mxs
+ * Currently no priority is handled, as linux doesn't use them anyway
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+enum {
+    ICOLL_VECTOR = 0,
+    ICOLL_LEVELACK = 1,
+    ICOLL_CTRL = 2,
+    // 3, reserved?

Any documentation with an answer? I'd just drop the comment TBH,

+    ICOLL_VBASE = 4,
+    ICOLL_STAT = 7,
+
+    ICOLL_REG_MAX,
+
+    ICOLL_RAW0 = 0xa,
+    ICOLL_RAW1,
+    ICOLL_RAW2,
+    ICOLL_RAW3,
+
+    ICOLL_INT0 = 0x12,
+    ICOLL_INT127 = 0x91,

Calculate ICOLL_INT127 arithmetically to be self documenting.

+};
+
+typedef struct mxs_icoll_state {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t   reg[ICOLL_REG_MAX];
+
+    uint32_t   raised[4];
+    uint32_t   fiq[4];
+    uint32_t   irq[4];
+
+    uint8_t    r[128];

Magic numbers "128" and "4" could use macro definition.

+
+    qemu_irq parent_irq;
+    qemu_irq parent_fiq;
+} mxs_icoll_state;
+
+static void mxs_icoll_update(mxs_icoll_state *s)
+{
+    int fiq = 0, irq = 0;
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        int id = ffs(s->raised[i]);
+        int vector = (i * 32) + id - 1;

ffs will set id to correspond to the first raised interrupt whether
its enabled on not. So in the case where a higher priority interrupt
is masked and both it and a lower priority interrupt occurs, the
vector logic below will be calcalated for the masked higher priority.
Do you really intend to take the vector for a disabled interrupt?

+        if (s->raised[i] & s->fiq[i]) {
+            fiq++;
+            s->reg[ICOLL_STAT] = vector;
+            break;
+        }
+        if (s->raised[i] & s->irq[i]) {
+            irq++;
+            s->reg[ICOLL_STAT] = vector;

So this feels strange. A set IRQ always takes priority over FIQ WRT the vector.

+            break;
+        }
+    }
+    qemu_set_irq(s->parent_irq, irq != 0);
+    qemu_set_irq(s->parent_fiq, fiq != 0);
+}
+
+static void mxs_icoll_set_irq(void *opaque, int irq, int level)
+{
+    mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+    if (level)
+        s->raised[(irq / 32)] |= 1 << (irq % 32);
+    else
+        s->raised[(irq / 32)] &= ~(1 << (irq % 32));

deposit32:

s->raised[irq / 32] = deposit32(s->raised[irq / 32], irq % 32, 1, !!level);

or something like that.

Although looking at this, your s->raised variables are double-edge
sensitive. If the original interrupt goes down so does the raised bit.
Does this really not have any latching behavior (esp for edge
interrupts)?

The other problem with this is level sens. interrupts. If a level
sensitive interrupt occurs from a device and stays high, your
controller can ack it, yet it shouldn't de-assert the interrupt as the
(level sens.) pin is still high. The code here wont retrigger until
the pin strobes from the originating device.

You may need to separate out the raw input pin state from the pending
interrupt state. Originally I was operating under the assumption that
s->raised is simply the raw pin state [1] ....

+    mxs_icoll_update(s);
+}
+
+static uint64_t mxs_icoll_read(void *opaque, hwaddr offset, unsigned size)
+{
+    mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+
+    switch (offset >> 4) {
+        case 0 ... ICOLL_REG_MAX:
+            return s->reg[offset >> 4];
+        case ICOLL_RAW0 ... ICOLL_RAW3:
+            return s->raised[(offset >> 4) - ICOLL_RAW0];
+        case ICOLL_INT0 ... ICOLL_INT127:
+            return s->r[(offset >> 4) - ICOLL_INT0];
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                    "%s: bad offset 0x%x\n", __func__, (int) offset);
+            break;
+    }
+    return 0;
+}
+
+static void mxs_icoll_write(
+        void *opaque, hwaddr offset, uint64_t value, unsigned size)
+{
+    mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+    uint32_t irqval, irqi = 0;
+    uint32_t * dst = NULL;
+    uint32_t oldvalue = 0;
+
+    switch (offset >> 4) {
+        case 0 ... ICOLL_REG_MAX:
+            dst = s->reg + (offset >> 4);
+            break;
+        case ICOLL_INT0 ... ICOLL_INT127:
+            irqi = (offset >> 4) - ICOLL_INT0;
+            irqval = s->r[irqi];
+            dst = &irqval;
+            break;
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                    "%s: bad offset 0x%x\n", __func__, (int) offset);
+            break;
+    }
+    if (!dst) {
+        return;
+    }
+    oldvalue = mxs_write(dst, offset, value, size);
+
+    switch (offset >> 4) {
+        case ICOLL_CTRL:
+            if ((oldvalue ^ s->r[ICOLL_CTRL]) == 0x80000000
+                    && !(oldvalue & 0x80000000)) {
+                //     printf("%s reseting, anding clockgate\n", __func__);

"resetting"

+                s->r[ICOLL_CTRL] |= 0x40000000;
+            }
+            break;
+        case ICOLL_LEVELACK:
+            irqi = s->reg[ICOLL_STAT] & 0x7f;
+            s->raised[(irqi / 32)] &= ~(1 << (irqi % 32));

[1] ... but this code here is clearing s->raised on software command.
So s->raised seems to have a mixed definition between "raw pin state"
and "interrupt is pending". Perhaps you can clarify what is the
intended meaning of raised?

+            s->reg[ICOLL_STAT] = 0x7f;
+            break;
+        case ICOLL_INT0 ... ICOLL_INT127:
+            s->r[irqi] = irqval & ~(0x40); // dont' set softirq bit
+            if (irqval & 0x4) // ENABLE
+                s->irq[irqi / 32] |= (1 << (irqi % 32));
+            else
+                s->irq[irqi / 32] &= ~(1 << (irqi % 32));

deposit32 can make short work of this.