| /* |
| * Raspberry Pi emulation (c) 2012 Gregory Estrade |
| * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. |
| * Heavily based on pl190.c, copyright terms below: |
| * |
| * Arm PrimeCell PL190 Vector Interrupt Controller |
| * |
| * Copyright (c) 2006 CodeSourcery. |
| * Written by Paul Brook |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "hw/intc/bcm2835_ic.h" |
| #include "hw/irq.h" |
| #include "migration/vmstate.h" |
| #include "qemu/log.h" |
| #include "qemu/module.h" |
| #include "trace.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); |
| trace_bcm2835_ic_set_gpu_irq(irq, level); |
| 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); |
| trace_bcm2835_ic_set_cpu_irq(irq, level); |
| 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 = (const 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); |
| |
| device_class_set_legacy_reset(dc, bcm2835_ic_reset); |
| dc->vmsd = &vmstate_bcm2835_ic; |
| } |
| |
| static const 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) |