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