root / hw / imx_avic.c @ a8170e5e
History | View | Annotate | Download (11.7 kB)
1 |
/*
|
---|---|
2 |
* i.MX31 Vectored Interrupt Controller
|
3 |
*
|
4 |
* Note this is NOT the PL192 provided by ARM, but
|
5 |
* a custom implementation by Freescale.
|
6 |
*
|
7 |
* Copyright (c) 2008 OKL
|
8 |
* Copyright (c) 2011 NICTA Pty Ltd
|
9 |
* Originally written by Hans Jiang
|
10 |
*
|
11 |
* This code is licensed under the GPL version 2 or later. See
|
12 |
* the COPYING file in the top-level directory.
|
13 |
*
|
14 |
* TODO: implement vectors.
|
15 |
*/
|
16 |
|
17 |
#include "hw.h" |
18 |
#include "sysbus.h" |
19 |
#include "host-utils.h" |
20 |
|
21 |
#define DEBUG_INT 1 |
22 |
#undef DEBUG_INT /* comment out for debugging */ |
23 |
|
24 |
#ifdef DEBUG_INT
|
25 |
#define DPRINTF(fmt, args...) \
|
26 |
do { printf("imx_avic: " fmt , ##args); } while (0) |
27 |
#else
|
28 |
#define DPRINTF(fmt, args...) do {} while (0) |
29 |
#endif
|
30 |
|
31 |
/*
|
32 |
* Define to 1 for messages about attempts to
|
33 |
* access unimplemented registers or similar.
|
34 |
*/
|
35 |
#define DEBUG_IMPLEMENTATION 1 |
36 |
#if DEBUG_IMPLEMENTATION
|
37 |
# define IPRINTF(fmt, args...) \
|
38 |
do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0) |
39 |
#else
|
40 |
# define IPRINTF(fmt, args...) do {} while (0) |
41 |
#endif
|
42 |
|
43 |
#define IMX_AVIC_NUM_IRQS 64 |
44 |
|
45 |
/* Interrupt Control Bits */
|
46 |
#define ABFLAG (1<<25) |
47 |
#define ABFEN (1<<24) |
48 |
#define NIDIS (1<<22) /* Normal Interrupt disable */ |
49 |
#define FIDIS (1<<21) /* Fast interrupt disable */ |
50 |
#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */ |
51 |
#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */ |
52 |
#define NM (1<<18) /* Normal interrupt mode */ |
53 |
|
54 |
|
55 |
#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4) |
56 |
#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD)
|
57 |
|
58 |
typedef struct { |
59 |
SysBusDevice busdev; |
60 |
MemoryRegion iomem; |
61 |
uint64_t pending; |
62 |
uint64_t enabled; |
63 |
uint64_t is_fiq; |
64 |
uint32_t intcntl; |
65 |
uint32_t intmask; |
66 |
qemu_irq irq; |
67 |
qemu_irq fiq; |
68 |
uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */
|
69 |
} IMXAVICState; |
70 |
|
71 |
static const VMStateDescription vmstate_imx_avic = { |
72 |
.name = "imx-avic",
|
73 |
.version_id = 1,
|
74 |
.minimum_version_id = 1,
|
75 |
.minimum_version_id_old = 1,
|
76 |
.fields = (VMStateField[]) { |
77 |
VMSTATE_UINT64(pending, IMXAVICState), |
78 |
VMSTATE_UINT64(enabled, IMXAVICState), |
79 |
VMSTATE_UINT64(is_fiq, IMXAVICState), |
80 |
VMSTATE_UINT32(intcntl, IMXAVICState), |
81 |
VMSTATE_UINT32(intmask, IMXAVICState), |
82 |
VMSTATE_UINT32_ARRAY(prio, IMXAVICState, PRIO_WORDS), |
83 |
VMSTATE_END_OF_LIST() |
84 |
}, |
85 |
}; |
86 |
|
87 |
|
88 |
|
89 |
static inline int imx_avic_prio(IMXAVICState *s, int irq) |
90 |
{ |
91 |
uint32_t word = irq / PRIO_PER_WORD; |
92 |
uint32_t part = 4 * (irq % PRIO_PER_WORD);
|
93 |
return 0xf & (s->prio[word] >> part); |
94 |
} |
95 |
|
96 |
static inline void imx_avic_set_prio(IMXAVICState *s, int irq, int prio) |
97 |
{ |
98 |
uint32_t word = irq / PRIO_PER_WORD; |
99 |
uint32_t part = 4 * (irq % PRIO_PER_WORD);
|
100 |
uint32_t mask = ~(0xf << part);
|
101 |
s->prio[word] &= mask; |
102 |
s->prio[word] |= prio << part; |
103 |
} |
104 |
|
105 |
/* Update interrupts. */
|
106 |
static void imx_avic_update(IMXAVICState *s) |
107 |
{ |
108 |
int i;
|
109 |
uint64_t new = s->pending & s->enabled; |
110 |
uint64_t flags; |
111 |
|
112 |
flags = new & s->is_fiq; |
113 |
qemu_set_irq(s->fiq, !!flags); |
114 |
|
115 |
flags = new & ~s->is_fiq; |
116 |
if (!flags || (s->intmask == 0x1f)) { |
117 |
qemu_set_irq(s->irq, !!flags); |
118 |
return;
|
119 |
} |
120 |
|
121 |
/*
|
122 |
* Take interrupt if there's a pending interrupt with
|
123 |
* priority higher than the value of intmask
|
124 |
*/
|
125 |
for (i = 0; i < IMX_AVIC_NUM_IRQS; i++) { |
126 |
if (flags & (1UL << i)) { |
127 |
if (imx_avic_prio(s, i) > s->intmask) {
|
128 |
qemu_set_irq(s->irq, 1);
|
129 |
return;
|
130 |
} |
131 |
} |
132 |
} |
133 |
qemu_set_irq(s->irq, 0);
|
134 |
} |
135 |
|
136 |
static void imx_avic_set_irq(void *opaque, int irq, int level) |
137 |
{ |
138 |
IMXAVICState *s = (IMXAVICState *)opaque; |
139 |
|
140 |
if (level) {
|
141 |
DPRINTF("Raising IRQ %d, prio %d\n",
|
142 |
irq, imx_avic_prio(s, irq)); |
143 |
s->pending |= (1ULL << irq);
|
144 |
} else {
|
145 |
DPRINTF("Clearing IRQ %d, prio %d\n",
|
146 |
irq, imx_avic_prio(s, irq)); |
147 |
s->pending &= ~(1ULL << irq);
|
148 |
} |
149 |
|
150 |
imx_avic_update(s); |
151 |
} |
152 |
|
153 |
|
154 |
static uint64_t imx_avic_read(void *opaque, |
155 |
hwaddr offset, unsigned size)
|
156 |
{ |
157 |
IMXAVICState *s = (IMXAVICState *)opaque; |
158 |
|
159 |
|
160 |
DPRINTF("read(offset = 0x%x)\n", offset >> 2); |
161 |
switch (offset >> 2) { |
162 |
case 0: /* INTCNTL */ |
163 |
return s->intcntl;
|
164 |
|
165 |
case 1: /* Normal Interrupt Mask Register, NIMASK */ |
166 |
return s->intmask;
|
167 |
|
168 |
case 2: /* Interrupt Enable Number Register, INTENNUM */ |
169 |
case 3: /* Interrupt Disable Number Register, INTDISNUM */ |
170 |
return 0; |
171 |
|
172 |
case 4: /* Interrupt Enabled Number Register High */ |
173 |
return s->enabled >> 32; |
174 |
|
175 |
case 5: /* Interrupt Enabled Number Register Low */ |
176 |
return s->enabled & 0xffffffffULL; |
177 |
|
178 |
case 6: /* Interrupt Type Register High */ |
179 |
return s->is_fiq >> 32; |
180 |
|
181 |
case 7: /* Interrupt Type Register Low */ |
182 |
return s->is_fiq & 0xffffffffULL; |
183 |
|
184 |
case 8: /* Normal Interrupt Priority Register 7 */ |
185 |
case 9: /* Normal Interrupt Priority Register 6 */ |
186 |
case 10:/* Normal Interrupt Priority Register 5 */ |
187 |
case 11:/* Normal Interrupt Priority Register 4 */ |
188 |
case 12:/* Normal Interrupt Priority Register 3 */ |
189 |
case 13:/* Normal Interrupt Priority Register 2 */ |
190 |
case 14:/* Normal Interrupt Priority Register 1 */ |
191 |
case 15:/* Normal Interrupt Priority Register 0 */ |
192 |
return s->prio[15-(offset>>2)]; |
193 |
|
194 |
case 16: /* Normal interrupt vector and status register */ |
195 |
{ |
196 |
/*
|
197 |
* This returns the highest priority
|
198 |
* outstanding interrupt. Where there is more than
|
199 |
* one pending IRQ with the same priority,
|
200 |
* take the highest numbered one.
|
201 |
*/
|
202 |
uint64_t flags = s->pending & s->enabled & ~s->is_fiq; |
203 |
int i;
|
204 |
int prio = -1; |
205 |
int irq = -1; |
206 |
for (i = 63; i >= 0; --i) { |
207 |
if (flags & (1ULL<<i)) { |
208 |
int irq_prio = imx_avic_prio(s, i);
|
209 |
if (irq_prio > prio) {
|
210 |
irq = i; |
211 |
prio = irq_prio; |
212 |
} |
213 |
} |
214 |
} |
215 |
if (irq >= 0) { |
216 |
imx_avic_set_irq(s, irq, 0);
|
217 |
return irq << 16 | prio; |
218 |
} |
219 |
return 0xffffffffULL; |
220 |
} |
221 |
case 17:/* Fast Interrupt vector and status register */ |
222 |
{ |
223 |
uint64_t flags = s->pending & s->enabled & s->is_fiq; |
224 |
int i = ctz64(flags);
|
225 |
if (i < 64) { |
226 |
imx_avic_set_irq(opaque, i, 0);
|
227 |
return i;
|
228 |
} |
229 |
return 0xffffffffULL; |
230 |
} |
231 |
case 18:/* Interrupt source register high */ |
232 |
return s->pending >> 32; |
233 |
|
234 |
case 19:/* Interrupt source register low */ |
235 |
return s->pending & 0xffffffffULL; |
236 |
|
237 |
case 20:/* Interrupt Force Register high */ |
238 |
case 21:/* Interrupt Force Register low */ |
239 |
return 0; |
240 |
|
241 |
case 22:/* Normal Interrupt Pending Register High */ |
242 |
return (s->pending & s->enabled & ~s->is_fiq) >> 32; |
243 |
|
244 |
case 23:/* Normal Interrupt Pending Register Low */ |
245 |
return (s->pending & s->enabled & ~s->is_fiq) & 0xffffffffULL; |
246 |
|
247 |
case 24: /* Fast Interrupt Pending Register High */ |
248 |
return (s->pending & s->enabled & s->is_fiq) >> 32; |
249 |
|
250 |
case 25: /* Fast Interrupt Pending Register Low */ |
251 |
return (s->pending & s->enabled & s->is_fiq) & 0xffffffffULL; |
252 |
|
253 |
case 0x40: /* AVIC vector 0, use for WFI WAR */ |
254 |
return 0x4; |
255 |
|
256 |
default:
|
257 |
IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset); |
258 |
return 0; |
259 |
} |
260 |
} |
261 |
|
262 |
static void imx_avic_write(void *opaque, hwaddr offset, |
263 |
uint64_t val, unsigned size)
|
264 |
{ |
265 |
IMXAVICState *s = (IMXAVICState *)opaque; |
266 |
|
267 |
/* Vector Registers not yet supported */
|
268 |
if (offset >= 0x100 && offset <= 0x2fc) { |
269 |
IPRINTF("imx_avic_write to vector register %d ignored\n",
|
270 |
(unsigned int)((offset - 0x100) >> 2)); |
271 |
return;
|
272 |
} |
273 |
|
274 |
DPRINTF("imx_avic_write(0x%x) = %x\n",
|
275 |
(unsigned int)offset>>2, (unsigned int)val); |
276 |
switch (offset >> 2) { |
277 |
case 0: /* Interrupt Control Register, INTCNTL */ |
278 |
s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM); |
279 |
if (s->intcntl & ABFEN) {
|
280 |
s->intcntl &= ~(val & ABFLAG); |
281 |
} |
282 |
break;
|
283 |
|
284 |
case 1: /* Normal Interrupt Mask Register, NIMASK */ |
285 |
s->intmask = val & 0x1f;
|
286 |
break;
|
287 |
|
288 |
case 2: /* Interrupt Enable Number Register, INTENNUM */ |
289 |
DPRINTF("enable(%d)\n", (int)val); |
290 |
val &= 0x3f;
|
291 |
s->enabled |= (1ULL << val);
|
292 |
break;
|
293 |
|
294 |
case 3: /* Interrupt Disable Number Register, INTDISNUM */ |
295 |
DPRINTF("disable(%d)\n", (int)val); |
296 |
val &= 0x3f;
|
297 |
s->enabled &= ~(1ULL << val);
|
298 |
break;
|
299 |
|
300 |
case 4: /* Interrupt Enable Number Register High */ |
301 |
s->enabled = (s->enabled & 0xffffffffULL) | (val << 32); |
302 |
break;
|
303 |
|
304 |
case 5: /* Interrupt Enable Number Register Low */ |
305 |
s->enabled = (s->enabled & 0xffffffff00000000ULL) | val;
|
306 |
break;
|
307 |
|
308 |
case 6: /* Interrupt Type Register High */ |
309 |
s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32); |
310 |
break;
|
311 |
|
312 |
case 7: /* Interrupt Type Register Low */ |
313 |
s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val;
|
314 |
break;
|
315 |
|
316 |
case 8: /* Normal Interrupt Priority Register 7 */ |
317 |
case 9: /* Normal Interrupt Priority Register 6 */ |
318 |
case 10:/* Normal Interrupt Priority Register 5 */ |
319 |
case 11:/* Normal Interrupt Priority Register 4 */ |
320 |
case 12:/* Normal Interrupt Priority Register 3 */ |
321 |
case 13:/* Normal Interrupt Priority Register 2 */ |
322 |
case 14:/* Normal Interrupt Priority Register 1 */ |
323 |
case 15:/* Normal Interrupt Priority Register 0 */ |
324 |
s->prio[15-(offset>>2)] = val; |
325 |
break;
|
326 |
|
327 |
/* Read-only registers, writes ignored */
|
328 |
case 16:/* Normal Interrupt Vector and Status register */ |
329 |
case 17:/* Fast Interrupt vector and status register */ |
330 |
case 18:/* Interrupt source register high */ |
331 |
case 19:/* Interrupt source register low */ |
332 |
return;
|
333 |
|
334 |
case 20:/* Interrupt Force Register high */ |
335 |
s->pending = (s->pending & 0xffffffffULL) | (val << 32); |
336 |
break;
|
337 |
|
338 |
case 21:/* Interrupt Force Register low */ |
339 |
s->pending = (s->pending & 0xffffffff00000000ULL) | val;
|
340 |
break;
|
341 |
|
342 |
case 22:/* Normal Interrupt Pending Register High */ |
343 |
case 23:/* Normal Interrupt Pending Register Low */ |
344 |
case 24: /* Fast Interrupt Pending Register High */ |
345 |
case 25: /* Fast Interrupt Pending Register Low */ |
346 |
return;
|
347 |
|
348 |
default:
|
349 |
IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset); |
350 |
} |
351 |
imx_avic_update(s); |
352 |
} |
353 |
|
354 |
static const MemoryRegionOps imx_avic_ops = { |
355 |
.read = imx_avic_read, |
356 |
.write = imx_avic_write, |
357 |
.endianness = DEVICE_NATIVE_ENDIAN, |
358 |
}; |
359 |
|
360 |
static void imx_avic_reset(DeviceState *dev) |
361 |
{ |
362 |
IMXAVICState *s = container_of(dev, IMXAVICState, busdev.qdev); |
363 |
s->pending = 0;
|
364 |
s->enabled = 0;
|
365 |
s->is_fiq = 0;
|
366 |
s->intmask = 0x1f;
|
367 |
s->intcntl = 0;
|
368 |
memset(s->prio, 0, sizeof s->prio); |
369 |
} |
370 |
|
371 |
static int imx_avic_init(SysBusDevice *dev) |
372 |
{ |
373 |
IMXAVICState *s = FROM_SYSBUS(IMXAVICState, dev);; |
374 |
|
375 |
memory_region_init_io(&s->iomem, &imx_avic_ops, s, "imx_avic", 0x1000); |
376 |
sysbus_init_mmio(dev, &s->iomem); |
377 |
|
378 |
qdev_init_gpio_in(&dev->qdev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS); |
379 |
sysbus_init_irq(dev, &s->irq); |
380 |
sysbus_init_irq(dev, &s->fiq); |
381 |
|
382 |
return 0; |
383 |
} |
384 |
|
385 |
|
386 |
static void imx_avic_class_init(ObjectClass *klass, void *data) |
387 |
{ |
388 |
DeviceClass *dc = DEVICE_CLASS(klass); |
389 |
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
390 |
k->init = imx_avic_init; |
391 |
dc->vmsd = &vmstate_imx_avic; |
392 |
dc->reset = imx_avic_reset; |
393 |
dc->desc = "i.MX Advanced Vector Interrupt Controller";
|
394 |
} |
395 |
|
396 |
static const TypeInfo imx_avic_info = { |
397 |
.name = "imx_avic",
|
398 |
.parent = TYPE_SYS_BUS_DEVICE, |
399 |
.instance_size = sizeof(IMXAVICState),
|
400 |
.class_init = imx_avic_class_init, |
401 |
}; |
402 |
|
403 |
static void imx_avic_register_types(void) |
404 |
{ |
405 |
type_register_static(&imx_avic_info); |
406 |
} |
407 |
|
408 |
type_init(imx_avic_register_types) |