iomd_irq.S revision 1.2 1 /* $NetBSD: iomd_irq.S,v 1.2 2001/12/20 01:20:23 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1994-1998 Mark Brinicombe.
5 * Copyright (c) 1994 Brini.
6 * All rights reserved.
7 *
8 * This code is derived from software written for Brini by Mark Brinicombe
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Mark Brinicombe
21 * for the NetBSD Project.
22 * 4. The name of the company nor the name of the author may be used to
23 * endorse or promote products derived from this software without specific
24 * prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Low level irq and fiq handlers
38 *
39 * Created : 27/09/94
40 */
41
42 #include "opt_irqstats.h"
43
44 #include "assym.h"
45 #include <machine/asm.h>
46 #include <machine/cpu.h>
47 #include <machine/frame.h>
48 #include <arm/iomd/iomdreg.h>
49
50 .text
51 .align 0
52 /*
53 * ffs table used for servicing irq's quickly must be here otherwise adr can't
54 * reach it
55 * The algorithm for ffs was devised by D. Seal and posted to
56 * comp.sys.arm on 16 Feb 1994.
57 */
58 .type Lirq_ffs_table, _ASM_TYPE_OBJECT;
59 Lirq_ffs_table:
60 /* same as ffs table but all nums are -1 from that */
61 /* 0 1 2 3 4 5 6 7 */
62 .byte 0, 0, 1, 12, 2, 6, 0, 13 /* 0- 7 */
63 .byte 3, 0, 7, 0, 0, 0, 0, 14 /* 8-15 */
64 .byte 10, 4, 0, 0, 8, 0, 0, 25 /* 16-23 */
65 .byte 0, 0, 0, 0, 0, 21, 27, 15 /* 24-31 */
66 .byte 31, 11, 5, 0, 0, 0, 0, 0 /* 32-39 */
67 .byte 9, 0, 0, 24, 0, 0, 20, 26 /* 40-47 */
68 .byte 30, 0, 0, 0, 0, 23, 0, 19 /* 48-55 */
69 .byte 29, 0, 22, 18, 28, 17, 16, 0 /* 56-63 */
70
71 /*
72 *
73 * irq_entry
74 *
75 * Main entry point for the IRQ vector
76 *
77 * This function reads the irq request bits in the IOMD registers
78 * IRQRQA, IRQRQB and DMARQ
79 * It then calls an installed handler for each bit that is set.
80 * The function stray_irqhandler is called if a handler is not defined
81 * for a particular interrupt.
82 * If a interrupt handler is found then it is called with r0 containing
83 * the argument defined in the handler structure. If the field ih_arg
84 * is zero then a pointer to the IRQ frame on the stack is passed instead.
85 */
86
87 Ldisabled_mask:
88 .word _C_LABEL(disabled_mask)
89
90 Lcurrent_spl_level:
91 .word _C_LABEL(current_spl_level)
92
93 Lcurrent_intr_depth:
94 .word _C_LABEL(current_intr_depth)
95
96 Lspl_masks:
97 .word _C_LABEL(spl_masks)
98
99 /*
100 * Register usage
101 *
102 * r5 - Address of ffs table
103 * r6 - Address of current handler
104 * r7 - Pointer to handler pointer list
105 * r8 - Current IRQ requests.
106 * r10 - Base address of IOMD
107 * r11 - IRQ requests still to service.
108 */
109
110 Liomd_base:
111 .word _C_LABEL(iomd_base)
112
113 Larm7500_ioc_found:
114 .word _C_LABEL(arm7500_ioc_found)
115
116 ASENTRY_NP(irq_entry)
117 sub lr, lr, #0x00000004 /* Adjust the lr */
118
119 PUSHFRAMEINSVC /* Push an interrupt frame */
120
121 /* Load r8 with the IOMD interrupt requests */
122
123 ldr r10, Liomd_base
124 ldr r10, [r10] /* Point to the IOMD */
125 ldrb r8, [r10, #(IOMD_IRQRQA << 2)] /* Get IRQ request A */
126 ldrb r9, [r10, #(IOMD_IRQRQB << 2)] /* Get IRQ request B */
127 orr r8, r8, r9, lsl #8
128
129 ldr r9, Larm7500_ioc_found
130 ldr r9, [r9] /* get the flag */
131 cmp r9, #0
132 beq skip_extended_IRQs_reading
133
134 /* ARM 7500 only */
135 ldrb r9, [r10, #(IOMD_IRQRQC << 2)] /* Get IRQ request C */
136 orr r8, r8, r9, lsl #16
137 ldrb r9, [r10, #(IOMD_IRQRQD << 2)] /* Get IRQ request D */
138 orr r8, r8, r9, lsl #24
139 ldrb r9, [r10, #(IOMD_DMARQ << 2)] /* Get DMA Request */
140 tst r9, #0x10
141 orrne r8, r8, r9, lsl #27
142 b irq_entry_continue
143
144 skip_extended_IRQs_reading:
145 /* non ARM7500 machines */
146 ldrb r9, [r10, #(IOMD_DMARQ << 2)] /* Get DMA Request */
147 orr r8, r8, r9, lsl #16
148 irq_entry_continue:
149
150 and r0, r8, #0x7d /* Clear IOMD IRQA bits */
151 strb r0, [r10, #(IOMD_IRQRQA << 2)]
152
153 /*
154 * Note that we have entered the IRQ handler.
155 * We are in SVC mode so we cannot use the processor mode
156 * to determine if we are in an IRQ. Instead we will count the
157 * each time the interrupt handler is nested.
158 */
159
160 ldr r0, Lcurrent_intr_depth
161 ldr r1, [r0]
162 add r1, r1, #1
163 str r1, [r0]
164
165 /* Block the current requested interrupts */
166 ldr r1, Ldisabled_mask
167 ldr r0, [r1]
168 stmfd sp!, {r0}
169 orr r0, r0, r8
170
171 /*
172 * Need to block all interrupts at the IPL or lower for
173 * all asserted interrupts.
174 * This basically emulates hardware interrupt priority levels.
175 * Means we need to go through the interrupt mask and for
176 * every asserted interrupt we need to mask out all other
177 * interrupts at the same or lower IPL.
178 * If only we could wait until the main loop but we need to sort
179 * this out first so interrupts can be re-enabled.
180 *
181 * This would benefit from a special ffs type routine
182 */
183
184 mov r9, #(_SPL_LEVELS - 1)
185 ldr r7, Lspl_masks
186
187 Lfind_highest_ipl:
188 ldr r2, [r7, r9, lsl #2]
189 tst r8, r2
190 subeq r9, r9, #1
191 beq Lfind_highest_ipl
192
193 /* r9 = SPL level of highest priority interrupt */
194 add r9, r9, #1
195 ldr r2, [r7, r9, lsl #2]
196 mvn r2, r2
197 orr r0, r0, r2
198
199 str r0, [r1]
200
201 ldr r0, Lcurrent_spl_level
202 ldr r1, [r0]
203 str r9, [r0]
204 stmfd sp!, {r1}
205
206 /* Update the IOMD irq masks */
207 bl _C_LABEL(irq_setmasks)
208
209 mrs r0, cpsr_all /* Enable IRQ's */
210 bic r0, r0, #I32_bit
211 msr cpsr_all, r0
212
213 ldr r7, [pc, #Lirqhandlers - . - 8]
214
215 /*
216 * take a copy of the IRQ request so that we can strip bits out of it
217 * note that we only use 24 bits with iomd2 chips
218 */
219 ldr r4, Larm7500_ioc_found
220 ldr r4, [r4] /* get the flag */
221 cmp r4, #0
222 movne r11, r8 /* ARM7500 -> copy all bits */
223 biceq r11, r8, #0xff000000 /* !ARM7500 -> only use 24 bit */
224
225 /* ffs routine to find first irq to service */
226 /* standard trick to isolate bottom bit in a0 or 0 if a0 = 0 on entry */
227 rsb r4, r11, #0
228 ands r10, r11, r4
229
230 /*
231 * now r10 has at most 1 set bit, call this X
232 * if X = 0, branch to exit code
233 */
234 beq exitirq
235 adr r5, Lirq_ffs_table
236 irqloop:
237 /*
238 * at this point:
239 * r5 = address of ffs table
240 * r7 = address of irq handlers table
241 * r8 = irq request
242 * r10 = bit of irq to be serviced
243 * r11 = bitmask of IRQ's to service
244 */
245
246 /* find the set bit */
247 orr r9, r10, r10, lsl #4 /* X * 0x11 */
248 orr r9, r9, r9, lsl #6 /* X * 0x451 */
249 rsb r9, r9, r9, lsl #16 /* X * 0x0450fbaf */
250 /* fetch the bit number */
251 ldrb r9, [r5, r9, lsr #26 ]
252
253 /*
254 * r9 = irq to service
255 */
256
257 /* apologies for the dogs dinner of code here, but it's in an attempt
258 * to minimise stalling on SA's, hence lots of things happen here:
259 * - getting address of handler, if it doesn't exist we call
260 * stray_irqhandler this is assumed to be rare so we don't
261 * care about performance for it
262 * - statinfo is updated
263 * - unsetting of the irq bit in r11
264 * - irq stats (if enabled) also get put in the mix
265 */
266 ldr r4, Lcnt /* Stat info A */
267 ldr r6, [r7, r9, lsl #2] /* Get address of first handler structure */
268
269 ldr r1, [r4, #(V_INTR)] /* Stat info B */
270
271 teq r6, #0x00000000 /* Do we have a handler */
272 moveq r0, r8 /* IRQ requests as arg 0 */
273 addeq lr, pc, #nextirq - . - 8 /* return Address */
274 beq _C_LABEL(stray_irqhandler) /* call special handler */
275
276 #ifdef IRQSTATS
277 ldr r2, Lintrcnt
278 ldr r3, [r6, #(IH_NUM)]
279 #endif
280 /* stat info C */
281 add r1, r1, #0x00000001
282 str r1, [r4, #(V_INTR)]
283
284 #ifdef IRQSTATS
285 ldr r3, [r2, r3, lsl #2]!
286 #endif
287 bic r11, r11, r10 /* clear the IRQ bit */
288
289 #ifdef IRQSTATS
290 add r3, r3, #0x00000001
291 str r3, [r2]
292 #endif /* IRQSTATS */
293
294 irqchainloop:
295 ldr r0, [r6, #(IH_ARG)] /* Get argument pointer */
296 add lr, pc, #nextinchain - . - 8 /* return address */
297 teq r0, #0x00000000 /* If arg is zero pass stack frame */
298 addeq r0, sp, #8 /* ... stack frame [XXX needs care] */
299 ldr pc, [r6, #(IH_FUNC)] /* Call handler */
300
301 nextinchain:
302 ldr r6, [r6, #(IH_NEXT)] /* fetch next handler */
303
304 teq r0, #0x00000001 /* Was the irq serviced ? */
305
306 /* if it was it'll just fall through this: */
307 teqne r6, #0x00000000
308 bne irqchainloop
309 nextirq:
310 /* Check for next irq */
311 rsb r4, r11, #0
312 ands r10, r11, r4
313 /* check if there are anymore irq's to service */
314 bne irqloop
315
316 exitirq:
317 ldmfd sp!, {r2, r3}
318 ldr r9, Lcurrent_spl_level
319 ldr r1, Ldisabled_mask
320 str r2, [r9]
321 str r3, [r1]
322
323 bl _C_LABEL(irq_setmasks)
324
325 bl _C_LABEL(dosoftints) /* Handle the soft interrupts */
326
327 /* Manage AST's. Maybe this should be done as a soft interrupt ? */
328 ldr r0, [sp] /* Get the SPSR from stack */
329
330 and r0, r0, #(PSR_MODE) /* Test for USR32 mode before the IRQ */
331 teq r0, #(PSR_USR32_MODE)
332 ldreq r0, Lastpending /* Do we have an AST pending ? */
333 ldreq r1, [r0]
334 teqeq r1, #0x00000001
335
336 beq irqast /* call the AST handler */
337
338 /* Kill IRQ's in preparation for exit */
339 mrs r0, cpsr_all
340 orr r0, r0, #(I32_bit)
341 msr cpsr_all, r0
342
343 /* Decrement the nest count */
344 ldr r0, Lcurrent_intr_depth
345 ldr r1, [r0]
346 sub r1, r1, #1
347 str r1, [r0]
348
349 PULLFRAMEFROMSVCANDEXIT
350
351 /* NOT REACHED */
352 b . - 8
353
354 /*
355 * Ok, snag with current intr depth ...
356 * If ast() calls mi_sleep() the current_intr_depth will not be
357 * decremented until the process is woken up. This can result
358 * in the system believing it is still in the interrupt handler.
359 * If we are calling ast() then correct the current_intr_depth
360 * before the call.
361 */
362 irqast:
363 mov r1, #0x00000000 /* Clear ast_pending */
364 str r1, [r0]
365
366 /* Kill IRQ's so we atomically decrement current_intr_depth */
367 mrs r2, cpsr_all
368 orr r3, r2, #(I32_bit)
369 msr cpsr_all, r3
370
371 /* Decrement the interrupt nesting count */
372 ldr r0, Lcurrent_intr_depth
373 ldr r1, [r0]
374 sub r1, r1, #1
375 str r1, [r0]
376
377 /* Restore IRQ's */
378 msr cpsr_all, r2
379
380 mov r0, sp
381 bl _C_LABEL(ast)
382
383 /* Kill IRQ's in preparation for exit */
384 mrs r0, cpsr_all
385 orr r0, r0, #(I32_bit)
386 msr cpsr_all, r0
387
388 PULLFRAMEFROMSVCANDEXIT
389
390 /* NOT REACHED */
391 b . - 8
392
393
394 Lspl_mask:
395 .word _C_LABEL(spl_mask) /* irq's allowed at current spl level */
396
397 Lcurrent_mask:
398 .word _C_LABEL(current_mask) /* irq's that are usable */
399
400 ENTRY(irq_setmasks)
401 /* Disable interrupts */
402 mrs r3, cpsr_all
403 orr r1, r3, #(I32_bit)
404 msr cpsr_all, r1
405
406 /* Calculate IOMD interrupt mask */
407 ldr r1, Lcurrent_mask /* All the enabled interrupts */
408 ldr r2, Lspl_mask /* Block due to current spl level */
409 ldr r1, [r1]
410 ldr r2, [r2]
411 and r1, r1, r2
412 ldr r2, Ldisabled_mask /* Block due to active interrupts */
413 ldr r2, [r2]
414 bic r1, r1, r2
415
416 ldr r0, Liomd_base
417 ldr r0, [r0] /* Point to the IOMD */
418 strb r1, [r0, #(IOMD_IRQMSKA << 2)] /* Set IRQ mask A */
419 mov r1, r1, lsr #8
420 strb r1, [r0, #(IOMD_IRQMSKB << 2)] /* Set IRQ mask B */
421 mov r1, r1, lsr #8
422
423 ldr r2, Larm7500_ioc_found
424 ldr r2, [r2]
425 cmp r2, #0
426 beq skip_setting_extended_DMA_mask
427
428 /* only for ARM7500's */
429 strb r1, [r0, #(IOMD_IRQMSKC << 2)]
430 mov r1, r1, lsr #8
431 and r2, r1, #0xef
432 strb r2, [r0, #(IOMD_IRQMSKD << 2)]
433 mov r1, r1, lsr #3
434 and r2, r1, #0x10
435 strb r2, [r0, #(IOMD_DMAMSK << 2)] /* Set DMA mask */
436 b continue_setting_masks
437
438 skip_setting_extended_DMA_mask:
439 /* non ARM7500's */
440 strb r1, [r0, #(IOMD_DMAMSK << 2)] /* Set DMA mask */
441
442 continue_setting_masks:
443
444 /* Restore old cpsr and exit */
445 msr cpsr_all, r3
446 mov pc, lr
447
448 Lcnt:
449 .word _C_LABEL(uvmexp)
450
451 Lintrcnt:
452 .word _C_LABEL(intrcnt)
453
454
455 Lirqhandlers:
456 .word _C_LABEL(irqhandlers) /* Pointer to array of irqhandlers */
457
458 Lastpending:
459 .word _C_LABEL(astpending)
460
461 #ifdef IRQSTATS
462 /* These symbols are used by vmstat */
463
464 .text
465 .global _C_LABEL(_intrnames)
466 _C_LABEL(_intrnames):
467 .word _C_LABEL(intrnames)
468
469 .data
470
471 .globl _C_LABEL(intrnames), _C_LABEL(eintrnames), _C_LABEL(intrcnt), _C_LABEL(sintrcnt), _C_LABEL(eintrcnt)
472 _C_LABEL(intrnames):
473 .asciz "interrupt 0 "
474 .asciz "interrupt 1 " /* reserved0 */
475 .asciz "interrupt 2 "
476 .asciz "interrupt 3 "
477 .asciz "interrupt 4 "
478 .asciz "interrupt 5 "
479 .asciz "interrupt 6 "
480 .asciz "interrupt 7 " /* reserved1 */
481 .asciz "interrupt 8 " /* reserved2 */
482 .asciz "interrupt 9 "
483 .asciz "interrupt 10 "
484 .asciz "interrupt 11 "
485 .asciz "interrupt 12 "
486 .asciz "interrupt 13 "
487 .asciz "interrupt 14 "
488 .asciz "interrupt 15 "
489 .asciz "dma channel 0"
490 .asciz "dma channel 1"
491 .asciz "dma channel 2"
492 .asciz "dma channel 3"
493 .asciz "interrupt 20 "
494 .asciz "interrupt 21 "
495 .asciz "reserved 3 "
496 .asciz "reserved 4 "
497 .asciz "exp card 0 "
498 .asciz "exp card 1 "
499 .asciz "exp card 2 "
500 .asciz "exp card 3 "
501 .asciz "exp card 4 "
502 .asciz "exp card 5 "
503 .asciz "exp card 6 "
504 .asciz "exp card 7 "
505
506 _C_LABEL(sintrnames):
507 .asciz "softclock "
508 .asciz "softnet "
509 .asciz "softserial "
510 .asciz "softintr 3 "
511 .asciz "softintr 4 "
512 .asciz "softintr 5 "
513 .asciz "softintr 6 "
514 .asciz "softintr 7 "
515 .asciz "softintr 8 "
516 .asciz "softintr 9 "
517 .asciz "softintr 10 "
518 .asciz "softintr 11 "
519 .asciz "softintr 12 "
520 .asciz "softintr 13 "
521 .asciz "softintr 14 "
522 .asciz "softintr 15 "
523 .asciz "softintr 16 "
524 .asciz "softintr 17 "
525 .asciz "softintr 18 "
526 .asciz "softintr 19 "
527 .asciz "softintr 20 "
528 .asciz "softintr 21 "
529 .asciz "softintr 22 "
530 .asciz "softintr 23 "
531 .asciz "softintr 24 "
532 .asciz "softintr 25 "
533 .asciz "softintr 26 "
534 .asciz "softintr 27 "
535 .asciz "softintr 28 "
536 .asciz "softintr 29 "
537 .asciz "softintr 30 "
538 .asciz "softintr 31 "
539 _C_LABEL(eintrnames):
540
541 .bss
542 .align 0
543 _C_LABEL(intrcnt):
544 .space 32*4 /* XXX Should be linked to number of interrupts */
545
546 _C_LABEL(sintrcnt):
547 .space 32*4 /* XXX Should be linked to number of interrupts */
548 _C_LABEL(eintrcnt):
549
550 #else /* IRQSTATS */
551 /* Dummy entries to keep vmstat happy */
552
553 .text
554 .globl _C_LABEL(intrnames), _C_LABEL(eintrnames), _C_LABEL(intrcnt), _C_LABEL(eintrcnt)
555 _C_LABEL(intrnames):
556 .long 0
557 _C_LABEL(eintrnames):
558
559 _C_LABEL(intrcnt):
560 .long 0
561 _C_LABEL(eintrcnt):
562 #endif /* IRQSTATS */
563