xref: /linux/arch/m68k/kernel/ints.c (revision 5e8d780d745c1619aba81fe7166c5a4b5cad2b84)
1 /*
2  * linux/arch/m68k/kernel/ints.c -- Linux/m68k general interrupt handling code
3  *
4  * This file is subject to the terms and conditions of the GNU General Public
5  * License.  See the file COPYING in the main directory of this archive
6  * for more details.
7  *
8  * 07/03/96: Timer initialization, and thus mach_sched_init(),
9  *           removed from request_irq() and moved to init_time().
10  *           We should therefore consider renaming our add_isr() and
11  *           remove_isr() to request_irq() and free_irq()
12  *           respectively, so they are compliant with the other
13  *           architectures.                                     /Jes
14  * 11/07/96: Changed all add_/remove_isr() to request_/free_irq() calls.
15  *           Removed irq list support, if any machine needs an irq server
16  *           it must implement this itself (as it's already done), instead
17  *           only default handler are used with mach_default_handler.
18  *           request_irq got some flags different from other architectures:
19  *           - IRQ_FLG_REPLACE : Replace an existing handler (the default one
20  *                               can be replaced without this flag)
21  *           - IRQ_FLG_LOCK : handler can't be replaced
22  *           There are other machine depending flags, see there
23  *           If you want to replace a default handler you should know what
24  *           you're doing, since it might handle different other irq sources
25  *           which must be served                               /Roman Zippel
26  */
27 
28 #include <linux/config.h>
29 #include <linux/module.h>
30 #include <linux/types.h>
31 #include <linux/sched.h>
32 #include <linux/kernel_stat.h>
33 #include <linux/errno.h>
34 #include <linux/init.h>
35 
36 #include <asm/setup.h>
37 #include <asm/system.h>
38 #include <asm/irq.h>
39 #include <asm/traps.h>
40 #include <asm/page.h>
41 #include <asm/machdep.h>
42 #include <asm/cacheflush.h>
43 
44 #ifdef CONFIG_Q40
45 #include <asm/q40ints.h>
46 #endif
47 
48 extern u32 auto_irqhandler_fixup[];
49 extern u32 user_irqhandler_fixup[];
50 extern u16 user_irqvec_fixup[];
51 
52 /* table for system interrupt handlers */
53 static struct irq_node *irq_list[NR_IRQS];
54 static struct irq_controller *irq_controller[NR_IRQS];
55 static int irq_depth[NR_IRQS];
56 
57 static int m68k_first_user_vec;
58 
59 static struct irq_controller auto_irq_controller = {
60 	.name		= "auto",
61 	.lock		= SPIN_LOCK_UNLOCKED,
62 	.startup	= m68k_irq_startup,
63 	.shutdown	= m68k_irq_shutdown,
64 };
65 
66 static struct irq_controller user_irq_controller = {
67 	.name		= "user",
68 	.lock		= SPIN_LOCK_UNLOCKED,
69 	.startup	= m68k_irq_startup,
70 	.shutdown	= m68k_irq_shutdown,
71 };
72 
73 #define NUM_IRQ_NODES 100
74 static irq_node_t nodes[NUM_IRQ_NODES];
75 
76 /*
77  * void init_IRQ(void)
78  *
79  * Parameters:	None
80  *
81  * Returns:	Nothing
82  *
83  * This function should be called during kernel startup to initialize
84  * the IRQ handling routines.
85  */
86 
87 void __init init_IRQ(void)
88 {
89 	int i;
90 
91 	/* assembly irq entry code relies on this... */
92 	if (HARDIRQ_MASK != 0x00ff0000) {
93 		extern void hardirq_mask_is_broken(void);
94 		hardirq_mask_is_broken();
95 	}
96 
97 	for (i = IRQ_AUTO_1; i <= IRQ_AUTO_7; i++)
98 		irq_controller[i] = &auto_irq_controller;
99 
100 	mach_init_IRQ();
101 }
102 
103 /**
104  * m68k_setup_auto_interrupt
105  * @handler: called from auto vector interrupts
106  *
107  * setup the handler to be called from auto vector interrupts instead of the
108  * standard m68k_handle_int(), it will be called with irq numbers in the range
109  * from IRQ_AUTO_1 - IRQ_AUTO_7.
110  */
111 void __init m68k_setup_auto_interrupt(void (*handler)(unsigned int, struct pt_regs *))
112 {
113 	if (handler)
114 		*auto_irqhandler_fixup = (u32)handler;
115 	flush_icache();
116 }
117 
118 /**
119  * m68k_setup_user_interrupt
120  * @vec: first user vector interrupt to handle
121  * @cnt: number of active user vector interrupts
122  * @handler: called from user vector interrupts
123  *
124  * setup user vector interrupts, this includes activating the specified range
125  * of interrupts, only then these interrupts can be requested (note: this is
126  * different from auto vector interrupts). An optional handler can be installed
127  * to be called instead of the default m68k_handle_int(), it will be called
128  * with irq numbers starting from IRQ_USER.
129  */
130 void __init m68k_setup_user_interrupt(unsigned int vec, unsigned int cnt,
131 				      void (*handler)(unsigned int, struct pt_regs *))
132 {
133 	int i;
134 
135 	m68k_first_user_vec = vec;
136 	for (i = 0; i < cnt; i++)
137 		irq_controller[IRQ_USER + i] = &user_irq_controller;
138 	*user_irqvec_fixup = vec - IRQ_USER;
139 	if (handler)
140 		*user_irqhandler_fixup = (u32)handler;
141 	flush_icache();
142 }
143 
144 /**
145  * m68k_setup_irq_controller
146  * @contr: irq controller which controls specified irq
147  * @irq: first irq to be managed by the controller
148  *
149  * Change the controller for the specified range of irq, which will be used to
150  * manage these irq. auto/user irq already have a default controller, which can
151  * be changed as well, but the controller probably should use m68k_irq_startup/
152  * m68k_irq_shutdown.
153  */
154 void m68k_setup_irq_controller(struct irq_controller *contr, unsigned int irq,
155 			       unsigned int cnt)
156 {
157 	int i;
158 
159 	for (i = 0; i < cnt; i++)
160 		irq_controller[irq + i] = contr;
161 }
162 
163 irq_node_t *new_irq_node(void)
164 {
165 	irq_node_t *node;
166 	short i;
167 
168 	for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) {
169 		if (!node->handler) {
170 			memset(node, 0, sizeof(*node));
171 			return node;
172 		}
173 	}
174 
175 	printk ("new_irq_node: out of nodes\n");
176 	return NULL;
177 }
178 
179 int setup_irq(unsigned int irq, struct irq_node *node)
180 {
181 	struct irq_controller *contr;
182 	struct irq_node **prev;
183 	unsigned long flags;
184 
185 	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
186 		printk("%s: Incorrect IRQ %d from %s\n",
187 		       __FUNCTION__, irq, node->devname);
188 		return -ENXIO;
189 	}
190 
191 	spin_lock_irqsave(&contr->lock, flags);
192 
193 	prev = irq_list + irq;
194 	if (*prev) {
195 		/* Can't share interrupts unless both agree to */
196 		if (!((*prev)->flags & node->flags & SA_SHIRQ)) {
197 			spin_unlock_irqrestore(&contr->lock, flags);
198 			return -EBUSY;
199 		}
200 		while (*prev)
201 			prev = &(*prev)->next;
202 	}
203 
204 	if (!irq_list[irq]) {
205 		if (contr->startup)
206 			contr->startup(irq);
207 		else
208 			contr->enable(irq);
209 	}
210 	node->next = NULL;
211 	*prev = node;
212 
213 	spin_unlock_irqrestore(&contr->lock, flags);
214 
215 	return 0;
216 }
217 
218 int request_irq(unsigned int irq,
219 		irqreturn_t (*handler) (int, void *, struct pt_regs *),
220 		unsigned long flags, const char *devname, void *dev_id)
221 {
222 	struct irq_node *node;
223 	int res;
224 
225 	node = new_irq_node();
226 	if (!node)
227 		return -ENOMEM;
228 
229 	node->handler = handler;
230 	node->flags   = flags;
231 	node->dev_id  = dev_id;
232 	node->devname = devname;
233 
234 	res = setup_irq(irq, node);
235 	if (res)
236 		node->handler = NULL;
237 
238 	return res;
239 }
240 
241 EXPORT_SYMBOL(request_irq);
242 
243 void free_irq(unsigned int irq, void *dev_id)
244 {
245 	struct irq_controller *contr;
246 	struct irq_node **p, *node;
247 	unsigned long flags;
248 
249 	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
250 		printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq);
251 		return;
252 	}
253 
254 	spin_lock_irqsave(&contr->lock, flags);
255 
256 	p = irq_list + irq;
257 	while ((node = *p)) {
258 		if (node->dev_id == dev_id)
259 			break;
260 		p = &node->next;
261 	}
262 
263 	if (node) {
264 		*p = node->next;
265 		node->handler = NULL;
266 	} else
267 		printk("%s: Removing probably wrong IRQ %d\n",
268 		       __FUNCTION__, irq);
269 
270 	if (!irq_list[irq]) {
271 		if (contr->shutdown)
272 			contr->shutdown(irq);
273 		else
274 			contr->disable(irq);
275 	}
276 
277 	spin_unlock_irqrestore(&contr->lock, flags);
278 }
279 
280 EXPORT_SYMBOL(free_irq);
281 
282 void enable_irq(unsigned int irq)
283 {
284 	struct irq_controller *contr;
285 	unsigned long flags;
286 
287 	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
288 		printk("%s: Incorrect IRQ %d\n",
289 		       __FUNCTION__, irq);
290 		return;
291 	}
292 
293 	spin_lock_irqsave(&contr->lock, flags);
294 	if (irq_depth[irq]) {
295 		if (!--irq_depth[irq]) {
296 			if (contr->enable)
297 				contr->enable(irq);
298 		}
299 	} else
300 		WARN_ON(1);
301 	spin_unlock_irqrestore(&contr->lock, flags);
302 }
303 
304 EXPORT_SYMBOL(enable_irq);
305 
306 void disable_irq(unsigned int irq)
307 {
308 	struct irq_controller *contr;
309 	unsigned long flags;
310 
311 	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
312 		printk("%s: Incorrect IRQ %d\n",
313 		       __FUNCTION__, irq);
314 		return;
315 	}
316 
317 	spin_lock_irqsave(&contr->lock, flags);
318 	if (!irq_depth[irq]++) {
319 		if (contr->disable)
320 			contr->disable(irq);
321 	}
322 	spin_unlock_irqrestore(&contr->lock, flags);
323 }
324 
325 EXPORT_SYMBOL(disable_irq);
326 
327 int m68k_irq_startup(unsigned int irq)
328 {
329 	if (irq <= IRQ_AUTO_7)
330 		vectors[VEC_SPUR + irq] = auto_inthandler;
331 	else
332 		vectors[m68k_first_user_vec + irq - IRQ_USER] = user_inthandler;
333 	return 0;
334 }
335 
336 void m68k_irq_shutdown(unsigned int irq)
337 {
338 	if (irq <= IRQ_AUTO_7)
339 		vectors[VEC_SPUR + irq] = bad_inthandler;
340 	else
341 		vectors[m68k_first_user_vec + irq - IRQ_USER] = bad_inthandler;
342 }
343 
344 
345 /*
346  * Do we need these probe functions on the m68k?
347  *
348  *  ... may be useful with ISA devices
349  */
350 unsigned long probe_irq_on (void)
351 {
352 #ifdef CONFIG_Q40
353 	if (MACH_IS_Q40)
354 		return q40_probe_irq_on();
355 #endif
356 	return 0;
357 }
358 
359 EXPORT_SYMBOL(probe_irq_on);
360 
361 int probe_irq_off (unsigned long irqs)
362 {
363 #ifdef CONFIG_Q40
364 	if (MACH_IS_Q40)
365 		return q40_probe_irq_off(irqs);
366 #endif
367 	return 0;
368 }
369 
370 EXPORT_SYMBOL(probe_irq_off);
371 
372 unsigned int irq_canonicalize(unsigned int irq)
373 {
374 #ifdef CONFIG_Q40
375 	if (MACH_IS_Q40 && irq == 11)
376 		irq = 10;
377 #endif
378 	return irq;
379 }
380 
381 EXPORT_SYMBOL(irq_canonicalize);
382 
383 asmlinkage void m68k_handle_int(unsigned int irq, struct pt_regs *regs)
384 {
385 	struct irq_node *node;
386 
387 	kstat_cpu(0).irqs[irq]++;
388 	node = irq_list[irq];
389 	do {
390 		node->handler(irq, node->dev_id, regs);
391 		node = node->next;
392 	} while (node);
393 }
394 
395 asmlinkage void handle_badint(struct pt_regs *regs)
396 {
397 	kstat_cpu(0).irqs[0]++;
398 	printk("unexpected interrupt from %u\n", regs->vector);
399 }
400 
401 int show_interrupts(struct seq_file *p, void *v)
402 {
403 	struct irq_controller *contr;
404 	struct irq_node *node;
405 	int i = *(loff_t *) v;
406 
407 	/* autovector interrupts */
408 	if (irq_list[i]) {
409 		contr = irq_controller[i];
410 		node = irq_list[i];
411 		seq_printf(p, "%-8s %3u: %10u %s", contr->name, i, kstat_cpu(0).irqs[i], node->devname);
412 		while ((node = node->next))
413 			seq_printf(p, ", %s", node->devname);
414 		seq_puts(p, "\n");
415 	}
416 	return 0;
417 }
418 
419 void init_irq_proc(void)
420 {
421 	/* Insert /proc/irq driver here */
422 }
423 
424