ambaIntrCtl.c
资源名称:ixp425BSP.rar [点击查看]
上传用户:luoyougen
上传日期:2008-05-12
资源大小:23136k
文件大小:19k
源码类别:
VxWorks
开发平台:
C/C++
- /* ambaIntrCtl.c - AMBA interrupt controller driver */
- /* Copyright 1984-1998, Wind River Systems, Inc. */
- /*
- modification history
- --------------------
- 01i,03dec01,rec fix compiler warnings
- 01h,21sep98,cdp fixed intEnable/Disable(0) (TSR#115062);
- corrected range check in ambaIntLvlEnable/Disable;
- made AMBA_INT_CSR_MASK optional;
- added configurable interrupt priorities.
- 01f,10mar98,jpd layout tidying.
- 01e,13nov97,cdp include intLib.h and remove things pulled in from it;
- make default I/O access 32 (not 16) bits wide; documentation.
- 01d,15aug97,cdp rewritten for new interrupt structure (template 01d).
- 01c,14may97,jpd added conditional use of valid interrupts mask.
- 01b,18feb97,cdp renamed sysIntEnable/Disable and made global with result.
- Tidyup for mangen. intEnable/Disable now return int.
- 01a,13dec96,cdp created from target/config/pid7t/intPid.c.
- */
- /*
- This module implements the AMBA interrupt controller driver.
- The AMBA interrupt controller is a simple, generic interrupt controller
- described in full in the ARM Reference Peripherals Specification. The
- interrupt controller has status and request registers, separate enable
- and disable registers and supports level-sensitive interrupts. This
- library provides the routines to manage interrupts multiplexed by the
- AMBA interrupt controller.
- The AMBA interrupt controller has a number of registers. Those used by
- this driver are described below under the symbolic names used herein.
- AMBA_INT_CSR_ENB (write): this is the "Enable Set" register described
- in the ARM Reference Peripherals Specification. When this register is
- written, each data bit that is set (1) causes the corresponding
- interrupt to be enabled. Bits that are clear (0) have no effect.
- AMBA_INT_CSR_DIS (write): this is the "Enable Clear" register which has
- the opposite effect. When this register is written, each data bit that
- is set (1) causes the corresponding interrupt to be disabled. Bits
- that are clear (0) have no effect.
- AMBA_INT_CSR_PEND (read): this is the "Interrupt Request" register.
- When this register is read, each data bit that is set (1) indicates an
- interrupt source that is both active and enabled i.e. can interrupt the
- processor. This driver provides a means by which bits in this register
- can be masked if the particular implementation of AMBA returns bits in
- an undefined state. Setting AMBA_INT_CSR_MASK to be a bitwise OR of
- all the valid bits achieves the required effect. If
- AMBA_INT_PRIORITY_MAP is not #defined (see below), the driver also uses
- an external array ambaIntLvlMask[] to determine what should be written
- to AMBA_INT_CSR_ENB to change interrupt levels. This should be defined
- by the BSP such that ambaIntLvlMask[n] has a bit set (1) for each
- interrupt source that should be enabled for interrupt level <n>. For
- example, ambaIntLvlMask[0] should be 0 so that all interrupts are
- disabled when interrupt level 0 is selected.
- The number of interrupts supported by the device i.e. the number of
- bits in the AMBA_INT_CSR_PEND register, is specified by
- AMBA_INT_NUM_LEVELS.
- We assume that config.h or <bsp>.h has defined the addresses of the
- controller chips registers: AMBA_INT_CSR_ENB, AMBA_INT_CSR_DIS and
- AMBA_INT_CSR_PEND and also the driver constants AMBA_INT_CSR_MASK (if
- required) and AMBA_INT_NUM_LEVELS.
- This driver assumes that the chip is memory-mapped and does direct
- memory accesses to the registers which are assumed to be 32 bits wide.
- If a different access method is needed, the BSP can redefine the macros
- AMBA_INT_REG_READ(addr,result) and AMBA_INT_REG_WRITE(addr,data).
- This driver assumes that interrupt vector numbers are calculated and
- not the result of a special cycle on the bus. Vector numbers are
- generated by adding the current interrupt level number to
- AMBA_INT_VEC_BASE to generate a vector number which the architecture
- level will use to invoke the proper handling routine. If a different
- mapping scheme, or a special hardware routine is needed, then the BSP
- should redefine the macro AMBA_INT_LVL_VEC_MAP(level,vector) to
- override the version defined in this file.
- This driver was designed to support a single instance of a real
- device. At some point it should be upgraded to operate on an object
- model and to support any number of real devices.
- Priorities
- ==========
- The order of interrupt level priority is undefined at the architecture
- level. In this driver, level 0 is highest and and indicates that all
- levels are disabled; level <AMBA_INT_NUM_LEVELS> is the lowest and
- indicates that all levels are enabled.
- By default, this driver implements a least-significant bit first
- interrupt priority scheme (this is compatible with earlier versions of
- this driver) which requires a definition of ambaIntLvlMask (see above).
- If required, the driver can be compiled to implement a BSP-configurable
- interrupt priority scheme by #defining AMBA_INT_PRIORITY_MAP. For this
- model, the BSP should define an array of int called ambaIntLvlPriMap,
- each element of which is a bit number to be polled. The list should be
- terminated with an entry containing -1. This list is used in the
- interrupt handler to check bits in the requested order and is also used
- to generate a map of interrupt source to new interrupt level such that
- whilst servicing an interrupt, all interrupts defined by the BSP to be
- of lower priority than that interrupt are disabled. Interrupt sources
- not in the list are serviced after all others in least-significant bit
- first priority. (Note that the list is a list of ints rather than
- bytes because it causes the current compiler to generate faster code.)
- Note that in this priority system, intLevelSet(n) does not necessarily
- disable interrupt bit n and all lower-priority ones but uses
- ambaIntLvlPriMap to determine which interrupts should be masked e.g.
- if ambaIntLvlPriMap[] contains { 9, 4, 8, 5, -1 }, intLevelSet(0)
- disables all interrupt bits; intLevelSet(1) enables interrupt bit 9 but
- disables interrupts 4, 8, 5 and all others not listed; intLevelSet(3)
- enables interrupt bits 9, 4 and 8 but disables all others. This
- enabling of interrupts only occurs if the interrupt has been explicitly
- enabled via a call to ambaIntLvlEnable().
- If the list is empty (contains just a terminator) or ambaIntLvlPriMap
- is declared as an int pointer of value 0 (this is more efficient),
- interrupts are handled as least-significant bit is highest priority.
- The BSP will initialize this driver in sysHwInit2(), after initializing
- the main interrupt library, usually intLibInit(). The initialization
- routine, ambaIntDevInit() will setup the interrupt controller device,
- it will mask off all individual interrupt sources and then set the
- interrupt level to enable all interrupts. See ambaIntDevInit for more
- information.
- All of the functions in this library are global. This allows them to
- be used by the BSP if it is necessary to create wrapper routines or to
- incorporate several drivers together as one.
- */
- #include "vxWorks.h"
- #include "config.h"
- #include "intLib.h"
- IMPORT int ffsLsb (UINT32);
- /* Defines from config.h, or <bsp>.h */
- #if !defined (AMBA_INT_CSR_PEND) || !defined (AMBA_INT_CSR_ENB) ||
- !defined (AMBA_INT_NUM_LEVELS) || !defined (AMBA_INT_CSR_DIS)
- # error missing AMBA interrupt definitions
- #endif
- #define AMBA_INT_VEC_BASE (0x0)
- /* hardware access methods */
- #ifndef AMBA_INT_REG_READ
- #define AMBA_INT_REG_READ(reg,result)
- ((result) = *(volatile UINT32 *)(reg))
- #endif /*AMBA_INT_REG_READ*/
- #ifndef AMBA_INT_REG_WRITE
- #define AMBA_INT_REG_WRITE(reg,data)
- (*((volatile UINT32 *)(reg)) = (data))
- #endif /*AMBA_INT_REG_WRITE*/
- /* Convert level number to vector number */
- #ifndef AMBA_INT_LVL_VEC_MAP
- #define AMBA_INT_LVL_VEC_MAP(level, vector)
- ((vector) = ((level) + AMBA_INT_VEC_BASE))
- #endif /* AMBA_INT_LVL_VEC_MAP */
- #ifndef AMBA_INT_PRIORITY_MAP
- /* Convert pending register value, to a level number */
- #ifndef AMBA_INT_PEND_LVL_MAP
- #define AMBA_INT_PEND_LVL_MAP(pendReg, level)
- ((level) = (pendReg))
- #endif /* AMBA_INT_PEND_LVL_MAP */
- #endif /* AMBA_INT_PRIORITY_MAP */
- /* driver constants */
- #define AMBA_INT_ALL_ENABLED (AMBA_INT_NUM_LEVELS)
- #define AMBA_INT_ALL_DISABLED 0
- /* Local data */
- /* Current interrupt level setting (ambaIntLvlChg). */
- LOCAL UINT32 ambaIntLvlCurrent = AMBA_INT_ALL_DISABLED; /* all levels disabled*/
- /*
- * A mask word. Bits are set in this word when a specific level
- * is enabled. It is used to mask off individual levels that have
- * not been explicitly enabled.
- */
- LOCAL UINT32 ambaIntLvlEnabled;
- #ifdef AMBA_INT_PRIORITY_MAP
- /*
- * Controller masks: for each interrupt level, this provides a mask for
- * the controller (see IntLvlChg).
- * Mask is 32 bits * (levels + 1)
- */
- LOCAL UINT32 ambaIntLvlMask[(1 + AMBA_INT_NUM_LEVELS) * sizeof(UINT32)];
- /*
- * Map of interrupt bit number to level: if bit n is set, ambaIntLvlMap[n]
- * is the interrupt level to change to such that interrupt n and all lower
- * priority ones are disabled.
- */
- LOCAL int ambaIntLvlMap[AMBA_INT_NUM_LEVELS * sizeof(int)];
- #endif /* AMBA_INT_PRIORITY_MAP */
- /* forward declarations */
- STATUS ambaIntLvlVecChk (int*, int*);
- STATUS ambaIntLvlVecAck (int, int);
- int ambaIntLvlChg (int);
- STATUS ambaIntLvlEnable (int);
- STATUS ambaIntLvlDisable (int);
- /*******************************************************************************
- *
- * ambaIntDevInit - initialize the interrupt controller
- *
- * This routine will initialize the interrupt controller device, disabling all
- * interrupt sources. It will also connect the device driver specific routines
- * into the architecture level hooks. If the BSP needs to create a wrapper
- * routine around any of the arhitecture level routines, it should install the
- * pointer to the wrapper routine after calling this routine.
- *
- * If used with configurable priorities (#define AMBA_INT_PRIORITY_MAP),
- * before this routine is called, ambaIntLvlPriMap should be initialised
- * as a list of interrupt bits to poll in order of decreasing priority and
- * terminated by an entry containing -1. If ambaIntLvlPriMap is a null
- * pointer (or an empty list), the priority scheme used will be
- * least-significant bit first. This is equivalent to the scheme used if
- * AMBA_INT_PRIORITY_MAP is not defined but slightly less efficient.
- *
- * The return value ERROR indicates that the contents of
- * ambaIntLvlPriMap (if used) were invalid.
- *
- * RETURNS: OK or ERROR if ambaIntLvlPriMap invalid.
- */
- int ambaIntDevInit (void)
- {
- #ifdef AMBA_INT_PRIORITY_MAP
- int i, j;
- int level;
- UINT32 bit;
- /* if priorities are supplied, validate the supplied list */
- if (ambaIntLvlPriMap != 0)
- {
- /* first check the list is terminated (VecChk requires this) */
- for (i = 0; i < AMBA_INT_NUM_LEVELS; ++i)
- if (ambaIntLvlPriMap[i] == -1)
- break;
- if (!(i < AMBA_INT_NUM_LEVELS))
- return ERROR; /* no terminator */
- /* now check that all are in range and that there are no duplicates */
- for (i = 0; ambaIntLvlPriMap[i] != -1; ++i)
- if (ambaIntLvlPriMap[i] < 0 ||
- ambaIntLvlPriMap[i] >= AMBA_INT_NUM_LEVELS)
- {
- return ERROR; /* out of range */
- }
- else
- for (j = i + 1; ambaIntLvlPriMap[j] != -1; ++j)
- if (ambaIntLvlPriMap[j] == ambaIntLvlPriMap[i])
- {
- return ERROR; /* duplicate */
- }
- }
- /*
- * Now initialise the mask array.
- * For each level (in ascending order), the mask is the mask of the
- * previous level with the bit for the current level set to enable it.
- */
- ambaIntLvlMask[0] = 0; /* mask for level 0 = all disabled */
- /* do the levels for which priority has been specified */
- level = 1;
- if (ambaIntLvlPriMap != 0)
- {
- for ( ; level <= AMBA_INT_NUM_LEVELS &&
- (i = ambaIntLvlPriMap[level - 1], i >= 0); ++level)
- {
- /* copy previous level's mask to this one's */
- ambaIntLvlMask[level] = ambaIntLvlMask[level - 1];
- /* OR in the bit indicated by the next entry in PriMap[] */
- ambaIntLvlMask[level] |= 1 << i;
- /*
- * set index in level map: to disable this interrupt and all
- * lower-priority ones, select the level one less than this
- */
- ambaIntLvlMap[i] = level - 1;
- }
- }
- /* do the rest of the levels */
- i = 0; /* lowest-numbered interrupt bit */
- for ( ; level <= AMBA_INT_NUM_LEVELS; ++level)
- {
- /* copy previous level's mask to this one's */
- ambaIntLvlMask[level] = ambaIntLvlMask[level - 1];
- /* try to find a bit that has not yet been set */
- for ( ; ; ++i)
- {
- bit = 1 << i;
- if ((ambaIntLvlMask[level] & bit) == 0)
- {
- /* this bit not set so put it in the mask */
- ambaIntLvlMask[level] |= bit;
- /*
- * set index in level map: to disable this interrupt and all
- * lower-priority ones, select the level one less than this
- */
- ambaIntLvlMap[i] = level - 1;
- break;
- }
- }
- }
- #endif
- /* install the driver routines in the architecture hooks */
- sysIntLvlVecChkRtn = ambaIntLvlVecChk;
- sysIntLvlVecAckRtn = ambaIntLvlVecAck;
- sysIntLvlChgRtn = ambaIntLvlChg;
- sysIntLvlEnableRtn = ambaIntLvlEnable;
- sysIntLvlDisableRtn = ambaIntLvlDisable;
- ambaIntLvlEnabled = 0; /* all sources disabled */
- ambaIntLvlChg (AMBA_INT_ALL_ENABLED); /* enable all levels */
- return OK;
- }
- /*******************************************************************************
- *
- * ambaIntLvlVecChk - check for and return any pending interrupts
- *
- * This routine interrogates the hardware to determine the highest priority
- * interrupt pending. It returns the vector associated with that interrupt, and
- * also the interrupt priority level prior to the interrupt (not the
- * level of the interrupt). The current interrupt priority level is then
- * raised to the level of the current interrupt so that only higher priority
- * interrupts will be accepted until this interrupt is finished.
- *
- * This routine must be called with CPU interrupts disabled.
- *
- * The return value ERROR indicates that no pending interrupt was found and
- * that the level and vector values were not returned.
- *
- * RETURNS: OK or ERROR if no interrupt is pending.
- */
- STATUS ambaIntLvlVecChk
- (
- int* pLevel, /* ptr to receive old interrupt level */
- int* pVector /* ptr to receive current interrupt vector */
- )
- {
- int newLevel;
- UINT32 isr;
- #ifdef AMBA_INT_PRIORITY_MAP
- UINT32 *priPtr;
- int bitNum;
- #endif
- /* Read pending interrupt register and mask undefined bits */
- AMBA_INT_REG_READ (AMBA_INT_CSR_PEND, isr);
- #ifdef AMBA_INT_CSR_MASK
- isr &= AMBA_INT_CSR_MASK;
- #endif
- /* If no interrupt is pending, return ERROR */
- if (isr == 0)
- return ERROR;
- #ifdef AMBA_INT_PRIORITY_MAP
- priPtr = (UINT32 *)ambaIntLvlPriMap;
- if (priPtr == 0)
- bitNum = -1;
- else
- {
- /* service interrupts according to priority specifed in map */
- while (bitNum = *priPtr++, bitNum != -1)
- {
- /* bitNum = interrupt bit from priority map */
- if (isr & (1 << bitNum))
- {
- break;
- }
- }
- }
- /*
- * if priority scan didn't find anything, look for any bit set,
- * starting with the lowest-numbered bit
- */
- if (bitNum == -1)
- bitNum = ffsLsb (isr) - 1; /* ffsLsb returns numbers from 1, not 0 */
- /* if no interrupt is pending, return ERROR */
- if (bitNum == -1)
- return ERROR;
- /* map the interrupting device to an interrupt level number */
- newLevel = ambaIntLvlMap[bitNum];
- #else
- /* find first bit set in ISR, starting from lowest-numbered bit */
- if (newLevel = ffsLsb (isr), newLevel == 0)
- return ERROR;
- --newLevel; /* ffsLsb returns numbers from 1, not 0 */
- /* map the interrupting device to an interrupt level number */
- AMBA_INT_PEND_LVL_MAP (newLevel, newLevel);
- #endif /* ifdef AMBA_INT_PRIORITY_MAP */
- /* change to new interrupt level, returning previous level to caller */
- *pLevel = ambaIntLvlChg (newLevel);
- /* fetch, or compute the interrupt vector number */
- #ifdef AMBA_INT_PRIORITY_MAP
- AMBA_INT_LVL_VEC_MAP (bitNum, *pVector);
- #else
- AMBA_INT_LVL_VEC_MAP (newLevel, *pVector);
- #endif
- return OK;
- }
- /*******************************************************************************
- *
- * ambaIntLvlVecAck - acknowledge the current interrupt
- *
- * Acknowledge the current interrupt cycle. The level and vector values are
- * those generated during the ambaIntLvlVecChk() routine for this interrupt
- * cycle. The basic action is to reset the current interrupt and return
- * the interrupt level to its previous setting. Note that the AMBA interrupt
- * controller does not need an acknowledge cycle.
- *
- * RETURNS: OK or ERROR if a hardware fault is detected.
- * ARGSUSED
- */
- STATUS ambaIntLvlVecAck
- (
- int level, /* old interrupt level to be restored */
- int vector /* current interrupt vector, if needed */
- )
- {
- /* restore the previous interrupt level */
- ambaIntLvlChg (level);
- return OK;
- }
- /*******************************************************************************
- *
- * ambaIntLvlChg - change the interrupt level value
- *
- * This routine implements the overall interrupt setting. All levels
- * up to and including the specifed level are disabled. All levels above
- * the specified level will be enabled, but only if they were specifically
- * enabled by the ambaIntLvlEnable() routine.
- *
- * The specific priority level AMBA_INT_NUM_LEVELS is valid and represents
- * all levels enabled.
- *
- * RETURNS: Previous interrupt level.
- */
- int ambaIntLvlChg
- (
- int level /* new interrupt level */
- )
- {
- int oldLevel;
- oldLevel = ambaIntLvlCurrent;
- if (level >= 0 &&
- level <= AMBA_INT_NUM_LEVELS)
- {
- /* change current interrupt level */
- ambaIntLvlCurrent = level;
- }
- /* Switch off all interrupts */
- AMBA_INT_REG_WRITE (AMBA_INT_CSR_DIS, -1);
- /* Activate the enabled interrupts */
- AMBA_INT_REG_WRITE (AMBA_INT_CSR_ENB,
- (ambaIntLvlMask[ambaIntLvlCurrent] & ambaIntLvlEnabled));
- return oldLevel;
- }
- /*******************************************************************************
- *
- * ambaIntLvlEnable - enable a single interrupt level
- *
- * Enable a specific interrupt level. The enabled level will be allowed
- * to generate an interrupt when the overall interrupt level is set to
- * enable interrupts of this priority (as configured by ambaIntLvlPriMap,
- * if appropriate). Without being enabled, the interrupt is blocked
- * regardless of the overall interrupt level setting.
- *
- * RETURNS: OK or ERROR if the specified level cannot be enabled.
- */
- STATUS ambaIntLvlEnable
- (
- int level /* level to be enabled */
- )
- {
- int key;
- if (level < 0 ||
- level >= AMBA_INT_NUM_LEVELS)
- return ERROR;
- /* set bit in enable mask */
- key = intLock ();
- ambaIntLvlEnabled |= (1 << level);
- intUnlock (key);
- ambaIntLvlChg (-1); /* reset current mask */
- return OK;
- }
- /*******************************************************************************
- *
- * ambaIntLvlDisable - disable a single interrupt level
- *
- * Disable a specific interrupt level. The disabled level is prevented
- * from generating an interrupt even if the overall interrupt level is
- * set to enable interrupts of this priority (as configured by
- * ambaIntLvlPriMap, if appropriate).
- *
- * RETURNS: OK or ERROR, if the specified interrupt level cannot be disabled.
- */
- STATUS ambaIntLvlDisable
- (
- int level /* level to be disabled */
- )
- {
- int key;
- if (level < 0 ||
- level >= AMBA_INT_NUM_LEVELS)
- return ERROR;
- /* clear bit in enable mask */
- key = intLock ();
- ambaIntLvlEnabled &= ~(1 << level);
- intUnlock (key);
- ambaIntLvlChg (-1); /* reset current mask */
- return OK;
- }