1 | /* |
2 | * TI OMAP interrupt controller emulation. |
3 | * |
4 | * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org> |
5 | * Copyright (C) 2007-2008 Nokia Corporation |
6 | * |
7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License as |
9 | * published by the Free Software Foundation; either version 2 or |
10 | * (at your option) version 3 of the License. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "qemu/osdep.h" |
22 | #include "hw/irq.h" |
23 | #include "hw/qdev-properties.h" |
24 | #include "hw/arm/omap.h" |
25 | #include "hw/sysbus.h" |
26 | #include "qemu/error-report.h" |
27 | #include "qemu/module.h" |
28 | #include "qapi/error.h" |
29 | |
30 | /* Interrupt Handlers */ |
31 | struct omap_intr_handler_bank_s { |
32 | uint32_t irqs; |
33 | uint32_t inputs; |
34 | uint32_t mask; |
35 | uint32_t fiq; |
36 | uint32_t sens_edge; |
37 | uint32_t swi; |
38 | unsigned char priority[32]; |
39 | }; |
40 | |
41 | #define TYPE_OMAP_INTC "common-omap-intc" |
42 | #define OMAP_INTC(obj) \ |
43 | OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC) |
44 | |
45 | struct omap_intr_handler_s { |
46 | SysBusDevice parent_obj; |
47 | |
48 | qemu_irq *pins; |
49 | qemu_irq parent_intr[2]; |
50 | MemoryRegion mmio; |
51 | void *iclk; |
52 | void *fclk; |
53 | unsigned char nbanks; |
54 | int level_only; |
55 | uint32_t size; |
56 | |
57 | uint8_t revision; |
58 | |
59 | /* state */ |
60 | uint32_t new_agr[2]; |
61 | int sir_intr[2]; |
62 | int autoidle; |
63 | uint32_t mask; |
64 | struct omap_intr_handler_bank_s bank[3]; |
65 | }; |
66 | |
67 | static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) |
68 | { |
69 | int i, j, sir_intr, p_intr, p; |
70 | uint32_t level; |
71 | sir_intr = 0; |
72 | p_intr = 255; |
73 | |
74 | /* Find the interrupt line with the highest dynamic priority. |
75 | * Note: 0 denotes the hightest priority. |
76 | * If all interrupts have the same priority, the default order is IRQ_N, |
77 | * IRQ_N-1,...,IRQ_0. */ |
78 | for (j = 0; j < s->nbanks; ++j) { |
79 | level = s->bank[j].irqs & ~s->bank[j].mask & |
80 | (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); |
81 | |
82 | while (level != 0) { |
83 | i = ctz32(level); |
84 | p = s->bank[j].priority[i]; |
85 | if (p <= p_intr) { |
86 | p_intr = p; |
87 | sir_intr = 32 * j + i; |
88 | } |
89 | level &= level - 1; |
90 | } |
91 | } |
92 | s->sir_intr[is_fiq] = sir_intr; |
93 | } |
94 | |
95 | static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) |
96 | { |
97 | int i; |
98 | uint32_t has_intr = 0; |
99 | |
100 | for (i = 0; i < s->nbanks; ++i) |
101 | has_intr |= s->bank[i].irqs & ~s->bank[i].mask & |
102 | (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); |
103 | |
104 | if (s->new_agr[is_fiq] & has_intr & s->mask) { |
105 | s->new_agr[is_fiq] = 0; |
106 | omap_inth_sir_update(s, is_fiq); |
107 | qemu_set_irq(s->parent_intr[is_fiq], 1); |
108 | } |
109 | } |
110 | |
111 | #define INT_FALLING_EDGE 0 |
112 | #define INT_LOW_LEVEL 1 |
113 | |
114 | static void omap_set_intr(void *opaque, int irq, int req) |
115 | { |
116 | struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; |
117 | uint32_t rise; |
118 | |
119 | struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; |
120 | int n = irq & 31; |
121 | |
122 | if (req) { |
123 | rise = ~bank->irqs & (1 << n); |
124 | if (~bank->sens_edge & (1 << n)) |
125 | rise &= ~bank->inputs; |
126 | |
127 | bank->inputs |= (1 << n); |
128 | if (rise) { |
129 | bank->irqs |= rise; |
130 | omap_inth_update(ih, 0); |
131 | omap_inth_update(ih, 1); |
132 | } |
133 | } else { |
134 | rise = bank->sens_edge & bank->irqs & (1 << n); |
135 | bank->irqs &= ~rise; |
136 | bank->inputs &= ~(1 << n); |
137 | } |
138 | } |
139 | |
140 | /* Simplified version with no edge detection */ |
141 | static void omap_set_intr_noedge(void *opaque, int irq, int req) |
142 | { |
143 | struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; |
144 | uint32_t rise; |
145 | |
146 | struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; |
147 | int n = irq & 31; |
148 | |
149 | if (req) { |
150 | rise = ~bank->inputs & (1 << n); |
151 | if (rise) { |
152 | bank->irqs |= bank->inputs |= rise; |
153 | omap_inth_update(ih, 0); |
154 | omap_inth_update(ih, 1); |
155 | } |
156 | } else |
157 | bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; |
158 | } |
159 | |
160 | static uint64_t omap_inth_read(void *opaque, hwaddr addr, |
161 | unsigned size) |
162 | { |
163 | struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; |
164 | int i, offset = addr; |
165 | int bank_no = offset >> 8; |
166 | int line_no; |
167 | struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; |
168 | offset &= 0xff; |
169 | |
170 | switch (offset) { |
171 | case 0x00: /* ITR */ |
172 | return bank->irqs; |
173 | |
174 | case 0x04: /* MIR */ |
175 | return bank->mask; |
176 | |
177 | case 0x10: /* SIR_IRQ_CODE */ |
178 | case 0x14: /* SIR_FIQ_CODE */ |
179 | if (bank_no != 0) |
180 | break; |
181 | line_no = s->sir_intr[(offset - 0x10) >> 2]; |
182 | bank = &s->bank[line_no >> 5]; |
183 | i = line_no & 31; |
184 | if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) |
185 | bank->irqs &= ~(1 << i); |
186 | return line_no; |
187 | |
188 | case 0x18: /* CONTROL_REG */ |
189 | if (bank_no != 0) |
190 | break; |
191 | return 0; |
192 | |
193 | case 0x1c: /* ILR0 */ |
194 | case 0x20: /* ILR1 */ |
195 | case 0x24: /* ILR2 */ |
196 | case 0x28: /* ILR3 */ |
197 | case 0x2c: /* ILR4 */ |
198 | case 0x30: /* ILR5 */ |
199 | case 0x34: /* ILR6 */ |
200 | case 0x38: /* ILR7 */ |
201 | case 0x3c: /* ILR8 */ |
202 | case 0x40: /* ILR9 */ |
203 | case 0x44: /* ILR10 */ |
204 | case 0x48: /* ILR11 */ |
205 | case 0x4c: /* ILR12 */ |
206 | case 0x50: /* ILR13 */ |
207 | case 0x54: /* ILR14 */ |
208 | case 0x58: /* ILR15 */ |
209 | case 0x5c: /* ILR16 */ |
210 | case 0x60: /* ILR17 */ |
211 | case 0x64: /* ILR18 */ |
212 | case 0x68: /* ILR19 */ |
213 | case 0x6c: /* ILR20 */ |
214 | case 0x70: /* ILR21 */ |
215 | case 0x74: /* ILR22 */ |
216 | case 0x78: /* ILR23 */ |
217 | case 0x7c: /* ILR24 */ |
218 | case 0x80: /* ILR25 */ |
219 | case 0x84: /* ILR26 */ |
220 | case 0x88: /* ILR27 */ |
221 | case 0x8c: /* ILR28 */ |
222 | case 0x90: /* ILR29 */ |
223 | case 0x94: /* ILR30 */ |
224 | case 0x98: /* ILR31 */ |
225 | i = (offset - 0x1c) >> 2; |
226 | return (bank->priority[i] << 2) | |
227 | (((bank->sens_edge >> i) & 1) << 1) | |
228 | ((bank->fiq >> i) & 1); |
229 | |
230 | case 0x9c: /* ISR */ |
231 | return 0x00000000; |
232 | |
233 | } |
234 | OMAP_BAD_REG(addr); |
235 | return 0; |
236 | } |
237 | |
238 | static void omap_inth_write(void *opaque, hwaddr addr, |
239 | uint64_t value, unsigned size) |
240 | { |
241 | struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; |
242 | int i, offset = addr; |
243 | int bank_no = offset >> 8; |
244 | struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; |
245 | offset &= 0xff; |
246 | |
247 | switch (offset) { |
248 | case 0x00: /* ITR */ |
249 | /* Important: ignore the clearing if the IRQ is level-triggered and |
250 | the input bit is 1 */ |
251 | bank->irqs &= value | (bank->inputs & bank->sens_edge); |
252 | return; |
253 | |
254 | case 0x04: /* MIR */ |
255 | bank->mask = value; |
256 | omap_inth_update(s, 0); |
257 | omap_inth_update(s, 1); |
258 | return; |
259 | |
260 | case 0x10: /* SIR_IRQ_CODE */ |
261 | case 0x14: /* SIR_FIQ_CODE */ |
262 | OMAP_RO_REG(addr); |
263 | break; |
264 | |
265 | case 0x18: /* CONTROL_REG */ |
266 | if (bank_no != 0) |
267 | break; |
268 | if (value & 2) { |
269 | qemu_set_irq(s->parent_intr[1], 0); |
270 | s->new_agr[1] = ~0; |
271 | omap_inth_update(s, 1); |
272 | } |
273 | if (value & 1) { |
274 | qemu_set_irq(s->parent_intr[0], 0); |
275 | s->new_agr[0] = ~0; |
276 | omap_inth_update(s, 0); |
277 | } |
278 | return; |
279 | |
280 | case 0x1c: /* ILR0 */ |
281 | case 0x20: /* ILR1 */ |
282 | case 0x24: /* ILR2 */ |
283 | case 0x28: /* ILR3 */ |
284 | case 0x2c: /* ILR4 */ |
285 | case 0x30: /* ILR5 */ |
286 | case 0x34: /* ILR6 */ |
287 | case 0x38: /* ILR7 */ |
288 | case 0x3c: /* ILR8 */ |
289 | case 0x40: /* ILR9 */ |
290 | case 0x44: /* ILR10 */ |
291 | case 0x48: /* ILR11 */ |
292 | case 0x4c: /* ILR12 */ |
293 | case 0x50: /* ILR13 */ |
294 | case 0x54: /* ILR14 */ |
295 | case 0x58: /* ILR15 */ |
296 | case 0x5c: /* ILR16 */ |
297 | case 0x60: /* ILR17 */ |
298 | case 0x64: /* ILR18 */ |
299 | case 0x68: /* ILR19 */ |
300 | case 0x6c: /* ILR20 */ |
301 | case 0x70: /* ILR21 */ |
302 | case 0x74: /* ILR22 */ |
303 | case 0x78: /* ILR23 */ |
304 | case 0x7c: /* ILR24 */ |
305 | case 0x80: /* ILR25 */ |
306 | case 0x84: /* ILR26 */ |
307 | case 0x88: /* ILR27 */ |
308 | case 0x8c: /* ILR28 */ |
309 | case 0x90: /* ILR29 */ |
310 | case 0x94: /* ILR30 */ |
311 | case 0x98: /* ILR31 */ |
312 | i = (offset - 0x1c) >> 2; |
313 | bank->priority[i] = (value >> 2) & 0x1f; |
314 | bank->sens_edge &= ~(1 << i); |
315 | bank->sens_edge |= ((value >> 1) & 1) << i; |
316 | bank->fiq &= ~(1 << i); |
317 | bank->fiq |= (value & 1) << i; |
318 | return; |
319 | |
320 | case 0x9c: /* ISR */ |
321 | for (i = 0; i < 32; i ++) |
322 | if (value & (1 << i)) { |
323 | omap_set_intr(s, 32 * bank_no + i, 1); |
324 | return; |
325 | } |
326 | return; |
327 | } |
328 | OMAP_BAD_REG(addr); |
329 | } |
330 | |
331 | static const MemoryRegionOps omap_inth_mem_ops = { |
332 | .read = omap_inth_read, |
333 | .write = omap_inth_write, |
334 | .endianness = DEVICE_NATIVE_ENDIAN, |
335 | .valid = { |
336 | .min_access_size = 4, |
337 | .max_access_size = 4, |
338 | }, |
339 | }; |
340 | |
341 | static void omap_inth_reset(DeviceState *dev) |
342 | { |
343 | struct omap_intr_handler_s *s = OMAP_INTC(dev); |
344 | int i; |
345 | |
346 | for (i = 0; i < s->nbanks; ++i){ |
347 | s->bank[i].irqs = 0x00000000; |
348 | s->bank[i].mask = 0xffffffff; |
349 | s->bank[i].sens_edge = 0x00000000; |
350 | s->bank[i].fiq = 0x00000000; |
351 | s->bank[i].inputs = 0x00000000; |
352 | s->bank[i].swi = 0x00000000; |
353 | memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); |
354 | |
355 | if (s->level_only) |
356 | s->bank[i].sens_edge = 0xffffffff; |
357 | } |
358 | |
359 | s->new_agr[0] = ~0; |
360 | s->new_agr[1] = ~0; |
361 | s->sir_intr[0] = 0; |
362 | s->sir_intr[1] = 0; |
363 | s->autoidle = 0; |
364 | s->mask = ~0; |
365 | |
366 | qemu_set_irq(s->parent_intr[0], 0); |
367 | qemu_set_irq(s->parent_intr[1], 0); |
368 | } |
369 | |
370 | static void omap_intc_init(Object *obj) |
371 | { |
372 | DeviceState *dev = DEVICE(obj); |
373 | struct omap_intr_handler_s *s = OMAP_INTC(obj); |
374 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
375 | |
376 | s->nbanks = 1; |
377 | sysbus_init_irq(sbd, &s->parent_intr[0]); |
378 | sysbus_init_irq(sbd, &s->parent_intr[1]); |
379 | qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32); |
380 | memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s, |
381 | "omap-intc" , s->size); |
382 | sysbus_init_mmio(sbd, &s->mmio); |
383 | } |
384 | |
385 | static void omap_intc_realize(DeviceState *dev, Error **errp) |
386 | { |
387 | struct omap_intr_handler_s *s = OMAP_INTC(dev); |
388 | |
389 | if (!s->iclk) { |
390 | error_setg(errp, "omap-intc: clk not connected" ); |
391 | } |
392 | } |
393 | |
394 | static Property omap_intc_properties[] = { |
395 | DEFINE_PROP_UINT32("size" , struct omap_intr_handler_s, size, 0x100), |
396 | DEFINE_PROP_PTR("clk" , struct omap_intr_handler_s, iclk), |
397 | DEFINE_PROP_END_OF_LIST(), |
398 | }; |
399 | |
400 | static void omap_intc_class_init(ObjectClass *klass, void *data) |
401 | { |
402 | DeviceClass *dc = DEVICE_CLASS(klass); |
403 | |
404 | dc->reset = omap_inth_reset; |
405 | dc->props = omap_intc_properties; |
406 | /* Reason: pointer property "clk" */ |
407 | dc->user_creatable = false; |
408 | dc->realize = omap_intc_realize; |
409 | } |
410 | |
411 | static const TypeInfo omap_intc_info = { |
412 | .name = "omap-intc" , |
413 | .parent = TYPE_OMAP_INTC, |
414 | .instance_init = omap_intc_init, |
415 | .class_init = omap_intc_class_init, |
416 | }; |
417 | |
418 | static uint64_t omap2_inth_read(void *opaque, hwaddr addr, |
419 | unsigned size) |
420 | { |
421 | struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; |
422 | int offset = addr; |
423 | int bank_no, line_no; |
424 | struct omap_intr_handler_bank_s *bank = NULL; |
425 | |
426 | if ((offset & 0xf80) == 0x80) { |
427 | bank_no = (offset & 0x60) >> 5; |
428 | if (bank_no < s->nbanks) { |
429 | offset &= ~0x60; |
430 | bank = &s->bank[bank_no]; |
431 | } else { |
432 | OMAP_BAD_REG(addr); |
433 | return 0; |
434 | } |
435 | } |
436 | |
437 | switch (offset) { |
438 | case 0x00: /* INTC_REVISION */ |
439 | return s->revision; |
440 | |
441 | case 0x10: /* INTC_SYSCONFIG */ |
442 | return (s->autoidle >> 2) & 1; |
443 | |
444 | case 0x14: /* INTC_SYSSTATUS */ |
445 | return 1; /* RESETDONE */ |
446 | |
447 | case 0x40: /* INTC_SIR_IRQ */ |
448 | return s->sir_intr[0]; |
449 | |
450 | case 0x44: /* INTC_SIR_FIQ */ |
451 | return s->sir_intr[1]; |
452 | |
453 | case 0x48: /* INTC_CONTROL */ |
454 | return (!s->mask) << 2; /* GLOBALMASK */ |
455 | |
456 | case 0x4c: /* INTC_PROTECTION */ |
457 | return 0; |
458 | |
459 | case 0x50: /* INTC_IDLE */ |
460 | return s->autoidle & 3; |
461 | |
462 | /* Per-bank registers */ |
463 | case 0x80: /* INTC_ITR */ |
464 | return bank->inputs; |
465 | |
466 | case 0x84: /* INTC_MIR */ |
467 | return bank->mask; |
468 | |
469 | case 0x88: /* INTC_MIR_CLEAR */ |
470 | case 0x8c: /* INTC_MIR_SET */ |
471 | return 0; |
472 | |
473 | case 0x90: /* INTC_ISR_SET */ |
474 | return bank->swi; |
475 | |
476 | case 0x94: /* INTC_ISR_CLEAR */ |
477 | return 0; |
478 | |
479 | case 0x98: /* INTC_PENDING_IRQ */ |
480 | return bank->irqs & ~bank->mask & ~bank->fiq; |
481 | |
482 | case 0x9c: /* INTC_PENDING_FIQ */ |
483 | return bank->irqs & ~bank->mask & bank->fiq; |
484 | |
485 | /* Per-line registers */ |
486 | case 0x100 ... 0x300: /* INTC_ILR */ |
487 | bank_no = (offset - 0x100) >> 7; |
488 | if (bank_no > s->nbanks) |
489 | break; |
490 | bank = &s->bank[bank_no]; |
491 | line_no = (offset & 0x7f) >> 2; |
492 | return (bank->priority[line_no] << 2) | |
493 | ((bank->fiq >> line_no) & 1); |
494 | } |
495 | OMAP_BAD_REG(addr); |
496 | return 0; |
497 | } |
498 | |
499 | static void omap2_inth_write(void *opaque, hwaddr addr, |
500 | uint64_t value, unsigned size) |
501 | { |
502 | struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; |
503 | int offset = addr; |
504 | int bank_no, line_no; |
505 | struct omap_intr_handler_bank_s *bank = NULL; |
506 | |
507 | if ((offset & 0xf80) == 0x80) { |
508 | bank_no = (offset & 0x60) >> 5; |
509 | if (bank_no < s->nbanks) { |
510 | offset &= ~0x60; |
511 | bank = &s->bank[bank_no]; |
512 | } else { |
513 | OMAP_BAD_REG(addr); |
514 | return; |
515 | } |
516 | } |
517 | |
518 | switch (offset) { |
519 | case 0x10: /* INTC_SYSCONFIG */ |
520 | s->autoidle &= 4; |
521 | s->autoidle |= (value & 1) << 2; |
522 | if (value & 2) { /* SOFTRESET */ |
523 | omap_inth_reset(DEVICE(s)); |
524 | } |
525 | return; |
526 | |
527 | case 0x48: /* INTC_CONTROL */ |
528 | s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ |
529 | if (value & 2) { /* NEWFIQAGR */ |
530 | qemu_set_irq(s->parent_intr[1], 0); |
531 | s->new_agr[1] = ~0; |
532 | omap_inth_update(s, 1); |
533 | } |
534 | if (value & 1) { /* NEWIRQAGR */ |
535 | qemu_set_irq(s->parent_intr[0], 0); |
536 | s->new_agr[0] = ~0; |
537 | omap_inth_update(s, 0); |
538 | } |
539 | return; |
540 | |
541 | case 0x4c: /* INTC_PROTECTION */ |
542 | /* TODO: Make a bitmap (or sizeof(char)map) of access privileges |
543 | * for every register, see Chapter 3 and 4 for privileged mode. */ |
544 | if (value & 1) |
545 | fprintf(stderr, "%s: protection mode enable attempt\n" , |
546 | __func__); |
547 | return; |
548 | |
549 | case 0x50: /* INTC_IDLE */ |
550 | s->autoidle &= ~3; |
551 | s->autoidle |= value & 3; |
552 | return; |
553 | |
554 | /* Per-bank registers */ |
555 | case 0x84: /* INTC_MIR */ |
556 | bank->mask = value; |
557 | omap_inth_update(s, 0); |
558 | omap_inth_update(s, 1); |
559 | return; |
560 | |
561 | case 0x88: /* INTC_MIR_CLEAR */ |
562 | bank->mask &= ~value; |
563 | omap_inth_update(s, 0); |
564 | omap_inth_update(s, 1); |
565 | return; |
566 | |
567 | case 0x8c: /* INTC_MIR_SET */ |
568 | bank->mask |= value; |
569 | return; |
570 | |
571 | case 0x90: /* INTC_ISR_SET */ |
572 | bank->irqs |= bank->swi |= value; |
573 | omap_inth_update(s, 0); |
574 | omap_inth_update(s, 1); |
575 | return; |
576 | |
577 | case 0x94: /* INTC_ISR_CLEAR */ |
578 | bank->swi &= ~value; |
579 | bank->irqs = bank->swi & bank->inputs; |
580 | return; |
581 | |
582 | /* Per-line registers */ |
583 | case 0x100 ... 0x300: /* INTC_ILR */ |
584 | bank_no = (offset - 0x100) >> 7; |
585 | if (bank_no > s->nbanks) |
586 | break; |
587 | bank = &s->bank[bank_no]; |
588 | line_no = (offset & 0x7f) >> 2; |
589 | bank->priority[line_no] = (value >> 2) & 0x3f; |
590 | bank->fiq &= ~(1 << line_no); |
591 | bank->fiq |= (value & 1) << line_no; |
592 | return; |
593 | |
594 | case 0x00: /* INTC_REVISION */ |
595 | case 0x14: /* INTC_SYSSTATUS */ |
596 | case 0x40: /* INTC_SIR_IRQ */ |
597 | case 0x44: /* INTC_SIR_FIQ */ |
598 | case 0x80: /* INTC_ITR */ |
599 | case 0x98: /* INTC_PENDING_IRQ */ |
600 | case 0x9c: /* INTC_PENDING_FIQ */ |
601 | OMAP_RO_REG(addr); |
602 | return; |
603 | } |
604 | OMAP_BAD_REG(addr); |
605 | } |
606 | |
607 | static const MemoryRegionOps omap2_inth_mem_ops = { |
608 | .read = omap2_inth_read, |
609 | .write = omap2_inth_write, |
610 | .endianness = DEVICE_NATIVE_ENDIAN, |
611 | .valid = { |
612 | .min_access_size = 4, |
613 | .max_access_size = 4, |
614 | }, |
615 | }; |
616 | |
617 | static void omap2_intc_init(Object *obj) |
618 | { |
619 | DeviceState *dev = DEVICE(obj); |
620 | struct omap_intr_handler_s *s = OMAP_INTC(obj); |
621 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
622 | |
623 | s->level_only = 1; |
624 | s->nbanks = 3; |
625 | sysbus_init_irq(sbd, &s->parent_intr[0]); |
626 | sysbus_init_irq(sbd, &s->parent_intr[1]); |
627 | qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); |
628 | memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s, |
629 | "omap2-intc" , 0x1000); |
630 | sysbus_init_mmio(sbd, &s->mmio); |
631 | } |
632 | |
633 | static void omap2_intc_realize(DeviceState *dev, Error **errp) |
634 | { |
635 | struct omap_intr_handler_s *s = OMAP_INTC(dev); |
636 | |
637 | if (!s->iclk) { |
638 | error_setg(errp, "omap2-intc: iclk not connected" ); |
639 | return; |
640 | } |
641 | if (!s->fclk) { |
642 | error_setg(errp, "omap2-intc: fclk not connected" ); |
643 | return; |
644 | } |
645 | } |
646 | |
647 | static Property omap2_intc_properties[] = { |
648 | DEFINE_PROP_UINT8("revision" , struct omap_intr_handler_s, |
649 | revision, 0x21), |
650 | DEFINE_PROP_PTR("iclk" , struct omap_intr_handler_s, iclk), |
651 | DEFINE_PROP_PTR("fclk" , struct omap_intr_handler_s, fclk), |
652 | DEFINE_PROP_END_OF_LIST(), |
653 | }; |
654 | |
655 | static void omap2_intc_class_init(ObjectClass *klass, void *data) |
656 | { |
657 | DeviceClass *dc = DEVICE_CLASS(klass); |
658 | |
659 | dc->reset = omap_inth_reset; |
660 | dc->props = omap2_intc_properties; |
661 | /* Reason: pointer property "iclk", "fclk" */ |
662 | dc->user_creatable = false; |
663 | dc->realize = omap2_intc_realize; |
664 | } |
665 | |
666 | static const TypeInfo omap2_intc_info = { |
667 | .name = "omap2-intc" , |
668 | .parent = TYPE_OMAP_INTC, |
669 | .instance_init = omap2_intc_init, |
670 | .class_init = omap2_intc_class_init, |
671 | }; |
672 | |
673 | static const TypeInfo omap_intc_type_info = { |
674 | .name = TYPE_OMAP_INTC, |
675 | .parent = TYPE_SYS_BUS_DEVICE, |
676 | .instance_size = sizeof(struct omap_intr_handler_s), |
677 | .abstract = true, |
678 | }; |
679 | |
680 | static void omap_intc_register_types(void) |
681 | { |
682 | type_register_static(&omap_intc_type_info); |
683 | type_register_static(&omap_intc_info); |
684 | type_register_static(&omap2_intc_info); |
685 | } |
686 | |
687 | type_init(omap_intc_register_types) |
688 | |