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.
18 #define INTC_A7(x) ((x) & 0x1fffffff)
19 #define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0]))
21 #define INTC_MODE_NONE 0
22 #define INTC_MODE_DUAL_SET 1
23 #define INTC_MODE_DUAL_CLR 2
24 #define INTC_MODE_ENABLE_REG 3
25 #define INTC_MODE_MASK_REG 4
26 #define INTC_MODE_IS_PRIO 8
28 static unsigned int sh_intc_mode(unsigned long address
,
29 unsigned long set_reg
, unsigned long clr_reg
)
31 if ((address
!= INTC_A7(set_reg
)) &&
32 (address
!= INTC_A7(clr_reg
)))
33 return INTC_MODE_NONE
;
35 if (set_reg
&& clr_reg
) {
36 if (address
== INTC_A7(set_reg
))
37 return INTC_MODE_DUAL_SET
;
39 return INTC_MODE_DUAL_CLR
;
43 return INTC_MODE_ENABLE_REG
;
45 return INTC_MODE_MASK_REG
;
48 static void sh_intc_locate(struct intc_desc
*desc
,
49 unsigned long address
,
50 unsigned long **datap
,
58 /* this is slow but works for now */
60 if (desc
->mask_regs
) {
61 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
62 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
64 mode
= sh_intc_mode(address
, mr
->set_reg
, mr
->clr_reg
);
65 if (mode
== INTC_MODE_NONE
)
70 *enums
= mr
->enum_ids
;
71 *first
= mr
->reg_width
- 1;
77 if (desc
->prio_regs
) {
78 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
79 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
81 mode
= sh_intc_mode(address
, pr
->set_reg
, pr
->clr_reg
);
82 if (mode
== INTC_MODE_NONE
)
85 *modep
= mode
| INTC_MODE_IS_PRIO
;
87 *enums
= pr
->enum_ids
;
88 *first
= (pr
->reg_width
/ pr
->field_width
) - 1;
89 *width
= pr
->field_width
;
97 static void sh_intc_toggle(struct intc_desc
*desc
, intc_enum id
,
98 int enable
, int is_group
)
100 struct intc_source
*source
= desc
->sources
+ id
;
101 int old
= source
->enable_count
;
106 if (!source
->next_enum_id
&& (!source
->enable_max
|| !source
->vect
)) {
108 printf("sh_intc: reserved interrupt source %d modified\n", id
);
115 source
->enable_count
++;
117 source
->enable_count
--;
119 if (source
->enable_count
== source
->enable_max
) {
121 printf("sh_intc: enabling interrupt source %d -> 0x%04x\n",
126 if (old
== source
->enable_max
) {
128 printf("sh_intc: disabling interrupt source %d -> 0x%04x\n",
135 printf("setting interrupt group %d to %d\n", id
, !!enable
);
139 if ((is_group
|| !source
->vect
) && source
->next_enum_id
) {
140 sh_intc_toggle(desc
, source
->next_enum_id
, enable
, 1);
145 printf("setting interrupt group %d to %d - done\n", id
, !!enable
);
150 static uint32_t sh_intc_read(void *opaque
, target_phys_addr_t offset
)
152 struct intc_desc
*desc
= opaque
;
153 intc_enum
*enum_ids
= NULL
;
154 unsigned int first
= 0;
155 unsigned int width
= 0;
156 unsigned int mode
= 0;
157 unsigned long *valuep
;
160 printf("sh_intc_read 0x%lx\n", (unsigned long) offset
);
163 sh_intc_locate(desc
, (unsigned long)offset
, &valuep
,
164 &enum_ids
, &first
, &width
, &mode
);
168 static void sh_intc_write(void *opaque
, target_phys_addr_t offset
,
171 struct intc_desc
*desc
= opaque
;
172 intc_enum
*enum_ids
= NULL
;
173 unsigned int first
= 0;
174 unsigned int width
= 0;
175 unsigned int mode
= 0;
177 unsigned long *valuep
;
181 printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset
, value
);
184 sh_intc_locate(desc
, (unsigned long)offset
, &valuep
,
185 &enum_ids
, &first
, &width
, &mode
);
188 case INTC_MODE_ENABLE_REG
| INTC_MODE_IS_PRIO
: break;
189 case INTC_MODE_DUAL_SET
: value
|= *valuep
; break;
190 case INTC_MODE_DUAL_CLR
: value
= *valuep
& ~value
; break;
194 for (k
= 0; k
<= first
; k
++) {
195 mask
= ((1 << width
) - 1) << ((first
- k
) * width
);
197 if ((*valuep
& mask
) == (value
& mask
))
200 printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
201 k
, first
, enum_ids
[k
], (unsigned int)mask
);
203 sh_intc_toggle(desc
, enum_ids
[k
], value
& mask
, 0);
209 printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset
, value
);
213 static CPUReadMemoryFunc
*sh_intc_readfn
[] = {
219 static CPUWriteMemoryFunc
*sh_intc_writefn
[] = {
225 struct intc_source
*sh_intc_source(struct intc_desc
*desc
, intc_enum id
)
228 return desc
->sources
+ id
;
233 static void sh_intc_register(struct intc_desc
*desc
,
234 unsigned long address
)
237 cpu_register_physical_memory(INTC_A7(address
), 4, desc
->iomemtype
);
240 static void sh_intc_register_source(struct intc_desc
*desc
,
242 struct intc_group
*groups
,
246 struct intc_source
*s
;
248 if (desc
->mask_regs
) {
249 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
250 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
252 for (k
= 0; k
< INTC_ARRAY(mr
->enum_ids
); k
++) {
253 if (mr
->enum_ids
[k
] != source
)
256 s
= sh_intc_source(desc
, mr
->enum_ids
[k
]);
263 if (desc
->prio_regs
) {
264 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
265 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
267 for (k
= 0; k
< INTC_ARRAY(pr
->enum_ids
); k
++) {
268 if (pr
->enum_ids
[k
] != source
)
271 s
= sh_intc_source(desc
, pr
->enum_ids
[k
]);
279 for (i
= 0; i
< nr_groups
; i
++) {
280 struct intc_group
*gr
= groups
+ i
;
282 for (k
= 0; k
< INTC_ARRAY(gr
->enum_ids
); k
++) {
283 if (gr
->enum_ids
[k
] != source
)
286 s
= sh_intc_source(desc
, gr
->enum_ids
[k
]);
295 void sh_intc_register_sources(struct intc_desc
*desc
,
296 struct intc_vect
*vectors
,
298 struct intc_group
*groups
,
302 struct intc_source
*s
;
304 for (i
= 0; i
< nr_vectors
; i
++) {
305 struct intc_vect
*vect
= vectors
+ i
;
307 sh_intc_register_source(desc
, vect
->enum_id
, groups
, nr_groups
);
308 s
= sh_intc_source(desc
, vect
->enum_id
);
310 s
->vect
= vect
->vect
;
313 printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
314 vect
->enum_id
, s
->vect
, s
->enable_count
, s
->enable_max
);
319 for (i
= 0; i
< nr_groups
; i
++) {
320 struct intc_group
*gr
= groups
+ i
;
322 s
= sh_intc_source(desc
, gr
->enum_id
);
323 s
->next_enum_id
= gr
->enum_ids
[0];
325 for (k
= 1; k
< INTC_ARRAY(gr
->enum_ids
); k
++) {
326 if (!gr
->enum_ids
[k
])
329 s
= sh_intc_source(desc
, gr
->enum_ids
[k
- 1]);
330 s
->next_enum_id
= gr
->enum_ids
[k
];
334 printf("sh_intc: registered group %d (%d/%d)\n",
335 gr
->enum_id
, s
->enable_count
, s
->enable_max
);
341 int sh_intc_init(struct intc_desc
*desc
,
343 struct intc_mask_reg
*mask_regs
,
345 struct intc_prio_reg
*prio_regs
,
350 desc
->nr_sources
= nr_sources
;
351 desc
->mask_regs
= mask_regs
;
352 desc
->nr_mask_regs
= nr_mask_regs
;
353 desc
->prio_regs
= prio_regs
;
354 desc
->nr_prio_regs
= nr_prio_regs
;
356 i
= sizeof(struct intc_source
) * nr_sources
;
357 desc
->sources
= malloc(i
);
361 memset(desc
->sources
, 0, i
);
363 desc
->iomemtype
= cpu_register_io_memory(0, sh_intc_readfn
,
364 sh_intc_writefn
, desc
);
365 if (desc
->mask_regs
) {
366 for (i
= 0; i
< desc
->nr_mask_regs
; i
++) {
367 struct intc_mask_reg
*mr
= desc
->mask_regs
+ i
;
369 sh_intc_register(desc
, mr
->set_reg
);
370 sh_intc_register(desc
, mr
->clr_reg
);
374 if (desc
->prio_regs
) {
375 for (i
= 0; i
< desc
->nr_prio_regs
; i
++) {
376 struct intc_prio_reg
*pr
= desc
->prio_regs
+ i
;
378 sh_intc_register(desc
, pr
->set_reg
);
379 sh_intc_register(desc
, pr
->clr_reg
);