diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-02 00:02:35 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-02 00:02:35 -0400 |
commit | 367069f16e32e188d4687fe2c3e30f2ca583836f (patch) | |
tree | 977f87038b75e53af9d132cba2f7a2aecb2fa005 /drivers/of/irq.c | |
parent | 81a3c10ce8a7fd5bf9a06bfc38bd417512911831 (diff) | |
parent | c72dbae971400e466ad9ff16c920cd6d9d8c55a1 (diff) |
Merge branch 'next/dt' of git://git.linaro.org/people/arnd/arm-soc
* 'next/dt' of git://git.linaro.org/people/arnd/arm-soc:
ARM: gic: use module.h instead of export.h
ARM: gic: fix irq_alloc_descs handling for sparse irq
ARM: gic: add OF based initialization
ARM: gic: add irq_domain support
irq: support domains with non-zero hwirq base
of/irq: introduce of_irq_init
ARM: at91: add at91sam9g20 and Calao USB A9G20 DT support
ARM: at91: dt: at91sam9g45 family and board device tree files
arm/mx5: add device tree support for imx51 babbage
arm/mx5: add device tree support for imx53 boards
ARM: msm: Add devicetree support for msm8660-surf
msm_serial: Add devicetree support
msm_serial: Use relative resources for iomem
Fix up conflicts in arch/arm/mach-at91/{at91sam9260.c,at91sam9g45.c}
Diffstat (limited to 'drivers/of/irq.c')
-rw-r--r-- | drivers/of/irq.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 6a5b5e777dd2..6d3dd3988d0f 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c | |||
@@ -19,10 +19,12 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/errno.h> | 21 | #include <linux/errno.h> |
22 | #include <linux/list.h> | ||
22 | #include <linux/module.h> | 23 | #include <linux/module.h> |
23 | #include <linux/of.h> | 24 | #include <linux/of.h> |
24 | #include <linux/of_irq.h> | 25 | #include <linux/of_irq.h> |
25 | #include <linux/string.h> | 26 | #include <linux/string.h> |
27 | #include <linux/slab.h> | ||
26 | 28 | ||
27 | /* For archs that don't support NO_IRQ (such as x86), provide a dummy value */ | 29 | /* For archs that don't support NO_IRQ (such as x86), provide a dummy value */ |
28 | #ifndef NO_IRQ | 30 | #ifndef NO_IRQ |
@@ -386,3 +388,108 @@ int of_irq_to_resource_table(struct device_node *dev, struct resource *res, | |||
386 | 388 | ||
387 | return i; | 389 | return i; |
388 | } | 390 | } |
391 | |||
392 | struct intc_desc { | ||
393 | struct list_head list; | ||
394 | struct device_node *dev; | ||
395 | struct device_node *interrupt_parent; | ||
396 | }; | ||
397 | |||
398 | /** | ||
399 | * of_irq_init - Scan and init matching interrupt controllers in DT | ||
400 | * @matches: 0 terminated array of nodes to match and init function to call | ||
401 | * | ||
402 | * This function scans the device tree for matching interrupt controller nodes, | ||
403 | * and calls their initialization functions in order with parents first. | ||
404 | */ | ||
405 | void __init of_irq_init(const struct of_device_id *matches) | ||
406 | { | ||
407 | struct device_node *np, *parent = NULL; | ||
408 | struct intc_desc *desc, *temp_desc; | ||
409 | struct list_head intc_desc_list, intc_parent_list; | ||
410 | |||
411 | INIT_LIST_HEAD(&intc_desc_list); | ||
412 | INIT_LIST_HEAD(&intc_parent_list); | ||
413 | |||
414 | for_each_matching_node(np, matches) { | ||
415 | if (!of_find_property(np, "interrupt-controller", NULL)) | ||
416 | continue; | ||
417 | /* | ||
418 | * Here, we allocate and populate an intc_desc with the node | ||
419 | * pointer, interrupt-parent device_node etc. | ||
420 | */ | ||
421 | desc = kzalloc(sizeof(*desc), GFP_KERNEL); | ||
422 | if (WARN_ON(!desc)) | ||
423 | goto err; | ||
424 | |||
425 | desc->dev = np; | ||
426 | desc->interrupt_parent = of_irq_find_parent(np); | ||
427 | list_add_tail(&desc->list, &intc_desc_list); | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * The root irq controller is the one without an interrupt-parent. | ||
432 | * That one goes first, followed by the controllers that reference it, | ||
433 | * followed by the ones that reference the 2nd level controllers, etc. | ||
434 | */ | ||
435 | while (!list_empty(&intc_desc_list)) { | ||
436 | /* | ||
437 | * Process all controllers with the current 'parent'. | ||
438 | * First pass will be looking for NULL as the parent. | ||
439 | * The assumption is that NULL parent means a root controller. | ||
440 | */ | ||
441 | list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { | ||
442 | const struct of_device_id *match; | ||
443 | int ret; | ||
444 | of_irq_init_cb_t irq_init_cb; | ||
445 | |||
446 | if (desc->interrupt_parent != parent) | ||
447 | continue; | ||
448 | |||
449 | list_del(&desc->list); | ||
450 | match = of_match_node(matches, desc->dev); | ||
451 | if (WARN(!match->data, | ||
452 | "of_irq_init: no init function for %s\n", | ||
453 | match->compatible)) { | ||
454 | kfree(desc); | ||
455 | continue; | ||
456 | } | ||
457 | |||
458 | pr_debug("of_irq_init: init %s @ %p, parent %p\n", | ||
459 | match->compatible, | ||
460 | desc->dev, desc->interrupt_parent); | ||
461 | irq_init_cb = match->data; | ||
462 | ret = irq_init_cb(desc->dev, desc->interrupt_parent); | ||
463 | if (ret) { | ||
464 | kfree(desc); | ||
465 | continue; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * This one is now set up; add it to the parent list so | ||
470 | * its children can get processed in a subsequent pass. | ||
471 | */ | ||
472 | list_add_tail(&desc->list, &intc_parent_list); | ||
473 | } | ||
474 | |||
475 | /* Get the next pending parent that might have children */ | ||
476 | desc = list_first_entry(&intc_parent_list, typeof(*desc), list); | ||
477 | if (list_empty(&intc_parent_list) || !desc) { | ||
478 | pr_err("of_irq_init: children remain, but no parents\n"); | ||
479 | break; | ||
480 | } | ||
481 | list_del(&desc->list); | ||
482 | parent = desc->dev; | ||
483 | kfree(desc); | ||
484 | } | ||
485 | |||
486 | list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) { | ||
487 | list_del(&desc->list); | ||
488 | kfree(desc); | ||
489 | } | ||
490 | err: | ||
491 | list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { | ||
492 | list_del(&desc->list); | ||
493 | kfree(desc); | ||
494 | } | ||
495 | } | ||