diff options
| author | Arnd Bergmann <arnd@arndb.de> | 2011-10-31 09:08:10 -0400 |
|---|---|---|
| committer | Arnd Bergmann <arnd@arndb.de> | 2011-10-31 09:08:10 -0400 |
| commit | 08cab72f91c8b28ffabfd143119bccdd4a115ad7 (patch) | |
| tree | ccd5583971caecd82bf2d1e62691bf6e0362d650 /drivers/of | |
| parent | 86c1e5a74af11e4817ffa6d7748d9ac1353b5b53 (diff) | |
| parent | f37a53cc5d8a8fb199e41386d125d8c2ed9e54ef (diff) | |
Merge branch 'dt/gic' into next/dt
Conflicts:
arch/arm/include/asm/localtimer.h
arch/arm/mach-msm/board-msm8x60.c
arch/arm/mach-omap2/board-generic.c
Diffstat (limited to 'drivers/of')
| -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 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 | |||
| 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 | } | ||
