diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2012-06-17 18:17:04 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2012-07-11 11:15:37 -0400 |
commit | 98aa468e045a0091a7c34d9f5205a629634fabf4 (patch) | |
tree | a08da590d80462c302750640eb88ec6c72859e1b /kernel/irq/irqdomain.c | |
parent | 2a71a1a9da40dfbd5b23d4312aa1641385581f4a (diff) |
irqdomain: Support for static IRQ mapping and association.
This adds a new strict mapping API for supporting creation of linux IRQs
at existing positions within the domain. The new routines are as follows:
For dynamic allocation and insertion to specified ranges:
- irq_create_identity_mapping()
- irq_create_strict_mappings()
These will allocate and associate a range of linux IRQs at the specified
location. This can be used by controllers that have their own static linux IRQ
definitions to map a hwirq range to, as well as for platforms that wish to
establish 1:1 identity mapping between linux and hwirq space.
For insertion to specified ranges by platforms that do their own irq_desc
management:
- irq_domain_associate()
- irq_domain_associate_many()
These in turn call back in to the domain's ->map() routine, for further
processing by the platform. Disassociation of IRQs get handled through
irq_dispose_mapping() as normal.
With these in place it should be possible to begin migration of legacy IRQ
domains to linear ones, without requiring special handling for static vs
dynamic IRQ definitions in DT vs non-DT paths. This also makes it possible
for domains with static mappings to adopt whichever tree model best fits
their needs, rather than simply restricting them to linear revmaps.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
[grant.likely: Reorganized irq_domain_associate{,_many} to have all logic in one place]
[grant.likely: Add error checking for unallocated irq_descs at associate time]
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Rob Herring <rob.herring@calxeda.com>
Diffstat (limited to 'kernel/irq/irqdomain.c')
-rw-r--r-- | kernel/irq/irqdomain.c | 113 |
1 files changed, 88 insertions, 25 deletions
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 091732c9dbdc..a07d92446b66 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -410,36 +410,61 @@ static void irq_domain_disassociate_many(struct irq_domain *domain, | |||
410 | } | 410 | } |
411 | } | 411 | } |
412 | 412 | ||
413 | static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, | 413 | int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, |
414 | irq_hw_number_t hwirq) | 414 | irq_hw_number_t hwirq_base, int count) |
415 | { | 415 | { |
416 | struct irq_data *irq_data = irq_get_irq_data(virq); | 416 | unsigned int virq = irq_base; |
417 | irq_hw_number_t hwirq = hwirq_base; | ||
418 | int i; | ||
417 | 419 | ||
418 | irq_data->hwirq = hwirq; | 420 | pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__, |
419 | irq_data->domain = domain; | 421 | of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count); |
420 | if (domain->ops->map && domain->ops->map(domain, virq, hwirq)) { | ||
421 | pr_err("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); | ||
422 | irq_data->domain = NULL; | ||
423 | irq_data->hwirq = 0; | ||
424 | return -1; | ||
425 | } | ||
426 | 422 | ||
427 | switch (domain->revmap_type) { | 423 | for (i = 0; i < count; i++) { |
428 | case IRQ_DOMAIN_MAP_LINEAR: | 424 | struct irq_data *irq_data = irq_get_irq_data(virq + i); |
429 | if (hwirq < domain->revmap_data.linear.size) | 425 | |
430 | domain->revmap_data.linear.revmap[hwirq] = virq; | 426 | if (WARN(!irq_data, "error: irq_desc not allocated; " |
431 | break; | 427 | "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i)) |
432 | case IRQ_DOMAIN_MAP_TREE: | 428 | return -EINVAL; |
433 | mutex_lock(&revmap_trees_mutex); | 429 | if (WARN(irq_data->domain, "error: irq_desc already associated; " |
434 | irq_radix_revmap_insert(domain, virq, hwirq); | 430 | "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i)) |
435 | mutex_unlock(&revmap_trees_mutex); | 431 | return -EINVAL; |
436 | break; | 432 | }; |
437 | } | 433 | |
434 | for (i = 0; i < count; i++, virq++, hwirq++) { | ||
435 | struct irq_data *irq_data = irq_get_irq_data(virq); | ||
436 | |||
437 | irq_data->hwirq = hwirq; | ||
438 | irq_data->domain = domain; | ||
439 | if (domain->ops->map && domain->ops->map(domain, virq, hwirq)) { | ||
440 | pr_err("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); | ||
441 | irq_data->domain = NULL; | ||
442 | irq_data->hwirq = 0; | ||
443 | goto err_unmap; | ||
444 | } | ||
445 | |||
446 | switch (domain->revmap_type) { | ||
447 | case IRQ_DOMAIN_MAP_LINEAR: | ||
448 | if (hwirq < domain->revmap_data.linear.size) | ||
449 | domain->revmap_data.linear.revmap[hwirq] = virq; | ||
450 | break; | ||
451 | case IRQ_DOMAIN_MAP_TREE: | ||
452 | mutex_lock(&revmap_trees_mutex); | ||
453 | irq_radix_revmap_insert(domain, virq, hwirq); | ||
454 | mutex_unlock(&revmap_trees_mutex); | ||
455 | break; | ||
456 | } | ||
438 | 457 | ||
439 | irq_clear_status_flags(virq, IRQ_NOREQUEST); | 458 | irq_clear_status_flags(virq, IRQ_NOREQUEST); |
459 | } | ||
440 | 460 | ||
441 | return 0; | 461 | return 0; |
462 | |||
463 | err_unmap: | ||
464 | irq_domain_disassociate_many(domain, irq_base, i); | ||
465 | return -EINVAL; | ||
442 | } | 466 | } |
467 | EXPORT_SYMBOL_GPL(irq_domain_associate_many); | ||
443 | 468 | ||
444 | /** | 469 | /** |
445 | * irq_create_direct_mapping() - Allocate an irq for direct mapping | 470 | * irq_create_direct_mapping() - Allocate an irq for direct mapping |
@@ -472,7 +497,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) | |||
472 | } | 497 | } |
473 | pr_debug("create_direct obtained virq %d\n", virq); | 498 | pr_debug("create_direct obtained virq %d\n", virq); |
474 | 499 | ||
475 | if (irq_setup_virq(domain, virq, virq)) { | 500 | if (irq_domain_associate(domain, virq, virq)) { |
476 | irq_free_desc(virq); | 501 | irq_free_desc(virq); |
477 | return 0; | 502 | return 0; |
478 | } | 503 | } |
@@ -533,7 +558,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, | |||
533 | return 0; | 558 | return 0; |
534 | } | 559 | } |
535 | 560 | ||
536 | if (irq_setup_virq(domain, virq, hwirq)) { | 561 | if (irq_domain_associate(domain, virq, hwirq)) { |
537 | irq_free_desc(virq); | 562 | irq_free_desc(virq); |
538 | return 0; | 563 | return 0; |
539 | } | 564 | } |
@@ -545,6 +570,44 @@ unsigned int irq_create_mapping(struct irq_domain *domain, | |||
545 | } | 570 | } |
546 | EXPORT_SYMBOL_GPL(irq_create_mapping); | 571 | EXPORT_SYMBOL_GPL(irq_create_mapping); |
547 | 572 | ||
573 | /** | ||
574 | * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs | ||
575 | * @domain: domain owning the interrupt range | ||
576 | * @irq_base: beginning of linux IRQ range | ||
577 | * @hwirq_base: beginning of hardware IRQ range | ||
578 | * @count: Number of interrupts to map | ||
579 | * | ||
580 | * This routine is used for allocating and mapping a range of hardware | ||
581 | * irqs to linux irqs where the linux irq numbers are at pre-defined | ||
582 | * locations. For use by controllers that already have static mappings | ||
583 | * to insert in to the domain. | ||
584 | * | ||
585 | * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time | ||
586 | * domain insertion. | ||
587 | * | ||
588 | * 0 is returned upon success, while any failure to establish a static | ||
589 | * mapping is treated as an error. | ||
590 | */ | ||
591 | int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base, | ||
592 | irq_hw_number_t hwirq_base, int count) | ||
593 | { | ||
594 | int ret; | ||
595 | |||
596 | ret = irq_alloc_descs(irq_base, irq_base, count, | ||
597 | of_node_to_nid(domain->of_node)); | ||
598 | if (unlikely(ret < 0)) | ||
599 | return ret; | ||
600 | |||
601 | ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count); | ||
602 | if (unlikely(ret < 0)) { | ||
603 | irq_free_descs(irq_base, count); | ||
604 | return ret; | ||
605 | } | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | EXPORT_SYMBOL_GPL(irq_create_strict_mappings); | ||
610 | |||
548 | unsigned int irq_create_of_mapping(struct device_node *controller, | 611 | unsigned int irq_create_of_mapping(struct device_node *controller, |
549 | const u32 *intspec, unsigned int intsize) | 612 | const u32 *intspec, unsigned int intsize) |
550 | { | 613 | { |