diff options
-rw-r--r-- | include/linux/irqdomain.h | 19 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 113 |
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 | ||
150 | extern void irq_domain_remove(struct irq_domain *host); | 150 | extern void irq_domain_remove(struct irq_domain *host); |
151 | 151 | ||
152 | extern int irq_domain_associate_many(struct irq_domain *domain, | ||
153 | unsigned int irq_base, | ||
154 | irq_hw_number_t hwirq_base, int count); | ||
155 | static 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 | |||
152 | extern unsigned int irq_create_mapping(struct irq_domain *host, | 161 | extern unsigned int irq_create_mapping(struct irq_domain *host, |
153 | irq_hw_number_t hwirq); | 162 | irq_hw_number_t hwirq); |
154 | extern void irq_dispose_mapping(unsigned int virq); | 163 | extern void irq_dispose_mapping(unsigned int virq); |
155 | extern unsigned int irq_find_mapping(struct irq_domain *host, | 164 | extern unsigned int irq_find_mapping(struct irq_domain *host, |
156 | irq_hw_number_t hwirq); | 165 | irq_hw_number_t hwirq); |
157 | extern unsigned int irq_create_direct_mapping(struct irq_domain *host); | 166 | extern unsigned int irq_create_direct_mapping(struct irq_domain *host); |
167 | extern int irq_create_strict_mappings(struct irq_domain *domain, | ||
168 | unsigned int irq_base, | ||
169 | irq_hw_number_t hwirq_base, int count); | ||
170 | |||
171 | static 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 | |||
158 | extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, | 177 | extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, |
159 | irq_hw_number_t hwirq); | 178 | irq_hw_number_t hwirq); |
160 | extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, | 179 | extern 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 | ||
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 | { |