aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq/irqdomain.c
diff options
context:
space:
mode:
authorGrant Likely <grant.likely@secretlab.ca>2012-06-17 18:17:04 -0400
committerGrant Likely <grant.likely@secretlab.ca>2012-07-11 11:15:37 -0400
commit98aa468e045a0091a7c34d9f5205a629634fabf4 (patch)
treea08da590d80462c302750640eb88ec6c72859e1b /kernel/irq/irqdomain.c
parent2a71a1a9da40dfbd5b23d4312aa1641385581f4a (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.c113
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
413static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, 413int 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}
467EXPORT_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}
546EXPORT_SYMBOL_GPL(irq_create_mapping); 571EXPORT_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 */
591int 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}
609EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
610
548unsigned int irq_create_of_mapping(struct device_node *controller, 611unsigned 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{