summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/mvme5500/irq/irq.c
blob: 9c22dcb167441f3eefc7d236ae5df3bf83195310 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                                            
                                         
























































                                                                          
                                                             








                                                       
                                                            








                                                      
                                                                  


























                                                                      
                                                                   






















                                                                         
                                                                   





















                                                                           
                                                                    






















                                                                            
                                                                    






















































































                                                                                   












































































































































































                                                                                      

                                







                                                 
                                                                              

















































                                                                              
                                                        



































                                                                    
/*  irq.c
 *
 *  This file contains the implementation of the function 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.com/license/LICENSE.
 *
 *  Special acknowledgement to Till Straumann <strauman@slac.stanford.edu>
 *  for providing inputs to the IRQ handling and optimization.
 *
 *  Modified and added support for the MVME5500 board
 *  Copyright 2003, 2004, Shuchen Kate Feng <feng1@bnl.gov>,
 *                  NSLS,Brookhaven National Laboratory
 *
 */
  
#include <rtems/system.h>
#include <bsp.h>
#include <bsp/irq.h>
#include <rtems/score/thread.h>
#include <rtems/score/apiext.h>
#include <libcpu/raw_exception.h>
#include <libcpu/io.h>
#include <bsp/vectors.h>

#include <rtems/bspIo.h> /* for printk */
#include "bsp/gtreg.h"

#define HI_INT_CAUSE 0x40000000

/*#define DEBUG*/

int gpp_int_error =0;

/*
 * pointer to the mask representing the additionnal irq vectors
 * that must be disabled when a particular entry is activated.
 * They will be dynamically computed from teh prioruty table given
 * in BSP_rtems_irq_mngt_set();
 * CAUTION : this table is accessed directly by interrupt routine
 * 	     prologue.
 */
static unsigned int irq_prio_maskLO_tbl[BSP_MAIN_IRQ_NUMBER];
static unsigned int irq_prio_maskHI_tbl[BSP_MAIN_IRQ_NUMBER];

/*
 * 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;

static unsigned int irqCAUSE[20], irqLOW[20], irqHIGH[20];
static int irqIndex=0;

/*
 * Check if IRQ is a MAIN CPU internal IRQ 
 */
static inline int is_main_irq(const rtems_irq_number irqLine)
{
  return (((int) irqLine <= BSP_MICH_IRQ_MAX_OFFSET) &
	  ((int) irqLine >= BSP_MICL_IRQ_LOWEST_OFFSET)
	 );
}

/*
 * Check if IRQ is a GPP IRQ
 */
static inline int is_gpp_irq(const rtems_irq_number irqLine)
{
  return (((int) irqLine <= BSP_GPP_IRQ_MAX_OFFSET) &
	  ((int) irqLine >= BSP_GPP_IRQ_LOWEST_OFFSET)
	 );
}

/*
 * Check if IRQ is a Porcessor IRQ
 */
static inline int is_processor_irq(const rtems_irq_number irqLine)
{
  return (((int) irqLine <= BSP_PROCESSOR_IRQ_MAX_OFFSET) &
	  ((int) irqLine >= BSP_PROCESSOR_IRQ_LOWEST_OFFSET)
	 );
}

#define GT_GPP_Int1_Cause GT_GPP_Interrupt_Cause+1
#define GT_GPP_Int2_Cause GT_GPP_Interrupt_Cause+2
#define GT_GPP_Int3_Cause GT_GPP_Interrupt_Cause+3

void GT_GPP_IntHandler0()
{

  unsigned  gppCause, irqNum, bitNum;
  int i, found=0;

  gppCause = inb(GT_GPP_Interrupt_Cause) & GT_GPPirq_cache;

  for (i=0; GPP7_0IrqTbl[i]!=-1;i++){
    bitNum =GPP7_0IrqTbl[i];
    if (gppCause & (1<<bitNum)) {
        /* Clear the GPP interrupt cause bit */
      outb( ~(1<<bitNum), GT_GPP_Interrupt_Cause);/* Till Straumann */
        found = 1; 
        irqNum = bitNum+BSP_GPP_IRQ_LOWEST_OFFSET;
        /* call the necessary interrupt handlers */
        if (rtems_hdl_tbl[irqNum].hdl != default_rtems_entry.hdl)
	   rtems_hdl_tbl[irqNum].hdl(rtems_hdl_tbl[irqNum].handle);
        else
	   gpp_int_error= bitNum; /*GPP interrupt bitNum not connected */
    }
  }
  if ( !found) gpp_int_error = 33; /* spurious GPP interrupt  */
}

void GT_GPP_IntHandler1()
{
  unsigned  gppCause, irqNum, bitNum;
  int i, found=0;

  gppCause = inb(GT_GPP_Int1_Cause) & (GT_GPPirq_cache>>8);

  for (i=0; GPP15_8IrqTbl[i]!=-1;i++){
    bitNum =GPP15_8IrqTbl[i];
    if (gppCause & (1<<bitNum)) {
        /* Clear the GPP interrupt cause bit */
        outb( ~(1<<bitNum), GT_GPP_Int1_Cause); /* Till Straumann */
        found = 1; 
        irqNum = bitNum+BSP_GPP8_IRQ_OFFSET;
        /* call the necessary interrupt handlers */
        if (rtems_hdl_tbl[irqNum].hdl != default_rtems_entry.hdl)
	   rtems_hdl_tbl[irqNum].hdl(rtems_hdl_tbl[irqNum].handle);
        else
	   gpp_int_error= bitNum+8; /*GPP interrupt bitNum not connected */
    }
  }
  if ( !found) gpp_int_error = 33; /* spurious GPP interrupt  */
}
void GT_GPP_IntHandler2()
{
  unsigned  gppCause, irqNum, bitNum;
  int i, found=0;

  gppCause = inb(GT_GPP_Int2_Cause) & (GT_GPPirq_cache>>16);

  for (i=0; GPP23_16IrqTbl[i]!=-1;i++){
    bitNum =GPP23_16IrqTbl[i];
    if (gppCause & (1<<bitNum)) {
        /* Clear the GPP interrupt cause bit */
        outb( ~(1<<bitNum), GT_GPP_Int2_Cause);
        found = 1; 
        irqNum = bitNum+BSP_GPP16_IRQ_OFFSET;
        /* call the necessary interrupt handlers */
        if (rtems_hdl_tbl[irqNum].hdl != default_rtems_entry.hdl)
	    rtems_hdl_tbl[irqNum].hdl(rtems_hdl_tbl[irqNum].handle);
        else
	   gpp_int_error= bitNum+16; /*GPP interrupt bitNum not connected */
    }
  }
  if ( !found) gpp_int_error = 33; /* spurious GPP interrupt  */
}

void GT_GPP_IntHandler3()
{
  unsigned  gppCause, irqNum, bitNum;
  int i, found=0;

  gppCause = inb(GT_GPP_Int3_Cause) & (GT_GPPirq_cache>>24);

  for (i=0; GPP31_24IrqTbl[i]!=-1;i++){
    bitNum=GPP31_24IrqTbl[i];
    if (gppCause & (1<<bitNum)) {
        /* Clear the GPP interrupt cause bit */
        outb(~(1<<bitNum), GT_GPP_Int3_Cause);
        found = 1; 
        irqNum = bitNum+BSP_GPP24_IRQ_OFFSET;
        /* call the necessary interrupt handlers */
        if (rtems_hdl_tbl[irqNum].hdl != default_rtems_entry.hdl)
	    rtems_hdl_tbl[irqNum].hdl(rtems_hdl_tbl[irqNum].handle);
        else
	   gpp_int_error= bitNum+24; /*GPP interrupt bitNum not connected */
    }
  }
  if ( !found) gpp_int_error = 33; /* spurious GPP interrupt  */
}

/*
 * ------------------------ RTEMS Irq helper functions ----------------
 */
 
/*
 * Caution : this function assumes the variable "internal_config"
 * is already set and that the tables it contains are still valid
 * and accessible.
 */
static void compute_GT64260int_masks_from_prio ()
{
  int i,j;
  unsigned long long irq_prio_mask=0;

  /*
   * Always mask at least current interrupt to prevent re-entrance
   */
  for (i=0; i <BSP_MAIN_IRQ_NUMBER; i++) {
    irq_prio_mask = (unsigned long long) (1LLU << i);
    for (j = 0; j <BSP_MAIN_IRQ_NUMBER; j++) {
      /*
       * Mask interrupts at GT64260int level that have a lower priority
       * or <Till Straumann> a equal priority. 
       */
      if (internal_config->irqPrioTbl [i] >= internal_config->irqPrioTbl [j]) {
	 irq_prio_mask |= (unsigned long long)(1LLU << j);
      }
    }

    irq_prio_maskLO_tbl[i] = irq_prio_mask & 0xffffffff;
    irq_prio_maskHI_tbl[i] = (irq_prio_mask>>32) & 0xffffffff;
#ifdef DEBUG
    printk("irq_mask_prio_tbl[%d]:0x%8x%8x\n",i,irq_prio_maskHI_tbl[i],
	   irq_prio_maskLO_tbl[i]);
#endif  
  }
}

/*
 * This function check that the value given for the irq line
 * is valid.
 */

static int isValidInterrupt(int irq)
{
  if ( (irq < BSP_LOWEST_OFFSET) || (irq > BSP_MAX_OFFSET))
    return 0;
  return 1;
}

/*
 * ------------------------ RTEMS Single Irq Handler Mngt Routines ----------------
 */

int BSP_install_rtems_irq_handler  (const rtems_irq_connect_data* irq)
{
    unsigned int 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.
     */
    _CPU_ISR_Disable(level);
    if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
      _CPU_ISR_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;

    if (is_main_irq(irq->name)) {
      /*
       * Enable (internal ) Main Interrupt Cause Low and High
       */
#ifdef DEBUG_IRQ
      printk("main irq %d\n",irq->name);
#endif
      BSP_enable_main_irq(irq->name);
    }
    
    if (is_gpp_irq(irq->name)) {
      /*
       * Enable (external) GPP[x] interrupt 
       */
      BSP_enable_gpp_irq((int) irq->name);
    }

    if (is_processor_irq(irq->name)) {
      /*
       * Enable exception at processor level
       */
    }
    /*
     * Enable interrupt on device
     
    irq->on(irq);*/
    
    _CPU_ISR_Enable(level);

    return 1;
}


int BSP_get_current_rtems_irq_handler	(rtems_irq_connect_data* irq)
{
     if (!isValidInterrupt(irq->name)) {
      return 0;
     }
     *irq = rtems_hdl_tbl[irq->name];
     return 1;
}

int BSP_remove_rtems_irq_handler  (const rtems_irq_connect_data* irq)
{
    unsigned int 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.
     */
    if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
      return 0;
    }
    _CPU_ISR_Disable(level);

    if (is_main_irq(irq->name)) {
      /*
       * disable CPU main interrupt
       */
      BSP_disable_main_irq(irq->name);
    }
    if (is_gpp_irq(irq->name)) {
      /*
       * disable external interrupt
       */
      BSP_disable_gpp_irq(irq->name);
    }
    if (is_processor_irq(irq->name)) {
      /*
       * disable exception at processor level
       */
    }    

    /*
     * Disable interrupt on device
     */
    irq->off(irq);

    /*
     * restore the default irq value
     */
    rtems_hdl_tbl[irq->name] = default_rtems_entry;

    _CPU_ISR_Enable(level);

    return 1;
}

/*
 * ------------------------ RTEMS Global Irq Handler Mngt Routines ----------------
 */

int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config)
{
    int i;
    unsigned int level;
   /*
    * Store various code accelerators
    */
    internal_config 		= config;
    default_rtems_entry 	= config->defaultEntry;
    rtems_hdl_tbl 		= config->irqHdlTbl;

    _CPU_ISR_Disable(level);
    compute_GT64260int_masks_from_prio();

    /*
     * set up internal tables used by rtems interrupt prologue
     */
    /*
     * start with MAIN CPU IRQ
     */
    for (i=BSP_MICL_IRQ_LOWEST_OFFSET; i < BSP_GPP_IRQ_LOWEST_OFFSET ; i++) {
      if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
	BSP_enable_main_irq(i);
	rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
      }
      else {
	rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
	BSP_disable_main_irq(i);
      }
    }
    /*
     * continue with  external IRQ
     */
    for (i=BSP_GPP_IRQ_LOWEST_OFFSET; i<BSP_PROCESSOR_IRQ_LOWEST_OFFSET; i++) {
      if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
	BSP_enable_gpp_irq(i);
	rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
      }
      else {
	rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
	BSP_disable_gpp_irq(i);
      }
    }

    /*
     * finish with Processor exceptions handled like IRQ
     */
    for (i=BSP_PROCESSOR_IRQ_LOWEST_OFFSET; i < BSP_PROCESSOR_IRQ_MAX_OFFSET+1; i++) {
      if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
	rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
      }
      else {
	rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
      }
    }
    _CPU_ISR_Enable(level);
    return 1;
}

int BSP_rtems_irq_mngt_get(rtems_irq_global_settings** config)
{
    *config = internal_config;
    return 0;
}    

int _BSP_vme_bridge_irq = -1;

/*
 * High level IRQ handler called from shared_raw_irq_code_entry
 */
void C_dispatch_irq_handler (CPU_Interrupt_frame *frame, unsigned int excNum)
{
  register unsigned msr;
  register unsigned new_msr;
  unsigned mainCause[2];
  register unsigned selectCause;
  register unsigned oldMask[2]={0,0};
  unsigned i, regNum, irq, bitNum, startIrqNum=0;

  if (excNum == ASM_DEC_VECTOR) {
    _CPU_MSR_GET(msr);
    new_msr = msr | MSR_EE;
    _CPU_MSR_SET(new_msr);
    
    rtems_hdl_tbl[BSP_DECREMENTER].hdl(rtems_hdl_tbl[BSP_DECREMENTER].handle);

    _CPU_MSR_SET(msr);
    return;
    
  }
  selectCause = inl( GT_CPU_SEL_CAUSE); 
  if (selectCause & HI_INT_CAUSE ) {
    mainCause[1]= selectCause & inl(GT_CPU_INT_MASK_HI);
    startIrqNum=32;
  }
  else {
    mainCause[0] =inl(GT_MAIN_INT_CAUSE_LO)&inl(GT_CPU_INT_MASK_LO);
    mainCause[1] =inl(GT_MAIN_INT_CAUSE_HI)&inl(GT_CPU_INT_MASK_HI);
  }

#if 0
  /* very bad practice to put printk here, use only if for debug */
  printk("main 0 %x, main 1 %x \n", mainCause[0],mainCause[1]);
#endif
  oldMask[0]= GT_MAINirqLO_cache;
  oldMask[1]= GT_MAINirqHI_cache;

  for (i=0;mainIrqTbl[i]!=-1;i++) {
    irq=mainIrqTbl[i];
    if ( irq < startIrqNum ) continue;
    regNum = irq/32;
    bitNum = irq % 32;
    if ( mainCause[regNum] & (1<<bitNum)) {
      GT_MAINirqLO_cache=oldMask[0]&(~irq_prio_maskLO_tbl[irq]);
      outl(GT_MAINirqLO_cache, GT_CPU_INT_MASK_LO);
      __asm __volatile("sync");
      GT_MAINirqHI_cache=oldMask[1]&(~irq_prio_maskHI_tbl[irq]);
      outl(GT_MAINirqHI_cache, GT_CPU_INT_MASK_HI);
      __asm __volatile("sync");

      /* <skf> It seems that reading back is necessary to ensure the 
       * interrupt mask updated. Otherwise, spurious interrupt will
       * happen.  However, I do not want to use "while loop" to risk 
       * the CPU stuck.  I wound rather keep track of the interrupt
       * mask if not updated.
       */
      if (((irqLOW[irqIndex]= inl(GT_CPU_INT_MASK_LO))!=GT_MAINirqLO_cache)||
          ((irqHIGH[irqIndex]= inl(GT_CPU_INT_MASK_HI))!=GT_MAINirqHI_cache)){
         irqIndex++;
         irqIndex %=20;
         irqCAUSE[irqIndex] = irq;
      }
      _CPU_MSR_GET(msr);
      new_msr = msr | MSR_EE;
      _CPU_MSR_SET(new_msr);
      rtems_hdl_tbl[irq].hdl(rtems_hdl_tbl[irq].handle);
      _CPU_MSR_SET(msr);
      break;
    }
  }
  GT_MAINirqLO_cache=oldMask[0];
  outl(GT_MAINirqLO_cache, GT_CPU_INT_MASK_LO);
  GT_MAINirqHI_cache=oldMask[1];
  outl(GT_MAINirqHI_cache, GT_CPU_INT_MASK_HI);
}

void _ThreadProcessSignalsFromIrq (BSP_Exception_frame* ctx)
{
  /*
   * Process pending signals that have not already been
   * processed by _Thread_Displatch. This happens quite
   * unfrequently : the ISR must have posted an action
   * to the current running thread.
   */
  if ( _Thread_Do_post_task_switch_extension ||
       _Thread_Executing->do_post_task_switch_extension ) {
    _Thread_Executing->do_post_task_switch_extension = FALSE;
    _API_extensions_Run_postswitch();
  }
  /*
   * I plan to process other thread related events here.
   * This will include DEBUG session requested from keyboard...
   */
}

void BSP_printIRQMask()
{
  int i;

  for (i=0; i< 20; i++)
    printk("IRQ%d : 0x%x %x \n", irqCAUSE[i], irqHIGH[i],irqLOW[i]);
}