diff options
| -rw-r--r-- | arch/powerpc/include/asm/irq.h | 27 | ||||
| -rw-r--r-- | arch/powerpc/kernel/irq.c | 240 |
2 files changed, 69 insertions, 198 deletions
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h index cb06b39f8e61..abdd7ef28cbf 100644 --- a/arch/powerpc/include/asm/irq.h +++ b/arch/powerpc/include/asm/irq.h | |||
| @@ -191,33 +191,6 @@ extern unsigned int irq_linear_revmap(struct irq_domain *host, | |||
| 191 | irq_hw_number_t hwirq); | 191 | irq_hw_number_t hwirq); |
| 192 | 192 | ||
| 193 | 193 | ||
| 194 | |||
| 195 | /** | ||
| 196 | * irq_alloc_virt - Allocate virtual irq numbers | ||
| 197 | * @host: host owning these new virtual irqs | ||
| 198 | * @count: number of consecutive numbers to allocate | ||
| 199 | * @hint: pass a hint number, the allocator will try to use a 1:1 mapping | ||
| 200 | * | ||
| 201 | * This is a low level function that is used internally by irq_create_mapping() | ||
| 202 | * and that can be used by some irq controllers implementations for things | ||
| 203 | * like allocating ranges of numbers for MSIs. The revmaps are left untouched. | ||
| 204 | */ | ||
| 205 | extern unsigned int irq_alloc_virt(struct irq_domain *host, | ||
| 206 | unsigned int count, | ||
| 207 | unsigned int hint); | ||
| 208 | |||
| 209 | /** | ||
| 210 | * irq_free_virt - Free virtual irq numbers | ||
| 211 | * @virq: virtual irq number of the first interrupt to free | ||
| 212 | * @count: number of interrupts to free | ||
| 213 | * | ||
| 214 | * This function is the opposite of irq_alloc_virt. It will not clear reverse | ||
| 215 | * maps, this should be done previously by unmap'ing the interrupt. In fact, | ||
| 216 | * all interrupts covered by the range being freed should have been unmapped | ||
| 217 | * prior to calling this. | ||
| 218 | */ | ||
| 219 | extern void irq_free_virt(unsigned int virq, unsigned int count); | ||
| 220 | |||
| 221 | /** | 194 | /** |
| 222 | * irq_early_init - Init irq remapping subsystem | 195 | * irq_early_init - Init irq remapping subsystem |
| 223 | */ | 196 | */ |
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 7305f2f65534..03c95f03d792 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
| @@ -491,38 +491,29 @@ void do_softirq(void) | |||
| 491 | * IRQ controller and virtual interrupts | 491 | * IRQ controller and virtual interrupts |
| 492 | */ | 492 | */ |
| 493 | 493 | ||
| 494 | /* The main irq map itself is an array of NR_IRQ entries containing the | ||
| 495 | * associate host and irq number. An entry with a host of NULL is free. | ||
| 496 | * An entry can be allocated if it's free, the allocator always then sets | ||
| 497 | * hwirq first to the host's invalid irq number and then fills ops. | ||
| 498 | */ | ||
| 499 | struct irq_map_entry { | ||
| 500 | irq_hw_number_t hwirq; | ||
| 501 | struct irq_domain *host; | ||
| 502 | }; | ||
| 503 | |||
| 504 | static LIST_HEAD(irq_domain_list); | 494 | static LIST_HEAD(irq_domain_list); |
| 505 | static DEFINE_RAW_SPINLOCK(irq_big_lock); | 495 | static DEFINE_MUTEX(irq_domain_mutex); |
| 506 | static DEFINE_MUTEX(revmap_trees_mutex); | 496 | static DEFINE_MUTEX(revmap_trees_mutex); |
| 507 | static struct irq_map_entry irq_map[NR_IRQS]; | ||
| 508 | static unsigned int irq_virq_count = NR_IRQS; | 497 | static unsigned int irq_virq_count = NR_IRQS; |
| 509 | static struct irq_domain *irq_default_host; | 498 | static struct irq_domain *irq_default_host; |
| 510 | 499 | ||
| 511 | irq_hw_number_t irqd_to_hwirq(struct irq_data *d) | 500 | irq_hw_number_t irqd_to_hwirq(struct irq_data *d) |
| 512 | { | 501 | { |
| 513 | return irq_map[d->irq].hwirq; | 502 | return d->hwirq; |
| 514 | } | 503 | } |
| 515 | EXPORT_SYMBOL_GPL(irqd_to_hwirq); | 504 | EXPORT_SYMBOL_GPL(irqd_to_hwirq); |
| 516 | 505 | ||
| 517 | irq_hw_number_t virq_to_hw(unsigned int virq) | 506 | irq_hw_number_t virq_to_hw(unsigned int virq) |
| 518 | { | 507 | { |
| 519 | return irq_map[virq].hwirq; | 508 | struct irq_data *irq_data = irq_get_irq_data(virq); |
| 509 | return WARN_ON(!irq_data) ? 0 : irq_data->hwirq; | ||
| 520 | } | 510 | } |
| 521 | EXPORT_SYMBOL_GPL(virq_to_hw); | 511 | EXPORT_SYMBOL_GPL(virq_to_hw); |
| 522 | 512 | ||
| 523 | bool virq_is_host(unsigned int virq, struct irq_domain *host) | 513 | bool virq_is_host(unsigned int virq, struct irq_domain *host) |
| 524 | { | 514 | { |
| 525 | return irq_map[virq].host == host; | 515 | struct irq_data *irq_data = irq_get_irq_data(virq); |
| 516 | return irq_data ? irq_data->domain == host : false; | ||
| 526 | } | 517 | } |
| 527 | EXPORT_SYMBOL_GPL(virq_is_host); | 518 | EXPORT_SYMBOL_GPL(virq_is_host); |
| 528 | 519 | ||
| @@ -537,11 +528,10 @@ struct irq_domain *irq_alloc_host(struct device_node *of_node, | |||
| 537 | struct irq_domain_ops *ops, | 528 | struct irq_domain_ops *ops, |
| 538 | irq_hw_number_t inval_irq) | 529 | irq_hw_number_t inval_irq) |
| 539 | { | 530 | { |
| 540 | struct irq_domain *host; | 531 | struct irq_domain *host, *h; |
| 541 | unsigned int size = sizeof(struct irq_domain); | 532 | unsigned int size = sizeof(struct irq_domain); |
| 542 | unsigned int i; | 533 | unsigned int i; |
| 543 | unsigned int *rmap; | 534 | unsigned int *rmap; |
| 544 | unsigned long flags; | ||
| 545 | 535 | ||
| 546 | /* Allocate structure and revmap table if using linear mapping */ | 536 | /* Allocate structure and revmap table if using linear mapping */ |
| 547 | if (revmap_type == IRQ_DOMAIN_MAP_LINEAR) | 537 | if (revmap_type == IRQ_DOMAIN_MAP_LINEAR) |
| @@ -559,23 +549,20 @@ struct irq_domain *irq_alloc_host(struct device_node *of_node, | |||
| 559 | if (host->ops->match == NULL) | 549 | if (host->ops->match == NULL) |
| 560 | host->ops->match = default_irq_host_match; | 550 | host->ops->match = default_irq_host_match; |
| 561 | 551 | ||
| 562 | raw_spin_lock_irqsave(&irq_big_lock, flags); | 552 | mutex_lock(&irq_domain_mutex); |
| 563 | 553 | /* Make sure only one legacy controller can be created */ | |
| 564 | /* If it's a legacy controller, check for duplicates and | ||
| 565 | * mark it as allocated (we use irq 0 host pointer for that | ||
| 566 | */ | ||
| 567 | if (revmap_type == IRQ_DOMAIN_MAP_LEGACY) { | 554 | if (revmap_type == IRQ_DOMAIN_MAP_LEGACY) { |
| 568 | if (irq_map[0].host != NULL) { | 555 | list_for_each_entry(h, &irq_domain_list, link) { |
| 569 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | 556 | if (WARN_ON(h->revmap_type == IRQ_DOMAIN_MAP_LEGACY)) { |
| 570 | of_node_put(host->of_node); | 557 | mutex_unlock(&irq_domain_mutex); |
| 571 | kfree(host); | 558 | of_node_put(host->of_node); |
| 572 | return NULL; | 559 | kfree(host); |
| 560 | return NULL; | ||
| 561 | } | ||
| 573 | } | 562 | } |
| 574 | irq_map[0].host = host; | ||
| 575 | } | 563 | } |
| 576 | |||
| 577 | list_add(&host->link, &irq_domain_list); | 564 | list_add(&host->link, &irq_domain_list); |
| 578 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | 565 | mutex_unlock(&irq_domain_mutex); |
| 579 | 566 | ||
| 580 | /* Additional setups per revmap type */ | 567 | /* Additional setups per revmap type */ |
| 581 | switch(revmap_type) { | 568 | switch(revmap_type) { |
| @@ -584,10 +571,9 @@ struct irq_domain *irq_alloc_host(struct device_node *of_node, | |||
| 584 | host->inval_irq = 0; | 571 | host->inval_irq = 0; |
| 585 | /* setup us as the host for all legacy interrupts */ | 572 | /* setup us as the host for all legacy interrupts */ |
| 586 | for (i = 1; i < NUM_ISA_INTERRUPTS; i++) { | 573 | for (i = 1; i < NUM_ISA_INTERRUPTS; i++) { |
| 587 | irq_map[i].hwirq = i; | 574 | struct irq_data *irq_data = irq_get_irq_data(i); |
| 588 | smp_wmb(); | 575 | irq_data->hwirq = i; |
| 589 | irq_map[i].host = host; | 576 | irq_data->domain = host; |
| 590 | smp_wmb(); | ||
| 591 | 577 | ||
| 592 | /* Legacy flags are left to default at this point, | 578 | /* Legacy flags are left to default at this point, |
| 593 | * one can then use irq_create_mapping() to | 579 | * one can then use irq_create_mapping() to |
| @@ -604,7 +590,6 @@ struct irq_domain *irq_alloc_host(struct device_node *of_node, | |||
| 604 | for (i = 0; i < revmap_arg; i++) | 590 | for (i = 0; i < revmap_arg; i++) |
| 605 | rmap[i] = NO_IRQ; | 591 | rmap[i] = NO_IRQ; |
| 606 | host->revmap_data.linear.size = revmap_arg; | 592 | host->revmap_data.linear.size = revmap_arg; |
| 607 | smp_wmb(); | ||
| 608 | host->revmap_data.linear.revmap = rmap; | 593 | host->revmap_data.linear.revmap = rmap; |
| 609 | break; | 594 | break; |
| 610 | case IRQ_DOMAIN_MAP_TREE: | 595 | case IRQ_DOMAIN_MAP_TREE: |
| @@ -622,20 +607,19 @@ struct irq_domain *irq_alloc_host(struct device_node *of_node, | |||
| 622 | struct irq_domain *irq_find_host(struct device_node *node) | 607 | struct irq_domain *irq_find_host(struct device_node *node) |
| 623 | { | 608 | { |
| 624 | struct irq_domain *h, *found = NULL; | 609 | struct irq_domain *h, *found = NULL; |
| 625 | unsigned long flags; | ||
| 626 | 610 | ||
| 627 | /* We might want to match the legacy controller last since | 611 | /* We might want to match the legacy controller last since |
| 628 | * it might potentially be set to match all interrupts in | 612 | * it might potentially be set to match all interrupts in |
| 629 | * the absence of a device node. This isn't a problem so far | 613 | * the absence of a device node. This isn't a problem so far |
| 630 | * yet though... | 614 | * yet though... |
| 631 | */ | 615 | */ |
| 632 | raw_spin_lock_irqsave(&irq_big_lock, flags); | 616 | mutex_lock(&irq_domain_mutex); |
| 633 | list_for_each_entry(h, &irq_domain_list, link) | 617 | list_for_each_entry(h, &irq_domain_list, link) |
| 634 | if (h->ops->match(h, node)) { | 618 | |
