📄 sa150xintrctl.c
字号:
/* sa150xIntrCtl.c - interrupt controller driver for ARM 1500/1501 *//* Copyright 1998-2003, Wind River Systems, Inc. *//*modification history--------------------01a,21sep98,cdp created from 01a of ambaIntrCtl.c*//*This module implements the interrupt controller driver for theStrong-ARM 1500/1501 interrupt controllers.The interrupt controllers contained within the SA-1500 and SA-1501chips are edge-triggered, flexible controllers for multiple interruptsources. They have registers to configure the priority of interrupts(IRQ or FIQ), an enable/disable register, a register to configurepolarity (inversion of the input signal before edge-capture), status,request and latch set/clear registers. This library provides theroutines to manage interrupts multiplexed by a variable number of thesecontrollers.The following registers are defined in the header file for this library(sa150xIntrCtl.h).SA150X_INT_DESTINATION (read/write): this is the SA-1500 IPRI or theSA-1501 INTDEST register. When this register is written, each bitdetermines whether the corresponding interrupt will be directed to theIRQ or FIQ interrupt. On the versions of these chips available at thetime of writing, the sense of the bits is different between the twochips (0 configures IRQ on SA-1500 but FIQ on SA-1501). To handle thisin a flexible way, the symbol "SA150X_INT_HANDLE_1501_DESTINATION" canbe #defined before including this source file (see below).SA150X_INT_SPECIAL (read/write): this is the SA-1500 IDIR registerwhich is a test register on the SA-1501. This is used duringconfiguration. For details, see the BIU section of the SA-1500documentation.SA150X_INT_ENABLE (read/write): this is the SA-1500 IENAB or theSA-1501 INTENABLE register. When this register is written, each databit that is set (1) causes the corresponding interrupt to be enabled.Each bit that is that is clear (0) causes the corresponding interruptto be disabled. This register controls enabling of FIQs as well asIRQs (the actual destination of the interrupt is controlled bySA150X_INT_DESTINATION).SA150X_INT_POLARITY (read/write): this is the SA-1500 IPOL or SA-1501INTPOL register. When this register is written, each data bit that isset (1) causes the corresponding raw interrupt source to be invertedbefore it is clocked by the edge-capture logic. Each bit that is clear(0) leaves the raw interrupt source unaltered before capture. Thiseffectively controls whether rising or falling edges of the interruptsource cause interrupts. The values read from the status registers(below) take into account what has been written to this register.SA150X_INT_IRQ_REQUEST (read): this is the SA-1500 IRQ or SA-1501INTRIRQ register. When this register is read, each data bit that isset (1) indicates an IRQ interrupt source that is both active andenabled i.e. can interrupt the CPU.SA150X_INT_FIQ_REQUEST (read): this is the SA-1500 FIQ or SA-1501INTRFIQ register. When this register is read, each data bit that isset (1) indicates a FIQ interrupt source that is both active andenabled i.e. can interrupt the CPU. This register is not used byVxWorks.SA150X_INT_STATUS (read): this is the SA-1500 ISTAT or SA-1501INTSTATCLR register. When this register is read, each data bit that isset (1) indicates that an interrupt for the corresponding source ispending. Each data bit that is clear (0) indicates that no suchinterrupt is pending. This is the same register as SA150X_INT_CLEAR.SA150X_INT_CLEAR (write): this is the SA-1500 ICLR or SA-1501INTSTATCLR register. When this register is written, each data bit thatis set (1) clears the corresponding pending interrupt. Each data bitthat is clear (0) has no effect. This is the same register asSA150X_INT_STATUS.SA150X_INT_SOURCE (read): this is the SA-1500 ISRC or SA-1501 INTSRCSETregister. When this register is read, each data bit that is set (1)indicates that the corresponding interrupt is active. Each bit that isclear (0) indicates that the raw interrupt source is inactive. Thevalues read are after the polarity configuration but before theedge-capture logic i.e. they indicate the states of the raw sources.This is the same register as SA150X_INT_SET.SA150X_INT_SET (write): this is the SA-1500 ISET or SA-1501 INTSRCSETregister. When this register is written, each data bit that is set (1)sets the corresponding interrupt to be active. Each bit that is clear(0) has no effect. This is the same register as SA150X_INT_SOURCE.This driver uses an array of "sa150xIntrCtlDetails" to describe themultiple controllers to be handled. At its simplest, each elementgives the base address of the controller. If various features areenabled, other details are required (see below and sa150xIntrCtl.h).The number of devices supported by this driver i.e. the number ofregister blocks is specified by SA150X_INT_NUM_CONTROLLERS. There mustbe this number of entries in the sa150xIntrCtlDetails array.The number of interrupts supported is specified bySA150X_INT_NUM_LEVELS. Thirty-two bits per device are assumed so SA150X_INT_NUM_LEVELS = 32 * SA150X_INT_NUM_CONTROLLERS.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 macrosSA150X_INT_REG_READ(addr,result) and SA150X_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 bit number (bit number +controller_number * 32) to SA150X_INT_VEC_BASE to generate a vectornumber which the architecture level will use to invoke the properhandling routine. If a different mapping scheme, or a special hardwareroutine is needed, then the BSP should redefine the macroSA150X_INT_LVL_VEC_MAP(level,vector) to override the version defined inthe header file for this driver.This driver was designed to support multiple devices. For the SA-1500,it expects one entry in the sa150xIntrCtlDetails array; for the SA-1501(which has two sets of registers), it expects two entries.Priorities==========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 <SA150X_INT_NUM_LEVELS> is the lowest andindicates that all levels are enabled.This driver implements a BSP-configurable interrupt priority scheme.The BSP should define an array of int called sa150xIntLvlPriMap, eachelement of which is a bit number to be polled, where 0 is bit 0 in the0th controller, 31 is bit 31 in the 0th controller, 32 is bit 0 in the1st controller etc.. The list should be terminated with an entrycontaining -1. This list is used in the interrupt handler to checkbits in the requested order and is also used to generate a map ofinterrupt source to new interrupt level such that whilst servicing aninterrupt, all interrupts defined by the BSP to be of lower prioritythan that interrupt are disabled. Interrupt sources not in the listare serviced after all others with the least-significant bit beinghighest priority. (Note that the list is a list of ints rather thanbytes because it causes the current compiler to generate faster code.)Note that in this priority system, intLevelSet(n) does not necessarilydisable interrupt bit n and all lower-priority ones but usessa150xIntLvlPriMap to determine which interrupts should be masked e.g.if sa150xIntLvlPriMap[] contains { 9, 4, 8, 5, -1 }, intLevelSet(0)disables all interrupt bits; intLevelSet(1) enables interrupt bit 9 butdisables interrupts 4, 8, 5 and all others not listed; intLevelSet(3)enables interrupt bits 9, 4 and 8 but disables all others. Thisenabling of interrupts only occurs if the interrupt has been explicitlyenabled via a call to sa150xIntLvlEnable().If the list is empty (contains just a terminator) or sa150xIntLvlPriMapis declared as an int pointer of value 0 (this is more efficient),interrupts are handled as least-significant bit is highest priority.FIQs====Because the SA150X_INT_ENABLE register controls the enabling of FIQ aswell as IRQ (see above), individual FIQs *must* be enabled/disabled bycalling the routines in this file (sa150xIntFiqEnable andsa150xIntFiqDisable) rather than by direct accesses to the register.Otherwise, each IRQ will cause FIQs previously enabled to be disabled.In other respects, this driver (and VxWorks) do not support FIQ (seethe ARM Architecture Supplement).Special features================This driver can be compiled with certain features enabled by predefiningcertain symbols (e.g. in bsp.h or config.h). Some have been described abovebut here is the complete list.#define SA150X_INT_INITIALISE_SOURCESConfigures the driver so that the sa150xIntDevInit() call sets all interruptsources to IRQ, disables and clears them all and sets their polarity.#define SA150X_INT_HANDLE_1501_DESTINATIONConfigures the driver to handle the different meaning of bits withinthe DESTINATION register on different SA-150X chips. If this symbol isdefined, each element of the sa150xIntrCtlDetails array must have anextra field to modify the value written to the DESTINATION register(the value specified in the structure is exclusive ORred with the valueto be written to the register).#define SA150X_INT_RETRY_READSConfigures the driver to retry reads from the controller, where safe,to attempt to cope with corruption of values read.#define SA150X_INT_RETRY_WRITESConfigures the driver to retry writes to the controller, where safe, toattempt to cope with corruption of values written.#define SA150X_INT_CACHE_IRQ_REQUESTConfigures the driver to cache the interrupt request registers i.e. foreach interrupt request, the IRQ_REQUEST registers will only be readonce. Depending on the speed of RAM versus I/O, it is possible thatthis might improve performance but it was originally added to improvereliability on some systems.The BSP will initialize this driver in sysHwInit2(), after initializingthe main interrupt library, usually intLibInit(). The initializationroutine, sa150xIntDevInit() will setup the interrupt controller device,it will mask off all individual interrupt sources and then set theinterrupt level to enable all interrupts. See sa150xIntDevInit formore information.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"#include "drv/intrCtl/sa150xIntrCtl.h"/* imports */IMPORT int ffsLsb(UINT32);/* Defines from config.h, or <bsp>.h */#if !defined(SA150X_INT_NUM_LEVELS) || !defined(SA150X_INT_NUM_CONTROLLERS)#error missing definitions for sa150xIntrCtl.c#endif#if (SA150X_INT_NUM_LEVELS != (SA150X_INT_NUM_CONTROLLERS * 32))#error number of sa150x interrupt levels/controllers incompatible#endif#define SA150X_INT_VEC_BASE (0x0)/* driver constants */#define SA150X_INT_ALL_ENABLED (SA150X_INT_NUM_LEVELS)#define SA150X_INT_ALL_DISABLED 0/* Local data *//* Current interrupt level setting (sa150xIntLvlChg). */LOCAL UINT32 sa150xIntLvlCurrent = SA150X_INT_ALL_DISABLED;/* * Controller masks: for each interrupt level, this provides a mask for * each controller (see IntLvlChg). * Mask is 32 bits/controller * (levels + 1) */LOCAL UINT32 sa150xIntLvlMask[SA150X_INT_NUM_CONTROLLERS * (1 + SA150X_INT_NUM_LEVELS) * sizeof(UINT32)];/* * Map of interrupt bit number to level: if bit n is set, * sa150xIntLvlMap[n] is the interrupt level to change to such that * interrupt n and all lower priority ones are disabled. */LOCAL int sa150xIntLvlMap[SA150X_INT_NUM_LEVELS * sizeof(int)];/* forward declarations */STATUS sa150xIntLvlVecChk (int*, int*);STATUS sa150xIntLvlVecAck (int, int);int sa150xIntLvlChg (int);STATUS sa150xIntLvlEnable (int);STATUS sa150xIntLvlDisable (int);/********************************************************************************* sa150xIntDevInit - 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 architecture level* routines, it should install the pointer to the wrapper routine after* calling this routine.** Before this routine is called, sa150xIntLvlPriMap should be initialised* as a list of interrupt bits to poll in order of decreasing priority and* terminated by <-1>. If sa150xIntLvlPriMap is a null pointer or an* empty list, the priority scheme used will be least-significant bit* first.** The return value ERROR indicates that the contents of* sa150xIntLvlPriMap were invalid.** RETURNS: OK or ERROR if sa150xIntLvlPriMap invalid.*/int sa150xIntDevInit (void) { int i, j; int level, ctl; UINT32 bit; UINT32 val; sa150xIntrCtlDetails *p; /* if priorities are supplied, validate the supplied list */ if (sa150xIntLvlPriMap != 0) { /* first check the list is terminated (VecChk requires this) */ for (i = 0; i < SA150X_INT_NUM_LEVELS; ++i) if (sa150xIntLvlPriMap[i] == -1) break; if (!(i < SA150X_INT_NUM_LEVELS)) return ERROR; /* no terminator */ /* now check that all are in range and that there are no duplicates */ for (i = 0; sa150xIntLvlPriMap[i] != -1; ++i) if (sa150xIntLvlPriMap[i] < 0 || sa150xIntLvlPriMap[i] >= SA150X_INT_NUM_LEVELS) { return ERROR; /* out of range */ } else for (j = i + 1; sa150xIntLvlPriMap[j] != -1; ++j) if (sa150xIntLvlPriMap[j] == sa150xIntLvlPriMap[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. */ for (ctl = 0; ctl < SA150X_INT_NUM_CONTROLLERS; ++ctl) sa150xIntLvlMask[ctl] = 0; /* mask for level 0 = all disabled */ /* do the levels for which priority has been specified */ level = 1; if (sa150xIntLvlPriMap != 0) { for ( ; level <= SA150X_INT_NUM_LEVELS && (i = sa150xIntLvlPriMap[level - 1], i >= 0); ++level) { /* copy previous level's mask to this one's */ for (ctl = 0; ctl < SA150X_INT_NUM_CONTROLLERS; ++ctl) sa150xIntLvlMask[ level * SA150X_INT_NUM_CONTROLLERS + ctl] = sa150xIntLvlMask[ (level - 1) * SA150X_INT_NUM_CONTROLLERS + ctl]; /* OR in the bit indicated by the next entry in PriMap[] */ ctl = i / 32; bit = 1 << (i % 32); sa150xIntLvlMask[level * SA150X_INT_NUM_CONTROLLERS + ctl] |= bit; /* * set index in level map: to disable this interrupt and all * lower-priority ones, select the level one less than this */ sa150xIntLvlMap[i] = level - 1; } } /* do the rest of the levels */ i = 0; /* lowest-numbered interrupt bit */ for ( ; level <= SA150X_INT_NUM_LEVELS; ++level) { /* copy previous level's mask to this one's */ for (ctl = 0; ctl < SA150X_INT_NUM_CONTROLLERS; ++ctl) sa150xIntLvlMask[ level * SA150X_INT_NUM_CONTROLLERS + ctl] = sa150xIntLvlMask[ (level - 1) * SA150X_INT_NUM_CONTROLLERS + ctl]; /* try to find a bit that has not yet been set */ for ( ; ; ++i) { ctl = i / 32; bit = 1 << (i % 32); if ((sa150xIntLvlMask[level * SA150X_INT_NUM_CONTROLLERS + ctl] & bit) == 0) { /* this bit not set so put it in the mask */ sa150xIntLvlMask[level * SA150X_INT_NUM_CONTROLLERS + ctl] |= bit; /* * set index in level map: to disable this interrupt and all * lower-priority ones, select the level one less than this */ sa150xIntLvlMap[i] = level - 1; break; } } } /* install the driver routines in the architecture hooks */ sysIntLvlVecChkRtn = sa150xIntLvlVecChk; sysIntLvlVecAckRtn = sa150xIntLvlVecAck; sysIntLvlChgRtn = sa150xIntLvlChg; sysIntLvlEnableRtn = sa150xIntLvlEnable; sysIntLvlDisableRtn = sa150xIntLvlDisable; /* set up the controllers */ for (i = 0; i < SA150X_INT_NUM_CONTROLLERS; ++i) { p = &sa150xIntrCtl[i];#ifdef SA150X_INT_INITIALISE_SOURCES /* disable all sources */#ifdef SA150X_INT_RETRY_WRITES SA150X_INT_REG_WRITE_RETRY (SA150X_INT_ENABLE (p->base), 0);#else SA150X_INT_REG_WRITE (SA150X_INT_ENABLE (p->base), 0);#endif /* set all sources to IRQ - note: must not retry write (locks up) */ val = 0;#ifdef SA150X_INT_HANDLE_1501_DESTINATION val ^= p->destModifier;#endif SA150X_INT_REG_WRITE (SA150X_INT_DESTINATION (p->base), val); /* set polarity of edge-triggered interrupt to positive */#ifdef SA150X_INT_RETRY_WRITES SA150X_INT_REG_WRITE_RETRY (SA150X_INT_POLARITY (p->base), 0);#else SA150X_INT_REG_WRITE (SA150X_INT_POLARITY (p->base), 0);#endif /* clear all latches */ SA150X_INT_REG_WRITE (SA150X_INT_CLEAR (p->base), ~0);#endif /* SA150X_INT_INITIALISE_SOURCES */ p->enabledIrqs = 0; /* all sources disabled */ } sa150xIntLvlChg (SA150X_INT_ALL_ENABLED); /* enable all levels */ return OK; }/********************************************************************************* sa150xIntLvlConfigure - configure polarity and type of interrupt** This routine configures the polarity and type (edge or level) or an* interrupt. It should be called after initialisation of the driver and* before enabling the level.** The return value ERROR indicates that the parameters were invalid.** RETURNS: OK or ERROR if parameters invalid.*/STATUS sa150xIntLvlConfigure ( int level, /* level to configure */ int features /* edge/level/positive/negative */ ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -