4 * Copyright (c) 1994-1998 Mark Brinicombe.
5 * Copyright (c) 1994 Brini.
8 * This code is derived from software written for Brini by Mark Brinicombe
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
37 * Low level irq and fiq handlers
43 #include <machine/asm.h>
44 #include <machine/cpu.h>
45 #include <machine/frame.h>
46 #include <machine/irqhandler.h>
55 * Main entry point for the IRQ vector
57 * This function is called only on timer ticks, passed on to the
58 * kernel from the OFW tick handler.
60 * For now, I am trying to re-use as much of the code from the
61 * IOMD interrupt-handler as possible. In time, I will strip this
62 * down to something OFW-specific.
64 * Here's the original, IOMD-specific description:
65 * This function reads the irq request bits in the IOMD registers
66 * IRQRQA, IRQRQB and DMARQ
67 * It then calls an installed handler for each bit that is set.
68 * The function stray_irqhandler is called if a handler is not defined
69 * for a particular interrupt.
70 * If a interrupt handler is found then it is called with r0 containing
71 * the argument defined in the handler structure. If the field ih_arg
72 * is zero then a pointer to the IRQ frame on the stack is passed instead.
76 .word _C_LABEL(disabled_mask)
79 .word _C_LABEL(spl_masks)
82 .word _C_LABEL(ofw_ticktmp)
87 Lofwirqstk: /* hack */
88 .word ofwirqstk + 4096
92 AST_ALIGNMENT_FAULT_LOCALS
97 * r4 - Address of cpu_info
98 * r6 - Address of current handler
99 * r7 - Pointer to handler pointer list
100 * r8 - Current IRQ requests.
101 * r9 - Used to count through possible IRQ bits.
102 * r10 - Base address of IOMD
105 ASENTRY_NP(irq_entry)
107 * We come here following an OFW-handled timer tick.
109 * We are in the SVC frame, and interrupts are disabled.
110 * The state of the interrupted context is partially in
111 * the registers and partially in the global storage area
112 * labeled ofw_ticktmp. ofw_ticktmp is filled-in by the
113 * tick callback that is invoked by OFW on the way out of
114 * its interrupt handler. ofw_ticktmp contains the following:
116 * pc // interrupted instruction
119 * r1 // makes r1 available for scratch
120 * r0 // makes r0 available for scratch
121 * spsr_svc // cpsr of interrupted context
123 * The prologue of this routine must re-construct the
124 * machine state that existed at the time OFW's interrupt-
125 * handler fielded the interrupt. That allows us to use
126 * the rest of the code in this routine, and have it all
131 * Switch to IRQ mode.
132 * First check the spsr in ofw_ticktmp to see what the FIQ bit should be.
134 * I need 2 scratch registers to do this.
135 * Fortunately, r0 and r1 are already saved in ofw_ticktmp.
141 mov r1, #(I32_bit | PSR_IRQ32_MODE)
145 /* Now we're in IRQ mode. */
146 /* Restore contents of ofw_ticktmp. */
147 adr r0, Lofwirqstk /* Bummer! Mitch hasn't left me a stack. */
148 ldr sp, [r0] /* I'll use my own for now... */
149 ldr r0, Lofw_ticktmp /* r0 now points to ofw_ticktmp[0] */
150 ldr r1, [r0], #(4*3) /* skip over saved {r0, r1} */
151 msr spsr_all, r1 /* restore spsr */
152 ldmia r0, {sp, lr}^ /* restore user sp and lr */
153 add r0, r0, #(4*2) /* previous instruction can't writeback */
154 /* this one can't use banked registers */
155 ldr lr, [r0], #(-4*4) /* restore pc; point r0 at ofw_ticktmp[1] */
156 add lr, lr, #4 /* pc += 4; will be decremented below */
157 ldmia r0, {r0, r1} /* restore r0 and r1 */
159 /* OK, the machine state should be identical now to that when */
160 /* OFW fielded the interrupt. So just fall through... */
162 sub lr, lr, #0x00000004 /* Adjust the lr */
164 PUSHFRAMEINSVC /* Push an interrupt frame */
167 * Can't field this interrupt now if priority is IPL_CLOCK
168 * or higher. For now, we'll just ignore the interrupt.
169 * Soon, we will have to schedule it for later action.
171 ldr r0, Lcurrent_spl_level
172 ldr r0, [r4, #CI_CPL]
176 PULLFRAMEFROMSVCANDEXIT
177 movs pc, lr /* Exit */
180 * Stuff a bit-mask into r8 indicating which interrupts
181 * are pending. In our case, that is just the timer0
182 * interrupt: (1 << TIMER0). The existing code will take
183 * care of invoking that handler and the softint/ast stuff
188 ldr r0, [sp] /* Fetch SPSR */
190 ENABLE_ALIGNMENT_FAULTS
192 mov r8, #0x00000001 /* timer interrupt pending! */
193 mov r8, r8, lsl #IRQ_TIMER0
196 * Note that we have entered the IRQ handler.
197 * We are in SVC mode so we cannot use the processor mode
198 * to determine if we are in an IRQ. Instead we will count the
199 * each time the interrupt handler is nested.
202 ldr r1, [r4, #CI_INTR_DEPTH]
204 str r1, [r4, #CI_INTR_DEPTH]
206 /* Block the current requested interrupts */
207 ldr r1, Ldisabled_mask
213 * Need to block all interrupts at the IPL or lower for
214 * all asserted interrupts.
215 * This basically emulates hardware interrupt priority levels.
216 * Means we need to go through the interrupt mask and for
217 * every asserted interrupt we need to mask out all other
218 * interrupts at the same or lower IPL.
219 * If only we could wait until the main loop but we need to sort
220 * this out first so interrupts can be re-enabled.
222 * This would benefit from a special ffs type routine
229 ldr r2, [r7, r9, lsl #2]
232 beq Lfind_highest_ipl
234 /* r9 = SPL level of highest priority interrupt */
236 ldr r2, [r7, r9, lsl #2]
242 ldr r0, Lcurrent_spl_level
243 ldr r1, [r4, #CI_CPL]
244 str r9, [r4, #CI_CPL]
247 /* Update the irq masks */
248 bl _C_LABEL(irq_setmasks)
250 mrs r0, cpsr_all /* Enable IRQ's */
258 /* This would benefit from a special ffs type routine */
259 tst r8, r9 /* Is a bit set ? */
260 beq nextirq /* No ? try next bit */
262 ldr r6, [r7] /* Get address of first handler structure */
264 teq r6, #0x00000000 /* Do we have a handler */
265 moveq r0, r8 /* IRQ requests as arg 0 */
266 beq _C_LABEL(stray_irqhandler) /* call special handler */
269 ldr r1, [r0, #(V_INTR)]
270 add r1, r1, #0x00000001
271 str r1, [r0, #(V_INTR)]
274 ldr r0, [r6, #(IH_ARG)] /* Get argument pointer */
275 teq r0, #0x00000000 /* If arg is zero pass stack frame */
276 addeq r0, sp, #8 /* ... stack frame */
277 mov lr, pc /* return address */
278 ldr pc, [r6, #(IH_FUNC)] /* Call handler */
280 teq r0, #0x00000001 /* Was the irq serviced ? */
283 ldr r6, [r6, #(IH_NEXT)]
289 add r3, r6, #IH_EV_COUNT /* get address of ih's ev_count */
290 ldmia r3, {r1-r2} /* load ev_count */
291 adds r1, r1, #0x00000001 /* 64bit incr (lo) */
292 adc r2, r2, #0x00000000 /* 64bit incr (hi) */
293 stmia r3, {r1-r2} /* store ev_count */
296 add r7, r7, #0x00000004 /* update pointer to handlers */
297 mov r9, r9, lsl #1 /* move on to next bit */
298 teq r9, #(1 << 24) /* done the last bit ? */
299 bne irqloop /* no - loop back. */
302 str r2, [r4, #CI_CPL]
304 /* Restore previous disabled mask */
306 ldr r1, Ldisabled_mask
308 bl _C_LABEL(irq_setmasks)
310 bl _C_LABEL(dosoftints) /* Handle the soft interrupts */
312 /* Kill IRQ's in preparation for exit */
314 orr r0, r0, #(I32_bit)
317 /* Decrement the nest count */
318 ldr r1, [r4, #CI_INTR_DEPTH]
320 str r1, [r4, #CI_INTR_DEPTH]
324 DO_AST_AND_RESTORE_ALIGNMENT_FAULTS
325 PULLFRAMEFROMSVCANDEXIT
326 movs pc, lr /* Exit */
329 .word _C_LABEL(current_mask) /* irq's that are usable */
338 .word _C_LABEL(uvmexp)
341 .word _C_LABEL(irqhandlers) /* Pointer to array of irqhandlers */
344 .global _C_LABEL(dotickgrovelling)
347 * Do magic to cause OFW to call our irq_entry
348 * routine when it returns from its tick-handling.
350 * This consists of two sub-tasks:
351 * - save some machine state in ofw_ticktmp
352 * - punch some new machine state into the
355 * We are running in the IRQ frame, with
356 * interrupts disabled.
358 * r0 - base of saved OFW interrupt frame, which
359 * has the following format:
361 * pc // interrupted instruction
362 * lr // lr of interrupted context
363 * sp // sp of interrupted context
365 * ... // non-banked register values
366 * ... // of interrupted context
368 * spsr // psr of interrupted context
372 _C_LABEL(dotickgrovelling):
373 /*assert((cpsr & PSR_MODE) == PSR_IRQ32_MODE);*/
375 stmfd sp!, {r1-r5} /* scratch registers r1-r5 */
380 * Our irq_entry routine needs to re-construct
381 * the state of the machine at the time OFW
382 * fielded the interrupt, so that we can use
383 * the rest of the standard interrupt-handling
384 * code. Specifically, irq_entry needs to get
385 * at the following machine state:
387 * pc // interrupted instruction
390 * r0-r12 // the non-banked registers
391 * // at the time of interruption
392 * spsr // cpsr of interrupted context
394 * The non-banked registers will be valid at the
395 * time irq_entry is called, but the other values
396 * will not be. We must save them here, in the
397 * ofw_ticktmp storage block. We also save r0
398 * and r1 so that we have some free registers
399 * when it's time to do the re-construction.
401 * Note that interrupts are not enabled before
402 * irq_entry is entered, so we don't have to
403 * worry about ofw_ticktmp getting clobbered.
405 ldr r1, Lofw_ticktmp /* r1 points to ofw_ticktmp[0] */
407 ldr r2, [r0, #0] /* ofwframe[0] is spsr */
408 stmia r1!, {r2} /* put it in ofw_ticktmp[0] */
410 ldr r2, [r0, #(4*1)] /* ofwframe[1] is saved r0 */
411 stmia r1!, {r2} /* put it in ofw_ticktmp[1] */
413 ldr r2, [r0, #(4*2)] /* ofwframe[2] is saved r1 */
414 stmia r1!, {r2} /* put it in ofw_ticktmp[2] */
416 stmia r1, {sp, lr}^ /* put {sp,lr}_usr in ofw_ticktmp[3,4]; */
417 /* the user registers are still valid */
418 /* because we haven't left IRQ mode */
419 add r1, r1, #(4*2) /* previous instruction can't writeback */
420 /* this one can't use banked registers */
422 ldr r2, [r0, #(4*16)] /* ofwframe[16] is pc */
423 stmia r1!, {r2} /* put it in ofw_ticktmp[5] */
429 * Diddle the OFW-supplied frame such that
430 * control passes to irq_entry when OFW does
431 * its return from interrupt. There are 4
432 * fields in that frame that we need to plug:
434 * pc // gets irq_entry
437 * spsr // gets (I32_bit | PSR_SVC32_MODE)
440 mov r1, #(I32_bit | PSR_SVC32_MODE)
441 str r1, [r0, #0] /* plug spsr */
443 /* Sneak into SVC mode to get sp and lr */
445 bic r3, r3, #(PSR_MODE)
446 orr r3, r3, #(PSR_SVC32_MODE)
448 mov r4, lr /* snarf lr_svc */
449 mov r5, sp /* snarf sp_svc */
450 bic r3, r3, #(PSR_MODE)
451 orr r3, r3, #(PSR_IRQ32_MODE)
453 str r5, [r0, #(4*14)] /* plug sp */
454 str r4, [r0, #(4*15)] /* plug lr */
457 str r1, [r0, #(4*16)] /* plug pc */
466 _C_LABEL(ofw_ticktmp):
467 .space 4 * 6 /* temporary storage for 6 words of machine state */
469 ofwirqstk: /* hack */