Back to home page

LXR

 
 

    


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

0001 /*
0002  *  This file contains the implementation of the function described in irq.h
0003  */
0004 
0005 /*
0006  *  MPC5xx port sponsored by Defence Research and Development Canada - Suffield
0007  *  Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
0008  *
0009  *  Derived from libbsp/powerpc/mbx8xx/irq/irq.c:
0010  *
0011  *  Copyright (C) 1998, 1999 valette@crf.canon.fr
0012  *
0013  *  The license and distribution terms for this file may be
0014  *  found in the file LICENSE in this distribution or at
0015  *  http://www.rtems.org/license/LICENSE.
0016  */
0017 
0018 #include <rtems.h>
0019 #include <mpc5xx.h>
0020 #include <libcpu/vectors.h>
0021 #include <libcpu/raw_exception.h>
0022 #include <libcpu/irq.h>
0023 #include <bsp/irq.h>
0024 
0025 /*
0026  * Convert an rtems_irq_number constant to an interrupt level
0027  * suitable for programming into an I/O device's interrupt level field.
0028  */
0029 int CPU_irq_level_from_symbolic_name(const rtems_irq_number name)
0030 {
0031   if (CPU_USIU_EXT_IRQ_0 <= name && name <= CPU_USIU_INT_IRQ_7)
0032     return (name - CPU_USIU_EXT_IRQ_0) / 2;
0033 
0034   if (CPU_UIMB_IRQ_8 <= name && name <= CPU_UIMB_IRQ_31)
0035     return 8 + (name - CPU_UIMB_IRQ_8);
0036 
0037   return 31;            /* reasonable default */
0038 }
0039 
0040 /*
0041  * default handler connected on each irq after bsp initialization
0042  */
0043 static rtems_irq_connect_data       default_rtems_entry;
0044 
0045 /*
0046  * location used to store initial tables used for interrupt
0047  * management.
0048  */
0049 static rtems_irq_global_settings*   internal_config;
0050 static rtems_irq_connect_data*      rtems_hdl_tbl;
0051 
0052 /*
0053  * Check if symbolic IRQ name is an USIU IRQ
0054  */
0055 static inline int is_usiu_irq(const rtems_irq_number irqLine)
0056 {
0057   return (((int) irqLine <= CPU_USIU_IRQ_MAX_OFFSET) &&
0058       ((int) irqLine >= CPU_USIU_IRQ_MIN_OFFSET)
0059      );
0060 }
0061 
0062 /*
0063  * Check if symbolic IRQ name is an UIMB IRQ
0064  */
0065 static inline int is_uimb_irq(const rtems_irq_number irqLine)
0066 {
0067   return (((int) irqLine <= CPU_UIMB_IRQ_MAX_OFFSET) &&
0068       ((int) irqLine >= CPU_UIMB_IRQ_MIN_OFFSET)
0069      );
0070 }
0071 
0072 /*
0073  * Check if symbolic IRQ name is a Processor IRQ
0074  */
0075 static inline int is_proc_irq(const rtems_irq_number irqLine)
0076 {
0077   return (((int) irqLine <= CPU_PROC_IRQ_MAX_OFFSET) &&
0078       ((int) irqLine >= CPU_PROC_IRQ_MIN_OFFSET)
0079      );
0080 }
0081 
0082 
0083 /*
0084  * Masks used to mask off the interrupts. For exmaple, for ILVL2, the
0085  * mask is used to mask off interrupts ILVL2, IRQ3, ILVL3, ... IRQ7
0086  * and ILVL7.
0087  *
0088  */
0089 const static unsigned int USIU_IvectMask[CPU_USIU_IRQ_COUNT] =
0090 {
0091   0,                /* external IRQ 0 */
0092   0xFFFFFFFF << 31,         /* internal level 0 */
0093   0xFFFFFFFF << 30,         /* external IRQ 1 */
0094   0xFFFFFFFF << 29,         /* internal level 1 */
0095   0xFFFFFFFF << 28,         /* external IRQ 2 */
0096   0xFFFFFFFF << 27,         /* internal level 2 */
0097   0xFFFFFFFF << 26,         /* external IRQ 3 */
0098   0xFFFFFFFF << 25,         /* internal level 3 */
0099   0xFFFFFFFF << 24,         /* external IRQ 4 */
0100   0xFFFFFFFF << 23,         /* internal level 4 */
0101   0xFFFFFFFF << 22,         /* external IRQ 5 */
0102   0xFFFFFFFF << 21,         /* internal level 5 */
0103   0xFFFFFFFF << 20,         /* external IRQ 6 */
0104   0xFFFFFFFF << 19,         /* internal level 6 */
0105   0xFFFFFFFF << 18,         /* external IRQ 7 */
0106   0xFFFFFFFF << 17      /* internal level 7 */
0107 };
0108 
0109 
0110 /*
0111  * ------------------------ RTEMS Irq helper functions ----------------
0112  */
0113 
0114 /*
0115  * Caution : this function assumes the variable "internal_config"
0116  * is already set and that the tables it contains are still valid
0117  * and accessible.
0118  */
0119 static void compute_USIU_IvectMask_from_prio (void)
0120 {
0121   /*
0122    * In theory this is feasible. No time to code it yet. See i386/shared/irq.c
0123    * for an example based on 8259 controller mask. The actual masks defined
0124    * correspond to the priorities defined for the USIU in irq_init.c.
0125    */
0126 }
0127 
0128 /*
0129  * This function check that the value given for the irq line
0130  * is valid.
0131  */
0132 static int isValidInterrupt(int irq)
0133 {
0134   if ( (irq < CPU_MIN_OFFSET) || (irq > CPU_MAX_OFFSET)
0135         || (irq == CPU_UIMB_INTERRUPT) )
0136     return 0;
0137   return 1;
0138 }
0139 
0140 static int CPU_irq_enable_at_uimb(const rtems_irq_number irqLine)
0141 {
0142   if (!is_uimb_irq(irqLine))
0143     return 1;
0144   return 0;
0145 }
0146 
0147 static int CPU_irq_disable_at_uimb(const rtems_irq_number irqLine)
0148 {
0149   if (!is_uimb_irq(irqLine))
0150     return 1;
0151   return 0;
0152 }
0153 
0154 static int CPU_irq_enable_at_usiu(const rtems_irq_number irqLine)
0155 {
0156   int usiu_irq_index;
0157 
0158   if (!is_usiu_irq(irqLine))
0159     return 1;
0160 
0161   usiu_irq_index = ((int) (irqLine) - CPU_USIU_IRQ_MIN_OFFSET);
0162   ppc_cached_irq_mask |= (1 << (31-usiu_irq_index));
0163   usiu.simask = ppc_cached_irq_mask;
0164 
0165   return 0;
0166 }
0167 
0168 static int CPU_irq_disable_at_usiu(const rtems_irq_number irqLine)
0169 {
0170   int usiu_irq_index;
0171 
0172   if (!is_usiu_irq(irqLine))
0173     return 1;
0174 
0175   usiu_irq_index = ((int) (irqLine) - CPU_USIU_IRQ_MIN_OFFSET);
0176   ppc_cached_irq_mask &= ~(1 << (31-usiu_irq_index));
0177   usiu.simask = ppc_cached_irq_mask;
0178 
0179   return 0;
0180 }
0181 
0182 /*
0183  * --------------- RTEMS Single Irq Handler Mngt Routines ----------------
0184  */
0185 
0186 int CPU_install_rtems_irq_handler   (const rtems_irq_connect_data* irq)
0187 {
0188     rtems_interrupt_level       level;
0189 
0190     if (!isValidInterrupt(irq->name)) {
0191       return 0;
0192     }
0193     /*
0194      * Check if default handler is actually connected. If not issue an error.
0195      * You must first get the current handler via CPU_get_current_idt_entry
0196      * and then disconnect it using CPU_delete_idt_entry.
0197      * RATIONALE : to always have the same transition by forcing the user
0198      * to get the previous handler before accepting to disconnect.
0199      */
0200     if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
0201       return 0;
0202     }
0203 
0204     rtems_interrupt_disable(level);
0205 
0206     /*
0207      * store the data provided by user
0208      */
0209     rtems_hdl_tbl[irq->name] = *irq;
0210 
0211     if (is_uimb_irq(irq->name)) {
0212       /*
0213        * Enable interrupt at UIMB level
0214        */
0215       CPU_irq_enable_at_uimb (irq->name);
0216     }
0217 
0218     if (is_usiu_irq(irq->name)) {
0219       /*
0220        * Enable interrupt at USIU level
0221        */
0222       CPU_irq_enable_at_usiu (irq->name);
0223     }
0224 
0225     if (is_proc_irq(irq->name)) {
0226       /*
0227        * Should Enable exception at processor level but not needed.  Will restore
0228        * EE flags at the end of the routine anyway.
0229        */
0230     }
0231     /*
0232      * Enable interrupt on device
0233      */
0234     if (irq->on)
0235         irq->on(irq);
0236 
0237     rtems_interrupt_enable(level);
0238 
0239     return 1;
0240 }
0241 
0242 
0243 int CPU_get_current_rtems_irq_handler   (rtems_irq_connect_data* irq)
0244 {
0245      if (!isValidInterrupt(irq->name)) {
0246        return 0;
0247      }
0248      *irq = rtems_hdl_tbl[irq->name];
0249      return 1;
0250 }
0251 
0252 int CPU_remove_rtems_irq_handler  (const rtems_irq_connect_data* irq)
0253 {
0254     rtems_interrupt_level       level;
0255 
0256     if (!isValidInterrupt(irq->name)) {
0257       return 0;
0258     }
0259     /*
0260      * Check if default handler is actually connected. If not issue an error.
0261      * You must first get the current handler via CPU_get_current_idt_entry
0262      * and then disconnect it using CPU_delete_idt_entry.
0263      * RATIONALE : to always have the same transition by forcing the user
0264      * to get the previous handler before accepting to disconnect.
0265      */
0266     if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
0267       return 0;
0268     }
0269     rtems_interrupt_disable(level);
0270 
0271     /*
0272      * Disable interrupt on device
0273      */
0274     if (irq->off)
0275         irq->off(irq);
0276 
0277     if (is_uimb_irq(irq->name)) {
0278       /*
0279        * disable interrupt at UIMB level
0280        */
0281       CPU_irq_disable_at_uimb (irq->name);
0282     }
0283     if (is_usiu_irq(irq->name)) {
0284       /*
0285        * disable interrupt at USIU level
0286        */
0287       CPU_irq_disable_at_usiu (irq->name);
0288     }
0289     if (is_proc_irq(irq->name)) {
0290       /*
0291        * disable exception at processor level
0292        */
0293     }
0294 
0295     /*
0296      * restore the default irq value
0297      */
0298     rtems_hdl_tbl[irq->name] = default_rtems_entry;
0299 
0300     rtems_interrupt_enable(level);
0301 
0302     return 1;
0303 }
0304 
0305 /*
0306  * ---------------- RTEMS Global Irq Handler Mngt Routines ----------------
0307  */
0308 
0309 int CPU_rtems_irq_mngt_set  (rtems_irq_global_settings* config)
0310 {
0311     int                    i;
0312     rtems_interrupt_level  level;
0313 
0314    /*
0315     * Store various code accelerators
0316     */
0317     internal_config         = config;
0318     default_rtems_entry     = config->defaultEntry;
0319     rtems_hdl_tbl       = config->irqHdlTbl;
0320 
0321     rtems_interrupt_disable(level);
0322 
0323     /*
0324      * Start with UIMB IRQ
0325      */
0326     for (i = CPU_UIMB_IRQ_MIN_OFFSET; i <= CPU_UIMB_IRQ_MAX_OFFSET ; i++) {
0327       if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
0328     CPU_irq_enable_at_uimb (i);
0329     if (rtems_hdl_tbl[i].on)
0330         rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
0331       }
0332       else {
0333     if (rtems_hdl_tbl[i].off)
0334         rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
0335     CPU_irq_disable_at_uimb (i);
0336       }
0337     }
0338 
0339     /*
0340      * Continue with USIU IRQ
0341      * Set up internal tables used by rtems interrupt prologue
0342      */
0343     compute_USIU_IvectMask_from_prio ();
0344 
0345     for (i = CPU_USIU_IRQ_MIN_OFFSET; i <= CPU_USIU_IRQ_MAX_OFFSET ; i++) {
0346       if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
0347     CPU_irq_enable_at_usiu (i);
0348     if (rtems_hdl_tbl[i].on)
0349         rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
0350       }
0351       else {
0352     if (rtems_hdl_tbl[i].off)
0353         rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
0354     CPU_irq_disable_at_usiu (i);
0355        }
0356     }
0357 
0358     /*
0359      * Enable all UIMB interrupt lines, then enable at USIU.
0360      */
0361     imb.uimb.umcr |= UIMB_UMCR_IRQMUX(3);
0362     CPU_irq_enable_at_usiu (CPU_UIMB_INTERRUPT);
0363 
0364     /*
0365      * finish with Processor exceptions handled like IRQ
0366      */
0367     for (i = CPU_PROC_IRQ_MIN_OFFSET; i <= CPU_PROC_IRQ_MAX_OFFSET; i++) {
0368       if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
0369     if (rtems_hdl_tbl[i].on)
0370         rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
0371       }
0372       else {
0373     if (rtems_hdl_tbl[i].off)
0374         rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
0375       }
0376     }
0377     rtems_interrupt_enable(level);
0378     return 1;
0379 }
0380 
0381 int CPU_rtems_irq_mngt_get(rtems_irq_global_settings** config)
0382 {
0383     *config = internal_config;
0384     return 0;
0385 }
0386 
0387 
0388 /*
0389  * High level IRQ handler called from shared_raw_irq_code_entry
0390  */
0391 void C_dispatch_irq_handler (MPC5XX_Interrupt_frame *frame, unsigned int excNum)
0392 {
0393   register unsigned int irq;
0394   register unsigned uimbIntr;                 /* boolean */
0395   register unsigned oldMask;              /* old siu pic masks */
0396   register unsigned msr;
0397   register unsigned new_msr;
0398 
0399   /*
0400    * Handle decrementer interrupt
0401    */
0402   if (excNum == ASM_DEC_VECTOR) {
0403     _CPU_MSR_GET(msr);
0404     new_msr = msr | MSR_EE;
0405     _CPU_MSR_SET(new_msr);
0406 
0407     rtems_hdl_tbl[CPU_DECREMENTER].hdl(rtems_hdl_tbl[CPU_DECREMENTER].handle);
0408 
0409     _CPU_MSR_SET(msr);
0410     return;
0411   }
0412 
0413   /*
0414    * Handle external interrupt generated by USIU on PPC core
0415    */
0416   while ((ppc_cached_irq_mask & usiu.sipend) != 0) {
0417     irq = (usiu.sivec >> 26);
0418     uimbIntr = (irq == CPU_UIMB_INTERRUPT);
0419     /*
0420      * Disable the interrupt of the same and lower priority.
0421      */
0422     oldMask = ppc_cached_irq_mask;
0423     ppc_cached_irq_mask = oldMask & USIU_IvectMask[irq];
0424     usiu.simask = ppc_cached_irq_mask;
0425     /*
0426      * Acknowledge current interrupt. This has no effect on internal level
0427      * interrupts.
0428      */
0429     usiu.sipend = (1 << (31 - irq));
0430 
0431     if (uimbIntr)  {
0432       /*
0433        * Look at the bits set in the UIMB interrupt-pending register.  The
0434        * highest-order set bit indicates the handler we will run.
0435        *
0436        * Unfortunately, we can't easily mask individual UIMB interrupts
0437        * unless they use USIU levels 0 to 6, so we must mask all low-level
0438        * (level > 7) UIMB interrupts while we service any interrupt.
0439        */
0440       int uipend = imb.uimb.uipend << 8;
0441 
0442       if (uipend == 0) {    /* spurious interrupt?  use last vector */
0443         irq = CPU_UIMB_IRQ_MAX_OFFSET;
0444       }
0445       else {
0446         irq = CPU_UIMB_IRQ_MIN_OFFSET;
0447         for ( ; (uipend & 0x8000000) == 0; uipend <<= 1) {
0448           irq++;
0449         }
0450       }
0451     }
0452     _CPU_MSR_GET(msr);
0453     new_msr = msr | MSR_EE;
0454     _CPU_MSR_SET(new_msr);
0455 
0456     rtems_hdl_tbl[irq].hdl(rtems_hdl_tbl[irq].handle);
0457 
0458     _CPU_MSR_SET(msr);
0459 
0460     ppc_cached_irq_mask = oldMask;
0461     usiu.simask = ppc_cached_irq_mask;
0462   }
0463 }