]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
88dbd203 | 13 | #include <linux/ftrace.h> |
1da177e4 LT |
14 | #include <linux/errno.h> |
15 | #include <linux/kernel_stat.h> | |
16 | #include <linux/interrupt.h> | |
76d4e00a | 17 | #include <asm/cputime.h> |
1da177e4 LT |
18 | #include <asm/lowcore.h> |
19 | #include <asm/s390_ext.h> | |
5a489b98 | 20 | #include <asm/irq_regs.h> |
1da177e4 | 21 | #include <asm/irq.h> |
a806170e | 22 | #include "entry.h" |
1da177e4 LT |
23 | |
24 | /* | |
1da177e4 LT |
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 | */ | |
d2c993d8 | 30 | ext_int_info_t *ext_int_hash[256] = { NULL, }; |
1da177e4 | 31 | |
99b2d8df HC |
32 | static inline int ext_hash(__u16 code) |
33 | { | |
34 | return (code + (code >> 9)) & 0xff; | |
35 | } | |
36 | ||
1da177e4 LT |
37 | int register_external_interrupt(__u16 code, ext_int_handler_t handler) |
38 | { | |
39 | ext_int_info_t *p; | |
40 | int index; | |
41 | ||
5cbded58 | 42 | p = kmalloc(sizeof(ext_int_info_t), GFP_ATOMIC); |
1da177e4 LT |
43 | if (p == NULL) |
44 | return -ENOMEM; | |
45 | p->code = code; | |
46 | p->handler = handler; | |
99b2d8df | 47 | index = ext_hash(code); |
1da177e4 LT |
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; | |
99b2d8df | 62 | index = ext_hash(code); |
1da177e4 LT |
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 | ||
99b2d8df | 73 | index = ext_hash(code); |
1da177e4 LT |
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; | |
99b2d8df | 100 | index = ext_hash(code); |
1da177e4 LT |
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 | ||
88dbd203 | 116 | void __irq_entry do_extint(struct pt_regs *regs, unsigned short code) |
1da177e4 LT |
117 | { |
118 | ext_int_info_t *p; | |
119 | int index; | |
5a489b98 | 120 | struct pt_regs *old_regs; |
1da177e4 | 121 | |
5a489b98 | 122 | old_regs = set_irq_regs(regs); |
43ca5c3a | 123 | s390_idle_check(); |
9cfb9b3c | 124 | irq_enter(); |
5a62b192 HC |
125 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) |
126 | /* Serve timer interrupts first. */ | |
127 | clock_comparator_work(); | |
1da177e4 | 128 | kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; |
99b2d8df | 129 | index = ext_hash(code); |
1da177e4 | 130 | for (p = ext_int_hash[index]; p; p = p->next) { |
d54853ef MS |
131 | if (likely(p->code == code)) |
132 | p->handler(code); | |
1da177e4 LT |
133 | } |
134 | irq_exit(); | |
9d0a57cb | 135 | set_irq_regs(old_regs); |
1da177e4 LT |
136 | } |
137 | ||
138 | EXPORT_SYMBOL(register_external_interrupt); | |
139 | EXPORT_SYMBOL(unregister_external_interrupt); |