aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordave2007-01-03 09:27:58 +0000
committerdave2007-01-03 09:27:58 +0000
commitf863a32b28ca56e92ca816c45d9ac483f304d44a (patch)
treee1d1ab63bf7e7ce96fd22d7abc3e268801333c95
parenteccd14040f63d884a69d96e7646b062dc32bc023 (diff)
Refactor and flesh out AIC initialization. Add two helper functions to
manage interrupts from C code. These helpers stay loaded even after the payload has started.
-rw-r--r--crt0/crt0.h6
-rw-r--r--crt0/crt0_c.c61
-rw-r--r--crt0/crt0_s.S57
3 files changed, 100 insertions, 24 deletions
diff --git a/crt0/crt0.h b/crt0/crt0.h
new file mode 100644
index 0000000..b5ca7fa
--- /dev/null
+++ b/crt0/crt0.h
@@ -0,0 +1,6 @@
+/*
+ * Helper functions installed by the bootstrap code.
+ */
+
+extern void interrupts_disable();
+extern void interrupts_enable();
diff --git a/crt0/crt0_c.c b/crt0/crt0_c.c
index 0790d70..e22900e 100644
--- a/crt0/crt0_c.c
+++ b/crt0/crt0_c.c
@@ -74,10 +74,52 @@ static inline void init_clocks()
}
-void nxt_low_level_init()
-{
+/*
+ * Initialize the Advanced Interrupt Controller with stub handlers and
+ * all interrupts disabled.
+ */
+static inline void init_aic() {
int i;
+ /* Do some garbage collection on the AIC. All these are to protect
+ * against the case where we are coming from a warm boot. These
+ * values define the modes in which the AIC should be booting up.
+ *
+ * - Disable all peripheral interrupt lines.
+ * - Turn off Fast Forcing for all peripheral interrupt lines.
+ * - Clear all pending interrupts.
+ * - Tell the AIC that it is no longer handling any interrupt.
+ */
+ AT91C_BASE_AIC->AIC_IDCR = 0xFFFFFFFF;
+ AT91C_BASE_AIC->AIC_FFDR = 0xFFFFFFFF;
+ AT91C_BASE_AIC->AIC_ICCR = 0xFFFFFFFF;
+ AT91C_BASE_AIC->AIC_EOICR = 0xFFFFFFFF;
+
+ /* Since it can be useful for JTAG debugging, enable debug mode in
+ * the AIC. In this mode, reading the interrupt vector register will
+ * not unlock the AIC and let it trigger any higher priority
+ * IRQs. We will have to write back to the vector register to unlock
+ * the AIC (the low-level IRQ dispatcher routine does this already).
+ */
+ AT91C_BASE_AIC->AIC_DCR = 0x1;
+
+ /* Set up the AIC with the default handlers. The handlers at startup
+ * are undefined, which will cause undefined behavior if the kernel
+ * activates an interrupt line before configuring the handler.
+ */
+ AT91C_BASE_AIC->AIC_SVR[0] = (long int) nxt_default_fiq_handler;
+ for (i=1; i<31; i++) {
+ AT91C_BASE_AIC->AIC_SVR[i] = (long int) nxt_default_irq_handler;
+ }
+ AT91C_BASE_AIC->AIC_SPU = (long int) nxt_spurious_irq_handler;
+
+}
+
+
+/* Routine called by the low level bootstrapper assembly code to
+ * perform basic board initialization.
+ */
+void nxt_low_level_init() {
/* Configure the Flash controller with write speed settings. These
* settings are valid for writing everywhere but the non-volatile bits
* (lock, security, general-purpose NVM).
@@ -96,19 +138,8 @@ void nxt_low_level_init()
/* Start the board clocks and step up to 48MHz. */
init_clocks();
- /* Set up the Advanced Interrupt Controller with the default
- * handlers. The handlers at startup are undefined, which will cause
- * undefined behavior if the kernel activates an interrupt line
- * before configuring the handler.
- */
- AT91C_BASE_AIC->AIC_SVR[0] = (long int) nxt_default_fiq_handler;
- for (i=1; i<31; i++) {
- AT91C_BASE_AIC->AIC_SVR[i] = (long int) nxt_default_irq_handler;
- }
- AT91C_BASE_AIC->AIC_SPU = (long int) nxt_spurious_irq_handler;
-
- /* Initialize the reset controller, allowing hardware resets. */
- /* *AT91C_RSTC_RMR = 0x1 | (0x4 << 8); */
+ /* Initialize the Advanced Interrupt Controller. */
+ init_aic();
}
diff --git a/crt0/crt0_s.S b/crt0/crt0_s.S
index 59567cf..0f5503c 100644
--- a/crt0/crt0_s.S
+++ b/crt0/crt0_s.S
@@ -120,6 +120,53 @@ irq_dispatch:
/*
+ * Low level service routines that inhibit and enable interrupts in
+ * the CPU. These are provided as helpers to the OS, as C code cannot
+ * directly touch the core mode register.
+ */
+
+/* Disable IRQ and FIQ handling in the CPU. */
+interrupts_disable:
+ /* Get the interrupt counter and increment it. */
+ adr r1, interrupts_count
+ ldr r2, [r1]
+ adds r2, r2, #1
+ str r2, [r1]
+
+ /* If the add didn't cause an overflow, interrupts are already
+ * disabled. Return immediately.
+ */
+ bxvc lr
+
+ /* Otherwise, disable interrupts. */
+ mrs r0, CPSR
+ orr r0, r0, #(IRQ_MASK | FIQ_MASK)
+ msr CPSR_c, r0
+ bx lr
+
+/* Enable IRQ and FIQ handling in the CPU. */
+interrupts_enable:
+ adr r1, interrupts_count
+ ldr r2, [r1]
+ subs r2, r2, #1
+ str r2, [r1]
+
+ /* If we are still inside a nested interrupt disable, don't
+ * reenable interrupts. Return immediately.
+ */
+ bxpl lr
+
+ /* Otherwise, reenable interrupts. */
+ mrs r0, CPSR
+ bic r0, r0, #(IRQ_MASK | FIQ_MASK)
+ msr CPSR_c, r0
+ bx lr
+
+interrupts_count:
+ /* The byte count starts at 0, which is interrupts disabled with 1 level of nesting. Interrupts enabled is 0xFF. */
+ .int 0
+
+/*
* Default handlers for the interrupt controller. The board is
* initialized with interrupts disabled, and these handlers set.
*/
@@ -174,16 +221,8 @@ init_reset:
/* Go back to Supervisor mode and set up the Supervisor stack
* to start at the top of the IRQ stack.
- *
- *
- * Note that we also enable interrupts at this point, one
- * instruction before the SVC stack is set up. In theory, this is
- * a problem, because we might have an interrupt before we're
- * ready to handle it. In practice though, the low level init
- * function explicitely shut down all interrupts in the AIC, so
- * we're safe.
*/
- msr CPSR_c, #MODE_SVC
+ msr CPSR_c, #(IRQ_MASK | FIQ_MASK | MODE_SVC)
mov sp, r0
/* Copy the interrupt vectors and interrupt dispatchers into