aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/irqdomain.h19
-rw-r--r--kernel/irq/irqdomain.c113
2 files changed, 107 insertions, 25 deletions
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 17b60be30fff..eab8a0e60b8e 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -149,12 +149,31 @@ static inline struct irq_domain *irq_domain_add_legacy_isa(
149 149
150extern void irq_domain_remove(struct irq_domain *host); 150extern void irq_domain_remove(struct irq_domain *host);
151 151
152extern int irq_domain_associate_many(struct irq_domain *domain,
153 unsigned int irq_base,
154 irq_hw_number_t hwirq_base, int count);
155static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
156 irq_hw_number_t hwirq)
157{
158 return irq_domain_associate_many(domain, irq, hwirq, 1);
159}
160
152extern unsigned int irq_create_mapping(struct irq_domain *host, 161extern unsigned int irq_create_mapping(struct irq_domain *host,
153 irq_hw_number_t hwirq); 162 irq_hw_number_t hwirq);
154extern void irq_dispose_mapping(unsigned int virq); 163extern void irq_dispose_mapping(unsigned int virq);
155extern unsigned int irq_find_mapping(struct irq_domain *host, 164extern unsigned int irq_find_mapping(struct irq_domain *host,
156 irq_hw_number_t hwirq); 165 irq_hw_number_t hwirq);
157extern unsigned int irq_create_direct_mapping(struct irq_domain *host); 166extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
167extern int irq_create_strict_mappings(struct irq_domain *domain,
168 unsigned int irq_base,
169 irq_hw_number_t hwirq_base, int count);
170
171static inline int irq_create_identity_mapping(struct irq_domain *host,
172 irq_hw_number_t hwirq)
173{
174 return irq_create_strict_mappings(host, hwirq, hwirq, 1);
175}
176
158extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, 177extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq,
159 irq_hw_number_t hwirq); 178 irq_hw_number_t hwirq);
160extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, 179extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host,
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{