www.pudn.com > MPC8241BSP.rar > mpc8240Epic.c


/* mpc8240Epic.c - EPIC Interrupt Controller driver */ 
 
/* Copyright 1984-2000 Wind River Systems, Inc. */ 
/* Copyright 1996-2000 Motorola, Inc., All Rights Reserved */ 
#include "copyright_wrs.h" 
 
/* 
modification history 
-------------------- 
01a,28feb00,rhk  created from version 01d, MV2100 BSP. 
*/ 
 
/* 
DESCRIPTION 
 
This module implements the EPIC interrupt controller functionality. 
*/ 
 
 
/* includes */ 
 
#include "mpc8240.h" 
#include "mpc8240Epic.h" 
 
/* defines */ 
 
/* globals */ 
 
IMPORT STATUS	      (*_func_intConnectRtn) (VOIDFUNCPTR *, VOIDFUNCPTR, int); 
IMPORT int	      (*_func_intEnableRtn) (int); 
IMPORT int	      (*_func_intDisableRtn) (int); 
IMPORT UINT32 		sysPciInLong (UINT32); 
IMPORT void   		sysPciOutLong (UINT32, UINT32); 
IMPORT STATUS		excIntConnect (VOIDFUNCPTR *, VOIDFUNCPTR); 
IMPORT UINT		sysGetMpuSpd(void); 
IMPORT void		sysDecDelay (UINT usDelay); 
void			sysEpicIntHandler (void); 
 
/* system interrupt table */ 
 
#define SYSTEM_INTERRUPT_TABLE_SIZE     256 
 
INT_HANDLER_DESC * sysIntTbl [SYSTEM_INTERRUPT_TABLE_SIZE]; 
 
#ifdef INCLUDE_INSTRUMENTATION 
IMPORT	int             evtTimeStamp; 
#endif 
 
/* forward declarations */ 
 
LOCAL int       getEpicVecAddr (int); 
LOCAL STATUS	sysEpicIntConnect (VOIDFUNCPTR * vector, VOIDFUNCPTR routine, 
                                   int parameter); 
LOCAL int	sysEpicIntEnable (int); 
LOCAL int	sysEpicIntDisable (int); 
 
LOCAL int 	getEpicPrio(UINT32 vecRegAddr); 
 
#ifndef CPU_INT_LOCK 
#   define CPU_INT_LOCK(x) \ 
	(*x = intLock ()) 
#endif 
 
#ifndef CPU_INT_UNLOCK 
#   define CPU_INT_UNLOCK(data) \ 
	(intUnlock (data)) 
#endif 
 
 
/******************************************************************************* 
* 
* epicCurTaskPrioSet - set the priority of the current task. 
* 
* NOTES 
* 
* epicCurTaskPrioSet sets the priority of the Processor Current Task 
* Priority register to the value of the prioNum parameter.  This function 
* should be called after sysEpicInit() to lower the priority of the processor 
* current task. Note that valid priority values are 0 through 15 (15 being 
* the highest priority) 
* 
* NOMANUAL 
*  
* RETURNS: previous priority of the task. 
* 
* This function added by xdg, 00.10.13 
*/ 
 
ULONG epicCurTaskPrioSet 
    ( 
    int prioNum 
    ) 
    { 
    ULONG oldPrio; 
 
    if ((prioNum < 0) || (prioNum > 15)) 
    { 
        return (EPIC_INV_PRIO_ERROR); 
	} 
 
	oldPrio = sysPciInLong ((UINT32)EPIC_CUR_TASK_PRI_REG); 
     
    sysPciOutLong ((UINT32)EPIC_CUR_TASK_PRI_REG, (UINT32)prioNum); 
 
    return (oldPrio); 
    } 
 
 
/****************************************************************************** 
* 
* sysEpicInit - initialize the EPIC 
* 
* This function initializes the Embedded Programmable Interrupt Controller  
* (EPIC) contained in the Mpc8240 chip. 
* 
* It first initializes the system vector table, connects the EPIC interrupt 
* handler to the PPC external interrupt and attaches the local EPIC routines 
* for interrupt connecting, enabling and disabling to the corresponding system 
* routine pointers. 
* 
* It then initializes the EPIC registers, clears any pending EPIC interrupts, 
* and enables interrupt handling by the EPIC. 
* 
* RETURNS: OK always 
*/ 
STATUS sysEpicInit (void) 
    { 
    int			i; 
    UINT32 temp = 0; 
 
    /* Initialize the interrupt table */ 
 
    for (i = 0; i < SYSTEM_INTERRUPT_TABLE_SIZE; i++) 
        sysIntTbl[i] = NULL; 
     
    /*reset the EPIC unit*/ 
	sysPciOutLong((UINT32)EPIC_GLOBAL_CONFIG_REG, 0x80000000); 
	 
	/* wait for the reset sequence to be completed */ 
	while(sysPciInLong((UINT32)EPIC_GLOBAL_CONFIG_REG)&0x80000000) 
		{ 
		; 
		} 
	 
 
    /* Initialze the global timer frequency to the MPU speed. */ 
#if(0) 
    sysPciOutLong((UINT32)EPIC_TIMER_FREQ_REG, sysGetMpuSpd()); 
#endif 
    /* Initialze the 4 global timer interrupt sources */ 
 
    sysPciOutLong((UINT32)EPIC_TIMER0_VEC_PRI_REG, INIT_TMR_SRC0); 
    sysPciOutLong((UINT32)EPIC_TIMER1_VEC_PRI_REG, INIT_TMR_SRC0); 
    sysPciOutLong((UINT32)EPIC_TIMER2_VEC_PRI_REG, INIT_TMR_SRC0); 
    sysPciOutLong((UINT32)EPIC_TIMER3_VEC_PRI_REG, INIT_TMR_SRC0); 
     
#if(0) 
    /* Initialize the 16 serial interrupt sources */ 
 
    sysPciOutLong((UINT32)EPIC_SER_SRC0_VEC_PRI_REG, INIT_SER_SRC0); 
    sysPciOutLong((UINT32)EPIC_SER_SRC1_VEC_PRI_REG, INIT_SER_SRC1); 
    sysPciOutLong((UINT32)EPIC_SER_SRC2_VEC_PRI_REG, INIT_SER_SRC2); 
    sysPciOutLong((UINT32)EPIC_SER_SRC3_VEC_PRI_REG, INIT_SER_SRC3); 
    sysPciOutLong((UINT32)EPIC_SER_SRC4_VEC_PRI_REG, INIT_SER_SRC4); 
    sysPciOutLong((UINT32)EPIC_SER_SRC5_VEC_PRI_REG, INIT_SER_SRC5); 
    sysPciOutLong((UINT32)EPIC_SER_SRC6_VEC_PRI_REG, INIT_SER_SRC6); 
    sysPciOutLong((UINT32)EPIC_SER_SRC7_VEC_PRI_REG, INIT_SER_SRC7); 
    sysPciOutLong((UINT32)EPIC_SER_SRC8_VEC_PRI_REG, INIT_SER_SRC8); 
    sysPciOutLong((UINT32)EPIC_SER_SRC9_VEC_PRI_REG, INIT_SER_SRC9); 
    sysPciOutLong((UINT32)EPIC_SER_SRC10_VEC_PRI_REG, INIT_SER_SRC10); 
    sysPciOutLong((UINT32)EPIC_SER_SRC11_VEC_PRI_REG, INIT_SER_SRC11); 
    sysPciOutLong((UINT32)EPIC_SER_SRC12_VEC_PRI_REG, INIT_SER_SRC12); 
    sysPciOutLong((UINT32)EPIC_SER_SRC13_VEC_PRI_REG, INIT_SER_SRC13); 
    sysPciOutLong((UINT32)EPIC_SER_SRC14_VEC_PRI_REG, INIT_SER_SRC14); 
    sysPciOutLong((UINT32)EPIC_SER_SRC15_VEC_PRI_REG, INIT_SER_SRC15); 
#else 
    /* Initialize the 4 external interrupt sources */ 
    sysPciOutLong((UINT32)EPIC_EXT_SRC0_VEC_PRI_REG, INIT_EXT_SRC0); 
    sysPciOutLong((UINT32)EPIC_EXT_SRC1_VEC_PRI_REG, INIT_EXT_SRC1); 
    sysPciOutLong((UINT32)EPIC_EXT_SRC2_VEC_PRI_REG, INIT_EXT_SRC2); 
    sysPciOutLong((UINT32)EPIC_EXT_SRC3_VEC_PRI_REG, INIT_EXT_SRC3);             
    sysPciOutLong((UINT32)EPIC_EXT_SRC4_VEC_PRI_REG, INIT_EXT_SRC4); 
     
#endif 
 
    /* Initialize the 2 DMA interrupt sources */ 
 
    sysPciOutLong((UINT32)EPIC_DMA_CHAN0_INTR_VEC_SRC, INIT_DMA_SRC0); 
    sysPciOutLong((UINT32)EPIC_DMA_CHAN1_INTR_VEC_SRC, INIT_DMA_SRC1); 
 
    /* Initialize the I2C unit interrupt source */ 
 
    sysPciOutLong((UINT32)EPIC_I2C_INTR_VEC_SRC, INIT_I2C_SRC); 
 
    /* Initialize the message unit interrupt source. */ 
 
    sysPciOutLong((UINT32)EPIC_MSG_UNIT_INTR_VEC_SRC, INIT_MSGUNIT_SRC); 
 
    sysPciOutLong((UINT32)EPIC_DUART_CHAN1_INTR_VEC_SRC, INIT_DUART_SRC1); 
    sysPciOutLong((UINT32)EPIC_DUART_CHAN2_INTR_VEC_SRC, INIT_DUART_SRC2); 
    /* Setup the EPIC to mixed mode. */ 
 
    temp = sysPciInLong((UINT32)EPIC_GLOBAL_CONFIG_REG ); 
    sysPciOutLong((UINT32)EPIC_GLOBAL_CONFIG_REG, (temp | EPIC_GC_MIXED)); 
 
    /* Set to direct-interrupt mode (5 extern int  sources) and clock ratio to 4. */ 
 
    temp = sysPciInLong((UINT32)EPIC_INTR_CONFIG_REG); 
    temp = temp & (~EPIC_IC_R_MASK);  
    sysPciOutLong((UINT32)EPIC_INTR_CONFIG_REG, (temp |  
                                      (EPIC_IC_R_VALUE << EPIC_IC_R_SHFT))); 
 
	/* Clear all pending interrupt */ 
	while(sysPciInLong((UINT32)EPIC_IACK_REG)!=0xff) 
	{ 
		; 
	} 
 
    /* connect the interrupt demultiplexer to the PowerPC external interrupt */ 
#if(1) 
    excIntConnect ((VOIDFUNCPTR *) _EXC_OFF_INTR, sysEpicIntHandler); 
 
    /* attach the BSP specific routines to the operating system.  */ 
 
    _func_intConnectRtn = sysEpicIntConnect; 
    _func_intEnableRtn = sysEpicIntEnable; 
    _func_intDisableRtn = sysEpicIntDisable; 
#endif 
 
    /* Enable interrupts for this processor (1 and above). */ 
 
	epicCurTaskPrioSet( EPIC_TASK_PRI_0 ); 
     
    return(OK); 
    } 
 
 
/****************************************************************************** 
* 
* sysEpicIntConnect - connect an interrupt handler to the system vector table 
* 
* This function connects an interrupt handler to the system vector table. 
* 
* RETURNS: OK/ERROR. 
*/ 
 
LOCAL STATUS sysEpicIntConnect 
    ( 
    VOIDFUNCPTR *      vector,         /* interrupt vector to attach */ 
    VOIDFUNCPTR        routine,        /* routine to be called */ 
    int                parameter       /* parameter to be passed to routine */ 
    ) 
    { 
    INT_HANDLER_DESC * newHandler; 
    INT_HANDLER_DESC * currHandler; 
    LOCAL_INT_DATA     connect; 
    BOOL		sharing = FALSE; 
 
    /* validate the vector */ 
 
    if (((int)vector < 0) || ((int)vector > (SYSTEM_INTERRUPT_TABLE_SIZE - 1))) 
        return (ERROR); 
  
    /* create a new interrupt handler */ 
 
    if ((newHandler = (INT_HANDLER_DESC *)calloc (1, sizeof (INT_HANDLER_DESC))) 
	 == NULL) 
        return (ERROR); 
  
    /* initialize the new handler */ 
 
    newHandler->vec  = routine; 
    newHandler->arg  = parameter; 
    newHandler->next = NULL; 
 
    /* install the handler in the system interrupt table */ 
 
    if (sysIntTbl[(int)vector] == NULL) 
        { 
        sysIntTbl[(int)vector] = newHandler; /* single int. handler case */ 
        } 
    else 
        { 
        currHandler = sysIntTbl[(int)vector]; /* multiple int. handler case */ 
        while (currHandler->next != NULL) 
            { 
            currHandler = currHandler->next; 
            } 
        currHandler->next = newHandler; 
        sharing = TRUE; 
        } 
 
    /*  
     * if the connect is for an EPIC interrupt,  
     * then store the vector into the appropriate EPIC vector register 
     */ 
 
	if (!sharing) 
	{ 
    connect.regAddr = getEpicVecAddr((int)vector); 
 
    if (connect.regAddr > 0) 
        { 
        /* read the vector register */ 
 
        connect.regVal = sysPciInLong((UINT32)(connect.regAddr)); 
 
        /* store the interrupt vector number */ 
 
        connect.regVal |= (int)vector; 
 
        /* write the contents of the vector register back */ 
 
        sysPciOutLong((UINT32)(connect.regAddr), connect.regVal); 
        } 
       	else 
       	{ 
       		return ERROR;	 
       	} 
	} 
    return (OK); 
    } 
 
 
/****************************************************************************** 
* 
* sysEpicIntEnable - enable an Epic interrupt level 
* 
* This routine enables a specified Epic interrupt level. 
* 
* RETURNS: OK or ERROR if interrupt level not supported 
*/ 
 
LOCAL int sysEpicIntEnable 
    ( 
    int intLevel        /* interrupt level to enable */ 
    ) 
    { 
    LOCAL_INT_DATA	enable; 
	 
	/*int mask;*/ 
    /* Validate vector.  */ 
 
    if (intLevel < 0) 
        return (ERROR); 
 
#if(0) 
	/* add by xdg 00.10.13*/ 
	if(intLevel>=WBPIC_INTERRUPT_BASE && intLevel 0) 
        { 
        /* read the vector register */ 
 
        enable.regVal = sysPciInLong((UINT32)(enable.regAddr)); 
 
        /* enable the interrupt */ 
 
        enable.regVal &= (~INT_MASK_BIT); 
 
        /* write the contents of the vector register back */ 
 
        sysPciOutLong((UINT32)(enable.regAddr), enable.regVal); 
 
        } 
 
    return (OK); 
    } 
 
 
/****************************************************************************** 
* 
* sysEpicIntDisable - disable an Epic interrupt level 
* 
* This routine disables a specified Epic interrupt level. 
* 
* RETURNS: OK or ERROR if interrupt level not supported 
*/ 
 
LOCAL int sysEpicIntDisable 
    ( 
    int intLevel        /* interrupt level to disable */ 
    ) 
    { 
    LOCAL_INT_DATA      disable; 
 	/*int mask;*/ 
 
    /* Validate vector.  */ 
 
    if (intLevel < 0) 
        return (ERROR); 
 
#if(0)  /*because no winbond*/ 
	/* add by xdg 00.10.13*/ 
	if(intLevel>=WBPIC_INTERRUPT_BASE && intLevel 0) 
        { 
        /* read the vector register */ 
 
        disable.regVal = sysPciInLong((UINT32)(disable.regAddr)); 
 
        /* disable the interrupt */ 
 
        disable.regVal |= INT_MASK_BIT; 
 
        /* write the contents of the vector register back */ 
 
        sysPciOutLong((UINT32)(disable.regAddr), disable.regVal); 
        } 
 
    return (OK); 
    } 
 
 
/******************************************************************************* 
* 
* sysEpicIntHandlerExec  - execute the handlers for a given vector 
*  
* This routine executes all the handlers chained to a given vector. 
* If a vector has no handlers attached to it, a logMsg is generated. 
* 
* NOMANUAL 
* 
* RETURNS: N/A 
* This functiong added by xdg, 00.10.13 
*/ 
 
void sysEpicIntHandlerExec 
    ( 
    UCHAR intVec 
    ) 
	{     
    INT_HANDLER_DESC * pCurrHandler; 
 
    /*  call each respective interrupt handler */	 
 
    if ((pCurrHandler = sysIntTbl [intVec]) == NULL) 
    { 
		logMsg ("uninitalized PIC interrupt vector %x\r\n", 
                intVec, 0,0,0,0,0); 
    } 
    else 
    { 
 
		/* call Each respective chained interrupt handler  */ 
 
		while (pCurrHandler != NULL) 
    	{ 
  	    	(*pCurrHandler->vec) (pCurrHandler->arg); 
	    	pCurrHandler = pCurrHandler->next; 
    	} 
    } 
	} 
 
/*add by zhu*/ 
#ifndef PPC_EIEIO_SYNC 
#define PPC_EIEIO_SYNC	__asm__ volatile ("   eieio;  sync")	 
#endif 
 
void sysEUMBBARWrite 
    ( 
    ULONG regNum, 
    ULONG regVal 
    ) 
    { 
   
    *(ULONG *) (EPIC_EOI_REG + regNum) = LONGSWAP(regVal); 
    PPC_EIEIO_SYNC;   
    return ; 
    } 
 
 
/****************************************************************************** 
* 
* sysEpicIntHandler - handle an interrupt received at the Epic 
*  
* This routine will process interrupts received from PCI or ISA devices as 
* these interrupts arrive via the EPIC.  This routine supports EPIC interrupt 
* nesting. 
* 
* RETURNS: N/A 
*/ 
 
void sysEpicIntHandler  
    ( 
    void 
    ) 
    { 
    UINT32		vecNum; 
    UINT32		vecRegAddr; 
    int			dontCare; 
    int			intPriority; 
 
	int oldLevel; 
 
    /* get the vector from the EPIC IACK reg. */ 
 
    vecNum = sysPciInLong((UINT32)EPIC_IACK_REG); 
 
    vecNum &= VECTOR_MASK; 
 
	/* add by xdg */ 
 
    /* Check for spurious interrupt. */ 
 
    if (vecNum == 0xFF) 
    { 
        /*logMsg ("EPIC Spurious Interrupt!\n", 0,0,0,0,0,0);*/ 
        return; 
    } 
 
#if(1) 
	vecRegAddr = getEpicVecAddr(vecNum); 
	intPriority = getEpicPrio(vecRegAddr); 
	if ((oldLevel = epicCurTaskPrioSet (intPriority)) == EPIC_INV_PRIO_ERROR) 
    { 
		return; 
    } 
#endif 
  
    /* 
     * Allow maskable interrupts to the CPU.  EPIC will hold off 
     * lower and equal interrupts until EPIC_EOI is performed. 
     */ 
 
    CPU_INT_UNLOCK(_PPC_MSR_EE); 
 
#ifdef INCLUDE_INSTRUMENTATION /*should not be defined */ 
    if (evtLogTIsOn) 
      (* _func_evtLogT1_noTS) (EVENT_INT_ENT((int)vecNum), 
			       evtTimeStamp); 
#endif 
 
	sysEpicIntHandlerExec( (UCHAR)vecNum );	 
     
    /* Disable external interrupts. */ 
 
    CPU_INT_LOCK(&dontCare); 
 
    /* 
     * ERRATA FIX: 
     *   sysDecDelay() is needed for Mpc8240(MPC8240) Revision 1.0/1.1 
     *   Errata #2, EPIC Serial Mode Interrupt Twice from Single Source. 
     *   See Mpc8240 Errata Sheet - Version 1.0.3 
     */ 
 
 /*del by zhu , 6.11*/ 
/*    sysDecDelay(1);*/ 
 
    /* Issue an end-of-interrupt to the EPIC */ 
    sysPciOutLong((UINT32)EPIC_EOI_REG, 0); 
	PPC_EIEIO_SYNC; 
 
/* add by xdg */  
#if(1) 
	epicCurTaskPrioSet (oldLevel); 
#endif 
 
    return; 
    } 
 
/****************************************************************************** 
* 
* getEpicVecAddr - get the vector address of an EPIC register  
* 
* This routine returns the appropriate EPIC register address based on the 
* specified EPIC interrupt level. 
* 
* RETURNS: EPIC register address or zero if not a supported level. 
*/ 
 
LOCAL int getEpicVecAddr 
    ( 
    int		intLevel	/* interrupt level to translate */ 
    )  
    { 
    int 	offset = 0; 
 
    /* check for a timer interrupt level */ 
 
    if ((intLevel >= TIMER_INTERRUPT_BASE) &&  
        (intLevel < EXTERNAL_INTERRUPT_BASE)) 
        { 
        offset = intLevel - TIMER_INTERRUPT_BASE; 
        offset = (int)EPIC_TIMER0_VEC_PRI_REG + (offset * REG_OFFSET * 4); 
        } 
 
	/* check for Internal interrupt level */ 
    if ((intLevel >= INTERNAL_INTERRUPT_BASE) &&  
        (intLevel < SERIAL_INTERRUPT_BASE)) 
        { 
        offset = intLevel - INTERNAL_INTERRUPT_BASE; 
        offset = (int)EPIC_I2C_INTR_VEC_SRC + (offset * REG_OFFSET * 2); 
        } 
	 
	 
    /* check for external interrupt level */ 
 
    if ((intLevel >= EXTERNAL_INTERRUPT_BASE) &&  
        (intLevel < INTERNAL_INTERRUPT_BASE)) 
        { 
        offset = intLevel - EXTERNAL_INTERRUPT_BASE; 
        offset = (int)EPIC_EXT_SRC0_VEC_PRI_REG + (offset * REG_OFFSET * 2); 
        } 
  
    /* check for a serial interrupt level */ 
 
    if (intLevel >= SERIAL_INTERRUPT_BASE) 
        { 
        offset = intLevel - SERIAL_INTERRUPT_BASE; 
        offset = (int)EPIC_SER_SRC0_VEC_PRI_REG + (offset * REG_OFFSET * 2); 
    	} 
     
    /*check for Message Unit interrupt level, 8.21, by zhu*/ 
    if (intLevel == MSGUNIT_INT_LVL) 
    	{ 
    	offset = (int)EPIC_MSG_UNIT_INTR_VEC_SRC; 
    	} 
    if ((intLevel >= DUART_INTERRUPT_BASE) &&  
        (intLevel < DUART_INTERRUPT_BASE+2))   
        { 
        offset = intLevel - DUART_INTERRUPT_BASE; 
        offset = (int)EPIC_DUART_CHAN1_INTR_VEC_SRC + (offset * REG_OFFSET * 2); 
    	}  
    return (offset); 
    } 
 
 
/*************************************************** 
* getEpicPrio(UINT32 vecRegAddr) 
* 
* added by xdg, 01.1.9 
*/ 
 
LOCAL int getEpicPrio(UINT32 vecRegAddr) 
{ 
	UINT32 temp; 
 
	temp = sysPciInLong(vecRegAddr); 
	return((temp&EPIC_SRC_PRI_MASK)>>16); 
}