summaryrefslogtreecommitdiffstats
path: root/cpukit/score/cpu/arm/arm_exc_interrupt.S
diff options
context:
space:
mode:
authorThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-07-17 15:16:50 +0000
committerThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-07-17 15:16:50 +0000
commit9364cf663fc1dcebcc1b87cc4cec7beae5490031 (patch)
treea3788a17e3f6a72264a9bde798274805c56d9257 /cpukit/score/cpu/arm/arm_exc_interrupt.S
parentremove obsolete files (diff)
downloadrtems-9364cf663fc1dcebcc1b87cc4cec7beae5490031.tar.bz2
adding lpc24xx BSP parts
Diffstat (limited to 'cpukit/score/cpu/arm/arm_exc_interrupt.S')
-rw-r--r--cpukit/score/cpu/arm/arm_exc_interrupt.S229
1 files changed, 229 insertions, 0 deletions
diff --git a/cpukit/score/cpu/arm/arm_exc_interrupt.S b/cpukit/score/cpu/arm/arm_exc_interrupt.S
new file mode 100644
index 0000000000..7cfd56ff00
--- /dev/null
+++ b/cpukit/score/cpu/arm/arm_exc_interrupt.S
@@ -0,0 +1,229 @@
+/**
+ * @file
+ *
+ * @brief ARM interrupt exception prologue and epilogue.
+ */
+
+/*
+ * Copyright (c) 2009
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+/*
+ * These two non-volatile registers contain the program status for INT and SVC
+ * mode. It is important that they are directly accessible in THUMB
+ * instruction mode.
+ */
+#define MODE_INT r4
+#define MODE_SVC r5
+
+/*
+ * These three non-volatile registers are used to exchange information between
+ * INT and SVC mode.
+ */
+#define SCRATCH_0 r6
+#define SCRATCH_1 r7
+#define SCRATCH_2 r8
+
+/* List of scratch registers. They will be saved and restored in INT mode. */
+#define SCRATCH_LIST {SCRATCH_0, SCRATCH_1, SCRATCH_2}
+
+/*
+ * List of all volatile registers (r0, r1, r2, r3, r12 and lr), registers
+ * containing the interrupt context (return address in SCRATCH_0 and saved
+ * program status in SCRATCH_1) and non-volatile registers used for modes
+ * (MODE_INT and MODE_SVC). They will be saved and restored in SVC mode.
+ */
+#define TASK_CONTEXT_LIST \
+ {r0, r1, r2, r3, MODE_INT, MODE_SVC, SCRATCH_0, SCRATCH_1, r12, lr}
+
+/*
+ * List of all volatile registers (r0, r1, r2, r3, r12 and lr) and the saved
+ * program status (SCRATCH_0 register). They will be saved and restored in INT
+ * mode.
+ */
+#define INTERRUPT_CONTEXT_LIST \
+ {r0, r1, r2, r3, SCRATCH_0, r12, lr}
+
+.extern _ISR_Thread_dispatch
+.extern _ISR_Nest_level
+.extern _Thread_Dispatch_disable_level
+.extern bsp_interrupt_dispatch
+
+.arm
+.globl arm_exc_interrupt
+arm_exc_interrupt:
+
+ /* Save scratch registers on INT stack */
+ stmdb sp!, SCRATCH_LIST
+
+ /* Increment interrupt nest level */
+ ldr SCRATCH_0, =_ISR_Nest_level
+ ldr SCRATCH_1, [SCRATCH_0]
+ add SCRATCH_2, SCRATCH_1, #1
+ str SCRATCH_2, [SCRATCH_0]
+
+ /* Branch for nested interrupts */
+ cmp SCRATCH_1, #0
+ bne nested_interrupt_context_save
+
+ /* Move interrupt context and CPSR to scratch registers */
+ mov SCRATCH_0, lr
+ mrs SCRATCH_1, spsr
+ mrs SCRATCH_2, cpsr
+
+ /* Switch to SVC mode */
+ orr SCRATCH_2, SCRATCH_2, #0x1
+ msr cpsr_c, SCRATCH_2
+
+ /* Save context on SVC stack */
+ stmdb sp!, TASK_CONTEXT_LIST
+
+ /* Save SVC mode program status to non-volatile register */
+ mov MODE_SVC, SCRATCH_2
+
+ /* Save INT mode program status to non-volatile register */
+ bic MODE_INT, MODE_SVC, #0x1
+
+ /* Switch to INT mode */
+ msr cpsr_c, MODE_INT
+
+ /* Restore scratch registers from INT stack */
+ ldmia sp!, SCRATCH_LIST
+
+ /*
+ * At this point the INT stack is in the exception entry state and
+ * contains no data for us. The context is saved on the SVC stack. We
+ * can easily switch modes via the registers MODE_INT and MODE_SVC
+ * which are valid through subroutine calls. We can use all volatile
+ * registers in both modes. Note that this comment describes the non
+ * nested interrupt entry. For a nested interrupt things are
+ * different, since we can save everything on the INT stack and there
+ * is no need to switch modes.
+ */
+
+task_context_save_done:
+
+ /* Switch to THUMB instructions if necessary */
+#ifdef __thumb__
+ add r0, pc, #1
+ bx r0
+.thumb
+#endif /* __thumb__ */
+
+ /* Increment thread dispatch disable level */
+ ldr r1, =_Thread_Dispatch_disable_level
+ ldr r3, [r1]
+ add r3, r3, #1
+ str r3, [r1]
+
+ /* Call BSP dependent interrrupt dispatcher */
+ bl bsp_interrupt_dispatch
+
+ /* Decrement interrupt nest and thread dispatch disable level */
+ ldr r0, =_ISR_Nest_level
+ ldr r1, =_Thread_Dispatch_disable_level
+ ldr r2, [r0]
+ ldr r3, [r1]
+ sub r2, r2, #1
+ sub r3, r3, #1
+ str r2, [r0]
+ str r3, [r1]
+
+ /* Switch to ARM instructions if necessary */
+#ifdef __thumb__
+.align 2
+ bx pc
+.arm
+#endif /* __thumb__ */
+
+ /* Branch if we have a nested interrupt */
+ cmp r2, #0
+ bne nested_interrupt_return
+
+ /* Branch if thread dispatching is disabled */
+ cmp r3, #0
+ bne thread_dispatch_done
+
+ /*
+ * Switch to SVC mode. It is important to call the thread dispatcher
+ * in SVC mode since overwise the INT stack may need to store an
+ * arbitrary number of contexts and it may lead to an invalid order of
+ * stack operations.
+ */
+ msr cpsr_c, MODE_SVC
+
+ /* Call thread dispatcher */
+#ifdef __thumb__
+ ldr r0, =_ISR_Thread_dispatch
+ mov lr, pc
+ bx r0
+.thumb
+ bx pc
+ nop
+.arm
+#else
+ bl _ISR_Thread_dispatch
+#endif
+
+ /* Switch to INT mode */
+ msr cpsr_c, MODE_INT
+
+thread_dispatch_done:
+
+ /* Save scratch registers on INT stack */
+ stmdb sp!, SCRATCH_LIST
+
+ /* Switch to SVC mode */
+ msr cpsr_c, MODE_SVC
+
+ /* Move INT mode program status to scratch register */
+ mov SCRATCH_2, MODE_INT
+
+ /* Restore context from SVC stack */
+ ldmia sp!, TASK_CONTEXT_LIST
+
+ /* Switch to INT mode */
+ msr cpsr_c, SCRATCH_2
+
+ /* Restore interrupt context */
+ mov lr, SCRATCH_0
+ msr spsr, SCRATCH_1
+
+ /* Restore scratch registers from INT stack */
+ ldmia sp!, SCRATCH_LIST
+
+ /* Return from interrupt */
+ subs pc, lr, #4
+
+nested_interrupt_context_save:
+
+ /* Move saved program status register to scratch register */
+ mrs SCRATCH_0, spsr
+
+ /* Save context on INT stack */
+ stmdb sp!, INTERRUPT_CONTEXT_LIST
+
+ b task_context_save_done
+
+nested_interrupt_return:
+
+ /* Restore context from INT stack */
+ ldmia sp!, INTERRUPT_CONTEXT_LIST
+
+ /* Restore saved program status register */
+ msr spsr, SCRATCH_0
+
+ /* Restore scratch registers from INT stack */
+ ldmia sp!, SCRATCH_LIST
+
+ /* Return from interrupt */
+ subs pc, lr, #4