Fix up mix of man(7)/mdoc(7).
[netbsd-mini2440.git] / sys / arch / arm / iomd / iomd_irq.S
blobac8681a17cd1536f4b86eecc96d53ab4815d02bd
1 /*      $NetBSD: iomd_irq.S,v 1.11 2008/04/27 18:58:44 matt Exp $       */
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  */
42 #include "opt_irqstats.h"
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>
50         .text
51         .align  0
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 */
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  */
87 Lcurrent_spl_level:
88         .word   _C_LABEL(cpu_info_store) + CI_CPL
90 Ldisabled_mask:
91         .word   _C_LABEL(disabled_mask)
93 Lspl_masks:
94         .word   _C_LABEL(spl_masks)
96 LOCK_CAS_CHECK_LOCALS
98 AST_ALIGNMENT_FAULT_LOCALS
101  * Register usage
103  *  r4  - Address of cpu_info
104  *  r5  - Address of ffs table
105  *  r6  - Address of current handler
106  *  r7  - Pointer to handler pointer list
107  *  r8  - Current IRQ requests.
108  *  r10 - Base address of IOMD
109  *  r11 - IRQ requests still to service.
110  */
112 Liomd_base:
113         .word   _C_LABEL(iomd_base)
115 Larm7500_ioc_found:
116         .word   _C_LABEL(arm7500_ioc_found)
118 ASENTRY_NP(irq_entry)
119         sub     lr, lr, #0x00000004     /* Adjust the lr */
121         PUSHFRAMEINSVC                  /* Push an interrupt frame */
122         ENABLE_ALIGNMENT_FAULTS
124         /* Load r8 with the IOMD interrupt requests */
126         ldr     r10, Liomd_base
127         ldr     r10, [r10]                      /* Point to the IOMD */
128         ldrb    r8, [r10, #(IOMD_IRQRQA << 2)]  /* Get IRQ request A */
129         ldrb    r9, [r10, #(IOMD_IRQRQB << 2)]  /* Get IRQ request B */
130         orr     r8, r8, r9, lsl #8
132         ldr     r9, Larm7500_ioc_found
133         ldr     r9, [r9]                        /* get the flag      */
134         cmp     r9, #0
135         beq     skip_extended_IRQs_reading
137         /* ARM 7500 only */
138         ldrb    r9, [r10, #(IOMD_IRQRQC << 2)]  /* Get IRQ request C */
139         orr     r8, r8, r9, lsl #16
140         ldrb    r9, [r10, #(IOMD_IRQRQD << 2)]  /* Get IRQ request D */
141         orr     r8, r8, r9, lsl #24
142         ldrb    r9, [r10, #(IOMD_DMARQ << 2)]   /* Get DMA Request */
143         tst     r9, #0x10
144         orrne   r8, r8, r9, lsl #27
145         b       irq_entry_continue
147 skip_extended_IRQs_reading:
148         /* non ARM7500 machines */
149         ldrb    r9, [r10, #(IOMD_DMARQ << 2)]   /* Get DMA Request */
150         orr     r8, r8, r9, lsl #16
151 irq_entry_continue:
153         and     r0, r8, #0x7d           /* Clear IOMD IRQA bits */
154         strb    r0, [r10, #(IOMD_IRQRQA << 2)]
156         /*
157          * Note that we have entered the IRQ handler.
158          * We are in SVC mode so we cannot use the processor mode
159          * to determine if we are in an IRQ. Instead we will count the
160          * each time the interrupt handler is nested.
161          */
163         ldr     r0, [r4, #CI_INTR_DEPTH]
164         add     r0, r0, #1
165         str     r0, [r4, #CI_INTR_DEPTH]
167         /* Block the current requested interrupts */
168         ldr     r1, Ldisabled_mask
169         ldr     r0, [r1]
170         stmfd   sp!, {r0}
171         orr     r0, r0, r8
173         /*
174          * Need to block all interrupts at the IPL or lower for
175          * all asserted interrupts.
176          * This basically emulates hardware interrupt priority levels.
177          * Means we need to go through the interrupt mask and for
178          * every asserted interrupt we need to mask out all other
179          * interrupts at the same or lower IPL.
180          * If only we could wait until the main loop but we need to sort
181          * this out first so interrupts can be re-enabled.
182          *
183          * This would benefit from a special ffs type routine
184          */
186         mov     r9, #(NIPL - 1)
187         ldr     r7, Lspl_masks
189 Lfind_highest_ipl:
190         ldr     r2, [r7, r9, lsl #2]
191         tst     r8, r2
192         subeq   r9, r9, #1
193         beq     Lfind_highest_ipl
195         /* r9 = SPL level of highest priority interrupt */
196         add     r9, r9, #1
197         ldr     r2, [r7, r9, lsl #2]
198         mvn     r2, r2
199         orr     r0, r0, r2
201         str     r0, [r1]        
203         ldr     r0, [r4, #CI_CPL]
204         str     r9, [r4, #CI_CPL]
205         stmfd   sp!, {r0}
207         /* Update the IOMD irq masks */
208         bl      _C_LABEL(irq_setmasks)
210         mrs     r0, cpsr_all            /* Enable IRQ's */
211         bic     r0, r0, #I32_bit
212         msr     cpsr_all, r0
214         ldr     r7, Lirqhandlers
216         /* 
217          * take a copy of the IRQ request so that we can strip bits out of it
218          * note that we only use 24 bits with iomd2 chips
219          */
220         ldr     r5, Larm7500_ioc_found
221         ldr     r5, [r5]                        /* get the flag      */
222         cmp     r5, #0
223         movne   r11, r8                         /* ARM7500  -> copy all bits   */
224         biceq   r11, r8, #0xff000000            /* !ARM7500 -> only use 24 bit */
226         /* ffs routine to find first irq to service */
227         /* standard trick to isolate bottom bit in a0 or 0 if a0 = 0 on entry */
228         rsb     r5, r11, #0
229         ands    r10, r11, r5
231         /* 
232          * now r10 has at most 1 set bit, call this X
233          * if X = 0, branch to exit code
234          */
235         beq     exitirq
236 irqloop:
237         adr     r5, Lirq_ffs_table
238         /*
239          * at this point:
240          *      r5 = address of ffs table
241          *      r7 = address of irq handlers table
242          *      r8 = irq request
243          *      r10 = bit of irq to be serviced
244          *      r11 = bitmask of IRQ's to service
245          */
247         /* find the set bit */
248         orr     r9, r10, r10, lsl #4    /* X * 0x11 */
249         orr     r9, r9, r9, lsl #6      /* X * 0x451 */
250         rsb     r9, r9, r9, lsl #16     /* X * 0x0450fbaf */
251         /* fetch the bit number */
252         ldrb    r9, [r5, r9, lsr #26 ]
254         /* 
255          * r9 = irq to service
256          */
258         /* apologies for the dogs dinner of code here, but it's in an attempt
259          * to minimise stalling on SA's, hence lots of things happen here:
260          *      - getting address of handler, if it doesn't exist we call
261          *        stray_irqhandler this is assumed to be rare so we don't
262          *        care about performance for it
263          *      - statinfo is updated
264          *      - unsetting of the irq bit in r11
265          *      - irq stats (if enabled) also get put in the mix
266          */
267         ldr     r5, Lcnt                /* Stat info A */
268         ldr     r6, [r7, r9, lsl #2]    /* Get address of first handler structure */
270         ldr     r1, [r5, #(V_INTR)]     /* Stat info B */
271         
272         teq     r6, #0x00000000         /* Do we have a handler */
273         moveq   r0, r8                  /* IRQ requests as arg 0 */
274         adreq   lr, nextirq             /* return Address */
275         beq     _C_LABEL(stray_irqhandler) /* call special handler */
276         
277 #ifdef IRQSTATS
278         ldr     r2, Lintrcnt
279         ldr     r3, [r6, #(IH_NUM)]
280 #endif
281         /* stat info C */
282         add     r1, r1, #0x00000001
283         str     r1, [r5, #(V_INTR)]
285 #ifdef IRQSTATS
286         ldr     r3, [r2, r3, lsl #2]!
287 #endif
288         bic     r11, r11, r10           /* clear the IRQ bit */
290 #ifdef IRQSTATS
291         add     r3, r3, #0x00000001
292         str     r3, [r2]
293 #endif  /* IRQSTATS */
295 irqchainloop:
296         ldr     r0, [r6, #(IH_ARG)]     /* Get argument pointer */
297         teq     r0, #0x00000000         /* If arg is zero pass stack frame */
298         addeq   r0, sp, #8              /* ... stack frame [XXX needs care] */
299         mov     lr, pc                  /* return address */
300         ldr     pc, [r6, #(IH_FUNC)]    /* Call handler */
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     r5, r11, #0
312         ands    r10, r11, r5
313         /* check if there are anymore irq's to service */
314         bne     irqloop
316 exitirq:
317         ldmfd   sp!, {r2, r3}
318         ldr     r0, Ldisabled_mask
319         str     r2, [r4, #CI_CPL]
320         str     r3, [r0]
322         bl      _C_LABEL(irq_setmasks)
324 #if __HAVE_FAST_SOFTINTS
325         bl      _C_LABEL(dosoftints)    /* Handle the soft interrupts */
326 #endif
328         /* Kill IRQ's in preparation for exit */
329         mrs     r0, cpsr_all
330         orr     r0, r0, #(I32_bit)
331         msr     cpsr_all, r0
333         /* Decrement the nest count */
334         ldr     r0, [r4, #CI_INTR_DEPTH]
335         sub     r0, r0, #1
336         str     r0, [r4, #CI_INTR_DEPTH]
338         LOCK_CAS_CHECK
340         DO_AST_AND_RESTORE_ALIGNMENT_FAULTS
341         PULLFRAMEFROMSVCANDEXIT
343         /* NOT REACHED */
344         b       . - 8
346 Lcurrent_mask:
347         .word   _C_LABEL(current_mask)  /* irq's that are usable */
349 ENTRY(irq_setmasks)
350         /* Disable interrupts */
351         mrs     r3, cpsr_all
352         orr     r1, r3,  #(I32_bit)
353         msr     cpsr_all, r1
355         /* Calculate IOMD interrupt mask */
356         ldr     r1, Lcurrent_mask       /* All the enabled interrupts */
357         ldr     r1, [r1]
358         ldr     r0, Lspl_masks          /* Block due to current spl level */
359         ldr     r2, Lcurrent_spl_level
360         ldr     r2, [r2]
361         ldr     r2, [r0, r2, lsl #2]
362         and     r1, r1, r2
363         ldr     r2, Ldisabled_mask      /* Block due to active interrupts */
364         ldr     r2, [r2]
365         bic     r1, r1, r2
367         ldr     r0, Liomd_base
368         ldr     r0, [r0]                        /* Point to the IOMD */
369         strb    r1, [r0, #(IOMD_IRQMSKA << 2)]  /* Set IRQ mask A */
370         mov     r1, r1, lsr #8
371         strb    r1, [r0, #(IOMD_IRQMSKB << 2)]  /* Set IRQ mask B */
372         mov     r1, r1, lsr #8
374         ldr     r2, Larm7500_ioc_found
375         ldr     r2, [r2]
376         cmp     r2, #0
377         beq     skip_setting_extended_DMA_mask
379         /* only for ARM7500's */
380         strb    r1, [r0, #(IOMD_IRQMSKC << 2)]
381         mov     r1, r1, lsr #8
382         and     r2, r1, #0xef
383         strb    r2, [r0, #(IOMD_IRQMSKD << 2)]
384         mov     r1, r1, lsr #3
385         and     r2, r1, #0x10
386         strb    r2, [r0, #(IOMD_DMAMSK << 2)]   /* Set DMA mask */
387         b       continue_setting_masks
389 skip_setting_extended_DMA_mask:
390         /* non ARM7500's */
391         strb    r1, [r0, #(IOMD_DMAMSK << 2)]   /* Set DMA mask */
393 continue_setting_masks:
395         /* Restore old cpsr and exit */
396         msr     cpsr_all, r3
397         mov     pc, lr
399 Lcnt:
400         .word   _C_LABEL(uvmexp)
402 Lintrcnt:
403         .word   _C_LABEL(intrcnt)
406 Lirqhandlers:
407         .word   _C_LABEL(irqhandlers)   /* Pointer to array of irqhandlers */
409 #ifdef IRQSTATS
410 /* These symbols are used by vmstat */
412         .text
413         .global _C_LABEL(_intrnames)
414 _C_LABEL(_intrnames):
415         .word   _C_LABEL(intrnames)
417         .data
419         .globl  _C_LABEL(intrnames), _C_LABEL(eintrnames), _C_LABEL(intrcnt), _C_LABEL(sintrcnt), _C_LABEL(eintrcnt)
420 _C_LABEL(intrnames):
421         .asciz  "interrupt  0 "
422         .asciz  "interrupt  1 " /* reserved0 */
423         .asciz  "interrupt  2 "
424         .asciz  "interrupt  3 "
425         .asciz  "interrupt  4 "
426         .asciz  "interrupt  5 "
427         .asciz  "interrupt  6 "
428         .asciz  "interrupt  7 " /* reserved1 */
429         .asciz  "interrupt  8 " /* reserved2 */
430         .asciz  "interrupt  9 "
431         .asciz  "interrupt 10 "
432         .asciz  "interrupt 11 "
433         .asciz  "interrupt 12 "
434         .asciz  "interrupt 13 "
435         .asciz  "interrupt 14 "
436         .asciz  "interrupt 15 "
437         .asciz  "dma channel 0"
438         .asciz  "dma channel 1"
439         .asciz  "dma channel 2"
440         .asciz  "dma channel 3"
441         .asciz  "interrupt 20 "
442         .asciz  "interrupt 21 "
443         .asciz  "reserved 3   "
444         .asciz  "reserved 4   "
445         .asciz  "exp card 0   "
446         .asciz  "exp card 1   "
447         .asciz  "exp card 2   "
448         .asciz  "exp card 3   "
449         .asciz  "exp card 4   "
450         .asciz  "exp card 5   "
451         .asciz  "exp card 6   "
452         .asciz  "exp card 7   "
454 _C_LABEL(sintrnames):
455         .asciz  "softclock    "
456         .asciz  "softnet      "
457         .asciz  "softserial   "
458         .asciz  "softintr  3  "
459         .asciz  "softintr  4  "
460         .asciz  "softintr  5  "
461         .asciz  "softintr  6  "
462         .asciz  "softintr  7   "
463         .asciz  "softintr  8  "
464         .asciz  "softintr  9  "
465         .asciz  "softintr 10  "
466         .asciz  "softintr 11  "
467         .asciz  "softintr 12  "
468         .asciz  "softintr 13  "
469         .asciz  "softintr 14  "
470         .asciz  "softintr 15  "
471         .asciz  "softintr 16  "
472         .asciz  "softintr 17  "
473         .asciz  "softintr 18  "
474         .asciz  "softintr 19  "
475         .asciz  "softintr 20  "
476         .asciz  "softintr 21  "
477         .asciz  "softintr 22  "
478         .asciz  "softintr 23  "
479         .asciz  "softintr 24  "
480         .asciz  "softintr 25  "
481         .asciz  "softintr 26  "
482         .asciz  "softintr 27  "
483         .asciz  "softintr 28  "
484         .asciz  "softintr 29  "
485         .asciz  "softintr 30  "
486         .asciz  "softintr 31  "
487 _C_LABEL(eintrnames):
489         .bss
490         .align  0
491 _C_LABEL(intrcnt):
492         .space  32*4    /* XXX Should be linked to number of interrupts */
494 _C_LABEL(sintrcnt):
495         .space  32*4    /* XXX Should be linked to number of interrupts */
496 _C_LABEL(eintrcnt):
498 #else   /* IRQSTATS */
499         /* Dummy entries to keep vmstat happy */
501         .text
502         .globl  _C_LABEL(intrnames), _C_LABEL(eintrnames), _C_LABEL(intrcnt), _C_LABEL(eintrcnt)
503 _C_LABEL(intrnames):
504         .long   0
505 _C_LABEL(eintrnames):
507 _C_LABEL(intrcnt):
508         .long   0
509 _C_LABEL(eintrcnt):
510 #endif  /* IRQSTATS */