1 | /* |
2 | * arch/m68k/q40/q40ints.c |
3 | * |
4 | * Copyright (C) 1999,2001 Richard Zidlicky |
5 | * |
6 | * This file is subject to the terms and conditions of the GNU General Public |
7 | * License. See the file COPYING in the main directory of this archive |
8 | * for more details. |
9 | * |
10 | * .. used to be loosely based on bvme6000ints.c |
11 | * |
12 | */ |
13 | |
14 | #include <linux/types.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/errno.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/irq.h> |
19 | |
20 | #include <asm/machdep.h> |
21 | #include <asm/ptrace.h> |
22 | #include <asm/traps.h> |
23 | |
24 | #include <asm/q40_master.h> |
25 | #include <asm/q40ints.h> |
26 | |
27 | #include "q40.h" |
28 | |
29 | /* |
30 | * Q40 IRQs are defined as follows: |
31 | * 3,4,5,6,7,10,11,14,15 : ISA dev IRQs |
32 | * 16-31: reserved |
33 | * 32 : keyboard int |
34 | * 33 : frame int (50/200 Hz periodic timer) |
35 | * 34 : sample int (10/20 KHz periodic timer) |
36 | * |
37 | */ |
38 | |
39 | static void q40_irq_handler(unsigned int, struct pt_regs *fp); |
40 | static void q40_irq_enable(struct irq_data *data); |
41 | static void q40_irq_disable(struct irq_data *data); |
42 | |
43 | unsigned short q40_ablecount[35]; |
44 | unsigned short q40_state[35]; |
45 | |
46 | static unsigned int q40_irq_startup(struct irq_data *data) |
47 | { |
48 | unsigned int irq = data->irq; |
49 | |
50 | /* test for ISA ints not implemented by HW */ |
51 | switch (irq) { |
52 | case 1: case 2: case 8: case 9: |
53 | case 11: case 12: case 13: |
54 | pr_warn("%s: ISA IRQ %d not implemented by HW\n" , __func__, |
55 | irq); |
56 | /* FIXME return -ENXIO; */ |
57 | } |
58 | return 0; |
59 | } |
60 | |
61 | static void q40_irq_shutdown(struct irq_data *data) |
62 | { |
63 | } |
64 | |
65 | static struct irq_chip q40_irq_chip = { |
66 | .name = "q40" , |
67 | .irq_startup = q40_irq_startup, |
68 | .irq_shutdown = q40_irq_shutdown, |
69 | .irq_enable = q40_irq_enable, |
70 | .irq_disable = q40_irq_disable, |
71 | }; |
72 | |
73 | /* |
74 | * void q40_init_IRQ (void) |
75 | * |
76 | * Parameters: None |
77 | * |
78 | * Returns: Nothing |
79 | * |
80 | * This function is called during kernel startup to initialize |
81 | * the q40 IRQ handling routines. |
82 | */ |
83 | |
84 | static int disabled; |
85 | |
86 | void __init q40_init_IRQ(void) |
87 | { |
88 | m68k_setup_irq_controller(&q40_irq_chip, handle_simple_irq, 1, |
89 | Q40_IRQ_MAX); |
90 | |
91 | /* setup handler for ISA ints */ |
92 | m68k_setup_auto_interrupt(q40_irq_handler); |
93 | |
94 | m68k_irq_startup_irq(IRQ_AUTO_2); |
95 | m68k_irq_startup_irq(IRQ_AUTO_4); |
96 | |
97 | /* now enable some ints.. */ |
98 | master_outb(1, EXT_ENABLE_REG); /* ISA IRQ 5-15 */ |
99 | |
100 | /* make sure keyboard IRQ is disabled */ |
101 | master_outb(0, KEY_IRQ_ENABLE_REG); |
102 | } |
103 | |
104 | |
105 | /* |
106 | * this stuff doesn't really belong here.. |
107 | */ |
108 | |
109 | int ql_ticks; /* 200Hz ticks since last jiffie */ |
110 | static int sound_ticks; |
111 | |
112 | #define SVOL 45 |
113 | |
114 | void q40_mksound(unsigned int hz, unsigned int ticks) |
115 | { |
116 | /* for now ignore hz, except that hz==0 switches off sound */ |
117 | /* simply alternate the ampl (128-SVOL)-(128+SVOL)-..-.. at 200Hz */ |
118 | if (hz == 0) { |
119 | if (sound_ticks) |
120 | sound_ticks = 1; |
121 | |
122 | *DAC_LEFT = 128; |
123 | *DAC_RIGHT = 128; |
124 | |
125 | return; |
126 | } |
127 | /* sound itself is done in q40_timer_int */ |
128 | if (sound_ticks == 0) |
129 | sound_ticks = 1000; /* pretty long beep */ |
130 | sound_ticks = ticks << 1; |
131 | } |
132 | |
133 | static irqreturn_t q40_timer_int(int irq, void *dev_id) |
134 | { |
135 | ql_ticks = ql_ticks ? 0 : 1; |
136 | if (sound_ticks) { |
137 | unsigned char sval=(sound_ticks & 1) ? 128-SVOL : 128+SVOL; |
138 | sound_ticks--; |
139 | *DAC_LEFT=sval; |
140 | *DAC_RIGHT=sval; |
141 | } |
142 | |
143 | if (!ql_ticks) { |
144 | unsigned long flags; |
145 | |
146 | local_irq_save(flags); |
147 | legacy_timer_tick(ticks: 1); |
148 | timer_heartbeat(); |
149 | local_irq_restore(flags); |
150 | } |
151 | return IRQ_HANDLED; |
152 | } |
153 | |
154 | void q40_sched_init (void) |
155 | { |
156 | int timer_irq; |
157 | |
158 | timer_irq = Q40_IRQ_FRAME; |
159 | |
160 | if (request_irq(irq: timer_irq, handler: q40_timer_int, flags: 0, name: "timer" , NULL)) |
161 | panic(fmt: "Couldn't register timer int" ); |
162 | |
163 | master_outb(-1, FRAME_CLEAR_REG); |
164 | master_outb( 1, FRAME_RATE_REG); |
165 | } |
166 | |
167 | |
168 | /* |
169 | * tables to translate bits into IRQ numbers |
170 | * it is a good idea to order the entries by priority |
171 | * |
172 | */ |
173 | |
174 | struct IRQ_TABLE{ unsigned mask; int irq ;}; |
175 | #if 0 |
176 | static struct IRQ_TABLE iirqs[]={ |
177 | {Q40_IRQ_FRAME_MASK,Q40_IRQ_FRAME}, |
178 | {Q40_IRQ_KEYB_MASK,Q40_IRQ_KEYBOARD}, |
179 | {0,0}}; |
180 | #endif |
181 | static struct IRQ_TABLE eirqs[] = { |
182 | { .mask = Q40_IRQ3_MASK, .irq = 3 }, /* ser 1 */ |
183 | { .mask = Q40_IRQ4_MASK, .irq = 4 }, /* ser 2 */ |
184 | { .mask = Q40_IRQ14_MASK, .irq = 14 }, /* IDE 1 */ |
185 | { .mask = Q40_IRQ15_MASK, .irq = 15 }, /* IDE 2 */ |
186 | { .mask = Q40_IRQ6_MASK, .irq = 6 }, /* floppy, handled elsewhere */ |
187 | { .mask = Q40_IRQ7_MASK, .irq = 7 }, /* par */ |
188 | { .mask = Q40_IRQ5_MASK, .irq = 5 }, |
189 | { .mask = Q40_IRQ10_MASK, .irq = 10 }, |
190 | {0,0} |
191 | }; |
192 | |
193 | /* complain only this many times about spurious ints : */ |
194 | static int ccleirq=60; /* ISA dev IRQs*/ |
195 | /*static int cclirq=60;*/ /* internal */ |
196 | |
197 | /* FIXME: add shared ints,mask,unmask,probing.... */ |
198 | |
199 | #define IRQ_INPROGRESS 1 |
200 | /*static unsigned short saved_mask;*/ |
201 | //static int do_tint=0; |
202 | |
203 | #define DEBUG_Q40INT |
204 | /*#define IP_USE_DISABLE *//* would be nice, but crashes ???? */ |
205 | |
206 | static int mext_disabled; /* ext irq disabled by master chip? */ |
207 | static int aliased_irq; /* how many times inside handler ?*/ |
208 | |
209 | |
210 | /* got interrupt, dispatch to ISA or keyboard/timer IRQs */ |
211 | static void q40_irq_handler(unsigned int irq, struct pt_regs *fp) |
212 | { |
213 | unsigned mir, mer; |
214 | int i; |
215 | |
216 | //repeat: |
217 | mir = master_inb(IIRQ_REG); |
218 | #ifdef CONFIG_BLK_DEV_FD |
219 | if ((mir & Q40_IRQ_EXT_MASK) && |
220 | (master_inb(EIRQ_REG) & Q40_IRQ6_MASK)) { |
221 | floppy_hardint(); |
222 | return; |
223 | } |
224 | #endif |
225 | switch (irq) { |
226 | case 4: |
227 | case 6: |
228 | do_IRQ(Q40_IRQ_SAMPLE, fp); |
229 | return; |
230 | } |
231 | if (mir & Q40_IRQ_FRAME_MASK) { |
232 | do_IRQ(Q40_IRQ_FRAME, fp); |
233 | master_outb(-1, FRAME_CLEAR_REG); |
234 | } |
235 | if ((mir & Q40_IRQ_SER_MASK) || (mir & Q40_IRQ_EXT_MASK)) { |
236 | mer = master_inb(EIRQ_REG); |
237 | for (i = 0; eirqs[i].mask; i++) { |
238 | if (mer & eirqs[i].mask) { |
239 | irq = eirqs[i].irq; |
240 | /* |
241 | * There is a little mess wrt which IRQ really caused this irq request. The |
242 | * main problem is that IIRQ_REG and EIRQ_REG reflect the state when they |
243 | * are read - which is long after the request came in. In theory IRQs should |
244 | * not just go away but they occasionally do |
245 | */ |
246 | if (irq > 4 && irq <= 15 && mext_disabled) { |
247 | /*aliased_irq++;*/ |
248 | goto iirq; |
249 | } |
250 | if (q40_state[irq] & IRQ_INPROGRESS) { |
251 | /* some handlers do local_irq_enable() for irq latency reasons, */ |
252 | /* however reentering an active irq handler is not permitted */ |
253 | #ifdef IP_USE_DISABLE |
254 | /* in theory this is the better way to do it because it still */ |
255 | /* lets through eg the serial irqs, unfortunately it crashes */ |
256 | disable_irq(irq); |
257 | disabled = 1; |
258 | #else |
259 | /*pr_warn("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n", |
260 | irq, disabled ? "already" : "not yet"); */ |
261 | fp->sr = (((fp->sr) & (~0x700))+0x200); |
262 | disabled = 1; |
263 | #endif |
264 | goto iirq; |
265 | } |
266 | q40_state[irq] |= IRQ_INPROGRESS; |
267 | do_IRQ(irq, fp); |
268 | q40_state[irq] &= ~IRQ_INPROGRESS; |
269 | |
270 | /* naively enable everything, if that fails than */ |
271 | /* this function will be reentered immediately thus */ |
272 | /* getting another chance to disable the IRQ */ |
273 | |
274 | if (disabled) { |
275 | #ifdef IP_USE_DISABLE |
276 | if (irq > 4) { |
277 | disabled = 0; |
278 | enable_irq(irq); |
279 | } |
280 | #else |
281 | disabled = 0; |
282 | /*pr_info("reenabling irq %d\n", irq); */ |
283 | #endif |
284 | } |
285 | // used to do 'goto repeat;' here, this delayed bh processing too long |
286 | return; |
287 | } |
288 | } |
289 | if (mer && ccleirq > 0 && !aliased_irq) { |
290 | pr_warn("ISA interrupt from unknown source? EIRQ_REG = %x\n" , |
291 | mer); |
292 | ccleirq--; |
293 | } |
294 | } |
295 | iirq: |
296 | mir = master_inb(IIRQ_REG); |
297 | /* should test whether keyboard irq is really enabled, doing it in defhand */ |
298 | if (mir & Q40_IRQ_KEYB_MASK) |
299 | do_IRQ(Q40_IRQ_KEYBOARD, fp); |
300 | |
301 | return; |
302 | } |
303 | |
304 | void q40_irq_enable(struct irq_data *data) |
305 | { |
306 | unsigned int irq = data->irq; |
307 | |
308 | if (irq >= 5 && irq <= 15) { |
309 | mext_disabled--; |
310 | if (mext_disabled > 0) |
311 | pr_warn("q40_irq_enable : nested disable/enable\n" ); |
312 | if (mext_disabled == 0) |
313 | master_outb(1, EXT_ENABLE_REG); |
314 | } |
315 | } |
316 | |
317 | |
318 | void q40_irq_disable(struct irq_data *data) |
319 | { |
320 | unsigned int irq = data->irq; |
321 | |
322 | /* disable ISA iqs : only do something if the driver has been |
323 | * verified to be Q40 "compatible" - right now IDE, NE2K |
324 | * Any driver should not attempt to sleep across disable_irq !! |
325 | */ |
326 | |
327 | if (irq >= 5 && irq <= 15) { |
328 | master_outb(0, EXT_ENABLE_REG); |
329 | mext_disabled++; |
330 | if (mext_disabled > 1) |
331 | pr_info("disable_irq nesting count %d\n" , |
332 | mext_disabled); |
333 | } |
334 | } |
335 | |