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 17 enum { 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 25 struct 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 38 QFIELD_BEGIN(goldfish_int_fields) 39 QFIELD_INT32(level), 40 QFIELD_INT32(pending_count), 41 QFIELD_INT32(irq_enabled), 42 QFIELD_INT32(fiq_enabled), 43 QFIELD_END 44 45 static 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 52 static 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 62 static 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 73 static 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 95 static 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 117 static 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 150 static CPUReadMemoryFunc *goldfish_int_readfn[] = { 151 goldfish_int_read, 152 goldfish_int_read, 153 goldfish_int_read 154 }; 155 156 static CPUWriteMemoryFunc *goldfish_int_writefn[] = { 157 goldfish_int_write, 158 goldfish_int_write, 159 goldfish_int_write 160 }; 161 162 qemu_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, GFD_MAX_IRQ); 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