diff options
author | Magnus Damm <damm@igel.co.jp> | 2007-07-19 23:09:29 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2007-07-19 23:18:21 -0400 |
commit | 680c45981ae2b4029878806d76aa17bb62d3c674 (patch) | |
tree | 57d0f10573fb577d1330f15ac6177666fe3cfb25 /arch/sh | |
parent | d0afa579698f33a65bc5c21d3d667dbb46f9e440 (diff) |
sh: intc - improve group support
This patch improves intc group support, ie it makes it possible to
group interrupts together and mask / unmask the entire group. This
also works with priorities, so setting a priority for an entire group
is also possible. This patch is needed to properly support certain
processors such as the 7780.
Fixes for NULL pointers in DECLARE_INTC_DESC() are also included.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/kernel/cpu/irq/intc.c | 93 |
1 files changed, 73 insertions, 20 deletions
diff --git a/arch/sh/kernel/cpu/irq/intc.c b/arch/sh/kernel/cpu/irq/intc.c index 626b4d8d7932..9345a7130e9e 100644 --- a/arch/sh/kernel/cpu/irq/intc.c +++ b/arch/sh/kernel/cpu/irq/intc.c | |||
@@ -244,13 +244,33 @@ static unsigned int __init intc_find_prio_handler(unsigned int width) | |||
244 | return REG_FN_ERROR; | 244 | return REG_FN_ERROR; |
245 | } | 245 | } |
246 | 246 | ||
247 | static intc_enum __init intc_grp_id(struct intc_desc *desc, intc_enum enum_id) | ||
248 | { | ||
249 | struct intc_group *g = desc->groups; | ||
250 | unsigned int i, j; | ||
251 | |||
252 | for (i = 0; g && enum_id && i < desc->nr_groups; i++) { | ||
253 | g = desc->groups + i; | ||
254 | |||
255 | for (j = 0; g->enum_ids[j]; j++) { | ||
256 | if (g->enum_ids[j] != enum_id) | ||
257 | continue; | ||
258 | |||
259 | return g->enum_id; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
247 | static unsigned int __init intc_prio_value(struct intc_desc *desc, | 266 | static unsigned int __init intc_prio_value(struct intc_desc *desc, |
248 | intc_enum enum_id) | 267 | intc_enum enum_id, int do_grps) |
249 | { | 268 | { |
269 | struct intc_prio *p = desc->priorities; | ||
250 | unsigned int i; | 270 | unsigned int i; |
251 | 271 | ||
252 | for (i = 0; i < desc->nr_priorities; i++) { | 272 | for (i = 0; p && enum_id && i < desc->nr_priorities; i++) { |
253 | struct intc_prio *p = desc->priorities + i; | 273 | p = desc->priorities + i; |
254 | 274 | ||
255 | if (p->enum_id != enum_id) | 275 | if (p->enum_id != enum_id) |
256 | continue; | 276 | continue; |
@@ -258,16 +278,24 @@ static unsigned int __init intc_prio_value(struct intc_desc *desc, | |||
258 | return p->priority; | 278 | return p->priority; |
259 | } | 279 | } |
260 | 280 | ||
261 | return 1; /* default to the lowest priority if no priority is set */ | 281 | if (do_grps) |
282 | return intc_prio_value(desc, intc_grp_id(desc, enum_id), 0); | ||
283 | |||
284 | /* default to the lowest priority possible if no priority is set | ||
285 | * - this needs to be at least 2 for 5-bit priorities on 7780 | ||
286 | */ | ||
287 | |||
288 | return 2; | ||
262 | } | 289 | } |
263 | 290 | ||
264 | static unsigned int __init intc_mask_data(struct intc_desc *desc, | 291 | static unsigned int __init intc_mask_data(struct intc_desc *desc, |
265 | intc_enum enum_id) | 292 | intc_enum enum_id, int do_grps) |
266 | { | 293 | { |
294 | struct intc_mask_reg *mr = desc->mask_regs; | ||
267 | unsigned int i, j, fn; | 295 | unsigned int i, j, fn; |
268 | 296 | ||
269 | for (i = 0; i < desc->nr_mask_regs; i++) { | 297 | for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) { |
270 | struct intc_mask_reg *mr = desc->mask_regs + i; | 298 | mr = desc->mask_regs + i; |
271 | 299 | ||
272 | for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { | 300 | for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { |
273 | if (mr->enum_ids[j] != enum_id) | 301 | if (mr->enum_ids[j] != enum_id) |
@@ -281,16 +309,20 @@ static unsigned int __init intc_mask_data(struct intc_desc *desc, | |||
281 | } | 309 | } |
282 | } | 310 | } |
283 | 311 | ||
312 | if (do_grps) | ||
313 | return intc_mask_data(desc, intc_grp_id(desc, enum_id), 0); | ||
314 | |||
284 | return 0; | 315 | return 0; |
285 | } | 316 | } |
286 | 317 | ||
287 | static unsigned int __init intc_prio_data(struct intc_desc *desc, | 318 | static unsigned int __init intc_prio_data(struct intc_desc *desc, |
288 | intc_enum enum_id) | 319 | intc_enum enum_id, int do_grps) |
289 | { | 320 | { |
321 | struct intc_prio_reg *pr = desc->prio_regs; | ||
290 | unsigned int i, j, fn, bit, prio; | 322 | unsigned int i, j, fn, bit, prio; |
291 | 323 | ||
292 | for (i = 0; i < desc->nr_prio_regs; i++) { | 324 | for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) { |
293 | struct intc_prio_reg *pr = desc->prio_regs + i; | 325 | pr = desc->prio_regs + i; |
294 | 326 | ||
295 | for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) { | 327 | for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) { |
296 | if (pr->enum_ids[j] != enum_id) | 328 | if (pr->enum_ids[j] != enum_id) |
@@ -300,7 +332,7 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, | |||
300 | if (fn == REG_FN_ERROR) | 332 | if (fn == REG_FN_ERROR) |
301 | return 0; | 333 | return 0; |
302 | 334 | ||
303 | prio = intc_prio_value(desc, enum_id); | 335 | prio = intc_prio_value(desc, enum_id, 1); |
304 | bit = pr->reg_width - ((j + 1) * pr->field_width); | 336 | bit = pr->reg_width - ((j + 1) * pr->field_width); |
305 | 337 | ||
306 | BUG_ON(bit < 0); | 338 | BUG_ON(bit < 0); |
@@ -309,27 +341,48 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, | |||
309 | } | 341 | } |
310 | } | 342 | } |
311 | 343 | ||
344 | if (do_grps) | ||
345 | return intc_prio_data(desc, intc_grp_id(desc, enum_id), 0); | ||
346 | |||
312 | return 0; | 347 | return 0; |
313 | } | 348 | } |
314 | 349 | ||
315 | static void __init intc_register_irq(struct intc_desc *desc, intc_enum enum_id, | 350 | static void __init intc_register_irq(struct intc_desc *desc, intc_enum enum_id, |
316 | unsigned int irq) | 351 | unsigned int irq) |
317 | { | 352 | { |
318 | unsigned int mask_data = intc_mask_data(desc, enum_id); | 353 | unsigned int data[2], primary; |
319 | unsigned int prio_data = intc_prio_data(desc, enum_id); | 354 | |
320 | unsigned int data = mask_data ? mask_data : prio_data; | 355 | /* Prefer single interrupt source bitmap over other combinations: |
356 | * 1. bitmap, single interrupt source | ||
357 | * 2. priority, single interrupt source | ||
358 | * 3. bitmap, multiple interrupt sources (groups) | ||
359 | * 4. priority, multiple interrupt sources (groups) | ||
360 | */ | ||
321 | 361 | ||
322 | BUG_ON(!data); | 362 | data[0] = intc_mask_data(desc, enum_id, 0); |
363 | data[1] = intc_prio_data(desc, enum_id, 0); | ||
364 | |||
365 | primary = 0; | ||
366 | if (!data[0] && data[1]) | ||
367 | primary = 1; | ||
368 | |||
369 | data[0] = data[0] ? data[0] : intc_mask_data(desc, enum_id, 1); | ||
370 | data[1] = data[1] ? data[1] : intc_prio_data(desc, enum_id, 1); | ||
371 | |||
372 | if (!data[primary]) | ||
373 | primary ^= 1; | ||
374 | |||
375 | BUG_ON(!data[primary]); /* must have primary masking method */ | ||
323 | 376 | ||
324 | disable_irq_nosync(irq); | 377 | disable_irq_nosync(irq); |
325 | set_irq_chip_and_handler_name(irq, &desc->chip, | 378 | set_irq_chip_and_handler_name(irq, &desc->chip, |
326 | handle_level_irq, "level"); | 379 | handle_level_irq, "level"); |
327 | set_irq_chip_data(irq, (void *)data); | 380 | set_irq_chip_data(irq, (void *)data[primary]); |
328 | |||
329 | /* set priority */ | ||
330 | 381 | ||
331 | if (prio_data) | 382 | /* enable secondary masking method if present */ |
332 | intc_reg_fns[_INTC_FN(prio_data)].enable(desc, prio_data); | 383 | if (data[!primary]) |
384 | intc_reg_fns[_INTC_FN(data[!primary])].enable(desc, | ||
385 | data[!primary]); | ||
333 | 386 | ||
334 | /* irq should be disabled by default */ | 387 | /* irq should be disabled by default */ |
335 | desc->chip.mask(irq); | 388 | desc->chip.mask(irq); |