File: /usr/src/linux/arch/mips/dec/irq.c
1 /*
2 * Code to handle DECstation IRQs plus some generic interrupt stuff.
3 *
4 * Copyright (C) 1992 Linus Torvalds
5 * Copyright (C) 1994, 1995, 1996, 1997, 2000 Ralf Baechle
6 */
7 #include <linux/errno.h>
8 #include <linux/init.h>
9 #include <linux/kernel_stat.h>
10 #include <linux/signal.h>
11 #include <linux/sched.h>
12 #include <linux/types.h>
13 #include <linux/interrupt.h>
14 #include <linux/ioport.h>
15 #include <linux/timex.h>
16 #include <linux/slab.h>
17 #include <linux/random.h>
18
19 #include <asm/bitops.h>
20 #include <asm/bootinfo.h>
21 #include <asm/io.h>
22 #include <asm/irq.h>
23 #include <asm/mipsregs.h>
24 #include <asm/system.h>
25
26 #include <asm/dec/interrupts.h>
27
28 extern void dec_init_kn01(void);
29 extern void dec_init_kn230(void);
30 extern void dec_init_kn02(void);
31 extern void dec_init_kn02ba(void);
32 extern void dec_init_kn02ca(void);
33 extern void dec_init_kn03(void);
34
35 extern asmlinkage void decstation_handle_int(void);
36
37 unsigned long spurious_count = 0;
38
39 static inline void mask_irq(unsigned int irq_nr)
40 {
41 unsigned int dummy;
42
43 if (dec_interrupt[irq_nr].iemask) { /* This is an ASIC interrupt */
44 *imr &= ~dec_interrupt[irq_nr].iemask;
45 dummy = *imr;
46 dummy = *imr;
47 } else /* This is a cpu interrupt */
48 change_cp0_status(ST0_IM, read_32bit_cp0_register(CP0_STATUS) & ~dec_interrupt[irq_nr].cpu_mask);
49 }
50
51 static inline void unmask_irq(unsigned int irq_nr)
52 {
53 unsigned int dummy;
54
55 if (dec_interrupt[irq_nr].iemask) { /* This is an ASIC interrupt */
56 *imr |= dec_interrupt[irq_nr].iemask;
57 dummy = *imr;
58 dummy = *imr;
59 }
60 change_cp0_status(ST0_IM, read_32bit_cp0_register(CP0_STATUS) | dec_interrupt[irq_nr].cpu_mask);
61 }
62
63 void disable_irq(unsigned int irq_nr)
64 {
65 unsigned long flags;
66
67 save_and_cli(flags);
68 mask_irq(irq_nr);
69 restore_flags(flags);
70 }
71
72 void enable_irq(unsigned int irq_nr)
73 {
74 unsigned long flags;
75
76 save_and_cli(flags);
77 unmask_irq(irq_nr);
78 restore_flags(flags);
79 }
80
81 /*
82 * Pointers to the low-level handlers: first the general ones, then the
83 * fast ones, then the bad ones.
84 */
85 extern void interrupt(void);
86
87 static struct irqaction *irq_action[32] =
88 {
89 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
90 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
91 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
92 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
93 };
94
95 int get_irq_list(char *buf)
96 {
97 int i, len = 0;
98 struct irqaction *action;
99
100 for (i = 0; i < 32; i++) {
101 action = irq_action[i];
102 if (!action)
103 continue;
104 len += sprintf(buf + len, "%2d: %8d %c %s",
105 i, kstat.irqs[0][i],
106 (action->flags & SA_INTERRUPT) ? '+' : ' ',
107 action->name);
108 for (action = action->next; action; action = action->next) {
109 len += sprintf(buf + len, ",%s %s",
110 (action->flags & SA_INTERRUPT) ? " +" : "",
111 action->name);
112 }
113 len += sprintf(buf + len, "\n");
114 }
115 return len;
116 }
117
118 /*
119 * do_IRQ handles IRQ's that have been installed without the
120 * SA_INTERRUPT flag: it uses the full signal-handling return
121 * and runs with other interrupts enabled. All relatively slow
122 * IRQ's should use this format: notably the keyboard/timer
123 * routines.
124 */
125 asmlinkage void do_IRQ(int irq, struct pt_regs *regs)
126 {
127 struct irqaction *action;
128 int do_random, cpu;
129
130 cpu = smp_processor_id();
131 irq_enter(cpu, irq);
132 kstat.irqs[cpu][irq]++;
133
134 mask_irq(irq);
135 action = *(irq + irq_action);
136 if (action) {
137 if (!(action->flags & SA_INTERRUPT))
138 __sti();
139 action = *(irq + irq_action);
140 do_random = 0;
141 do {
142 do_random |= action->flags;
143 action->handler(irq, action->dev_id, regs);
144 action = action->next;
145 } while (action);
146 if (do_random & SA_SAMPLE_RANDOM)
147 add_interrupt_randomness(irq);
148 __cli();
149 unmask_irq(irq);
150 }
151 irq_exit(cpu, irq);
152
153 /* unmasking and bottom half handling is done magically for us. */
154 }
155
156 /*
157 * Idea is to put all interrupts
158 * in a single table and differenciate them just by number.
159 */
160 int setup_dec_irq(int irq, struct irqaction *new)
161 {
162 int shared = 0;
163 struct irqaction *old, **p;
164 unsigned long flags;
165
166 p = irq_action + irq;
167 if ((old = *p) != NULL) {
168 /* Can't share interrupts unless both agree to */
169 if (!(old->flags & new->flags & SA_SHIRQ))
170 return -EBUSY;
171
172 /* Can't share interrupts unless both are same type */
173 if ((old->flags ^ new->flags) & SA_INTERRUPT)
174 return -EBUSY;
175
176 /* add new interrupt at end of irq queue */
177 do {
178 p = &old->next;
179 old = *p;
180 } while (old);
181 shared = 1;
182 }
183 if (new->flags & SA_SAMPLE_RANDOM)
184 rand_initialize_irq(irq);
185
186 save_and_cli(flags);
187 *p = new;
188
189 if (!shared) {
190 unmask_irq(irq);
191 }
192 restore_flags(flags);
193 return 0;
194 }
195
196 int request_irq(unsigned int irq,
197 void (*handler) (int, void *, struct pt_regs *),
198 unsigned long irqflags,
199 const char *devname,
200 void *dev_id)
201 {
202 int retval;
203 struct irqaction *action;
204
205 if (irq >= 32)
206 return -EINVAL;
207 if (!handler)
208 return -EINVAL;
209
210 action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
211 if (!action)
212 return -ENOMEM;
213
214 action->handler = handler;
215 action->flags = irqflags;
216 action->mask = 0;
217 action->name = devname;
218 action->next = NULL;
219 action->dev_id = dev_id;
220
221 retval = setup_dec_irq(irq, action);
222
223 if (retval)
224 kfree(action);
225 return retval;
226 }
227
228 void free_irq(unsigned int irq, void *dev_id)
229 {
230 struct irqaction *action, **p;
231 unsigned long flags;
232
233 if (irq > 39) {
234 printk("Trying to free IRQ%d\n", irq);
235 return;
236 }
237 for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
238 if (action->dev_id != dev_id)
239 continue;
240
241 /* Found it - now free it */
242 save_and_cli(flags);
243 *p = action->next;
244 if (!irq[irq_action])
245 mask_irq(irq);
246 restore_flags(flags);
247 kfree(action);
248 return;
249 }
250 printk("Trying to free free IRQ%d\n", irq);
251 }
252
253 unsigned long probe_irq_on(void)
254 {
255 /* TODO */
256 return 0;
257 }
258
259 int probe_irq_off(unsigned long irqs)
260 {
261 /* TODO */
262 return 0;
263 }
264
265 void __init init_IRQ(void)
266 {
267 switch (mips_machtype) {
268 case MACH_DS23100:
269 dec_init_kn01();
270 break;
271 case MACH_DS5100: /* DS5100 MIPSMATE */
272 dec_init_kn230();
273 break;
274 case MACH_DS5000_200: /* DS5000 3max */
275 dec_init_kn02();
276 break;
277 case MACH_DS5000_1XX: /* DS5000/100 3min */
278 dec_init_kn02ba();
279 break;
280 case MACH_DS5000_2X0: /* DS5000/240 3max+ */
281 dec_init_kn03();
282 break;
283 case MACH_DS5000_XX: /* Personal DS5000/2x */
284 dec_init_kn02ca();
285 break;
286 case MACH_DS5800: /* DS5800 Isis */
287 panic("Don't know how to set this up!");
288 break;
289 case MACH_DS5400: /* DS5400 MIPSfair */
290 panic("Don't know how to set this up!");
291 break;
292 case MACH_DS5500: /* DS5500 MIPSfair-2 */
293 panic("Don't know how to set this up!");
294 break;
295 }
296 set_except_vector(0, decstation_handle_int);
297 }
298