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 licenced under the GPL.
17 //#define DEBUG_INTC_SOURCES
19 #define INTC_A7(x) ((x) & 0x1fffffff)
20 #define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0]))
22 void sh_intc_toggle_source(struct intc_source
*source
,
23 int enable_adj
, int assert_adj
)
25 int enable_changed
= 0;
26 int pending_changed
= 0;
29 if ((source
->enable_count
== source
->enable_max
) && (enable_adj
== -1))
32 source
->enable_count
+= enable_adj
;
34 if (source
->enable_count
== source
->enable_max
)
37 source
->asserted
+= assert_adj
;
39 old_pending
= source
->pending
;
40 source
->pending
= source
->asserted
&&
41 (source
->enable_count
== source
->enable_max
);
43 if (old_pending
!= source
->pending
)
46 if (pending_changed
) {
47 if (source
->pending
) {
48 source
->parent
->pending
++;
49 if (source
->parent
->pending
== 1)
50 cpu_interrupt(first_cpu
, CPU_INTERRUPT_HARD
);
53 source
->parent
->pending
--;
54 if (source
->parent
->pending
== 0)
55 cpu_reset_interrupt(first_cpu
, CPU_INTERRUPT_HARD
);
59 if (enable_changed
|| assert_adj
|| pending_changed
) {
60 #ifdef DEBUG_INTC_SOURCES
61 printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
62 source
->parent
->pending
,
67 source
->asserted
? "asserted " :
68 assert_adj
? "deasserted" : "",
69 enable_changed
== 1 ? "enabled " :
70 enable_changed
== -1 ? "disabled " : "",
71 source
->pending
? "pending" : "");
76 int sh_intc_get_pending_vector(struct intc_desc
*desc
, int imask
)
80 /* slow: use a linked lists of pending sources instead */
81 /* wrong: take interrupt priority into account (one list per priority) */
84 return -1; /* FIXME, update code to include priority per source */
87 for (i
= 0; i
< desc
->nr_sources
; i
++) {
88 struct intc_source
*source
= desc
->sources
+ i
;
90 if (source
->pending
) {
91 #ifdef DEBUG_INTC_SOURCES
92 printf("sh_intc: (%d) returning interrupt source 0x%x\n",
93 desc
->pending
, source
->vect
);
102 #define INTC_MODE_NONE 0
103 #define INTC_MODE_DUAL_SET 1
104 #define INTC_MODE_DUAL_CLR 2
105 #define INTC_MODE_ENABLE_REG 3
106 #define INTC_MODE_MASK_REG 4
107 #define INTC_MODE_IS_PRIO 8
109 static unsigned int sh_intc_mode(unsigned long address
,
110 unsigned long set_reg
, unsigned long clr_reg
)
112 if ((address
!= INTC_A7(set_reg
)) &&
113 (address
!= INTC_A7(clr_reg
)))
114 return INTC_MODE_NONE
;
116 if (set_reg
&& clr_reg
) {
117 if (address
== INTC_A7(set_reg
))
118 return INTC_MODE_DUAL_SET
;
120 return INTC_MODE_DUAL_CLR
;
124 return INTC_MODE_ENABLE_REG
;
126 return INTC_MODE_MASK_REG
;
129 static void sh_intc_locate(struct intc_desc
*desc
,
130 unsigned long address
,
131 unsigned long **datap
,
137 unsigned int i
, mode
;
139 /* this is slow but works for now */
141 if (desc
->mask_regs
) {
142 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
143 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
145 mode
= sh_intc_mode(address
, mr
->set_reg
, mr
->clr_reg
);
146 if (mode
== INTC_MODE_NONE
)
151 *enums
= mr
->enum_ids
;
152 *first
= mr
->reg_width
- 1;
158 if (desc
->prio_regs
) {
159 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
160 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
162 mode
= sh_intc_mode(address
, pr
->set_reg
, pr
->clr_reg
);
163 if (mode
== INTC_MODE_NONE
)
166 *modep
= mode
| INTC_MODE_IS_PRIO
;
168 *enums
= pr
->enum_ids
;
169 *first
= (pr
->reg_width
/ pr
->field_width
) - 1;
170 *width
= pr
->field_width
;
178 static void sh_intc_toggle_mask(struct intc_desc
*desc
, intc_enum id
,
179 int enable
, int is_group
)
181 struct intc_source
*source
= desc
->sources
+ id
;
186 if (!source
->next_enum_id
&& (!source
->enable_max
|| !source
->vect
)) {
187 #ifdef DEBUG_INTC_SOURCES
188 printf("sh_intc: reserved interrupt source %d modified\n", id
);
194 sh_intc_toggle_source(source
, enable
? 1 : -1, 0);
198 printf("setting interrupt group %d to %d\n", id
, !!enable
);
202 if ((is_group
|| !source
->vect
) && source
->next_enum_id
) {
203 sh_intc_toggle_mask(desc
, source
->next_enum_id
, enable
, 1);
208 printf("setting interrupt group %d to %d - done\n", id
, !!enable
);
213 static uint32_t sh_intc_read(void *opaque
, target_phys_addr_t offset
)
215 struct intc_desc
*desc
= opaque
;
216 intc_enum
*enum_ids
= NULL
;
217 unsigned int first
= 0;
218 unsigned int width
= 0;
219 unsigned int mode
= 0;
220 unsigned long *valuep
;
223 printf("sh_intc_read 0x%lx\n", (unsigned long) offset
);
226 sh_intc_locate(desc
, (unsigned long)offset
, &valuep
,
227 &enum_ids
, &first
, &width
, &mode
);
231 static void sh_intc_write(void *opaque
, target_phys_addr_t offset
,
234 struct intc_desc
*desc
= opaque
;
235 intc_enum
*enum_ids
= NULL
;
236 unsigned int first
= 0;
237 unsigned int width
= 0;
238 unsigned int mode
= 0;
240 unsigned long *valuep
;
244 printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset
, value
);
247 sh_intc_locate(desc
, (unsigned long)offset
, &valuep
,
248 &enum_ids
, &first
, &width
, &mode
);
251 case INTC_MODE_ENABLE_REG
| INTC_MODE_IS_PRIO
: break;
252 case INTC_MODE_DUAL_SET
: value
|= *valuep
; break;
253 case INTC_MODE_DUAL_CLR
: value
= *valuep
& ~value
; break;
257 for (k
= 0; k
<= first
; k
++) {
258 mask
= ((1 << width
) - 1) << ((first
- k
) * width
);
260 if ((*valuep
& mask
) == (value
& mask
))
263 printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
264 k
, first
, enum_ids
[k
], (unsigned int)mask
);
266 sh_intc_toggle_mask(desc
, enum_ids
[k
], value
& mask
, 0);
272 printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset
, value
);
276 static CPUReadMemoryFunc
*sh_intc_readfn
[] = {
282 static CPUWriteMemoryFunc
*sh_intc_writefn
[] = {
288 struct intc_source
*sh_intc_source(struct intc_desc
*desc
, intc_enum id
)
291 return desc
->sources
+ id
;
296 static void sh_intc_register(struct intc_desc
*desc
,
297 unsigned long address
)
300 cpu_register_physical_memory(INTC_A7(address
), 4, desc
->iomemtype
);
303 static void sh_intc_register_source(struct intc_desc
*desc
,
305 struct intc_group
*groups
,
309 struct intc_source
*s
;
311 if (desc
->mask_regs
) {
312 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
313 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
315 for (k
= 0; k
< INTC_ARRAY(mr
->enum_ids
); k
++) {
316 if (mr
->enum_ids
[k
] != source
)
319 s
= sh_intc_source(desc
, mr
->enum_ids
[k
]);
326 if (desc
->prio_regs
) {
327 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
328 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
330 for (k
= 0; k
< INTC_ARRAY(pr
->enum_ids
); k
++) {
331 if (pr
->enum_ids
[k
] != source
)
334 s
= sh_intc_source(desc
, pr
->enum_ids
[k
]);
342 for (i
= 0; i
< nr_groups
; i
++) {
343 struct intc_group
*gr
= groups
+ i
;
345 for (k
= 0; k
< INTC_ARRAY(gr
->enum_ids
); k
++) {
346 if (gr
->enum_ids
[k
] != source
)
349 s
= sh_intc_source(desc
, gr
->enum_ids
[k
]);
358 void sh_intc_register_sources(struct intc_desc
*desc
,
359 struct intc_vect
*vectors
,
361 struct intc_group
*groups
,
365 struct intc_source
*s
;
367 for (i
= 0; i
< nr_vectors
; i
++) {
368 struct intc_vect
*vect
= vectors
+ i
;
370 sh_intc_register_source(desc
, vect
->enum_id
, groups
, nr_groups
);
371 s
= sh_intc_source(desc
, vect
->enum_id
);
373 s
->vect
= vect
->vect
;
375 #ifdef DEBUG_INTC_SOURCES
376 printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
377 vect
->enum_id
, s
->vect
, s
->enable_count
, s
->enable_max
);
382 for (i
= 0; i
< nr_groups
; i
++) {
383 struct intc_group
*gr
= groups
+ i
;
385 s
= sh_intc_source(desc
, gr
->enum_id
);
386 s
->next_enum_id
= gr
->enum_ids
[0];
388 for (k
= 1; k
< INTC_ARRAY(gr
->enum_ids
); k
++) {
389 if (!gr
->enum_ids
[k
])
392 s
= sh_intc_source(desc
, gr
->enum_ids
[k
- 1]);
393 s
->next_enum_id
= gr
->enum_ids
[k
];
396 #ifdef DEBUG_INTC_SOURCES
397 printf("sh_intc: registered group %d (%d/%d)\n",
398 gr
->enum_id
, s
->enable_count
, s
->enable_max
);
404 int sh_intc_init(struct intc_desc
*desc
,
406 struct intc_mask_reg
*mask_regs
,
408 struct intc_prio_reg
*prio_regs
,
414 desc
->nr_sources
= nr_sources
;
415 desc
->mask_regs
= mask_regs
;
416 desc
->nr_mask_regs
= nr_mask_regs
;
417 desc
->prio_regs
= prio_regs
;
418 desc
->nr_prio_regs
= nr_prio_regs
;
420 i
= sizeof(struct intc_source
) * nr_sources
;
421 desc
->sources
= malloc(i
);
425 memset(desc
->sources
, 0, i
);
426 for (i
= 0; i
< desc
->nr_sources
; i
++) {
427 struct intc_source
*source
= desc
->sources
+ i
;
429 source
->parent
= desc
;
432 desc
->iomemtype
= cpu_register_io_memory(0, sh_intc_readfn
,
433 sh_intc_writefn
, desc
);
434 if (desc
->mask_regs
) {
435 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
436 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
438 sh_intc_register(desc
, mr
->set_reg
);
439 sh_intc_register(desc
, mr
->clr_reg
);
443 if (desc
->prio_regs
) {
444 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
445 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
447 sh_intc_register(desc
, pr
->set_reg
);
448 sh_intc_register(desc
, pr
->clr_reg
);