@@ -37,3 +37,4 @@ obj-y += faraday_a360.o faraday_a360_pmu.o
obj-y += faraday_a369.o faraday_a369_scu.o faraday_a369_keypad.o
obj-y += ftahbc020.o
obj-y += ftddrii030.o
+obj-y += ftintc020.o
@@ -28,12 +28,17 @@ typedef FaradayMachState A360State;
static void
a360_device_init(A360State *s)
{
+ qemu_irq *pic;
+
+ /* Interrupt Controller */
+ pic = ftintc020_init(0x98800000, s->cpu);
+
/* Serial (FTUART010 which is 16550A compatible) */
if (serial_hds[0]) {
serial_mm_init(s->as,
0x98200000,
2,
- NULL,
+ pic[10],
18432000 / 16,
serial_hds[0],
DEVICE_LITTLE_ENDIAN);
@@ -42,7 +47,7 @@ a360_device_init(A360State *s)
serial_mm_init(s->as,
0x98300000,
2,
- NULL,
+ pic[11],
18432000 / 16,
serial_hds[1],
DEVICE_LITTLE_ENDIAN);
@@ -29,12 +29,17 @@ typedef FaradayMachState A369State;
static void
a369_device_init(A369State *s)
{
+ qemu_irq *pic;
+
+ /* Interrupt Controller */
+ pic = ftintc020_init(0x90100000, s->cpu);
+
/* Serial (FTUART010 which is 16550A compatible) */
if (serial_hds[0]) {
serial_mm_init(s->as,
0x92b00000,
2,
- NULL,
+ pic[53],
18432000 / 16,
serial_hds[0],
DEVICE_LITTLE_ENDIAN);
@@ -43,7 +48,7 @@ a369_device_init(A369State *s)
serial_mm_init(s->as,
0x92c00000,
2,
- NULL,
+ pic[54],
18432000 / 16,
serial_hds[1],
DEVICE_LITTLE_ENDIAN);
@@ -53,7 +58,7 @@ a369_device_init(A369State *s)
s->scu = sysbus_create_simple("a369.scu", 0x92000000, NULL);
/* ftkbc010 */
- sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
+ sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
/* ftahbc020 */
s->ahbc = qdev_create(NULL, "ftahbc020");
new file mode 100644
@@ -0,0 +1,356 @@
+/*
+ * Faraday FTINTC020 Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include <hw/hw.h>
+#include <hw/sysbus.h>
+
+#include "ftintc020.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 */
+} Ftintc020State;
+
+#define FTINTC020(obj) \
+ OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
+
+qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
+
+static void ftintc020_update(Ftintc020State *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)
+{
+ Ftintc020State *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)
+{
+ Ftintc020State *s = FTINTC020(opaque);
+
+ switch (offset) {
+ /*
+ * IRQ
+ */
+ case REG_IRQSRC:
+ return s->irq_src[0];
+ case REG_IRQENR:
+ return s->irq_ena[0];
+ case REG_IRQMDR:
+ return s->irq_mod[0];
+ case REG_IRQLVR:
+ return s->irq_lvl[0];
+ case REG_IRQSR:
+ return s->irq_src[0] & s->irq_ena[0];
+ case REG_EIRQSRC:
+ return s->irq_src[1];
+ case REG_EIRQENR:
+ return s->irq_ena[1];
+ case REG_EIRQMDR:
+ return s->irq_mod[1];
+ case REG_EIRQLVR:
+ return s->irq_lvl[1];
+ case REG_EIRQSR:
+ return s->irq_src[1] & s->irq_ena[1];
+
+ /*
+ * FIQ
+ */
+ case REG_FIQSRC:
+ return s->fiq_src[0];
+ case REG_FIQENR:
+ return s->fiq_ena[0];
+ case REG_FIQMDR:
+ return s->fiq_mod[0];
+ case REG_FIQLVR:
+ return s->fiq_lvl[0];
+ case REG_FIQSR:
+ return s->fiq_src[0] & s->fiq_ena[0];
+ case REG_EFIQSRC:
+ return s->fiq_src[1];
+ case REG_EFIQENR:
+ return s->fiq_ena[1];
+ case REG_EFIQMDR:
+ return s->fiq_mod[1];
+ case REG_EFIQLVR:
+ return s->fiq_lvl[1];
+ case REG_EFIQSR:
+ 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)
+{
+ Ftintc020State *s = FTINTC020(opaque);
+
+ switch (offset) {
+ /*
+ * IRQ
+ */
+ case REG_IRQENR:
+ s->irq_ena[0] = (uint32_t)value;
+ break;
+ case REG_IRQSCR:
+ value = ~(value & s->irq_mod[0]);
+ s->irq_src[0] &= (uint32_t)value;
+ break;
+ case REG_IRQMDR:
+ s->irq_mod[0] = (uint32_t)value;
+ break;
+ case REG_IRQLVR:
+ s->irq_lvl[0] = (uint32_t)value;
+ break;
+ case REG_EIRQENR:
+ s->irq_ena[1] = (uint32_t)value;
+ break;
+ case REG_EIRQSCR:
+ value = ~(value & s->irq_mod[1]);
+ s->irq_src[1] &= (uint32_t)value;
+ break;
+ case REG_EIRQMDR:
+ s->irq_mod[1] = (uint32_t)value;
+ break;
+ case REG_EIRQLVR:
+ s->irq_lvl[1] = (uint32_t)value;
+ break;
+
+ /*
+ * FIQ
+ */
+ case REG_FIQENR:
+ s->fiq_ena[0] = (uint32_t)value;
+ break;
+ case REG_FIQSCR:
+ value = ~(value & s->fiq_mod[0]);
+ s->fiq_src[0] &= (uint32_t)value;
+ break;
+ case REG_FIQMDR:
+ s->fiq_mod[0] = (uint32_t)value;
+ break;
+ case REG_FIQLVR:
+ s->fiq_lvl[0] = (uint32_t)value;
+ break;
+ case REG_EFIQENR:
+ s->fiq_ena[1] = (uint32_t)value;
+ break;
+ case REG_EFIQSCR:
+ value = ~(value & s->fiq_mod[1]);
+ s->fiq_src[1] &= (uint32_t)value;
+ break;
+ case REG_EFIQMDR:
+ s->fiq_mod[1] = (uint32_t)value;
+ break;
+ case REG_EFIQLVR:
+ 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);
+ Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, 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);
+ Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, 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, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(irq_ena, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(irq_mod, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(irq_lvl, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(fiq_src, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(fiq_ena, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(fiq_mod, Ftintc020State, 2),
+ VMSTATE_UINT32_ARRAY(fiq_lvl, Ftintc020State, 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(Ftintc020State),
+ .class_init = ftintc020_class_init,
+};
+
+static void ftintc020_register_types(void)
+{
+ type_register_static(&ftintc020_info);
+}
+
+type_init(ftintc020_register_types)
new file mode 100644
@@ -0,0 +1,48 @@
+/*
+ * Faraday FTINTC020 Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTINTC020_H
+#define HW_ARM_FTINTC020_H
+
+/*
+ * IRQ/FIO: 0 ~ 31
+ */
+#define REG_IRQSRC 0x00 /* IRQ source register */
+#define REG_IRQENR 0x04 /* IRQ enable register */
+#define REG_IRQSCR 0x08 /* IRQ status clear register */
+#define REG_IRQMDR 0x0C /* IRQ trigger mode register */
+#define REG_IRQLVR 0x10 /* IRQ trigger level register */
+#define REG_IRQSR 0x14 /* IRQ status register */
+
+#define REG_FIQSRC 0x20 /* FIQ source register */
+#define REG_FIQENR 0x24 /* FIQ enable register */
+#define REG_FIQSCR 0x28 /* FIQ status clear register */
+#define REG_FIQMDR 0x2C /* FIQ trigger mode register */
+#define REG_FIQLVR 0x30 /* FIQ trigger level register */
+#define REG_FIQSR 0x34 /* FIQ status register */
+
+/*
+ * Extended IRQ/FIO: 32 ~ 63
+ */
+
+#define REG_EIRQSRC 0x60 /* Extended IRQ source register */
+#define REG_EIRQENR 0x64 /* Extended IRQ enable register */
+#define REG_EIRQSCR 0x68 /* Extended IRQ status clear register */
+#define REG_EIRQMDR 0x6C /* Extended IRQ trigger mode register */
+#define REG_EIRQLVR 0x70 /* Extended IRQ trigger level register */
+#define REG_EIRQSR 0x74 /* Extended IRQ status register */
+
+#define REG_EFIQSRC 0x80 /* Extended FIQ source register */
+#define REG_EFIQENR 0x84 /* Extended FIQ enable register */
+#define REG_EFIQSCR 0x88 /* Extended FIQ status clear register */
+#define REG_EFIQMDR 0x8C /* Extended FIQ trigger mode register */
+#define REG_EFIQLVR 0x90 /* Extended FIQ trigger level register */
+#define REG_EFIQSR 0x94 /* Extended FIQ status register */
+
+#endif