new file mode 100644
@@ -0,0 +1,354 @@
+/*
+ * Faraday FTINTC020 Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GPL v2.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+
+#define TYPE_FTINTC020 "ftintc020"
+
+typedef struct Ftintc020State {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ ARMCPU *cpu;
+ qemu_irq irqs[64];
+
+ uint32_t irq_pin[2]; /* IRQ pin state */
+ uint32_t fiq_pin[2]; /* IRQ pin state */
+
+ /* HW register caches */
+ uint32_t irq_src[2]; /* IRQ source register */
+ uint32_t irq_ena[2]; /* IRQ enable register */
+ uint32_t irq_mod[2]; /* IRQ mode register */
+ uint32_t irq_lvl[2]; /* IRQ level register */
+ uint32_t fiq_src[2]; /* FIQ source register */
+ uint32_t fiq_ena[2]; /* FIQ enable register */
+ uint32_t fiq_mod[2]; /* FIQ mode register */
+ uint32_t fiq_lvl[2]; /* FIQ level register */
+} ftintc020_state;
+
+#define FTINTC020(obj) \
+ OBJECT_CHECK(ftintc020_state, obj, TYPE_FTINTC020)
+
+qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
+
+static void ftintc020_update(ftintc020_state *s)
+{
+ uint32_t mask[2];
+
+ /* FIQ */
+ mask[0] = s->fiq_src[0] & s->fiq_ena[0];
+ mask[1] = s->fiq_src[1] & s->fiq_ena[1];
+
+ if (mask[0] || mask[1]) {
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
+ } else {
+ cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
+ }
+
+ /* IRQ */
+ mask[0] = s->irq_src[0] & s->irq_ena[0];
+ mask[1] = s->irq_src[1] & s->irq_ena[1];
+
+ if (mask[0] || mask[1]) {
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
+ }
+}
+
+/* Note: Here level means state of the signal on a pin */
+static void ftintc020_set_irq(void *opaque, int irq, int level)
+{
+ ftintc020_state *s = FTINTC020(opaque);
+ uint32_t i = irq / 32;
+ uint32_t mask = 1 << (irq % 32);
+
+ if (s->irq_mod[i] & mask) {
+ /* Edge Triggered */
+ if (s->irq_lvl[i] & mask) {
+ /* Falling Active */
+ if ((s->irq_pin[i] & mask) && !level) {
+ s->irq_src[i] |= mask;
+ s->fiq_src[i] |= mask;
+ }
+ } else {
+ /* Rising Active */
+ if (!(s->irq_pin[i] & mask) && level) {
+ s->irq_src[i] |= mask;
+ s->fiq_src[i] |= mask;
+ }
+ }
+ } else {
+ /* Level Triggered */
+ if (s->irq_lvl[i] & mask) {
+ /* Low Active */
+ if (level) {
+ s->irq_src[i] &= ~mask;
+ s->fiq_src[i] &= ~mask;
+ } else {
+ s->irq_src[i] |= mask;
+ s->fiq_src[i] |= mask;
+ }
+ } else {
+ /* High Active */
+ if (!level) {
+ s->irq_src[i] &= ~mask;
+ s->fiq_src[i] &= ~mask;
+ } else {
+ s->irq_src[i] |= mask;
+ s->fiq_src[i] |= mask;
+ }
+ }
+ }
+
+ /* update IRQ/FIQ pin states */
+ if (level) {
+ s->irq_pin[i] |= mask;
+ s->fiq_pin[i] |= mask;
+ } else {
+ s->irq_pin[i] &= ~mask;
+ s->fiq_pin[i] &= ~mask;
+ }
+
+ ftintc020_update(s);
+}
+
+static uint64_t ftintc020_mem_read(void *opaque, hwaddr offset, unsigned size)
+{
+ ftintc020_state *s = FTINTC020(opaque);
+
+ switch (offset) {
+ /*
+ * IRQ
+ */
+ case 0x0000: /* IRQ source register */
+ return s->irq_src[0];
+ case 0x0004: /* IRQ enable register */
+ return s->irq_ena[0];
+ case 0x000C: /* IRQ trigger-mode register */
+ return s->irq_mod[0];
+ case 0x0010: /* IRQ trigger-level register */
+ return s->irq_lvl[0];
+ case 0x0014: /* IRQ status register */
+ return s->irq_src[0] & s->irq_ena[0];
+ case 0x0060: /* IRQ source register */
+ return s->irq_src[1];
+ case 0x0064: /* IRQ enable register */
+ return s->irq_ena[1];
+ case 0x006C: /* IRQ trigger-mode register */
+ return s->irq_mod[1];
+ case 0x0070: /* IRQ trigger-level register */
+ return s->irq_lvl[1];
+ case 0x0074: /* IRQ status register */
+ return s->irq_src[1] & s->irq_ena[1];
+
+ /*
+ * FIQ
+ */
+ case 0x0020: /* FIQ source register */
+ return s->fiq_src[0];
+ case 0x0024: /* FIQ enable register */
+ return s->fiq_ena[0];
+ case 0x002C: /* FIQ trigger-mode register */
+ return s->fiq_mod[0];
+ case 0x0030: /* FIQ trigger-level register */
+ return s->fiq_lvl[0];
+ case 0x0034: /* FIQ status register */
+ return s->fiq_src[0] & s->fiq_ena[0];
+ case 0x0080: /* FIQ source register */
+ return s->fiq_src[1];
+ case 0x0084: /* FIQ enable register */
+ return s->fiq_ena[1];
+ case 0x008C: /* FIQ trigger-mode register */
+ return s->fiq_mod[1];
+ case 0x0090: /* FIQ trigger-level register */
+ return s->fiq_lvl[1];
+ case 0x0094: /* FIQ status register */
+ return s->fiq_src[1] & s->fiq_ena[1];
+ default:
+ return 0;
+ }
+}
+
+static void ftintc020_mem_write(void *opaque,
+ hwaddr offset,
+ uint64_t value,
+ unsigned size)
+{
+ ftintc020_state *s = FTINTC020(opaque);
+
+ switch (offset) {
+ /*
+ * IRQ
+ */
+ case 0x0004: /* IRQ enable register */
+ s->irq_ena[0] = (uint32_t)value;
+ break;
+ case 0x0008: /* IRQ interrupt clear register */
+ value = ~(value & s->irq_mod[0]);
+ s->irq_src[0] &= (uint32_t)value;
+ break;
+ case 0x000C:
+ s->irq_mod[0] = (uint32_t)value;
+ break;
+ case 0x0010:
+ s->irq_lvl[0] = (uint32_t)value;
+ break;
+ case 0x0064: /* IRQ enable register */
+ s->irq_ena[1] = (uint32_t)value;
+ break;
+ case 0x0068: /* IRQ interrupt clear register */
+ value = ~(value & s->irq_mod[1]);
+ s->irq_src[1] &= (uint32_t)value;
+ break;
+ case 0x006C:
+ s->irq_mod[1] = (uint32_t)value;
+ break;
+ case 0x0070:
+ s->irq_lvl[1] = (uint32_t)value;
+ break;
+
+ /*
+ * FIQ
+ */
+ case 0x0024: /* FIQ enable register */
+ s->fiq_ena[0] = (uint32_t)value;
+ break;
+ case 0x0028: /* FIQ interrupt clear register */
+ value = ~(value & s->fiq_mod[0]);
+ s->fiq_src[0] &= (uint32_t)value;
+ break;
+ case 0x002C:
+ s->fiq_mod[0] = (uint32_t)value;
+ break;
+ case 0x0030:
+ s->fiq_lvl[0] = (uint32_t)value;
+ break;
+ case 0x0084: /* FIQ enable register */
+ s->fiq_ena[1] = (uint32_t)value;
+ break;
+ case 0x0088: /* FIQ interrupt clear register */
+ value = ~(value & s->fiq_mod[1]);
+ s->fiq_src[1] &= (uint32_t)value;
+ break;
+ case 0x008C:
+ s->fiq_mod[1] = (uint32_t)value;
+ break;
+ case 0x0090:
+ s->fiq_lvl[1] = (uint32_t)value;
+ break;
+ default:
+ return;
+ }
+
+ ftintc020_update(s);
+}
+
+static const MemoryRegionOps ftintc020_mem_ops = {
+ .read = ftintc020_mem_read,
+ .write = ftintc020_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ftintc020_reset(DeviceState *ds)
+{
+ SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+ ftintc020_state *s = FTINTC020(FROM_SYSBUS(ftintc020_state, busdev));
+
+ s->irq_pin[0] = 0;
+ s->irq_pin[1] = 0;
+ s->fiq_pin[0] = 0;
+ s->fiq_pin[1] = 0;
+
+ s->irq_src[0] = 0;
+ s->irq_src[1] = 0;
+ s->irq_ena[0] = 0;
+ s->irq_ena[1] = 0;
+ s->fiq_src[0] = 0;
+ s->fiq_src[1] = 0;
+ s->fiq_ena[0] = 0;
+ s->fiq_ena[1] = 0;
+
+ ftintc020_update(s);
+}
+
+qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)
+{
+ int i;
+ DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020);
+ SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+ ftintc020_state *s = FTINTC020(FROM_SYSBUS(ftintc020_state, busdev));
+
+ s->cpu = cpu;
+ ftintc020_reset(ds);
+ qdev_init_nofail(ds);
+ qdev_init_gpio_in(ds, ftintc020_set_irq, 64);
+ for (i = 0; i < 64; ++i) {
+ s->irqs[i] = qdev_get_gpio_in(ds, i);
+ }
+
+ /* Enable IC memory-mapped registers access. */
+ memory_region_init_io(&s->iomem,
+ &ftintc020_mem_ops,
+ s,
+ TYPE_FTINTC020,
+ 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(ds), &s->iomem);
+ sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, base);
+
+ return s->irqs;
+}
+
+static VMStateDescription vmstate_ftintc020 = {
+ .name = TYPE_FTINTC020,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(irq_src, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(irq_ena, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(irq_mod, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(irq_lvl, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(fiq_src, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(fiq_ena, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(fiq_mod, ftintc020_state, 2),
+ VMSTATE_UINT32_ARRAY(fiq_lvl, ftintc020_state, 2),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static int ftintc020_initfn(SysBusDevice *dev)
+{
+ return 0;
+}
+
+static void ftintc020_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->init = ftintc020_initfn;
+ dc->vmsd = &vmstate_ftintc020;
+ dc->reset = ftintc020_reset;
+ dc->no_user = 1;
+}
+
+static const TypeInfo ftintc020_info = {
+ .name = TYPE_FTINTC020,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ftintc020_state),
+ .class_init = ftintc020_class_init,
+};
+
+static void ftintc020_register_types(void)
+{
+ type_register_static(&ftintc020_info);
+}
+
+type_init(ftintc020_register_types)