vserver 2.0 rc7
[linux-2.6.git] / arch / s390 / kernel / s390_ext.c
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