1 | /* |
2 | * arch/m68k/atari/ataints.c -- Atari Linux interrupt handling code |
3 | * |
4 | * 5/2/94 Roman Hodek: |
5 | * Added support for TT interrupts; setup for TT SCU (may someone has |
6 | * twiddled there and we won't get the right interrupts :-() |
7 | * |
8 | * Major change: The device-independent code in m68k/ints.c didn't know |
9 | * about non-autovec ints yet. It hardcoded the number of possible ints to |
10 | * 7 (IRQ1...IRQ7). But the Atari has lots of non-autovec ints! I made the |
11 | * number of possible ints a constant defined in interrupt.h, which is |
12 | * 47 for the Atari. So we can call request_irq() for all Atari interrupts |
13 | * just the normal way. Additionally, all vectors >= 48 are initialized to |
14 | * call trap() instead of inthandler(). This must be changed here, too. |
15 | * |
16 | * 1995-07-16 Lars Brinkhoff <f93labr@dd.chalmers.se>: |
17 | * Corrected a bug in atari_add_isr() which rejected all SCC |
18 | * interrupt sources if there were no TT MFP! |
19 | * |
20 | * 12/13/95: New interface functions atari_level_triggered_int() and |
21 | * atari_register_vme_int() as support for level triggered VME interrupts. |
22 | * |
23 | * 02/12/96: (Roman) |
24 | * Total rewrite of Atari interrupt handling, for new scheme see comments |
25 | * below. |
26 | * |
27 | * 1996-09-03 lars brinkhoff <f93labr@dd.chalmers.se>: |
28 | * Added new function atari_unregister_vme_int(), and |
29 | * modified atari_register_vme_int() as well as IS_VALID_INTNO() |
30 | * to work with it. |
31 | * |
32 | * This file is subject to the terms and conditions of the GNU General Public |
33 | * License. See the file COPYING in the main directory of this archive |
34 | * for more details. |
35 | * |
36 | */ |
37 | |
38 | #include <linux/types.h> |
39 | #include <linux/kernel.h> |
40 | #include <linux/kernel_stat.h> |
41 | #include <linux/init.h> |
42 | #include <linux/seq_file.h> |
43 | #include <linux/module.h> |
44 | #include <linux/irq.h> |
45 | |
46 | #include <asm/traps.h> |
47 | |
48 | #include <asm/atarihw.h> |
49 | #include <asm/atariints.h> |
50 | #include <asm/atari_stdma.h> |
51 | #include <asm/irq.h> |
52 | #include <asm/entry.h> |
53 | #include <asm/io.h> |
54 | |
55 | #include "atari.h" |
56 | |
57 | /* |
58 | * Atari interrupt handling scheme: |
59 | * -------------------------------- |
60 | * |
61 | * All interrupt source have an internal number (defined in |
62 | * <asm/atariints.h>): Autovector interrupts are 1..7, then follow ST-MFP, |
63 | * TT-MFP, SCC, and finally VME interrupts. Vector numbers for the latter can |
64 | * be allocated by atari_register_vme_int(). |
65 | */ |
66 | |
67 | /* |
68 | * Bitmap for free interrupt vector numbers |
69 | * (new vectors starting from 0x70 can be allocated by |
70 | * atari_register_vme_int()) |
71 | */ |
72 | static int free_vme_vec_bitmap; |
73 | |
74 | /* GK: |
75 | * HBL IRQ handler for Falcon. Nobody needs it :-) |
76 | * ++andreas: raise ipl to disable further HBLANK interrupts. |
77 | */ |
78 | asmlinkage void falcon_hblhandler(void); |
79 | asm(".text\n" |
80 | __ALIGN_STR "\n\t" |
81 | "falcon_hblhandler:\n\t" |
82 | "orw #0x200,%sp@\n\t" /* set saved ipl to 2 */ |
83 | "rte" ); |
84 | |
85 | static unsigned int atari_irq_startup(struct irq_data *data) |
86 | { |
87 | unsigned int irq = data->irq; |
88 | |
89 | m68k_irq_startup(data); |
90 | atari_turnon_irq(irq); |
91 | atari_enable_irq(irq); |
92 | return 0; |
93 | } |
94 | |
95 | static void atari_irq_shutdown(struct irq_data *data) |
96 | { |
97 | unsigned int irq = data->irq; |
98 | |
99 | atari_disable_irq(irq); |
100 | atari_turnoff_irq(irq); |
101 | m68k_irq_shutdown(data); |
102 | |
103 | if (irq == IRQ_AUTO_4) |
104 | vectors[VEC_INT4] = falcon_hblhandler; |
105 | } |
106 | |
107 | static void atari_irq_enable(struct irq_data *data) |
108 | { |
109 | atari_enable_irq(data->irq); |
110 | } |
111 | |
112 | static void atari_irq_disable(struct irq_data *data) |
113 | { |
114 | atari_disable_irq(data->irq); |
115 | } |
116 | |
117 | static struct irq_chip atari_irq_chip = { |
118 | .name = "atari" , |
119 | .irq_startup = atari_irq_startup, |
120 | .irq_shutdown = atari_irq_shutdown, |
121 | .irq_enable = atari_irq_enable, |
122 | .irq_disable = atari_irq_disable, |
123 | }; |
124 | |
125 | /* |
126 | * ST-MFP timer D chained interrupts - each driver gets its own timer |
127 | * interrupt instance. |
128 | */ |
129 | |
130 | struct mfptimerbase { |
131 | volatile struct MFP *mfp; |
132 | unsigned char mfp_mask, mfp_data; |
133 | unsigned short int_mask; |
134 | int handler_irq, mfptimer_irq, server_irq; |
135 | char *name; |
136 | } stmfp_base = { |
137 | .mfp = &st_mfp, |
138 | .int_mask = 0x0, |
139 | .handler_irq = IRQ_MFP_TIMD, |
140 | .mfptimer_irq = IRQ_MFP_TIMER1, |
141 | .name = "MFP Timer D" |
142 | }; |
143 | |
144 | static irqreturn_t mfp_timer_d_handler(int irq, void *dev_id) |
145 | { |
146 | struct mfptimerbase *base = dev_id; |
147 | int mach_irq; |
148 | unsigned char ints; |
149 | |
150 | mach_irq = base->mfptimer_irq; |
151 | ints = base->int_mask; |
152 | for (; ints; mach_irq++, ints >>= 1) { |
153 | if (ints & 1) |
154 | generic_handle_irq(irq: mach_irq); |
155 | } |
156 | return IRQ_HANDLED; |
157 | } |
158 | |
159 | |
160 | static void atari_mfptimer_enable(struct irq_data *data) |
161 | { |
162 | int mfp_num = data->irq - IRQ_MFP_TIMER1; |
163 | stmfp_base.int_mask |= 1 << mfp_num; |
164 | atari_enable_irq(IRQ_MFP_TIMD); |
165 | } |
166 | |
167 | static void atari_mfptimer_disable(struct irq_data *data) |
168 | { |
169 | int mfp_num = data->irq - IRQ_MFP_TIMER1; |
170 | stmfp_base.int_mask &= ~(1 << mfp_num); |
171 | if (!stmfp_base.int_mask) |
172 | atari_disable_irq(IRQ_MFP_TIMD); |
173 | } |
174 | |
175 | static struct irq_chip atari_mfptimer_chip = { |
176 | .name = "timer_d" , |
177 | .irq_enable = atari_mfptimer_enable, |
178 | .irq_disable = atari_mfptimer_disable, |
179 | }; |
180 | |
181 | |
182 | /* |
183 | * EtherNAT CPLD interrupt handling |
184 | * CPLD interrupt register is at phys. 0x80000023 |
185 | * Need this mapped in at interrupt startup time |
186 | * Possibly need this mapped on demand anyway - |
187 | * EtherNAT USB driver needs to disable IRQ before |
188 | * startup! |
189 | */ |
190 | |
191 | static unsigned char *enat_cpld; |
192 | |
193 | static unsigned int atari_ethernat_startup(struct irq_data *data) |
194 | { |
195 | int enat_num = 140 - data->irq + 1; |
196 | |
197 | m68k_irq_startup(data); |
198 | /* |
199 | * map CPLD interrupt register |
200 | */ |
201 | if (!enat_cpld) |
202 | enat_cpld = (unsigned char *)ioremap((ATARI_ETHERNAT_PHYS_ADDR+0x23), 0x2); |
203 | /* |
204 | * do _not_ enable the USB chip interrupt here - causes interrupt storm |
205 | * and triggers dead interrupt watchdog |
206 | * Need to reset the USB chip to a sane state in early startup before |
207 | * removing this hack |
208 | */ |
209 | if (enat_num == 1) |
210 | *enat_cpld |= 1 << enat_num; |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static void atari_ethernat_enable(struct irq_data *data) |
216 | { |
217 | int enat_num = 140 - data->irq + 1; |
218 | /* |
219 | * map CPLD interrupt register |
220 | */ |
221 | if (!enat_cpld) |
222 | enat_cpld = (unsigned char *)ioremap((ATARI_ETHERNAT_PHYS_ADDR+0x23), 0x2); |
223 | *enat_cpld |= 1 << enat_num; |
224 | } |
225 | |
226 | static void atari_ethernat_disable(struct irq_data *data) |
227 | { |
228 | int enat_num = 140 - data->irq + 1; |
229 | /* |
230 | * map CPLD interrupt register |
231 | */ |
232 | if (!enat_cpld) |
233 | enat_cpld = (unsigned char *)ioremap((ATARI_ETHERNAT_PHYS_ADDR+0x23), 0x2); |
234 | *enat_cpld &= ~(1 << enat_num); |
235 | } |
236 | |
237 | static void atari_ethernat_shutdown(struct irq_data *data) |
238 | { |
239 | int enat_num = 140 - data->irq + 1; |
240 | if (enat_cpld) { |
241 | *enat_cpld &= ~(1 << enat_num); |
242 | iounmap(addr: enat_cpld); |
243 | enat_cpld = NULL; |
244 | } |
245 | } |
246 | |
247 | static struct irq_chip atari_ethernat_chip = { |
248 | .name = "ethernat" , |
249 | .irq_startup = atari_ethernat_startup, |
250 | .irq_shutdown = atari_ethernat_shutdown, |
251 | .irq_enable = atari_ethernat_enable, |
252 | .irq_disable = atari_ethernat_disable, |
253 | }; |
254 | |
255 | /* |
256 | * void atari_init_IRQ (void) |
257 | * |
258 | * Parameters: None |
259 | * |
260 | * Returns: Nothing |
261 | * |
262 | * This function should be called during kernel startup to initialize |
263 | * the atari IRQ handling routines. |
264 | */ |
265 | |
266 | void __init atari_init_IRQ(void) |
267 | { |
268 | m68k_setup_user_interrupt(VEC_USER, NUM_ATARI_SOURCES - IRQ_USER); |
269 | m68k_setup_irq_controller(&atari_irq_chip, handle_simple_irq, 1, |
270 | NUM_ATARI_SOURCES - 1); |
271 | |
272 | /* Initialize the MFP(s) */ |
273 | |
274 | #ifdef ATARI_USE_SOFTWARE_EOI |
275 | st_mfp.vec_adr = 0x48; /* Software EOI-Mode */ |
276 | #else |
277 | st_mfp.vec_adr = 0x40; /* Automatic EOI-Mode */ |
278 | #endif |
279 | st_mfp.int_en_a = 0x00; /* turn off MFP-Ints */ |
280 | st_mfp.int_en_b = 0x00; |
281 | st_mfp.int_mk_a = 0xff; /* no Masking */ |
282 | st_mfp.int_mk_b = 0xff; |
283 | |
284 | if (ATARIHW_PRESENT(TT_MFP)) { |
285 | #ifdef ATARI_USE_SOFTWARE_EOI |
286 | tt_mfp.vec_adr = 0x58; /* Software EOI-Mode */ |
287 | #else |
288 | tt_mfp.vec_adr = 0x50; /* Automatic EOI-Mode */ |
289 | #endif |
290 | tt_mfp.int_en_a = 0x00; /* turn off MFP-Ints */ |
291 | tt_mfp.int_en_b = 0x00; |
292 | tt_mfp.int_mk_a = 0xff; /* no Masking */ |
293 | tt_mfp.int_mk_b = 0xff; |
294 | } |
295 | |
296 | if (ATARIHW_PRESENT(SCC) && !atari_SCC_reset_done) { |
297 | atari_scc.cha_a_ctrl = 9; |
298 | MFPDELAY(); |
299 | atari_scc.cha_a_ctrl = (char) 0xc0; /* hardware reset */ |
300 | } |
301 | |
302 | if (ATARIHW_PRESENT(SCU)) { |
303 | /* init the SCU if present */ |
304 | tt_scu.sys_mask = 0x10; /* enable VBL (for the cursor) and |
305 | * disable HSYNC interrupts (who |
306 | * needs them?) MFP and SCC are |
307 | * enabled in VME mask |
308 | */ |
309 | tt_scu.vme_mask = 0x60; /* enable MFP and SCC ints */ |
310 | } else { |
311 | /* If no SCU and no Hades, the HSYNC interrupt needs to be |
312 | * disabled this way. (Else _inthandler in kernel/sys_call.S |
313 | * gets overruns) |
314 | */ |
315 | |
316 | vectors[VEC_INT2] = falcon_hblhandler; |
317 | vectors[VEC_INT4] = falcon_hblhandler; |
318 | } |
319 | |
320 | if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) { |
321 | /* Initialize the LM1992 Sound Controller to enable |
322 | the PSG sound. This is misplaced here, it should |
323 | be in an atasound_init(), that doesn't exist yet. */ |
324 | atari_microwire_cmd(MW_LM1992_PSG_HIGH); |
325 | } |
326 | |
327 | stdma_init(); |
328 | |
329 | /* Initialize the PSG: all sounds off, both ports output */ |
330 | sound_ym.rd_data_reg_sel = 7; |
331 | sound_ym.wd_data = 0xff; |
332 | |
333 | m68k_setup_irq_controller(&atari_mfptimer_chip, handle_simple_irq, |
334 | IRQ_MFP_TIMER1, 8); |
335 | |
336 | irq_set_status_flags(IRQ_MFP_TIMER1, IRQ_IS_POLLED); |
337 | irq_set_status_flags(IRQ_MFP_TIMER2, IRQ_IS_POLLED); |
338 | |
339 | /* prepare timer D data for use as poll interrupt */ |
340 | /* set Timer D data Register - needs to be > 0 */ |
341 | st_mfp.tim_dt_d = 254; /* < 100 Hz */ |
342 | /* start timer D, div = 1:100 */ |
343 | st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 0xf0) | 0x6; |
344 | |
345 | /* request timer D dispatch handler */ |
346 | if (request_irq(IRQ_MFP_TIMD, mfp_timer_d_handler, IRQF_SHARED, |
347 | stmfp_base.name, &stmfp_base)) |
348 | pr_err("Couldn't register %s interrupt\n" , stmfp_base.name); |
349 | |
350 | /* |
351 | * EtherNAT ethernet / USB interrupt handlers |
352 | */ |
353 | |
354 | m68k_setup_irq_controller(&atari_ethernat_chip, handle_simple_irq, |
355 | 139, 2); |
356 | } |
357 | |
358 | |
359 | /* |
360 | * atari_register_vme_int() returns the number of a free interrupt vector for |
361 | * hardware with a programmable int vector (probably a VME board). |
362 | */ |
363 | |
364 | unsigned int atari_register_vme_int(void) |
365 | { |
366 | int i; |
367 | |
368 | for (i = 0; i < 32; i++) |
369 | if ((free_vme_vec_bitmap & (1 << i)) == 0) |
370 | break; |
371 | |
372 | if (i == 16) |
373 | return 0; |
374 | |
375 | free_vme_vec_bitmap |= 1 << i; |
376 | return VME_SOURCE_BASE + i; |
377 | } |
378 | EXPORT_SYMBOL(atari_register_vme_int); |
379 | |
380 | |
381 | void atari_unregister_vme_int(unsigned int irq) |
382 | { |
383 | if (irq >= VME_SOURCE_BASE && irq < VME_SOURCE_BASE + VME_MAX_SOURCES) { |
384 | irq -= VME_SOURCE_BASE; |
385 | free_vme_vec_bitmap &= ~(1 << irq); |
386 | } |
387 | } |
388 | EXPORT_SYMBOL(atari_unregister_vme_int); |
389 | |
390 | |
391 | |