aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
authorRob Herring <rob.herring@calxeda.com>2011-09-20 16:13:50 -0400
committerArnd Bergmann <arnd@arndb.de>2011-10-31 09:03:22 -0400
commitc71a54b0820179e53483d5220cdef1a0df8d5bd1 (patch)
tree844e32441d70118f616d4b4c84b510aaec2e5dc1 /drivers/of
parent3a8254364277fabe01bc0e12b9691722939f5ef3 (diff)
of/irq: introduce of_irq_init
of_irq_init will scan the devicetree for matching interrupt controller nodes. Then it calls an initialization function for each found controller in the proper order with parent nodes initialized before child nodes. Based on initial pseudo code from Grant Likely. Changes in v4: - Drop unnecessary empty list check - Be more verbose on errors - Simplify "if (!desc) WARN_ON(1)" to "if (WARN_ON(!desc))" Changes in v3: - add missing kfree's found by Jamie - Implement Grant's comments to simplify the init loop - fix function comments Changes in v2: - Complete re-write of list searching code from Grant Likely Signed-off-by: Rob Herring <rob.herring@calxeda.com> Reviewed-by: Jamie Iles <jamie@jamieiles.com> Tested-by: Thomas Abraham <thomas.abraham@linaro.org> Acked-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/irq.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 9f689f1da0fc..791270b8bd1c 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
392struct 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 */
405void __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 }
490err:
491 list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
492 list_del(&desc->list);
493 kfree(desc);
494 }
495}