diff --git a/src/interrupt-utils.c b/src/interrupt-utils.c new file mode 100644 index 000000000..cd3e1bd81 --- /dev/null +++ b/src/interrupt-utils.c @@ -0,0 +1,84 @@ +/****************************************************************************** + * + * $RCSfile: interrupt-utils.c,v $ + * $Revision: 1.2 $ + * + * This module provides the interface routines for setting up and + * controlling the various interrupt modes present on the ARM processor. + * Copyright 2004, R O SoftWare + * No guarantees, warrantees, or promises, implied or otherwise. + * May be used for hobby or commercial purposes provided copyright + * notice remains intact. + * + *****************************************************************************/ +#include "interrupt-utils.h" + +#define IRQ_MASK 0x00000080 +#define FIQ_MASK 0x00000040 +#define INT_MASK (IRQ_MASK | FIQ_MASK) + +static inline unsigned __get_cpsr(void) +{ + unsigned long retval; + asm volatile (" mrs %0, cpsr" : "=r" (retval) : /* no inputs */ ); + return retval; +} + +static inline void __set_cpsr(unsigned val) +{ + asm volatile (" msr cpsr_c, %0" : /* no outputs */ : "r" (val) ); +} + +unsigned disableIRQ(void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr | IRQ_MASK); + return _cpsr; +} + +unsigned restoreIRQ(unsigned oldCPSR) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr((_cpsr & ~IRQ_MASK) | (oldCPSR & IRQ_MASK)); + return _cpsr; +} + +unsigned enableIRQ(void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr & ~IRQ_MASK); + return _cpsr; +} + +unsigned disableFIQ(void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr | FIQ_MASK); + return _cpsr; +} + +unsigned restoreFIQ(unsigned oldCPSR) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr((_cpsr & ~FIQ_MASK) | (oldCPSR & FIQ_MASK)); + return _cpsr; +} + +unsigned enableFIQ(void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr & ~FIQ_MASK); + return _cpsr; +} diff --git a/src/interrupt-utils.h b/src/interrupt-utils.h new file mode 100644 index 000000000..29b307d88 --- /dev/null +++ b/src/interrupt-utils.h @@ -0,0 +1,272 @@ +/* + * Defines and Macros for Interrupt-Service-Routines + * collected and partly created by + * Martin Thomas + * + * Copyright 2005 M. Thomas + * No guarantees, warrantees, or promises, implied or otherwise. + * May be used for hobby or commercial purposes provided copyright + * notice remains intact. + */ + +#ifndef interrupt_utils_ +#define interrupt_utils_ + +/* + The following defines are usefull for + interrupt service routine declarations. +*/ + +/* + RAMFUNC + Attribute which defines a function to be located + in memory section .fastrun and called via "long calls". + See linker-skript and startup-code to see how the + .fastrun-section is handled. + The definition is not only useful for ISRs but since + ISRs should be executed fast the macro is defined in + this header. +*/ +#define RAMFUNC __attribute__ ((long_call, section (".fastrun"))) + + +/* + INTFUNC + standard attribute for arm-elf-gcc which marks + a function as ISR (for the VIC). Since gcc seems + to produce wrong code if this attribute is used in + thumb/thumb-interwork the attribute should only be + used for "pure ARM-mode" binaries. +*/ +#define INTFUNC __attribute__ ((interrupt("IRQ"))) + + +/* + NACKEDFUNC + gcc will not add any code to a function declared + "nacked". The user has to take care to save registers + and add the needed code for ISR functions. Some + macros for this tasks are provided below. +*/ +#define NACKEDFUNC __attribute__((naked)) + + +/****************************************************************************** + * + * MACRO Name: ISR_STORE() + * + * Description: + * This MACRO is used upon entry to an ISR with interrupt nesting. + * Should be used together with ISR_ENABLE_NEST(). The MACRO + * performs the following steps: + * + * 1 - Save the non-banked registers r0-r12 and lr onto the IRQ stack. + * + *****************************************************************************/ +#define ISR_STORE() asm volatile( \ + "STMDB SP!,{R0-R12,LR}\n" ) + + /****************************************************************************** + * + * MACRO Name: ISR_RESTORE() + * + * Description: + * This MACRO is used upon exit from an ISR with interrupt nesting. + * Should be used together with ISR_DISABLE_NEST(). The MACRO + * performs the following steps: + * + * 1 - Load the non-banked registers r0-r12 and lr from the IRQ stack. + * 2 - Adjusts resume adress + * + *****************************************************************************/ +#define ISR_RESTORE() asm volatile( \ + "LDMIA SP!,{R0-R12,LR}\n" \ + "SUBS R15,R14,#0x0004\n" ) + +/****************************************************************************** + * + * MACRO Name: ISR_ENABLE_NEST() + * + * Description: + * This MACRO is used upon entry from an ISR with interrupt nesting. + * Should be used after ISR_STORE. + * + *****************************************************************************/ + +#define ISR_ENABLE_NEST() asm volatile( \ + "MRS LR, SPSR \n" \ + "STMFD SP!, {LR} \n" \ + "MSR CPSR_c, #0x1f \n" \ + "STMFD SP!, {LR} " ) + +/****************************************************************************** + * + * MACRO Name: ISR_DISABLE_NEST() + * + * Description: + * This MACRO is used upon entry from an ISR with interrupt nesting. + * Should be used before ISR_RESTORE. + * + *****************************************************************************/ + +#define ISR_DISABLE_NEST() asm volatile( \ + "LDMFD SP!, {LR} \n" \ + "MSR CPSR_c, #0x92 \n" \ + "LDMFD SP!, {LR} \n" \ + "MSR SPSR_cxsf, LR \n" ) + + + +/* + * The following marcos are from the file "armVIC.h" by: + * + * Copyright 2004, R O SoftWare + * No guarantees, warrantees, or promises, implied or otherwise. + * May be used for hobby or commercial purposes provided copyright + * notice remains intact. + * + */ + +/****************************************************************************** + * + * MACRO Name: ISR_ENTRY() + * + * Description: + * This MACRO is used upon entry to an ISR. The current version of + * the gcc compiler for ARM does not produce correct code for + * interrupt routines to operate properly with THUMB code. The MACRO + * performs the following steps: + * + * 1 - Adjust address at which execution should resume after servicing + * ISR to compensate for IRQ entry + * 2 - Save the non-banked registers r0-r12 and lr onto the IRQ stack. + * 3 - Get the status of the interrupted program is in SPSR. + * 4 - Push it onto the IRQ stack as well. + * + *****************************************************************************/ +#define ISR_ENTRY() asm volatile(" sub lr, lr,#4\n" \ + " stmfd sp!,{r0-r12,lr}\n" \ + " mrs r1, spsr\n" \ + " stmfd sp!,{r1}") + +/****************************************************************************** + * + * MACRO Name: ISR_EXIT() + * + * Description: + * This MACRO is used to exit an ISR. The current version of the gcc + * compiler for ARM does not produce correct code for interrupt + * routines to operate properly with THUMB code. The MACRO performs + * the following steps: + * + * 1 - Recover SPSR value from stack + * 2 - and restore its value + * 3 - Pop the return address & the saved general registers from + * the IRQ stack & return + * + *****************************************************************************/ +#define ISR_EXIT() asm volatile(" ldmfd sp!,{r1}\n" \ + " msr spsr_c,r1\n" \ + " ldmfd sp!,{r0-r12,pc}^") + +/****************************************************************************** + * + * Function Name: disableIRQ() + * + * Description: + * This function sets the IRQ disable bit in the status register + * + * Calling Sequence: + * void + * + * Returns: + * previous value of CPSR + * + *****************************************************************************/ +unsigned disableIRQ(void); + +/****************************************************************************** + * + * Function Name: enableIRQ() + * + * Description: + * This function clears the IRQ disable bit in the status register + * + * Calling Sequence: + * void + * + * Returns: + * previous value of CPSR + * + *****************************************************************************/ +unsigned enableIRQ(void); + +/****************************************************************************** + * + * Function Name: restoreIRQ() + * + * Description: + * This function restores the IRQ disable bit in the status register + * to the value contained within passed oldCPSR + * + * Calling Sequence: + * void + * + * Returns: + * previous value of CPSR + * + *****************************************************************************/ +unsigned restoreIRQ(unsigned oldCPSR); + +/****************************************************************************** + * + * Function Name: disableFIQ() + * + * Description: + * This function sets the FIQ disable bit in the status register + * + * Calling Sequence: + * void + * + * Returns: + * previous value of CPSR + * + *****************************************************************************/ +unsigned disableFIQ(void); + +/****************************************************************************** + * + * Function Name: enableFIQ() + * + * Description: + * This function clears the FIQ disable bit in the status register + * + * Calling Sequence: + * void + * + * Returns: + * previous value of CPSR + * + *****************************************************************************/ +unsigned enableFIQ(void); + +/****************************************************************************** + * + * Function Name: restoreFIQ() + * + * Description: + * This function restores the FIQ disable bit in the status register + * to the value contained within passed oldCPSR + * + * Calling Sequence: + * void + * + * Returns: + * previous value of CPSR + * + *****************************************************************************/ +unsigned restoreFIQ(unsigned oldCPSR); + + +#endif + diff --git a/src/sys-interrupt.c b/src/sys-interrupt.c new file mode 100644 index 000000000..b540145cb --- /dev/null +++ b/src/sys-interrupt.c @@ -0,0 +1,100 @@ +#include +#include +#include + +#define ATTR + +#ifndef NULL +#define NULL 0 +#endif + + +static SystemInterruptHandler *handlers = NULL; + +static void +system_int_safe (void) __attribute__((noinline)); + +static void +system_int_safe (void) +{ + SystemInterruptHandler *h; + h = handlers; + while (h) { + if (h->handler()) break; + h = h->next; + } +} + +static void NACKEDFUNC ATTR +system_int (void) /* System Interrupt Handler */ +{ + ISR_ENTRY(); + system_int_safe(); + *AT91C_AIC_EOICR = 0; /* End of Interrupt */ + ISR_EXIT(); +} + +static unsigned int enabled = 0; /* Number of times the system + interrupt has been enabled */ + +#define DIS_INT *AT91C_AIC_IDCR = (1 << AT91C_ID_SYS) +#define EN_INT if (enabled > 0) *AT91C_AIC_IECR = (1 << AT91C_ID_SYS) + +void +sys_interrupt_enable() +{ + if (enabled++ == 0) { + /* Level trigged at priority 5 */ + AT91C_AIC_SMR[AT91C_ID_SYS] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 5; + /* Interrupt vector */ + AT91C_AIC_SVR[AT91C_ID_SYS] = (unsigned long) system_int; + /* Enable */ + EN_INT; + } +} + + +void +sys_interrupt_disable() +{ + if (--enabled == 0) { + DIS_INT; + } +} + +void +sys_interrupt_append_handler(SystemInterruptHandler *handler) +{ + SystemInterruptHandler **h = &handlers; + while(*h) { + h = &(*h)->next; + } + DIS_INT; + *h = handler; + handler->next = NULL; + EN_INT; +} + +void +sys_interrupt_prepend_handler(SystemInterruptHandler *handler) +{ + DIS_INT; + handler->next = handlers; + handlers = handler; + EN_INT; +} + +void +sys_interrupt_remove_handler(SystemInterruptHandler *handler) +{ + SystemInterruptHandler **h = &handlers; + while(*h) { + if (*h == handler) { + DIS_INT; + *h = handler->next; + EN_INT; + break; + } + h = &(*h)->next; + } +} diff --git a/src/sys-interrupt.h b/src/sys-interrupt.h new file mode 100644 index 000000000..433db4a0f --- /dev/null +++ b/src/sys-interrupt.h @@ -0,0 +1,31 @@ +#ifndef __SYS_INTERRUPT_H__QIHZ66NP8K__ +#define __SYS_INTERRUPT_H__QIHZ66NP8K__ + + +/* Returns true if it handled an activbe interrupt */ +typedef int (*SystemInterruptFunc)(); + +typedef struct _SystemInterruptHandler SystemInterruptHandler; +struct _SystemInterruptHandler +{ + SystemInterruptHandler *next; + SystemInterruptFunc handler; +}; + + +void +sys_interrupt_enable(); + +void +sys_interrupt_disable(); + +void +sys_interrupt_append_handler(SystemInterruptHandler *handler); + +void +sys_interrupt_prepend_handler(SystemInterruptHandler *handler); + +void +sys_interrupt_remove_handler(SystemInterruptHandler *handler); + +#endif /* __SYS_INTERRUPT_H__QIHZ66NP8K__ */ diff --git a/tests/tmr-ints.c b/tests/tmr-ints.c new file mode 100644 index 000000000..22c12a75d --- /dev/null +++ b/tests/tmr-ints.c @@ -0,0 +1,92 @@ +/* Timer registers are all 16-bit wide with 16-bit access only */ +#define TMR_OFFSET (0x20) +#define TMR_BASE (0x80007000) +#define TMR0_BASE (TMR_BASE) +#define TMR1_BASE (TMR_BASE + TMR_OFFSET*1) +#define TMR2_BASE (TMR_BASE + TMR_OFFSET*2) +#define TMR3_BASE (TMR_BASE + TMR_OFFSET*3) + +#define TMR_REGOFF_COMP1 (0x0) +#define TMR_REGOFF_COMP2 (0x2) +#define TMR_REGOFF_CAPT (0x4) +#define TMR_REGOFF_LOAD (0x6) +#define TMR_REGOFF_HOLD (0x8) +#define TMR_REGOFF_CNTR (0xa) +#define TMR_REGOFF_CTRL (0xc) +#define TMR_REGOFF_SCTRL (0xe) +#define TMR_REGOFF_CMPLD1 (0x10) +#define TMR_REGOFF_CMPLD2 (0x12) +#define TMR_REGOFF_CSCTRL (0x14) +#define TMR_REGOFF_ENBL (0x1e) + +/* Timer 0 registers */ +#define TMR0_COMP1 (TMR0_BASE + TMR_REGOFF_COMP1) +#define TMR0_COMP_UP TMR0_COMP1 +#define TMR0_COMP2 (TMR0_BASE + TMR_REGOFF_COMP2) +#define TMR0_COMP_DOWN TMR0_COMP2 +#define TMR0_CAPT (TMR0_BASE + TMR_REGOFF_CAPT) +#define TMR0_LOAD (TMR0_BASE + TMR_REGOFF_LOAD) +#define TMR0_HOLD (TMR0_BASE + TMR_REGOFF_HOLD) +#define TMR0_CNTR (TMR0_BASE + TMR_REGOFF_CTRL) +#define TMR0_CTRL (TMR0_BASE + TMR_REGOFF_CTRL) +#define TMR0_SCTRL (TMR0_BASE + TMR_REGOFF_SCTRL) +#define TMR0_CMPLD1 (TMR0_BASE + TMR_REGOFF_CMPLD1) +#define TMR0_CMPLD2 (TMR0_BASE + TMR_REGOFF_CMPLD2) +#define TMR0_CSCTRL (TMR0_BASE + TMR_REGOFF_CSCTRL) + +/* one enable register to rule them all */ +#define TMR_ENBL TMR0_BASE + TMR_REGOFF_ENBL + +#define MBAR_GPIO 0x80000000 +#define GPIO_PAD_DIR0 0x80000000 +#define GPIO_DATA0 0x80000008 +#define UART1_DATA 0x80005008 +#define DELAY 400000 + +#define reg32(x) (*(volatile uint32_t *)(x)) +#define reg16(x) (*(volatile uint16_t *)(x)) + +#include "embedded_types.h" + +__attribute__ ((section ("startup"))) +void main(void) { + + /* pin direction */ + reg32(GPIO_PAD_DIR0) = 0x00000400; + + /* timer setup */ + /* CTRL */ +#define COUNT_MODE 1 /* use rising edge of primary source */ +#define PRIME_SRC 0xf /* Perip. clock with 128 prescale (for 24Mhz = 187500Hz)*/ +#define SEC_SRC 0 /* don't need this */ +#define ONCE 0 /* keep counting */ +#define LEN 1 /* count until compare then reload with value in LOAD */ +#define DIR 0 /* count up */ +#define CO_INIT 0 /* other counters cannot force a re-initialization of this counter */ +#define OUT_MODE 0 /* OFLAG is asserted while counter is active */ + + reg16(TMR_ENBL) = 0; /* tmrs reset to enabled */ + reg16(TMR0_SCTRL) = 0; + reg16(TMR0_LOAD) = 0; /* reload to zero */ + reg16(TMR0_COMP_UP) = 18750; /* trigger a reload at the end */ + reg16(TMR0_CMPLD1) = 18750; /* compare 1 triggered reload level, 10HZ maybe? */ + reg16(TMR0_CNTR) = 0; /* reset count register */ + reg16(TMR0_CTRL) = (COUNT_MODE<<13) | (PRIME_SRC<<9) | (SEC_SRC<<7) | (ONCE<<6) | (LEN<<5) | (DIR<<4) | (CO_INIT<<3) | (OUT_MODE); + reg16(TMR_ENBL) = 0xf; /* enable all the timers --- why not? */ + + while(1) { + + /* blink on */ + reg32(GPIO_DATA0) = 0x00000400; + + while((reg16(TMR0_SCTRL)>>15) == 0) { continue; } + reg16(TMR0_SCTRL) = 0; /*clear bit 15, and all the others --- should be ok, but clearly not "the right thing to do" */ + + /* blink off */ + reg32(GPIO_DATA0) = 0x00000000; + + while((reg16(TMR0_SCTRL)>>15) == 0) { continue; } + reg16(TMR0_SCTRL) = 0; /*clear bit 15, and all the others --- should be ok, but clearly not "the right thing to do" */ + + }; +}