2 * SuperH interrupt controller module
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.
8 * This code is licensed under the GPL.
16 //#define DEBUG_INTC_SOURCES
18 #define INTC_A7(x) ((x) & 0x1fffffff)
20 void sh_intc_toggle_source(struct intc_source
*source
,
21 int enable_adj
, int assert_adj
)
23 int enable_changed
= 0;
24 int pending_changed
= 0;
27 if ((source
->enable_count
== source
->enable_max
) && (enable_adj
== -1))
30 source
->enable_count
+= enable_adj
;
32 if (source
->enable_count
== source
->enable_max
)
35 source
->asserted
+= assert_adj
;
37 old_pending
= source
->pending
;
38 source
->pending
= source
->asserted
&&
39 (source
->enable_count
== source
->enable_max
);
41 if (old_pending
!= source
->pending
)
44 if (pending_changed
) {
45 if (source
->pending
) {
46 source
->parent
->pending
++;
47 if (source
->parent
->pending
== 1)
48 cpu_interrupt(first_cpu
, CPU_INTERRUPT_HARD
);
51 source
->parent
->pending
--;
52 if (source
->parent
->pending
== 0)
53 cpu_reset_interrupt(first_cpu
, CPU_INTERRUPT_HARD
);
57 if (enable_changed
|| assert_adj
|| pending_changed
) {
58 #ifdef DEBUG_INTC_SOURCES
59 printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
60 source
->parent
->pending
,
65 source
->asserted
? "asserted " :
66 assert_adj
? "deasserted" : "",
67 enable_changed
== 1 ? "enabled " :
68 enable_changed
== -1 ? "disabled " : "",
69 source
->pending
? "pending" : "");
74 static void sh_intc_set_irq (void *opaque
, int n
, int level
)
76 struct intc_desc
*desc
= opaque
;
77 struct intc_source
*source
= &(desc
->sources
[n
]);
79 if (level
&& !source
->asserted
)
80 sh_intc_toggle_source(source
, 0, 1);
81 else if (!level
&& source
->asserted
)
82 sh_intc_toggle_source(source
, 0, -1);
85 int sh_intc_get_pending_vector(struct intc_desc
*desc
, int imask
)
89 /* slow: use a linked lists of pending sources instead */
90 /* wrong: take interrupt priority into account (one list per priority) */
93 return -1; /* FIXME, update code to include priority per source */
96 for (i
= 0; i
< desc
->nr_sources
; i
++) {
97 struct intc_source
*source
= desc
->sources
+ i
;
99 if (source
->pending
) {
100 #ifdef DEBUG_INTC_SOURCES
101 printf("sh_intc: (%d) returning interrupt source 0x%x\n",
102 desc
->pending
, source
->vect
);
111 #define INTC_MODE_NONE 0
112 #define INTC_MODE_DUAL_SET 1
113 #define INTC_MODE_DUAL_CLR 2
114 #define INTC_MODE_ENABLE_REG 3
115 #define INTC_MODE_MASK_REG 4
116 #define INTC_MODE_IS_PRIO 8
118 static unsigned int sh_intc_mode(unsigned long address
,
119 unsigned long set_reg
, unsigned long clr_reg
)
121 if ((address
!= INTC_A7(set_reg
)) &&
122 (address
!= INTC_A7(clr_reg
)))
123 return INTC_MODE_NONE
;
125 if (set_reg
&& clr_reg
) {
126 if (address
== INTC_A7(set_reg
))
127 return INTC_MODE_DUAL_SET
;
129 return INTC_MODE_DUAL_CLR
;
133 return INTC_MODE_ENABLE_REG
;
135 return INTC_MODE_MASK_REG
;
138 static void sh_intc_locate(struct intc_desc
*desc
,
139 unsigned long address
,
140 unsigned long **datap
,
146 unsigned int i
, mode
;
148 /* this is slow but works for now */
150 if (desc
->mask_regs
) {
151 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
152 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
154 mode
= sh_intc_mode(address
, mr
->set_reg
, mr
->clr_reg
);
155 if (mode
== INTC_MODE_NONE
)
160 *enums
= mr
->enum_ids
;
161 *first
= mr
->reg_width
- 1;
167 if (desc
->prio_regs
) {
168 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
169 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
171 mode
= sh_intc_mode(address
, pr
->set_reg
, pr
->clr_reg
);
172 if (mode
== INTC_MODE_NONE
)
175 *modep
= mode
| INTC_MODE_IS_PRIO
;
177 *enums
= pr
->enum_ids
;
178 *first
= (pr
->reg_width
/ pr
->field_width
) - 1;
179 *width
= pr
->field_width
;
187 static void sh_intc_toggle_mask(struct intc_desc
*desc
, intc_enum id
,
188 int enable
, int is_group
)
190 struct intc_source
*source
= desc
->sources
+ id
;
195 if (!source
->next_enum_id
&& (!source
->enable_max
|| !source
->vect
)) {
196 #ifdef DEBUG_INTC_SOURCES
197 printf("sh_intc: reserved interrupt source %d modified\n", id
);
203 sh_intc_toggle_source(source
, enable
? 1 : -1, 0);
207 printf("setting interrupt group %d to %d\n", id
, !!enable
);
211 if ((is_group
|| !source
->vect
) && source
->next_enum_id
) {
212 sh_intc_toggle_mask(desc
, source
->next_enum_id
, enable
, 1);
217 printf("setting interrupt group %d to %d - done\n", id
, !!enable
);
222 static uint32_t sh_intc_read(void *opaque
, target_phys_addr_t offset
)
224 struct intc_desc
*desc
= opaque
;
225 intc_enum
*enum_ids
= NULL
;
226 unsigned int first
= 0;
227 unsigned int width
= 0;
228 unsigned int mode
= 0;
229 unsigned long *valuep
;
232 printf("sh_intc_read 0x%lx\n", (unsigned long) offset
);
235 sh_intc_locate(desc
, (unsigned long)offset
, &valuep
,
236 &enum_ids
, &first
, &width
, &mode
);
240 static void sh_intc_write(void *opaque
, target_phys_addr_t offset
,
243 struct intc_desc
*desc
= opaque
;
244 intc_enum
*enum_ids
= NULL
;
245 unsigned int first
= 0;
246 unsigned int width
= 0;
247 unsigned int mode
= 0;
249 unsigned long *valuep
;
253 printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset
, value
);
256 sh_intc_locate(desc
, (unsigned long)offset
, &valuep
,
257 &enum_ids
, &first
, &width
, &mode
);
260 case INTC_MODE_ENABLE_REG
| INTC_MODE_IS_PRIO
: break;
261 case INTC_MODE_DUAL_SET
: value
|= *valuep
; break;
262 case INTC_MODE_DUAL_CLR
: value
= *valuep
& ~value
; break;
266 for (k
= 0; k
<= first
; k
++) {
267 mask
= ((1 << width
) - 1) << ((first
- k
) * width
);
269 if ((*valuep
& mask
) == (value
& mask
))
272 printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
273 k
, first
, enum_ids
[k
], (unsigned int)mask
);
275 sh_intc_toggle_mask(desc
, enum_ids
[k
], value
& mask
, 0);
281 printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset
, value
);
285 static CPUReadMemoryFunc
* const sh_intc_readfn
[] = {
291 static CPUWriteMemoryFunc
* const sh_intc_writefn
[] = {
297 struct intc_source
*sh_intc_source(struct intc_desc
*desc
, intc_enum id
)
300 return desc
->sources
+ id
;
305 static void sh_intc_register(struct intc_desc
*desc
,
306 unsigned long address
)
309 cpu_register_physical_memory_offset(P4ADDR(address
), 4,
310 desc
->iomemtype
, INTC_A7(address
));
311 cpu_register_physical_memory_offset(A7ADDR(address
), 4,
312 desc
->iomemtype
, INTC_A7(address
));
316 static void sh_intc_register_source(struct intc_desc
*desc
,
318 struct intc_group
*groups
,
322 struct intc_source
*s
;
324 if (desc
->mask_regs
) {
325 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
326 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
328 for (k
= 0; k
< ARRAY_SIZE(mr
->enum_ids
); k
++) {
329 if (mr
->enum_ids
[k
] != source
)
332 s
= sh_intc_source(desc
, mr
->enum_ids
[k
]);
339 if (desc
->prio_regs
) {
340 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
341 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
343 for (k
= 0; k
< ARRAY_SIZE(pr
->enum_ids
); k
++) {
344 if (pr
->enum_ids
[k
] != source
)
347 s
= sh_intc_source(desc
, pr
->enum_ids
[k
]);
355 for (i
= 0; i
< nr_groups
; i
++) {
356 struct intc_group
*gr
= groups
+ i
;
358 for (k
= 0; k
< ARRAY_SIZE(gr
->enum_ids
); k
++) {
359 if (gr
->enum_ids
[k
] != source
)
362 s
= sh_intc_source(desc
, gr
->enum_ids
[k
]);
371 void sh_intc_register_sources(struct intc_desc
*desc
,
372 struct intc_vect
*vectors
,
374 struct intc_group
*groups
,
378 struct intc_source
*s
;
380 for (i
= 0; i
< nr_vectors
; i
++) {
381 struct intc_vect
*vect
= vectors
+ i
;
383 sh_intc_register_source(desc
, vect
->enum_id
, groups
, nr_groups
);
384 s
= sh_intc_source(desc
, vect
->enum_id
);
386 s
->vect
= vect
->vect
;
388 #ifdef DEBUG_INTC_SOURCES
389 printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
390 vect
->enum_id
, s
->vect
, s
->enable_count
, s
->enable_max
);
395 for (i
= 0; i
< nr_groups
; i
++) {
396 struct intc_group
*gr
= groups
+ i
;
398 s
= sh_intc_source(desc
, gr
->enum_id
);
399 s
->next_enum_id
= gr
->enum_ids
[0];
401 for (k
= 1; k
< ARRAY_SIZE(gr
->enum_ids
); k
++) {
402 if (!gr
->enum_ids
[k
])
405 s
= sh_intc_source(desc
, gr
->enum_ids
[k
- 1]);
406 s
->next_enum_id
= gr
->enum_ids
[k
];
409 #ifdef DEBUG_INTC_SOURCES
410 printf("sh_intc: registered group %d (%d/%d)\n",
411 gr
->enum_id
, s
->enable_count
, s
->enable_max
);
417 int sh_intc_init(struct intc_desc
*desc
,
419 struct intc_mask_reg
*mask_regs
,
421 struct intc_prio_reg
*prio_regs
,
427 desc
->nr_sources
= nr_sources
;
428 desc
->mask_regs
= mask_regs
;
429 desc
->nr_mask_regs
= nr_mask_regs
;
430 desc
->prio_regs
= prio_regs
;
431 desc
->nr_prio_regs
= nr_prio_regs
;
433 i
= sizeof(struct intc_source
) * nr_sources
;
434 desc
->sources
= qemu_mallocz(i
);
436 for (i
= 0; i
< desc
->nr_sources
; i
++) {
437 struct intc_source
*source
= desc
->sources
+ i
;
439 source
->parent
= desc
;
442 desc
->irqs
= qemu_allocate_irqs(sh_intc_set_irq
, desc
, nr_sources
);
444 desc
->iomemtype
= cpu_register_io_memory(sh_intc_readfn
,
445 sh_intc_writefn
, desc
,
446 DEVICE_NATIVE_ENDIAN
);
447 if (desc
->mask_regs
) {
448 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
449 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
451 sh_intc_register(desc
, mr
->set_reg
);
452 sh_intc_register(desc
, mr
->clr_reg
);
456 if (desc
->prio_regs
) {
457 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
458 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
460 sh_intc_register(desc
, pr
->set_reg
);
461 sh_intc_register(desc
, pr
->clr_reg
);
468 /* Assert level <n> IRL interrupt.
469 0:deassert. 1:lowest priority,... 15:highest priority. */
470 void sh_intc_set_irl(void *opaque
, int n
, int level
)
472 struct intc_source
*s
= opaque
;
473 int i
, irl
= level
^ 15;
474 for (i
= 0; (s
= sh_intc_source(s
->parent
, s
->next_enum_id
)); i
++) {
476 sh_intc_toggle_source(s
, s
->enable_count
?0:1, s
->asserted
?0:1);
479 sh_intc_toggle_source(s
, 0, -1);