--- /dev/null
+#include "kernel430x.def"
+
+.section ".lowtext"
+.globl interrupt_handler
+interrupt_handler:
+ /* Interrupts are off here and we're running on... a... stack. This
+ * may be the user process or the kernel's. We haven't saved any
+ * registers yet. */
+
+ mov SP, &istack_switched_sp ; save old stack (16 bit!)
+ mov #istack_top, SP ; load new one
+
+ pushm.a #13, r15 ; save all registers (r15 to r3)
+
+ calla #platform_interrupt
+
+ popm.a #13, r15 ; restore all registers (r15 to r3)
+
+ mov &istack_switched_sp, SP ; restore stack
+ reti
+
#include "msp430fr5969.h"
#include "kernel.def"
#include "kernel430x.def"
+#include "globals.h"
; imported symbols
.globl fuzix_main
.section ".noinit"
; The interrupt stack.
+.globl istack_top
istack_base:
.fill 254
istack_top:
-; The saved interrupt pointer.
-.globl istack_switch_sp
-.comm istack_switch_sp, 2
+; The saved interrupt pointer. Stacks must be in low memory.
+.globl istack_switched_sp
+.comm istack_switched_sp, 2
; The userdata/system stack structure. See Kernel/include/kernel.h.
.globl udata
mov.b #CSKEY>>8, &CSCTL0_H ; Unlock clock registers
mov.w #DCOFSEL_3 | DCORSEL, &CSCTL1 ; DCO to 8MHz
; ACLK = VLO; SMCLK = MCLK = DCO
- mov.w #(SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK), &CSCTL2
+ mov.w #(SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK), &CSCTL2
mov.w #(DIVA__1 | DIVS__1 | DIVM__1), &CSCTL3
+
+ ; Configure the LFXT.
+ mov.w #0, &PJSEL1
+ mov.w #BIT4|BIT5, &PJSEL0
+ bis.w #LFXTDRIVE_3|HFXTOFF, &CSCTL4
+ bic.w #LFXTBYPASS|LFXTOFF, &CSCTL4
+
+ ; Wait for oscillators to settle.
+
+1:
+ bic.w #LFXTOFFG|HFXTOFFG, &CSCTL5
+ bic.w #OFIFG, &SFRIFG1
+ bit.w #OFIFG, &SFRIFG1
+ jnz 1b
+
mov.b #0, &CSCTL0_H ; Relock clock registers
; Init stack.
.word __main
.text
+.globl last_interrupt
+.comm last_interrupt, 1
+
+#define standard_interrupt(name, irq) \
+ .globl name ## _trampoline { \
+ .section ".lowtext", "ax", @progbits { \
+ name ## _trampoline: \
+ mov.b #(irq), &last_interrupt { \
+ bra #interrupt_handler { \
+ .section #name, "ax", @progbits { \
+ .word name ## _trampoline
+
#define fallback_interrupt(name) \
.text { \
1: mov #3f, r12 { \
calla #kprintf { \
2: jmp 2b { \
.data { \
- 3: .asciz name { \
- .section name, "ax", @progbits { \
+ 3: .asciz #name { \
+ .section #name, "ax", @progbits { \
.word 1b
;fallback_interrupt("__interrupt_vector_comp_e")
-fallback_interrupt("__interrupt_vector_unmi")
-fallback_interrupt("__interrupt_vector_sysnmi")
+fallback_interrupt(__interrupt_vector_unmi)
+fallback_interrupt(__interrupt_vector_sysnmi)
+
+standard_interrupt(__interrupt_vector_wdt, INTERRUPT_WDT)
#include <tty.h>
#include <devsd.h>
#include <blkdev.h>
+#include <printf.h>
+#include <timer.h>
+#include "msp430fr5969.h"
+#include "globals.h"
+
+extern uint8_t last_interrupt;
struct devsw dev_tab[] = /* The device driver switch table */
{
void device_init(void)
{
+ /* Configure the watchdog timer to use ACLK as the system interrupt.
+ * ACLK was set up in the boot sequence to use the LFXT clock, which runs
+ * (relatively accurately) at 32kHz. 512 ticks at 32kHz is 64Hz.
+ */
+
+ WDTCTL = WDTPW | WDTSSEL__ACLK | WDTTMSEL | WDTCNTCL | WDTIS__512;
+ SFRIE1 |= WDTIE;
+
devsd_init();
}
+/* This is called with interrupts off. */
+void platform_interrupt(void)
+{
+ switch (last_interrupt)
+ {
+ case INTERRUPT_WDT:
+ timer_interrupt();
+ break;
+ }
+}
+