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 "migration/qemu-file.h" 13 #include "hw/arm/pic.h" 14 #include "hw/android/goldfish/device.h" 15 #include "hw/irq.h" 16 #include "hw/hw.h" 17 18 enum { 19 INTERRUPT_STATUS = 0x00, // number of pending interrupts 20 INTERRUPT_NUMBER = 0x04, 21 INTERRUPT_DISABLE_ALL = 0x08, 22 INTERRUPT_DISABLE = 0x0c, 23 INTERRUPT_ENABLE = 0x10 24 }; 25 26 struct goldfish_int_state { 27 struct goldfish_device dev; 28 uint32_t level; 29 uint32_t pending_count; 30 uint32_t irq_enabled; 31 uint32_t fiq_enabled; 32 qemu_irq parent_irq; 33 qemu_irq parent_fiq; 34 }; 35 36 #define GOLDFISH_INT_SAVE_VERSION 1 37 38 #define QFIELD_STRUCT struct goldfish_int_state 39 QFIELD_BEGIN(goldfish_int_fields) 40 QFIELD_INT32(level), 41 QFIELD_INT32(pending_count), 42 QFIELD_INT32(irq_enabled), 43 QFIELD_INT32(fiq_enabled), 44 QFIELD_END 45 46 static void goldfish_int_save(QEMUFile* f, void* opaque) 47 { 48 struct goldfish_int_state* s = opaque; 49 50 qemu_put_struct(f, goldfish_int_fields, s); 51 } 52 53 static int goldfish_int_load(QEMUFile* f, void* opaque, int version_id) 54 { 55 struct goldfish_int_state* s = opaque; 56 57 if (version_id != GOLDFISH_INT_SAVE_VERSION) 58 return -1; 59 60 return qemu_get_struct(f, goldfish_int_fields, s); 61 } 62 63 static void goldfish_int_update(struct goldfish_int_state *s) 64 { 65 uint32_t flags; 66 67 flags = (s->level & s->irq_enabled); 68 qemu_set_irq(s->parent_irq, flags != 0); 69 70 flags = (s->level & s->fiq_enabled); 71 qemu_set_irq(s->parent_fiq, flags != 0); 72 } 73 74 static void goldfish_int_set_irq(void *opaque, int irq, int level) 75 { 76 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque; 77 uint32_t mask = (1U << irq); 78 79 if(level) { 80 if(!(s->level & mask)) { 81 if(s->irq_enabled & mask) 82 s->pending_count++; 83 s->level |= mask; 84 } 85 } 86 else { 87 if(s->level & mask) { 88 if(s->irq_enabled & mask) 89 s->pending_count--; 90 s->level &= ~mask; 91 } 92 } 93 goldfish_int_update(s); 94 } 95 96 static uint32_t goldfish_int_read(void *opaque, hwaddr offset) 97 { 98 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque; 99 100 switch (offset) { 101 case INTERRUPT_STATUS: /* IRQ_STATUS */ 102 return s->pending_count; 103 case INTERRUPT_NUMBER: { 104 int i; 105 uint32_t pending = s->level & s->irq_enabled; 106 for(i = 0; i < 32; i++) { 107 if(pending & (1U << i)) 108 return i; 109 } 110 return 0; 111 } 112 default: 113 cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset); 114 return 0; 115 } 116 } 117 118 static void goldfish_int_write(void *opaque, hwaddr offset, uint32_t value) 119 { 120 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque; 121 uint32_t mask = (1U << value); 122 123 switch (offset) { 124 case INTERRUPT_DISABLE_ALL: 125 s->pending_count = 0; 126 s->level = 0; 127 break; 128 129 case INTERRUPT_DISABLE: 130 if(s->irq_enabled & mask) { 131 if(s->level & mask) 132 s->pending_count--; 133 s->irq_enabled &= ~mask; 134 } 135 break; 136 case INTERRUPT_ENABLE: 137 if(!(s->irq_enabled & mask)) { 138 s->irq_enabled |= mask; 139 if(s->level & mask) 140 s->pending_count++; 141 } 142 break; 143 144 default: 145 cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset); 146 return; 147 } 148 goldfish_int_update(s); 149 } 150 151 static CPUReadMemoryFunc *goldfish_int_readfn[] = { 152 goldfish_int_read, 153 goldfish_int_read, 154 goldfish_int_read 155 }; 156 157 static CPUWriteMemoryFunc *goldfish_int_writefn[] = { 158 goldfish_int_write, 159 goldfish_int_write, 160 goldfish_int_write 161 }; 162 163 qemu_irq* goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq) 164 { 165 int ret; 166 struct goldfish_int_state *s; 167 qemu_irq* qi; 168 169 s = g_malloc0(sizeof(*s)); 170 qi = qemu_allocate_irqs(goldfish_int_set_irq, s, GFD_MAX_IRQ); 171 s->dev.name = "goldfish_interrupt_controller"; 172 s->dev.id = -1; 173 s->dev.base = base; 174 s->dev.size = 0x1000; 175 s->parent_irq = parent_irq; 176 s->parent_fiq = parent_fiq; 177 178 ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s); 179 if(ret) { 180 g_free(s); 181 return NULL; 182 } 183 184 register_savevm(NULL, 185 "goldfish_int", 186 0, 187 GOLDFISH_INT_SAVE_VERSION, 188 goldfish_int_save, 189 goldfish_int_load, 190 s); 191 192 return qi; 193 } 194 195