1/* $NetBSD: sa11x0_irq.S,v 1.2 2002/04/12 18:50:32 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 1998 Mark Brinicombe. 5 * Copyright (c) 1998 Causality Limited 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to the NetBSD Foundation 9 * by IWAMOTO Toshihiro. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Mark Brinicombe 22 * for the NetBSD Project. 23 * 4. The name of the company nor the name of the author may be used to 24 * endorse or promote products derived from this software without specific 25 * prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 29 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40#include "opt_irqstats.h" 41 42#include "assym.h" 43#include <machine/asm.h> 44#include <machine/cpu.h> 45#include <machine/frame.h> 46#include <hpcarm/sa11x0/sa11x0_reg.h> 47 48 49 .text 50 .align 0 51 52Lcurrent_spl_level: 53 .word _C_LABEL(current_spl_level) 54 55Lcurrent_intr_depth: 56 .word _C_LABEL(current_intr_depth) 57 58Lspl_masks: 59 .word _C_LABEL(spl_masks) 60 61 .globl _C_LABEL(saipic_base) 62_C_LABEL(saipic_base): 63 .word 0x00000000 64 65#ifdef INTR_DEBUG 66Ldbg_str: 67 .asciz "irq_entry %x %x\n" 68#endif 69 70/* 71 * Regsister usage 72 * 73 * r6 - Address of current handler 74 * r7 - Pointer to handler pointer list 75 * r8 - Current IRQ requests. 76 * r9 - Used to count through possible IRQ bits. 77 * r10 - Base address of SAIP 78 */ 79 80ASENTRY_NP(irq_entry) 81 sub lr, lr, #0x00000004 /* Adjust the lr */ 82 83 PUSHFRAMEINSVC /* Push an interrupt frame */ 84 85 /* Load r8 with the SAIPIC interrupt requests */ 86 87 ldr r10, [pc, #_C_LABEL(saipic_base) - . - 8] 88 ldr r8, [r10, #(SAIPIC_IP)] /* Load IRQ pending register */ 89 90#ifdef INTR_DEBUG 91 ldr r2, [r10, #(SAIPIC_MR)] 92 add r0, pc, #Ldbg_str - . - 8 93 mov r1, r8 94 bl _C_LABEL(printf) 95#endif 96 /* 97 * Note that we have entered the IRQ handler. 98 * We are in SVC mode so we cannot use the processor mode 99 * to determine if we are in an IRQ. Instead we will count the 100 * each time the interrupt handler is nested. 101 */ 102 103 ldr r0, Lcurrent_intr_depth 104 ldr r1, [r0] 105 add r1, r1, #1 106 str r1, [r0] 107 108 /* 109 * Need to block all interrupts at the IPL or lower for 110 * all asserted interrupts. 111 * This basically emulates hardware interrupt priority levels. 112 * Means we need to go through the interrupt mask and for 113 * every asserted interrupt we need to mask out all other 114 * interrupts at the same or lower IPL. 115 * If only we could wait until the main loop but we need to sort 116 * this out first so interrupts can be re-enabled. 117 * 118 * This would benefit from a special ffs type routine 119 */ 120 121 mov r9, #(_SPL_LEVELS - 1) 122 ldr r7, Lspl_masks 123 124Lfind_highest_ipl: 125 ldr r2, [r7, r9, lsl #2] 126 tst r8, r2 127 subeq r9, r9, #1 128 beq Lfind_highest_ipl 129 130 /* r9 = SPL level of highest priority interrupt */ 131 add r9, r9, #1 132 ldr r2, [r7, r9, lsl #2] 133 mvn r2, r2 134 135 ldr r0, Lcurrent_spl_level 136 ldr r1, [r0] 137 str r9, [r0] 138 stmfd sp!, {r1} 139 140 /* Update the SAIP irq masks */ 141 bl _C_LABEL(irq_setmasks) 142 143#ifdef INTR_DEBUG 144 stmfd sp!, {r0,r1,r2} 145 add r0, pc, #Ldbg_str - . - 8 146 mov r1, #1 147 mov r2, r9 148 bl _C_LABEL(printf) 149 ldmia sp!, {r0,r1,r2} 150#endif 151 mrs r0, cpsr_all /* Enable IRQ's */ 152 bic r0, r0, #I32_bit 153 msr cpsr_all, r0 154 155 ldr r7, [pc, #Lirqhandlers - . - 8] 156 mov r9, #0x00000001 157 158irqloop: 159 /* This would benefit from a special ffs type routine */ 160 tst r8, r9 /* Is a bit set ? */ 161 beq nextirq /* No ? try next bit */ 162 163 ldr r6, [r7] /* Get address of first handler structure */ 164 165 teq r6, #0x00000000 /* Do we have a handler */ 166 moveq r0, r8 /* IRQ requests as arg 0 */ 167 beq _C_LABEL(stray_irqhandler) /* call special handler */ 168 169 ldr r0, Lcnt /* Stat info */ 170 ldr r1, [r0, #(V_INTR)] 171 add r1, r1, #0x00000001 172 str r1, [r0, #(V_INTR)] 173 174 /* 175 * XXX: Should stats be accumlated for every interrupt routine 176 * called or for every physical interrupt that is serviced. 177 */ 178 179#ifdef IRQSTATS 180 ldr r0, Lintrcnt 181 ldr r1, [r6, #(IH_COUNT)] 182 183 add r0, r0, r1, lsl #2 184 ldr r1, [r0] 185 add r1, r1, #0x00000001 186 str r1, [r0] 187#endif /* IRQSTATS */ 188 189irqchainloop: 190#ifdef INTR_DEBUG 191 stmfd sp!, {r0,r1,r2} 192 add r0, pc, #Ldbg_str - . - 8 193 mov r1, #2 194 bl _C_LABEL(printf) 195 ldmia sp!, {r0,r1,r2} 196#endif 197 add lr, pc, #nextinchain - . - 8 /* return address */ 198 ldr r0, [r6, #(IH_ARG)] /* Get argument pointer */ 199 teq r0, #0x00000000 /* If arg is zero pass stack frame */ 200 addeq r0, sp, #4 /* ... stack frame [XXX needs care] */ 201 ldr pc, [r6, #(IH_FUNC)] /* Call handler */ 202 203nextinchain: 204 teq r0, #0x00000001 /* Was the irq serviced ? */ 205 beq irqdone 206 207 ldr r6, [r6, #(IH_NEXT)] 208 teq r6, #0x00000000 209 bne irqchainloop 210 211irqdone: 212nextirq: 213 add r7, r7, #0x00000004 /* update pointer to handlers */ 214 mov r9, r9, lsl #1 /* move on to next bit */ 215 teq r9, #(1 << 31) /* done the last bit ? */ 216 bne irqloop /* no - loop back. */ 217 218 ldmfd sp!, {r2} 219 ldr r1, Lcurrent_spl_level 220 str r2, [r1] 221 222 /* Restore previous disabled mask */ 223 bl _C_LABEL(irq_setmasks) 224 225 bl _C_LABEL(dosoftints) /* Handle the soft interrupts */ 226 227 /* Manage AST's. Maybe this should be done as a soft interrupt ? */ 228 ldr r0, [sp] /* Get the SPSR from stack */ 229 230 and r0, r0, #(PSR_MODE) /* Test for USR32 mode before the IRQ */ 231 teq r0, #(PSR_USR32_MODE) 232 ldreq r0, Lastpending /* Do we have an AST pending ? */ 233 ldreq r1, [r0] 234 teqeq r1, #0x00000001 235 236 beq irqast /* call the AST handler */ 237 238 /* Kill IRQ's in preparation for exit */ 239 mrs r0, cpsr_all 240 orr r0, r0, #(I32_bit) 241 msr cpsr_all, r0 242 243#ifdef INTR_DEBUG 244 add r0, pc, #Ldbg_str - . - 8 245 mov r1, #3 246 ldr r2, [r10, #(SAIPIC_MR)] 247 bl _C_LABEL(printf) 248#endif 249 250 /* Decrement the nest count */ 251 ldr r0, Lcurrent_intr_depth 252 ldr r1, [r0] 253 sub r1, r1, #1 254 str r1, [r0] 255 256 PULLFRAMEFROMSVCANDEXIT 257 258 /* NOT REACHED */ 259 b . - 8 260 261 /* 262 * Ok, snag with current intr depth ... 263 * If ast() calls mi_sleep() the current_intr_depth will not be 264 * decremented until the process is woken up. This can result 265 * in the system believing it is still in the interrupt handler. 266 * If we are calling ast() then correct the current_intr_depth 267 * before the call. 268 */ 269irqast: 270 mov r1, #0x00000000 /* Clear ast_pending */ 271 str r1, [r0] 272 273 /* Kill IRQ's so we atomically decrement current_intr_depth */ 274 mrs r2, cpsr_all 275 orr r3, r2, #(I32_bit) 276 msr cpsr_all, r3 277 278 /* Decrement the interrupt nesting count */ 279 ldr r0, Lcurrent_intr_depth 280 ldr r1, [r0] 281 sub r1, r1, #1 282 str r1, [r0] 283 284 /* Restore IRQ's */ 285 msr cpsr_all, r2 286 287 mov r0, sp 288 bl _C_LABEL(ast) 289 290 /* Kill IRQ's in preparation for exit */ 291 mrs r0, cpsr_all 292 orr r0, r0, #(I32_bit) 293 msr cpsr_all, r0 294 295 PULLFRAMEFROMSVCANDEXIT 296 297 /* NOT REACHED */ 298 b . - 8 299 300 301ENTRY(irq_setmasks) 302 /* Disable interrupts */ 303 mrs r3, cpsr_all 304 orr r1, r3, #(I32_bit) 305 msr cpsr_all, r1 306 307 /* Calculate interrupt mask */ 308 ldr r0, Lspl_masks 309 ldr r2, Lcurrent_spl_level 310 ldr r2, [r2] 311 ldr r2, [r0, r2, lsl #2] 312 313 ldr r0, [pc, #_C_LABEL(saipic_base) - . - 8] 314 str r2, [r0, #(SAIPIC_MR)] /* Set mask register */ 315 316 /* Restore old cpsr and exit */ 317 msr cpsr_all, r3 318 mov pc, lr 319 320Lcnt: 321 .word _C_LABEL(uvmexp) 322 323#ifdef IRQSTATS 324Lintrcnt: 325 .word _C_LABEL(intrcnt) 326#endif 327 328Lirqhandlers: 329 .word _C_LABEL(irqhandlers) /* Pointer to array of irqhandlers */ 330 331Lastpending: 332 .word _C_LABEL(astpending) 333 334 335 336#ifdef IRQSTATS 337 .globl _C_LABEL(intrcnt), _C_LABEL(sintrcnt) 338 339_C_LABEL(intrcnt): 340 .space ICU_LEN*4 /* XXX Should be linked to number of interrupts */ 341 342_C_LABEL(sintrcnt): 343 .space 32*4 344#endif 345