diff options
Diffstat (limited to 'arch/s390/kernel/s390_ext.c')
-rw-r--r-- | arch/s390/kernel/s390_ext.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/arch/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c new file mode 100644 index 000000000000..3bdd38ec71da --- /dev/null +++ b/arch/s390/kernel/s390_ext.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * arch/s390/kernel/s390_ext.c | ||
3 | * | ||
4 | * S390 version | ||
5 | * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
6 | * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com), | ||
7 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/kernel_stat.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | |||
17 | #include <asm/lowcore.h> | ||
18 | #include <asm/s390_ext.h> | ||
19 | #include <asm/irq.h> | ||
20 | |||
21 | /* | ||
22 | * Simple hash strategy: index = code & 0xff; | ||
23 | * ext_int_hash[index] is the start of the list for all external interrupts | ||
24 | * that hash to this index. With the current set of external interrupts | ||
25 | * (0x1202 external call, 0x1004 cpu timer, 0x2401 hwc console, 0x4000 | ||
26 | * iucv and 0x2603 pfault) this is always the first element. | ||
27 | */ | ||
28 | ext_int_info_t *ext_int_hash[256] = { 0, }; | ||
29 | |||
30 | int register_external_interrupt(__u16 code, ext_int_handler_t handler) | ||
31 | { | ||
32 | ext_int_info_t *p; | ||
33 | int index; | ||
34 | |||
35 | p = (ext_int_info_t *) kmalloc(sizeof(ext_int_info_t), GFP_ATOMIC); | ||
36 | if (p == NULL) | ||
37 | return -ENOMEM; | ||
38 | p->code = code; | ||
39 | p->handler = handler; | ||
40 | index = code & 0xff; | ||
41 | p->next = ext_int_hash[index]; | ||
42 | ext_int_hash[index] = p; | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | int register_early_external_interrupt(__u16 code, ext_int_handler_t handler, | ||
47 | ext_int_info_t *p) | ||
48 | { | ||
49 | int index; | ||
50 | |||
51 | if (p == NULL) | ||
52 | return -EINVAL; | ||
53 | p->code = code; | ||
54 | p->handler = handler; | ||
55 | index = code & 0xff; | ||
56 | p->next = ext_int_hash[index]; | ||
57 | ext_int_hash[index] = p; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | int unregister_external_interrupt(__u16 code, ext_int_handler_t handler) | ||
62 | { | ||
63 | ext_int_info_t *p, *q; | ||
64 | int index; | ||
65 | |||
66 | index = code & 0xff; | ||
67 | q = NULL; | ||
68 | p = ext_int_hash[index]; | ||
69 | while (p != NULL) { | ||
70 | if (p->code == code && p->handler == handler) | ||
71 | break; | ||
72 | q = p; | ||
73 | p = p->next; | ||
74 | } | ||
75 | if (p == NULL) | ||
76 | return -ENOENT; | ||
77 | if (q != NULL) | ||
78 | q->next = p->next; | ||
79 | else | ||
80 | ext_int_hash[index] = p->next; | ||
81 | kfree(p); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | int unregister_early_external_interrupt(__u16 code, ext_int_handler_t handler, | ||
86 | ext_int_info_t *p) | ||
87 | { | ||
88 | ext_int_info_t *q; | ||
89 | int index; | ||
90 | |||
91 | if (p == NULL || p->code != code || p->handler != handler) | ||
92 | return -EINVAL; | ||
93 | index = code & 0xff; | ||
94 | q = ext_int_hash[index]; | ||
95 | if (p != q) { | ||
96 | while (q != NULL) { | ||
97 | if (q->next == p) | ||
98 | break; | ||
99 | q = q->next; | ||
100 | } | ||
101 | if (q == NULL) | ||
102 | return -ENOENT; | ||
103 | q->next = p->next; | ||
104 | } else | ||
105 | ext_int_hash[index] = p->next; | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | void do_extint(struct pt_regs *regs, unsigned short code) | ||
110 | { | ||
111 | ext_int_info_t *p; | ||
112 | int index; | ||
113 | |||
114 | irq_enter(); | ||
115 | asm volatile ("mc 0,0"); | ||
116 | if (S390_lowcore.int_clock >= S390_lowcore.jiffy_timer) | ||
117 | /** | ||
118 | * Make sure that the i/o interrupt did not "overtake" | ||
119 | * the last HZ timer interrupt. | ||
120 | */ | ||
121 | account_ticks(regs); | ||
122 | kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; | ||
123 | index = code & 0xff; | ||
124 | for (p = ext_int_hash[index]; p; p = p->next) { | ||
125 | if (likely(p->code == code)) { | ||
126 | if (likely(p->handler)) | ||
127 | p->handler(regs, code); | ||
128 | } | ||
129 | } | ||
130 | irq_exit(); | ||
131 | } | ||
132 | |||
133 | EXPORT_SYMBOL(register_external_interrupt); | ||
134 | EXPORT_SYMBOL(unregister_external_interrupt); | ||
135 | |||