/* 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_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;
}
|