diff options
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 | } | ||