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)
QFIELD_INT32(level)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
goldfish_int_load(QEMUFile * f,void * opaque,int version_id)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
goldfish_int_update(struct goldfish_int_state * s)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
goldfish_int_set_irq(void * opaque,int irq,int level)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
goldfish_int_read(void * opaque,hwaddr offset)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
goldfish_int_write(void * opaque,hwaddr offset,uint32_t value)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
goldfish_interrupt_init(uint32_t base,qemu_irq parent_irq,qemu_irq parent_fiq)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