aboutsummaryrefslogtreecommitdiffstats
path: root/hw/goldfish_interrupt.c
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 (patch)
tree550ce922ea0e125ac6a9738210ce2939bf2fe901 /hw/goldfish_interrupt.c
parent413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (diff)
downloadexternal_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.zip
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.gz
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.bz2
Initial Contribution
Diffstat (limited to 'hw/goldfish_interrupt.c')
-rw-r--r--hw/goldfish_interrupt.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/hw/goldfish_interrupt.c b/hw/goldfish_interrupt.c
new file mode 100644
index 0000000..c89130d
--- /dev/null
+++ b/hw/goldfish_interrupt.c
@@ -0,0 +1,190 @@
+/* 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 "vl.h"
+#include "arm_pic.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
+};
+
+struct goldfish_int_state {
+ struct goldfish_device dev;
+ uint32_t level;
+ uint32_t pending_count;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+ qemu_irq parent_irq;
+ qemu_irq parent_fiq;
+};
+
+#define GOLDFISH_INT_SAVE_VERSION 1
+
+#define QFIELD_STRUCT struct goldfish_int_state
+QFIELD_BEGIN(goldfish_int_fields)
+ QFIELD_INT32(level),
+ QFIELD_INT32(pending_count),
+ QFIELD_INT32(irq_enabled),
+ QFIELD_INT32(fiq_enabled),
+QFIELD_END
+
+static void goldfish_int_save(QEMUFile* f, void* opaque)
+{
+ struct goldfish_int_state* s = opaque;
+
+ qemu_put_struct(f, goldfish_int_fields, s);
+}
+
+static int goldfish_int_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct goldfish_int_state* s = opaque;
+
+ if (version_id != GOLDFISH_INT_SAVE_VERSION)
+ return -1;
+
+ return qemu_get_struct(f, goldfish_int_fields, s);
+}
+
+static void goldfish_int_update(struct goldfish_int_state *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)
+{
+ struct goldfish_int_state *s = (struct goldfish_int_state *)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)
+{
+ struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+ offset -= s->dev.base;
+
+ 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)
+{
+ struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+ uint32_t mask = (1U << value);
+ offset -= s->dev.base;
+
+ 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
+};
+
+qemu_irq* goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq)
+{
+ int ret;
+ struct goldfish_int_state *s;
+ qemu_irq* qi;
+
+ s = qemu_mallocz(sizeof(*s));
+ qi = qemu_allocate_irqs(goldfish_int_set_irq, s, 32);
+ s->dev.name = "goldfish_interrupt_controller";
+ s->dev.id = -1;
+ s->dev.base = base;
+ s->dev.size = 0x1000;
+ s->parent_irq = parent_irq;
+ s->parent_fiq = parent_fiq;
+
+ ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s);
+ if(ret) {
+ qemu_free(s);
+ return NULL;
+ }
+
+ register_savevm( "goldfish_int", 0, GOLDFISH_INT_SAVE_VERSION,
+ goldfish_int_save, goldfish_int_load, s);
+
+ return qi;
+}
+