Signed-off-by: Patrick Jackson <patricksjack...@gmail.com> --- Makefile.target | 2 +- hw/android_arm.c | 5 ++ hw/goldfish_device.h | 1 + hw/goldfish_interrupt.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 1 deletions(-) create mode 100644 hw/goldfish_interrupt.c
diff --git a/Makefile.target b/Makefile.target index eea7e8b..c251b99 100644 --- a/Makefile.target +++ b/Makefile.target @@ -360,7 +360,7 @@ obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o -obj-arm-y += android_arm.o goldfish_device.o +obj-arm-y += android_arm.o goldfish_device.o goldfish_interrupt.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/hw/android_arm.c b/hw/android_arm.c index a6ba12c..aaa0b26 100644 --- a/hw/android_arm.c +++ b/hw/android_arm.c @@ -14,6 +14,7 @@ #include "devices.h" #include "net.h" #include "sysemu.h" +#include "goldfish_device.h" #include "audio/audio.h" #include "arm-misc.h" #include "console.h" @@ -38,6 +39,7 @@ static void android_arm_init_(ram_addr_t ram_size, CPUState *env; qemu_irq *cpu_pic; ram_addr_t ram_offset; + DeviceState *gf_int; if (!cpu_model) cpu_model = "arm926"; @@ -48,6 +50,9 @@ static void android_arm_init_(ram_addr_t ram_size, cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); cpu_pic = arm_pic_init_cpu(env); + GoldfishBus *gbus = goldfish_bus_init(0xff001000, 1); + gf_int = goldfish_int_create(gbus, 0xff000000, cpu_pic[ARM_PIC_CPU_IRQ], cpu_pic[ARM_PIC_CPU_FIQ]); + goldfish_device_init(gf_int, 0xff010000, 10); info.ram_size = ram_size; info.kernel_filename = kernel_filename; diff --git a/hw/goldfish_device.h b/hw/goldfish_device.h index 3d13cc0..cee6abc 100644 --- a/hw/goldfish_device.h +++ b/hw/goldfish_device.h @@ -42,6 +42,7 @@ typedef struct GoldfishBus { /* QDEV device creation */ GoldfishBus *goldfish_bus_init(uint32_t base, uint32_t irq); +DeviceState *goldfish_int_create(GoldfishBus *gbus, uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq); /* Global functions provided by Goldfish devices */ void goldfish_bus_register_withprop(GoldfishDeviceInfo *info); diff --git a/hw/goldfish_interrupt.c b/hw/goldfish_interrupt.c new file mode 100644 index 0000000..e46e661 --- /dev/null +++ b/hw/goldfish_interrupt.c @@ -0,0 +1,182 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "hw.h" +#include "arm-misc.h" +#include "goldfish_device.h" +#include "irq.h" + +enum { + INTERRUPT_STATUS = 0x00, // number of pending interrupts + INTERRUPT_NUMBER = 0x04, + INTERRUPT_DISABLE_ALL = 0x08, + INTERRUPT_DISABLE = 0x0c, + INTERRUPT_ENABLE = 0x10 +}; + +typedef struct GoldfishInterruptDevice { + GoldfishDevice dev; + uint32_t level; + uint32_t pending_count; + uint32_t irq_enabled; + uint32_t fiq_enabled; + qemu_irq parent_irq; + qemu_irq parent_fiq; +} GoldfishInterruptDevice; + +static void goldfish_int_update(GoldfishInterruptDevice *s) +{ + uint32_t flags; + + flags = (s->level & s->irq_enabled); + qemu_set_irq(s->parent_irq, flags != 0); + + flags = (s->level & s->fiq_enabled); + qemu_set_irq(s->parent_fiq, flags != 0); +} + +static void goldfish_int_set_irq(void *opaque, int irq, int level) +{ + GoldfishInterruptDevice *s = (GoldfishInterruptDevice *)opaque; + uint32_t mask = (1U << irq); + + if(level) { + if(!(s->level & mask)) { + if(s->irq_enabled & mask) + s->pending_count++; + s->level |= mask; + } + } + else { + if(s->level & mask) { + if(s->irq_enabled & mask) + s->pending_count--; + s->level &= ~mask; + } + } + goldfish_int_update(s); +} + +static uint32_t goldfish_int_read(void *opaque, target_phys_addr_t offset) +{ + GoldfishInterruptDevice *s = (GoldfishInterruptDevice *)opaque; + + switch (offset) { + case INTERRUPT_STATUS: /* IRQ_STATUS */ + return s->pending_count; + case INTERRUPT_NUMBER: { + int i; + uint32_t pending = s->level & s->irq_enabled; + for(i = 0; i < 32; i++) { + if(pending & (1U << i)) + return i; + } + return 0; + } + default: + cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset); + return 0; + } +} + +static void goldfish_int_write(void *opaque, target_phys_addr_t offset, uint32_t value) +{ + GoldfishInterruptDevice *s = (GoldfishInterruptDevice *)opaque; + uint32_t mask = (1U << value); + + switch (offset) { + case INTERRUPT_DISABLE_ALL: + s->pending_count = 0; + s->level = 0; + break; + + case INTERRUPT_DISABLE: + if(s->irq_enabled & mask) { + if(s->level & mask) + s->pending_count--; + s->irq_enabled &= ~mask; + } + break; + case INTERRUPT_ENABLE: + if(!(s->irq_enabled & mask)) { + s->irq_enabled |= mask; + if(s->level & mask) + s->pending_count++; + } + break; + + default: + cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset); + return; + } + goldfish_int_update(s); +} + +static CPUReadMemoryFunc *goldfish_int_readfn[] = { + goldfish_int_read, + goldfish_int_read, + goldfish_int_read +}; + +static CPUWriteMemoryFunc *goldfish_int_writefn[] = { + goldfish_int_write, + goldfish_int_write, + goldfish_int_write +}; + +static int goldfish_int_init(GoldfishDevice *dev) +{ + GoldfishInterruptDevice *idev = (GoldfishInterruptDevice *)dev; + + qdev_init_gpio_in(&idev->dev.qdev, goldfish_int_set_irq, 32); + + return 0; +} + +DeviceState *goldfish_int_create(GoldfishBus *gbus, uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq) +{ + DeviceState *dev; + GoldfishDevice *gdev; + GoldfishInterruptDevice *idev; + char *name = (char *)"goldfish_int"; + + dev = qdev_create(&gbus->bus, name); + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint32(dev, "base", base); + qdev_init_nofail(dev); + gdev = (GoldfishDevice *)dev; + idev = DO_UPCAST(GoldfishInterruptDevice, dev, gdev); + idev->parent_irq = parent_irq; + idev->parent_fiq = parent_fiq; + + return dev; +} + +static GoldfishDeviceInfo goldfish_int_info = { + .init = goldfish_int_init, + .readfn = goldfish_int_readfn, + .writefn = goldfish_int_writefn, + .qdev.name = "goldfish_int", + .qdev.size = sizeof(GoldfishInterruptDevice), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("id", GoldfishDevice, id, -1), + DEFINE_PROP_UINT32("base", GoldfishDevice, base, 0), + DEFINE_PROP_UINT32("size", GoldfishDevice, size, 0x1000), + DEFINE_PROP_STRING("name", GoldfishDevice, name), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void goldfish_int_register(void) +{ + goldfish_bus_register_withprop(&goldfish_int_info); +} +device_init(goldfish_int_register); -- 1.7.4.1