diff options
Diffstat (limited to 'arch/s390/kernel/irq.c')
-rw-r--r-- | arch/s390/kernel/irq.c | 177 |
1 files changed, 164 insertions, 13 deletions
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 026a37a94fc9..e3264f6a9720 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
@@ -1,28 +1,66 @@ | |||
1 | /* | 1 | /* |
2 | * arch/s390/kernel/irq.c | 2 | * Copyright IBM Corp. 2004,2011 |
3 | * | 3 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>, |
4 | * Copyright IBM Corp. 2004,2007 | 4 | * Holger Smolinski <Holger.Smolinski@de.ibm.com>, |
5 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), | 5 | * Thomas Spatzier <tspat@de.ibm.com>, |
6 | * Thomas Spatzier (tspat@de.ibm.com) | ||
7 | * | 6 | * |
8 | * This file contains interrupt related functions. | 7 | * This file contains interrupt related functions. |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/kernel_stat.h> | 10 | #include <linux/kernel_stat.h> |
14 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
15 | #include <linux/seq_file.h> | 12 | #include <linux/seq_file.h> |
16 | #include <linux/cpu.h> | ||
17 | #include <linux/proc_fs.h> | 13 | #include <linux/proc_fs.h> |
18 | #include <linux/profile.h> | 14 | #include <linux/profile.h> |
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/ftrace.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/cpu.h> | ||
21 | #include <asm/irq_regs.h> | ||
22 | #include <asm/cputime.h> | ||
23 | #include <asm/lowcore.h> | ||
24 | #include <asm/irq.h> | ||
25 | #include "entry.h" | ||
26 | |||
27 | struct irq_class { | ||
28 | char *name; | ||
29 | char *desc; | ||
30 | }; | ||
31 | |||
32 | static const struct irq_class intrclass_names[] = { | ||
33 | {.name = "EXT" }, | ||
34 | {.name = "I/O" }, | ||
35 | {.name = "CLK", .desc = "[EXT] Clock Comparator" }, | ||
36 | {.name = "IPI", .desc = "[EXT] Signal Processor" }, | ||
37 | {.name = "TMR", .desc = "[EXT] CPU Timer" }, | ||
38 | {.name = "TAL", .desc = "[EXT] Timing Alert" }, | ||
39 | {.name = "PFL", .desc = "[EXT] Pseudo Page Fault" }, | ||
40 | {.name = "DSD", .desc = "[EXT] DASD Diag" }, | ||
41 | {.name = "VRT", .desc = "[EXT] Virtio" }, | ||
42 | {.name = "SCP", .desc = "[EXT] Service Call" }, | ||
43 | {.name = "IUC", .desc = "[EXT] IUCV" }, | ||
44 | {.name = "CPM", .desc = "[EXT] CPU Measurement" }, | ||
45 | {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" }, | ||
46 | {.name = "QDI", .desc = "[I/O] QDIO Interrupt" }, | ||
47 | {.name = "DAS", .desc = "[I/O] DASD" }, | ||
48 | {.name = "C15", .desc = "[I/O] 3215" }, | ||
49 | {.name = "C70", .desc = "[I/O] 3270" }, | ||
50 | {.name = "TAP", .desc = "[I/O] Tape" }, | ||
51 | {.name = "VMR", .desc = "[I/O] Unit Record Devices" }, | ||
52 | {.name = "LCS", .desc = "[I/O] LCS" }, | ||
53 | {.name = "CLW", .desc = "[I/O] CLAW" }, | ||
54 | {.name = "CTC", .desc = "[I/O] CTC" }, | ||
55 | {.name = "APB", .desc = "[I/O] AP Bus" }, | ||
56 | {.name = "NMI", .desc = "[NMI] Machine Check" }, | ||
57 | }; | ||
19 | 58 | ||
20 | /* | 59 | /* |
21 | * show_interrupts is needed by /proc/interrupts. | 60 | * show_interrupts is needed by /proc/interrupts. |
22 | */ | 61 | */ |
23 | int show_interrupts(struct seq_file *p, void *v) | 62 | int show_interrupts(struct seq_file *p, void *v) |
24 | { | 63 | { |
25 | static const char *intrclass_names[] = { "EXT", "I/O", }; | ||
26 | int i = *(loff_t *) v, j; | 64 | int i = *(loff_t *) v, j; |
27 | 65 | ||
28 | get_online_cpus(); | 66 | get_online_cpus(); |
@@ -34,15 +72,16 @@ int show_interrupts(struct seq_file *p, void *v) | |||
34 | } | 72 | } |
35 | 73 | ||
36 | if (i < NR_IRQS) { | 74 | if (i < NR_IRQS) { |
37 | seq_printf(p, "%s: ", intrclass_names[i]); | 75 | seq_printf(p, "%s: ", intrclass_names[i].name); |
38 | #ifndef CONFIG_SMP | 76 | #ifndef CONFIG_SMP |
39 | seq_printf(p, "%10u ", kstat_irqs(i)); | 77 | seq_printf(p, "%10u ", kstat_irqs(i)); |
40 | #else | 78 | #else |
41 | for_each_online_cpu(j) | 79 | for_each_online_cpu(j) |
42 | seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); | 80 | seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); |
43 | #endif | 81 | #endif |
82 | if (intrclass_names[i].desc) | ||
83 | seq_printf(p, " %s", intrclass_names[i].desc); | ||
44 | seq_putc(p, '\n'); | 84 | seq_putc(p, '\n'); |
45 | |||
46 | } | 85 | } |
47 | put_online_cpus(); | 86 | put_online_cpus(); |
48 | return 0; | 87 | return 0; |
@@ -52,8 +91,7 @@ int show_interrupts(struct seq_file *p, void *v) | |||
52 | * For compatibilty only. S/390 specific setup of interrupts et al. is done | 91 | * For compatibilty only. S/390 specific setup of interrupts et al. is done |
53 | * much later in init_channel_subsystem(). | 92 | * much later in init_channel_subsystem(). |
54 | */ | 93 | */ |
55 | void __init | 94 | void __init init_IRQ(void) |
56 | init_IRQ(void) | ||
57 | { | 95 | { |
58 | /* nothing... */ | 96 | /* nothing... */ |
59 | } | 97 | } |
@@ -104,3 +142,116 @@ void init_irq_proc(void) | |||
104 | create_prof_cpu_mask(root_irq_dir); | 142 | create_prof_cpu_mask(root_irq_dir); |
105 | } | 143 | } |
106 | #endif | 144 | #endif |
145 | |||
146 | /* | ||
147 | * ext_int_hash[index] is the start of the list for all external interrupts | ||
148 | * that hash to this index. With the current set of external interrupts | ||
149 | * (0x1202 external call, 0x1004 cpu timer, 0x2401 hwc console, 0x4000 | ||
150 | * iucv and 0x2603 pfault) this is always the first element. | ||
151 | */ | ||
152 | |||
153 | struct ext_int_info { | ||
154 | struct ext_int_info *next; | ||
155 | ext_int_handler_t handler; | ||
156 | u16 code; | ||
157 | }; | ||
158 | |||
159 | static struct ext_int_info *ext_int_hash[256]; | ||
160 | |||
161 | static inline int ext_hash(u16 code) | ||
162 | { | ||
163 | return (code + (code >> 9)) & 0xff; | ||
164 | } | ||
165 | |||
166 | int register_external_interrupt(u16 code, ext_int_handler_t handler) | ||
167 | { | ||
168 | struct ext_int_info *p; | ||
169 | int index; | ||
170 | |||
171 | p = kmalloc(sizeof(*p), GFP_ATOMIC); | ||
172 | if (!p) | ||
173 | return -ENOMEM; | ||
174 | p->code = code; | ||
175 | p->handler = handler; | ||
176 | index = ext_hash(code); | ||
177 | p->next = ext_int_hash[index]; | ||
178 | ext_int_hash[index] = p; | ||
179 | return 0; | ||
180 | } | ||
181 | EXPORT_SYMBOL(register_external_interrupt); | ||
182 | |||
183 | int unregister_external_interrupt(u16 code, ext_int_handler_t handler) | ||
184 | { | ||
185 | struct ext_int_info *p, *q; | ||
186 | int index; | ||
187 | |||
188 | index = ext_hash(code); | ||
189 | q = NULL; | ||
190 | p = ext_int_hash[index]; | ||
191 | while (p) { | ||
192 | if (p->code == code && p->handler == handler) | ||
193 | break; | ||
194 | q = p; | ||
195 | p = p->next; | ||
196 | } | ||
197 | if (!p) | ||
198 | return -ENOENT; | ||
199 | if (q) | ||
200 | q->next = p->next; | ||
201 | else | ||
202 | ext_int_hash[index] = p->next; | ||
203 | kfree(p); | ||
204 | return 0; | ||
205 | } | ||
206 | EXPORT_SYMBOL(unregister_external_interrupt); | ||
207 | |||
208 | void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code, | ||
209 | unsigned int param32, unsigned long param64) | ||
210 | { | ||
211 | struct pt_regs *old_regs; | ||
212 | unsigned short code; | ||
213 | struct ext_int_info *p; | ||
214 | int index; | ||
215 | |||
216 | code = (unsigned short) ext_int_code; | ||
217 | old_regs = set_irq_regs(regs); | ||
218 | s390_idle_check(regs, S390_lowcore.int_clock, | ||
219 | S390_lowcore.async_enter_timer); | ||
220 | irq_enter(); | ||
221 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) | ||
222 | /* Serve timer interrupts first. */ | ||
223 | clock_comparator_work(); | ||
224 | kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; | ||
225 | if (code != 0x1004) | ||
226 | __get_cpu_var(s390_idle).nohz_delay = 1; | ||
227 | index = ext_hash(code); | ||
228 | for (p = ext_int_hash[index]; p; p = p->next) { | ||
229 | if (likely(p->code == code)) | ||
230 | p->handler(ext_int_code, param32, param64); | ||
231 | } | ||
232 | irq_exit(); | ||
233 | set_irq_regs(old_regs); | ||
234 | } | ||
235 | |||
236 | static DEFINE_SPINLOCK(sc_irq_lock); | ||
237 | static int sc_irq_refcount; | ||
238 | |||
239 | void service_subclass_irq_register(void) | ||
240 | { | ||
241 | spin_lock(&sc_irq_lock); | ||
242 | if (!sc_irq_refcount) | ||
243 | ctl_set_bit(0, 9); | ||
244 | sc_irq_refcount++; | ||
245 | spin_unlock(&sc_irq_lock); | ||
246 | } | ||
247 | EXPORT_SYMBOL(service_subclass_irq_register); | ||
248 | |||
249 | void service_subclass_irq_unregister(void) | ||
250 | { | ||
251 | spin_lock(&sc_irq_lock); | ||
252 | sc_irq_refcount--; | ||
253 | if (!sc_irq_refcount) | ||
254 | ctl_clear_bit(0, 9); | ||
255 | spin_unlock(&sc_irq_lock); | ||
256 | } | ||
257 | EXPORT_SYMBOL(service_subclass_irq_unregister); | ||