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);
}