root / hw / arm_gic.c @ 57d69a91
History | View | Annotate | Download (19.4 kB)
1 |
/*
|
---|---|
2 |
* ARM Generic/Distributed Interrupt Controller
|
3 |
*
|
4 |
* Copyright (c) 2006-2007 CodeSourcery.
|
5 |
* Written by Paul Brook
|
6 |
*
|
7 |
* This code is licenced under the GPL.
|
8 |
*/
|
9 |
|
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. */
|
13 |
|
14 |
//#define DEBUG_GIC
|
15 |
|
16 |
#ifdef DEBUG_GIC
|
17 |
#define DPRINTF(fmt, args...) \
|
18 |
do { printf("arm_gic: " fmt , ##args); } while (0) |
19 |
#else
|
20 |
#define DPRINTF(fmt, args...) do {} while(0) |
21 |
#endif
|
22 |
|
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
|
31 |
static const uint8_t gic_id[] = |
32 |
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; |
33 |
#define GIC_DIST_OFFSET 0x1000 |
34 |
#define GIC_BASE_IRQ 0 |
35 |
#endif
|
36 |
|
37 |
typedef struct gic_irq_state |
38 |
{ |
39 |
/* ??? The documentation seems to imply the enable bits are global, even
|
40 |
for per-cpu interrupts. This seems strange. */
|
41 |
unsigned enabled:1; |
42 |
unsigned pending:NCPU;
|
43 |
unsigned active:NCPU;
|
44 |
unsigned level:1; |
45 |
unsigned model:1; /* 0 = N:N, 1 = 1:N */ |
46 |
unsigned trigger:1; /* nonzero = edge triggered. */ |
47 |
} gic_irq_state; |
48 |
|
49 |
#define ALL_CPU_MASK ((1 << NCPU) - 1) |
50 |
|
51 |
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1 |
52 |
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0 |
53 |
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
|
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) |
60 |
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 |
61 |
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 |
62 |
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
|
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) |
66 |
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 |
67 |
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 |
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
|
76 |
|
77 |
typedef struct gic_state |
78 |
{ |
79 |
uint32_t base; |
80 |
qemu_irq parent_irq[NCPU]; |
81 |
int enabled;
|
82 |
int cpu_enabled[NCPU];
|
83 |
|
84 |
gic_irq_state irq_state[GIC_NIRQ]; |
85 |
#ifndef NVIC
|
86 |
int irq_target[GIC_NIRQ];
|
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
|
101 |
} gic_state; |
102 |
|
103 |
/* TODO: Many places that call this routine could be optimized. */
|
104 |
/* Update interrupt status after enabled or pending bits have been changed. */
|
105 |
static void gic_update(gic_state *s) |
106 |
{ |
107 |
int best_irq;
|
108 |
int best_prio;
|
109 |
int 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 |
} |
129 |
} |
130 |
} |
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 |
} |
138 |
} |
139 |
qemu_set_irq(s->parent_irq[cpu], level); |
140 |
} |
141 |
} |
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. */
|
157 |
static void gic_set_irq(void *opaque, int irq, int level) |
158 |
{ |
159 |
gic_state *s = (gic_state *)opaque; |
160 |
/* The first external input line is internal interrupt 32. */
|
161 |
irq += 32;
|
162 |
if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
|
163 |
return;
|
164 |
|
165 |
if (level) {
|
166 |
GIC_SET_LEVEL(irq, ALL_CPU_MASK); |
167 |
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
|
168 |
DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
|
169 |
GIC_SET_PENDING(irq, GIC_TARGET(irq)); |
170 |
} |
171 |
} else {
|
172 |
GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK); |
173 |
} |
174 |
gic_update(s); |
175 |
} |
176 |
|
177 |
static void gic_set_running_irq(gic_state *s, int cpu, int irq) |
178 |
{ |
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 |
} |
185 |
gic_update(s); |
186 |
} |
187 |
|
188 |
static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) |
189 |
{ |
190 |
int new_irq;
|
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]) { |
195 |
DPRINTF("ACK no pending IRQ\n");
|
196 |
return 1023; |
197 |
} |
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); |
203 |
DPRINTF("ACK %d\n", new_irq);
|
204 |
return new_irq;
|
205 |
} |
206 |
|
207 |
static void gic_complete_irq(gic_state * s, int cpu, int irq) |
208 |
{ |
209 |
int update = 0; |
210 |
int cm = 1 << cpu; |
211 |
DPRINTF("EOI %d\n", irq);
|
212 |
if (s->running_irq[cpu] == 1023) |
213 |
return; /* No active IRQ. */ |
214 |
if (irq != 1023) { |
215 |
/* Mark level triggered interrupts as pending if they are still
|
216 |
raised. */
|
217 |
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(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); |
221 |
update = 1;
|
222 |
} |
223 |
} |
224 |
if (irq != s->running_irq[cpu]) {
|
225 |
/* Complete an IRQ that is not currently running. */
|
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]; |
230 |
break;
|
231 |
} |
232 |
tmp = s->last_active[tmp][cpu]; |
233 |
} |
234 |
if (update) {
|
235 |
gic_update(s); |
236 |
} |
237 |
} else {
|
238 |
/* Complete the current running IRQ. */
|
239 |
gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]); |
240 |
} |
241 |
} |
242 |
|
243 |
static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) |
244 |
{ |
245 |
gic_state *s = (gic_state *)opaque; |
246 |
uint32_t res; |
247 |
int irq;
|
248 |
int i;
|
249 |
int cpu;
|
250 |
int cm;
|
251 |
int mask;
|
252 |
|
253 |
cpu = gic_get_current_cpu(); |
254 |
cm = 1 << cpu;
|
255 |
offset -= s->base + GIC_DIST_OFFSET; |
256 |
if (offset < 0x100) { |
257 |
#ifndef NVIC
|
258 |
if (offset == 0) |
259 |
return s->enabled;
|
260 |
if (offset == 4) |
261 |
return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5); |
262 |
if (offset < 0x08) |
263 |
return 0; |
264 |
#endif
|
265 |
goto bad_reg;
|
266 |
} else if (offset < 0x200) { |
267 |
/* Interrupt Set/Clear Enable. */
|
268 |
if (offset < 0x180) |
269 |
irq = (offset - 0x100) * 8; |
270 |
else
|
271 |
irq = (offset - 0x180) * 8; |
272 |
irq += GIC_BASE_IRQ; |
273 |
if (irq >= GIC_NIRQ)
|
274 |
goto bad_reg;
|
275 |
res = 0;
|
276 |
for (i = 0; i < 8; i++) { |
277 |
if (GIC_TEST_ENABLED(irq + i)) {
|
278 |
res |= (1 << i);
|
279 |
} |
280 |
} |
281 |
} else if (offset < 0x300) { |
282 |
/* Interrupt Set/Clear Pending. */
|
283 |
if (offset < 0x280) |
284 |
irq = (offset - 0x200) * 8; |
285 |
else
|
286 |
irq = (offset - 0x280) * 8; |
287 |
irq += GIC_BASE_IRQ; |
288 |
if (irq >= GIC_NIRQ)
|
289 |
goto bad_reg;
|
290 |
res = 0;
|
291 |
mask = (irq < 32) ? cm : ALL_CPU_MASK;
|
292 |
for (i = 0; i < 8; i++) { |
293 |
if (GIC_TEST_PENDING(irq + i, mask)) {
|
294 |
res |= (1 << i);
|
295 |
} |
296 |
} |
297 |
} else if (offset < 0x400) { |
298 |
/* Interrupt Active. */
|
299 |
irq = (offset - 0x300) * 8 + GIC_BASE_IRQ; |
300 |
if (irq >= GIC_NIRQ)
|
301 |
goto bad_reg;
|
302 |
res = 0;
|
303 |
mask = (irq < 32) ? cm : ALL_CPU_MASK;
|
304 |
for (i = 0; i < 8; i++) { |
305 |
if (GIC_TEST_ACTIVE(irq + i, mask)) {
|
306 |
res |= (1 << i);
|
307 |
} |
308 |
} |
309 |
} else if (offset < 0x800) { |
310 |
/* Interrupt Priority. */
|
311 |
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
312 |
if (irq >= GIC_NIRQ)
|
313 |
goto bad_reg;
|
314 |
res = GIC_GET_PRIORITY(irq, cpu); |
315 |
#ifndef NVIC
|
316 |
} else if (offset < 0xc00) { |
317 |
/* Interrupt CPU Target. */
|
318 |
irq = (offset - 0x800) + GIC_BASE_IRQ;
|
319 |
if (irq >= GIC_NIRQ)
|
320 |
goto bad_reg;
|
321 |
if (irq >= 29 && irq <= 31) { |
322 |
res = cm; |
323 |
} else {
|
324 |
res = GIC_TARGET(irq); |
325 |
} |
326 |
} else if (offset < 0xf00) { |
327 |
/* Interrupt Configuration. */
|
328 |
irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ; |
329 |
if (irq >= GIC_NIRQ)
|
330 |
goto bad_reg;
|
331 |
res = 0;
|
332 |
for (i = 0; i < 4; i++) { |
333 |
if (GIC_TEST_MODEL(irq + i))
|
334 |
res |= (1 << (i * 2)); |
335 |
if (GIC_TEST_TRIGGER(irq + i))
|
336 |
res |= (2 << (i * 2)); |
337 |
} |
338 |
#endif
|
339 |
} else if (offset < 0xfe0) { |
340 |
goto bad_reg;
|
341 |
} else /* offset >= 0xfe0 */ { |
342 |
if (offset & 3) { |
343 |
res = 0;
|
344 |
} else {
|
345 |
res = gic_id[(offset - 0xfe0) >> 2]; |
346 |
} |
347 |
} |
348 |
return res;
|
349 |
bad_reg:
|
350 |
cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset); |
351 |
return 0; |
352 |
} |
353 |
|
354 |
static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) |
355 |
{ |
356 |
uint32_t val; |
357 |
val = gic_dist_readb(opaque, offset); |
358 |
val |= gic_dist_readb(opaque, offset + 1) << 8; |
359 |
return val;
|
360 |
} |
361 |
|
362 |
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) |
363 |
{ |
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
|
372 |
val = gic_dist_readw(opaque, offset); |
373 |
val |= gic_dist_readw(opaque, offset + 2) << 16; |
374 |
return val;
|
375 |
} |
376 |
|
377 |
static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, |
378 |
uint32_t value) |
379 |
{ |
380 |
gic_state *s = (gic_state *)opaque; |
381 |
int irq;
|
382 |
int i;
|
383 |
int cpu;
|
384 |
|
385 |
cpu = gic_get_current_cpu(); |
386 |
offset -= s->base + GIC_DIST_OFFSET; |
387 |
if (offset < 0x100) { |
388 |
#ifdef NVIC
|
389 |
goto bad_reg;
|
390 |
#else
|
391 |
if (offset == 0) { |
392 |
s->enabled = (value & 1);
|
393 |
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); |
394 |
} else if (offset < 4) { |
395 |
/* ignored. */
|
396 |
} else {
|
397 |
goto bad_reg;
|
398 |
} |
399 |
#endif
|
400 |
} else if (offset < 0x180) { |
401 |
/* Interrupt Set Enable. */
|
402 |
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; |
403 |
if (irq >= GIC_NIRQ)
|
404 |
goto bad_reg;
|
405 |
if (irq < 16) |
406 |
value = 0xff;
|
407 |
for (i = 0; i < 8; i++) { |
408 |
if (value & (1 << i)) { |
409 |
int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); |
410 |
if (!GIC_TEST_ENABLED(irq + i))
|
411 |
DPRINTF("Enabled IRQ %d\n", irq + i);
|
412 |
GIC_SET_ENABLED(irq + i); |
413 |
/* If a raised level triggered IRQ enabled then mark
|
414 |
is as pending. */
|
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 |
} |
420 |
} |
421 |
} |
422 |
} else if (offset < 0x200) { |
423 |
/* Interrupt Clear Enable. */
|
424 |
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ; |
425 |
if (irq >= GIC_NIRQ)
|
426 |
goto bad_reg;
|
427 |
if (irq < 16) |
428 |
value = 0;
|
429 |
for (i = 0; i < 8; i++) { |
430 |
if (value & (1 << i)) { |
431 |
if (GIC_TEST_ENABLED(irq + i))
|
432 |
DPRINTF("Disabled IRQ %d\n", irq + i);
|
433 |
GIC_CLEAR_ENABLED(irq + i); |
434 |
} |
435 |
} |
436 |
} else if (offset < 0x280) { |
437 |
/* Interrupt Set Pending. */
|
438 |
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ; |
439 |
if (irq >= GIC_NIRQ)
|
440 |
goto bad_reg;
|
441 |
if (irq < 16) |
442 |
irq = 0;
|
443 |
|
444 |
for (i = 0; i < 8; i++) { |
445 |
if (value & (1 << i)) { |
446 |
GIC_SET_PENDING(irq + i, GIC_TARGET(irq)); |
447 |
} |
448 |
} |
449 |
} else if (offset < 0x300) { |
450 |
/* Interrupt Clear Pending. */
|
451 |
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ; |
452 |
if (irq >= GIC_NIRQ)
|
453 |
goto bad_reg;
|
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. */
|
458 |
if (value & (1 << i)) { |
459 |
GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK); |
460 |
} |
461 |
} |
462 |
} else if (offset < 0x400) { |
463 |
/* Interrupt Active. */
|
464 |
goto bad_reg;
|
465 |
} else if (offset < 0x800) { |
466 |
/* Interrupt Priority. */
|
467 |
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
468 |
if (irq >= GIC_NIRQ)
|
469 |
goto bad_reg;
|
470 |
if (irq < 32) { |
471 |
s->priority1[irq][cpu] = value; |
472 |
} else {
|
473 |
s->priority2[irq - 32] = value;
|
474 |
} |
475 |
#ifndef NVIC
|
476 |
} else if (offset < 0xc00) { |
477 |
/* Interrupt CPU Target. */
|
478 |
irq = (offset - 0x800) + GIC_BASE_IRQ;
|
479 |
if (irq >= GIC_NIRQ)
|
480 |
goto bad_reg;
|
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; |
486 |
} else if (offset < 0xf00) { |
487 |
/* Interrupt Configuration. */
|
488 |
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; |
489 |
if (irq >= GIC_NIRQ)
|
490 |
goto bad_reg;
|
491 |
if (irq < 32) |
492 |
value |= 0xaa;
|
493 |
for (i = 0; i < 4; i++) { |
494 |
if (value & (1 << (i * 2))) { |
495 |
GIC_SET_MODEL(irq + i); |
496 |
} else {
|
497 |
GIC_CLEAR_MODEL(irq + i); |
498 |
} |
499 |
if (value & (2 << (i * 2))) { |
500 |
GIC_SET_TRIGGER(irq + i); |
501 |
} else {
|
502 |
GIC_CLEAR_TRIGGER(irq + i); |
503 |
} |
504 |
} |
505 |
#endif
|
506 |
} else {
|
507 |
/* 0xf00 is only handled for 32-bit writes. */
|
508 |
goto bad_reg;
|
509 |
} |
510 |
gic_update(s); |
511 |
return;
|
512 |
bad_reg:
|
513 |
cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset); |
514 |
} |
515 |
|
516 |
static void gic_dist_writew(void *opaque, target_phys_addr_t offset, |
517 |
uint32_t value) |
518 |
{ |
519 |
gic_dist_writeb(opaque, offset, value & 0xff);
|
520 |
gic_dist_writeb(opaque, offset + 1, value >> 8); |
521 |
} |
522 |
|
523 |
static void gic_dist_writel(void *opaque, target_phys_addr_t offset, |
524 |
uint32_t value) |
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 |
} |
561 |
gic_dist_writew(opaque, offset, value & 0xffff);
|
562 |
gic_dist_writew(opaque, offset + 2, value >> 16); |
563 |
} |
564 |
|
565 |
static CPUReadMemoryFunc *gic_dist_readfn[] = {
|
566 |
gic_dist_readb, |
567 |
gic_dist_readw, |
568 |
gic_dist_readl |
569 |
}; |
570 |
|
571 |
static CPUWriteMemoryFunc *gic_dist_writefn[] = {
|
572 |
gic_dist_writeb, |
573 |
gic_dist_writew, |
574 |
gic_dist_writel |
575 |
}; |
576 |
|
577 |
#ifndef NVIC
|
578 |
static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset) |
579 |
{ |
580 |
switch (offset) {
|
581 |
case 0x00: /* Control */ |
582 |
return s->cpu_enabled[cpu];
|
583 |
case 0x04: /* Priority mask */ |
584 |
return s->priority_mask[cpu];
|
585 |
case 0x08: /* Binary Point */ |
586 |
/* ??? Not implemented. */
|
587 |
return 0; |
588 |
case 0x0c: /* Acknowledge */ |
589 |
return gic_acknowledge_irq(s, cpu);
|
590 |
case 0x14: /* Runing Priority */ |
591 |
return s->running_priority[cpu];
|
592 |
case 0x18: /* Highest Pending Interrupt */ |
593 |
return s->current_pending[cpu];
|
594 |
default:
|
595 |
cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
|
596 |
(int)offset);
|
597 |
return 0; |
598 |
} |
599 |
} |
600 |
|
601 |
static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value) |
602 |
{ |
603 |
switch (offset) {
|
604 |
case 0x00: /* Control */ |
605 |
s->cpu_enabled[cpu] = (value & 1);
|
606 |
DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); |
607 |
break;
|
608 |
case 0x04: /* Priority mask */ |
609 |
s->priority_mask[cpu] = (value & 0xff);
|
610 |
break;
|
611 |
case 0x08: /* Binary Point */ |
612 |
/* ??? Not implemented. */
|
613 |
break;
|
614 |
case 0x10: /* End Of Interrupt */ |
615 |
return gic_complete_irq(s, cpu, value & 0x3ff); |
616 |
default:
|
617 |
cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
|
618 |
(int)offset);
|
619 |
return;
|
620 |
} |
621 |
gic_update(s); |
622 |
} |
623 |
#endif
|
624 |
|
625 |
static void gic_reset(gic_state *s) |
626 |
{ |
627 |
int i;
|
628 |
memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state)); |
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 |
} |
641 |
for (i = 0; i < 16; i++) { |
642 |
GIC_SET_ENABLED(i); |
643 |
GIC_SET_TRIGGER(i); |
644 |
} |
645 |
#ifdef NVIC
|
646 |
/* The NVIC is always enabled. */
|
647 |
s->enabled = 1;
|
648 |
#else
|
649 |
s->enabled = 0;
|
650 |
#endif
|
651 |
} |
652 |
|
653 |
static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
|
654 |
{ |
655 |
gic_state *s; |
656 |
int iomemtype;
|
657 |
int i;
|
658 |
|
659 |
s = (gic_state *)qemu_mallocz(sizeof(gic_state));
|
660 |
if (!s)
|
661 |
return NULL; |
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]; |
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; |
671 |
gic_reset(s); |
672 |
return s;
|
673 |
} |