1/* Copyright (C) 2007-2008 The Android Open Source Project 2** 3** This software is licensed under the terms of the GNU General Public 4** License version 2, as published by the Free Software Foundation, and 5** may be copied, distributed, and modified under those terms. 6** 7** This program is distributed in the hope that it will be useful, 8** but WITHOUT ANY WARRANTY; without even the implied warranty of 9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10** GNU General Public License for more details. 11*/ 12#include "qemu_file.h" 13#include "arm_pic.h" 14#include "goldfish_device.h" 15#include "irq.h" 16 17enum { 18 INTERRUPT_STATUS = 0x00, // number of pending interrupts 19 INTERRUPT_NUMBER = 0x04, 20 INTERRUPT_DISABLE_ALL = 0x08, 21 INTERRUPT_DISABLE = 0x0c, 22 INTERRUPT_ENABLE = 0x10 23}; 24 25struct goldfish_int_state { 26 struct goldfish_device dev; 27 uint32_t level; 28 uint32_t pending_count; 29 uint32_t irq_enabled; 30 uint32_t fiq_enabled; 31 qemu_irq parent_irq; 32 qemu_irq parent_fiq; 33}; 34 35#define GOLDFISH_INT_SAVE_VERSION 1 36 37#define QFIELD_STRUCT struct goldfish_int_state 38QFIELD_BEGIN(goldfish_int_fields) 39 QFIELD_INT32(level), 40 QFIELD_INT32(pending_count), 41 QFIELD_INT32(irq_enabled), 42 QFIELD_INT32(fiq_enabled), 43QFIELD_END 44 45static void goldfish_int_save(QEMUFile* f, void* opaque) 46{ 47 struct goldfish_int_state* s = opaque; 48 49 qemu_put_struct(f, goldfish_int_fields, s); 50} 51 52static int goldfish_int_load(QEMUFile* f, void* opaque, int version_id) 53{ 54 struct goldfish_int_state* s = opaque; 55 56 if (version_id != GOLDFISH_INT_SAVE_VERSION) 57 return -1; 58 59 return qemu_get_struct(f, goldfish_int_fields, s); 60} 61 62static void goldfish_int_update(struct goldfish_int_state *s) 63{ 64 uint32_t flags; 65 66 flags = (s->level & s->irq_enabled); 67 qemu_set_irq(s->parent_irq, flags != 0); 68 69 flags = (s->level & s->fiq_enabled); 70 qemu_set_irq(s->parent_fiq, flags != 0); 71} 72 73static void goldfish_int_set_irq(void *opaque, int irq, int level) 74{ 75 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque; 76 uint32_t mask = (1U << irq); 77 78 if(level) { 79 if(!(s->level & mask)) { 80 if(s->irq_enabled & mask) 81 s->pending_count++; 82 s->level |= mask; 83 } 84 } 85 else { 86 if(s->level & mask) { 87 if(s->irq_enabled & mask) 88 s->pending_count--; 89 s->level &= ~mask; 90 } 91 } 92 goldfish_int_update(s); 93} 94 95static uint32_t goldfish_int_read(void *opaque, target_phys_addr_t offset) 96{ 97 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque; 98 99 switch (offset) { 100 case INTERRUPT_STATUS: /* IRQ_STATUS */ 101 return s->pending_count; 102 case INTERRUPT_NUMBER: { 103 int i; 104 uint32_t pending = s->level & s->irq_enabled; 105 for(i = 0; i < 32; i++) { 106 if(pending & (1U << i)) 107 return i; 108 } 109 return 0; 110 } 111 default: 112 cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset); 113 return 0; 114 } 115} 116 117static void goldfish_int_write(void *opaque, target_phys_addr_t offset, uint32_t value) 118{ 119 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque; 120 uint32_t mask = (1U << value); 121 122 switch (offset) { 123 case INTERRUPT_DISABLE_ALL: 124 s->pending_count = 0; 125 s->level = 0; 126 break; 127 128 case INTERRUPT_DISABLE: 129 if(s->irq_enabled & mask) { 130 if(s->level & mask) 131 s->pending_count--; 132 s->irq_enabled &= ~mask; 133 } 134 break; 135 case INTERRUPT_ENABLE: 136 if(!(s->irq_enabled & mask)) { 137 s->irq_enabled |= mask; 138 if(s->level & mask) 139 s->pending_count++; 140 } 141 break; 142 143 default: 144 cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset); 145 return; 146 } 147 goldfish_int_update(s); 148} 149 150static CPUReadMemoryFunc *goldfish_int_readfn[] = { 151 goldfish_int_read, 152 goldfish_int_read, 153 goldfish_int_read 154}; 155 156static CPUWriteMemoryFunc *goldfish_int_writefn[] = { 157 goldfish_int_write, 158 goldfish_int_write, 159 goldfish_int_write 160}; 161 162qemu_irq* goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq) 163{ 164 int ret; 165 struct goldfish_int_state *s; 166 qemu_irq* qi; 167 168 s = qemu_mallocz(sizeof(*s)); 169 qi = qemu_allocate_irqs(goldfish_int_set_irq, s, 32); 170 s->dev.name = "goldfish_interrupt_controller"; 171 s->dev.id = -1; 172 s->dev.base = base; 173 s->dev.size = 0x1000; 174 s->parent_irq = parent_irq; 175 s->parent_fiq = parent_fiq; 176 177 ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s); 178 if(ret) { 179 qemu_free(s); 180 return NULL; 181 } 182 183 register_savevm( "goldfish_int", 0, GOLDFISH_INT_SAVE_VERSION, 184 goldfish_int_save, goldfish_int_load, s); 185 186 return qi; 187} 188 189