blob: 563397d3ed9641d25cb0697e7794d1b1f3f21b0e [file] [log] [blame]
ths5fafdf22007-09-16 21:08:06 +00001/*
pbrook9ee6e8b2007-11-11 00:04:49 +00002 * ARM Generic/Distributed Interrupt Controller
pbrooke69954b2006-09-23 17:40:58 +00003 *
pbrook9ee6e8b2007-11-11 00:04:49 +00004 * Copyright (c) 2006-2007 CodeSourcery.
pbrooke69954b2006-09-23 17:40:58 +00005 * Written by Paul Brook
6 *
7 * This code is licenced under the GPL.
8 */
9
pbrook9ee6e8b2007-11-11 00:04:49 +000010/* This file contains implementation code for the RealView EB interrupt
11 controller, MPCore distributed interrupt controller and ARMv7-M
12 Nested Vectored Interrupt Controller. */
pbrooke69954b2006-09-23 17:40:58 +000013
14//#define DEBUG_GIC
15
16#ifdef DEBUG_GIC
Blue Swirl001faf32009-05-13 17:53:17 +000017#define DPRINTF(fmt, ...) \
18do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0)
pbrooke69954b2006-09-23 17:40:58 +000019#else
Blue Swirl001faf32009-05-13 17:53:17 +000020#define DPRINTF(fmt, ...) do {} while(0)
pbrooke69954b2006-09-23 17:40:58 +000021#endif
22
pbrook9ee6e8b2007-11-11 00:04:49 +000023#ifdef NVIC
24static const uint8_t gic_id[] =
25{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
pbrook9ee6e8b2007-11-11 00:04:49 +000026/* The NVIC has 16 internal vectors. However these are not exposed
27 through the normal GIC interface. */
28#define GIC_BASE_IRQ 32
29#else
pbrooke69954b2006-09-23 17:40:58 +000030static const uint8_t gic_id[] =
31{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
pbrook9ee6e8b2007-11-11 00:04:49 +000032#define GIC_BASE_IRQ 0
33#endif
pbrooke69954b2006-09-23 17:40:58 +000034
Paul Brookfe7e8752009-05-14 22:35:08 +010035#define FROM_SYSBUSGIC(type, dev) \
36 DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev))
37
pbrooke69954b2006-09-23 17:40:58 +000038typedef struct gic_irq_state
39{
pbrook9ee6e8b2007-11-11 00:04:49 +000040 /* ??? The documentation seems to imply the enable bits are global, even
41 for per-cpu interrupts. This seems strange. */
pbrooke69954b2006-09-23 17:40:58 +000042 unsigned enabled:1;
pbrook9ee6e8b2007-11-11 00:04:49 +000043 unsigned pending:NCPU;
44 unsigned active:NCPU;
aurel32a45db6c2009-03-07 21:47:53 +000045 unsigned level:NCPU;
pbrook9ee6e8b2007-11-11 00:04:49 +000046 unsigned model:1; /* 0 = N:N, 1 = 1:N */
pbrooke69954b2006-09-23 17:40:58 +000047 unsigned trigger:1; /* nonzero = edge triggered. */
48} gic_irq_state;
49
pbrook9ee6e8b2007-11-11 00:04:49 +000050#define ALL_CPU_MASK ((1 << NCPU) - 1)
51
pbrooke69954b2006-09-23 17:40:58 +000052#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
53#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
54#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
pbrook9ee6e8b2007-11-11 00:04:49 +000055#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
56#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
57#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
58#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
59#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
60#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
pbrooke69954b2006-09-23 17:40:58 +000061#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
62#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
63#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
pbrook9ee6e8b2007-11-11 00:04:49 +000064#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
65#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
balrog57d69a92008-05-06 14:45:30 +000066#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
pbrooke69954b2006-09-23 17:40:58 +000067#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
68#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
69#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
pbrook9ee6e8b2007-11-11 00:04:49 +000070#define GIC_GET_PRIORITY(irq, cpu) \
71 (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
72#ifdef NVIC
73#define GIC_TARGET(irq) 1
74#else
75#define GIC_TARGET(irq) s->irq_target[irq]
76#endif
pbrooke69954b2006-09-23 17:40:58 +000077
78typedef struct gic_state
79{
Paul Brookfe7e8752009-05-14 22:35:08 +010080 SysBusDevice busdev;
pbrook9ee6e8b2007-11-11 00:04:49 +000081 qemu_irq parent_irq[NCPU];
pbrooke69954b2006-09-23 17:40:58 +000082 int enabled;
pbrook9ee6e8b2007-11-11 00:04:49 +000083 int cpu_enabled[NCPU];
pbrooke69954b2006-09-23 17:40:58 +000084
85 gic_irq_state irq_state[GIC_NIRQ];
pbrook9ee6e8b2007-11-11 00:04:49 +000086#ifndef NVIC
pbrooke69954b2006-09-23 17:40:58 +000087 int irq_target[GIC_NIRQ];
pbrook9ee6e8b2007-11-11 00:04:49 +000088#endif
89 int priority1[32][NCPU];
90 int priority2[GIC_NIRQ - 32];
91 int last_active[GIC_NIRQ][NCPU];
pbrooke69954b2006-09-23 17:40:58 +000092
pbrook9ee6e8b2007-11-11 00:04:49 +000093 int priority_mask[NCPU];
94 int running_irq[NCPU];
95 int running_priority[NCPU];
96 int current_pending[NCPU];
97
Paul Brookfe7e8752009-05-14 22:35:08 +010098 int iomemtype;
pbrooke69954b2006-09-23 17:40:58 +000099} gic_state;
100
101/* TODO: Many places that call this routine could be optimized. */
102/* Update interrupt status after enabled or pending bits have been changed. */
103static void gic_update(gic_state *s)
104{
105 int best_irq;
106 int best_prio;
107 int irq;
pbrook9ee6e8b2007-11-11 00:04:49 +0000108 int level;
109 int cpu;
110 int cm;
pbrooke69954b2006-09-23 17:40:58 +0000111
pbrook9ee6e8b2007-11-11 00:04:49 +0000112 for (cpu = 0; cpu < NCPU; cpu++) {
113 cm = 1 << cpu;
114 s->current_pending[cpu] = 1023;
115 if (!s->enabled || !s->cpu_enabled[cpu]) {
116 qemu_irq_lower(s->parent_irq[cpu]);
117 return;
118 }
119 best_prio = 0x100;
120 best_irq = 1023;
121 for (irq = 0; irq < GIC_NIRQ; irq++) {
122 if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
123 if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
124 best_prio = GIC_GET_PRIORITY(irq, cpu);
125 best_irq = irq;
126 }
pbrooke69954b2006-09-23 17:40:58 +0000127 }
128 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000129 level = 0;
130 if (best_prio <= s->priority_mask[cpu]) {
131 s->current_pending[cpu] = best_irq;
132 if (best_prio < s->running_priority[cpu]) {
133 DPRINTF("Raised pending IRQ %d\n", best_irq);
134 level = 1;
135 }
pbrooke69954b2006-09-23 17:40:58 +0000136 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000137 qemu_set_irq(s->parent_irq[cpu], level);
pbrooke69954b2006-09-23 17:40:58 +0000138 }
139}
140
pbrook9ee6e8b2007-11-11 00:04:49 +0000141static void __attribute__((unused))
142gic_set_pending_private(gic_state *s, int cpu, int irq)
143{
144 int cm = 1 << cpu;
145
146 if (GIC_TEST_PENDING(irq, cm))
147 return;
148
149 DPRINTF("Set %d pending cpu %d\n", irq, cpu);
150 GIC_SET_PENDING(irq, cm);
151 gic_update(s);
152}
153
154/* Process a change in an external IRQ input. */
pbrooke69954b2006-09-23 17:40:58 +0000155static void gic_set_irq(void *opaque, int irq, int level)
156{
157 gic_state *s = (gic_state *)opaque;
158 /* The first external input line is internal interrupt 32. */
159 irq += 32;
pbrook9ee6e8b2007-11-11 00:04:49 +0000160 if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
pbrooke69954b2006-09-23 17:40:58 +0000161 return;
162
163 if (level) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000164 GIC_SET_LEVEL(irq, ALL_CPU_MASK);
pbrooke69954b2006-09-23 17:40:58 +0000165 if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000166 DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
167 GIC_SET_PENDING(irq, GIC_TARGET(irq));
pbrooke69954b2006-09-23 17:40:58 +0000168 }
169 } else {
pbrook9ee6e8b2007-11-11 00:04:49 +0000170 GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
pbrooke69954b2006-09-23 17:40:58 +0000171 }
172 gic_update(s);
173}
174
pbrook9ee6e8b2007-11-11 00:04:49 +0000175static void gic_set_running_irq(gic_state *s, int cpu, int irq)
pbrooke69954b2006-09-23 17:40:58 +0000176{
pbrook9ee6e8b2007-11-11 00:04:49 +0000177 s->running_irq[cpu] = irq;
178 if (irq == 1023) {
179 s->running_priority[cpu] = 0x100;
180 } else {
181 s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
182 }
pbrooke69954b2006-09-23 17:40:58 +0000183 gic_update(s);
184}
185
pbrook9ee6e8b2007-11-11 00:04:49 +0000186static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
pbrooke69954b2006-09-23 17:40:58 +0000187{
188 int new_irq;
pbrook9ee6e8b2007-11-11 00:04:49 +0000189 int cm = 1 << cpu;
190 new_irq = s->current_pending[cpu];
191 if (new_irq == 1023
192 || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
pbrooke69954b2006-09-23 17:40:58 +0000193 DPRINTF("ACK no pending IRQ\n");
194 return 1023;
195 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000196 s->last_active[new_irq][cpu] = s->running_irq[cpu];
197 /* Clear pending flags for both level and edge triggered interrupts.
198 Level triggered IRQs will be reasserted once they become inactive. */
199 GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
200 gic_set_running_irq(s, cpu, new_irq);
pbrooke69954b2006-09-23 17:40:58 +0000201 DPRINTF("ACK %d\n", new_irq);
202 return new_irq;
203}
204
pbrook9ee6e8b2007-11-11 00:04:49 +0000205static void gic_complete_irq(gic_state * s, int cpu, int irq)
pbrooke69954b2006-09-23 17:40:58 +0000206{
207 int update = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000208 int cm = 1 << cpu;
pbrookdf628ff2007-01-02 19:33:15 +0000209 DPRINTF("EOI %d\n", irq);
pbrook9ee6e8b2007-11-11 00:04:49 +0000210 if (s->running_irq[cpu] == 1023)
pbrooke69954b2006-09-23 17:40:58 +0000211 return; /* No active IRQ. */
212 if (irq != 1023) {
213 /* Mark level triggered interrupts as pending if they are still
214 raised. */
215 if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
pbrook9ee6e8b2007-11-11 00:04:49 +0000216 && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
217 DPRINTF("Set %d pending mask %x\n", irq, cm);
218 GIC_SET_PENDING(irq, cm);
pbrooke69954b2006-09-23 17:40:58 +0000219 update = 1;
220 }
221 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000222 if (irq != s->running_irq[cpu]) {
pbrooke69954b2006-09-23 17:40:58 +0000223 /* Complete an IRQ that is not currently running. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000224 int tmp = s->running_irq[cpu];
225 while (s->last_active[tmp][cpu] != 1023) {
226 if (s->last_active[tmp][cpu] == irq) {
227 s->last_active[tmp][cpu] = s->last_active[irq][cpu];
pbrooke69954b2006-09-23 17:40:58 +0000228 break;
229 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000230 tmp = s->last_active[tmp][cpu];
pbrooke69954b2006-09-23 17:40:58 +0000231 }
232 if (update) {
233 gic_update(s);
234 }
235 } else {
236 /* Complete the current running IRQ. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000237 gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
pbrooke69954b2006-09-23 17:40:58 +0000238 }
239}
240
241static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
242{
243 gic_state *s = (gic_state *)opaque;
244 uint32_t res;
245 int irq;
246 int i;
pbrook9ee6e8b2007-11-11 00:04:49 +0000247 int cpu;
248 int cm;
249 int mask;
pbrooke69954b2006-09-23 17:40:58 +0000250
pbrook9ee6e8b2007-11-11 00:04:49 +0000251 cpu = gic_get_current_cpu();
252 cm = 1 << cpu;
pbrooke69954b2006-09-23 17:40:58 +0000253 if (offset < 0x100) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000254#ifndef NVIC
pbrooke69954b2006-09-23 17:40:58 +0000255 if (offset == 0)
256 return s->enabled;
257 if (offset == 4)
pbrook9ee6e8b2007-11-11 00:04:49 +0000258 return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
pbrooke69954b2006-09-23 17:40:58 +0000259 if (offset < 0x08)
260 return 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000261#endif
pbrooke69954b2006-09-23 17:40:58 +0000262 goto bad_reg;
263 } else if (offset < 0x200) {
264 /* Interrupt Set/Clear Enable. */
265 if (offset < 0x180)
266 irq = (offset - 0x100) * 8;
267 else
268 irq = (offset - 0x180) * 8;
pbrook9ee6e8b2007-11-11 00:04:49 +0000269 irq += GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000270 if (irq >= GIC_NIRQ)
271 goto bad_reg;
272 res = 0;
273 for (i = 0; i < 8; i++) {
274 if (GIC_TEST_ENABLED(irq + i)) {
275 res |= (1 << i);
276 }
277 }
278 } else if (offset < 0x300) {
279 /* Interrupt Set/Clear Pending. */
280 if (offset < 0x280)
281 irq = (offset - 0x200) * 8;
282 else
283 irq = (offset - 0x280) * 8;
pbrook9ee6e8b2007-11-11 00:04:49 +0000284 irq += GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000285 if (irq >= GIC_NIRQ)
286 goto bad_reg;
287 res = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000288 mask = (irq < 32) ? cm : ALL_CPU_MASK;
pbrooke69954b2006-09-23 17:40:58 +0000289 for (i = 0; i < 8; i++) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000290 if (GIC_TEST_PENDING(irq + i, mask)) {
pbrooke69954b2006-09-23 17:40:58 +0000291 res |= (1 << i);
292 }
293 }
294 } else if (offset < 0x400) {
295 /* Interrupt Active. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000296 irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000297 if (irq >= GIC_NIRQ)
298 goto bad_reg;
299 res = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000300 mask = (irq < 32) ? cm : ALL_CPU_MASK;
pbrooke69954b2006-09-23 17:40:58 +0000301 for (i = 0; i < 8; i++) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000302 if (GIC_TEST_ACTIVE(irq + i, mask)) {
pbrooke69954b2006-09-23 17:40:58 +0000303 res |= (1 << i);
304 }
305 }
306 } else if (offset < 0x800) {
307 /* Interrupt Priority. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000308 irq = (offset - 0x400) + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000309 if (irq >= GIC_NIRQ)
310 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000311 res = GIC_GET_PRIORITY(irq, cpu);
312#ifndef NVIC
pbrooke69954b2006-09-23 17:40:58 +0000313 } else if (offset < 0xc00) {
314 /* Interrupt CPU Target. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000315 irq = (offset - 0x800) + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000316 if (irq >= GIC_NIRQ)
317 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000318 if (irq >= 29 && irq <= 31) {
319 res = cm;
320 } else {
321 res = GIC_TARGET(irq);
322 }
pbrooke69954b2006-09-23 17:40:58 +0000323 } else if (offset < 0xf00) {
324 /* Interrupt Configuration. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000325 irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000326 if (irq >= GIC_NIRQ)
327 goto bad_reg;
328 res = 0;
329 for (i = 0; i < 4; i++) {
330 if (GIC_TEST_MODEL(irq + i))
331 res |= (1 << (i * 2));
332 if (GIC_TEST_TRIGGER(irq + i))
333 res |= (2 << (i * 2));
334 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000335#endif
pbrooke69954b2006-09-23 17:40:58 +0000336 } else if (offset < 0xfe0) {
337 goto bad_reg;
338 } else /* offset >= 0xfe0 */ {
339 if (offset & 3) {
340 res = 0;
341 } else {
342 res = gic_id[(offset - 0xfe0) >> 2];
343 }
344 }
345 return res;
346bad_reg:
Paul Brook2ac71172009-05-08 02:35:15 +0100347 hw_error("gic_dist_readb: Bad offset %x\n", (int)offset);
pbrooke69954b2006-09-23 17:40:58 +0000348 return 0;
349}
350
351static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
352{
353 uint32_t val;
354 val = gic_dist_readb(opaque, offset);
355 val |= gic_dist_readb(opaque, offset + 1) << 8;
356 return val;
357}
358
359static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
360{
361 uint32_t val;
pbrook9ee6e8b2007-11-11 00:04:49 +0000362#ifdef NVIC
363 gic_state *s = (gic_state *)opaque;
364 uint32_t addr;
pbrook8da3ff12008-12-01 18:59:50 +0000365 addr = offset;
pbrook9ee6e8b2007-11-11 00:04:49 +0000366 if (addr < 0x100 || addr > 0xd00)
Paul Brookfe7e8752009-05-14 22:35:08 +0100367 return nvic_readl(s, addr);
pbrook9ee6e8b2007-11-11 00:04:49 +0000368#endif
pbrooke69954b2006-09-23 17:40:58 +0000369 val = gic_dist_readw(opaque, offset);
370 val |= gic_dist_readw(opaque, offset + 2) << 16;
371 return val;
372}
373
374static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
375 uint32_t value)
376{
377 gic_state *s = (gic_state *)opaque;
378 int irq;
379 int i;
pbrook9ee6e8b2007-11-11 00:04:49 +0000380 int cpu;
pbrooke69954b2006-09-23 17:40:58 +0000381
pbrook9ee6e8b2007-11-11 00:04:49 +0000382 cpu = gic_get_current_cpu();
pbrooke69954b2006-09-23 17:40:58 +0000383 if (offset < 0x100) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000384#ifdef NVIC
385 goto bad_reg;
386#else
pbrooke69954b2006-09-23 17:40:58 +0000387 if (offset == 0) {
388 s->enabled = (value & 1);
389 DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
390 } else if (offset < 4) {
391 /* ignored. */
392 } else {
393 goto bad_reg;
394 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000395#endif
pbrooke69954b2006-09-23 17:40:58 +0000396 } else if (offset < 0x180) {
397 /* Interrupt Set Enable. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000398 irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000399 if (irq >= GIC_NIRQ)
400 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000401 if (irq < 16)
402 value = 0xff;
pbrooke69954b2006-09-23 17:40:58 +0000403 for (i = 0; i < 8; i++) {
404 if (value & (1 << i)) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000405 int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
pbrooke69954b2006-09-23 17:40:58 +0000406 if (!GIC_TEST_ENABLED(irq + i))
407 DPRINTF("Enabled IRQ %d\n", irq + i);
408 GIC_SET_ENABLED(irq + i);
409 /* If a raised level triggered IRQ enabled then mark
410 is as pending. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000411 if (GIC_TEST_LEVEL(irq + i, mask)
412 && !GIC_TEST_TRIGGER(irq + i)) {
413 DPRINTF("Set %d pending mask %x\n", irq + i, mask);
414 GIC_SET_PENDING(irq + i, mask);
415 }
pbrooke69954b2006-09-23 17:40:58 +0000416 }
417 }
418 } else if (offset < 0x200) {
419 /* Interrupt Clear Enable. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000420 irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000421 if (irq >= GIC_NIRQ)
422 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000423 if (irq < 16)
424 value = 0;
pbrooke69954b2006-09-23 17:40:58 +0000425 for (i = 0; i < 8; i++) {
426 if (value & (1 << i)) {
427 if (GIC_TEST_ENABLED(irq + i))
428 DPRINTF("Disabled IRQ %d\n", irq + i);
429 GIC_CLEAR_ENABLED(irq + i);
430 }
431 }
432 } else if (offset < 0x280) {
433 /* Interrupt Set Pending. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000434 irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000435 if (irq >= GIC_NIRQ)
436 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000437 if (irq < 16)
438 irq = 0;
439
pbrooke69954b2006-09-23 17:40:58 +0000440 for (i = 0; i < 8; i++) {
441 if (value & (1 << i)) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000442 GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
pbrooke69954b2006-09-23 17:40:58 +0000443 }
444 }
445 } else if (offset < 0x300) {
446 /* Interrupt Clear Pending. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000447 irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000448 if (irq >= GIC_NIRQ)
449 goto bad_reg;
450 for (i = 0; i < 8; i++) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000451 /* ??? This currently clears the pending bit for all CPUs, even
452 for per-CPU interrupts. It's unclear whether this is the
453 corect behavior. */
pbrooke69954b2006-09-23 17:40:58 +0000454 if (value & (1 << i)) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000455 GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
pbrooke69954b2006-09-23 17:40:58 +0000456 }
457 }
458 } else if (offset < 0x400) {
459 /* Interrupt Active. */
460 goto bad_reg;
461 } else if (offset < 0x800) {
462 /* Interrupt Priority. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000463 irq = (offset - 0x400) + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000464 if (irq >= GIC_NIRQ)
465 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000466 if (irq < 32) {
467 s->priority1[irq][cpu] = value;
468 } else {
469 s->priority2[irq - 32] = value;
470 }
471#ifndef NVIC
pbrooke69954b2006-09-23 17:40:58 +0000472 } else if (offset < 0xc00) {
473 /* Interrupt CPU Target. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000474 irq = (offset - 0x800) + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000475 if (irq >= GIC_NIRQ)
476 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000477 if (irq < 29)
478 value = 0;
479 else if (irq < 32)
480 value = ALL_CPU_MASK;
481 s->irq_target[irq] = value & ALL_CPU_MASK;
pbrooke69954b2006-09-23 17:40:58 +0000482 } else if (offset < 0xf00) {
483 /* Interrupt Configuration. */
pbrook9ee6e8b2007-11-11 00:04:49 +0000484 irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
pbrooke69954b2006-09-23 17:40:58 +0000485 if (irq >= GIC_NIRQ)
486 goto bad_reg;
pbrook9ee6e8b2007-11-11 00:04:49 +0000487 if (irq < 32)
488 value |= 0xaa;
pbrooke69954b2006-09-23 17:40:58 +0000489 for (i = 0; i < 4; i++) {
490 if (value & (1 << (i * 2))) {
491 GIC_SET_MODEL(irq + i);
492 } else {
493 GIC_CLEAR_MODEL(irq + i);
494 }
495 if (value & (2 << (i * 2))) {
496 GIC_SET_TRIGGER(irq + i);
497 } else {
498 GIC_CLEAR_TRIGGER(irq + i);
499 }
500 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000501#endif
pbrooke69954b2006-09-23 17:40:58 +0000502 } else {
pbrook9ee6e8b2007-11-11 00:04:49 +0000503 /* 0xf00 is only handled for 32-bit writes. */
pbrooke69954b2006-09-23 17:40:58 +0000504 goto bad_reg;
505 }
506 gic_update(s);
507 return;
508bad_reg:
Paul Brook2ac71172009-05-08 02:35:15 +0100509 hw_error("gic_dist_writeb: Bad offset %x\n", (int)offset);
pbrooke69954b2006-09-23 17:40:58 +0000510}
511
512static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
513 uint32_t value)
514{
pbrooke69954b2006-09-23 17:40:58 +0000515 gic_dist_writeb(opaque, offset, value & 0xff);
516 gic_dist_writeb(opaque, offset + 1, value >> 8);
517}
518
519static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
520 uint32_t value)
521{
pbrook9ee6e8b2007-11-11 00:04:49 +0000522 gic_state *s = (gic_state *)opaque;
523#ifdef NVIC
524 uint32_t addr;
pbrook8da3ff12008-12-01 18:59:50 +0000525 addr = offset;
pbrook9ee6e8b2007-11-11 00:04:49 +0000526 if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
Paul Brookfe7e8752009-05-14 22:35:08 +0100527 nvic_writel(s, addr, value);
pbrook9ee6e8b2007-11-11 00:04:49 +0000528 return;
529 }
530#endif
pbrook8da3ff12008-12-01 18:59:50 +0000531 if (offset == 0xf00) {
pbrook9ee6e8b2007-11-11 00:04:49 +0000532 int cpu;
533 int irq;
534 int mask;
535
536 cpu = gic_get_current_cpu();
537 irq = value & 0x3ff;
538 switch ((value >> 24) & 3) {
539 case 0:
540 mask = (value >> 16) & ALL_CPU_MASK;
541 break;
542 case 1:
543 mask = 1 << cpu;
544 break;
545 case 2:
546 mask = ALL_CPU_MASK ^ (1 << cpu);
547 break;
548 default:
549 DPRINTF("Bad Soft Int target filter\n");
550 mask = ALL_CPU_MASK;
551 break;
552 }
553 GIC_SET_PENDING(irq, mask);
554 gic_update(s);
555 return;
556 }
pbrooke69954b2006-09-23 17:40:58 +0000557 gic_dist_writew(opaque, offset, value & 0xffff);
558 gic_dist_writew(opaque, offset + 2, value >> 16);
559}
560
561static CPUReadMemoryFunc *gic_dist_readfn[] = {
562 gic_dist_readb,
563 gic_dist_readw,
564 gic_dist_readl
565};
566
567static CPUWriteMemoryFunc *gic_dist_writefn[] = {
568 gic_dist_writeb,
569 gic_dist_writew,
570 gic_dist_writel
571};
572
pbrook9ee6e8b2007-11-11 00:04:49 +0000573#ifndef NVIC
574static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
pbrooke69954b2006-09-23 17:40:58 +0000575{
pbrooke69954b2006-09-23 17:40:58 +0000576 switch (offset) {
577 case 0x00: /* Control */
pbrook9ee6e8b2007-11-11 00:04:49 +0000578 return s->cpu_enabled[cpu];
pbrooke69954b2006-09-23 17:40:58 +0000579 case 0x04: /* Priority mask */
pbrook9ee6e8b2007-11-11 00:04:49 +0000580 return s->priority_mask[cpu];
pbrooke69954b2006-09-23 17:40:58 +0000581 case 0x08: /* Binary Point */
582 /* ??? Not implemented. */
583 return 0;
584 case 0x0c: /* Acknowledge */
pbrook9ee6e8b2007-11-11 00:04:49 +0000585 return gic_acknowledge_irq(s, cpu);
pbrooke69954b2006-09-23 17:40:58 +0000586 case 0x14: /* Runing Priority */
pbrook9ee6e8b2007-11-11 00:04:49 +0000587 return s->running_priority[cpu];
pbrooke69954b2006-09-23 17:40:58 +0000588 case 0x18: /* Highest Pending Interrupt */
pbrook9ee6e8b2007-11-11 00:04:49 +0000589 return s->current_pending[cpu];
pbrooke69954b2006-09-23 17:40:58 +0000590 default:
Paul Brook2ac71172009-05-08 02:35:15 +0100591 hw_error("gic_cpu_read: Bad offset %x\n", (int)offset);
pbrooke69954b2006-09-23 17:40:58 +0000592 return 0;
593 }
594}
595
pbrook9ee6e8b2007-11-11 00:04:49 +0000596static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
pbrooke69954b2006-09-23 17:40:58 +0000597{
pbrooke69954b2006-09-23 17:40:58 +0000598 switch (offset) {
599 case 0x00: /* Control */
pbrook9ee6e8b2007-11-11 00:04:49 +0000600 s->cpu_enabled[cpu] = (value & 1);
pbrooke69954b2006-09-23 17:40:58 +0000601 DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
602 break;
603 case 0x04: /* Priority mask */
pbrook9ee6e8b2007-11-11 00:04:49 +0000604 s->priority_mask[cpu] = (value & 0xff);
pbrooke69954b2006-09-23 17:40:58 +0000605 break;
606 case 0x08: /* Binary Point */
607 /* ??? Not implemented. */
608 break;
609 case 0x10: /* End Of Interrupt */
pbrook9ee6e8b2007-11-11 00:04:49 +0000610 return gic_complete_irq(s, cpu, value & 0x3ff);
pbrooke69954b2006-09-23 17:40:58 +0000611 default:
Paul Brook2ac71172009-05-08 02:35:15 +0100612 hw_error("gic_cpu_write: Bad offset %x\n", (int)offset);
pbrooke69954b2006-09-23 17:40:58 +0000613 return;
614 }
615 gic_update(s);
616}
pbrook9ee6e8b2007-11-11 00:04:49 +0000617#endif
pbrooke69954b2006-09-23 17:40:58 +0000618
619static void gic_reset(gic_state *s)
620{
621 int i;
622 memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
pbrook9ee6e8b2007-11-11 00:04:49 +0000623 for (i = 0 ; i < NCPU; i++) {
624 s->priority_mask[i] = 0xf0;
625 s->current_pending[i] = 1023;
626 s->running_irq[i] = 1023;
627 s->running_priority[i] = 0x100;
628#ifdef NVIC
629 /* The NVIC doesn't have per-cpu interfaces, so enable by default. */
630 s->cpu_enabled[i] = 1;
631#else
632 s->cpu_enabled[i] = 0;
633#endif
634 }
pbrooke57ec012007-11-24 03:09:07 +0000635 for (i = 0; i < 16; i++) {
pbrooke69954b2006-09-23 17:40:58 +0000636 GIC_SET_ENABLED(i);
637 GIC_SET_TRIGGER(i);
638 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000639#ifdef NVIC
640 /* The NVIC is always enabled. */
641 s->enabled = 1;
642#else
pbrooke69954b2006-09-23 17:40:58 +0000643 s->enabled = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000644#endif
pbrooke69954b2006-09-23 17:40:58 +0000645}
646
pbrook23e39292008-07-02 16:48:32 +0000647static void gic_save(QEMUFile *f, void *opaque)
648{
649 gic_state *s = (gic_state *)opaque;
650 int i;
651 int j;
652
653 qemu_put_be32(f, s->enabled);
654 for (i = 0; i < NCPU; i++) {
655 qemu_put_be32(f, s->cpu_enabled[i]);
656#ifndef NVIC
657 qemu_put_be32(f, s->irq_target[i]);
658#endif
659 for (j = 0; j < 32; j++)
660 qemu_put_be32(f, s->priority1[j][i]);
661 for (j = 0; j < GIC_NIRQ; j++)
662 qemu_put_be32(f, s->last_active[j][i]);
663 qemu_put_be32(f, s->priority_mask[i]);
664 qemu_put_be32(f, s->running_irq[i]);
665 qemu_put_be32(f, s->running_priority[i]);
666 qemu_put_be32(f, s->current_pending[i]);
667 }
668 for (i = 0; i < GIC_NIRQ - 32; i++) {
669 qemu_put_be32(f, s->priority2[i]);
670 }
671 for (i = 0; i < GIC_NIRQ; i++) {
672 qemu_put_byte(f, s->irq_state[i].enabled);
673 qemu_put_byte(f, s->irq_state[i].pending);
674 qemu_put_byte(f, s->irq_state[i].active);
675 qemu_put_byte(f, s->irq_state[i].level);
676 qemu_put_byte(f, s->irq_state[i].model);
677 qemu_put_byte(f, s->irq_state[i].trigger);
678 }
679}
680
681static int gic_load(QEMUFile *f, void *opaque, int version_id)
682{
683 gic_state *s = (gic_state *)opaque;
684 int i;
685 int j;
686
687 if (version_id != 1)
688 return -EINVAL;
689
690 s->enabled = qemu_get_be32(f);
691 for (i = 0; i < NCPU; i++) {
692 s->cpu_enabled[i] = qemu_get_be32(f);
693#ifndef NVIC
694 s->irq_target[i] = qemu_get_be32(f);
695#endif
696 for (j = 0; j < 32; j++)
697 s->priority1[j][i] = qemu_get_be32(f);
698 for (j = 0; j < GIC_NIRQ; j++)
699 s->last_active[j][i] = qemu_get_be32(f);
700 s->priority_mask[i] = qemu_get_be32(f);
701 s->running_irq[i] = qemu_get_be32(f);
702 s->running_priority[i] = qemu_get_be32(f);
703 s->current_pending[i] = qemu_get_be32(f);
704 }
705 for (i = 0; i < GIC_NIRQ - 32; i++) {
706 s->priority2[i] = qemu_get_be32(f);
707 }
708 for (i = 0; i < GIC_NIRQ; i++) {
709 s->irq_state[i].enabled = qemu_get_byte(f);
710 s->irq_state[i].pending = qemu_get_byte(f);
711 s->irq_state[i].active = qemu_get_byte(f);
712 s->irq_state[i].level = qemu_get_byte(f);
713 s->irq_state[i].model = qemu_get_byte(f);
714 s->irq_state[i].trigger = qemu_get_byte(f);
715 }
716
717 return 0;
718}
719
Paul Brookfe7e8752009-05-14 22:35:08 +0100720static void gic_init(gic_state *s)
pbrooke69954b2006-09-23 17:40:58 +0000721{
pbrook9ee6e8b2007-11-11 00:04:49 +0000722 int i;
pbrooke69954b2006-09-23 17:40:58 +0000723
Paul Brook067a3dd2009-05-26 14:56:11 +0100724 qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, GIC_NIRQ - 32);
pbrook9ee6e8b2007-11-11 00:04:49 +0000725 for (i = 0; i < NCPU; i++) {
Paul Brookfe7e8752009-05-14 22:35:08 +0100726 sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
pbrooke69954b2006-09-23 17:40:58 +0000727 }
Avi Kivity1eed09c2009-06-14 11:38:51 +0300728 s->iomemtype = cpu_register_io_memory(gic_dist_readfn,
Paul Brookfe7e8752009-05-14 22:35:08 +0100729 gic_dist_writefn, s);
pbrooke69954b2006-09-23 17:40:58 +0000730 gic_reset(s);
pbrook23e39292008-07-02 16:48:32 +0000731 register_savevm("arm_gic", -1, 1, gic_save, gic_load, s);
pbrooke69954b2006-09-23 17:40:58 +0000732}