summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c')
-rw-r--r--c/src/lib/libcpu/powerpc/ppc403/ictrl/ictrl.c250
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;
+}
+