📄 sa1100intrctl.c
字号:
/* sa1100IntrCtl.c - Digital Semiconductor SA-1100 interrupt controller driver *//* Copyright 1984-1998, Wind River Systems, Inc. *//*modification history--------------------01b,21sep98,cdp fixed intEnable/Disable(0) (TSR#115062); corrected range check in sa1100IntLvlEnable/Disable.01a,09dec97,cdp created from 01e of ambaIntrCtl.c.*//*This module implements the Digital Semiconductor SA-1100 interrupt controllerdriver.The SA-1100 interrupt controller is a simple interrupt controllerdescribed in full in the Digital StrongARM SA-1100 Data Sheet. Theinterrupt controller has request registers, a mask register, a levelregister and supports level and edge-triggered interrupts. Thislibrary provides the routines to manage interrupts multiplexed by theSA-1100 interrupt controller.The SA-1100 interrupt controller has a number of registers. Those used bythis driver are described below under the symbolic names used herein.SA1100_INT_CSR_ICMR (read/write): this is the "Interrupt Controller MaskRegister" (ICMR) described in the SA-1100 Data Sheet. When this register iswritten, each data bit that is set (1) causes the correspondinginterrupt to be enabled; each data bit that is clear (0) causes thecorresponding interrupt to be disabled. When this register is read, each databit that is set (1) indicates an interrupt source that is enabled.SA1100_INT_CSR_ICIP (read): this is the "Interrupt Controller IRQPending Register" (ICIP) described in the SA-1100 Data Sheet. Whenthis register is read, each data bit that is set (1) indicates aninterrupt source that is both active and enabled (not masked) i.e. caninterrupt the processor. This driver provides a means by which bits inthis register can be permanently masked e.g. if the particularimplementation of SA-1100 returns bits in an undefined state. SettingSA1100_INT_ICIP_MASK to be a bitwise OR of all the valid bitsachieves the required effect. The driver also provides a simpleoptimisation in the interrupt checking routine sa1100IntLvlVecChk(): ifSA1100_INT_ICIP_FIRST_BIT is set to the lowest numbered bit of theinterrupt controller that is used by the BSP, lower-numbered bits thanthis will not be checked. The driver uses an external arraysa1100IntLvlMask[] to determine what should be written toSA1100_INT_CSR_ICMR to change interrupt levels. This should be definedby the BSP such that sa1100IntLvlMask[n] has a bit set (1) for eachinterrupt source that should be enabled for interrupt level <n>. Forexample, sa1100IntLvlMask[0] should be 0 so that all interrupts aredisabled when interrupt level 0 is selected.SA1100_INT_CSR_ICLR (read/write): this is the "Interrupt ControllerLevel Register" (ICLR) described in the SA-1100 Data Sheet. When thisregister is written, each data bit that is set (1) directs theinterrupt associated with that bit position to the processor's FIQinput; Each data bit that is clear (0) directs the interrupt associatedwith that bit position to the processor's IRQ input. When thisregister is read, each data bit that is set (1) indicates that theinterrupt associated with that bit position is directed to theprocessor's FIQ input; each data bit that is clear (0) indicates thatthe interrupt associated with that bit position is directed to theprocessor's IRQ input.SA1100_INT_CSR_ICCR (read/write): this is the "Interrupt ControllerControl Register" (ICCR) described in the SA-1100 Data Sheet. Whenthis register is written, if data bit zero is set (1), the contents ofthe ICMR control which interrupts can bring the SA-1100 out of idlemode; if data bit zero is clear (0), any interrupt can bring theSA-1100 out of idle mode, even if it is masked in the ICMR.The number of interrupts supported by the device i.e. the number ofbits in the SA1100_INT_CSR_ICIP register, is specified bySA1100_INT_NUM_LEVELS.We assume that config.h or <bsp>.h has defined the addresses of thecontroller chips registers: SA1100_INT_CSR_ICMR, SA1100_INT_CSR_ICIP,SA1100_INT_CSR_ICLR, SA1100_INT_CSR_ICCR and also the driverconstants SA1100_INT_ICIP_MASK, SA1100_INT_NUM_LEVELS.This driver assumes that the chip is memory-mapped and does directmemory accesses to the registers which are assumed to be 32 bits wide.If a different access method is needed, the BSP can redefine the macrosSA1100_INT_REG_READ(addr,result) and SA1100_INT_REG_WRITE(addr,data).This driver assumes that interrupt vector numbers are calculated andnot the result of a special cycle on the bus. Vector numbers aregenerated by adding the current interrupt level number toSA1100_INT_VEC_BASE to generate a vector number which the architecturelevel will use to invoke the proper handling routine. If a differentmapping scheme, or a special hardware routine is needed, then the BSPshould redefine the macro SA1100_INT_LVL_VEC_MAP(level,vector) tooverride the version defined in this file.The order of interrupt level priority is undefined at the architecturelevel. In this driver, level 0 is highest and and indicates that alllevels are disabled; level <SA1100_INT_NUM_LEVELS> is the lowest andindicates that all levels are enabled.This driver was designed to support a single instance of a realdevice. At some point it should be upgraded to operate on an objectmodel and to support any number of real devices.The BSP will initialize this driver in sysHwInit2(), after initializingthe main interrupt library, usually intLibInit(). The initializationroutine, sa1100IntDevInit() will setup the interrupt controller device,it will mask off all individual interrupt sources and then set theinterrupt level to enable all interrupts. See sa1100IntDevInit for moreinformation.All of the functions in this library are global. This allows them tobe used by the BSP if it is necessary to create wrapper routines or toincorporate several drivers together as one.*/#include "vxWorks.h"#include "config.h"#include "intLib.h"/* Defines from config.h, or <bsp>.h */#if !defined(SA1100_INT_CSR_ICMR) || !defined(SA1100_INT_CSR_ICIP) || \ !defined(SA1100_INT_CSR_ICLR) || !defined(SA1100_INT_CSR_ICCR) || \ !defined(SA1100_INT_ICIP_MASK) || !defined(SA1100_INT_NUM_LEVELS)# error missing SA-1100 interrupt definitions#endif#ifndef SA1100_INT_ICIP_FIRST_BIT#define SA1100_INT_ICIP_FIRST_BIT 0#endif#define SA1100_INT_VEC_BASE (0x0)/* hardware access methods */#ifndef SA1100_INT_REG_READ#define SA1100_INT_REG_READ(reg,result) \ ((result) = *(volatile UINT32 *)(reg))#endif /* SA1100_INT_REG_READ */#ifndef SA1100_INT_REG_WRITE#define SA1100_INT_REG_WRITE(reg,data) \ (*((volatile UINT32 *)(reg)) = (data))#endif /* SA1100_INT_REG_WRITE *//* Convert level number to vector number */#ifndef SA1100_INT_LVL_VEC_MAP#define SA1100_INT_LVL_VEC_MAP(level, vector) \ ((vector) = ((level) + SA1100_INT_VEC_BASE))#endif /* SA1100_INT_LVL_VEC_MAP *//* Convert pending register value, to a level number */#ifndef SA1100_INT_PEND_LVL_MAP#define SA1100_INT_PEND_LVL_MAP(pendReg, level) \ ((level) = (pendReg))#endif /* SA1100_INT_PEND_LVL_MAP *//* driver constants */#define SA1100_INT_ALL_ENABLED (SA1100_INT_NUM_LEVELS)#define SA1100_INT_ALL_DISABLED 0/* Local data *//* Current interrupt level setting (sa1100IntLvlChg). */LOCAL UINT32 sa1100IntLvlCurrent = SA1100_INT_ALL_DISABLED; /* all 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 sa1100IntLvlEnabled;/* forward declarations */STATUS sa1100IntLvlVecChk (int*, int*);STATUS sa1100IntLvlVecAck (int, int);int sa1100IntLvlChg (int);STATUS sa1100IntLvlEnable (int);STATUS sa1100IntLvlDisable (int);/********************************************************************************* sa1100IntDevInit - 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. On the SA-1100, this configures the chip so* that only unmasked interrupts can bring the CPU out of Idle Mode but* does not write the ICLR to configure whether interrupt sources cause* IRQ or FIQ because that is done in sysALib.s.* * RETURNS: N/A*/void sa1100IntDevInit (void) { /* install the driver routines in the architecture hooks */ sysIntLvlVecChkRtn = sa1100IntLvlVecChk; sysIntLvlVecAckRtn = sa1100IntLvlVecAck; sysIntLvlChgRtn = sa1100IntLvlChg; sysIntLvlEnableRtn = sa1100IntLvlEnable; sysIntLvlDisableRtn = sa1100IntLvlDisable; /* set DIM so that only unmasked interrupts can cause Idle Mode exit */ SA1100_INT_REG_WRITE (SA1100_INT_CSR_ICCR, 1); sa1100IntLvlEnabled = 0; /* all sources disabled */ sa1100IntLvlChg (SA1100_INT_ALL_ENABLED); /* enable all levels */ }/********************************************************************************* sa1100IntLvlVecChk - 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.** 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 sa1100IntLvlVecChk ( int* pLevel, /* ptr to receive old interrupt level */ int* pVector /* ptr to receive current interrupt vector */ ) { int newLevel; UINT32 isr; /* Read pending interrupt register and mask undefined bits */ SA1100_INT_REG_READ (SA1100_INT_CSR_ICIP, isr); isr &= SA1100_INT_ICIP_MASK; /* If no interrupt is pending, return ERROR */ if (isr == 0) return ERROR; newLevel = SA1100_INT_ICIP_FIRST_BIT; /* don't check unused low bits */ /* * Step through the bits looking for a 1. This *will* terminate. * We could use ffsLsb() for this if we don't mind the function call * overhead */ for ( ; (isr & (1 << newLevel)) == 0; ++newLevel) /* do nothing */ ; /* map the interrupting device to an interrupt level number */ SA1100_INT_PEND_LVL_MAP (newLevel, newLevel); /* change to new interrupt level, returning previous level to caller */ *pLevel = sa1100IntLvlChg (newLevel); /* fetch, or compute the interrupt vector number */ SA1100_INT_LVL_VEC_MAP (newLevel, *pVector); return OK; }/********************************************************************************* sa1100IntLvlVecAck - acknowledge the current interrupt** Acknowledge the current interrupt cycle. The level and vector values are* those generated during the sa1100IntLvlVecChk() 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 SA_1100 interrupt* controller does not need an acknowledge cycle.** RETURNS: OK or ERROR if a hardware fault is detected.* ARGSUSED*/STATUS sa1100IntLvlVecAck ( int level, /* old interrupt level to be restored */ int vector /* current interrupt vector, if needed */ ) { /* restore the previous interrupt level */ sa1100IntLvlChg (level); return OK; }/********************************************************************************* sa1100IntLvlChg - 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 sa1100IntLvlEnable() routine.** The specific priority level SA1100_INT_NUM_LEVELS is valid and represents* all levels enabled.** RETURNS: Previous interrupt level.*/int sa1100IntLvlChg ( int level /* new interrupt level */ ) { int oldLevel; oldLevel = sa1100IntLvlCurrent; if (level >= 0 && level <= SA1100_INT_NUM_LEVELS) { /* change current interrupt level */ sa1100IntLvlCurrent = level; } /* Activate the enabled interrupts */ SA1100_INT_REG_WRITE (SA1100_INT_CSR_ICMR, (sa1100IntLvlMask[sa1100IntLvlCurrent] & sa1100IntLvlEnabled)); return oldLevel; }/********************************************************************************* sa1100IntLvlEnable - 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 below the* specified level. 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 sa1100IntLvlEnable ( int level /* level to be enabled */ ) { int key; if (level < 0 || level >= SA1100_INT_NUM_LEVELS) return ERROR; /* set bit in enable mask */ key = intLock (); sa1100IntLvlEnabled |= (1 << level); intUnlock (key); sa1100IntLvlChg (-1); /* reset current mask */ return OK; }/********************************************************************************* sa1100IntLvlDisable - 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* below the specified level.** RETURNS: OK or ERROR, if the specified interrupt level cannot be disabled.*/STATUS sa1100IntLvlDisable ( int level /* level to be disabled */ ) { int key; if (level < 0 || level >= SA1100_INT_NUM_LEVELS) return ERROR; /* clear bit in enable mask */ key = intLock (); sa1100IntLvlEnabled &= ~(1 << level); intUnlock (key); sa1100IntLvlChg (-1); /* reset current mask */ return OK; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -