summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c
diff options
context:
space:
mode:
authorTill Straumann <strauman@slac.stanford.edu>2007-12-08 23:43:24 +0000
committerTill Straumann <strauman@slac.stanford.edu>2007-12-08 23:43:24 +0000
commit94e1931c5b8ce7e34c4293efbb8dcc106a532ac9 (patch)
tree3723b8eeed0bb1cdf8905c4ba89a8b25720243da /c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c
parent2007-12-08 Till Straumann <strauman@slac.stanford.edu> (diff)
downloadrtems-94e1931c5b8ce7e34c4293efbb8dcc106a532ac9.tar.bz2
2007-12-08 Till Straumann <strauman@slac.stanford.edu>
* new-exceptions/bspsupport/, new-exceptions/bspsupport/ppc_exc.S, new-exceptions/bspsupport/ppc_exc_test.c, new-exceptions/bspsupport/vectors.h, new-exceptions/bspsupport/vectors_init.c, new-exceptions/bspsupport/irq.c, new-exceptions/bspsupport/ppc_exc_bspsupp.h, new-exceptions/bspsupport/ppc_exc_hdl.c, new-exceptions/bspsupport/ppc_exc_asm_macros.h, new-exceptions/bspsupport/nested_irq_test.c: New files. Added 'middleware' code for helping BSPs implement exception and interrupt handling and implementing the 'new' RTEMS IRQ API (which I personally dislike).
Diffstat (limited to 'c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c')
-rw-r--r--c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c b/c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c
new file mode 100644
index 0000000000..01d96a2a3b
--- /dev/null
+++ b/c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c
@@ -0,0 +1,374 @@
+/*
+ *
+ * This file contains the PIC-independent implementation of the functions described in irq.h
+ *
+ * Copyright (C) 1998, 1999 valette@crf.canon.fr
+ *
+ * The license and distribution terms for this file may be
+ * found in found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
+ */
+
+#include <stdlib.h>
+
+#include <rtems.h>
+#include "irq_supp.h"
+#include <rtems/score/apiext.h> /* for post ISR signal processing */
+#include <libcpu/raw_exception.h>
+#include <libcpu/cpuIdent.h>
+#include "vectors.h"
+#include <stdlib.h>
+#include <rtems/bspIo.h> /* for printk */
+#include <libcpu/spr.h>
+
+/*
+ * default handler connected on each irq after bsp initialization
+ */
+static rtems_irq_connect_data default_rtems_entry;
+
+/*
+ * location used to store initial tables used for interrupt
+ * management.
+ */
+static rtems_irq_global_settings* internal_config;
+static rtems_irq_connect_data* rtems_hdl_tbl;
+
+
+SPR_RW(BOOKE_TSR)
+
+/* legacy mode for bookE DEC exception;
+ * to avoid the double layer of function calls
+ * (dec_handler_bookE -> C_dispatch_irq_handler -> user handler)
+ * it is preferrable for the user to hook the DEC
+ * exception directly.
+ * However, the legacy mode works with less modifications
+ * of user code.
+ */
+void C_dispatch_dec_handler_bookE (BSP_Exception_frame *frame, unsigned int excNum)
+{
+ /* clear interrupt; we must do this
+ * before C_dispatch_irq_handler()
+ * re-enables MSR_EE.
+ */
+ _write_BOOKE_TSR( BOOKE_TSR_DIS );
+ C_dispatch_irq_handler(frame, ASM_DEC_VECTOR);
+}
+
+/*
+ * ------------------------ RTEMS Irq helper functions ----------------
+ */
+
+/*
+ * This function check that the value given for the irq line
+ * is valid.
+ */
+
+static int isValidInterrupt(int irq)
+{
+ if ( (irq < internal_config->irqBase) || (irq >= internal_config->irqBase + internal_config->irqNb))
+ return 0;
+ return 1;
+}
+
+/*
+ * ------------------------ RTEMS Shared Irq Handler Mngt Routines ----------------
+ */
+int BSP_install_rtems_shared_irq_handler (const rtems_irq_connect_data* irq)
+{
+ rtems_interrupt_level level;
+ rtems_irq_connect_data* vchain;
+
+ if (!isValidInterrupt(irq->name)) {
+ printk("Invalid interrupt vector %d\n",irq->name);
+ return 0;
+ }
+
+ rtems_interrupt_disable(level);
+
+ if ( (int)rtems_hdl_tbl[irq->name].next_handler == -1 ) {
+ rtems_interrupt_enable(level);
+ printk("IRQ vector %d already connected to an unshared handler\n",irq->name);
+ return 0;
+ }
+
+ vchain = (rtems_irq_connect_data*)malloc(sizeof(rtems_irq_connect_data));
+
+ /* save off topmost handler */
+ vchain[0]= rtems_hdl_tbl[irq->name];
+
+ /*
+ * store the data provided by user
+ */
+ rtems_hdl_tbl[irq->name] = *irq;
+
+ /* link chain to new topmost handler */
+ rtems_hdl_tbl[irq->name].next_handler = (void *)vchain;
+
+ /*
+ * enable_irq_at_pic is supposed to ignore
+ * requests to disable interrupts outside
+ * of the range handled by the PIC
+ */
+ BSP_enable_irq_at_pic(irq->name);
+
+ /*
+ * Enable interrupt on device
+ */
+ if (irq->on)
+ irq->on(irq);
+
+ rtems_interrupt_enable(level);
+
+ return 1;
+}
+
+/*
+ * ------------------------ RTEMS Single Irq Handler Mngt Routines ----------------
+ */
+
+int BSP_install_rtems_irq_handler (const rtems_irq_connect_data* irq)
+{
+ rtems_interrupt_level level;
+
+ if (!isValidInterrupt(irq->name)) {
+ printk("Invalid interrupt vector %d\n",irq->name);
+ return 0;
+ }
+ /*
+ * Check if default handler is actually connected. If not issue an error.
+ * You must first get the current handler via i386_get_current_idt_entry
+ * and then disconnect it using i386_delete_idt_entry.
+ * RATIONALE : to always have the same transition by forcing the user
+ * to get the previous handler before accepting to disconnect.
+ */
+ rtems_interrupt_disable(level);
+ if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
+ rtems_interrupt_enable(level);
+ printk("IRQ vector %d already connected\n",irq->name);
+ return 0;
+ }
+
+ /*
+ * store the data provided by user
+ */
+ rtems_hdl_tbl[irq->name] = *irq;
+ rtems_hdl_tbl[irq->name].next_handler = (void *)-1;
+
+ /*
+ * enable_irq_at_pic is supposed to ignore
+ * requests to disable interrupts outside
+ * of the range handled by the PIC
+ */
+ BSP_enable_irq_at_pic(irq->name);
+
+ /*
+ * Enable interrupt on device
+ */
+ if (irq->on)
+ irq->on(irq);
+
+ rtems_interrupt_enable(level);
+
+ return 1;
+}
+
+int BSP_get_current_rtems_irq_handler (rtems_irq_connect_data* irq)
+{
+ rtems_interrupt_level level;
+
+ if (!isValidInterrupt(irq->name)) {
+ return 0;
+ }
+ rtems_interrupt_disable(level);
+ *irq = rtems_hdl_tbl[irq->name];
+ rtems_interrupt_enable(level);
+ return 1;
+}
+
+int BSP_remove_rtems_irq_handler (const rtems_irq_connect_data* irq)
+{
+ rtems_irq_connect_data *pchain= NULL, *vchain = NULL;
+ rtems_interrupt_level level;
+
+ if (!isValidInterrupt(irq->name)) {
+ return 0;
+ }
+ /*
+ * Check if default handler is actually connected. If not issue an error.
+ * You must first get the current handler via i386_get_current_idt_entry
+ * and then disconnect it using i386_delete_idt_entry.
+ * RATIONALE : to always have the same transition by forcing the user
+ * to get the previous handler before accepting to disconnect.
+ */
+ rtems_interrupt_disable(level);
+ if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
+ rtems_interrupt_enable(level);
+ return 0;
+ }
+
+ if( (int)rtems_hdl_tbl[irq->name].next_handler != -1 )
+ {
+ int found = 0;
+
+ for( (pchain= NULL, vchain = &rtems_hdl_tbl[irq->name]);
+ (vchain->hdl != default_rtems_entry.hdl);
+ (pchain= vchain, vchain = (rtems_irq_connect_data*)vchain->next_handler) )
+ {
+ if( vchain->hdl == irq->hdl )
+ {
+ found= -1; break;
+ }
+ }
+
+ if( !found )
+ {
+ rtems_interrupt_enable(level);
+ return 0;
+ }
+ }
+ else
+ {
+ if (rtems_hdl_tbl[irq->name].hdl != irq->hdl)
+ {
+ rtems_interrupt_enable(level);
+ return 0;
+ }
+ }
+
+ /*
+ * disable_irq_at_pic is supposed to ignore
+ * requests to disable interrupts outside
+ * of the range handled by the PIC
+ */
+ BSP_disable_irq_at_pic(irq->name);
+
+ /*
+ * Disable interrupt on device
+ */
+ if (irq->off)
+ irq->off(irq);
+
+ /*
+ * restore the default irq value
+ */
+ if( !vchain )
+ {
+ /* single handler vector... */
+ rtems_hdl_tbl[irq->name] = default_rtems_entry;
+ }
+ else
+ {
+ if( pchain )
+ {
+ /* non-first handler being removed */
+ pchain->next_handler = vchain->next_handler;
+ }
+ else
+ {
+ /* first handler isn't malloc'ed, so just overwrite it. Since
+ the contents of vchain are being struct copied, vchain itself
+ goes away */
+ vchain = vchain->next_handler;
+ rtems_hdl_tbl[irq->name]= *vchain;
+ }
+ free(vchain);
+ }
+
+ rtems_interrupt_enable(level);
+
+ return 1;
+}
+
+/*
+ * Less cumbersome, alternate entry points;
+ * RETURNS: more traditional, 0 on success, nonzero on error
+ */
+
+static int doit(
+ int (*p)(const rtems_irq_connect_data*),
+ rtems_irq_number n,
+ rtems_irq_hdl hdl,
+ rtems_irq_hdl_param prm)
+{
+rtems_irq_connect_data xx;
+ xx.name = n;
+ xx.hdl = hdl;
+ xx.handle = prm;
+ xx.on = 0;
+ xx.off = 0;
+ xx.isOn = 0;
+ return ! p(&xx);
+}
+
+int BSP_rtems_int_connect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
+{
+ return doit(BSP_install_rtems_shared_irq_handler, n, hdl, p);
+}
+
+int BSP_rtems_int_disconnect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
+{
+ return doit(BSP_remove_rtems_irq_handler, n, hdl, p);
+}
+
+
+/*
+ * RTEMS Global Interrupt Handler Management Routines
+ */
+
+int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config)
+{
+ int i;
+ rtems_interrupt_level level;
+ rtems_irq_connect_data* vchain;
+
+ /*
+ * Store various code accelerators
+ */
+ internal_config = config;
+ default_rtems_entry = config->defaultEntry;
+ rtems_hdl_tbl = config->irqHdlTbl;
+
+ rtems_interrupt_disable(level);
+
+ if ( !BSP_setup_the_pic(config) ) {
+ printk("PIC setup failed; leaving IRQs OFF\n");
+ return 0;
+ }
+
+ for ( i = config->irqBase; i < config->irqBase + config->irqNb; i++ ) {
+ for( vchain = &rtems_hdl_tbl[i];
+ ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl);
+ vchain = (rtems_irq_connect_data*)vchain->next_handler )
+ {
+ if (vchain->on)
+ vchain->on(vchain);
+ }
+ }
+
+ rtems_interrupt_enable(level);
+
+ {
+ ppc_exc_set_handler(ASM_EXT_VECTOR, C_dispatch_irq_handler);
+
+ if ( ppc_cpu_is_bookE() ) {
+ /* bookE decrementer interrupt needs to be cleared BEFORE
+ * dispatching the user ISR (because the user ISR is called
+ * with EE enabled)
+ * We do this so that existing DEC handlers can be used
+ * with minor modifications.
+ */
+ ppc_exc_set_handler(ASM_BOOKE_PIT_VECTOR, C_dispatch_dec_handler_bookE);
+ } else {
+ ppc_exc_set_handler(ASM_DEC_VECTOR, C_dispatch_irq_handler);
+ }
+ }
+ return 1;
+}
+
+int BSP_rtems_irq_mngt_get(rtems_irq_global_settings** config)
+{
+ *config = internal_config;
+ return 0;
+}