| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  linux/arch/arm/kernel/irq.c | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 1992 Linus Torvalds | 
|  | 5 | *  Modifications for ARM processor Copyright (C) 1995-2000 Russell King. | 
|  | 6 | * | 
| Russell King | 8749af6 | 2005-06-25 19:39:45 +0100 | [diff] [blame] | 7 | *  Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation. | 
|  | 8 | *  Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and | 
|  | 9 | *  Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>. | 
|  | 10 | * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 | * This program is free software; you can redistribute it and/or modify | 
|  | 12 | * it under the terms of the GNU General Public License version 2 as | 
|  | 13 | * published by the Free Software Foundation. | 
|  | 14 | * | 
|  | 15 | *  This file contains the code used by various IRQ handling routines: | 
|  | 16 | *  asking for different IRQ's should be done through these routines | 
|  | 17 | *  instead of just grabbing them. Thus setups with different IRQ numbers | 
|  | 18 | *  shouldn't result in any weird surprises, and installing new handlers | 
|  | 19 | *  should be easier. | 
|  | 20 | * | 
|  | 21 | *  IRQ's are in fact implemented a bit like signal handlers for the kernel. | 
|  | 22 | *  Naturally it's not a 1:1 relation, but there are similarities. | 
|  | 23 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 24 | #include <linux/kernel_stat.h> | 
|  | 25 | #include <linux/module.h> | 
|  | 26 | #include <linux/signal.h> | 
|  | 27 | #include <linux/ioport.h> | 
|  | 28 | #include <linux/interrupt.h> | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 29 | #include <linux/irq.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | #include <linux/slab.h> | 
|  | 31 | #include <linux/random.h> | 
|  | 32 | #include <linux/smp.h> | 
|  | 33 | #include <linux/init.h> | 
|  | 34 | #include <linux/seq_file.h> | 
|  | 35 | #include <linux/errno.h> | 
|  | 36 | #include <linux/list.h> | 
|  | 37 | #include <linux/kallsyms.h> | 
|  | 38 | #include <linux/proc_fs.h> | 
|  | 39 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | #include <asm/system.h> | 
| Russell King | 897d852 | 2008-08-03 15:04:04 +0100 | [diff] [blame] | 41 | #include <asm/mach/irq.h> | 
| Russell King | 8749af6 | 2005-06-25 19:39:45 +0100 | [diff] [blame] | 42 | #include <asm/mach/time.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 |  | 
|  | 44 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | * No architecture-specific irq_finish function defined in arm/arch/irqs.h. | 
|  | 46 | */ | 
|  | 47 | #ifndef irq_finish | 
|  | 48 | #define irq_finish(irq) do { } while (0) | 
|  | 49 | #endif | 
|  | 50 |  | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 51 | void (*init_arch_irq)(void) __initdata = NULL; | 
|  | 52 | unsigned long irq_err_count; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 |  | 
|  | 54 | int show_interrupts(struct seq_file *p, void *v) | 
|  | 55 | { | 
|  | 56 | int i = *(loff_t *) v, cpu; | 
|  | 57 | struct irqaction * action; | 
|  | 58 | unsigned long flags; | 
|  | 59 |  | 
|  | 60 | if (i == 0) { | 
|  | 61 | char cpuname[12]; | 
|  | 62 |  | 
|  | 63 | seq_printf(p, "    "); | 
|  | 64 | for_each_present_cpu(cpu) { | 
|  | 65 | sprintf(cpuname, "CPU%d", cpu); | 
|  | 66 | seq_printf(p, " %10s", cpuname); | 
|  | 67 | } | 
|  | 68 | seq_putc(p, '\n'); | 
|  | 69 | } | 
|  | 70 |  | 
|  | 71 | if (i < NR_IRQS) { | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 72 | spin_lock_irqsave(&irq_desc[i].lock, flags); | 
|  | 73 | action = irq_desc[i].action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 74 | if (!action) | 
|  | 75 | goto unlock; | 
|  | 76 |  | 
|  | 77 | seq_printf(p, "%3d: ", i); | 
|  | 78 | for_each_present_cpu(cpu) | 
| Yinghai Lu | 0b0f0b1 | 2009-01-11 13:35:56 -0800 | [diff] [blame] | 79 | seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu)); | 
| David Brownell | 38c677c | 2006-08-01 22:26:25 +0100 | [diff] [blame] | 80 | seq_printf(p, " %10s", irq_desc[i].chip->name ? : "-"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 81 | seq_printf(p, "  %s", action->name); | 
|  | 82 | for (action = action->next; action; action = action->next) | 
|  | 83 | seq_printf(p, ", %s", action->name); | 
|  | 84 |  | 
|  | 85 | seq_putc(p, '\n'); | 
|  | 86 | unlock: | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 87 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 88 | } else if (i == NR_IRQS) { | 
|  | 89 | #ifdef CONFIG_ARCH_ACORN | 
|  | 90 | show_fiq_list(p, v); | 
|  | 91 | #endif | 
|  | 92 | #ifdef CONFIG_SMP | 
|  | 93 | show_ipi_list(p); | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 94 | show_local_irqs(p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | #endif | 
|  | 96 | seq_printf(p, "Err: %10lu\n", irq_err_count); | 
|  | 97 | } | 
|  | 98 | return 0; | 
|  | 99 | } | 
|  | 100 |  | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 101 | /* Handle bad interrupts */ | 
|  | 102 | static struct irq_desc bad_irq_desc = { | 
|  | 103 | .handle_irq = handle_bad_irq, | 
| Uwe Kleine-König | 6fd7ad9 | 2009-01-31 01:21:55 +0100 | [diff] [blame] | 104 | .lock = __SPIN_LOCK_UNLOCKED(bad_irq_desc.lock), | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 105 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 |  | 
| Mike Travis | e65e49d | 2009-01-12 15:27:13 -0800 | [diff] [blame] | 107 | #ifdef CONFIG_CPUMASK_OFFSTACK | 
|  | 108 | /* We are not allocating bad_irq_desc.affinity or .pending_mask */ | 
|  | 109 | #error "ARM architecture does not support CONFIG_CPUMASK_OFFSTACK." | 
|  | 110 | #endif | 
|  | 111 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 | /* | 
|  | 113 | * do_IRQ handles all hardware IRQ's.  Decoded IRQs should not | 
|  | 114 | * come via this function.  Instead, they should provide their | 
|  | 115 | * own 'handler' | 
|  | 116 | */ | 
| Russell King | 7ab3f8d | 2007-03-02 15:01:36 +0000 | [diff] [blame] | 117 | asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | { | 
| Linus Torvalds | e630015 | 2006-10-06 13:11:15 -0700 | [diff] [blame] | 119 | struct pt_regs *old_regs = set_irq_regs(regs); | 
| Dmitry Baryshkov | d8aa025 | 2008-10-09 13:36:24 +0100 | [diff] [blame] | 120 |  | 
|  | 121 | irq_enter(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 |  | 
|  | 123 | /* | 
|  | 124 | * Some hardware gives randomly wrong interrupts.  Rather | 
|  | 125 | * than crashing, do something sensible. | 
|  | 126 | */ | 
|  | 127 | if (irq >= NR_IRQS) | 
| Dmitry Baryshkov | d8aa025 | 2008-10-09 13:36:24 +0100 | [diff] [blame] | 128 | handle_bad_irq(irq, &bad_irq_desc); | 
|  | 129 | else | 
|  | 130 | generic_handle_irq(irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 131 |  | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 132 | /* AT91 specific workaround */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | irq_finish(irq); | 
|  | 134 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 | irq_exit(); | 
| Linus Torvalds | e630015 | 2006-10-06 13:11:15 -0700 | [diff] [blame] | 136 | set_irq_regs(old_regs); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 137 | } | 
|  | 138 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | void set_irq_flags(unsigned int irq, unsigned int iflags) | 
|  | 140 | { | 
| Russell King | 10dd5ce | 2006-11-23 11:41:32 +0000 | [diff] [blame] | 141 | struct irq_desc *desc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 142 | unsigned long flags; | 
|  | 143 |  | 
|  | 144 | if (irq >= NR_IRQS) { | 
|  | 145 | printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq); | 
|  | 146 | return; | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | desc = irq_desc + irq; | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 150 | spin_lock_irqsave(&desc->lock, flags); | 
|  | 151 | desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; | 
|  | 152 | if (iflags & IRQF_VALID) | 
|  | 153 | desc->status &= ~IRQ_NOREQUEST; | 
|  | 154 | if (iflags & IRQF_PROBE) | 
|  | 155 | desc->status &= ~IRQ_NOPROBE; | 
|  | 156 | if (!(iflags & IRQF_NOAUTOEN)) | 
|  | 157 | desc->status &= ~IRQ_NOAUTOEN; | 
|  | 158 | spin_unlock_irqrestore(&desc->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 | } | 
|  | 160 |  | 
|  | 161 | void __init init_IRQ(void) | 
|  | 162 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | int irq; | 
|  | 164 |  | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 165 | for (irq = 0; irq < NR_IRQS; irq++) | 
| Ingo Molnar | d7e25f3 | 2007-02-16 01:28:24 -0800 | [diff] [blame] | 166 | irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE; | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 167 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | #ifdef CONFIG_SMP | 
| Mike Travis | e65e49d | 2009-01-12 15:27:13 -0800 | [diff] [blame] | 169 | cpumask_setall(bad_irq_desc.affinity); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 | bad_irq_desc.cpu = smp_processor_id(); | 
|  | 171 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 172 | init_arch_irq(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 | } | 
|  | 174 |  | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 175 | #ifdef CONFIG_HOTPLUG_CPU | 
| Thomas Gleixner | f7ede37 | 2006-07-11 22:54:34 +0100 | [diff] [blame] | 176 |  | 
| Russell King | 10dd5ce | 2006-11-23 11:41:32 +0000 | [diff] [blame] | 177 | static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu) | 
| Thomas Gleixner | f7ede37 | 2006-07-11 22:54:34 +0100 | [diff] [blame] | 178 | { | 
|  | 179 | pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu); | 
|  | 180 |  | 
|  | 181 | spin_lock_irq(&desc->lock); | 
| Rusty Russell | 0de2652 | 2008-12-13 21:20:26 +1030 | [diff] [blame] | 182 | desc->chip->set_affinity(irq, cpumask_of(cpu)); | 
| Thomas Gleixner | f7ede37 | 2006-07-11 22:54:34 +0100 | [diff] [blame] | 183 | spin_unlock_irq(&desc->lock); | 
|  | 184 | } | 
|  | 185 |  | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 186 | /* | 
|  | 187 | * The CPU has been marked offline.  Migrate IRQs off this CPU.  If | 
|  | 188 | * the affinity settings do not allow other CPUs, force them onto any | 
|  | 189 | * available CPU. | 
|  | 190 | */ | 
|  | 191 | void migrate_irqs(void) | 
|  | 192 | { | 
|  | 193 | unsigned int i, cpu = smp_processor_id(); | 
|  | 194 |  | 
|  | 195 | for (i = 0; i < NR_IRQS; i++) { | 
| Russell King | 10dd5ce | 2006-11-23 11:41:32 +0000 | [diff] [blame] | 196 | struct irq_desc *desc = irq_desc + i; | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 197 |  | 
|  | 198 | if (desc->cpu == cpu) { | 
| Mike Travis | e65e49d | 2009-01-12 15:27:13 -0800 | [diff] [blame] | 199 | unsigned int newcpu = cpumask_any_and(desc->affinity, | 
|  | 200 | cpu_online_mask); | 
|  | 201 | if (newcpu >= nr_cpu_ids) { | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 202 | if (printk_ratelimit()) | 
|  | 203 | printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n", | 
|  | 204 | i, cpu); | 
|  | 205 |  | 
| Mike Travis | e65e49d | 2009-01-12 15:27:13 -0800 | [diff] [blame] | 206 | cpumask_setall(desc->affinity); | 
|  | 207 | newcpu = cpumask_any_and(desc->affinity, | 
|  | 208 | cpu_online_mask); | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 209 | } | 
|  | 210 |  | 
|  | 211 | route_irq(desc, i, newcpu); | 
|  | 212 | } | 
|  | 213 | } | 
|  | 214 | } | 
|  | 215 | #endif /* CONFIG_HOTPLUG_CPU */ |