diff options
Diffstat (limited to 'c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c')
-rw-r--r-- | c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c b/c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c new file mode 100644 index 0000000000..11d09f748a --- /dev/null +++ b/c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c @@ -0,0 +1,250 @@ +/* ictrl.c + * + * This routine installs and handles external interrupt vectors for + * PowerPC 403 CPU built-in external interrupt controller + * + * Author: Thomas Doerfler <td@imd.m.isar.de> + * + * COPYRIGHT (c) 1998 by IMD, Puchheim, Germany + * + * To anyone who acknowledges that this file is provided "AS IS" + * without any express or implied warranty: + * permission to use, copy, modify, and distribute this file + * for any purpose is hereby granted without fee, provided that + * the above copyright notice and this notice appears in all + * copies, and that the name of IMD not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * IMD makes no representations about the suitability + * of this software for any purpose. + * + */ + +#include "ictrl.h" +#include <bsp.h> +#include <rtems/libio.h> + +#include <stdlib.h> /* for atexit() */ + +/* + * ISR vector table to dispatch external interrupts + */ + +rtems_isr_entry ictrl_vector_table[PPC_IRQ_EXT_MAX]; + +/* + * + * some utilities to access the EXI* registers + * + */ + +/* + * clear bits in EXISR that have a bit set in mask + */ +RTEMS_INLINE_ROUTINE void +clr_exisr(unsigned32 mask) +{ + asm volatile ("mtdcr 0x40,%0"::"r" (mask));/*EXISR*/ +} + +/* + * get value of EXISR + */ +RTEMS_INLINE_ROUTINE unsigned32 +get_exisr(void) +{ + unsigned32 val; + + asm volatile ("mfdcr %0,0x40":"=r" (val));/*EXISR*/ + return val; +} + +/* + * get value of EXIER + */ +RTEMS_INLINE_ROUTINE unsigned32 +get_exier(void) +{ + unsigned32 val; + asm volatile ("mfdcr %0,0x42":"=r" (val));/*EXIER*/ + return val; +} + +/* + * set value of EXIER + */ +RTEMS_INLINE_ROUTINE void +set_exier(unsigned32 val) +{ + asm volatile ("mtdcr 0x42,%0"::"r" (val));/*EXIER*/ +} + +/* + * enable an external interrupt, make this interrupt consistent + */ +RTEMS_INLINE_ROUTINE void +enable_ext_irq( unsigned32 mask) +{ + unsigned32 isrlvl; + _CPU_ISR_Disable(isrlvl); + set_exier(get_exier() | ((mask)&PPC_EXI_MASK)); + _CPU_ISR_Enable(isrlvl); +} + +/* + * disable an external interrupt, make this interrupt consistent + */ +RTEMS_INLINE_ROUTINE void +disable_ext_irq( unsigned32 mask) +{ + unsigned32 isrlvl; + _CPU_ISR_Disable(isrlvl); + set_exier(get_exier() & ~(mask) & PPC_EXI_MASK); + _CPU_ISR_Enable(isrlvl); +} + +/* + * + * this function is called, when a external interrupt is present and + * enabled but there is no handler installed. It will clear + * the corresponding enable bits and call the spurious handler + * present in the _CPU_Table, if any. + * + */ +void +ictrl_spurious_handler(unsigned32 spurious_mask, + CPU_Interrupt_frame *cpu_frame) +{ + int v; + + for (v=0; v < PPC_IRQ_EXT_MAX; v++) { + if (VEC_TO_EXMSK(v) & spurious_mask) { + clr_exisr(VEC_TO_EXMSK(v)); + disable_ext_irq(VEC_TO_EXMSK(v)); +#if 0 + printf("spurious external interrupt: %d at pc 0x%x; disabling\n", + vector, cpu_frame->Interrupt.pcoqfront); +#endif + if (_CPU_Table.spurious_handler) { + _CPU_Table.spurious_handler(v + PPC_IRQ_EXT_BASE,cpu_frame); + } + } + } +} + + +/* + * ISR Handler: this is called from the primary exception dispatcher + */ + +void +ictrl_isr(rtems_vector_number vector,CPU_Interrupt_frame *cpu_frame) +{ + unsigned32 istat, + mask, + global_vec; + int exvec; + rtems_isr_entry handler; + + istat = get_exisr() & get_exier() & PPC_EXI_MASK; + + /* FIXME: this may be speeded up using cntlzw instruction */ + for (exvec = 0;exvec < PPC_IRQ_EXT_MAX;exvec++) { + mask = VEC_TO_EXMSK(exvec); + if (0 != (istat & mask)) { + clr_exisr(mask); + handler = ictrl_vector_table[exvec]; + if (handler) { + istat &= ~mask; + global_vec = exvec + PPC_IRQ_EXT_BASE; + (handler)(global_vec); + } + } + } + if (istat != 0) { /* anything left? then we have a spurious interrupt */ + ictrl_spurious_handler(istat,cpu_frame); + } +} + +/* + * + * the following functions form the user interface + * + */ + +/* + * + * install a user vector for one of the external interrupt sources + * + */ +rtems_status_code +ictrl_set_vector(rtems_isr_entry new_handler, + unsigned32 vector, + rtems_isr_entry *old_handler +) +{ + /* + * We put the actual user ISR address in 'ictrl_vector_table'. This will + * be used by the _ictrl_isr so the user gets control. + */ + + /* check for valid vector range */ + if ((vector >= PPC_IRQ_EXT_BASE) && + (vector < PPC_IRQ_EXT_BASE + PPC_IRQ_EXT_MAX)) { + + /* return old handler entry */ + *old_handler = ictrl_vector_table[vector - PPC_IRQ_EXT_BASE]; + + if (new_handler != NULL) { + /* store handler function... */ + ictrl_vector_table[vector - PPC_IRQ_EXT_BASE] = new_handler; + /* then enable it in EXIER register */ + enable_ext_irq(VEC_TO_EXMSK(vector - PPC_IRQ_EXT_BASE)); + } + else { /* new_handler == NULL */ + /* then disable it in EXIER register */ + disable_ext_irq(VEC_TO_EXMSK(vector - PPC_IRQ_EXT_BASE)); + ictrl_vector_table[vector - PPC_IRQ_EXT_BASE] = NULL; + } + return RTEMS_SUCCESSFUL; + } + else { + return RTEMS_INVALID_NUMBER; + } +} + +/* + * Called via atexit() + * deactivate the interrupt controller + */ + +void +ictrl_exit(void) +{ + /* mark them all unused */ + disable_ext_irq(~0); + clr_exisr(~0); + +} + +/* + * activate the interrupt controller + */ + +rtems_status_code +ictrl_init(void) +{ + proc_ptr dummy; + + /* mark them all unused */ + disable_ext_irq(~0); + clr_exisr(~0); + + /* install the external interrupt handler */ + _CPU_ISR_install_vector(PPC_IRQ_EXTERNAL, + ictrl_isr, + &dummy); + atexit(ictrl_exit); + return RTEMS_SUCCESSFUL; +} + |