/* ictrl.c * * This routine installs and handles external interrupt vectors for * PowerPC 403 CPU built-in external interrupt controller * * Author: Thomas Doerfler * * 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 #include #include /* 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 Configuration 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 (rtems_cpu_configuration_get_spurious_handler()) { rtems_cpu_configuration_get_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; }