summaryrefslogtreecommitdiffstats
path: root/hw/intc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/intc')
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/bcm2835_ic.c236
-rw-r--r--hw/intc/bcm2836_control.c303
3 files changed, 540 insertions, 0 deletions
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 004b0c25e4..6a13a39519 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -24,6 +24,7 @@ obj-$(CONFIG_GRLIB) += grlib_irqmp.o
obj-$(CONFIG_IOAPIC) += ioapic.o
obj-$(CONFIG_OMAP) += omap_intc.o
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
+obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c
new file mode 100644
index 0000000000..005a72b1e2
--- /dev/null
+++ b/hw/intc/bcm2835_ic.c
@@ -0,0 +1,236 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
+ * This code is licensed under the GNU GPLv2 and later.
+ * Heavily based on pl190.c, copyright terms below:
+ *
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/intc/bcm2835_ic.h"
+
+#define GPU_IRQS 64
+#define ARM_IRQS 8
+
+#define IRQ_PENDING_BASIC 0x00 /* IRQ basic pending */
+#define IRQ_PENDING_1 0x04 /* IRQ pending 1 */
+#define IRQ_PENDING_2 0x08 /* IRQ pending 2 */
+#define FIQ_CONTROL 0x0C /* FIQ register */
+#define IRQ_ENABLE_1 0x10 /* Interrupt enable register 1 */
+#define IRQ_ENABLE_2 0x14 /* Interrupt enable register 2 */
+#define IRQ_ENABLE_BASIC 0x18 /* Base interrupt enable register */
+#define IRQ_DISABLE_1 0x1C /* Interrupt disable register 1 */
+#define IRQ_DISABLE_2 0x20 /* Interrupt disable register 2 */
+#define IRQ_DISABLE_BASIC 0x24 /* Base interrupt disable register */
+
+/* Update interrupts. */
+static void bcm2835_ic_update(BCM2835ICState *s)
+{
+ bool set = false;
+
+ if (s->fiq_enable) {
+ if (s->fiq_select >= GPU_IRQS) {
+ /* ARM IRQ */
+ set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1);
+ } else {
+ set = extract64(s->gpu_irq_level, s->fiq_select, 1);
+ }
+ }
+ qemu_set_irq(s->fiq, set);
+
+ set = (s->gpu_irq_level & s->gpu_irq_enable)
+ || (s->arm_irq_level & s->arm_irq_enable);
+ qemu_set_irq(s->irq, set);
+
+}
+
+static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level)
+{
+ BCM2835ICState *s = opaque;
+
+ assert(irq >= 0 && irq < 64);
+ s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0);
+ bcm2835_ic_update(s);
+}
+
+static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level)
+{
+ BCM2835ICState *s = opaque;
+
+ assert(irq >= 0 && irq < 8);
+ s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0);
+ bcm2835_ic_update(s);
+}
+
+static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 };
+
+static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835ICState *s = opaque;
+ uint32_t res = 0;
+ uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable;
+ int i;
+
+ switch (offset) {
+ case IRQ_PENDING_BASIC:
+ /* bits 0-7: ARM irqs */
+ res = s->arm_irq_level & s->arm_irq_enable;
+
+ /* bits 8 & 9: pending registers 1 & 2 */
+ res |= (((uint32_t)gpu_pending) != 0) << 8;
+ res |= ((gpu_pending >> 32) != 0) << 9;
+
+ /* bits 10-20: selected GPU IRQs */
+ for (i = 0; i < ARRAY_SIZE(irq_dups); i++) {
+ res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10);
+ }
+ break;
+ case IRQ_PENDING_1:
+ res = gpu_pending;
+ break;
+ case IRQ_PENDING_2:
+ res = gpu_pending >> 32;
+ break;
+ case FIQ_CONTROL:
+ res = (s->fiq_enable << 7) | s->fiq_select;
+ break;
+ case IRQ_ENABLE_1:
+ res = s->gpu_irq_enable;
+ break;
+ case IRQ_ENABLE_2:
+ res = s->gpu_irq_enable >> 32;
+ break;
+ case IRQ_ENABLE_BASIC:
+ res = s->arm_irq_enable;
+ break;
+ case IRQ_DISABLE_1:
+ res = ~s->gpu_irq_enable;
+ break;
+ case IRQ_DISABLE_2:
+ res = ~s->gpu_irq_enable >> 32;
+ break;
+ case IRQ_DISABLE_BASIC:
+ res = ~s->arm_irq_enable;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ BCM2835ICState *s = opaque;
+
+ switch (offset) {
+ case FIQ_CONTROL:
+ s->fiq_select = extract32(val, 0, 7);
+ s->fiq_enable = extract32(val, 7, 1);
+ break;
+ case IRQ_ENABLE_1:
+ s->gpu_irq_enable |= val;
+ break;
+ case IRQ_ENABLE_2:
+ s->gpu_irq_enable |= val << 32;
+ break;
+ case IRQ_ENABLE_BASIC:
+ s->arm_irq_enable |= val & 0xff;
+ break;
+ case IRQ_DISABLE_1:
+ s->gpu_irq_enable &= ~val;
+ break;
+ case IRQ_DISABLE_2:
+ s->gpu_irq_enable &= ~(val << 32);
+ break;
+ case IRQ_DISABLE_BASIC:
+ s->arm_irq_enable &= ~val & 0xff;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+ bcm2835_ic_update(s);
+}
+
+static const MemoryRegionOps bcm2835_ic_ops = {
+ .read = bcm2835_ic_read,
+ .write = bcm2835_ic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void bcm2835_ic_reset(DeviceState *d)
+{
+ BCM2835ICState *s = BCM2835_IC(d);
+
+ s->gpu_irq_enable = 0;
+ s->arm_irq_enable = 0;
+ s->fiq_enable = false;
+ s->fiq_select = 0;
+}
+
+static void bcm2835_ic_init(Object *obj)
+{
+ BCM2835ICState *s = BCM2835_IC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC,
+ 0x200);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+
+ qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq,
+ BCM2835_IC_GPU_IRQ, GPU_IRQS);
+ qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq,
+ BCM2835_IC_ARM_IRQ, ARM_IRQS);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq);
+}
+
+static const VMStateDescription vmstate_bcm2835_ic = {
+ .name = TYPE_BCM2835_IC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(gpu_irq_level, BCM2835ICState),
+ VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState),
+ VMSTATE_UINT8(arm_irq_level, BCM2835ICState),
+ VMSTATE_UINT8(arm_irq_enable, BCM2835ICState),
+ VMSTATE_BOOL(fiq_enable, BCM2835ICState),
+ VMSTATE_UINT8(fiq_select, BCM2835ICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_ic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = bcm2835_ic_reset;
+ dc->vmsd = &vmstate_bcm2835_ic;
+}
+
+static TypeInfo bcm2835_ic_info = {
+ .name = TYPE_BCM2835_IC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835ICState),
+ .class_init = bcm2835_ic_class_init,
+ .instance_init = bcm2835_ic_init,
+};
+
+static void bcm2835_ic_register_types(void)
+{
+ type_register_static(&bcm2835_ic_info);
+}
+
+type_init(bcm2835_ic_register_types)
diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
new file mode 100644
index 0000000000..ad622aa99f
--- /dev/null
+++ b/hw/intc/bcm2836_control.c
@@ -0,0 +1,303 @@
+/*
+ * Rasperry Pi 2 emulation ARM control logic module.
+ * Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * At present, only implements interrupt routing, and mailboxes (i.e.,
+ * not local timer, PMU interrupt, or AXI counters).
+ *
+ * Ref:
+ * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
+ */
+
+#include "hw/intc/bcm2836_control.h"
+
+#define REG_GPU_ROUTE 0x0c
+#define REG_TIMERCONTROL 0x40
+#define REG_MBOXCONTROL 0x50
+#define REG_IRQSRC 0x60
+#define REG_FIQSRC 0x70
+#define REG_MBOX0_WR 0x80
+#define REG_MBOX0_RDCLR 0xc0
+#define REG_LIMIT 0x100
+
+#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
+#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
+
+#define IRQ_CNTPSIRQ 0
+#define IRQ_CNTPNSIRQ 1
+#define IRQ_CNTHPIRQ 2
+#define IRQ_CNTVIRQ 3
+#define IRQ_MAILBOX0 4
+#define IRQ_MAILBOX1 5
+#define IRQ_MAILBOX2 6
+#define IRQ_MAILBOX3 7
+#define IRQ_GPU 8
+#define IRQ_PMU 9
+#define IRQ_AXI 10
+#define IRQ_TIMER 11
+#define IRQ_MAX IRQ_TIMER
+
+static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
+ uint32_t controlreg, uint8_t controlidx)
+{
+ if (FIQ_BIT(controlreg, controlidx)) {
+ /* deliver a FIQ */
+ s->fiqsrc[core] |= (uint32_t)1 << irq;
+ } else if (IRQ_BIT(controlreg, controlidx)) {
+ /* deliver an IRQ */
+ s->irqsrc[core] |= (uint32_t)1 << irq;
+ } else {
+ /* the interrupt is masked */
+ }
+}
+
+/* Update interrupts. */
+static void bcm2836_control_update(BCM2836ControlState *s)
+{
+ int i, j;
+
+ /* reset pending IRQs/FIQs */
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ s->irqsrc[i] = s->fiqsrc[i] = 0;
+ }
+
+ /* apply routing logic, update status regs */
+ if (s->gpu_irq) {
+ assert(s->route_gpu_irq < BCM2836_NCORES);
+ s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
+ }
+
+ if (s->gpu_fiq) {
+ assert(s->route_gpu_fiq < BCM2836_NCORES);
+ s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
+ }
+
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ /* handle local timer interrupts for this core */
+ if (s->timerirqs[i]) {
+ assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
+ for (j = 0; j <= IRQ_CNTVIRQ; j++) {
+ if ((s->timerirqs[i] & (1 << j)) != 0) {
+ /* local interrupt j is set */
+ deliver_local(s, i, j, s->timercontrol[i], j);
+ }
+ }
+ }
+
+ /* handle mailboxes for this core */
+ for (j = 0; j < BCM2836_MBPERCORE; j++) {
+ if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
+ /* mailbox j is set */
+ deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
+ }
+ }
+ }
+
+ /* call set_irq appropriately for each output */
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
+ qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
+ }
+}
+
+static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
+ int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ assert(core >= 0 && core < BCM2836_NCORES);
+ assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
+
+ s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
+
+ bcm2836_control_update(s);
+}
+
+/* XXX: the following wrapper functions are a kludgy workaround,
+ * needed because I can't seem to pass useful information in the "irq"
+ * parameter when using named interrupts. Feel free to clean this up!
+ */
+
+static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 0, level);
+}
+
+static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 1, level);
+}
+
+static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 2, level);
+}
+
+static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 3, level);
+}
+
+static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ s->gpu_irq = level;
+
+ bcm2836_control_update(s);
+}
+
+static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ s->gpu_fiq = level;
+
+ bcm2836_control_update(s);
+}
+
+static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2836ControlState *s = opaque;
+
+ if (offset == REG_GPU_ROUTE) {
+ assert(s->route_gpu_fiq < BCM2836_NCORES
+ && s->route_gpu_irq < BCM2836_NCORES);
+ return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+ } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
+ return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
+ } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
+ return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
+ } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
+ return s->irqsrc[(offset - REG_IRQSRC) >> 2];
+ } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
+ return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
+ } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
+ return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void bcm2836_control_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ BCM2836ControlState *s = opaque;
+
+ if (offset == REG_GPU_ROUTE) {
+ s->route_gpu_irq = val & 0x3;
+ s->route_gpu_fiq = (val >> 2) & 0x3;
+ } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
+ s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
+ } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
+ s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
+ } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
+ s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
+ } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
+ s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+
+ bcm2836_control_update(s);
+}
+
+static const MemoryRegionOps bcm2836_control_ops = {
+ .read = bcm2836_control_read,
+ .write = bcm2836_control_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void bcm2836_control_reset(DeviceState *d)
+{
+ BCM2836ControlState *s = BCM2836_CONTROL(d);
+ int i;
+
+ s->route_gpu_irq = s->route_gpu_fiq = 0;
+
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ s->timercontrol[i] = 0;
+ s->mailboxcontrol[i] = 0;
+ }
+
+ for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
+ s->mailboxes[i] = 0;
+ }
+}
+
+static void bcm2836_control_init(Object *obj)
+{
+ BCM2836ControlState *s = BCM2836_CONTROL(obj);
+ DeviceState *dev = DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
+ TYPE_BCM2836_CONTROL, REG_LIMIT);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+
+ /* inputs from each CPU core */
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
+ BCM2836_NCORES);
+
+ /* IRQ and FIQ inputs from upstream bcm2835 controller */
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
+
+ /* outputs to CPU cores */
+ qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
+ qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
+}
+
+static const VMStateDescription vmstate_bcm2836_control = {
+ .name = TYPE_BCM2836_CONTROL,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
+ BCM2836_NCORES * BCM2836_MBPERCORE),
+ VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
+ VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
+ VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
+ VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
+ BCM2836_NCORES),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2836_control_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = bcm2836_control_reset;
+ dc->vmsd = &vmstate_bcm2836_control;
+}
+
+static TypeInfo bcm2836_control_info = {
+ .name = TYPE_BCM2836_CONTROL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2836ControlState),
+ .class_init = bcm2836_control_class_init,
+ .instance_init = bcm2836_control_init,
+};
+
+static void bcm2836_control_register_types(void)
+{
+ type_register_static(&bcm2836_control_info);
+}
+
+type_init(bcm2836_control_register_types)