Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:58

0001 /**
0002  * @file PIC-independent implementation of the functions described in irq.h
0003  */
0004 
0005 /*
0006  * Copyright (C) 1998, 1999 valette@crf.canon.fr
0007  *
0008  * The license and distribution terms for this file may be
0009  * found in the file LICENSE in this distribution or at
0010  * http://www.rtems.org/license/LICENSE.
0011  */
0012 
0013 #include <stdlib.h>
0014 
0015 #include <rtems.h>
0016 #include <rtems/malloc.h>
0017 #include <rtems/bspIo.h> /* for printk */
0018 #include <libcpu/spr.h>
0019 #include <bsp/irq_supp.h>
0020 #include <bsp/vectors.h>
0021 
0022 /*
0023  * default handler connected on each irq after bsp initialization
0024  */
0025 static rtems_irq_connect_data   default_rtems_entry;
0026 
0027 /*
0028  * location used to store initial tables used for interrupt
0029  * management.
0030  */
0031 static rtems_irq_global_settings*   internal_config;
0032 static rtems_irq_connect_data*      rtems_hdl_tbl;
0033 
0034 
0035 SPR_RW(BOOKE_TSR)
0036 SPR_RW(PPC405_TSR)
0037 
0038 /* legacy mode for bookE DEC exception;
0039  * to avoid the double layer of function calls
0040  * (dec_handler_bookE -> C_dispatch_irq_handler -> user handler)
0041  * it is preferrable for the user to hook the DEC
0042  * exception directly.
0043  * However, the legacy mode works with less modifications
0044  * of user code.
0045  */
0046 static int C_dispatch_dec_handler_bookE (BSP_Exception_frame *frame, unsigned int excNum)
0047 {
0048     /* clear interrupt; we must do this
0049      * before C_dispatch_irq_handler()
0050      * re-enables MSR_EE.
0051      * Note that PPC405 uses a different SPR# for TSR
0052      */
0053     if ( ppc_cpu_is_bookE()==PPC_BOOKE_405)
0054         _write_PPC405_TSR( BOOKE_TSR_DIS );
0055     else
0056         _write_BOOKE_TSR( BOOKE_TSR_DIS );
0057     return C_dispatch_irq_handler(frame, ASM_DEC_VECTOR);
0058 }
0059 
0060 /*
0061  * ------------------------ RTEMS Irq helper functions ----------------
0062  */
0063 
0064 /*
0065  * This function check that the value given for the irq line
0066  * is valid.
0067  */
0068 
0069 static int isValidInterrupt(int irq)
0070 {
0071   if ( (irq < internal_config->irqBase) || (irq >= internal_config->irqBase + internal_config->irqNb))
0072     return 0;
0073   return 1;
0074 }
0075 
0076 /*
0077  * ------------------------ RTEMS Shared Irq Handler Mngt Routines ----------------
0078  */
0079 int BSP_install_rtems_shared_irq_handler  (const rtems_irq_connect_data* irq)
0080 {
0081     rtems_interrupt_level   level;
0082     rtems_irq_connect_data* vchain;
0083 
0084     if (!isValidInterrupt(irq->name)) {
0085       printk("Invalid interrupt vector %d\n",irq->name);
0086       return 0;
0087     }
0088 
0089     /* pre-allocate memory outside of critical section */
0090     vchain = (rtems_irq_connect_data*) rtems_malloc(sizeof(rtems_irq_connect_data));
0091 
0092     rtems_interrupt_disable(level);
0093 
0094     if ( (intptr_t)rtems_hdl_tbl[irq->name].next_handler  == -1 ) {
0095       rtems_interrupt_enable(level);
0096       printk("IRQ vector %d already connected to an unshared handler\n",irq->name);
0097       free(vchain);
0098       return 0;
0099     }
0100 
0101     /* save off topmost handler */
0102     vchain[0]= rtems_hdl_tbl[irq->name];
0103 
0104     /*
0105      * store the data provided by user
0106      */
0107     rtems_hdl_tbl[irq->name] = *irq;
0108 
0109     /* link chain to new topmost handler */
0110     rtems_hdl_tbl[irq->name].next_handler = (void *)vchain;
0111 
0112     /*
0113      * enable_irq_at_pic is supposed to ignore
0114      * requests to disable interrupts outside
0115      * of the range handled by the PIC
0116      */
0117     BSP_enable_irq_at_pic(irq->name);
0118 
0119     /*
0120      * Enable interrupt on device
0121      */
0122     if (irq->on)
0123         irq->on(irq);
0124 
0125     rtems_interrupt_enable(level);
0126 
0127     return 1;
0128 }
0129 
0130 /*
0131  * ------------------------ RTEMS Single Irq Handler Mngt Routines ----------------
0132  */
0133 
0134 int BSP_install_rtems_irq_handler  (const rtems_irq_connect_data* irq)
0135 {
0136     rtems_interrupt_level  level;
0137 
0138     if (!isValidInterrupt(irq->name)) {
0139       printk("Invalid interrupt vector %d\n",irq->name);
0140       return 0;
0141     }
0142     /*
0143      * Check if default handler is actually connected. If not issue an error.
0144      * You must first get the current handler via i386_get_current_idt_entry
0145      * and then disconnect it using i386_delete_idt_entry.
0146      * RATIONALE : to always have the same transition by forcing the user
0147      * to get the previous handler before accepting to disconnect.
0148      */
0149     rtems_interrupt_disable(level);
0150     if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
0151       rtems_interrupt_enable(level);
0152       printk("IRQ vector %d already connected\n",irq->name);
0153       return 0;
0154     }
0155 
0156     /*
0157      * store the data provided by user
0158      */
0159     rtems_hdl_tbl[irq->name] = *irq;
0160     rtems_hdl_tbl[irq->name].next_handler = (void *)-1;
0161 
0162     /*
0163      * enable_irq_at_pic is supposed to ignore
0164      * requests to disable interrupts outside
0165      * of the range handled by the PIC
0166      */
0167     BSP_enable_irq_at_pic(irq->name);
0168 
0169     /*
0170      * Enable interrupt on device
0171      */
0172     if (irq->on)
0173         irq->on(irq);
0174 
0175     rtems_interrupt_enable(level);
0176 
0177     return 1;
0178 }
0179 
0180 int BSP_get_current_rtems_irq_handler   (rtems_irq_connect_data* irq)
0181 {
0182     rtems_interrupt_level       level;
0183 
0184     if (!isValidInterrupt(irq->name)) {
0185       return 0;
0186     }
0187     rtems_interrupt_disable(level);
0188       *irq = rtems_hdl_tbl[irq->name];
0189     rtems_interrupt_enable(level);
0190     return 1;
0191 }
0192 
0193 int BSP_remove_rtems_irq_handler  (const rtems_irq_connect_data* irq)
0194 {
0195     rtems_irq_connect_data *pchain= NULL, *vchain = NULL;
0196     rtems_interrupt_level   level;
0197 
0198     if (!isValidInterrupt(irq->name)) {
0199       return 0;
0200     }
0201     /*
0202      * Check if default handler is actually connected. If not issue an error.
0203      * You must first get the current handler via i386_get_current_idt_entry
0204      * and then disconnect it using i386_delete_idt_entry.
0205      * RATIONALE : to always have the same transition by forcing the user
0206      * to get the previous handler before accepting to disconnect.
0207      */
0208     rtems_interrupt_disable(level);
0209     if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
0210       rtems_interrupt_enable(level);
0211       return 0;
0212     }
0213 
0214     if( (intptr_t)rtems_hdl_tbl[irq->name].next_handler != -1 )
0215     {
0216        int found = 0;
0217 
0218        for( (pchain= NULL, vchain = &rtems_hdl_tbl[irq->name]);
0219             (vchain->hdl != default_rtems_entry.hdl);
0220             (pchain= vchain, vchain = (rtems_irq_connect_data*)vchain->next_handler) )
0221        {
0222           if( vchain->hdl == irq->hdl )
0223           {
0224              found= -1; break;
0225           }
0226        }
0227 
0228        if( !found )
0229        {
0230           rtems_interrupt_enable(level);
0231           return 0;
0232        }
0233     }
0234     else
0235     {
0236         if (rtems_hdl_tbl[irq->name].hdl != irq->hdl)
0237         {
0238             rtems_interrupt_enable(level);
0239             return 0;
0240         }
0241     }
0242 
0243     /*
0244      * Disable interrupt on device
0245      */
0246     if (irq->off)
0247         irq->off(irq);
0248 
0249     /*
0250      * restore the default irq value
0251      */
0252     if( !vchain )
0253     {
0254        /* single handler vector... */
0255        rtems_hdl_tbl[irq->name] = default_rtems_entry;
0256     }
0257     else
0258     {
0259        if( pchain )
0260        {
0261           /* non-first handler being removed */
0262           pchain->next_handler = vchain->next_handler;
0263        }
0264        else
0265        {
0266           /* first handler isn't malloc'ed, so just overwrite it.  Since
0267           the contents of vchain are being struct copied, vchain itself
0268           goes away */
0269           vchain = vchain->next_handler;
0270           rtems_hdl_tbl[irq->name]= *vchain;
0271        }
0272     }
0273 
0274     /* Only disable at PIC if we removed the last handler */
0275     if ( rtems_hdl_tbl[irq->name].hdl == default_rtems_entry.hdl ) {
0276         /*
0277          * disable_irq_at_pic is supposed to ignore
0278          * requests to disable interrupts outside
0279          * of the range handled by the PIC;
0280          */
0281         BSP_disable_irq_at_pic(irq->name);
0282     }
0283 
0284     rtems_interrupt_enable(level);
0285 
0286     free(vchain);
0287 
0288     return 1;
0289 }
0290 
0291 /*
0292  * Less cumbersome, alternate entry points;
0293  * RETURNS: more traditional, 0 on success, nonzero on error
0294  */
0295 
0296 static int doit(
0297     int (*p)(const rtems_irq_connect_data*),
0298     rtems_irq_number n,
0299     rtems_irq_hdl hdl,
0300     rtems_irq_hdl_param prm)
0301 {
0302 rtems_irq_connect_data  xx;
0303     xx.name   = n;
0304     xx.hdl    = hdl;
0305     xx.handle = prm;
0306     xx.on     = 0;
0307     xx.off    = 0;
0308     xx.isOn   = 0;
0309     return ! p(&xx);
0310 }
0311 
0312 int BSP_rtems_int_connect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
0313 {
0314     return doit(BSP_install_rtems_shared_irq_handler, n, hdl, p);
0315 }
0316 
0317 int BSP_rtems_int_disconnect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
0318 {
0319     return doit(BSP_remove_rtems_irq_handler, n, hdl, p);
0320 }
0321 
0322 
0323 /*
0324  * RTEMS Global Interrupt Handler Management Routines
0325  */
0326 
0327 int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config)
0328 {
0329     int                           i;
0330     rtems_interrupt_level         level;
0331     rtems_irq_connect_data*       vchain;
0332 
0333     /*
0334      * Store various code accelerators
0335      */
0336     internal_config         = config;
0337     default_rtems_entry     = config->defaultEntry;
0338     rtems_hdl_tbl       = config->irqHdlTbl;
0339 
0340     rtems_interrupt_disable(level);
0341 
0342     if ( !BSP_setup_the_pic(config) ) {
0343         printk("PIC setup failed; leaving IRQs OFF\n");
0344         return 0;
0345     }
0346 
0347     for ( i = config->irqBase; i < config->irqBase + config->irqNb; i++ ) {
0348         for( vchain = &rtems_hdl_tbl[i];
0349              ((intptr_t)vchain != -1 && vchain->hdl != default_rtems_entry.hdl);
0350              vchain = (rtems_irq_connect_data*)vchain->next_handler )
0351         {
0352             if (vchain->on)
0353               vchain->on(vchain);
0354         }
0355         if ( vchain != &rtems_hdl_tbl[i] ) {
0356             /* at least one handler registered */
0357             BSP_enable_irq_at_pic(i);
0358         } else {
0359 /* Do NOT disable; there might be boards with cascaded
0360  * interrupt controllers where the BSP (incorrectly) does
0361  * not ignore the cascaded interrupts in BSP_disable_irq_at_pic()!
0362  * Instead, we rely on BSP_setup_the_pic() for a good
0363  * initial configuration.
0364  *
0365             BSP_disable_irq_at_pic(i);
0366  */
0367         }
0368     }
0369 
0370     rtems_interrupt_enable(level);
0371 
0372     {
0373             ppc_exc_set_handler(ASM_EXT_VECTOR, C_dispatch_irq_handler);
0374 
0375             if ( ppc_cpu_is_bookE() ) {
0376                 /* bookE decrementer interrupt needs to be cleared BEFORE
0377                  * dispatching the user ISR (because the user ISR is called
0378                  * with EE enabled)
0379                  * We do this so that existing DEC handlers can be used
0380                  * with minor modifications.
0381                  */
0382                 ppc_exc_set_handler(ASM_BOOKE_DEC_VECTOR, C_dispatch_dec_handler_bookE);
0383             } else {
0384                 ppc_exc_set_handler(ASM_DEC_VECTOR, C_dispatch_irq_handler);
0385             }
0386     }
0387     return 1;
0388 }
0389 
0390 int BSP_rtems_irq_mngt_get(rtems_irq_global_settings** config)
0391 {
0392     *config = internal_config;
0393     return 0;
0394 }