]> Git Repo - qemu.git/blame - hw/intc/sh_intc.c
hw/sh4: Coding style: Add missing braces
[qemu.git] / hw / intc / sh_intc.c
CommitLineData
80f515e6
AZ
1/*
2 * SuperH interrupt controller module
3 *
4 * Copyright (c) 2007 Magnus Damm
5 * Based on sh_timer.c and arm_timer.c by Paul Brook
6 * Copyright (c) 2005-2006 CodeSourcery.
7 *
8e31bf38 8 * This code is licensed under the GPL.
80f515e6
AZ
9 */
10
90191d07 11#include "qemu/osdep.h"
4771d756 12#include "cpu.h"
0d09e41a 13#include "hw/sh4/sh_intc.h"
64552b6b 14#include "hw/irq.h"
0d09e41a 15#include "hw/sh4/sh.h"
80f515e6
AZ
16
17//#define DEBUG_INTC
e96e2044 18//#define DEBUG_INTC_SOURCES
80f515e6
AZ
19
20#define INTC_A7(x) ((x) & 0x1fffffff)
80f515e6 21
e96e2044 22void sh_intc_toggle_source(struct intc_source *source,
b3793b8a 23 int enable_adj, int assert_adj)
e96e2044
TS
24{
25 int enable_changed = 0;
26 int pending_changed = 0;
27 int old_pending;
28
ac3c9e74 29 if ((source->enable_count == source->enable_max) && (enable_adj == -1)) {
e96e2044 30 enable_changed = -1;
ac3c9e74 31 }
e96e2044
TS
32 source->enable_count += enable_adj;
33
ac3c9e74 34 if (source->enable_count == source->enable_max) {
e96e2044 35 enable_changed = 1;
ac3c9e74 36 }
e96e2044
TS
37 source->asserted += assert_adj;
38
39 old_pending = source->pending;
40 source->pending = source->asserted &&
41 (source->enable_count == source->enable_max);
42
ac3c9e74 43 if (old_pending != source->pending) {
e96e2044 44 pending_changed = 1;
ac3c9e74 45 }
e96e2044
TS
46 if (pending_changed) {
47 if (source->pending) {
48 source->parent->pending++;
c3affe56 49 if (source->parent->pending == 1) {
182735ef 50 cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
c3affe56 51 }
d8ed887b 52 } else {
e96e2044 53 source->parent->pending--;
d8ed887b 54 if (source->parent->pending == 0) {
182735ef 55 cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
d8ed887b 56 }
b3793b8a 57 }
e96e2044
TS
58 }
59
60 if (enable_changed || assert_adj || pending_changed) {
61#ifdef DEBUG_INTC_SOURCES
62 printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
b3793b8a
BZ
63 source->parent->pending,
64 source->asserted,
65 source->enable_count,
66 source->enable_max,
67 source->vect,
68 source->asserted ? "asserted " :
69 assert_adj ? "deasserted" : "",
70 enable_changed == 1 ? "enabled " :
71 enable_changed == -1 ? "disabled " : "",
72 source->pending ? "pending" : "");
e96e2044 73#endif
f94bff13 74 }
e96e2044
TS
75}
76
f94bff13 77static void sh_intc_set_irq(void *opaque, int n, int level)
96e2fc41 78{
f94bff13
BZ
79 struct intc_desc *desc = opaque;
80 struct intc_source *source = &(desc->sources[n]);
96e2fc41 81
ac3c9e74
BZ
82 if (level && !source->asserted) {
83 sh_intc_toggle_source(source, 0, 1);
84 } else if (!level && source->asserted) {
85 sh_intc_toggle_source(source, 0, -1);
86 }
96e2fc41
AJ
87}
88
e96e2044
TS
89int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
90{
91 unsigned int i;
92
93 /* slow: use a linked lists of pending sources instead */
94 /* wrong: take interrupt priority into account (one list per priority) */
95
96 if (imask == 0x0f) {
97 return -1; /* FIXME, update code to include priority per source */
98 }
99
100 for (i = 0; i < desc->nr_sources; i++) {
101 struct intc_source *source = desc->sources + i;
102
b3793b8a 103 if (source->pending) {
e96e2044
TS
104#ifdef DEBUG_INTC_SOURCES
105 printf("sh_intc: (%d) returning interrupt source 0x%x\n",
b3793b8a 106 desc->pending, source->vect);
e96e2044
TS
107#endif
108 return source->vect;
b3793b8a 109 }
e96e2044
TS
110 }
111
43dc2a64 112 abort();
e96e2044
TS
113}
114
80f515e6
AZ
115#define INTC_MODE_NONE 0
116#define INTC_MODE_DUAL_SET 1
117#define INTC_MODE_DUAL_CLR 2
118#define INTC_MODE_ENABLE_REG 3
119#define INTC_MODE_MASK_REG 4
120#define INTC_MODE_IS_PRIO 8
121
122static unsigned int sh_intc_mode(unsigned long address,
b3793b8a 123 unsigned long set_reg, unsigned long clr_reg)
80f515e6
AZ
124{
125 if ((address != INTC_A7(set_reg)) &&
b3793b8a 126 (address != INTC_A7(clr_reg)))
80f515e6
AZ
127 return INTC_MODE_NONE;
128
129 if (set_reg && clr_reg) {
ac3c9e74 130 if (address == INTC_A7(set_reg)) {
80f515e6 131 return INTC_MODE_DUAL_SET;
ac3c9e74 132 } else {
80f515e6 133 return INTC_MODE_DUAL_CLR;
ac3c9e74 134 }
80f515e6
AZ
135 }
136
ac3c9e74 137 if (set_reg) {
80f515e6 138 return INTC_MODE_ENABLE_REG;
ac3c9e74 139 } else {
80f515e6 140 return INTC_MODE_MASK_REG;
ac3c9e74 141 }
80f515e6
AZ
142}
143
144static void sh_intc_locate(struct intc_desc *desc,
b3793b8a
BZ
145 unsigned long address,
146 unsigned long **datap,
147 intc_enum **enums,
148 unsigned int *first,
149 unsigned int *width,
150 unsigned int *modep)
80f515e6
AZ
151{
152 unsigned int i, mode;
153
154 /* this is slow but works for now */
155
156 if (desc->mask_regs) {
157 for (i = 0; i < desc->nr_mask_regs; i++) {
b3793b8a 158 struct intc_mask_reg *mr = desc->mask_regs + i;
80f515e6 159
b3793b8a 160 mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg);
ac3c9e74 161 if (mode == INTC_MODE_NONE) {
80f515e6 162 continue;
ac3c9e74 163 }
b3793b8a
BZ
164 *modep = mode;
165 *datap = &mr->value;
166 *enums = mr->enum_ids;
167 *first = mr->reg_width - 1;
168 *width = 1;
169 return;
170 }
80f515e6
AZ
171 }
172
173 if (desc->prio_regs) {
174 for (i = 0; i < desc->nr_prio_regs; i++) {
b3793b8a 175 struct intc_prio_reg *pr = desc->prio_regs + i;
80f515e6 176
b3793b8a 177 mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg);
ac3c9e74 178 if (mode == INTC_MODE_NONE) {
80f515e6 179 continue;
ac3c9e74 180 }
b3793b8a
BZ
181 *modep = mode | INTC_MODE_IS_PRIO;
182 *datap = &pr->value;
183 *enums = pr->enum_ids;
184 *first = (pr->reg_width / pr->field_width) - 1;
185 *width = pr->field_width;
186 return;
187 }
80f515e6
AZ
188 }
189
43dc2a64 190 abort();
80f515e6
AZ
191}
192
e96e2044 193static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id,
b3793b8a 194 int enable, int is_group)
80f515e6
AZ
195{
196 struct intc_source *source = desc->sources + id;
80f515e6 197
ac3c9e74 198 if (!id) {
b3793b8a 199 return;
ac3c9e74 200 }
80f515e6 201 if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
e96e2044 202#ifdef DEBUG_INTC_SOURCES
80f515e6
AZ
203 printf("sh_intc: reserved interrupt source %d modified\n", id);
204#endif
b3793b8a 205 return;
80f515e6
AZ
206 }
207
ac3c9e74 208 if (source->vect) {
e96e2044 209 sh_intc_toggle_source(source, enable ? 1 : -1, 0);
ac3c9e74 210 }
80f515e6
AZ
211#ifdef DEBUG_INTC
212 else {
213 printf("setting interrupt group %d to %d\n", id, !!enable);
214 }
215#endif
216
217 if ((is_group || !source->vect) && source->next_enum_id) {
e96e2044 218 sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1);
80f515e6
AZ
219 }
220
221#ifdef DEBUG_INTC
222 if (!source->vect) {
223 printf("setting interrupt group %d to %d - done\n", id, !!enable);
224 }
225#endif
226}
227
a8170e5e 228static uint64_t sh_intc_read(void *opaque, hwaddr offset,
b279e5ef 229 unsigned size)
80f515e6
AZ
230{
231 struct intc_desc *desc = opaque;
232 intc_enum *enum_ids = NULL;
233 unsigned int first = 0;
234 unsigned int width = 0;
235 unsigned int mode = 0;
236 unsigned long *valuep;
237
238#ifdef DEBUG_INTC
239 printf("sh_intc_read 0x%lx\n", (unsigned long) offset);
240#endif
241
f94bff13 242 sh_intc_locate(desc, (unsigned long)offset, &valuep,
b3793b8a 243 &enum_ids, &first, &width, &mode);
80f515e6
AZ
244 return *valuep;
245}
246
a8170e5e 247static void sh_intc_write(void *opaque, hwaddr offset,
b279e5ef 248 uint64_t value, unsigned size)
80f515e6
AZ
249{
250 struct intc_desc *desc = opaque;
251 intc_enum *enum_ids = NULL;
252 unsigned int first = 0;
253 unsigned int width = 0;
254 unsigned int mode = 0;
255 unsigned int k;
256 unsigned long *valuep;
257 unsigned long mask;
258
259#ifdef DEBUG_INTC
260 printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
261#endif
262
f94bff13 263 sh_intc_locate(desc, (unsigned long)offset, &valuep,
b3793b8a 264 &enum_ids, &first, &width, &mode);
80f515e6
AZ
265
266 switch (mode) {
f94bff13
BZ
267 case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO:
268 break;
269 case INTC_MODE_DUAL_SET:
270 value |= *valuep;
271 break;
272 case INTC_MODE_DUAL_CLR:
273 value = *valuep & ~value;
274 break;
275 default:
276 abort();
80f515e6
AZ
277 }
278
279 for (k = 0; k <= first; k++) {
280 mask = ((1 << width) - 1) << ((first - k) * width);
281
ac3c9e74 282 if ((*valuep & mask) == (value & mask)) {
80f515e6 283 continue;
ac3c9e74 284 }
80f515e6 285#if 0
b3793b8a
BZ
286 printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
287 k, first, enum_ids[k], (unsigned int)mask);
80f515e6 288#endif
e96e2044 289 sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
80f515e6
AZ
290 }
291
292 *valuep = value;
293
294#ifdef DEBUG_INTC
295 printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value);
296#endif
297}
298
12f30833 299static const MemoryRegionOps sh_intc_ops = {
b279e5ef
BC
300 .read = sh_intc_read,
301 .write = sh_intc_write,
302 .endianness = DEVICE_NATIVE_ENDIAN,
80f515e6
AZ
303};
304
305struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id)
306{
ac3c9e74 307 if (id) {
80f515e6 308 return desc->sources + id;
ac3c9e74 309 }
80f515e6
AZ
310 return NULL;
311}
312
b279e5ef 313static unsigned int sh_intc_register(MemoryRegion *sysmem,
b3793b8a
BZ
314 struct intc_desc *desc,
315 const unsigned long address,
316 const char *type,
317 const char *action,
318 const unsigned int index)
80f515e6 319{
b279e5ef
BC
320 char name[60];
321 MemoryRegion *iomem, *iomem_p4, *iomem_a7;
322
323 if (!address) {
324 return 0;
5c16736a 325 }
b279e5ef
BC
326
327 iomem = &desc->iomem;
328 iomem_p4 = desc->iomem_aliases + index;
329 iomem_a7 = iomem_p4 + 1;
330
331#define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s"
332 snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4");
2c9b15ca 333 memory_region_init_alias(iomem_p4, NULL, name, iomem, INTC_A7(address), 4);
b279e5ef
BC
334 memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4);
335
336 snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7");
2c9b15ca 337 memory_region_init_alias(iomem_a7, NULL, name, iomem, INTC_A7(address), 4);
b279e5ef
BC
338 memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7);
339#undef SH_INTC_IOMEM_FORMAT
340
341 /* used to increment aliases index */
342 return 2;
80f515e6
AZ
343}
344
345static void sh_intc_register_source(struct intc_desc *desc,
b3793b8a
BZ
346 intc_enum source,
347 struct intc_group *groups,
348 int nr_groups)
80f515e6
AZ
349{
350 unsigned int i, k;
351 struct intc_source *s;
352
353 if (desc->mask_regs) {
354 for (i = 0; i < desc->nr_mask_regs; i++) {
b3793b8a 355 struct intc_mask_reg *mr = desc->mask_regs + i;
80f515e6 356
b3793b8a 357 for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) {
ac3c9e74 358 if (mr->enum_ids[k] != source) {
80f515e6 359 continue;
ac3c9e74 360 }
b3793b8a 361 s = sh_intc_source(desc, mr->enum_ids[k]);
ac3c9e74 362 if (s) {
80f515e6 363 s->enable_max++;
ac3c9e74 364 }
b3793b8a
BZ
365 }
366 }
80f515e6
AZ
367 }
368
369 if (desc->prio_regs) {
370 for (i = 0; i < desc->nr_prio_regs; i++) {
b3793b8a 371 struct intc_prio_reg *pr = desc->prio_regs + i;
80f515e6 372
b3793b8a 373 for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) {
ac3c9e74 374 if (pr->enum_ids[k] != source) {
80f515e6 375 continue;
ac3c9e74 376 }
b3793b8a 377 s = sh_intc_source(desc, pr->enum_ids[k]);
ac3c9e74 378 if (s) {
80f515e6 379 s->enable_max++;
ac3c9e74 380 }
b3793b8a
BZ
381 }
382 }
80f515e6
AZ
383 }
384
385 if (groups) {
386 for (i = 0; i < nr_groups; i++) {
b3793b8a 387 struct intc_group *gr = groups + i;
80f515e6 388
b3793b8a 389 for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) {
ac3c9e74 390 if (gr->enum_ids[k] != source) {
80f515e6 391 continue;
ac3c9e74 392 }
b3793b8a 393 s = sh_intc_source(desc, gr->enum_ids[k]);
ac3c9e74 394 if (s) {
80f515e6 395 s->enable_max++;
ac3c9e74 396 }
b3793b8a
BZ
397 }
398 }
80f515e6
AZ
399 }
400
401}
402
403void sh_intc_register_sources(struct intc_desc *desc,
b3793b8a
BZ
404 struct intc_vect *vectors,
405 int nr_vectors,
406 struct intc_group *groups,
407 int nr_groups)
80f515e6
AZ
408{
409 unsigned int i, k;
410 struct intc_source *s;
411
412 for (i = 0; i < nr_vectors; i++) {
b3793b8a 413 struct intc_vect *vect = vectors + i;
80f515e6 414
b3793b8a
BZ
415 sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
416 s = sh_intc_source(desc, vect->enum_id);
6f9faa91
SW
417 if (s) {
418 s->vect = vect->vect;
80f515e6 419
e96e2044 420#ifdef DEBUG_INTC_SOURCES
6f9faa91
SW
421 printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
422 vect->enum_id, s->vect, s->enable_count, s->enable_max);
80f515e6 423#endif
6f9faa91 424 }
80f515e6
AZ
425 }
426
427 if (groups) {
428 for (i = 0; i < nr_groups; i++) {
b3793b8a 429 struct intc_group *gr = groups + i;
80f515e6 430
b3793b8a
BZ
431 s = sh_intc_source(desc, gr->enum_id);
432 s->next_enum_id = gr->enum_ids[0];
80f515e6 433
b3793b8a 434 for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) {
ac3c9e74 435 if (!gr->enum_ids[k]) {
80f515e6 436 continue;
ac3c9e74 437 }
b3793b8a
BZ
438 s = sh_intc_source(desc, gr->enum_ids[k - 1]);
439 s->next_enum_id = gr->enum_ids[k];
440 }
80f515e6 441
e96e2044 442#ifdef DEBUG_INTC_SOURCES
b3793b8a
BZ
443 printf("sh_intc: registered group %d (%d/%d)\n",
444 gr->enum_id, s->enable_count, s->enable_max);
80f515e6 445#endif
b3793b8a 446 }
80f515e6
AZ
447 }
448}
449
b279e5ef 450int sh_intc_init(MemoryRegion *sysmem,
b3793b8a
BZ
451 struct intc_desc *desc,
452 int nr_sources,
453 struct intc_mask_reg *mask_regs,
454 int nr_mask_regs,
455 struct intc_prio_reg *prio_regs,
456 int nr_prio_regs)
80f515e6 457{
b279e5ef 458 unsigned int i, j;
80f515e6 459
e96e2044 460 desc->pending = 0;
80f515e6
AZ
461 desc->nr_sources = nr_sources;
462 desc->mask_regs = mask_regs;
463 desc->nr_mask_regs = nr_mask_regs;
464 desc->prio_regs = prio_regs;
465 desc->nr_prio_regs = nr_prio_regs;
22138965 466 /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases) */
b279e5ef
BC
467 desc->iomem_aliases = g_new0(MemoryRegion,
468 (nr_mask_regs + nr_prio_regs) * 4);
80f515e6 469
b279e5ef 470 j = 0;
80f515e6 471 i = sizeof(struct intc_source) * nr_sources;
7267c094 472 desc->sources = g_malloc0(i);
80f515e6 473
e96e2044
TS
474 for (i = 0; i < desc->nr_sources; i++) {
475 struct intc_source *source = desc->sources + i;
476
477 source->parent = desc;
478 }
96e2fc41
AJ
479
480 desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources);
f94bff13 481
2c9b15ca 482 memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc,
b279e5ef
BC
483 "interrupt-controller", 0x100000000ULL);
484
485#define INT_REG_PARAMS(reg_struct, type, action, j) \
486 reg_struct->action##_reg, #type, #action, j
80f515e6
AZ
487 if (desc->mask_regs) {
488 for (i = 0; i < desc->nr_mask_regs; i++) {
b3793b8a 489 struct intc_mask_reg *mr = desc->mask_regs + i;
80f515e6 490
b279e5ef
BC
491 j += sh_intc_register(sysmem, desc,
492 INT_REG_PARAMS(mr, mask, set, j));
493 j += sh_intc_register(sysmem, desc,
494 INT_REG_PARAMS(mr, mask, clr, j));
b3793b8a 495 }
80f515e6
AZ
496 }
497
498 if (desc->prio_regs) {
499 for (i = 0; i < desc->nr_prio_regs; i++) {
b3793b8a 500 struct intc_prio_reg *pr = desc->prio_regs + i;
80f515e6 501
b279e5ef
BC
502 j += sh_intc_register(sysmem, desc,
503 INT_REG_PARAMS(pr, prio, set, j));
504 j += sh_intc_register(sysmem, desc,
505 INT_REG_PARAMS(pr, prio, clr, j));
b3793b8a 506 }
80f515e6 507 }
b279e5ef 508#undef INT_REG_PARAMS
80f515e6
AZ
509
510 return 0;
511}
c6d86a33 512
22138965
BZ
513/*
514 * Assert level <n> IRL interrupt.
515 * 0:deassert. 1:lowest priority,... 15:highest priority
516 */
c6d86a33
AZ
517void sh_intc_set_irl(void *opaque, int n, int level)
518{
519 struct intc_source *s = opaque;
520 int i, irl = level ^ 15;
521 for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) {
ac3c9e74 522 if (i == irl) {
f94bff13
BZ
523 sh_intc_toggle_source(s, s->enable_count ? 0 : 1,
524 s->asserted ? 0 : 1);
ac3c9e74
BZ
525 } else if (s->asserted) {
526 sh_intc_toggle_source(s, 0, -1);
527 }
c6d86a33
AZ
528 }
529}
This page took 1.086089 seconds and 4 git commands to generate.