Files
m241Neelima/vxworks-6.9/target/src/drv/intrCtl/ambaIntrCtl.c
T
2026-07-30 11:19:44 +05:30

636 lines
19 KiB
C

/* ambaIntrCtl.c - AMBA interrupt controller driver */
/* Copyright 1984-2005, Wind River Systems, Inc. */
/*
modification history
--------------------
01k,01apr05,rec rmove warnings
01j,09feb05,rec fix manual autogenerate format problems
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;
/* 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.
*/
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, (UINT32)-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;
}