diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/amiga/amiints.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc/amiga/amiints.c')
-rw-r--r-- | arch/ppc/amiga/amiints.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c new file mode 100644 index 000000000000..91195e2ce38d --- /dev/null +++ b/arch/ppc/amiga/amiints.c | |||
@@ -0,0 +1,323 @@ | |||
1 | /* | ||
2 | * arch/ppc/amiga/amiints.c -- Amiga Linux 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 | * 11/07/96: rewritten interrupt handling, irq lists are exists now only for | ||
9 | * this sources where it makes sense (VERTB/PORTS/EXTER) and you must | ||
10 | * be careful that dev_id for this sources is unique since this the | ||
11 | * only possibility to distinguish between different handlers for | ||
12 | * free_irq. irq lists also have different irq flags: | ||
13 | * - IRQ_FLG_FAST: handler is inserted at top of list (after other | ||
14 | * fast handlers) | ||
15 | * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before | ||
16 | * they're executed irq level is set to the previous | ||
17 | * one, but handlers don't need to be reentrant, if | ||
18 | * reentrance occurred, slow handlers will be just | ||
19 | * called again. | ||
20 | * The whole interrupt handling for CIAs is moved to cia.c | ||
21 | * /Roman Zippel | ||
22 | * | ||
23 | * 07/08/99: rewamp of the interrupt handling - we now have two types of | ||
24 | * interrupts, normal and fast handlers, fast handlers being | ||
25 | * marked with SA_INTERRUPT and runs with all other interrupts | ||
26 | * disabled. Normal interrupts disable their own source but | ||
27 | * run with all other interrupt sources enabled. | ||
28 | * PORTS and EXTER interrupts are always shared even if the | ||
29 | * drivers do not explicitly mark this when calling | ||
30 | * request_irq which they really should do. | ||
31 | * This is similar to the way interrupts are handled on all | ||
32 | * other architectures and makes a ton of sense besides | ||
33 | * having the advantage of making it easier to share | ||
34 | * drivers. | ||
35 | * /Jes | ||
36 | */ | ||
37 | |||
38 | #include <linux/config.h> | ||
39 | #include <linux/types.h> | ||
40 | #include <linux/kernel.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/interrupt.h> | ||
43 | #include <linux/irq.h> | ||
44 | #include <linux/kernel_stat.h> | ||
45 | #include <linux/init.h> | ||
46 | |||
47 | #include <asm/system.h> | ||
48 | #include <asm/irq.h> | ||
49 | #include <asm/traps.h> | ||
50 | #include <asm/amigahw.h> | ||
51 | #include <asm/amigaints.h> | ||
52 | #include <asm/amipcmcia.h> | ||
53 | |||
54 | #ifdef CONFIG_APUS | ||
55 | #include <asm/amigappc.h> | ||
56 | #endif | ||
57 | |||
58 | extern void cia_init_IRQ(struct ciabase *base); | ||
59 | |||
60 | unsigned short ami_intena_vals[AMI_STD_IRQS] = { | ||
61 | IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT, | ||
62 | IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_SOFT, IF_PORTS, IF_EXTER | ||
63 | }; | ||
64 | static const unsigned char ami_servers[AMI_STD_IRQS] = { | ||
65 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 | ||
66 | }; | ||
67 | |||
68 | static short ami_ablecount[AMI_IRQS]; | ||
69 | |||
70 | static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) | ||
71 | { | ||
72 | /* num_spurious += 1;*/ | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * void amiga_init_IRQ(void) | ||
77 | * | ||
78 | * Parameters: None | ||
79 | * | ||
80 | * Returns: Nothing | ||
81 | * | ||
82 | * This function should be called during kernel startup to initialize | ||
83 | * the amiga IRQ handling routines. | ||
84 | */ | ||
85 | |||
86 | __init | ||
87 | void amiga_init_IRQ(void) | ||
88 | { | ||
89 | int i; | ||
90 | |||
91 | for (i = 0; i < AMI_IRQS; i++) | ||
92 | ami_ablecount[i] = 0; | ||
93 | |||
94 | /* turn off PCMCIA interrupts */ | ||
95 | if (AMIGAHW_PRESENT(PCMCIA)) | ||
96 | gayle.inten = GAYLE_IRQ_IDE; | ||
97 | |||
98 | /* turn off all interrupts... */ | ||
99 | custom.intena = 0x7fff; | ||
100 | custom.intreq = 0x7fff; | ||
101 | |||
102 | #ifdef CONFIG_APUS | ||
103 | /* Clear any inter-CPU interrupt requests. Circumvents bug in | ||
104 | Blizzard IPL emulation HW (or so it appears). */ | ||
105 | APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK); | ||
106 | |||
107 | /* Init IPL emulation. */ | ||
108 | APUS_WRITE(APUS_REG_INT, REGINT_INTMASTER | REGINT_ENABLEIPL); | ||
109 | APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); | ||
110 | APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_IPLMASK); | ||
111 | #endif | ||
112 | /* ... and enable the master interrupt bit */ | ||
113 | custom.intena = IF_SETCLR | IF_INTEN; | ||
114 | |||
115 | cia_init_IRQ(&ciaa_base); | ||
116 | cia_init_IRQ(&ciab_base); | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Enable/disable a particular machine specific interrupt source. | ||
121 | * Note that this may affect other interrupts in case of a shared interrupt. | ||
122 | * This function should only be called for a _very_ short time to change some | ||
123 | * internal data, that may not be changed by the interrupt at the same time. | ||
124 | * ami_(enable|disable)_irq calls may also be nested. | ||
125 | */ | ||
126 | |||
127 | void amiga_enable_irq(unsigned int irq) | ||
128 | { | ||
129 | if (irq >= AMI_IRQS) { | ||
130 | printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); | ||
131 | return; | ||
132 | } | ||
133 | |||
134 | ami_ablecount[irq]--; | ||
135 | if (ami_ablecount[irq]<0) | ||
136 | ami_ablecount[irq]=0; | ||
137 | else if (ami_ablecount[irq]) | ||
138 | return; | ||
139 | |||
140 | /* No action for auto-vector interrupts */ | ||
141 | if (irq >= IRQ_AMIGA_AUTO){ | ||
142 | printk("%s: Trying to enable auto-vector IRQ %i\n", | ||
143 | __FUNCTION__, irq - IRQ_AMIGA_AUTO); | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | if (irq >= IRQ_AMIGA_CIAA) { | ||
148 | cia_set_irq(irq, 0); | ||
149 | cia_able_irq(irq, 1); | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | /* enable the interrupt */ | ||
154 | custom.intena = IF_SETCLR | ami_intena_vals[irq]; | ||
155 | } | ||
156 | |||
157 | void amiga_disable_irq(unsigned int irq) | ||
158 | { | ||
159 | if (irq >= AMI_IRQS) { | ||
160 | printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | if (ami_ablecount[irq]++) | ||
165 | return; | ||
166 | |||
167 | /* No action for auto-vector interrupts */ | ||
168 | if (irq >= IRQ_AMIGA_AUTO) { | ||
169 | printk("%s: Trying to disable auto-vector IRQ %i\n", | ||
170 | __FUNCTION__, irq - IRQ_AMIGA_AUTO); | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | if (irq >= IRQ_AMIGA_CIAA) { | ||
175 | cia_able_irq(irq, 0); | ||
176 | return; | ||
177 | } | ||
178 | |||
179 | /* disable the interrupt */ | ||
180 | custom.intena = ami_intena_vals[irq]; | ||
181 | } | ||
182 | |||
183 | inline void amiga_do_irq(int irq, struct pt_regs *fp) | ||
184 | { | ||
185 | irq_desc_t *desc = irq_desc + irq; | ||
186 | struct irqaction *action = desc->action; | ||
187 | |||
188 | kstat_cpu(0).irqs[irq]++; | ||
189 | action->handler(irq, action->dev_id, fp); | ||
190 | } | ||
191 | |||
192 | void amiga_do_irq_list(int irq, struct pt_regs *fp) | ||
193 | { | ||
194 | irq_desc_t *desc = irq_desc + irq; | ||
195 | struct irqaction *action; | ||
196 | |||
197 | kstat_cpu(0).irqs[irq]++; | ||
198 | |||
199 | custom.intreq = ami_intena_vals[irq]; | ||
200 | |||
201 | for (action = desc->action; action; action = action->next) | ||
202 | action->handler(irq, action->dev_id, fp); | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * The builtin Amiga hardware interrupt handlers. | ||
207 | */ | ||
208 | |||
209 | static void ami_int1(int irq, void *dev_id, struct pt_regs *fp) | ||
210 | { | ||
211 | unsigned short ints = custom.intreqr & custom.intenar; | ||
212 | |||
213 | /* if serial transmit buffer empty, interrupt */ | ||
214 | if (ints & IF_TBE) { | ||
215 | custom.intreq = IF_TBE; | ||
216 | amiga_do_irq(IRQ_AMIGA_TBE, fp); | ||
217 | } | ||
218 | |||
219 | /* if floppy disk transfer complete, interrupt */ | ||
220 | if (ints & IF_DSKBLK) { | ||
221 | custom.intreq = IF_DSKBLK; | ||
222 | amiga_do_irq(IRQ_AMIGA_DSKBLK, fp); | ||
223 | } | ||
224 | |||
225 | /* if software interrupt set, interrupt */ | ||
226 | if (ints & IF_SOFT) { | ||
227 | custom.intreq = IF_SOFT; | ||
228 | amiga_do_irq(IRQ_AMIGA_SOFT, fp); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | static void ami_int3(int irq, void *dev_id, struct pt_regs *fp) | ||
233 | { | ||
234 | unsigned short ints = custom.intreqr & custom.intenar; | ||
235 | |||
236 | /* if a blitter interrupt */ | ||
237 | if (ints & IF_BLIT) { | ||
238 | custom.intreq = IF_BLIT; | ||
239 | amiga_do_irq(IRQ_AMIGA_BLIT, fp); | ||
240 | } | ||
241 | |||
242 | /* if a copper interrupt */ | ||
243 | if (ints & IF_COPER) { | ||
244 | custom.intreq = IF_COPER; | ||
245 | amiga_do_irq(IRQ_AMIGA_COPPER, fp); | ||
246 | } | ||
247 | |||
248 | /* if a vertical blank interrupt */ | ||
249 | if (ints & IF_VERTB) | ||
250 | amiga_do_irq_list(IRQ_AMIGA_VERTB, fp); | ||
251 | } | ||
252 | |||
253 | static void ami_int4(int irq, void *dev_id, struct pt_regs *fp) | ||
254 | { | ||
255 | unsigned short ints = custom.intreqr & custom.intenar; | ||
256 | |||
257 | /* if audio 0 interrupt */ | ||
258 | if (ints & IF_AUD0) { | ||
259 | custom.intreq = IF_AUD0; | ||
260 | amiga_do_irq(IRQ_AMIGA_AUD0, fp); | ||
261 | } | ||
262 | |||
263 | /* if audio 1 interrupt */ | ||
264 | if (ints & IF_AUD1) { | ||
265 | custom.intreq = IF_AUD1; | ||
266 | amiga_do_irq(IRQ_AMIGA_AUD1, fp); | ||
267 | } | ||
268 | |||
269 | /* if audio 2 interrupt */ | ||
270 | if (ints & IF_AUD2) { | ||
271 | custom.intreq = IF_AUD2; | ||
272 | amiga_do_irq(IRQ_AMIGA_AUD2, fp); | ||
273 | } | ||
274 | |||
275 | /* if audio 3 interrupt */ | ||
276 | if (ints & IF_AUD3) { | ||
277 | custom.intreq = IF_AUD3; | ||
278 | amiga_do_irq(IRQ_AMIGA_AUD3, fp); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | static void ami_int5(int irq, void *dev_id, struct pt_regs *fp) | ||
283 | { | ||
284 | unsigned short ints = custom.intreqr & custom.intenar; | ||
285 | |||
286 | /* if serial receive buffer full interrupt */ | ||
287 | if (ints & IF_RBF) { | ||
288 | /* acknowledge of IF_RBF must be done by the serial interrupt */ | ||
289 | amiga_do_irq(IRQ_AMIGA_RBF, fp); | ||
290 | } | ||
291 | |||
292 | /* if a disk sync interrupt */ | ||
293 | if (ints & IF_DSKSYN) { | ||
294 | custom.intreq = IF_DSKSYN; | ||
295 | amiga_do_irq(IRQ_AMIGA_DSKSYN, fp); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | static void ami_int7(int irq, void *dev_id, struct pt_regs *fp) | ||
300 | { | ||
301 | panic ("level 7 interrupt received\n"); | ||
302 | } | ||
303 | |||
304 | #ifdef CONFIG_APUS | ||
305 | /* The PPC irq handling links all handlers requested on the same vector | ||
306 | and executes them in a loop. Having ami_badint at the end of the chain | ||
307 | is a bad idea. */ | ||
308 | struct irqaction amiga_sys_irqaction[AUTO_IRQS] = { | ||
309 | { .handler = ami_badint, .name = "spurious int" }, | ||
310 | { .handler = ami_int1, .name = "int1 handler" }, | ||
311 | { 0, /* CIAA */ }, | ||
312 | { .handler = ami_int3, .name = "int3 handler" }, | ||
313 | { .handler = ami_int4, .name = "int4 handler" }, | ||
314 | { .handler = ami_int5, .name = "int5 handler" }, | ||
315 | { 0, /* CIAB */ }, | ||
316 | { .handler = ami_int7, .name = "int7 handler" }, | ||
317 | }; | ||
318 | #else | ||
319 | void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { | ||
320 | ami_badint, ami_int1, ami_badint, ami_int3, | ||
321 | ami_int4, ami_int5, ami_badint, ami_int7 | ||
322 | }; | ||
323 | #endif | ||