Blame view

arch/m68k/kernel/ints.c 9.99 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  /*
   * linux/arch/m68k/kernel/ints.c -- Linux/m68k general interrupt handling code
   *
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file COPYING in the main directory of this archive
   * for more details.
   *
   * 07/03/96: Timer initialization, and thus mach_sched_init(),
   *           removed from request_irq() and moved to init_time().
   *           We should therefore consider renaming our add_isr() and
   *           remove_isr() to request_irq() and free_irq()
   *           respectively, so they are compliant with the other
   *           architectures.                                     /Jes
   * 11/07/96: Changed all add_/remove_isr() to request_/free_irq() calls.
   *           Removed irq list support, if any machine needs an irq server
   *           it must implement this itself (as it's already done), instead
   *           only default handler are used with mach_default_handler.
   *           request_irq got some flags different from other architectures:
   *           - IRQ_FLG_REPLACE : Replace an existing handler (the default one
   *                               can be replaced without this flag)
   *           - IRQ_FLG_LOCK : handler can't be replaced
   *           There are other machine depending flags, see there
   *           If you want to replace a default handler you should know what
   *           you're doing, since it might handle different other irq sources
   *           which must be served                               /Roman Zippel
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/sched.h>
6168a702a   Andrew Morton   [PATCH] Declare i...
30
  #include <linux/interrupt.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
36
37
38
39
40
  #include <linux/kernel_stat.h>
  #include <linux/errno.h>
  #include <linux/init.h>
  
  #include <asm/setup.h>
  #include <asm/system.h>
  #include <asm/irq.h>
  #include <asm/traps.h>
  #include <asm/page.h>
  #include <asm/machdep.h>
68387c448   Roman Zippel   [PATCH] m68k: con...
41
  #include <asm/cacheflush.h>
2850bc273   Al Viro   [PATCH] m68k pt_r...
42
  #include <asm/irq_regs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
  
  #ifdef CONFIG_Q40
  #include <asm/q40ints.h>
  #endif
68387c448   Roman Zippel   [PATCH] m68k: con...
47
48
49
  extern u32 auto_irqhandler_fixup[];
  extern u32 user_irqhandler_fixup[];
  extern u16 user_irqvec_fixup[];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  /* table for system interrupt handlers */
68387c448   Roman Zippel   [PATCH] m68k: con...
51
52
53
54
55
  static struct irq_node *irq_list[NR_IRQS];
  static struct irq_controller *irq_controller[NR_IRQS];
  static int irq_depth[NR_IRQS];
  
  static int m68k_first_user_vec;
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
56
57
58
59
60
61
62
  
  static struct irq_controller auto_irq_controller = {
  	.name		= "auto",
  	.lock		= SPIN_LOCK_UNLOCKED,
  	.startup	= m68k_irq_startup,
  	.shutdown	= m68k_irq_shutdown,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

68387c448   Roman Zippel   [PATCH] m68k: con...
64
65
66
67
68
  static struct irq_controller user_irq_controller = {
  	.name		= "user",
  	.lock		= SPIN_LOCK_UNLOCKED,
  	.startup	= m68k_irq_startup,
  	.shutdown	= m68k_irq_shutdown,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  #define NUM_IRQ_NODES 100
  static irq_node_t nodes[NUM_IRQ_NODES];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  /*
   * void init_IRQ(void)
   *
   * Parameters:	None
   *
   * Returns:	Nothing
   *
   * This function should be called during kernel startup to initialize
   * the IRQ handling routines.
   */
  
  void __init init_IRQ(void)
  {
  	int i;
6d2f16a89   Roman Zippel   [PATCH] m68k: adj...
86
87
88
89
90
  	/* assembly irq entry code relies on this... */
  	if (HARDIRQ_MASK != 0x00ff0000) {
  		extern void hardirq_mask_is_broken(void);
  		hardirq_mask_is_broken();
  	}
68387c448   Roman Zippel   [PATCH] m68k: con...
91
  	for (i = IRQ_AUTO_1; i <= IRQ_AUTO_7; i++)
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
92
  		irq_controller[i] = &auto_irq_controller;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93

68387c448   Roman Zippel   [PATCH] m68k: con...
94
95
96
97
98
99
100
101
  	mach_init_IRQ();
  }
  
  /**
   * m68k_setup_auto_interrupt
   * @handler: called from auto vector interrupts
   *
   * setup the handler to be called from auto vector interrupts instead of the
2850bc273   Al Viro   [PATCH] m68k pt_r...
102
   * standard __m68k_handle_int(), it will be called with irq numbers in the range
68387c448   Roman Zippel   [PATCH] m68k: con...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
   * from IRQ_AUTO_1 - IRQ_AUTO_7.
   */
  void __init m68k_setup_auto_interrupt(void (*handler)(unsigned int, struct pt_regs *))
  {
  	if (handler)
  		*auto_irqhandler_fixup = (u32)handler;
  	flush_icache();
  }
  
  /**
   * m68k_setup_user_interrupt
   * @vec: first user vector interrupt to handle
   * @cnt: number of active user vector interrupts
   * @handler: called from user vector interrupts
   *
   * setup user vector interrupts, this includes activating the specified range
   * of interrupts, only then these interrupts can be requested (note: this is
   * different from auto vector interrupts). An optional handler can be installed
2850bc273   Al Viro   [PATCH] m68k pt_r...
121
   * to be called instead of the default __m68k_handle_int(), it will be called
68387c448   Roman Zippel   [PATCH] m68k: con...
122
123
124
125
126
127
   * with irq numbers starting from IRQ_USER.
   */
  void __init m68k_setup_user_interrupt(unsigned int vec, unsigned int cnt,
  				      void (*handler)(unsigned int, struct pt_regs *))
  {
  	int i;
69961c375   Geert Uytterhoeven   [PATCH] m68k/Atar...
128
  	BUG_ON(IRQ_USER + cnt >= NR_IRQS);
68387c448   Roman Zippel   [PATCH] m68k: con...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  	m68k_first_user_vec = vec;
  	for (i = 0; i < cnt; i++)
  		irq_controller[IRQ_USER + i] = &user_irq_controller;
  	*user_irqvec_fixup = vec - IRQ_USER;
  	if (handler)
  		*user_irqhandler_fixup = (u32)handler;
  	flush_icache();
  }
  
  /**
   * m68k_setup_irq_controller
   * @contr: irq controller which controls specified irq
   * @irq: first irq to be managed by the controller
   *
   * Change the controller for the specified range of irq, which will be used to
   * manage these irq. auto/user irq already have a default controller, which can
   * be changed as well, but the controller probably should use m68k_irq_startup/
   * m68k_irq_shutdown.
   */
  void m68k_setup_irq_controller(struct irq_controller *contr, unsigned int irq,
  			       unsigned int cnt)
  {
  	int i;
  
  	for (i = 0; i < cnt; i++)
  		irq_controller[irq + i] = contr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
  }
  
  irq_node_t *new_irq_node(void)
  {
  	irq_node_t *node;
  	short i;
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
161
162
163
  	for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) {
  		if (!node->handler) {
  			memset(node, 0, sizeof(*node));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
  			return node;
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
165
166
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
171
  
  	printk ("new_irq_node: out of nodes
  ");
  	return NULL;
  }
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
172
  int setup_irq(unsigned int irq, struct irq_node *node)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  {
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
174
175
176
  	struct irq_controller *contr;
  	struct irq_node **prev;
  	unsigned long flags;
68387c448   Roman Zippel   [PATCH] m68k: con...
177
  	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
  		printk("%s: Incorrect IRQ %d from %s
  ",
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
180
  		       __FUNCTION__, irq, node->devname);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  		return -ENXIO;
  	}
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
183
184
185
186
187
  	spin_lock_irqsave(&contr->lock, flags);
  
  	prev = irq_list + irq;
  	if (*prev) {
  		/* Can't share interrupts unless both agree to */
b0b9fdc12   Thomas Gleixner   [PATCH] irq-flags...
188
  		if (!((*prev)->flags & node->flags & IRQF_SHARED)) {
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
189
  			spin_unlock_irqrestore(&contr->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
  			return -EBUSY;
  		}
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
192
193
  		while (*prev)
  			prev = &(*prev)->next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195

b5dc7840b   Roman Zippel   [PATCH] m68k: int...
196
197
198
199
200
201
202
203
204
205
  	if (!irq_list[irq]) {
  		if (contr->startup)
  			contr->startup(irq);
  		else
  			contr->enable(irq);
  	}
  	node->next = NULL;
  	*prev = node;
  
  	spin_unlock_irqrestore(&contr->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
  	return 0;
  }
68387c448   Roman Zippel   [PATCH] m68k: con...
208
  int request_irq(unsigned int irq,
40220c1a1   David Howells   IRQ: Use the new ...
209
  		irq_handler_t handler,
68387c448   Roman Zippel   [PATCH] m68k: con...
210
  		unsigned long flags, const char *devname, void *dev_id)
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  {
  	struct irq_node *node;
  	int res;
  
  	node = new_irq_node();
  	if (!node)
  		return -ENOMEM;
  
  	node->handler = handler;
  	node->flags   = flags;
  	node->dev_id  = dev_id;
  	node->devname = devname;
  
  	res = setup_irq(irq, node);
  	if (res)
  		node->handler = NULL;
  
  	return res;
  }
68387c448   Roman Zippel   [PATCH] m68k: con...
230
231
232
  EXPORT_SYMBOL(request_irq);
  
  void free_irq(unsigned int irq, void *dev_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  {
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
234
235
236
  	struct irq_controller *contr;
  	struct irq_node **p, *node;
  	unsigned long flags;
68387c448   Roman Zippel   [PATCH] m68k: con...
237
  	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
  		printk("%s: Incorrect IRQ %d
  ", __FUNCTION__, irq);
  		return;
  	}
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  	spin_lock_irqsave(&contr->lock, flags);
  
  	p = irq_list + irq;
  	while ((node = *p)) {
  		if (node->dev_id == dev_id)
  			break;
  		p = &node->next;
  	}
  
  	if (node) {
  		*p = node->next;
  		node->handler = NULL;
  	} else
  		printk("%s: Removing probably wrong IRQ %d
  ",
  		       __FUNCTION__, irq);
68387c448   Roman Zippel   [PATCH] m68k: con...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  	if (!irq_list[irq]) {
  		if (contr->shutdown)
  			contr->shutdown(irq);
  		else
  			contr->disable(irq);
  	}
  
  	spin_unlock_irqrestore(&contr->lock, flags);
  }
  
  EXPORT_SYMBOL(free_irq);
  
  void enable_irq(unsigned int irq)
  {
  	struct irq_controller *contr;
  	unsigned long flags;
  
  	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
  		printk("%s: Incorrect IRQ %d
  ",
  		       __FUNCTION__, irq);
  		return;
  	}
  
  	spin_lock_irqsave(&contr->lock, flags);
  	if (irq_depth[irq]) {
  		if (!--irq_depth[irq]) {
  			if (contr->enable)
  				contr->enable(irq);
  		}
  	} else
  		WARN_ON(1);
  	spin_unlock_irqrestore(&contr->lock, flags);
  }
  
  EXPORT_SYMBOL(enable_irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294

68387c448   Roman Zippel   [PATCH] m68k: con...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  void disable_irq(unsigned int irq)
  {
  	struct irq_controller *contr;
  	unsigned long flags;
  
  	if (irq >= NR_IRQS || !(contr = irq_controller[irq])) {
  		printk("%s: Incorrect IRQ %d
  ",
  		       __FUNCTION__, irq);
  		return;
  	}
  
  	spin_lock_irqsave(&contr->lock, flags);
  	if (!irq_depth[irq]++) {
  		if (contr->disable)
  			contr->disable(irq);
  	}
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
312
  	spin_unlock_irqrestore(&contr->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  }
68387c448   Roman Zippel   [PATCH] m68k: con...
314
  EXPORT_SYMBOL(disable_irq);
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
315
316
317
318
  int m68k_irq_startup(unsigned int irq)
  {
  	if (irq <= IRQ_AUTO_7)
  		vectors[VEC_SPUR + irq] = auto_inthandler;
68387c448   Roman Zippel   [PATCH] m68k: con...
319
320
  	else
  		vectors[m68k_first_user_vec + irq - IRQ_USER] = user_inthandler;
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
321
322
323
324
325
326
327
  	return 0;
  }
  
  void m68k_irq_shutdown(unsigned int irq)
  {
  	if (irq <= IRQ_AUTO_7)
  		vectors[VEC_SPUR + irq] = bad_inthandler;
68387c448   Roman Zippel   [PATCH] m68k: con...
328
329
  	else
  		vectors[m68k_first_user_vec + irq - IRQ_USER] = bad_inthandler;
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
330
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  /*
   * Do we need these probe functions on the m68k?
   *
   *  ... may be useful with ISA devices
   */
  unsigned long probe_irq_on (void)
  {
  #ifdef CONFIG_Q40
  	if (MACH_IS_Q40)
  		return q40_probe_irq_on();
  #endif
  	return 0;
  }
  
  EXPORT_SYMBOL(probe_irq_on);
  
  int probe_irq_off (unsigned long irqs)
  {
  #ifdef CONFIG_Q40
  	if (MACH_IS_Q40)
  		return q40_probe_irq_off(irqs);
  #endif
  	return 0;
  }
  
  EXPORT_SYMBOL(probe_irq_off);
68387c448   Roman Zippel   [PATCH] m68k: con...
357
  unsigned int irq_canonicalize(unsigned int irq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  {
68387c448   Roman Zippel   [PATCH] m68k: con...
359
360
361
362
363
  #ifdef CONFIG_Q40
  	if (MACH_IS_Q40 && irq == 11)
  		irq = 10;
  #endif
  	return irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  }
68387c448   Roman Zippel   [PATCH] m68k: con...
365
  EXPORT_SYMBOL(irq_canonicalize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366

2850bc273   Al Viro   [PATCH] m68k pt_r...
367
  asmlinkage void m68k_handle_int(unsigned int irq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  {
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
369
  	struct irq_node *node;
92445eaaa   Roman Zippel   [PATCH] m68k: sep...
370
  	kstat_cpu(0).irqs[irq]++;
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
371
372
  	node = irq_list[irq];
  	do {
2850bc273   Al Viro   [PATCH] m68k pt_r...
373
  		node->handler(irq, node->dev_id);
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
374
375
  		node = node->next;
  	} while (node);
92445eaaa   Roman Zippel   [PATCH] m68k: sep...
376
  }
2850bc273   Al Viro   [PATCH] m68k pt_r...
377
378
379
380
381
382
383
  asmlinkage void __m68k_handle_int(unsigned int irq, struct pt_regs *regs)
  {
  	struct pt_regs *old_regs;
  	old_regs = set_irq_regs(regs);
  	m68k_handle_int(irq);
  	set_irq_regs(old_regs);
  }
92445eaaa   Roman Zippel   [PATCH] m68k: sep...
384
385
386
387
388
  asmlinkage void handle_badint(struct pt_regs *regs)
  {
  	kstat_cpu(0).irqs[0]++;
  	printk("unexpected interrupt from %u
  ", regs->vector);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
  }
  
  int show_interrupts(struct seq_file *p, void *v)
  {
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
393
394
  	struct irq_controller *contr;
  	struct irq_node *node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
397
  	int i = *(loff_t *) v;
  
  	/* autovector interrupts */
68387c448   Roman Zippel   [PATCH] m68k: con...
398
  	if (irq_list[i]) {
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
399
400
  		contr = irq_controller[i];
  		node = irq_list[i];
68387c448   Roman Zippel   [PATCH] m68k: con...
401
  		seq_printf(p, "%-8s %3u: %10u %s", contr->name, i, kstat_cpu(0).irqs[i], node->devname);
b5dc7840b   Roman Zippel   [PATCH] m68k: int...
402
403
404
405
  		while ((node = node->next))
  			seq_printf(p, ", %s", node->devname);
  		seq_puts(p, "
  ");
68387c448   Roman Zippel   [PATCH] m68k: con...
406
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
  	return 0;
  }
  
  void init_irq_proc(void)
  {
  	/* Insert /proc/irq driver here */
  }