Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[linux-2.6] / 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/ftrace.h>
14 #include <linux/errno.h>
15 #include <linux/kernel_stat.h>
16 #include <linux/interrupt.h>
17 #include <asm/cputime.h>
18 #include <asm/lowcore.h>
19 #include <asm/s390_ext.h>
20 #include <asm/irq_regs.h>
21 #include <asm/irq.h>
22 #include "entry.h"
23
24 /*
25  * ext_int_hash[index] is the start of the list for all external interrupts
26  * that hash to this index. With the current set of external interrupts 
27  * (0x1202 external call, 0x1004 cpu timer, 0x2401 hwc console, 0x4000
28  * iucv and 0x2603 pfault) this is always the first element. 
29  */
30 ext_int_info_t *ext_int_hash[256] = { NULL, };
31
32 static inline int ext_hash(__u16 code)
33 {
34         return (code + (code >> 9)) & 0xff;
35 }
36
37 int register_external_interrupt(__u16 code, ext_int_handler_t handler)
38 {
39         ext_int_info_t *p;
40         int index;
41
42         p = kmalloc(sizeof(ext_int_info_t), GFP_ATOMIC);
43         if (p == NULL)
44                 return -ENOMEM;
45         p->code = code;
46         p->handler = handler;
47         index = ext_hash(code);
48         p->next = ext_int_hash[index];
49         ext_int_hash[index] = p;
50         return 0;
51 }
52
53 int register_early_external_interrupt(__u16 code, ext_int_handler_t handler,
54                                       ext_int_info_t *p)
55 {
56         int index;
57
58         if (p == NULL)
59                 return -EINVAL;
60         p->code = code;
61         p->handler = handler;
62         index = ext_hash(code);
63         p->next = ext_int_hash[index];
64         ext_int_hash[index] = p;
65         return 0;
66 }
67
68 int unregister_external_interrupt(__u16 code, ext_int_handler_t handler)
69 {
70         ext_int_info_t *p, *q;
71         int index;
72
73         index = ext_hash(code);
74         q = NULL;
75         p = ext_int_hash[index];
76         while (p != NULL) {
77                 if (p->code == code && p->handler == handler)
78                         break;
79                 q = p;
80                 p = p->next;
81         }
82         if (p == NULL)
83                 return -ENOENT;
84         if (q != NULL)
85                 q->next = p->next;
86         else
87                 ext_int_hash[index] = p->next;
88         kfree(p);
89         return 0;
90 }
91
92 int unregister_early_external_interrupt(__u16 code, ext_int_handler_t handler,
93                                         ext_int_info_t *p)
94 {
95         ext_int_info_t *q;
96         int index;
97
98         if (p == NULL || p->code != code || p->handler != handler)
99                 return -EINVAL;
100         index = ext_hash(code);
101         q = ext_int_hash[index];
102         if (p != q) {
103                 while (q != NULL) {
104                         if (q->next == p)
105                                 break;
106                         q = q->next;
107                 }
108                 if (q == NULL)
109                         return -ENOENT;
110                 q->next = p->next;
111         } else
112                 ext_int_hash[index] = p->next;
113         return 0;
114 }
115
116 void __irq_entry do_extint(struct pt_regs *regs, unsigned short code)
117 {
118         ext_int_info_t *p;
119         int index;
120         struct pt_regs *old_regs;
121
122         old_regs = set_irq_regs(regs);
123         s390_idle_check();
124         irq_enter();
125         if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
126                 /* Serve timer interrupts first. */
127                 clock_comparator_work();
128         kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++;
129         index = ext_hash(code);
130         for (p = ext_int_hash[index]; p; p = p->next) {
131                 if (likely(p->code == code))
132                         p->handler(code);
133         }
134         irq_exit();
135         set_irq_regs(old_regs);
136 }
137
138 EXPORT_SYMBOL(register_external_interrupt);
139 EXPORT_SYMBOL(unregister_external_interrupt);