diff options
author | Paul Mundt <lethal@linux-sh.org> | 2011-05-23 23:05:26 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-05-23 23:05:26 -0400 |
commit | 8b1aaeaf54f1bcaa0bbec6bb170db367c998d27c (patch) | |
tree | 2478e1708f5a3da261597f4aa1011d4d41c2cc84 /arch/powerpc/kernel/irq.c | |
parent | bca606a646a2b1f4fa1ae2b22a0ac707505d7297 (diff) | |
parent | 5e152b4c9e0fce6149c74406346a7ae7e7a17727 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into rmobile-latest
Diffstat (limited to 'arch/powerpc/kernel/irq.c')
-rw-r--r-- | arch/powerpc/kernel/irq.c | 166 |
1 files changed, 63 insertions, 103 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index f621b7d2d869..a24d37d4cf51 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -66,7 +66,6 @@ | |||
66 | #include <asm/ptrace.h> | 66 | #include <asm/ptrace.h> |
67 | #include <asm/machdep.h> | 67 | #include <asm/machdep.h> |
68 | #include <asm/udbg.h> | 68 | #include <asm/udbg.h> |
69 | #include <asm/dbell.h> | ||
70 | #include <asm/smp.h> | 69 | #include <asm/smp.h> |
71 | 70 | ||
72 | #ifdef CONFIG_PPC64 | 71 | #ifdef CONFIG_PPC64 |
@@ -160,7 +159,8 @@ notrace void arch_local_irq_restore(unsigned long en) | |||
160 | 159 | ||
161 | #if defined(CONFIG_BOOKE) && defined(CONFIG_SMP) | 160 | #if defined(CONFIG_BOOKE) && defined(CONFIG_SMP) |
162 | /* Check for pending doorbell interrupts and resend to ourself */ | 161 | /* Check for pending doorbell interrupts and resend to ourself */ |
163 | doorbell_check_self(); | 162 | if (cpu_has_feature(CPU_FTR_DBELL)) |
163 | smp_muxed_ipi_resend(); | ||
164 | #endif | 164 | #endif |
165 | 165 | ||
166 | /* | 166 | /* |
@@ -397,24 +397,28 @@ struct thread_info *mcheckirq_ctx[NR_CPUS] __read_mostly; | |||
397 | void exc_lvl_ctx_init(void) | 397 | void exc_lvl_ctx_init(void) |
398 | { | 398 | { |
399 | struct thread_info *tp; | 399 | struct thread_info *tp; |
400 | int i, hw_cpu; | 400 | int i, cpu_nr; |
401 | 401 | ||
402 | for_each_possible_cpu(i) { | 402 | for_each_possible_cpu(i) { |
403 | hw_cpu = get_hard_smp_processor_id(i); | 403 | #ifdef CONFIG_PPC64 |
404 | memset((void *)critirq_ctx[hw_cpu], 0, THREAD_SIZE); | 404 | cpu_nr = i; |
405 | tp = critirq_ctx[hw_cpu]; | 405 | #else |
406 | tp->cpu = i; | 406 | cpu_nr = get_hard_smp_processor_id(i); |
407 | #endif | ||
408 | memset((void *)critirq_ctx[cpu_nr], 0, THREAD_SIZE); | ||
409 | tp = critirq_ctx[cpu_nr]; | ||
410 | tp->cpu = cpu_nr; | ||
407 | tp->preempt_count = 0; | 411 | tp->preempt_count = 0; |
408 | 412 | ||
409 | #ifdef CONFIG_BOOKE | 413 | #ifdef CONFIG_BOOKE |
410 | memset((void *)dbgirq_ctx[hw_cpu], 0, THREAD_SIZE); | 414 | memset((void *)dbgirq_ctx[cpu_nr], 0, THREAD_SIZE); |
411 | tp = dbgirq_ctx[hw_cpu]; | 415 | tp = dbgirq_ctx[cpu_nr]; |
412 | tp->cpu = i; | 416 | tp->cpu = cpu_nr; |
413 | tp->preempt_count = 0; | 417 | tp->preempt_count = 0; |
414 | 418 | ||
415 | memset((void *)mcheckirq_ctx[hw_cpu], 0, THREAD_SIZE); | 419 | memset((void *)mcheckirq_ctx[cpu_nr], 0, THREAD_SIZE); |
416 | tp = mcheckirq_ctx[hw_cpu]; | 420 | tp = mcheckirq_ctx[cpu_nr]; |
417 | tp->cpu = i; | 421 | tp->cpu = cpu_nr; |
418 | tp->preempt_count = HARDIRQ_OFFSET; | 422 | tp->preempt_count = HARDIRQ_OFFSET; |
419 | #endif | 423 | #endif |
420 | } | 424 | } |
@@ -477,20 +481,41 @@ void do_softirq(void) | |||
477 | * IRQ controller and virtual interrupts | 481 | * IRQ controller and virtual interrupts |
478 | */ | 482 | */ |
479 | 483 | ||
484 | /* The main irq map itself is an array of NR_IRQ entries containing the | ||
485 | * associate host and irq number. An entry with a host of NULL is free. | ||
486 | * An entry can be allocated if it's free, the allocator always then sets | ||
487 | * hwirq first to the host's invalid irq number and then fills ops. | ||
488 | */ | ||
489 | struct irq_map_entry { | ||
490 | irq_hw_number_t hwirq; | ||
491 | struct irq_host *host; | ||
492 | }; | ||
493 | |||
480 | static LIST_HEAD(irq_hosts); | 494 | static LIST_HEAD(irq_hosts); |
481 | static DEFINE_RAW_SPINLOCK(irq_big_lock); | 495 | static DEFINE_RAW_SPINLOCK(irq_big_lock); |
482 | static unsigned int revmap_trees_allocated; | ||
483 | static DEFINE_MUTEX(revmap_trees_mutex); | 496 | static DEFINE_MUTEX(revmap_trees_mutex); |
484 | struct irq_map_entry irq_map[NR_IRQS]; | 497 | static struct irq_map_entry irq_map[NR_IRQS]; |
485 | static unsigned int irq_virq_count = NR_IRQS; | 498 | static unsigned int irq_virq_count = NR_IRQS; |
486 | static struct irq_host *irq_default_host; | 499 | static struct irq_host *irq_default_host; |
487 | 500 | ||
501 | irq_hw_number_t irqd_to_hwirq(struct irq_data *d) | ||
502 | { | ||
503 | return irq_map[d->irq].hwirq; | ||
504 | } | ||
505 | EXPORT_SYMBOL_GPL(irqd_to_hwirq); | ||
506 | |||
488 | irq_hw_number_t virq_to_hw(unsigned int virq) | 507 | irq_hw_number_t virq_to_hw(unsigned int virq) |
489 | { | 508 | { |
490 | return irq_map[virq].hwirq; | 509 | return irq_map[virq].hwirq; |
491 | } | 510 | } |
492 | EXPORT_SYMBOL_GPL(virq_to_hw); | 511 | EXPORT_SYMBOL_GPL(virq_to_hw); |
493 | 512 | ||
513 | bool virq_is_host(unsigned int virq, struct irq_host *host) | ||
514 | { | ||
515 | return irq_map[virq].host == host; | ||
516 | } | ||
517 | EXPORT_SYMBOL_GPL(virq_is_host); | ||
518 | |||
494 | static int default_irq_host_match(struct irq_host *h, struct device_node *np) | 519 | static int default_irq_host_match(struct irq_host *h, struct device_node *np) |
495 | { | 520 | { |
496 | return h->of_node != NULL && h->of_node == np; | 521 | return h->of_node != NULL && h->of_node == np; |
@@ -511,7 +536,7 @@ struct irq_host *irq_alloc_host(struct device_node *of_node, | |||
511 | /* Allocate structure and revmap table if using linear mapping */ | 536 | /* Allocate structure and revmap table if using linear mapping */ |
512 | if (revmap_type == IRQ_HOST_MAP_LINEAR) | 537 | if (revmap_type == IRQ_HOST_MAP_LINEAR) |
513 | size += revmap_arg * sizeof(unsigned int); | 538 | size += revmap_arg * sizeof(unsigned int); |
514 | host = zalloc_maybe_bootmem(size, GFP_KERNEL); | 539 | host = kzalloc(size, GFP_KERNEL); |
515 | if (host == NULL) | 540 | if (host == NULL) |
516 | return NULL; | 541 | return NULL; |
517 | 542 | ||
@@ -561,14 +586,14 @@ struct irq_host *irq_alloc_host(struct device_node *of_node, | |||
561 | irq_map[i].host = host; | 586 | irq_map[i].host = host; |
562 | smp_wmb(); | 587 | smp_wmb(); |
563 | 588 | ||
564 | /* Clear norequest flags */ | ||
565 | irq_clear_status_flags(i, IRQ_NOREQUEST); | ||
566 | |||
567 | /* Legacy flags are left to default at this point, | 589 | /* Legacy flags are left to default at this point, |
568 | * one can then use irq_create_mapping() to | 590 | * one can then use irq_create_mapping() to |
569 | * explicitly change them | 591 | * explicitly change them |
570 | */ | 592 | */ |
571 | ops->map(host, i, i); | 593 | ops->map(host, i, i); |
594 | |||
595 | /* Clear norequest flags */ | ||
596 | irq_clear_status_flags(i, IRQ_NOREQUEST); | ||
572 | } | 597 | } |
573 | break; | 598 | break; |
574 | case IRQ_HOST_MAP_LINEAR: | 599 | case IRQ_HOST_MAP_LINEAR: |
@@ -579,6 +604,9 @@ struct irq_host *irq_alloc_host(struct device_node *of_node, | |||
579 | smp_wmb(); | 604 | smp_wmb(); |
580 | host->revmap_data.linear.revmap = rmap; | 605 | host->revmap_data.linear.revmap = rmap; |
581 | break; | 606 | break; |
607 | case IRQ_HOST_MAP_TREE: | ||
608 | INIT_RADIX_TREE(&host->revmap_data.tree, GFP_KERNEL); | ||
609 | break; | ||
582 | default: | 610 | default: |
583 | break; | 611 | break; |
584 | } | 612 | } |
@@ -636,8 +664,6 @@ static int irq_setup_virq(struct irq_host *host, unsigned int virq, | |||
636 | goto error; | 664 | goto error; |
637 | } | 665 | } |
638 | 666 | ||
639 | irq_clear_status_flags(virq, IRQ_NOREQUEST); | ||
640 | |||
641 | /* map it */ | 667 | /* map it */ |
642 | smp_wmb(); | 668 | smp_wmb(); |
643 | irq_map[virq].hwirq = hwirq; | 669 | irq_map[virq].hwirq = hwirq; |
@@ -648,6 +674,8 @@ static int irq_setup_virq(struct irq_host *host, unsigned int virq, | |||
648 | goto errdesc; | 674 | goto errdesc; |
649 | } | 675 | } |
650 | 676 | ||
677 | irq_clear_status_flags(virq, IRQ_NOREQUEST); | ||
678 | |||
651 | return 0; | 679 | return 0; |
652 | 680 | ||
653 | errdesc: | 681 | errdesc: |
@@ -704,8 +732,6 @@ unsigned int irq_create_mapping(struct irq_host *host, | |||
704 | */ | 732 | */ |
705 | virq = irq_find_mapping(host, hwirq); | 733 | virq = irq_find_mapping(host, hwirq); |
706 | if (virq != NO_IRQ) { | 734 | if (virq != NO_IRQ) { |
707 | if (host->ops->remap) | ||
708 | host->ops->remap(host, virq, hwirq); | ||
709 | pr_debug("irq: -> existing mapping on virq %d\n", virq); | 735 | pr_debug("irq: -> existing mapping on virq %d\n", virq); |
710 | return virq; | 736 | return virq; |
711 | } | 737 | } |
@@ -786,14 +812,15 @@ void irq_dispose_mapping(unsigned int virq) | |||
786 | return; | 812 | return; |
787 | 813 | ||
788 | host = irq_map[virq].host; | 814 | host = irq_map[virq].host; |
789 | WARN_ON (host == NULL); | 815 | if (WARN_ON(host == NULL)) |
790 | if (host == NULL) | ||
791 | return; | 816 | return; |
792 | 817 | ||
793 | /* Never unmap legacy interrupts */ | 818 | /* Never unmap legacy interrupts */ |
794 | if (host->revmap_type == IRQ_HOST_MAP_LEGACY) | 819 | if (host->revmap_type == IRQ_HOST_MAP_LEGACY) |
795 | return; | 820 | return; |
796 | 821 | ||
822 | irq_set_status_flags(virq, IRQ_NOREQUEST); | ||
823 | |||
797 | /* remove chip and handler */ | 824 | /* remove chip and handler */ |
798 | irq_set_chip_and_handler(virq, NULL, NULL); | 825 | irq_set_chip_and_handler(virq, NULL, NULL); |
799 | 826 | ||
@@ -813,13 +840,6 @@ void irq_dispose_mapping(unsigned int virq) | |||
813 | host->revmap_data.linear.revmap[hwirq] = NO_IRQ; | 840 | host->revmap_data.linear.revmap[hwirq] = NO_IRQ; |
814 | break; | 841 | break; |
815 | case IRQ_HOST_MAP_TREE: | 842 | case IRQ_HOST_MAP_TREE: |
816 | /* | ||
817 | * Check if radix tree allocated yet, if not then nothing to | ||
818 | * remove. | ||
819 | */ | ||
820 | smp_rmb(); | ||
821 | if (revmap_trees_allocated < 1) | ||
822 | break; | ||
823 | mutex_lock(&revmap_trees_mutex); | 843 | mutex_lock(&revmap_trees_mutex); |
824 | radix_tree_delete(&host->revmap_data.tree, hwirq); | 844 | radix_tree_delete(&host->revmap_data.tree, hwirq); |
825 | mutex_unlock(&revmap_trees_mutex); | 845 | mutex_unlock(&revmap_trees_mutex); |
@@ -830,8 +850,6 @@ void irq_dispose_mapping(unsigned int virq) | |||
830 | smp_mb(); | 850 | smp_mb(); |
831 | irq_map[virq].hwirq = host->inval_irq; | 851 | irq_map[virq].hwirq = host->inval_irq; |
832 | 852 | ||
833 | irq_set_status_flags(virq, IRQ_NOREQUEST); | ||
834 | |||
835 | irq_free_descs(virq, 1); | 853 | irq_free_descs(virq, 1); |
836 | /* Free it */ | 854 | /* Free it */ |
837 | irq_free_virt(virq, 1); | 855 | irq_free_virt(virq, 1); |
@@ -877,16 +895,9 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host, | |||
877 | struct irq_map_entry *ptr; | 895 | struct irq_map_entry *ptr; |
878 | unsigned int virq; | 896 | unsigned int virq; |
879 | 897 | ||
880 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); | 898 | if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_TREE)) |
881 | |||
882 | /* | ||
883 | * Check if the radix tree exists and has bee initialized. | ||
884 | * If not, we fallback to slow mode | ||
885 | */ | ||
886 | if (revmap_trees_allocated < 2) | ||
887 | return irq_find_mapping(host, hwirq); | 899 | return irq_find_mapping(host, hwirq); |
888 | 900 | ||
889 | /* Now try to resolve */ | ||
890 | /* | 901 | /* |
891 | * No rcu_read_lock(ing) needed, the ptr returned can't go under us | 902 | * No rcu_read_lock(ing) needed, the ptr returned can't go under us |
892 | * as it's referencing an entry in the static irq_map table. | 903 | * as it's referencing an entry in the static irq_map table. |
@@ -909,16 +920,7 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host, | |||
909 | void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, | 920 | void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, |
910 | irq_hw_number_t hwirq) | 921 | irq_hw_number_t hwirq) |
911 | { | 922 | { |
912 | 923 | if (WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE)) | |
913 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); | ||
914 | |||
915 | /* | ||
916 | * Check if the radix tree exists yet. | ||
917 | * If not, then the irq will be inserted into the tree when it gets | ||
918 | * initialized. | ||
919 | */ | ||
920 | smp_rmb(); | ||
921 | if (revmap_trees_allocated < 1) | ||
922 | return; | 924 | return; |
923 | 925 | ||
924 | if (virq != NO_IRQ) { | 926 | if (virq != NO_IRQ) { |
@@ -934,7 +936,8 @@ unsigned int irq_linear_revmap(struct irq_host *host, | |||
934 | { | 936 | { |
935 | unsigned int *revmap; | 937 | unsigned int *revmap; |
936 | 938 | ||
937 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR); | 939 | if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_LINEAR)) |
940 | return irq_find_mapping(host, hwirq); | ||
938 | 941 | ||
939 | /* Check revmap bounds */ | 942 | /* Check revmap bounds */ |
940 | if (unlikely(hwirq >= host->revmap_data.linear.size)) | 943 | if (unlikely(hwirq >= host->revmap_data.linear.size)) |
@@ -1028,53 +1031,6 @@ int arch_early_irq_init(void) | |||
1028 | return 0; | 1031 | return 0; |
1029 | } | 1032 | } |
1030 | 1033 | ||
1031 | /* We need to create the radix trees late */ | ||
1032 | static int irq_late_init(void) | ||
1033 | { | ||
1034 | struct irq_host *h; | ||
1035 | unsigned int i; | ||
1036 | |||
1037 | /* | ||
1038 | * No mutual exclusion with respect to accessors of the tree is needed | ||
1039 | * here as the synchronization is done via the state variable | ||
1040 | * revmap_trees_allocated. | ||
1041 | */ | ||
1042 | list_for_each_entry(h, &irq_hosts, link) { | ||
1043 | if (h->revmap_type == IRQ_HOST_MAP_TREE) | ||
1044 | INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL); | ||
1045 | } | ||
1046 | |||
1047 | /* | ||
1048 | * Make sure the radix trees inits are visible before setting | ||
1049 | * the flag | ||
1050 | */ | ||
1051 | smp_wmb(); | ||
1052 | revmap_trees_allocated = 1; | ||
1053 | |||
1054 | /* | ||
1055 | * Insert the reverse mapping for those interrupts already present | ||
1056 | * in irq_map[]. | ||
1057 | */ | ||
1058 | mutex_lock(&revmap_trees_mutex); | ||
1059 | for (i = 0; i < irq_virq_count; i++) { | ||
1060 | if (irq_map[i].host && | ||
1061 | (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE)) | ||
1062 | radix_tree_insert(&irq_map[i].host->revmap_data.tree, | ||
1063 | irq_map[i].hwirq, &irq_map[i]); | ||
1064 | } | ||
1065 | mutex_unlock(&revmap_trees_mutex); | ||
1066 | |||
1067 | /* | ||
1068 | * Make sure the radix trees insertions are visible before setting | ||
1069 | * the flag | ||
1070 | */ | ||
1071 | smp_wmb(); | ||
1072 | revmap_trees_allocated = 2; | ||
1073 | |||
1074 | return 0; | ||
1075 | } | ||
1076 | arch_initcall(irq_late_init); | ||
1077 | |||
1078 | #ifdef CONFIG_VIRQ_DEBUG | 1034 | #ifdef CONFIG_VIRQ_DEBUG |
1079 | static int virq_debug_show(struct seq_file *m, void *private) | 1035 | static int virq_debug_show(struct seq_file *m, void *private) |
1080 | { | 1036 | { |
@@ -1082,10 +1038,11 @@ static int virq_debug_show(struct seq_file *m, void *private) | |||
1082 | struct irq_desc *desc; | 1038 | struct irq_desc *desc; |
1083 | const char *p; | 1039 | const char *p; |
1084 | static const char none[] = "none"; | 1040 | static const char none[] = "none"; |
1041 | void *data; | ||
1085 | int i; | 1042 | int i; |
1086 | 1043 | ||
1087 | seq_printf(m, "%-5s %-7s %-15s %s\n", "virq", "hwirq", | 1044 | seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq", |
1088 | "chip name", "host name"); | 1045 | "chip name", "chip data", "host name"); |
1089 | 1046 | ||
1090 | for (i = 1; i < nr_irqs; i++) { | 1047 | for (i = 1; i < nr_irqs; i++) { |
1091 | desc = irq_to_desc(i); | 1048 | desc = irq_to_desc(i); |
@@ -1098,7 +1055,7 @@ static int virq_debug_show(struct seq_file *m, void *private) | |||
1098 | struct irq_chip *chip; | 1055 | struct irq_chip *chip; |
1099 | 1056 | ||
1100 | seq_printf(m, "%5d ", i); | 1057 | seq_printf(m, "%5d ", i); |
1101 | seq_printf(m, "0x%05lx ", virq_to_hw(i)); | 1058 | seq_printf(m, "0x%05lx ", irq_map[i].hwirq); |
1102 | 1059 | ||
1103 | chip = irq_desc_get_chip(desc); | 1060 | chip = irq_desc_get_chip(desc); |
1104 | if (chip && chip->name) | 1061 | if (chip && chip->name) |
@@ -1107,6 +1064,9 @@ static int virq_debug_show(struct seq_file *m, void *private) | |||
1107 | p = none; | 1064 | p = none; |
1108 | seq_printf(m, "%-15s ", p); | 1065 | seq_printf(m, "%-15s ", p); |
1109 | 1066 | ||
1067 | data = irq_desc_get_chip_data(desc); | ||
1068 | seq_printf(m, "0x%16p ", data); | ||
1069 | |||
1110 | if (irq_map[i].host && irq_map[i].host->of_node) | 1070 | if (irq_map[i].host && irq_map[i].host->of_node) |
1111 | p = irq_map[i].host->of_node->full_name; | 1071 | p = irq_map[i].host->of_node->full_name; |
1112 | else | 1072 | else |