| 1 | /* Copyright (C) 2007 the NxOS developers |
|---|
| 2 | * |
|---|
| 3 | * See AUTHORS for a full list of the developers. |
|---|
| 4 | * |
|---|
| 5 | * Redistribution of this file is permitted under |
|---|
| 6 | * the terms of the GNU Public License (GPL) version 2. |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | .text |
|---|
| 10 | .code 32 |
|---|
| 11 | .align 0 |
|---|
| 12 | |
|---|
| 13 | #include "asm_decls.h" |
|---|
| 14 | |
|---|
| 15 | /* |
|---|
| 16 | * Definition of a few constants, for readability. |
|---|
| 17 | */ |
|---|
| 18 | #define AIC_IVR 0xFFFFF100 /* AIC Interrupt Vector Register */ |
|---|
| 19 | #define AIC_EOICR 0xFFFFF130 /* End Of Interrupt Control Register */ |
|---|
| 20 | |
|---|
| 21 | /********************************************************** |
|---|
| 22 | * Default handlers for unknown IRQ and FIQ interrupts, and |
|---|
| 23 | * the spurious IRQ handler and unknown exception handler. |
|---|
| 24 | */ |
|---|
| 25 | .global nx__default_irq |
|---|
| 26 | .global nx__default_fiq |
|---|
| 27 | .global nx__spurious_irq |
|---|
| 28 | .global nx__unhandled_exception |
|---|
| 29 | nx__default_irq: |
|---|
| 30 | nx__default_fiq: |
|---|
| 31 | nx__spurious_irq: |
|---|
| 32 | nx__unhandled_exception: |
|---|
| 33 | b nx__unhandled_exception |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | /********************************************************** |
|---|
| 37 | * IRQ entry/exit routine. This gets called for all |
|---|
| 38 | * interrupts, saves state, switches to Supervisor mode, |
|---|
| 39 | * enables nested IRQ handling, and calls the service |
|---|
| 40 | * routing that was registered with the AIC for the given |
|---|
| 41 | * IRQ. |
|---|
| 42 | */ |
|---|
| 43 | .global nx__irq_handler |
|---|
| 44 | nx__irq_handler: |
|---|
| 45 | /* Save the return address and return processor mode registers |
|---|
| 46 | * on the IRQ stack. This is needed to allow nested interrupts, |
|---|
| 47 | * where a higher-priority interrupt halts execution of a lower |
|---|
| 48 | * priority interrupt. |
|---|
| 49 | */ |
|---|
| 50 | sub lr, lr, #4 |
|---|
| 51 | stmfd sp!, {lr} |
|---|
| 52 | mrs lr, spsr |
|---|
| 53 | stmfd sp!, {lr} |
|---|
| 54 | |
|---|
| 55 | /* If we were interrupted out of User or System mode, there is a |
|---|
| 56 | * scheduler running, and we need to save the task state nicely. |
|---|
| 57 | * |
|---|
| 58 | * This code is beautiful. An ode of joy to everything ARM. Conditional |
|---|
| 59 | * execution, status bit updates, the next few instructions |
|---|
| 60 | * have it all. |
|---|
| 61 | * |
|---|
| 62 | * Note that each code path pushes one final value on the |
|---|
| 63 | * stack. This value is 0 if this interrupt is nested, and !0 if |
|---|
| 64 | * this interrupt is interrupting a user task. This value will be |
|---|
| 65 | * used to figure out how to return from the interrupt later. |
|---|
| 66 | */ |
|---|
| 67 | |
|---|
| 68 | /* Compare the SPSR mode bits to both 0000 and 1111 (usr/sys mode). */ |
|---|
| 69 | ands lr, lr, #7 |
|---|
| 70 | eornes lr, lr, #7 |
|---|
| 71 | |
|---|
| 72 | /* Switch to either sys or svc mode to save the state. */ |
|---|
| 73 | msreq cpsr_c, #(MODE_SYS | IRQ_FIQ_MASK) |
|---|
| 74 | msrne cpsr_c, #(MODE_SVC | IRQ_FIQ_MASK) |
|---|
| 75 | |
|---|
| 76 | /* Save the state. If we're saving a user task, also grab the task |
|---|
| 77 | * CPSR and PC from the IRQ stack. |
|---|
| 78 | */ |
|---|
| 79 | stmfd sp!, {r0-r12,lr} |
|---|
| 80 | ldreq r0, =__irq_stack__ |
|---|
| 81 | ldmeqdb r0, {r1,r2} |
|---|
| 82 | stmeqfd sp!, {r1,r2} |
|---|
| 83 | movne r0, #0 |
|---|
| 84 | |
|---|
| 85 | /* Get the IVR value. */ |
|---|
| 86 | ldr r1, =AIC_IVR |
|---|
| 87 | ldr r2, [r1] |
|---|
| 88 | |
|---|
| 89 | /* If we're in Protected mode (usually for JTAG debugging), we |
|---|
| 90 | * need to write back to the IVR register to tell the AIC it |
|---|
| 91 | * can dispatch other higher priority interrupts again. |
|---|
| 92 | * In normal mode, this has no effect, so we can safely do it. |
|---|
| 93 | */ |
|---|
| 94 | str r1, [r1] |
|---|
| 95 | |
|---|
| 96 | /* Switch to Supervisor mode if necessary, reenable IRQ and FIQ handling, |
|---|
| 97 | * and stack either 0 (nested IRQ) or the address of the IRQ stack base (for |
|---|
| 98 | * a task IRQ). |
|---|
| 99 | */ |
|---|
| 100 | msr cpsr_c, #MODE_SVC |
|---|
| 101 | stmfd sp!, {r0} |
|---|
| 102 | |
|---|
| 103 | /* Dispatch the IRQ to the registered handler. */ |
|---|
| 104 | mov lr, pc |
|---|
| 105 | bx r2 |
|---|
| 106 | |
|---|
| 107 | /* Restore the interrupted state. How this is done depends on the value at |
|---|
| 108 | * the top of the stack, as explained above. |
|---|
| 109 | * |
|---|
| 110 | * Note that we inhibit IRQ and FIQ handling during task restoration only if |
|---|
| 111 | * we're restoring a user task. Getting interrupted while we're tweaking around |
|---|
| 112 | * in system mode would confuse the IRQ handler into breaking things. |
|---|
| 113 | */ |
|---|
| 114 | ldmfd sp!, {r0} |
|---|
| 115 | cmp r0, #0 |
|---|
| 116 | msrne cpsr_c, #(MODE_SYS | IRQ_FIQ_MASK) |
|---|
| 117 | ldrne r0, =__irq_stack__ |
|---|
| 118 | ldmnefd sp!, {r1,r2} |
|---|
| 119 | stmnedb r0, {r1,r2} |
|---|
| 120 | ldmfd sp!, {r0-r12,lr} |
|---|
| 121 | |
|---|
| 122 | /* Switch back to IRQ mode and tell the AIC that the interrupt has been |
|---|
| 123 | * handled. |
|---|
| 124 | */ |
|---|
| 125 | msr cpsr_c, #(MODE_IRQ | IRQ_FIQ_MASK) |
|---|
| 126 | ldr lr, =AIC_EOICR |
|---|
| 127 | str lr, [lr] |
|---|
| 128 | |
|---|
| 129 | /* Restore the SPSR */ |
|---|
| 130 | ldmfd sp!, {lr} |
|---|
| 131 | msr spsr_all, lr |
|---|
| 132 | |
|---|
| 133 | /* Restore execution to the main code. */ |
|---|
| 134 | ldmfd sp!, {pc}^ |
|---|
| 135 | |
|---|
| 136 | |
|---|
| 137 | /********************************************************** |
|---|
| 138 | * Abort entry points. These get run when the CPU enters |
|---|
| 139 | * prefetch or data abort modes. These handlers just set |
|---|
| 140 | * up the necessary arguments and invoke nx__abort(). |
|---|
| 141 | */ |
|---|
| 142 | .extern nx__abort |
|---|
| 143 | .global nx__prefetch_abort_handler |
|---|
| 144 | nx__prefetch_abort_handler: |
|---|
| 145 | sub r1, lr, #4 |
|---|
| 146 | mov r0, #0 |
|---|
| 147 | mrs r2, spsr |
|---|
| 148 | b nx__abort |
|---|
| 149 | |
|---|
| 150 | .global nx__data_abort_handler |
|---|
| 151 | nx__data_abort_handler: |
|---|
| 152 | sub r1, lr, #8 |
|---|
| 153 | mov r0, #1 |
|---|
| 154 | mrs r2, spsr |
|---|
| 155 | b nx__abort |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | /********************************************************** |
|---|
| 159 | * Nested interrupt disable/enable routines. The |
|---|
| 160 | * interrupts_disable routine disables IRQ/FIQ handling, |
|---|
| 161 | * and increments a nesting counter. The interrupts_enable |
|---|
| 162 | * routine decrements the counter, and reenables interrupts |
|---|
| 163 | * when the counter reaches zero. |
|---|
| 164 | * |
|---|
| 165 | * When the system boots, the internal counter is set to |
|---|
| 166 | * interrupts disabled with 1 level of nesting (ie. calling |
|---|
| 167 | * interrupts_enable once will enable interrupts). |
|---|
| 168 | */ |
|---|
| 169 | .global nx_interrupts_disable |
|---|
| 170 | .global nx_interrupts_enable |
|---|
| 171 | nx_interrupts_disable: |
|---|
| 172 | mrs r0, cpsr |
|---|
| 173 | orr r0, r0, #IRQ_FIQ_MASK |
|---|
| 174 | msr cpsr_c, r0 |
|---|
| 175 | |
|---|
| 176 | ldr r1, =interrupts_count |
|---|
| 177 | ldr r0, [r1] |
|---|
| 178 | add r0, r0, #1 |
|---|
| 179 | str r0, [r1] |
|---|
| 180 | |
|---|
| 181 | bx lr |
|---|
| 182 | |
|---|
| 183 | nx_interrupts_enable: |
|---|
| 184 | ldr r0, =interrupts_count |
|---|
| 185 | ldr r1, [r0] |
|---|
| 186 | subs r1, r1, #1 |
|---|
| 187 | str r1, [r0] |
|---|
| 188 | |
|---|
| 189 | bxne lr |
|---|
| 190 | |
|---|
| 191 | mrs r0, cpsr |
|---|
| 192 | bic r0, r0, #IRQ_FIQ_MASK |
|---|
| 193 | msr cpsr_c, r0 |
|---|
| 194 | bx lr |
|---|
| 195 | |
|---|
| 196 | interrupts_count: .long 1 |
|---|