Revision 9ee6e8bb hw/arm_gic.c
b/hw/arm_gic.c | ||
---|---|---|
1 | 1 |
/* |
2 |
* ARM AMBA Generic/Distributed Interrupt Controller
|
|
2 |
* ARM Generic/Distributed Interrupt Controller |
|
3 | 3 |
* |
4 |
* Copyright (c) 2006 CodeSourcery. |
|
4 |
* Copyright (c) 2006-2007 CodeSourcery.
|
|
5 | 5 |
* Written by Paul Brook |
6 | 6 |
* |
7 | 7 |
* This code is licenced under the GPL. |
8 | 8 |
*/ |
9 | 9 |
|
10 |
/* TODO: Some variants of this controller can handle multiple CPUs. |
|
11 |
Currently only single CPU operation is implemented. */ |
|
12 |
|
|
13 |
#include "vl.h" |
|
14 |
#include "arm_pic.h" |
|
10 |
/* This file contains implementation code for the RealView EB interrupt |
|
11 |
controller, MPCore distributed interrupt controller and ARMv7-M |
|
12 |
Nested Vectored Interrupt Controller. */ |
|
15 | 13 |
|
16 | 14 |
//#define DEBUG_GIC |
17 | 15 |
|
... | ... | |
22 | 20 |
#define DPRINTF(fmt, args...) do {} while(0) |
23 | 21 |
#endif |
24 | 22 |
|
25 |
/* Distributed interrupt controller. */ |
|
26 |
|
|
23 |
#ifdef NVIC |
|
24 |
static const uint8_t gic_id[] = |
|
25 |
{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 }; |
|
26 |
#define GIC_DIST_OFFSET 0 |
|
27 |
/* The NVIC has 16 internal vectors. However these are not exposed |
|
28 |
through the normal GIC interface. */ |
|
29 |
#define GIC_BASE_IRQ 32 |
|
30 |
#else |
|
27 | 31 |
static const uint8_t gic_id[] = |
28 | 32 |
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; |
29 |
|
|
30 |
#define GIC_NIRQ 96 |
|
33 |
#define GIC_DIST_OFFSET 0x1000 |
|
34 |
#define GIC_BASE_IRQ 0 |
|
35 |
#endif |
|
31 | 36 |
|
32 | 37 |
typedef struct gic_irq_state |
33 | 38 |
{ |
39 |
/* ??? The documentation seems to imply the enable bits are global, even |
|
40 |
for per-cpu interrupts. This seems strange. */ |
|
34 | 41 |
unsigned enabled:1; |
35 |
unsigned pending:1;
|
|
36 |
unsigned active:1;
|
|
42 |
unsigned pending:NCPU;
|
|
43 |
unsigned active:NCPU;
|
|
37 | 44 |
unsigned level:1; |
38 |
unsigned model:1; /* 0 = 1:N, 1 = N:N */
|
|
45 |
unsigned model:1; /* 0 = N:N, 1 = 1:N */
|
|
39 | 46 |
unsigned trigger:1; /* nonzero = edge triggered. */ |
40 | 47 |
} gic_irq_state; |
41 | 48 |
|
49 |
#define ALL_CPU_MASK ((1 << NCPU) - 1) |
|
50 |
|
|
42 | 51 |
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1 |
43 | 52 |
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0 |
44 | 53 |
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled |
45 |
#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
|
|
46 |
#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
|
|
47 |
#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
|
|
48 |
#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
|
|
49 |
#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
|
|
50 |
#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
|
|
54 |
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
|
|
55 |
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
|
|
56 |
#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
|
|
57 |
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
|
|
58 |
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
|
|
59 |
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
|
|
51 | 60 |
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 |
52 | 61 |
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 |
53 | 62 |
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model |
54 |
#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
|
|
55 |
#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
|
|
56 |
#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
|
|
63 |
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
|
|
64 |
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
|
|
65 |
#define GIC_TEST_LEVEL(irq, cm) (s->irq_state[irq].level & (cm)) != 0
|
|
57 | 66 |
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 |
58 | 67 |
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 |
59 | 68 |
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger |
69 |
#define GIC_GET_PRIORITY(irq, cpu) \ |
|
70 |
(((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32]) |
|
71 |
#ifdef NVIC |
|
72 |
#define GIC_TARGET(irq) 1 |
|
73 |
#else |
|
74 |
#define GIC_TARGET(irq) s->irq_target[irq] |
|
75 |
#endif |
|
60 | 76 |
|
61 | 77 |
typedef struct gic_state |
62 | 78 |
{ |
63 | 79 |
uint32_t base; |
64 |
qemu_irq parent_irq; |
|
80 |
qemu_irq parent_irq[NCPU];
|
|
65 | 81 |
int enabled; |
66 |
int cpu_enabled; |
|
82 |
int cpu_enabled[NCPU];
|
|
67 | 83 |
|
68 | 84 |
gic_irq_state irq_state[GIC_NIRQ]; |
85 |
#ifndef NVIC |
|
69 | 86 |
int irq_target[GIC_NIRQ]; |
70 |
int priority[GIC_NIRQ]; |
|
71 |
int last_active[GIC_NIRQ]; |
|
72 |
|
|
73 |
int priority_mask; |
|
74 |
int running_irq; |
|
75 |
int running_priority; |
|
76 |
int current_pending; |
|
87 |
#endif |
|
88 |
int priority1[32][NCPU]; |
|
89 |
int priority2[GIC_NIRQ - 32]; |
|
90 |
int last_active[GIC_NIRQ][NCPU]; |
|
91 |
|
|
92 |
int priority_mask[NCPU]; |
|
93 |
int running_irq[NCPU]; |
|
94 |
int running_priority[NCPU]; |
|
95 |
int current_pending[NCPU]; |
|
96 |
|
|
97 |
qemu_irq *in; |
|
98 |
#ifdef NVIC |
|
99 |
void *nvic; |
|
100 |
#endif |
|
77 | 101 |
} gic_state; |
78 | 102 |
|
79 | 103 |
/* TODO: Many places that call this routine could be optimized. */ |
... | ... | |
83 | 107 |
int best_irq; |
84 | 108 |
int best_prio; |
85 | 109 |
int irq; |
86 |
|
|
87 |
s->current_pending = 1023; |
|
88 |
if (!s->enabled || !s->cpu_enabled) { |
|
89 |
qemu_irq_lower(s->parent_irq); |
|
90 |
return; |
|
91 |
} |
|
92 |
best_prio = 0x100; |
|
93 |
best_irq = 1023; |
|
94 |
for (irq = 0; irq < 96; irq++) { |
|
95 |
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) { |
|
96 |
if (s->priority[irq] < best_prio) { |
|
97 |
best_prio = s->priority[irq]; |
|
98 |
best_irq = irq; |
|
110 |
int level; |
|
111 |
int cpu; |
|
112 |
int cm; |
|
113 |
|
|
114 |
for (cpu = 0; cpu < NCPU; cpu++) { |
|
115 |
cm = 1 << cpu; |
|
116 |
s->current_pending[cpu] = 1023; |
|
117 |
if (!s->enabled || !s->cpu_enabled[cpu]) { |
|
118 |
qemu_irq_lower(s->parent_irq[cpu]); |
|
119 |
return; |
|
120 |
} |
|
121 |
best_prio = 0x100; |
|
122 |
best_irq = 1023; |
|
123 |
for (irq = 0; irq < GIC_NIRQ; irq++) { |
|
124 |
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) { |
|
125 |
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { |
|
126 |
best_prio = GIC_GET_PRIORITY(irq, cpu); |
|
127 |
best_irq = irq; |
|
128 |
} |
|
99 | 129 |
} |
100 | 130 |
} |
101 |
} |
|
102 |
if (best_prio > s->priority_mask) { |
|
103 |
qemu_irq_lower(s->parent_irq); |
|
104 |
} else { |
|
105 |
s->current_pending = best_irq; |
|
106 |
if (best_prio < s->running_priority) { |
|
107 |
DPRINTF("Raised pending IRQ %d\n", best_irq); |
|
108 |
qemu_irq_raise(s->parent_irq); |
|
131 |
level = 0; |
|
132 |
if (best_prio <= s->priority_mask[cpu]) { |
|
133 |
s->current_pending[cpu] = best_irq; |
|
134 |
if (best_prio < s->running_priority[cpu]) { |
|
135 |
DPRINTF("Raised pending IRQ %d\n", best_irq); |
|
136 |
level = 1; |
|
137 |
} |
|
109 | 138 |
} |
139 |
qemu_set_irq(s->parent_irq[cpu], level); |
|
110 | 140 |
} |
111 | 141 |
} |
112 | 142 |
|
143 |
static void __attribute__((unused)) |
|
144 |
gic_set_pending_private(gic_state *s, int cpu, int irq) |
|
145 |
{ |
|
146 |
int cm = 1 << cpu; |
|
147 |
|
|
148 |
if (GIC_TEST_PENDING(irq, cm)) |
|
149 |
return; |
|
150 |
|
|
151 |
DPRINTF("Set %d pending cpu %d\n", irq, cpu); |
|
152 |
GIC_SET_PENDING(irq, cm); |
|
153 |
gic_update(s); |
|
154 |
} |
|
155 |
|
|
156 |
/* Process a change in an external IRQ input. */ |
|
113 | 157 |
static void gic_set_irq(void *opaque, int irq, int level) |
114 | 158 |
{ |
115 | 159 |
gic_state *s = (gic_state *)opaque; |
116 | 160 |
/* The first external input line is internal interrupt 32. */ |
117 | 161 |
irq += 32; |
118 |
if (level == GIC_TEST_LEVEL(irq)) |
|
162 |
if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
|
|
119 | 163 |
return; |
120 | 164 |
|
121 | 165 |
if (level) { |
122 |
GIC_SET_LEVEL(irq); |
|
166 |
GIC_SET_LEVEL(irq, ALL_CPU_MASK);
|
|
123 | 167 |
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) { |
124 |
DPRINTF("Set %d pending\n", irq);
|
|
125 |
GIC_SET_PENDING(irq); |
|
168 |
DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
|
|
169 |
GIC_SET_PENDING(irq, GIC_TARGET(irq));
|
|
126 | 170 |
} |
127 | 171 |
} else { |
128 |
GIC_CLEAR_LEVEL(irq); |
|
172 |
GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
|
|
129 | 173 |
} |
130 | 174 |
gic_update(s); |
131 | 175 |
} |
132 | 176 |
|
133 |
static void gic_set_running_irq(gic_state *s, int irq) |
|
177 |
static void gic_set_running_irq(gic_state *s, int cpu, int irq)
|
|
134 | 178 |
{ |
135 |
s->running_irq = irq; |
|
136 |
if (irq == 1023) |
|
137 |
s->running_priority = 0x100; |
|
138 |
else |
|
139 |
s->running_priority = s->priority[irq]; |
|
179 |
s->running_irq[cpu] = irq; |
|
180 |
if (irq == 1023) { |
|
181 |
s->running_priority[cpu] = 0x100; |
|
182 |
} else { |
|
183 |
s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu); |
|
184 |
} |
|
140 | 185 |
gic_update(s); |
141 | 186 |
} |
142 | 187 |
|
143 |
static uint32_t gic_acknowledge_irq(gic_state *s) |
|
188 |
static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
|
|
144 | 189 |
{ |
145 | 190 |
int new_irq; |
146 |
new_irq = s->current_pending; |
|
147 |
if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) { |
|
191 |
int cm = 1 << cpu; |
|
192 |
new_irq = s->current_pending[cpu]; |
|
193 |
if (new_irq == 1023 |
|
194 |
|| GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) { |
|
148 | 195 |
DPRINTF("ACK no pending IRQ\n"); |
149 | 196 |
return 1023; |
150 | 197 |
} |
151 |
qemu_irq_lower(s->parent_irq); |
|
152 |
s->last_active[new_irq] = s->running_irq; |
|
153 |
/* For level triggered interrupts we clear the pending bit while |
|
154 |
the interrupt is active. */ |
|
155 |
GIC_CLEAR_PENDING(new_irq); |
|
156 |
gic_set_running_irq(s, new_irq); |
|
198 |
s->last_active[new_irq][cpu] = s->running_irq[cpu]; |
|
199 |
/* Clear pending flags for both level and edge triggered interrupts. |
|
200 |
Level triggered IRQs will be reasserted once they become inactive. */ |
|
201 |
GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm); |
|
202 |
gic_set_running_irq(s, cpu, new_irq); |
|
157 | 203 |
DPRINTF("ACK %d\n", new_irq); |
158 | 204 |
return new_irq; |
159 | 205 |
} |
160 | 206 |
|
161 |
static void gic_complete_irq(gic_state * s, int irq) |
|
207 |
static void gic_complete_irq(gic_state * s, int cpu, int irq)
|
|
162 | 208 |
{ |
163 | 209 |
int update = 0; |
210 |
int cm = 1 << cpu; |
|
164 | 211 |
DPRINTF("EOI %d\n", irq); |
165 |
if (s->running_irq == 1023) |
|
212 |
if (s->running_irq[cpu] == 1023)
|
|
166 | 213 |
return; /* No active IRQ. */ |
167 | 214 |
if (irq != 1023) { |
168 | 215 |
/* Mark level triggered interrupts as pending if they are still |
169 | 216 |
raised. */ |
170 | 217 |
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq) |
171 |
&& GIC_TEST_LEVEL(irq)) { |
|
172 |
GIC_SET_PENDING(irq); |
|
218 |
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { |
|
219 |
DPRINTF("Set %d pending mask %x\n", irq, cm); |
|
220 |
GIC_SET_PENDING(irq, cm); |
|
173 | 221 |
update = 1; |
174 | 222 |
} |
175 | 223 |
} |
176 |
if (irq != s->running_irq) { |
|
224 |
if (irq != s->running_irq[cpu]) {
|
|
177 | 225 |
/* Complete an IRQ that is not currently running. */ |
178 |
int tmp = s->running_irq; |
|
179 |
while (s->last_active[tmp] != 1023) { |
|
180 |
if (s->last_active[tmp] == irq) { |
|
181 |
s->last_active[tmp] = s->last_active[irq];
|
|
226 |
int tmp = s->running_irq[cpu];
|
|
227 |
while (s->last_active[tmp][cpu] != 1023) {
|
|
228 |
if (s->last_active[tmp][cpu] == irq) {
|
|
229 |
s->last_active[tmp][cpu] = s->last_active[irq][cpu];
|
|
182 | 230 |
break; |
183 | 231 |
} |
184 |
tmp = s->last_active[tmp]; |
|
232 |
tmp = s->last_active[tmp][cpu];
|
|
185 | 233 |
} |
186 | 234 |
if (update) { |
187 | 235 |
gic_update(s); |
188 | 236 |
} |
189 | 237 |
} else { |
190 | 238 |
/* Complete the current running IRQ. */ |
191 |
gic_set_running_irq(s, s->last_active[s->running_irq]);
|
|
239 |
gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
|
|
192 | 240 |
} |
193 | 241 |
} |
194 | 242 |
|
... | ... | |
198 | 246 |
uint32_t res; |
199 | 247 |
int irq; |
200 | 248 |
int i; |
249 |
int cpu; |
|
250 |
int cm; |
|
251 |
int mask; |
|
201 | 252 |
|
202 |
offset -= s->base + 0x1000; |
|
253 |
cpu = gic_get_current_cpu(); |
|
254 |
cm = 1 << cpu; |
|
255 |
offset -= s->base + GIC_DIST_OFFSET; |
|
203 | 256 |
if (offset < 0x100) { |
257 |
#ifndef NVIC |
|
204 | 258 |
if (offset == 0) |
205 | 259 |
return s->enabled; |
206 | 260 |
if (offset == 4) |
207 |
return (GIC_NIRQ / 32) - 1;
|
|
261 |
return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
|
|
208 | 262 |
if (offset < 0x08) |
209 | 263 |
return 0; |
264 |
#endif |
|
210 | 265 |
goto bad_reg; |
211 | 266 |
} else if (offset < 0x200) { |
212 | 267 |
/* Interrupt Set/Clear Enable. */ |
... | ... | |
214 | 269 |
irq = (offset - 0x100) * 8; |
215 | 270 |
else |
216 | 271 |
irq = (offset - 0x180) * 8; |
272 |
irq += GIC_BASE_IRQ; |
|
217 | 273 |
if (irq >= GIC_NIRQ) |
218 | 274 |
goto bad_reg; |
219 | 275 |
res = 0; |
... | ... | |
228 | 284 |
irq = (offset - 0x200) * 8; |
229 | 285 |
else |
230 | 286 |
irq = (offset - 0x280) * 8; |
287 |
irq += GIC_BASE_IRQ; |
|
231 | 288 |
if (irq >= GIC_NIRQ) |
232 | 289 |
goto bad_reg; |
233 | 290 |
res = 0; |
291 |
mask = (irq < 32) ? cm : ALL_CPU_MASK; |
|
234 | 292 |
for (i = 0; i < 8; i++) { |
235 |
if (GIC_TEST_PENDING(irq + i)) { |
|
293 |
if (GIC_TEST_PENDING(irq + i, mask)) {
|
|
236 | 294 |
res |= (1 << i); |
237 | 295 |
} |
238 | 296 |
} |
239 | 297 |
} else if (offset < 0x400) { |
240 | 298 |
/* Interrupt Active. */ |
241 |
irq = (offset - 0x300) * 8; |
|
299 |
irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
|
|
242 | 300 |
if (irq >= GIC_NIRQ) |
243 | 301 |
goto bad_reg; |
244 | 302 |
res = 0; |
303 |
mask = (irq < 32) ? cm : ALL_CPU_MASK; |
|
245 | 304 |
for (i = 0; i < 8; i++) { |
246 |
if (GIC_TEST_ACTIVE(irq + i)) { |
|
305 |
if (GIC_TEST_ACTIVE(irq + i, mask)) {
|
|
247 | 306 |
res |= (1 << i); |
248 | 307 |
} |
249 | 308 |
} |
250 | 309 |
} else if (offset < 0x800) { |
251 | 310 |
/* Interrupt Priority. */ |
252 |
irq = offset - 0x400;
|
|
311 |
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
|
253 | 312 |
if (irq >= GIC_NIRQ) |
254 | 313 |
goto bad_reg; |
255 |
res = s->priority[irq]; |
|
314 |
res = GIC_GET_PRIORITY(irq, cpu); |
|
315 |
#ifndef NVIC |
|
256 | 316 |
} else if (offset < 0xc00) { |
257 | 317 |
/* Interrupt CPU Target. */ |
258 |
irq = offset - 0x800;
|
|
318 |
irq = (offset - 0x800) + GIC_BASE_IRQ;
|
|
259 | 319 |
if (irq >= GIC_NIRQ) |
260 | 320 |
goto bad_reg; |
261 |
res = s->irq_target[irq]; |
|
321 |
if (irq >= 29 && irq <= 31) { |
|
322 |
res = cm; |
|
323 |
} else { |
|
324 |
res = GIC_TARGET(irq); |
|
325 |
} |
|
262 | 326 |
} else if (offset < 0xf00) { |
263 | 327 |
/* Interrupt Configuration. */ |
264 |
irq = (offset - 0xc00) * 2; |
|
328 |
irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
|
|
265 | 329 |
if (irq >= GIC_NIRQ) |
266 | 330 |
goto bad_reg; |
267 | 331 |
res = 0; |
... | ... | |
271 | 335 |
if (GIC_TEST_TRIGGER(irq + i)) |
272 | 336 |
res |= (2 << (i * 2)); |
273 | 337 |
} |
338 |
#endif |
|
274 | 339 |
} else if (offset < 0xfe0) { |
275 | 340 |
goto bad_reg; |
276 | 341 |
} else /* offset >= 0xfe0 */ { |
... | ... | |
282 | 347 |
} |
283 | 348 |
return res; |
284 | 349 |
bad_reg: |
285 |
cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
|
|
350 |
cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
|
|
286 | 351 |
return 0; |
287 | 352 |
} |
288 | 353 |
|
... | ... | |
297 | 362 |
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) |
298 | 363 |
{ |
299 | 364 |
uint32_t val; |
365 |
#ifdef NVIC |
|
366 |
gic_state *s = (gic_state *)opaque; |
|
367 |
uint32_t addr; |
|
368 |
addr = offset - s->base; |
|
369 |
if (addr < 0x100 || addr > 0xd00) |
|
370 |
return nvic_readl(s->nvic, addr); |
|
371 |
#endif |
|
300 | 372 |
val = gic_dist_readw(opaque, offset); |
301 | 373 |
val |= gic_dist_readw(opaque, offset + 2) << 16; |
302 | 374 |
return val; |
... | ... | |
308 | 380 |
gic_state *s = (gic_state *)opaque; |
309 | 381 |
int irq; |
310 | 382 |
int i; |
383 |
int cpu; |
|
311 | 384 |
|
312 |
offset -= s->base + 0x1000; |
|
385 |
cpu = gic_get_current_cpu(); |
|
386 |
offset -= s->base + GIC_DIST_OFFSET; |
|
313 | 387 |
if (offset < 0x100) { |
388 |
#ifdef NVIC |
|
389 |
goto bad_reg; |
|
390 |
#else |
|
314 | 391 |
if (offset == 0) { |
315 | 392 |
s->enabled = (value & 1); |
316 | 393 |
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); |
... | ... | |
319 | 396 |
} else { |
320 | 397 |
goto bad_reg; |
321 | 398 |
} |
399 |
#endif |
|
322 | 400 |
} else if (offset < 0x180) { |
323 | 401 |
/* Interrupt Set Enable. */ |
324 |
irq = (offset - 0x100) * 8; |
|
402 |
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
|
|
325 | 403 |
if (irq >= GIC_NIRQ) |
326 | 404 |
goto bad_reg; |
405 |
if (irq < 16) |
|
406 |
value = 0xff; |
|
327 | 407 |
for (i = 0; i < 8; i++) { |
328 | 408 |
if (value & (1 << i)) { |
409 |
int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); |
|
329 | 410 |
if (!GIC_TEST_ENABLED(irq + i)) |
330 | 411 |
DPRINTF("Enabled IRQ %d\n", irq + i); |
331 | 412 |
GIC_SET_ENABLED(irq + i); |
332 | 413 |
/* If a raised level triggered IRQ enabled then mark |
333 | 414 |
is as pending. */ |
334 |
if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i)) |
|
335 |
GIC_SET_PENDING(irq + i); |
|
415 |
if (GIC_TEST_LEVEL(irq + i, mask) |
|
416 |
&& !GIC_TEST_TRIGGER(irq + i)) { |
|
417 |
DPRINTF("Set %d pending mask %x\n", irq + i, mask); |
|
418 |
GIC_SET_PENDING(irq + i, mask); |
|
419 |
} |
|
336 | 420 |
} |
337 | 421 |
} |
338 | 422 |
} else if (offset < 0x200) { |
339 | 423 |
/* Interrupt Clear Enable. */ |
340 |
irq = (offset - 0x180) * 8; |
|
424 |
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
|
|
341 | 425 |
if (irq >= GIC_NIRQ) |
342 | 426 |
goto bad_reg; |
427 |
if (irq < 16) |
|
428 |
value = 0; |
|
343 | 429 |
for (i = 0; i < 8; i++) { |
344 | 430 |
if (value & (1 << i)) { |
345 | 431 |
if (GIC_TEST_ENABLED(irq + i)) |
... | ... | |
349 | 435 |
} |
350 | 436 |
} else if (offset < 0x280) { |
351 | 437 |
/* Interrupt Set Pending. */ |
352 |
irq = (offset - 0x200) * 8; |
|
438 |
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
|
|
353 | 439 |
if (irq >= GIC_NIRQ) |
354 | 440 |
goto bad_reg; |
441 |
if (irq < 16) |
|
442 |
irq = 0; |
|
443 |
|
|
355 | 444 |
for (i = 0; i < 8; i++) { |
356 | 445 |
if (value & (1 << i)) { |
357 |
GIC_SET_PENDING(irq + i); |
|
446 |
GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
|
|
358 | 447 |
} |
359 | 448 |
} |
360 | 449 |
} else if (offset < 0x300) { |
361 | 450 |
/* Interrupt Clear Pending. */ |
362 |
irq = (offset - 0x280) * 8; |
|
451 |
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
|
|
363 | 452 |
if (irq >= GIC_NIRQ) |
364 | 453 |
goto bad_reg; |
365 | 454 |
for (i = 0; i < 8; i++) { |
455 |
/* ??? This currently clears the pending bit for all CPUs, even |
|
456 |
for per-CPU interrupts. It's unclear whether this is the |
|
457 |
corect behavior. */ |
|
366 | 458 |
if (value & (1 << i)) { |
367 |
GIC_CLEAR_PENDING(irq + i); |
|
459 |
GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
|
|
368 | 460 |
} |
369 | 461 |
} |
370 | 462 |
} else if (offset < 0x400) { |
... | ... | |
372 | 464 |
goto bad_reg; |
373 | 465 |
} else if (offset < 0x800) { |
374 | 466 |
/* Interrupt Priority. */ |
375 |
irq = offset - 0x400;
|
|
467 |
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
|
376 | 468 |
if (irq >= GIC_NIRQ) |
377 | 469 |
goto bad_reg; |
378 |
s->priority[irq] = value; |
|
470 |
if (irq < 32) { |
|
471 |
s->priority1[irq][cpu] = value; |
|
472 |
} else { |
|
473 |
s->priority2[irq - 32] = value; |
|
474 |
} |
|
475 |
#ifndef NVIC |
|
379 | 476 |
} else if (offset < 0xc00) { |
380 | 477 |
/* Interrupt CPU Target. */ |
381 |
irq = offset - 0x800;
|
|
478 |
irq = (offset - 0x800) + GIC_BASE_IRQ;
|
|
382 | 479 |
if (irq >= GIC_NIRQ) |
383 | 480 |
goto bad_reg; |
384 |
s->irq_target[irq] = value; |
|
481 |
if (irq < 29) |
|
482 |
value = 0; |
|
483 |
else if (irq < 32) |
|
484 |
value = ALL_CPU_MASK; |
|
485 |
s->irq_target[irq] = value & ALL_CPU_MASK; |
|
385 | 486 |
} else if (offset < 0xf00) { |
386 | 487 |
/* Interrupt Configuration. */ |
387 |
irq = (offset - 0xc00) * 4; |
|
488 |
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
|
|
388 | 489 |
if (irq >= GIC_NIRQ) |
389 | 490 |
goto bad_reg; |
491 |
if (irq < 32) |
|
492 |
value |= 0xaa; |
|
390 | 493 |
for (i = 0; i < 4; i++) { |
391 | 494 |
if (value & (1 << (i * 2))) { |
392 | 495 |
GIC_SET_MODEL(irq + i); |
... | ... | |
399 | 502 |
GIC_CLEAR_TRIGGER(irq + i); |
400 | 503 |
} |
401 | 504 |
} |
505 |
#endif |
|
402 | 506 |
} else { |
403 |
/* 0xf00 is only handled for word writes. */
|
|
507 |
/* 0xf00 is only handled for 32-bit writes. */
|
|
404 | 508 |
goto bad_reg; |
405 | 509 |
} |
406 | 510 |
gic_update(s); |
407 | 511 |
return; |
408 | 512 |
bad_reg: |
409 |
cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
|
|
513 |
cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
|
|
410 | 514 |
} |
411 | 515 |
|
412 | 516 |
static void gic_dist_writew(void *opaque, target_phys_addr_t offset, |
413 | 517 |
uint32_t value) |
414 | 518 |
{ |
415 |
gic_state *s = (gic_state *)opaque; |
|
416 |
if (offset - s->base == 0xf00) { |
|
417 |
GIC_SET_PENDING(value & 0x3ff); |
|
418 |
gic_update(s); |
|
419 |
return; |
|
420 |
} |
|
421 | 519 |
gic_dist_writeb(opaque, offset, value & 0xff); |
422 | 520 |
gic_dist_writeb(opaque, offset + 1, value >> 8); |
423 | 521 |
} |
... | ... | |
425 | 523 |
static void gic_dist_writel(void *opaque, target_phys_addr_t offset, |
426 | 524 |
uint32_t value) |
427 | 525 |
{ |
526 |
gic_state *s = (gic_state *)opaque; |
|
527 |
#ifdef NVIC |
|
528 |
uint32_t addr; |
|
529 |
addr = offset - s->base; |
|
530 |
if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) { |
|
531 |
nvic_writel(s->nvic, addr, value); |
|
532 |
return; |
|
533 |
} |
|
534 |
#endif |
|
535 |
if (offset - s->base == GIC_DIST_OFFSET + 0xf00) { |
|
536 |
int cpu; |
|
537 |
int irq; |
|
538 |
int mask; |
|
539 |
|
|
540 |
cpu = gic_get_current_cpu(); |
|
541 |
irq = value & 0x3ff; |
|
542 |
switch ((value >> 24) & 3) { |
|
543 |
case 0: |
|
544 |
mask = (value >> 16) & ALL_CPU_MASK; |
|
545 |
break; |
|
546 |
case 1: |
|
547 |
mask = 1 << cpu; |
|
548 |
break; |
|
549 |
case 2: |
|
550 |
mask = ALL_CPU_MASK ^ (1 << cpu); |
|
551 |
break; |
|
552 |
default: |
|
553 |
DPRINTF("Bad Soft Int target filter\n"); |
|
554 |
mask = ALL_CPU_MASK; |
|
555 |
break; |
|
556 |
} |
|
557 |
GIC_SET_PENDING(irq, mask); |
|
558 |
gic_update(s); |
|
559 |
return; |
|
560 |
} |
|
428 | 561 |
gic_dist_writew(opaque, offset, value & 0xffff); |
429 | 562 |
gic_dist_writew(opaque, offset + 2, value >> 16); |
430 | 563 |
} |
... | ... | |
441 | 574 |
gic_dist_writel |
442 | 575 |
}; |
443 | 576 |
|
444 |
static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset) |
|
577 |
#ifndef NVIC |
|
578 |
static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset) |
|
445 | 579 |
{ |
446 |
gic_state *s = (gic_state *)opaque; |
|
447 |
offset -= s->base; |
|
448 | 580 |
switch (offset) { |
449 | 581 |
case 0x00: /* Control */ |
450 |
return s->cpu_enabled; |
|
582 |
return s->cpu_enabled[cpu];
|
|
451 | 583 |
case 0x04: /* Priority mask */ |
452 |
return s->priority_mask; |
|
584 |
return s->priority_mask[cpu];
|
|
453 | 585 |
case 0x08: /* Binary Point */ |
454 | 586 |
/* ??? Not implemented. */ |
455 | 587 |
return 0; |
456 | 588 |
case 0x0c: /* Acknowledge */ |
457 |
return gic_acknowledge_irq(s); |
|
589 |
return gic_acknowledge_irq(s, cpu);
|
|
458 | 590 |
case 0x14: /* Runing Priority */ |
459 |
return s->running_priority; |
|
591 |
return s->running_priority[cpu];
|
|
460 | 592 |
case 0x18: /* Highest Pending Interrupt */ |
461 |
return s->current_pending; |
|
593 |
return s->current_pending[cpu];
|
|
462 | 594 |
default: |
463 |
cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset); |
|
595 |
cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n", |
|
596 |
(int)offset); |
|
464 | 597 |
return 0; |
465 | 598 |
} |
466 | 599 |
} |
467 | 600 |
|
468 |
static void gic_cpu_write(void *opaque, target_phys_addr_t offset, |
|
469 |
uint32_t value) |
|
601 |
static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value) |
|
470 | 602 |
{ |
471 |
gic_state *s = (gic_state *)opaque; |
|
472 |
offset -= s->base; |
|
473 | 603 |
switch (offset) { |
474 | 604 |
case 0x00: /* Control */ |
475 |
s->cpu_enabled = (value & 1); |
|
605 |
s->cpu_enabled[cpu] = (value & 1);
|
|
476 | 606 |
DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); |
477 | 607 |
break; |
478 | 608 |
case 0x04: /* Priority mask */ |
479 |
s->priority_mask = (value & 0x3ff);
|
|
609 |
s->priority_mask[cpu] = (value & 0xff);
|
|
480 | 610 |
break; |
481 | 611 |
case 0x08: /* Binary Point */ |
482 | 612 |
/* ??? Not implemented. */ |
483 | 613 |
break; |
484 | 614 |
case 0x10: /* End Of Interrupt */ |
485 |
return gic_complete_irq(s, value & 0x3ff); |
|
615 |
return gic_complete_irq(s, cpu, value & 0x3ff);
|
|
486 | 616 |
default: |
487 |
cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset); |
|
617 |
cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n", |
|
618 |
(int)offset); |
|
488 | 619 |
return; |
489 | 620 |
} |
490 | 621 |
gic_update(s); |
491 | 622 |
} |
492 |
|
|
493 |
static CPUReadMemoryFunc *gic_cpu_readfn[] = { |
|
494 |
gic_cpu_read, |
|
495 |
gic_cpu_read, |
|
496 |
gic_cpu_read |
|
497 |
}; |
|
498 |
|
|
499 |
static CPUWriteMemoryFunc *gic_cpu_writefn[] = { |
|
500 |
gic_cpu_write, |
|
501 |
gic_cpu_write, |
|
502 |
gic_cpu_write |
|
503 |
}; |
|
623 |
#endif |
|
504 | 624 |
|
505 | 625 |
static void gic_reset(gic_state *s) |
506 | 626 |
{ |
507 | 627 |
int i; |
508 | 628 |
memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state)); |
509 |
s->priority_mask = 0xf0; |
|
510 |
s->current_pending = 1023; |
|
511 |
s->running_irq = 1023; |
|
512 |
s->running_priority = 0x100; |
|
629 |
for (i = 0 ; i < NCPU; i++) { |
|
630 |
s->priority_mask[i] = 0xf0; |
|
631 |
s->current_pending[i] = 1023; |
|
632 |
s->running_irq[i] = 1023; |
|
633 |
s->running_priority[i] = 0x100; |
|
634 |
#ifdef NVIC |
|
635 |
/* The NVIC doesn't have per-cpu interfaces, so enable by default. */ |
|
636 |
s->cpu_enabled[i] = 1; |
|
637 |
#else |
|
638 |
s->cpu_enabled[i] = 0; |
|
639 |
#endif |
|
640 |
} |
|
513 | 641 |
for (i = 0; i < 15; i++) { |
514 | 642 |
GIC_SET_ENABLED(i); |
515 | 643 |
GIC_SET_TRIGGER(i); |
516 | 644 |
} |
645 |
#ifdef NVIC |
|
646 |
/* The NVIC is always enabled. */ |
|
647 |
s->enabled = 1; |
|
648 |
#else |
|
517 | 649 |
s->enabled = 0; |
518 |
s->cpu_enabled = 0;
|
|
650 |
#endif
|
|
519 | 651 |
} |
520 | 652 |
|
521 |
qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq)
|
|
653 |
static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
|
|
522 | 654 |
{ |
523 | 655 |
gic_state *s; |
524 |
qemu_irq *qi; |
|
525 | 656 |
int iomemtype; |
657 |
int i; |
|
526 | 658 |
|
527 | 659 |
s = (gic_state *)qemu_mallocz(sizeof(gic_state)); |
528 | 660 |
if (!s) |
529 | 661 |
return NULL; |
530 |
qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ); |
|
531 |
s->parent_irq = parent_irq; |
|
532 |
if (base != 0xffffffff) { |
|
533 |
iomemtype = cpu_register_io_memory(0, gic_cpu_readfn, |
|
534 |
gic_cpu_writefn, s); |
|
535 |
cpu_register_physical_memory(base, 0x00001000, iomemtype); |
|
536 |
iomemtype = cpu_register_io_memory(0, gic_dist_readfn, |
|
537 |
gic_dist_writefn, s); |
|
538 |
cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype); |
|
539 |
s->base = base; |
|
540 |
} else { |
|
541 |
s->base = 0; |
|
662 |
s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ); |
|
663 |
for (i = 0; i < NCPU; i++) { |
|
664 |
s->parent_irq[i] = parent_irq[i]; |
|
542 | 665 |
} |
666 |
iomemtype = cpu_register_io_memory(0, gic_dist_readfn, |
|
667 |
gic_dist_writefn, s); |
|
668 |
cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000, |
|
669 |
iomemtype); |
|
670 |
s->base = base; |
|
543 | 671 |
gic_reset(s); |
544 |
return qi;
|
|
672 |
return s;
|
|
545 | 673 |
} |
Also available in: Unified diff