diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-12 16:42:16 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-19 07:00:20 +0100 |
commit | ac04bb85ebea019afe4253253945f978661b8fa2 (patch) | |
tree | e783ec8c5197df54f31b983fb319370b300828ac /bsps | |
parent | i386/smp: Export _CPU_SMP_Prepare_start_multitasking as a function (diff) | |
download | rtems-ac04bb85ebea019afe4253253945f978661b8fa2.tar.bz2 |
bsps/powerpc: Move legacy IRQ support
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
-rw-r--r-- | bsps/powerpc/shared/irq/ppc-irq-legacy.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/bsps/powerpc/shared/irq/ppc-irq-legacy.c b/bsps/powerpc/shared/irq/ppc-irq-legacy.c new file mode 100644 index 0000000000..931a9c6074 --- /dev/null +++ b/bsps/powerpc/shared/irq/ppc-irq-legacy.c @@ -0,0 +1,393 @@ +/* + * + * 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 the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <stdlib.h> + +#include <rtems.h> +#include <stdlib.h> +#include <rtems/bspIo.h> /* for printk */ +#include <libcpu/spr.h> +#include <bsp/irq_supp.h> +#include <bsp/vectors.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) +SPR_RW(PPC405_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. + */ +static int 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. + * Note that PPC405 uses a different SPR# for TSR + */ + if ( ppc_cpu_is_bookE()==PPC_BOOKE_405) + _write_PPC405_TSR( BOOKE_TSR_DIS ); + else + _write_BOOKE_TSR( BOOKE_TSR_DIS ); + return 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; + } + + /* pre-allocate memory outside of critical section */ + vchain = (rtems_irq_connect_data*)malloc(sizeof(rtems_irq_connect_data)); + + rtems_interrupt_disable(level); + + if ( (intptr_t)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); + free(vchain); + return 0; + } + + /* 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( (intptr_t)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 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; + } + } + + /* Only disable at PIC if we removed the last handler */ + if ( rtems_hdl_tbl[irq->name].hdl == default_rtems_entry.hdl ) { + /* + * 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); + } + + rtems_interrupt_enable(level); + + free(vchain); + + 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]; + ((intptr_t)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + if (vchain->on) + vchain->on(vchain); + } + if ( vchain != &rtems_hdl_tbl[i] ) { + /* at least one handler registered */ + BSP_enable_irq_at_pic(i); + } else { +/* Do NOT disable; there might be boards with cascaded + * interrupt controllers where the BSP (incorrectly) does + * not ignore the cascaded interrupts in BSP_disable_irq_at_pic()! + * Instead, we rely on BSP_setup_the_pic() for a good + * initial configuration. + * + BSP_disable_irq_at_pic(i); + */ + } + } + + 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_DEC_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; +} |