diff options
Diffstat (limited to 'arch/powerpc/kernel/irq.c')
-rw-r--r-- | arch/powerpc/kernel/irq.c | 140 |
1 files changed, 93 insertions, 47 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 9040330b0530..64f6f2031c22 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -73,8 +73,10 @@ | |||
73 | #define CREATE_TRACE_POINTS | 73 | #define CREATE_TRACE_POINTS |
74 | #include <asm/trace.h> | 74 | #include <asm/trace.h> |
75 | 75 | ||
76 | DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); | ||
77 | EXPORT_PER_CPU_SYMBOL(irq_stat); | ||
78 | |||
76 | int __irq_offset_value; | 79 | int __irq_offset_value; |
77 | static int ppc_spurious_interrupts; | ||
78 | 80 | ||
79 | #ifdef CONFIG_PPC32 | 81 | #ifdef CONFIG_PPC32 |
80 | EXPORT_SYMBOL(__irq_offset_value); | 82 | EXPORT_SYMBOL(__irq_offset_value); |
@@ -180,30 +182,64 @@ notrace void raw_local_irq_restore(unsigned long en) | |||
180 | EXPORT_SYMBOL(raw_local_irq_restore); | 182 | EXPORT_SYMBOL(raw_local_irq_restore); |
181 | #endif /* CONFIG_PPC64 */ | 183 | #endif /* CONFIG_PPC64 */ |
182 | 184 | ||
185 | static int show_other_interrupts(struct seq_file *p, int prec) | ||
186 | { | ||
187 | int j; | ||
188 | |||
189 | #if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT) | ||
190 | if (tau_initialized) { | ||
191 | seq_printf(p, "%*s: ", prec, "TAU"); | ||
192 | for_each_online_cpu(j) | ||
193 | seq_printf(p, "%10u ", tau_interrupts(j)); | ||
194 | seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); | ||
195 | } | ||
196 | #endif /* CONFIG_PPC32 && CONFIG_TAU_INT */ | ||
197 | |||
198 | seq_printf(p, "%*s: ", prec, "LOC"); | ||
199 | for_each_online_cpu(j) | ||
200 | seq_printf(p, "%10u ", per_cpu(irq_stat, j).timer_irqs); | ||
201 | seq_printf(p, " Local timer interrupts\n"); | ||
202 | |||
203 | seq_printf(p, "%*s: ", prec, "SPU"); | ||
204 | for_each_online_cpu(j) | ||
205 | seq_printf(p, "%10u ", per_cpu(irq_stat, j).spurious_irqs); | ||
206 | seq_printf(p, " Spurious interrupts\n"); | ||
207 | |||
208 | seq_printf(p, "%*s: ", prec, "CNT"); | ||
209 | for_each_online_cpu(j) | ||
210 | seq_printf(p, "%10u ", per_cpu(irq_stat, j).pmu_irqs); | ||
211 | seq_printf(p, " Performance monitoring interrupts\n"); | ||
212 | |||
213 | seq_printf(p, "%*s: ", prec, "MCE"); | ||
214 | for_each_online_cpu(j) | ||
215 | seq_printf(p, "%10u ", per_cpu(irq_stat, j).mce_exceptions); | ||
216 | seq_printf(p, " Machine check exceptions\n"); | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
183 | int show_interrupts(struct seq_file *p, void *v) | 221 | int show_interrupts(struct seq_file *p, void *v) |
184 | { | 222 | { |
185 | int i = *(loff_t *)v, j; | 223 | unsigned long flags, any_count = 0; |
224 | int i = *(loff_t *) v, j, prec; | ||
186 | struct irqaction *action; | 225 | struct irqaction *action; |
187 | struct irq_desc *desc; | 226 | struct irq_desc *desc; |
188 | unsigned long flags; | ||
189 | 227 | ||
228 | if (i > nr_irqs) | ||
229 | return 0; | ||
230 | |||
231 | for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec) | ||
232 | j *= 10; | ||
233 | |||
234 | if (i == nr_irqs) | ||
235 | return show_other_interrupts(p, prec); | ||
236 | |||
237 | /* print header */ | ||
190 | if (i == 0) { | 238 | if (i == 0) { |
191 | seq_puts(p, " "); | 239 | seq_printf(p, "%*s", prec + 8, ""); |
192 | for_each_online_cpu(j) | 240 | for_each_online_cpu(j) |
193 | seq_printf(p, "CPU%d ", j); | 241 | seq_printf(p, "CPU%-8d", j); |
194 | seq_putc(p, '\n'); | 242 | seq_putc(p, '\n'); |
195 | } else if (i == nr_irqs) { | ||
196 | #if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT) | ||
197 | if (tau_initialized){ | ||
198 | seq_puts(p, "TAU: "); | ||
199 | for_each_online_cpu(j) | ||
200 | seq_printf(p, "%10u ", tau_interrupts(j)); | ||
201 | seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); | ||
202 | } | ||
203 | #endif /* CONFIG_PPC32 && CONFIG_TAU_INT*/ | ||
204 | seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts); | ||
205 | |||
206 | return 0; | ||
207 | } | 243 | } |
208 | 244 | ||
209 | desc = irq_to_desc(i); | 245 | desc = irq_to_desc(i); |
@@ -211,37 +247,48 @@ int show_interrupts(struct seq_file *p, void *v) | |||
211 | return 0; | 247 | return 0; |
212 | 248 | ||
213 | raw_spin_lock_irqsave(&desc->lock, flags); | 249 | raw_spin_lock_irqsave(&desc->lock, flags); |
214 | 250 | for_each_online_cpu(j) | |
251 | any_count |= kstat_irqs_cpu(i, j); | ||
215 | action = desc->action; | 252 | action = desc->action; |
216 | if (!action || !action->handler) | 253 | if (!action && !any_count) |
217 | goto skip; | 254 | goto out; |
218 | 255 | ||
219 | seq_printf(p, "%3d: ", i); | 256 | seq_printf(p, "%*d: ", prec, i); |
220 | #ifdef CONFIG_SMP | ||
221 | for_each_online_cpu(j) | 257 | for_each_online_cpu(j) |
222 | seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); | 258 | seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); |
223 | #else | ||
224 | seq_printf(p, "%10u ", kstat_irqs(i)); | ||
225 | #endif /* CONFIG_SMP */ | ||
226 | 259 | ||
227 | if (desc->chip) | 260 | if (desc->chip) |
228 | seq_printf(p, " %s ", desc->chip->name); | 261 | seq_printf(p, " %-16s", desc->chip->name); |
229 | else | 262 | else |
230 | seq_puts(p, " None "); | 263 | seq_printf(p, " %-16s", "None"); |
264 | seq_printf(p, " %-8s", (desc->status & IRQ_LEVEL) ? "Level" : "Edge"); | ||
231 | 265 | ||
232 | seq_printf(p, "%s", (desc->status & IRQ_LEVEL) ? "Level " : "Edge "); | 266 | if (action) { |
233 | seq_printf(p, " %s", action->name); | 267 | seq_printf(p, " %s", action->name); |
268 | while ((action = action->next) != NULL) | ||
269 | seq_printf(p, ", %s", action->name); | ||
270 | } | ||
234 | 271 | ||
235 | for (action = action->next; action; action = action->next) | ||
236 | seq_printf(p, ", %s", action->name); | ||
237 | seq_putc(p, '\n'); | 272 | seq_putc(p, '\n'); |
238 | 273 | out: | |
239 | skip: | ||
240 | raw_spin_unlock_irqrestore(&desc->lock, flags); | 274 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
241 | |||
242 | return 0; | 275 | return 0; |
243 | } | 276 | } |
244 | 277 | ||
278 | /* | ||
279 | * /proc/stat helpers | ||
280 | */ | ||
281 | u64 arch_irq_stat_cpu(unsigned int cpu) | ||
282 | { | ||
283 | u64 sum = per_cpu(irq_stat, cpu).timer_irqs; | ||
284 | |||
285 | sum += per_cpu(irq_stat, cpu).pmu_irqs; | ||
286 | sum += per_cpu(irq_stat, cpu).mce_exceptions; | ||
287 | sum += per_cpu(irq_stat, cpu).spurious_irqs; | ||
288 | |||
289 | return sum; | ||
290 | } | ||
291 | |||
245 | #ifdef CONFIG_HOTPLUG_CPU | 292 | #ifdef CONFIG_HOTPLUG_CPU |
246 | void fixup_irqs(cpumask_t map) | 293 | void fixup_irqs(cpumask_t map) |
247 | { | 294 | { |
@@ -353,8 +400,7 @@ void do_IRQ(struct pt_regs *regs) | |||
353 | if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) | 400 | if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) |
354 | handle_one_irq(irq); | 401 | handle_one_irq(irq); |
355 | else if (irq != NO_IRQ_IGNORE) | 402 | else if (irq != NO_IRQ_IGNORE) |
356 | /* That's not SMP safe ... but who cares ? */ | 403 | __get_cpu_var(irq_stat).spurious_irqs++; |
357 | ppc_spurious_interrupts++; | ||
358 | 404 | ||
359 | irq_exit(); | 405 | irq_exit(); |
360 | set_irq_regs(old_regs); | 406 | set_irq_regs(old_regs); |
@@ -474,7 +520,7 @@ void do_softirq(void) | |||
474 | */ | 520 | */ |
475 | 521 | ||
476 | static LIST_HEAD(irq_hosts); | 522 | static LIST_HEAD(irq_hosts); |
477 | static DEFINE_SPINLOCK(irq_big_lock); | 523 | static DEFINE_RAW_SPINLOCK(irq_big_lock); |
478 | static unsigned int revmap_trees_allocated; | 524 | static unsigned int revmap_trees_allocated; |
479 | static DEFINE_MUTEX(revmap_trees_mutex); | 525 | static DEFINE_MUTEX(revmap_trees_mutex); |
480 | struct irq_map_entry irq_map[NR_IRQS]; | 526 | struct irq_map_entry irq_map[NR_IRQS]; |
@@ -520,14 +566,14 @@ struct irq_host *irq_alloc_host(struct device_node *of_node, | |||
520 | if (host->ops->match == NULL) | 566 | if (host->ops->match == NULL) |
521 | host->ops->match = default_irq_host_match; | 567 | host->ops->match = default_irq_host_match; |
522 | 568 | ||
523 | spin_lock_irqsave(&irq_big_lock, flags); | 569 | raw_spin_lock_irqsave(&irq_big_lock, flags); |
524 | 570 | ||
525 | /* If it's a legacy controller, check for duplicates and | 571 | /* If it's a legacy controller, check for duplicates and |
526 | * mark it as allocated (we use irq 0 host pointer for that | 572 | * mark it as allocated (we use irq 0 host pointer for that |
527 | */ | 573 | */ |
528 | if (revmap_type == IRQ_HOST_MAP_LEGACY) { | 574 | if (revmap_type == IRQ_HOST_MAP_LEGACY) { |
529 | if (irq_map[0].host != NULL) { | 575 | if (irq_map[0].host != NULL) { |
530 | spin_unlock_irqrestore(&irq_big_lock, flags); | 576 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); |
531 | /* If we are early boot, we can't free the structure, | 577 | /* If we are early boot, we can't free the structure, |
532 | * too bad... | 578 | * too bad... |
533 | * this will be fixed once slab is made available early | 579 | * this will be fixed once slab is made available early |
@@ -541,7 +587,7 @@ struct irq_host *irq_alloc_host(struct device_node *of_node, | |||
541 | } | 587 | } |
542 | 588 | ||
543 | list_add(&host->link, &irq_hosts); | 589 | list_add(&host->link, &irq_hosts); |
544 | spin_unlock_irqrestore(&irq_big_lock, flags); | 590 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); |
545 | 591 | ||
546 | /* Additional setups per revmap type */ | 592 | /* Additional setups per revmap type */ |
547 | switch(revmap_type) { | 593 | switch(revmap_type) { |
@@ -592,13 +638,13 @@ struct irq_host *irq_find_host(struct device_node *node) | |||
592 | * the absence of a device node. This isn't a problem so far | 638 | * the absence of a device node. This isn't a problem so far |
593 | * yet though... | 639 | * yet though... |
594 | */ | 640 | */ |
595 | spin_lock_irqsave(&irq_big_lock, flags); | 641 | raw_spin_lock_irqsave(&irq_big_lock, flags); |
596 | list_for_each_entry(h, &irq_hosts, link) | 642 | list_for_each_entry(h, &irq_hosts, link) |
597 | if (h->ops->match(h, node)) { | 643 | if (h->ops->match(h, node)) { |
598 | found = h; | 644 | found = h; |
599 | break; | 645 | break; |
600 | } | 646 | } |
601 | spin_unlock_irqrestore(&irq_big_lock, flags); | 647 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); |
602 | return found; | 648 | return found; |
603 | } | 649 | } |
604 | EXPORT_SYMBOL_GPL(irq_find_host); | 650 | EXPORT_SYMBOL_GPL(irq_find_host); |
@@ -967,7 +1013,7 @@ unsigned int irq_alloc_virt(struct irq_host *host, | |||
967 | if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS)) | 1013 | if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS)) |
968 | return NO_IRQ; | 1014 | return NO_IRQ; |
969 | 1015 | ||
970 | spin_lock_irqsave(&irq_big_lock, flags); | 1016 | raw_spin_lock_irqsave(&irq_big_lock, flags); |
971 | 1017 | ||
972 | /* Use hint for 1 interrupt if any */ | 1018 | /* Use hint for 1 interrupt if any */ |
973 | if (count == 1 && hint >= NUM_ISA_INTERRUPTS && | 1019 | if (count == 1 && hint >= NUM_ISA_INTERRUPTS && |
@@ -991,7 +1037,7 @@ unsigned int irq_alloc_virt(struct irq_host *host, | |||
991 | } | 1037 | } |
992 | } | 1038 | } |
993 | if (found == NO_IRQ) { | 1039 | if (found == NO_IRQ) { |
994 | spin_unlock_irqrestore(&irq_big_lock, flags); | 1040 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); |
995 | return NO_IRQ; | 1041 | return NO_IRQ; |
996 | } | 1042 | } |
997 | hint_found: | 1043 | hint_found: |
@@ -1000,7 +1046,7 @@ unsigned int irq_alloc_virt(struct irq_host *host, | |||
1000 | smp_wmb(); | 1046 | smp_wmb(); |
1001 | irq_map[i].host = host; | 1047 | irq_map[i].host = host; |
1002 | } | 1048 | } |
1003 | spin_unlock_irqrestore(&irq_big_lock, flags); | 1049 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); |
1004 | return found; | 1050 | return found; |
1005 | } | 1051 | } |
1006 | 1052 | ||
@@ -1012,7 +1058,7 @@ void irq_free_virt(unsigned int virq, unsigned int count) | |||
1012 | WARN_ON (virq < NUM_ISA_INTERRUPTS); | 1058 | WARN_ON (virq < NUM_ISA_INTERRUPTS); |
1013 | WARN_ON (count == 0 || (virq + count) > irq_virq_count); | 1059 | WARN_ON (count == 0 || (virq + count) > irq_virq_count); |
1014 | 1060 | ||
1015 | spin_lock_irqsave(&irq_big_lock, flags); | 1061 | raw_spin_lock_irqsave(&irq_big_lock, flags); |
1016 | for (i = virq; i < (virq + count); i++) { | 1062 | for (i = virq; i < (virq + count); i++) { |
1017 | struct irq_host *host; | 1063 | struct irq_host *host; |
1018 | 1064 | ||
@@ -1025,7 +1071,7 @@ void irq_free_virt(unsigned int virq, unsigned int count) | |||
1025 | smp_wmb(); | 1071 | smp_wmb(); |
1026 | irq_map[i].host = NULL; | 1072 | irq_map[i].host = NULL; |
1027 | } | 1073 | } |
1028 | spin_unlock_irqrestore(&irq_big_lock, flags); | 1074 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); |
1029 | } | 1075 | } |
1030 | 1076 | ||
1031 | int arch_early_irq_init(void) | 1077 | int arch_early_irq_init(void) |