summaryrefslogtreecommitdiff
path: root/hw/intc
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2013-03-01 13:59:19 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2013-04-08 18:13:12 +0200
commit49ab747f668f421138d5b40d83fa279c4c5e278d (patch)
tree943225a04eac885aed038731adf058f2250a2f40 /hw/intc
parentce3b494cb504f96992f2d37ebc8f56deed202b06 (diff)
downloadqemu-49ab747f668f421138d5b40d83fa279c4c5e278d.tar.gz
hw: move target-independent files to subdirectories
This patch tackles all files that are compiled once, moving them to subdirectories of hw/. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/intc')
-rw-r--r--hw/intc/Makefile.objs5
-rw-r--r--hw/intc/heathrow_pic.c215
-rw-r--r--hw/intc/i8259.c496
-rw-r--r--hw/intc/i8259_common.c161
-rw-r--r--hw/intc/pl190.c289
-rw-r--r--hw/intc/puv3_intc.c135
-rw-r--r--hw/intc/xilinx_intc.c190
7 files changed, 1491 insertions, 0 deletions
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index e69de29bb2..2813adb3e7 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -0,0 +1,5 @@
+common-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
+common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
+common-obj-$(CONFIG_PL190) += pl190.o
+common-obj-$(CONFIG_PUV3) += puv3_intc.o
+common-obj-$(CONFIG_XILINX) += xilinx_intc.o
diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c
new file mode 100644
index 0000000000..beb9661182
--- /dev/null
+++ b/hw/intc/heathrow_pic.c
@@ -0,0 +1,215 @@
+/*
+ * Heathrow PIC support (OldWorld PowerMac)
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ppc/mac.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+#ifdef DEBUG_PIC
+#define PIC_DPRINTF(fmt, ...) \
+ do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define PIC_DPRINTF(fmt, ...)
+#endif
+
+typedef struct HeathrowPIC {
+ uint32_t events;
+ uint32_t mask;
+ uint32_t levels;
+ uint32_t level_triggered;
+} HeathrowPIC;
+
+typedef struct HeathrowPICS {
+ MemoryRegion mem;
+ HeathrowPIC pics[2];
+ qemu_irq *irqs;
+} HeathrowPICS;
+
+static inline int check_irq(HeathrowPIC *pic)
+{
+ return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
+}
+
+/* update the CPU irq state */
+static void heathrow_pic_update(HeathrowPICS *s)
+{
+ if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
+ qemu_irq_raise(s->irqs[0]);
+ } else {
+ qemu_irq_lower(s->irqs[0]);
+ }
+}
+
+static void pic_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int n;
+
+ n = ((addr & 0xfff) - 0x10) >> 4;
+ PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+ if (n >= 2)
+ return;
+ pic = &s->pics[n];
+ switch(addr & 0xf) {
+ case 0x04:
+ pic->mask = value;
+ heathrow_pic_update(s);
+ break;
+ case 0x08:
+ /* do not reset level triggered IRQs */
+ value &= ~pic->level_triggered;
+ pic->events &= ~value;
+ heathrow_pic_update(s);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint64_t pic_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int n;
+ uint32_t value;
+
+ n = ((addr & 0xfff) - 0x10) >> 4;
+ if (n >= 2) {
+ value = 0;
+ } else {
+ pic = &s->pics[n];
+ switch(addr & 0xf) {
+ case 0x0:
+ value = pic->events;
+ break;
+ case 0x4:
+ value = pic->mask;
+ break;
+ case 0xc:
+ value = pic->levels;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ }
+ PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+ return value;
+}
+
+static const MemoryRegionOps heathrow_pic_ops = {
+ .read = pic_read,
+ .write = pic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void heathrow_pic_set_irq(void *opaque, int num, int level)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int irq_bit;
+
+#if defined(DEBUG)
+ {
+ static int last_level[64];
+ if (last_level[num] != level) {
+ PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level);
+ last_level[num] = level;
+ }
+ }
+#endif
+ pic = &s->pics[1 - (num >> 5)];
+ irq_bit = 1 << (num & 0x1f);
+ if (level) {
+ pic->events |= irq_bit & ~pic->level_triggered;
+ pic->levels |= irq_bit;
+ } else {
+ pic->levels &= ~irq_bit;
+ }
+ heathrow_pic_update(s);
+}
+
+static const VMStateDescription vmstate_heathrow_pic_one = {
+ .name = "heathrow_pic_one",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(events, HeathrowPIC),
+ VMSTATE_UINT32(mask, HeathrowPIC),
+ VMSTATE_UINT32(levels, HeathrowPIC),
+ VMSTATE_UINT32(level_triggered, HeathrowPIC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_heathrow_pic = {
+ .name = "heathrow_pic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1,
+ vmstate_heathrow_pic_one, HeathrowPIC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void heathrow_pic_reset_one(HeathrowPIC *s)
+{
+ memset(s, '\0', sizeof(HeathrowPIC));
+}
+
+static void heathrow_pic_reset(void *opaque)
+{
+ HeathrowPICS *s = opaque;
+
+ heathrow_pic_reset_one(&s->pics[0]);
+ heathrow_pic_reset_one(&s->pics[1]);
+
+ s->pics[0].level_triggered = 0;
+ s->pics[1].level_triggered = 0x1ff00000;
+}
+
+qemu_irq *heathrow_pic_init(MemoryRegion **pmem,
+ int nb_cpus, qemu_irq **irqs)
+{
+ HeathrowPICS *s;
+
+ s = g_malloc0(sizeof(HeathrowPICS));
+ /* only 1 CPU */
+ s->irqs = irqs[0];
+ memory_region_init_io(&s->mem, &heathrow_pic_ops, s,
+ "heathrow-pic", 0x1000);
+ *pmem = &s->mem;
+
+ vmstate_register(NULL, -1, &vmstate_heathrow_pic, s);
+ qemu_register_reset(heathrow_pic_reset, s);
+ return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64);
+}
diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c
new file mode 100644
index 0000000000..ce14bd0f94
--- /dev/null
+++ b/hw/intc/i8259.c
@@ -0,0 +1,496 @@
+/*
+ * QEMU 8259 interrupt controller emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "monitor/monitor.h"
+#include "qemu/timer.h"
+#include "hw/isa/i8259_internal.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+#ifdef DEBUG_PIC
+#define DPRINTF(fmt, ...) \
+ do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+//#define DEBUG_IRQ_LATENCY
+//#define DEBUG_IRQ_COUNT
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+static int irq_level[16];
+#endif
+#ifdef DEBUG_IRQ_COUNT
+static uint64_t irq_count[16];
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+static int64_t irq_time[16];
+#endif
+DeviceState *isa_pic;
+static PICCommonState *slave_pic;
+
+/* return the highest priority found in mask (highest = smallest
+ number). Return 8 if no irq */
+static int get_priority(PICCommonState *s, int mask)
+{
+ int priority;
+
+ if (mask == 0) {
+ return 8;
+ }
+ priority = 0;
+ while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
+ priority++;
+ }
+ return priority;
+}
+
+/* return the pic wanted interrupt. return -1 if none */
+static int pic_get_irq(PICCommonState *s)
+{
+ int mask, cur_priority, priority;
+
+ mask = s->irr & ~s->imr;
+ priority = get_priority(s, mask);
+ if (priority == 8) {
+ return -1;
+ }
+ /* compute current priority. If special fully nested mode on the
+ master, the IRQ coming from the slave is not taken into account
+ for the priority computation. */
+ mask = s->isr;
+ if (s->special_mask) {
+ mask &= ~s->imr;
+ }
+ if (s->special_fully_nested_mode && s->master) {
+ mask &= ~(1 << 2);
+ }
+ cur_priority = get_priority(s, mask);
+ if (priority < cur_priority) {
+ /* higher priority found: an irq should be generated */
+ return (priority + s->priority_add) & 7;
+ } else {
+ return -1;
+ }
+}
+
+/* Update INT output. Must be called every time the output may have changed. */
+static void pic_update_irq(PICCommonState *s)
+{
+ int irq;
+
+ irq = pic_get_irq(s);
+ if (irq >= 0) {
+ DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
+ s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
+ qemu_irq_raise(s->int_out[0]);
+ } else {
+ qemu_irq_lower(s->int_out[0]);
+ }
+}
+
+/* set irq level. If an edge is detected, then the IRR is set to 1 */
+static void pic_set_irq(void *opaque, int irq, int level)
+{
+ PICCommonState *s = opaque;
+ int mask = 1 << irq;
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
+ defined(DEBUG_IRQ_LATENCY)
+ int irq_index = s->master ? irq : irq + 8;
+#endif
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+ if (level != irq_level[irq_index]) {
+ DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
+ irq_level[irq_index] = level;
+#ifdef DEBUG_IRQ_COUNT
+ if (level == 1) {
+ irq_count[irq_index]++;
+ }
+#endif
+ }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+ if (level) {
+ irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
+ }
+#endif
+
+ if (s->elcr & mask) {
+ /* level triggered */
+ if (level) {
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->irr &= ~mask;
+ s->last_irr &= ~mask;
+ }
+ } else {
+ /* edge triggered */
+ if (level) {
+ if ((s->last_irr & mask) == 0) {
+ s->irr |= mask;
+ }
+ s->last_irr |= mask;
+ } else {
+ s->last_irr &= ~mask;
+ }
+ }
+ pic_update_irq(s);
+}
+
+/* acknowledge interrupt 'irq' */
+static void pic_intack(PICCommonState *s, int irq)
+{
+ if (s->auto_eoi) {
+ if (s->rotate_on_auto_eoi) {
+ s->priority_add = (irq + 1) & 7;
+ }
+ } else {
+ s->isr |= (1 << irq);
+ }
+ /* We don't clear a level sensitive interrupt here */
+ if (!(s->elcr & (1 << irq))) {
+ s->irr &= ~(1 << irq);
+ }
+ pic_update_irq(s);
+}
+
+int pic_read_irq(DeviceState *d)
+{
+ PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
+ int irq, irq2, intno;
+
+ irq = pic_get_irq(s);
+ if (irq >= 0) {
+ if (irq == 2) {
+ irq2 = pic_get_irq(slave_pic);
+ if (irq2 >= 0) {
+ pic_intack(slave_pic, irq2);
+ } else {
+ /* spurious IRQ on slave controller */
+ irq2 = 7;
+ }
+ intno = slave_pic->irq_base + irq2;
+ } else {
+ intno = s->irq_base + irq;
+ }
+ pic_intack(s, irq);
+ } else {
+ /* spurious IRQ on host controller */
+ irq = 7;
+ intno = s->irq_base + irq;
+ }
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
+ if (irq == 2) {
+ irq = irq2 + 8;
+ }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+ printf("IRQ%d latency=%0.3fus\n",
+ irq,
+ (double)(qemu_get_clock_ns(vm_clock) -
+ irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
+#endif
+ DPRINTF("pic_interrupt: irq=%d\n", irq);
+ return intno;
+}
+
+static void pic_init_reset(PICCommonState *s)
+{
+ pic_reset_common(s);
+ pic_update_irq(s);
+}
+
+static void pic_reset(DeviceState *dev)
+{
+ PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev);
+
+ s->elcr = 0;
+ pic_init_reset(s);
+}
+
+static void pic_ioport_write(void *opaque, hwaddr addr64,
+ uint64_t val64, unsigned size)
+{
+ PICCommonState *s = opaque;
+ uint32_t addr = addr64;
+ uint32_t val = val64;
+ int priority, cmd, irq;
+
+ DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
+ if (addr == 0) {
+ if (val & 0x10) {
+ pic_init_reset(s);
+ s->init_state = 1;
+ s->init4 = val & 1;
+ s->single_mode = val & 2;
+ if (val & 0x08) {
+ hw_error("level sensitive irq not supported");
+ }
+ } else if (val & 0x08) {
+ if (val & 0x04) {
+ s->poll = 1;
+ }
+ if (val & 0x02) {
+ s->read_reg_select = val & 1;
+ }
+ if (val & 0x40) {
+ s->special_mask = (val >> 5) & 1;
+ }
+ } else {
+ cmd = val >> 5;
+ switch (cmd) {
+ case 0:
+ case 4:
+ s->rotate_on_auto_eoi = cmd >> 2;
+ break;
+ case 1: /* end of interrupt */
+ case 5:
+ priority = get_priority(s, s->isr);
+ if (priority != 8) {
+ irq = (priority + s->priority_add) & 7;
+ s->isr &= ~(1 << irq);
+ if (cmd == 5) {
+ s->priority_add = (irq + 1) & 7;
+ }
+ pic_update_irq(s);
+ }
+ break;
+ case 3:
+ irq = val & 7;
+ s->isr &= ~(1 << irq);
+ pic_update_irq(s);
+ break;
+ case 6:
+ s->priority_add = (val + 1) & 7;
+ pic_update_irq(s);
+ break;
+ case 7:
+ irq = val & 7;
+ s->isr &= ~(1 << irq);
+ s->priority_add = (irq + 1) & 7;
+ pic_update_irq(s);
+ break;
+ default:
+ /* no operation */
+ break;
+ }
+ }
+ } else {
+ switch (s->init_state) {
+ case 0:
+ /* normal mode */
+ s->imr = val;
+ pic_update_irq(s);
+ break;
+ case 1:
+ s->irq_base = val & 0xf8;
+ s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
+ break;
+ case 2:
+ if (s->init4) {
+ s->init_state = 3;
+ } else {
+ s->init_state = 0;
+ }
+ break;
+ case 3:
+ s->special_fully_nested_mode = (val >> 4) & 1;
+ s->auto_eoi = (val >> 1) & 1;
+ s->init_state = 0;
+ break;
+ }
+ }
+}
+
+static uint64_t pic_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PICCommonState *s = opaque;
+ int ret;
+
+ if (s->poll) {
+ ret = pic_get_irq(s);
+ if (ret >= 0) {
+ pic_intack(s, ret);
+ ret |= 0x80;
+ } else {
+ ret = 0;
+ }
+ s->poll = 0;
+ } else {
+ if (addr == 0) {
+ if (s->read_reg_select) {
+ ret = s->isr;
+ } else {
+ ret = s->irr;
+ }
+ } else {
+ ret = s->imr;
+ }
+ }
+ DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
+ return ret;
+}
+
+int pic_get_output(DeviceState *d)
+{
+ PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
+
+ return (pic_get_irq(s) >= 0);
+}
+
+static void elcr_ioport_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PICCommonState *s = opaque;
+ s->elcr = val & s->elcr_mask;
+}
+
+static uint64_t elcr_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PICCommonState *s = opaque;
+ return s->elcr;
+}
+
+static const MemoryRegionOps pic_base_ioport_ops = {
+ .read = pic_ioport_read,
+ .write = pic_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps pic_elcr_ioport_ops = {
+ .read = elcr_ioport_read,
+ .write = elcr_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static void pic_init(PICCommonState *s)
+{
+ memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
+ memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
+
+ qdev_init_gpio_out(&s->dev.qdev, s->int_out, ARRAY_SIZE(s->int_out));
+ qdev_init_gpio_in(&s->dev.qdev, pic_set_irq, 8);
+}
+
+void pic_info(Monitor *mon, const QDict *qdict)
+{
+ int i;
+ PICCommonState *s;
+
+ if (!isa_pic) {
+ return;
+ }
+ for (i = 0; i < 2; i++) {
+ s = i == 0 ? DO_UPCAST(PICCommonState, dev.qdev, isa_pic) : slave_pic;
+ monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
+ "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
+ i, s->irr, s->imr, s->isr, s->priority_add,
+ s->irq_base, s->read_reg_select, s->elcr,
+ s->special_fully_nested_mode);
+ }
+}
+
+void irq_info(Monitor *mon, const QDict *qdict)
+{
+#ifndef DEBUG_IRQ_COUNT
+ monitor_printf(mon, "irq statistic code not compiled.\n");
+#else
+ int i;
+ int64_t count;
+
+ monitor_printf(mon, "IRQ statistics:\n");
+ for (i = 0; i < 16; i++) {
+ count = irq_count[i];
+ if (count > 0) {
+ monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
+ }
+ }
+#endif
+}
+
+qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
+{
+ qemu_irq *irq_set;
+ ISADevice *dev;
+ int i;
+
+ irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
+
+ dev = i8259_init_chip("isa-i8259", bus, true);
+
+ qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
+ for (i = 0 ; i < 8; i++) {
+ irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
+ }
+
+ isa_pic = &dev->qdev;
+
+ dev = i8259_init_chip("isa-i8259", bus, false);
+
+ qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
+ for (i = 0 ; i < 8; i++) {
+ irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
+ }
+
+ slave_pic = DO_UPCAST(PICCommonState, dev, dev);
+
+ return irq_set;
+}
+
+static void i8259_class_init(ObjectClass *klass, void *data)
+{
+ PICCommonClass *k = PIC_COMMON_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->init = pic_init;
+ dc->reset = pic_reset;
+}
+
+static const TypeInfo i8259_info = {
+ .name = "isa-i8259",
+ .instance_size = sizeof(PICCommonState),
+ .parent = TYPE_PIC_COMMON,
+ .class_init = i8259_class_init,
+};
+
+static void pic_register_types(void)
+{
+ type_register_static(&i8259_info);
+}
+
+type_init(pic_register_types)
diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c
new file mode 100644
index 0000000000..996ba9dfdb
--- /dev/null
+++ b/hw/intc/i8259_common.c
@@ -0,0 +1,161 @@
+/*
+ * QEMU 8259 - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2011 Jan Kiszka, Siemens AG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/i386/pc.h"
+#include "hw/isa/i8259_internal.h"
+
+void pic_reset_common(PICCommonState *s)
+{
+ s->last_irr = 0;
+ s->irr &= s->elcr;
+ s->imr = 0;
+ s->isr = 0;
+ s->priority_add = 0;
+ s->irq_base = 0;
+ s->read_reg_select = 0;
+ s->poll = 0;
+ s->special_mask = 0;
+ s->init_state = 0;
+ s->auto_eoi = 0;
+ s->rotate_on_auto_eoi = 0;
+ s->special_fully_nested_mode = 0;
+ s->init4 = 0;
+ s->single_mode = 0;
+ /* Note: ELCR is not reset */
+}
+
+static void pic_dispatch_pre_save(void *opaque)
+{
+ PICCommonState *s = opaque;
+ PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
+
+ if (info->pre_save) {
+ info->pre_save(s);
+ }
+}
+
+static int pic_dispatch_post_load(void *opaque, int version_id)
+{
+ PICCommonState *s = opaque;
+ PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
+
+ if (info->post_load) {
+ info->post_load(s);
+ }
+ return 0;
+}
+
+static int pic_init_common(ISADevice *dev)
+{
+ PICCommonState *s = DO_UPCAST(PICCommonState, dev, dev);
+ PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
+
+ info->init(s);
+
+ isa_register_ioport(NULL, &s->base_io, s->iobase);
+ if (s->elcr_addr != -1) {
+ isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr);
+ }
+
+ qdev_set_legacy_instance_id(&s->dev.qdev, s->iobase, 1);
+
+ return 0;
+}
+
+ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master)
+{
+ ISADevice *dev;
+
+ dev = isa_create(bus, name);
+ qdev_prop_set_uint32(&dev->qdev, "iobase", master ? 0x20 : 0xa0);
+ qdev_prop_set_uint32(&dev->qdev, "elcr_addr", master ? 0x4d0 : 0x4d1);
+ qdev_prop_set_uint8(&dev->qdev, "elcr_mask", master ? 0xf8 : 0xde);
+ qdev_prop_set_bit(&dev->qdev, "master", master);
+ qdev_init_nofail(&dev->qdev);
+
+ return dev;
+}
+
+static const VMStateDescription vmstate_pic_common = {
+ .name = "i8259",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = pic_dispatch_pre_save,
+ .post_load = pic_dispatch_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(last_irr, PICCommonState),
+ VMSTATE_UINT8(irr, PICCommonState),
+ VMSTATE_UINT8(imr, PICCommonState),
+ VMSTATE_UINT8(isr, PICCommonState),
+ VMSTATE_UINT8(priority_add, PICCommonState),
+ VMSTATE_UINT8(irq_base, PICCommonState),
+ VMSTATE_UINT8(read_reg_select, PICCommonState),
+ VMSTATE_UINT8(poll, PICCommonState),
+ VMSTATE_UINT8(special_mask, PICCommonState),
+ VMSTATE_UINT8(init_state, PICCommonState),
+ VMSTATE_UINT8(auto_eoi, PICCommonState),
+ VMSTATE_UINT8(rotate_on_auto_eoi, PICCommonState),
+ VMSTATE_UINT8(special_fully_nested_mode, PICCommonState),
+ VMSTATE_UINT8(init4, PICCommonState),
+ VMSTATE_UINT8(single_mode, PICCommonState),
+ VMSTATE_UINT8(elcr, PICCommonState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property pic_properties_common[] = {
+ DEFINE_PROP_HEX32("iobase", PICCommonState, iobase, -1),
+ DEFINE_PROP_HEX32("elcr_addr", PICCommonState, elcr_addr, -1),
+ DEFINE_PROP_HEX8("elcr_mask", PICCommonState, elcr_mask, -1),
+ DEFINE_PROP_BIT("master", PICCommonState, master, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pic_common_class_init(ObjectClass *klass, void *data)
+{
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_pic_common;
+ dc->no_user = 1;
+ dc->props = pic_properties_common;
+ ic->init = pic_init_common;
+}
+
+static const TypeInfo pic_common_type = {
+ .name = TYPE_PIC_COMMON,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(PICCommonState),
+ .class_size = sizeof(PICCommonClass),
+ .class_init = pic_common_class_init,
+ .abstract = true,
+};
+
+static void register_types(void)
+{
+ type_register_static(&pic_common_type);
+}
+
+type_init(register_types);
diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c
new file mode 100644
index 0000000000..9610673d94
--- /dev/null
+++ b/hw/intc/pl190.c
@@ -0,0 +1,289 @@
+/*
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+
+/* The number of virtual priority levels. 16 user vectors plus the
+ unvectored IRQ. Chained interrupts would require an additional level
+ if implemented. */
+
+#define PL190_NUM_PRIO 17
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t level;
+ uint32_t soft_level;
+ uint32_t irq_enable;
+ uint32_t fiq_select;
+ uint8_t vect_control[16];
+ uint32_t vect_addr[PL190_NUM_PRIO];
+ /* Mask containing interrupts with higher priority than this one. */
+ uint32_t prio_mask[PL190_NUM_PRIO + 1];
+ int protected;
+ /* Current priority level. */
+ int priority;
+ int prev_prio[PL190_NUM_PRIO];
+ qemu_irq irq;
+ qemu_irq fiq;
+} pl190_state;
+
+static const unsigned char pl190_id[] =
+{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
+
+static inline uint32_t pl190_irq_level(pl190_state *s)
+{
+ return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
+}
+
+/* Update interrupts. */
+static void pl190_update(pl190_state *s)
+{
+ uint32_t level = pl190_irq_level(s);
+ int set;
+
+ set = (level & s->prio_mask[s->priority]) != 0;
+ qemu_set_irq(s->irq, set);
+ set = ((s->level | s->soft_level) & s->fiq_select) != 0;
+ qemu_set_irq(s->fiq, set);
+}
+
+static void pl190_set_irq(void *opaque, int irq, int level)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ pl190_update(s);
+}
+
+static void pl190_update_vectors(pl190_state *s)
+{
+ uint32_t mask;
+ int i;
+ int n;
+
+ mask = 0;
+ for (i = 0; i < 16; i++)
+ {
+ s->prio_mask[i] = mask;
+ if (s->vect_control[i] & 0x20)
+ {
+ n = s->vect_control[i] & 0x1f;
+ mask |= 1 << n;
+ }
+ }
+ s->prio_mask[16] = mask;
+ pl190_update(s);
+}
+
+static uint64_t pl190_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ pl190_state *s = (pl190_state *)opaque;
+ int i;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl190_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x140) {
+ return s->vect_addr[(offset - 0x100) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ return s->vect_control[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* IRQSTATUS */
+ return pl190_irq_level(s);
+ case 1: /* FIQSATUS */
+ return (s->level | s->soft_level) & s->fiq_select;
+ case 2: /* RAWINTR */
+ return s->level | s->soft_level;
+ case 3: /* INTSELECT */
+ return s->fiq_select;
+ case 4: /* INTENABLE */
+ return s->irq_enable;
+ case 6: /* SOFTINT */
+ return s->soft_level;
+ case 8: /* PROTECTION */
+ return s->protected;
+ case 12: /* VECTADDR */
+ /* Read vector address at the start of an ISR. Increases the
+ * current priority level to that of the current interrupt.
+ *
+ * Since an enabled interrupt X at priority P causes prio_mask[Y]
+ * to have bit X set for all Y > P, this loop will stop with
+ * i == the priority of the highest priority set interrupt.
+ */
+ for (i = 0; i < s->priority; i++) {
+ if ((s->level | s->soft_level) & s->prio_mask[i + 1]) {
+ break;
+ }
+ }
+
+ /* Reading this value with no pending interrupts is undefined.
+ We return the default address. */
+ if (i == PL190_NUM_PRIO)
+ return s->vect_addr[16];
+ if (i < s->priority)
+ {
+ s->prev_prio[i] = s->priority;
+ s->priority = i;
+ pl190_update(s);
+ }
+ return s->vect_addr[s->priority];
+ case 13: /* DEFVECTADDR */
+ return s->vect_addr[16];
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl190_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl190_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ if (offset >= 0x100 && offset < 0x140) {
+ s->vect_addr[(offset - 0x100) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ s->vect_control[(offset - 0x200) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* SELECT */
+ /* This is a readonly register, but linux tries to write to it
+ anyway. Ignore the write. */
+ break;
+ case 3: /* INTSELECT */
+ s->fiq_select = val;
+ break;
+ case 4: /* INTENABLE */
+ s->irq_enable |= val;
+ break;
+ case 5: /* INTENCLEAR */
+ s->irq_enable &= ~val;
+ break;
+ case 6: /* SOFTINT */
+ s->soft_level |= val;
+ break;
+ case 7: /* SOFTINTCLEAR */
+ s->soft_level &= ~val;
+ break;
+ case 8: /* PROTECTION */
+ /* TODO: Protection (supervisor only access) is not implemented. */
+ s->protected = val & 1;
+ break;
+ case 12: /* VECTADDR */
+ /* Restore the previous priority level. The value written is
+ ignored. */
+ if (s->priority < PL190_NUM_PRIO)
+ s->priority = s->prev_prio[s->priority];
+ break;
+ case 13: /* DEFVECTADDR */
+ s->vect_addr[16] = val;
+ break;
+ case 0xc0: /* ITCR */
+ if (val) {
+ qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n");
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl190_write: Bad offset %x\n", (int)offset);
+ return;
+ }
+ pl190_update(s);
+}
+
+static const MemoryRegionOps pl190_ops = {
+ .read = pl190_read,
+ .write = pl190_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pl190_reset(DeviceState *d)
+{
+ pl190_state *s = DO_UPCAST(pl190_state, busdev.qdev, d);
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ s->vect_addr[i] = 0;
+ s->vect_control[i] = 0;
+ }
+ s->vect_addr[16] = 0;
+ s->prio_mask[17] = 0xffffffff;
+ s->priority = PL190_NUM_PRIO;
+ pl190_update_vectors(s);
+}
+
+static int pl190_init(SysBusDevice *dev)
+{
+ pl190_state *s = FROM_SYSBUS(pl190_state, dev);
+
+ memory_region_init_io(&s->iomem, &pl190_ops, s, "pl190", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+ return 0;
+}
+
+static const VMStateDescription vmstate_pl190 = {
+ .name = "pl190",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, pl190_state),
+ VMSTATE_UINT32(soft_level, pl190_state),
+ VMSTATE_UINT32(irq_enable, pl190_state),
+ VMSTATE_UINT32(fiq_select, pl190_state),
+ VMSTATE_UINT8_ARRAY(vect_control, pl190_state, 16),
+ VMSTATE_UINT32_ARRAY(vect_addr, pl190_state, PL190_NUM_PRIO),
+ VMSTATE_UINT32_ARRAY(prio_mask, pl190_state, PL190_NUM_PRIO+1),
+ VMSTATE_INT32(protected, pl190_state),
+ VMSTATE_INT32(priority, pl190_state),
+ VMSTATE_INT32_ARRAY(prev_prio, pl190_state, PL190_NUM_PRIO),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pl190_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pl190_init;
+ dc->no_user = 1;
+ dc->reset = pl190_reset;
+ dc->vmsd = &vmstate_pl190;
+}
+
+static const TypeInfo pl190_info = {
+ .name = "pl190",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(pl190_state),
+ .class_init = pl190_class_init,
+};
+
+static void pl190_register_types(void)
+{
+ type_register_static(&pl190_info);
+}
+
+type_init(pl190_register_types)
diff --git a/hw/intc/puv3_intc.c b/hw/intc/puv3_intc.c
new file mode 100644
index 0000000000..0cd5e9eae0
--- /dev/null
+++ b/hw/intc/puv3_intc.c
@@ -0,0 +1,135 @@
+/*
+ * INTC device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/sysbus.h"
+
+#undef DEBUG_PUV3
+#include "hw/unicore32/puv3.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq parent_irq;
+
+ uint32_t reg_ICMR;
+ uint32_t reg_ICPR;
+} PUV3INTCState;
+
+/* Update interrupt status after enabled or pending bits have been changed. */
+static void puv3_intc_update(PUV3INTCState *s)
+{
+ if (s->reg_ICMR & s->reg_ICPR) {
+ qemu_irq_raise(s->parent_irq);
+ } else {
+ qemu_irq_lower(s->parent_irq);
+ }
+}
+
+/* Process a change in an external INTC input. */
+static void puv3_intc_handler(void *opaque, int irq, int level)
+{
+ PUV3INTCState *s = opaque;
+
+ DPRINTF("irq 0x%x, level 0x%x\n", irq, level);
+ if (level) {
+ s->reg_ICPR |= (1 << irq);
+ } else {
+ s->reg_ICPR &= ~(1 << irq);
+ }
+ puv3_intc_update(s);
+}
+
+static uint64_t puv3_intc_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ PUV3INTCState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (offset) {
+ case 0x04: /* INTC_ICMR */
+ ret = s->reg_ICMR;
+ break;
+ case 0x0c: /* INTC_ICIP */
+ ret = s->reg_ICPR; /* the same value with ICPR */
+ break;
+ default:
+ DPRINTF("Bad offset %x\n", (int)offset);
+ }
+ DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+ return ret;
+}
+
+static void puv3_intc_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ PUV3INTCState *s = opaque;
+
+ DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+ switch (offset) {
+ case 0x00: /* INTC_ICLR */
+ case 0x14: /* INTC_ICCR */
+ break;
+ case 0x04: /* INTC_ICMR */
+ s->reg_ICMR = value;
+ break;
+ default:
+ DPRINTF("Bad offset 0x%x\n", (int)offset);
+ return;
+ }
+ puv3_intc_update(s);
+}
+
+static const MemoryRegionOps puv3_intc_ops = {
+ .read = puv3_intc_read,
+ .write = puv3_intc_write,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_intc_init(SysBusDevice *dev)
+{
+ PUV3INTCState *s = FROM_SYSBUS(PUV3INTCState, dev);
+
+ qdev_init_gpio_in(&s->busdev.qdev, puv3_intc_handler, PUV3_IRQS_NR);
+ sysbus_init_irq(&s->busdev, &s->parent_irq);
+
+ s->reg_ICMR = 0;
+ s->reg_ICPR = 0;
+
+ memory_region_init_io(&s->iomem, &puv3_intc_ops, s, "puv3_intc",
+ PUV3_REGS_OFFSET);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void puv3_intc_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = puv3_intc_init;
+}
+
+static const TypeInfo puv3_intc_info = {
+ .name = "puv3_intc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PUV3INTCState),
+ .class_init = puv3_intc_class_init,
+};
+
+static void puv3_intc_register_type(void)
+{
+ type_register_static(&puv3_intc_info);
+}
+
+type_init(puv3_intc_register_type)
diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c
new file mode 100644
index 0000000000..b106e724ab
--- /dev/null
+++ b/hw/intc/xilinx_intc.c
@@ -0,0 +1,190 @@
+/*
+ * QEMU Xilinx OPB Interrupt Controller.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+
+#define D(x)
+
+#define R_ISR 0
+#define R_IPR 1
+#define R_IER 2
+#define R_IAR 3
+#define R_SIE 4
+#define R_CIE 5
+#define R_IVR 6
+#define R_MER 7
+#define R_MAX 8
+
+struct xlx_pic
+{
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq parent_irq;
+
+ /* Configuration reg chosen at synthesis-time. QEMU populates
+ the bits at board-setup. */
+ uint32_t c_kind_of_intr;
+
+ /* Runtime control registers. */
+ uint32_t regs[R_MAX];
+};
+
+static void update_irq(struct xlx_pic *p)
+{
+ uint32_t i;
+ /* Update the pending register. */
+ p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER];
+
+ /* Update the vector register. */
+ for (i = 0; i < 32; i++) {
+ if (p->regs[R_IPR] & (1 << i))
+ break;
+ }
+ if (i == 32)
+ i = ~0;
+
+ p->regs[R_IVR] = i;
+ if ((p->regs[R_MER] & 1) && p->regs[R_IPR]) {
+ qemu_irq_raise(p->parent_irq);
+ } else {
+ qemu_irq_lower(p->parent_irq);
+ }
+}
+
+static uint64_t
+pic_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ struct xlx_pic *p = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ default:
+ if (addr < ARRAY_SIZE(p->regs))
+ r = p->regs[addr];
+ break;
+
+ }
+ D(printf("%s %x=%x\n", __func__, addr * 4, r));
+ return r;
+}
+
+static void
+pic_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ struct xlx_pic *p = opaque;
+ uint32_t value = val64;
+
+ addr >>= 2;
+ D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
+ switch (addr)
+ {
+ case R_IAR:
+ p->regs[R_ISR] &= ~value; /* ACK. */
+ break;
+ case R_SIE:
+ p->regs[R_IER] |= value; /* Atomic set ie. */
+ break;
+ case R_CIE:
+ p->regs[R_IER] &= ~value; /* Atomic clear ie. */
+ break;
+ default:
+ if (addr < ARRAY_SIZE(p->regs))
+ p->regs[addr] = value;
+ break;
+ }
+ update_irq(p);
+}
+
+static const MemoryRegionOps pic_ops = {
+ .read = pic_read,
+ .write = pic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ struct xlx_pic *p = opaque;
+
+ if (!(p->regs[R_MER] & 2)) {
+ qemu_irq_lower(p->parent_irq);
+ return;
+ }
+
+ /* Update source flops. Don't clear unless level triggered.
+ Edge triggered interrupts only go away when explicitely acked to
+ the interrupt controller. */
+ if (!(p->c_kind_of_intr & (1 << irq)) || level) {
+ p->regs[R_ISR] &= ~(1 << irq);
+ p->regs[R_ISR] |= (level << irq);
+ }
+ update_irq(p);
+}
+
+static int xilinx_intc_init(SysBusDevice *dev)
+{
+ struct xlx_pic *p = FROM_SYSBUS(typeof (*p), dev);
+
+ qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+ sysbus_init_irq(dev, &p->parent_irq);
+
+ memory_region_init_io(&p->mmio, &pic_ops, p, "xlnx.xps-intc", R_MAX * 4);
+ sysbus_init_mmio(dev, &p->mmio);
+ return 0;
+}
+
+static Property xilinx_intc_properties[] = {
+ DEFINE_PROP_UINT32("kind-of-intr", struct xlx_pic, c_kind_of_intr, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_intc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = xilinx_intc_init;
+ dc->props = xilinx_intc_properties;
+}
+
+static const TypeInfo xilinx_intc_info = {
+ .name = "xlnx.xps-intc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct xlx_pic),
+ .class_init = xilinx_intc_class_init,
+};
+
+static void xilinx_intc_register_types(void)
+{
+ type_register_static(&xilinx_intc_info);
+}
+
+type_init(xilinx_intc_register_types)