GICv2控制器将中断分为两组。配置对应寄存器可以控制这两组中断的使能与禁能。
中断向量使能
中断使能清除寄存器。写入1禁能对应中断向量号,写0没有影响。读取数据表示对应中断向量号的使能状态。每32个中断向量使用一个寄存器位,后续中断向量放在接下来的寄存器地址上。
中断阻塞寄存器。读取为0表示中断不会再任何处理器上挂起,读取为1表示中断挂起在相应的处理器上。写入0没有影响,写入1将原来的非活动中断改为挂起状态或者将活动状态改为活动和挂起状态。
中断清除阻塞寄存器。读取为0表示中断不会再任何处理器上挂起,读取为1表示中断挂起在相应的处理器上。写入0没有影响,写入1挂起状态改为非活动态,将原来的活动和挂起状态改为活动状态。
中断活动寄存器。读0表示中断非活动状态,读1表示中断活动。写入0没有影响,写入1激活对应的中断为活动状态。
中断清除活动寄存器。读0表示中断非活动状态,读1表示中断活动。写入0没有影响,写入1清除中断的激活状态。
中断优先级寄存器。每个中断向量有8位来表示中断的优先级。中断优先级数字越低表示优先级越高。
中断处理器目标寄存器。每个中断向量有8位配置中断向量发送到哪个或者哪些处理器上。配置的一个字节的每一个位表示一个处理器目标。
中断配置寄存器。每个中断向量有两位的配置位,其中高位写1表示中断电平触发,写0表示中断边沿触发。低位是兼容旧版本GIC实现留下的保留位。
TargetListFilter 选择软中断的目标处理器,自主选择触发哪些CPU中断,发送给除自己以外所有处理器或者发送给自己软终端。CPUTargetList 表示软中断触发哪些处理器。SIGIINTID 配置触发的软中断的中断向量号。
软件中断触发寄存器。
CPU处理器接口控制寄存器。
控制GICC_EOIR和GICC_DIR寄存器的功能
控制中断的旁路中断的工作状态。
CBPR控制 GIC_BPR寄存器的工作状态。 FIQEn寄存器控制中断向量组0发出IRQ信号还是FIQ信号。
最低两位控制两个中断向量组的使能。
中断优先级掩码寄存器。CPU只接受中断优先级高于掩码优先级的中断响应,并且根据中断优先级的数量配置对应的位数为0。
中断优先级划分寄存器。根据配置的参数将中断优先级分为组优先级和次优先级。
中断应答寄存器。对于软终端 CPUID 标识出中断请求来源的CPU号。InterruptID表示触发的中断向量号。
中断结束寄存器。CPUID表示软中断从GICC_IAR读取的软件中断请求来源。EIOINTID表示中断结束的中断向量号。
GIC处理器接口识别寄存器。存放了GIC的版本信息和JEP106代码。GIC版本信息应该是0x2,JEP106代码应该是0x43B
/*********************************************************************************************************
** 函数名称: bspIntInit
** 功能描述: 中断系统初始化
** 输 入 : NONE
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID bspIntInit (VOID)
{
ULONG ulCPUId = LW_CPU_GET_CUR_ID();
ARM_GIC armGic;
ARM_GIC_REDIST armGicRedist[4];
armGicRedist[0].AGICRD_ulRedistBase = ALLWINNERT3_GIC_REDIST_BA;
armGicRedist[1].AGICRD_ulRedistBase = ALLWINNERT3_GIC_REDIST_BA;
armGicRedist[2].AGICRD_ulRedistBase = ALLWINNERT3_GIC_REDIST_BA;
armGicRedist[3].AGICRD_ulRedistBase = ALLWINNERT3_GIC_REDIST_BA;
armGic.AGIC_ulDistBase = ALLWINNERT3_GIC_DIST_BA;
armGic.AGIC_uiRedistNum = 4;
armGic.AGIC_pRedist = armGicRedist;
armGicPrimaryInit(ARM_GIC_V2, &armGic);
API_InterVectorIpi(ulCPUId, ulCPUId); /* 安装 CPU 0 IPI 向量 */
API_InterVectorSetFlag(SPI_PIO, LW_IRQ_FLAG_QUEUE);
API_InterVectorSetPriority(SPI_PIO, 10);
}
中断初始化中,给定 GIC 控制器的基地址,初始化 GICv2 版本的中断控制器。如果系统是 SMP 模式,设置CPU的核间中断(软终端)。如果系统工作在 AMP 模式,将 IO 外部中断配置为一个中断向量对应多个中断服务函数的处理模式。
GICv2 版本初始化其实调用的还是v1版本初始化的代码,内部功能兼容。
/*********************************************************************************************************
** 函数名称: armGicV1Init
** 功能描述: 初始化 GIC V1 版本中断控制器
** 输 入 : pcGicName 中断控制器名称
** parmGic 中断控制器结构指针
** parmGicOp 中断控制器操作集
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
INT armGicV1Init (CPCHAR pcGicName, PARM_GIC parmGic, PARM_GIC_OP parmGicOp)
{
PARM_GIC_CPU_INF parmGicInf;
ULONG ulDistBase;
UINT uiInfNum;
UINT uiIrqNum;
INT i;
uiInfNum = parmGic->AGIC_ulCpuInfNum;
ulDistBase = parmGic->AGIC_ulDistBase;
uiIrqNum = GICD_TYPER_IRQS(read32(ulDistBase + GICD_TYPER)); /* 获取支持的中断数 */
if (uiIrqNum > 1020) {
uiIrqNum = 1020;
}
parmGicInf = __SHEAP_ZALLOC(uiInfNum * sizeof(ARM_GIC_CPU_INF));
if (!parmGicInf) {
_ErrorHandle(ENOMEM);
return (PX_ERROR);
}
for (i = 0; i < uiInfNum; i++) {
parmGicInf[i].AGICRD_ulCpuInfBase = parmGic->AGIC_pCpuInf[i].AGICRD_ulCpuInfBase;
}
_G_armGicV1Data.AGICD_pCpuInf = parmGicInf;
_G_armGicV1Data.AGICD_ulDistBase = ulDistBase;
_G_armGicV1Data.AGICD_uiIrqNum = uiIrqNum;
armGicV1DistInit(); /* 初始化 GIC 发布器 */
armGicV1CpuInit(); /* 初始化 CPU 部分 */
parmGicOp->AGIC_pfuncIrqEnable = armGicV1IrqEnable;
parmGicOp->AGIC_pfuncIrqDisable = armGicV1IrqDisable;
parmGicOp->AGIC_pfuncIrqIsEnable = armGicV1IrqIsEnable;
parmGicOp->AGIC_pfuncRaiseSoftIrq = armGicV1RaiseSoftIrq;
parmGicOp->AGIC_pfuncIrqHandle = armGicV1IrqHandle;
parmGicOp->AGIC_pfuncIrqPrioritySet = armGicV1IrqPrioritySet;
parmGicOp->AGIC_pfuncIrqPriorityGet = armGicV1IrqPriorityGet;
parmGicOp->AGIC_pfuncIrqAffinitySet = armGicV1IrqAffinitySet;
parmGicOp->AGIC_pfuncIrqAffinityGet = armGicV1IrqAffinityGet;
parmGicOp->AGIC_pfuncIrqTypeSet = armGicV1IrqTypeSet;
return (ERROR_NONE);
}
获取 GIC 控制器支持的中断向量数量,初始化GIC结构体。分别初始化GIC发布器和GIC处理器,并安装了对应的GIC控制函数。
/*********************************************************************************************************
** 函数名称: armGicV1CpuMaskGet
** 功能描述: 获取 GIC V1 的 CPU 掩码
** 输 入 : ulDistBase Distributor 基址
** 输 出 : CPU 掩码
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static UINT8 armGicV1CpuMaskGet (ULONG ulDistBase)
{
UINT32 uiMask;
INT i;
for (i = 0; i < 32; i += 4) {
uiMask = read32(ulDistBase + GICD_ITARGETSR + i);
uiMask |= uiMask >> 16;
uiMask |= uiMask >> 8;
if (uiMask) {
break;
}
}
return ((UINT8)uiMask);
}
/*********************************************************************************************************
** 函数名称: armGicDistConfig
** 功能描述: GIC Distributor 配置
** 输 入 : ulDistBase Distributor 基址
** uiIrqNum 中断数量
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID armGicDistConfig (ULONG ulDistBase, UINT uiIrqNum)
{
INT i;
//GICD_INT_ACTLOW_LVLTRIG == 0x00
//配置所有的外围设备中断为低电平触发
for (i = 32; i < uiIrqNum; i += 16) { /* SPI 中断默认为低电平触发 */
write32(GICD_INT_ACTLOW_LVLTRIG, ulDistBase + GICD_ICFGR + i / 4);
}
//GICD_INT_DEF_PRI_X4 = 0xa0a0a0a0
//配置所有的外围设备中断优先级为0xa0
for (i = 32; i < uiIrqNum; i += 4) { /* SPI 中断默认的中断优先级 */
write32(GICD_INT_DEF_PRI_X4, ulDistBase + GICD_IPRIORITYR + i);
}
//GICD_INT_EN_CLR_X32 == 0xffffffff
//清除所有外围设备中断的状态
for (i = 32; i < uiIrqNum; i += 32) {
write32(GICD_INT_EN_CLR_X32,
ulDistBase + GICD_ICACTIVER + i / 8); /* 失效所有的 SPI 中断 */
write32(GICD_INT_EN_CLR_X32,
ulDistBase + GICD_ICENABLER + i / 8); /* 禁用所有的 SPI 中断 */
write32(GICD_INT_EN_CLR_X32,
ulDistBase + GICD_ICPENDR + i / 8); /* 清除所有的 SPI 中断 */
}
}
/*********************************************************************************************************
** 函数名称: armGicV1DistInit
** 功能描述: 初始化 GIC V1 Distributor
** 输 入 : NONE
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID armGicV1DistInit (VOID)
{
UINT32 uiMask;
ULONG ulDistBase;
UINT uiIrqNum;
UINT i;
ulDistBase = GIC_DIST_BASE_GET();
uiIrqNum = GIC_IRQ_NUM_GET();
//关闭两个GIC分组的中断
write32(0, ulDistBase + GICD_CTLR); /* 禁用发布器 */
//这里找到前32个中断向量号的CPU目标的设置,默认配置到所有的中断向量上
uiMask = armGicV1CpuMaskGet(ulDistBase); /* 默认路由到 BOOT CPU */
uiMask |= uiMask << 8;
uiMask |= uiMask << 16;
for (i = 32; i < uiIrqNum; i += 4) {
write32(uiMask, ulDistBase + GICD_ITARGETSR + i * 4 / 4);
}
armGicDistConfig(ulDistBase, uiIrqNum); /* SPI 默认状态设置 */
//打开第0组的中断使能
//SylixOS下没有特地把中断分组,所有的中断reset之后默认都存在0组中
write32(1, ulDistBase + GICD_CTLR); /* 使能发布器 */
}
/*********************************************************************************************************
** 函数名称: armGicCpuConfig
** 功能描述: GIC CPU 端配置
** 输 入 : ulCpuIfBase CPU 端基址
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID armGicCpuConfig (ULONG ulCpuIfBase)
{
INT i;
//GICD_INT_EN_CLR_X32 == 0xffffffff
//清除前32个中断向量的激活状态
write32(GICD_INT_EN_CLR_X32, ulCpuIfBase + GICD_ICACTIVER);
//GICD_INT_EN_CLR_PPI == 0xffff0000
//禁能16~31号中断,这一部分表示私有外设中断
write32(GICD_INT_EN_CLR_PPI, ulCpuIfBase + GICD_ICENABLER);
//GICD_INT_EN_SET_SGI == 0x0000ffff
//使能0~15号中断,这一部分表示软终端,用于核间中断
write32(GICD_INT_EN_SET_SGI, ulCpuIfBase + GICD_ISENABLER);
//GICD_INT_DEF_PRI_X4 = 0xa0a0a0a0
//配置前32个中断的默认中断优先级
for (i = 0; i < 32; i += 4) {
write32(GICD_INT_DEF_PRI_X4,
ulCpuIfBase + GICD_IPRIORITYR + i * 4 / 4); /* 设置 PPI 和 SGI 优先级 */
}
}
/*********************************************************************************************************
** 函数名称: armGicV2CpuInfCheck
** 功能描述: 判断是否是 GIC V2 的 CPU Interface
** 输 入 : ulBase CPU Interface 基址
** 输 出 : LW_TRUE 为 GIC V2,LW_FALSE 不是 GIC V2
** 全局变量:
** 调用模块:
*********************************************************************************************************/
BOOL armGicV2CpuInfCheck (ULONG ulBase)
{
UINT32 uiVal;
//读取GIC的ID寄存器,判断GIC控制器的版本
uiVal = read32(ulBase + GICC_IIDR);
return ((uiVal & 0xff0fff) == 0x02043b);
}
/*********************************************************************************************************
** 函数名称: armGicV1CpuInit
** 功能描述: 初始化 GIC V1 CPU 部分
** 输 入 : NONE
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID armGicV1CpuInit (VOID)
{
ULONG ulCpuInfBase;
UINT32 uiBypass;
INT i;
ulCpuInfBase = GIC_CUR_CPU_INF_BASE_GET();
armGicCpuConfig(ulCpuInfBase);
//GICC_INT_PRI_THRESHOLD == 0xff
//配置CPU接受的中断优先级掩码为最低优先级,CPU可以响应所有的优先级的中断
write32(GICC_INT_PRI_THRESHOLD, ulCpuInfBase + GICC_PMR);
//配置中断优先级的主优先级和次优先级划分,具体见下图
write32(0x0, ulCpuInfBase + GICC_BPR);
if (armGicV2CpuInfCheck(ulCpuInfBase)) {
for (i = 0; i < 4; i++) {
write32(0, ulCpuInfBase + GICC_APR + i * 4);
}
}
uiBypass = read32(ulCpuInfBase + GICC_CTRL);
//关闭中断旁路功能
uiBypass &= GICC_DIS_BYPASS_MASK;
//GICC_ENABLE == 0x1
//打开中断向量组0的使能位
write32(uiBypass | GICC_ENABLE, ulCpuInfBase + GICC_CTRL);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqEnable
** 功能描述: GIC V1 设置中断使能
** 输 入 : ulVector 中断号
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID armGicV1IrqEnable (ULONG ulVector)
{
UINT32 uiMask;
ULONG ulBase;
uiMask = 1 << (ulVector % 32);
ulBase = GIC_DIST_BASE_GET();
write32(uiMask, ulBase + GICD_ISENABLER + (ulVector / 32) * 4);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqDisable
** 功能描述: GIC V1 设置中断禁能
** 输 入 : ulVector 中断号
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID armGicV1IrqDisable (ULONG ulVector)
{
UINT32 uiMask;
ULONG ulBase;
uiMask = 1 << (ulVector % 32);
ulBase = GIC_DIST_BASE_GET();
write32(uiMask, ulBase + GICD_ICENABLER + (ulVector / 32) * 4);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqIsEnable
** 功能描述: 判断 GIC V1 中断是否使能
** 输 入 : ulVector 中断号
** 输 出 : LW_TRUE 中断使能,LW_FALSE 中断禁能
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static BOOL armGicV1IrqIsEnable (ULONG ulVector)
{
UINT32 uiMask;
UINT32 uiReg;
ULONG ulBase;
uiMask = 1 << (ulVector % 32);
ulBase = GIC_DIST_BASE_GET();
uiReg = read32(ulBase + GICD_ICENABLER + (ulVector / 32) * 4);
return ((uiReg & uiMask) ? (LW_TRUE) : (LW_FALSE));
}
/*********************************************************************************************************
** 函数名称: armGicV1RaiseSoftIrq
** 功能描述: GIC V1 发送软中断
** 输 入 : ullCpuId 目标 CPU
** ulVector 中断号
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID armGicV1RaiseSoftIrq (UINT64 ullCpuId, ULONG ulVector)
{
ULONG ulBase;
UINT32 uiVal;
ulBase = GIC_DIST_BASE_GET();
//工作在SMP的模式下,当SMP的系统号为1的时候,软中断发送给本身的CPU上
if (LW_NCPUS == 1) {
write32(2 << 24 | ulVector, ulBase + GICD_SGIR);
return;
}
KN_SMP_WMB();
//(0 << 24)表示发送软中断到指定的CPU上
//((1 << ullCpuId) << 16)表示软终端发送到ullCpuId对应的CPU上
//ulVector表示触发的中断向量号
uiVal = (0 << 24) | (UINT32)((1 << ullCpuId) << 16) | (UINT32)ulVector;
write32(uiVal, ulBase + GICD_SGIR);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqPrioritySet
** 功能描述: GIC V1 中断优先级设置
** 输 入 : ulVector 中断号
** uiPriority 优先级
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT armGicV1IrqPrioritySet (ULONG ulVector, UINT32 uiPriority)
{
ULONG ulBase;
UINT32 uiVal;
ulBase = GIC_DIST_BASE_GET();
uiVal = read32(ulBase + GICD_IPRIORITYR + ulVector / 4 * 4);
uiVal &= ~(0xff << (ulVector % 4 * 8));
//配置中断优先级为新的优先级
uiVal |= (uiPriority & 0xff) << (ulVector % 4 * 8);
write32(uiVal, ulBase + GICD_IPRIORITYR + ulVector / 4 * 4);
return (ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqPriorityGet
** 功能描述: GIC V1 中断优先级获取
** 输 入 : ulVector 中断号
** puiPriority 优先级
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT armGicV1IrqPriorityGet (ULONG ulVector, UINT32 *puiPriority)
{
ULONG ulBase;
UINT32 uiVal;
ulBase = GIC_DIST_BASE_GET();
uiVal = read32(ulBase + GICD_IPRIORITYR + ulVector / 4 * 4);
//读取目标中断优先级
*puiPriority = (uiVal >> (ulVector % 4 * 8)) & 0xff;
return (ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqAffinitySet
** 功能描述: GIC V1 中断亲和设置
** 输 入 : ulVector 中断号
** ullCPUId 目标 CPU
** bForce 是否强制设置
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT armGicV1IrqAffinitySet (ULONG ulVector, UINT64 ullCPUId, BOOL bForce)
{
ULONG ulBase;
UINT32 uiVal;
ulBase = GIC_DIST_BASE_GET();
uiVal = read32(ulBase + GICD_ITARGETSR + ulVector / 4 * 4);
uiVal &= ~(0xff << (ulVector % 4 * 8));
//配置对应中断向量的中断目标CPU
uiVal |= (1 << ullCPUId) << (ulVector % 4 * 8);
write32(uiVal, ulBase + GICD_ITARGETSR + ulVector / 4 * 4);
return (ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqAffinityGet
** 功能描述: GIC V1 中断亲和获取
** 输 入 : ulVector 中断号
** pullCPUId 目标 CPU
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT armGicV1IrqAffinityGet (ULONG ulVector, UINT64 *pullCPUId)
{
ULONG ulBase;
UINT32 uiVal;
INT i;
ulBase = GIC_DIST_BASE_GET();
uiVal = read32(ulBase + GICD_ITARGETSR + ulVector / 4 * 4);
uiVal = (uiVal >> (ulVector % 4 * 8)) & 0xff;
//SMP模式下,中断目标只设定在CPU0上
for (i = 0; i < LW_NCPUS; i++) {
for (i = 0; i < 4; i++) {
//检查CPU0~CPU3,对应中断绑定在哪一个CPU上
if (uiVal & (1 << i)) {
*pullCPUId = i;
break;
}
}
return (ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqTypeSet
** 功能描述: GIC V1 设置中断触发方式
** 输 入 : ulVector 中断号
** uiType 中断触发方式
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT armGicV1IrqTypeSet (ULONG ulVector, UINT uiType)
{
ULONG ulBase;
UINT32 uiEnableMask;
UINT32 uiEnableOff;
UINT32 uiConfigMask;
UINT32 uiConfigOff;
UINT32 uiVal;
UINT32 uiOldVal;
BOOL bEnabled;
INT iRet = ERROR_NONE;
if (ulVector < 16) { /* SGI 中断不能设置触发类型 */
_ErrorHandle(EINVAL);
return (PX_ERROR);
}
if ((ulVector >= 32) &&
(uiType != IRQ_TYPE_LEVEL_HIGH) &&
(uiType != IRQ_TYPE_EDGE_RISING)) { /* SPI 中断有指定的触发范围 */
_ErrorHandle(EINVAL);
return (PX_ERROR);
}
ulBase = GIC_DIST_BASE_GET();
uiEnableMask = 1 << (ulVector % 32);
uiEnableOff = (ulVector / 32) * 4;
uiConfigMask = 0x2 << ((ulVector % 16) * 2);
uiConfigOff = (ulVector / 16) * 4;
bEnabled = LW_FALSE;
uiVal = uiOldVal = read32(ulBase + GICD_ICFGR + uiConfigOff);
//根据uiType判断是电平触发还是边沿触发配置相应的触发方式
if (uiType & IRQ_TYPE_LEVEL_MASK) {
uiVal &= ~uiConfigMask;
} else if (uiType & IRQ_TYPE_EDGE_BOTH) {
uiVal |= uiConfigMask;
}
if (read32(ulBase + GICD_ISENABLER + uiEnableOff) & uiEnableMask) {
//在改变触发方式之前,先使能对应中断向量
write32(uiEnableMask, ulBase + GICD_ICENABLER + uiEnableOff);
bEnabled = LW_TRUE;
}
write32(uiVal, ulBase + GICD_ICFGR + uiConfigOff);
if ((read32(ulBase + GICD_ICFGR + uiConfigOff) != uiVal) &&
(uiVal != uiOldVal)) {
_ErrorHandle(EIO);
iRet = PX_ERROR;
}
if (bEnabled) {
//改变触发方式之后,重新使能中断你向量
write32(uiEnableMask, ulBase + GICD_ISENABLER + uiEnableOff);
}
return (iRet);
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqHandle
** 功能描述: GIC V1 中断服务程序
** 输 入 : bPreemptive 是否可抢占
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID armGicV1IrqHandle (BOOL bPreemptive)
{
REGISTER UINT32 uiAck;
REGISTER UINT32 uiVector;
REGISTER ULONG ulBase;
ulBase = GIC_CUR_CPU_INF_BASE_GET();
//读取中断应答寄存器,获得中断请求来源和中断向量号
uiAck = read32(ulBase + GICC_IAR);
uiVector = uiAck & 0x1ff;
extern VOID t3GpioClearIrq(PLW_GPIO_CHIP pGpioChip, UINT uiNum);
extern irqreturn_t t3GpioSvrIrq(PLW_GPIO_CHIP pGpioChip, UINT uiNum);
extern INT bspBoardGpioEINTGet(INT iIdx);
extern INT bspBoardGpioEINTNumGet(VOID);
INT i;
/*
* [SPI_PIO=60] shared by all cores
* [0x1FF] means INT occur but GicIrqAck read by other core.
*/
if (uiVector == SPI_PIO || uiVector == 0x1FF) {
//外部中断需要单独处理,一个中断向量对应了多个中断服务函数
for (i = 0; i < bspBoardGpioEINTNumGet(); i++) {
if (t3GpioSvrIrq(LW_NULL, bspBoardGpioEINTGet(i)) != LW_IRQ_NONE) {
//处理对应中断向量的中断处理函数
archIntHandle((ULONG)SPI_PIO, bPreemptive); /* handle interrupt */
}
}
if (uiVector == SPI_PIO) { /* clear interrupt SPI_PIO */
//中断结束,触发中断结束寄存器
write32(uiAck, ulBase + GICC_EOIR);
}
return;
}
//处理对应中断向量的中断处理函数
archIntHandle((ULONG)uiVector, bPreemptive);
//中断结束,触发中断结束寄存器
write32(uiAck, ulBase + GICC_EOIR);
}
以API_InterVectorEnable为例,其他的驱动函数的实现方式相似。
/*********************************************************************************************************
** 函数名称: API_InterVectorEnable
** 功能描述: 使能指定向量的中断
** 输 入 : ulVector 中断向量号
** 输 出 : ERROR
** 全局变量:
** 调用模块:
API 函数
*********************************************************************************************************/
LW_API
ULONG API_InterVectorEnable (ULONG ulVector)
{
INTREG iregInterLevel;
if (_Inter_Vector_Invalid(ulVector)) {
_ErrorHandle(ERROR_KERNEL_VECTOR_NULL);
return (ERROR_KERNEL_VECTOR_NULL);
}
LW_SPIN_LOCK_QUICK(&_K_slcaVectorTable.SLCA_sl, &iregInterLevel);
//宏定义函数入口
__ARCH_INT_VECTOR_ENABLE(ulVector);
LW_SPIN_UNLOCK_QUICK(&_K_slcaVectorTable.SLCA_sl, iregInterLevel);
MONITOR_EVT_LONG1(MONITOR_EVENT_ID_INT, MONITOR_EVENT_INT_VECT_EN, ulVector, LW_NULL);
return (ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: armGicIrqEnable
** 功能描述: GIC 中断使能
** 输 入 : ulVector 中断号
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID armGicIrqEnable (ULONG ulVector)
{
if (_G_armGicOp.AGIC_pfuncIrqEnable) {
//本质上调用了中断初始化的时候注册的函数
_G_armGicOp.AGIC_pfuncIrqEnable(ulVector);
}
}
/*********************************************************************************************************
** 函数名称: bspIntVectorEnable
** 功能描述: 使能指定的中断向量
** 输 入 : ulVector 中断向量
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID bspIntVectorEnable (ULONG ulVector)
{
ULONG ulCpuIdStart;
ULONG ulCpuIdEnd;
ULONG ulCpuId;
UINT64 ullMprId;
/*
* 外部中断特殊处理 (AMP 架构中需要将外部中断分发到各个系统中)
*/
extern INT armGicV1IrqAffinityMoreSet(ULONG ulVector, UINT64 ullTarget, BOOL bForce);
if (ulVector == SPI_PIO) {
armGicV1IrqAffinityMoreSet(SPI_PIO, 0x3, LW_TRUE);
armGicIrqEnable(SPI_PIO);
return;
}
/*
* 调整中断绑核,判断当前系统的核范围
*/
ulCpuIdStart = BSP_CFG_SYS0_CPU;
ulCpuIdEnd = ulCpuIdStart + BSP_CFG_SYS0_CPU_NUM - 1;
ulCpuIdStart = BSP_CFG_SYS1_CPU;
ulCpuIdEnd = ulCpuIdStart + BSP_CFG_SYS1_CPU_NUM - 1;
/*
* 若当前中断绑核不在当前系统核号范围内,则绑核到当前系统的起始核
*/
armGicIrqAffinityGet(ulVector, &ullMprId);
ulCpuId = (ULONG)ullMprId;
if (ulCpuId < ulCpuIdStart || ulCpuIdStart > ulCpuIdEnd) {
armGicIrqAffinitySet(ulVector, ulCpuIdStart, LW_TRUE);
}
//实际的入口
armGicIrqEnable(ulVector);
}
/*********************************************************************************************************
** 函数名称: __GpioInit
** 功能描述: 初始化 Gpio 相关配置
** 输 入 : GpioDev GPIO 设备结构体
** 输 出 : NONE
** 返 回 : ERROR_CODE
*********************************************************************************************************/
static INT __GpioInit (Gpio_DEV GpioDev)
{
INT iRet = 0;
//请求一个 GPIO 的使用
iRet = API_GpioRequestOne(GpioDev->Gpio_Cfg.uiGpioIrq, LW_GPIOF_DIR_IN, LW_NULL);
if (iRet < 0) {
GPIO_ERR("request gpio err, ret = %d!\n", iRet);
__GpioGpioDeInit(GpioDev);
return (PX_ERROR);
} else {
GpioDev->Gpio_uGpioIrqPinUseFlag = FLAG_IN_USE;
}
//配置一个GPIO的外部中断
GpioDev->Gpio_irqn = API_GpioSetupIrq(GpioDev->Gpio_Cfg.uiGpioIrq,
GpioDev->Gpio_Cfg.bIsIrqLevel,
GpioDev->Gpio_Cfg.uiIrqType);
if (GpioDev->Gpio_irqn == LW_VECTOR_INVALID) {
GPIO_ERR("gpio setup irq error, irqn = %d!\n", GpioDev->Gpio_irqn);
__GpioGpioDeInit(GpioDev);
return (PX_ERROR);
}
//配置标志位,GPIO中断为一个中断向量对应多个中断服务函数
API_InterVectorSetFlag(GpioDev->Gpio_irqn, LW_IRQ_FLAG_QUEUE);
API_InterVectorDisable(GpioDev->Gpio_irqn);
//注册中断服务函数
if (API_InterVectorConnect(GpioDev->Gpio_irqn,
(PINT_SVR_ROUTINE)__GpioGpioIsr, /* 触发采集的中断入口 */
(PVOID)GpioDev,
"gpio_isr") != ERROR_NONE) {
GPIO_ERR("API_InterVectorConnect fail\r\n");
__GpioGpioDeInit(GpioDev);
return (PX_ERROR);
} else {
GpioDev->Gpio_uGpioVectorUseFlag = FLAG_IN_USE;
}
//配置中断优先级
API_InterVectorSetPriority(GpioDev->Gpio_irqn, 10);
//中断使能
API_InterVectorEnable(GpioDev->Gpio_irqn);
return iRet;
}
/*********************************************************************************************************
** 函数名称: __t3GpioSetupIrq
** 功能描述: 设置指定 GPIO 为外部中断输入管脚
** 输 入 : pGpioChip GPIO 芯片
** uiNum GPIO 在系统中的编号
** bIsLevel 是否为电平触发, 1 表示电平触发,0 表示边沿触发
** uiType 如果为电平触发, 1 表示高电平触发, 0 表示低电平触发
** 如果为边沿触发, 1 表示上升沿触发, 0 表示下降沿触发, 2 双边沿触发
** 输 出 : IRQ 向量号 -1:错误
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static ULONG __t3GpioSetupIrq (PLW_GPIO_CHIP pGpioChip, UINT uiNum, BOOL bIsLevel, UINT uiType)
{
addr_t ulAddr;
UINT32 uiValue;
UINT32 uiCfg;
INT iIRQn;
UINT32 uiIntN;
INT32 iOffset = gpioOffset(uiNum);
if (PX_ERROR == iOffset) {
return (PX_ERROR);
}
if (iOffset >= GPIO_NUM(GPIO_PORT_H, GPIO_PIN_00) &&
iOffset <= GPIO_NUM(GPIO_PORT_H, GPIO_PIN_21)) {
uiIntN = iOffset - GPIO_NUM(GPIO_PORT_H, GPIO_PIN_00); /* 中断EINT0-21 */
} else if (iOffset >= GPIO_NUM(GPIO_PORT_I, GPIO_PIN_10) &&
iOffset <= GPIO_NUM(GPIO_PORT_I, GPIO_PIN_19)) {
uiIntN = iOffset - GPIO_NUM(GPIO_PORT_I, GPIO_PIN_10) + 22; /* 中断EINT22-31 */
} else {
return (PX_ERROR); /* 该端口没有中断功能 */
}
/*
* 设置中断时钟源
*/
ulAddr = GPIO_INT_DEB + GPIO_A_I_BASE;
API_SpinLock(&gpioSpinlock);
uiValue = readl(ulAddr);
uiValue |= (1 << 0); /* HOSC 24MHz */
//配置中断时钟源
writel(uiValue, ulAddr);
API_SpinUnlock(&gpioSpinlock);
ulAddr = (uiIntN / 8 * 4) + GPIO_INT_CFG + GPIO_A_I_BASE;
//配置中断向量号SPI_PIO,对于所有的外部中断共用这一个中断向量号
iIRQn = SPI_PIO;
/*
* 设置中断触发类型
*/
if (bIsLevel) { /* 如果是电平触发中断 */
if (uiType) {
uiCfg = EINT_MODE_HIGH_LEVEL; /* 高电平触发 */
} else {
uiCfg = EINT_MODE_LOW_LEVEL; /* 低电平触发 */
}
} else {
if (uiType == 0) { /* 下降沿触发 */
uiCfg = EINT_MODE_NEGATIVE_EDGE;
} else if (uiType == 1) { /* 上升沿触发 */
uiCfg = EINT_MODE_POSITIVE_EDGE;
} else { /* 双边沿触发 */
uiCfg = EINT_MODE_DOUBLE_EDGE;
}
}
API_SpinLock(&gpioSpinlock);
uiValue = readl(ulAddr);
uiValue &= ~(0x0f << ((uiIntN % 8) * 4));
uiValue |= (uiCfg << ((uiIntN % 8) * 4));
//配置外部中断的触发方式
writel(uiValue, ulAddr);
API_SpinUnlock(&gpioSpinlock);
/*
* 设置引脚为中断输入功能
*/
gpioPinmuxSet(uiNum, GPIO_EINT);
/*
* 使能引脚中断响应
*/
ulAddr = GPIO_INT_CTL + GPIO_A_I_BASE;
API_SpinLock(&gpioSpinlock);
uiValue = readl(ulAddr);
uiValue |= 1 << uiIntN;
//使能对应的外部中断
writel(uiValue, ulAddr);
API_SpinUnlock(&gpioSpinlock);
return (iIRQn);
}
/*********************************************************************************************************
** 函数名称: API_GpioSetupIrq
** 功能描述: 根据指定 GPIO 号设置相应的外部中断, 并返回对应的 IRQ 号
** 输 入 : uiGpio GPIO 号
** bIsLevel 是否为电平触发
** uiType 如果为电平触发, 1 表示高电平触发, 0 表示低电平触发
** 如果为边沿触发, 1 表示上升沿触发, 0 表示下降沿触发, 2 表示双边沿触发
** 输 出 : IRQ 号, 错误返回 LW_VECTOR_INVALID
** 全局变量:
** 调用模块:
API 函数
*********************************************************************************************************/
LW_API
ULONG API_GpioSetupIrq (UINT uiGpio, BOOL bIsLevel, UINT uiType)
{
ULONG ulVector;
PLW_GPIO_DESC pgdesc;
PLW_GPIO_CHIP pgchip;
pgdesc = __gpioGetDesc(uiGpio, LW_TRUE);
if (!pgdesc) {
_ErrorHandle(EINVAL);
return (LW_VECTOR_INVALID);
}
pgchip = pgdesc->GD_pgcChip;
if (pgchip->GC_pfuncSetupIrq) {
ulVector = pgchip->GC_pfuncSetupIrq(pgchip, GPIO_CHIP_HWGPIO(pgdesc), bIsLevel, uiType);
if (ulVector != LW_VECTOR_INVALID) { /* 外部中断设置成功 */
if (bIsLevel) {
pgdesc->GD_ulFlags |= LW_GPIODF_TRIG_LEVEL;
}
if (uiType == 0) {
pgdesc->GD_ulFlags |= LW_GPIODF_TRIG_FALL;
} else if (uiType == 1) {
pgdesc->GD_ulFlags |= LW_GPIODF_TRIG_RISE;
} else if (uiType == 2) {
pgdesc->GD_ulFlags |= (LW_GPIODF_TRIG_FALL | LW_GPIODF_TRIG_RISE);
}
}
return (ulVector);
} else {
_ErrorHandle(ENXIO);
return (LW_VECTOR_INVALID);
}
}
/*********************************************************************************************************
** 函数名称: API_InterVectorSetFlag
** 功能描述: 设置指定中断向量的特性.
** 输 入 : ulVector 中断向量号
** ulFlag 特性
** 输 出 : ERROR CODE
** 全局变量:
** 调用模块:
** 注 意 : LW_IRQ_FLAG_QUEUE 必须在安装任何一个驱动前设置, 且设置后不再能取消.
最好放在 bspIntInit() 函数中完成设置.
API 函数
*********************************************************************************************************/
LW_API
ULONG API_InterVectorSetFlag (ULONG ulVector, ULONG ulFlag)
{
INTREG iregInterLevel;
PLW_CLASS_INTDESC pidesc;
if (_Inter_Vector_Invalid(ulVector)) {
_ErrorHandle(ERROR_KERNEL_VECTOR_NULL);
return (ERROR_KERNEL_VECTOR_NULL);
}
pidesc = LW_IVEC_GET_IDESC(ulVector);
LW_SPIN_LOCK_QUICK(&pidesc->IDESC_slLock, &iregInterLevel); /* 关闭中断同时锁住 spinlock */
if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_QUEUE) { /* 已经是 QUEUE 类型中断向量 */
LW_IVEC_SET_FLAG(ulVector, ulFlag | LW_IRQ_FLAG_QUEUE);
} else {
LW_IVEC_SET_FLAG(ulVector, ulFlag);
}
LW_SPIN_UNLOCK_QUICK(&pidesc->IDESC_slLock, iregInterLevel); /* 打开中断, 同时打开 spinlock */
return (ERROR_NONE);
}
最终调用到对应的中断向量禁能的函数接口
/*********************************************************************************************************
** 函数名称: API_InterVectorConnect
** 功能描述: 设置系统指定向量中断服务
** 输 入 : ulVector 中断向量号
** pfuncIsr 服务函数
** pvArg 服务函数参数
** pcName 中断服务名称
** 输 出 : ERROR CODE
** 全局变量:
** 调用模块:
API 函数
*********************************************************************************************************/
LW_API
ULONG API_InterVectorConnect (ULONG ulVector,
PINT_SVR_ROUTINE pfuncIsr,
PVOID pvArg,
CPCHAR pcName)
{
return (API_InterVectorConnectEx(ulVector, pfuncIsr, LW_NULL, pvArg, pcName));
}
/*********************************************************************************************************
** 函数名称: API_InterVectorConnectEx
** 功能描述: 设置系统指定向量中断服务
** 输 入 : ulVector 中断向量号
** pfuncIsr 服务函数
** pfuncClear 附加中断清除函数(可为 NULL)
** pvArg 服务函数参数
** pcName 中断服务名称
** 输 出 : ERROR CODE
** 全局变量:
** 调用模块:
API 函数
*********************************************************************************************************/
LW_API
ULONG API_InterVectorConnectEx (ULONG ulVector,
PINT_SVR_ROUTINE pfuncIsr,
VOIDFUNCPTR pfuncClear,
PVOID pvArg,
CPCHAR pcName)
{
INTREG iregInterLevel;
BOOL bNeedFree;
PLW_LIST_LINE plineTemp;
PLW_CLASS_INTACT piactionOld;
PLW_CLASS_INTACT piaction;
PLW_CLASS_INTDESC pidesc;
if (LW_CPU_GET_CUR_NESTING()) { /* 不能在中断中调用 */
_DebugHandle(__ERRORMESSAGE_LEVEL, "called from ISR.\r\n");
_ErrorHandle(ERROR_KERNEL_IN_ISR);
return (ERROR_KERNEL_IN_ISR);
}
INTER_SHOWLOCK_CREATE();
if (_Object_Name_Invalid(pcName)) { /* 检查名字有效性 */
_DebugHandle(__ERRORMESSAGE_LEVEL, "name too long.\r\n");
_ErrorHandle(ERROR_KERNEL_PNAME_TOO_LONG);
return (ERROR_KERNEL_PNAME_TOO_LONG);
}
if (_Inter_Vector_Invalid(ulVector)) {
_ErrorHandle(ERROR_KERNEL_VECTOR_NULL);
return (ERROR_KERNEL_VECTOR_NULL);
}
if (pfuncIsr == LW_NULL) {
_ErrorHandle(ERROR_KERNEL_VECTOR_NULL);
return (ERROR_KERNEL_VECTOR_NULL);
}
piaction = (PLW_CLASS_INTACT)__KHEAP_ALLOC(sizeof(LW_CLASS_INTACT));
if (piaction == LW_NULL) {
_DebugHandle(__ERRORMESSAGE_LEVEL, "kernel low memory.\r\n");
_ErrorHandle(ERROR_KERNEL_LOW_MEMORY);
return (ERROR_KERNEL_LOW_MEMORY);
}
lib_bzero(piaction, sizeof(LW_CLASS_INTACT));
//将服务函数的参数填写到action结构体中
piaction->IACT_pfuncIsr = pfuncIsr;
piaction->IACT_pfuncClear = pfuncClear;
piaction->IACT_pvArg = pvArg;
if (pcName) {
lib_strcpy(piaction->IACT_cInterName, pcName);
} else {
piaction->IACT_cInterName[0] = PX_EOS;
}
pidesc = LW_IVEC_GET_IDESC(ulVector);
LW_SPIN_LOCK_QUICK(&pidesc->IDESC_slLock, &iregInterLevel); /* 关闭中断同时锁住 spinlock */
if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_QUEUE) { /* 队列服务类型向量 */
//GPIO中断属于队列服务类型向量
for (plineTemp = pidesc->IDESC_plineAction;
plineTemp != LW_NULL;
plineTemp = _list_line_get_next(plineTemp)) {
//获取链表中的中断服务函数结构体
piactionOld = _LIST_ENTRY(plineTemp, LW_CLASS_INTACT, IACT_plineManage);
if ((piactionOld->IACT_pfuncIsr == pfuncIsr) &&
(piactionOld->IACT_pvArg == pvArg)) { /* 中断处理函数是否被重复安装 */
break;
}
}
if (plineTemp) { /* 此中断被重复安装 */
bNeedFree = LW_TRUE;
} else {
//将中断服务函数安装到线性链表中
_List_Line_Add_Ahead(&piaction->IACT_plineManage,
&pidesc->IDESC_plineAction);
bNeedFree = LW_FALSE;
}
} else { /* 非队列服务式中断向量 */
if (pidesc->IDESC_plineAction) {
piactionOld = _LIST_ENTRY(pidesc->IDESC_plineAction,
LW_CLASS_INTACT,
IACT_plineManage);
piactionOld->IACT_pfuncIsr = piaction->IACT_pfuncIsr;
piactionOld->IACT_pfuncClear = piaction->IACT_pfuncClear;
piactionOld->IACT_pvArg = piaction->IACT_pvArg;
lib_strcpy(piactionOld->IACT_cInterName, piaction->IACT_cInterName);
bNeedFree = LW_TRUE;
} else {
_List_Line_Add_Ahead(&piaction->IACT_plineManage,
&pidesc->IDESC_plineAction);
bNeedFree = LW_FALSE;
}
}
LW_SPIN_UNLOCK_QUICK(&pidesc->IDESC_slLock, iregInterLevel); /* 打开中断, 同时打开 spinlock */
if (bNeedFree) {
__KHEAP_FREE(piaction);
}
_DebugFormat(__LOGMESSAGE_LEVEL, "IRQ %d : %s connect : %p\r\n",
(INT)ulVector, (pcName ? pcName : ""), (PVOID)pfuncIsr);
return (ERROR_NONE);
}
最终调用到对应的中断优先级设置的函数接口
/*********************************************************************************************************
** 函数名称: bspIntVectorEnable
** 功能描述: 使能指定的中断向量
** 输 入 : ulVector 中断向量
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID bspIntVectorEnable (ULONG ulVector)
{
ULONG ulCpuIdStart;
ULONG ulCpuIdEnd;
ULONG ulCpuId;
UINT64 ullMprId;
/*
* 外部中断特殊处理 (AMP 架构中需要将外部中断分发到各个系统中)
*/
extern INT armGicV1IrqAffinityMoreSet(ULONG ulVector, UINT64 ullTarget, BOOL bForce);
if (ulVector == SPI_PIO) {
//配置外部中断发往的CPU编号
armGicV1IrqAffinityMoreSet(SPI_PIO, 0x3, LW_TRUE);
armGicIrqEnable(SPI_PIO);
return;
}
/*
* 调整中断绑核,判断当前系统的核范围
*/
ulCpuIdStart = BSP_CFG_SYS0_CPU;
ulCpuIdEnd = ulCpuIdStart + BSP_CFG_SYS0_CPU_NUM - 1;
ulCpuIdStart = BSP_CFG_SYS1_CPU;
ulCpuIdEnd = ulCpuIdStart + BSP_CFG_SYS1_CPU_NUM - 1;
/*
* 若当前中断绑核不在当前系统核号范围内,则绑核到当前系统的起始核
*/
armGicIrqAffinityGet(ulVector, &ullMprId);
ulCpuId = (ULONG)ullMprId;
if (ulCpuId < ulCpuIdStart || ulCpuIdStart > ulCpuIdEnd) {
//其他的中断向量,绑定到对应系统编号下对应的起始CPU
armGicIrqAffinitySet(ulVector, ulCpuIdStart, LW_TRUE);
}
armGicIrqEnable(ulVector);
}
中断会触发中断异常,进入startup.S处理中断异常。archIntEntry进入不同架构下对应的中断处理函数。
FUNC_DEF(vector)
B reset
LDR PC, undefineEntry
LDR PC, swiEntry
LDR PC, prefetchEntry
LDR PC, abortEntry
LDR PC, reserveEntry
LDR PC, irqEntry
LDR PC, fiqEntry
FUNC_END()
FUNC_LABEL(irqEntry)
.word archIntEntry
进入到Arm架构下的中断处理函数
;/*********************************************************************************************************
; 中断入口
;*********************************************************************************************************/
FUNC_DEF(archIntEntry)
;/*
; * 保存 REG 到 IRQ 模式栈空间(这里做了个必须成立的假设, 之前必须工作在 SYS 或 USR 模式)
; */
SUB LR , LR, #4 ;/* 调整用于中断返回的 PC 值 */
STMFD SP!, {LR} ;/* 保存返回地址 */
STMFD SP!, {R0-R12} ;/* 保存寄存器 */
MOV R1 , SP
MSR CPSR_c, #(DIS_INT | SYS32_MODE) ;/* 回到 SYS 模式 */
STMFD R1!, {SP} ;/* 保存 SP_sys */
STMFD R1 , {LR} ;/* 保存 LR_sys */
MSR CPSR_c, #(DIS_INT | IRQ32_MODE) ;/* 回到 IRQ 模式 */
SUB SP , SP , #(2 * 4) ;/* 调整 SP_irq */
MRS R2 , SPSR
STMFD SP!, {R2} ;/* 保存 CPSR_sys */
;/*
; * API_InterEnter(SP_irq), 如果是第一次中断, 会将 IRQ 模式栈空间的 ARCH_REG_CTX
; * 拷贝到当前任务 TCB 的 ARCH_REG_CTX 里
; */
MOV R0 , SP
LDR R1 , =API_InterEnter
MOV LR , PC
BX R1
;/*
; * 如果不是第一次进入中断, 那么上一次中断(工作在 SYS 模式)已经设置 SP_sys, 只需要回到 SYS 模式
; */
CMP R0 , #1
BNE 1f
;/*
; * 第一次进入中断: 因为已经将 IRQ 模式栈空间的 ARCH_REG_CTX 拷贝到当前任务 TCB 的 ARCH_REG_CTX 里
; * 调整 SP_irq
; */
ADD SP , SP , #(ARCH_REG_CTX_SIZE)
;/*
; * 第一次进入中断: 获得当前 CPU 中断堆栈栈顶, 并回到 SYS 模式, 并设置 SP_sys
; */
LDR R0 , =API_InterStackBaseGet
MOV LR , PC
BX R0
MSR CPSR_c, #(DIS_INT | SYS32_MODE) ;/* 回到 SYS 模式 */
MOV SP , R0 ;/* 设置 SP_sys */
1:
MSR CPSR_c, #(DIS_INT | SYS32_MODE) ;/* 回到 SYS 模式(不是多余的) */
;/*
; * bspIntHandle()
; */
LDR R1 , =bspIntHandle
MOV LR , PC
BX R1
;/*
; * API_InterExit()
; * 如果没有发生中断嵌套, 则 API_InterExit 会调用 archIntCtxLoad 函数, SP_irq 在上面已经调整好
; */
LDR R1 , =API_InterExit
MOV LR , PC
BX R1
;/*
; * 来到这里, 说明发生了中断嵌套
; */
MSR CPSR_c, #(DIS_INT | IRQ32_MODE) ;/* 回到 IRQ 模式 */
MOV R0 , SP
LDMIA R0!, {R2-R4} ;/* 读取 CPSR LR SP */
ADD SP , SP , #(ARCH_REG_CTX_SIZE) ;/* 调整 SP_irq */
MSR CPSR_c, #(DIS_INT | SYS32_MODE) ;/* 回到 SYS 模式 */
MOV SP , R4 ;/* 恢复 SP_sys */
MOV LR , R3 ;/* 恢复 LR_sys */
MSR CPSR_c, #(DIS_INT | IRQ32_MODE) ;/* 回到 IRQ 模式 */
MSR SPSR_cxsf , R2
LDMIA R0 , {R0-R12, PC}^ ;/* 恢复包括 PC 的所有寄存器, */
;/* 同时更新 CPSR */
FUNC_END()
进入中断处理函数bspIntHandle
/*********************************************************************************************************
** 函数名称: armGicIrqHandle
** 功能描述: GIC 中断服务函数
** 输 入 : bPreemptive 是否可抢占
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID armGicIrqHandle (BOOL bPreemptive)
{
if (_G_armGicOp.AGIC_pfuncIrqHandle) {
_G_armGicOp.AGIC_pfuncIrqHandle(bPreemptive);
}
}
/*********************************************************************************************************
** 函数名称: bspIntHandle
** 功能描述: 中断入口
** 输 入 : NONE
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID bspIntHandle (VOID)
{
armGicIrqHandle(LW_FALSE);
}
进入中断处理函数
/*********************************************************************************************************
** 函数名称: bspBoardGpioEINTGet
** 功能描述: 获取此核需要触发的外部中断引脚
** 输 入 : NONE
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
INT bspBoardGpioEINTGet (INT iIdx)
{
return _G_iGpioEINTSourceTable[iIdx];
}
/*********************************************************************************************************
** 函数名称: bspBoardGpioEINTNumGet
** 功能描述: 获取此核需要触发的外部中断引脚总数
** 输 入 : NONE
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
INT bspBoardGpioEINTNumGet (VOID)
{
return (sizeof(_G_iGpioEINTSourceTable) / sizeof(INT));
}
/*********************************************************************************************************
** 函数名称: __t3GpioSvrIrq
** 功能描述: 判断 GPIO 中断标志
** 输 入 : pGpioChip GPIO 芯片
** uiNum GPIO 在系统中的编号
** 输 出 : 中断返回值
** 全局变量:
** 调用模块:
*********************************************************************************************************/
irqreturn_t t3GpioSvrIrq (PLW_GPIO_CHIP pGpioChip, UINT uiNum)
{
addr_t ulAddr;
UINT32 uiRegister;
UINT32 uiIntN;
INT32 iOffset = gpioOffset(uiNum);
if (PX_ERROR == iOffset) {
return (PX_ERROR);
}
if (iOffset >= GPIO_NUM(GPIO_PORT_H, GPIO_PIN_00) &&
iOffset <= GPIO_NUM(GPIO_PORT_H, GPIO_PIN_21)) {
uiIntN = iOffset - GPIO_NUM(GPIO_PORT_H, GPIO_PIN_00); /* 中断EINT0-21 */
} else if (iOffset >= GPIO_NUM(GPIO_PORT_I, GPIO_PIN_10) &&
iOffset <= GPIO_NUM(GPIO_PORT_I, GPIO_PIN_19)) {
uiIntN = iOffset - GPIO_NUM(GPIO_PORT_I, GPIO_PIN_10) + 22; /* 中断EINT22-31 */
} else {
return (PX_ERROR); /* 该端口没有中断功能 */
}
ulAddr = GPIO_INT_STA + GPIO_A_I_BASE;
uiRegister = readl(ulAddr);
//判断外部中断状态位,判断是否是当前中断管脚触发了外部中断
if (uiRegister & (1 << uiIntN)) {
return (LW_IRQ_HANDLED_CONT);
} else {
return (LW_IRQ_NONE);
}
}
/*********************************************************************************************************
** 函数名称: armGicV1IrqHandle
** 功能描述: GIC V1 中断服务程序
** 输 入 : bPreemptive 是否可抢占
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID armGicV1IrqHandle (BOOL bPreemptive)
{
REGISTER UINT32 uiAck;
REGISTER UINT32 uiVector;
REGISTER ULONG ulBase;
ulBase = GIC_CUR_CPU_INF_BASE_GET();
//读取中断应答寄存器,获得中断请求来源和中断向量号
uiAck = read32(ulBase + GICC_IAR);
uiVector = uiAck & 0x1ff;
extern VOID t3GpioClearIrq(PLW_GPIO_CHIP pGpioChip, UINT uiNum);
extern irqreturn_t t3GpioSvrIrq(PLW_GPIO_CHIP pGpioChip, UINT uiNum);
extern INT bspBoardGpioEINTGet(INT iIdx);
extern INT bspBoardGpioEINTNumGet(VOID);
INT i;
/*
* [SPI_PIO=60] shared by all cores
* [0x1FF] means INT occur but GicIrqAck read by other core.
*/
if (uiVector == SPI_PIO || uiVector == 0x1FF) {
//外部中断需要单独处理,一个中断向量对应了多个中断服务函数
//bspBoardGpioEINTNumGet()获取了一个系统对应的外部中断管脚配置的个数
for (i = 0; i < bspBoardGpioEINTNumGet(); i++) {
//t3GpioSvrIrq()便利系统中所有的外部中断管脚,查询哪一个中断管脚被触发了
if (t3GpioSvrIrq(LW_NULL, bspBoardGpioEINTGet(i)) != LW_IRQ_NONE) {
//处理对应中断向量的中断处理函数
archIntHandle((ULONG)SPI_PIO, bPreemptive); /* handle interrupt */
}
}
if (uiVector == SPI_PIO) { /* clear interrupt SPI_PIO */
//中断结束,触发中断结束寄存器
write32(uiAck, ulBase + GICC_EOIR);
}
return;
}
//处理对应中断向量的中断处理函数
archIntHandle((ULONG)uiVector, bPreemptive);
//中断结束,触发中断结束寄存器
write32(uiAck, ulBase + GICC_EOIR);
}
archIntHandle调用对应架构下的中断处理函数,这里进入Arm的中断处理函数
;/*********************************************************************************************************
; ARM 关闭总中断
;*********************************************************************************************************/
FUNC_DEF(archIntDisable)
MRS R0 , CPSR
ORR R1 , R0, #0x80
MSR CPSR_c, R1
MRS R2 , CPSR
AND R2 , R2, #0x80
CMP R2 , #0x80
BNE archIntDisable
BX LR
FUNC_END()
FUNC_DEF(archIntEnable)
MSR CPSR_c, R0
BX LR
FUNC_END()
FUNC_DEF(archIntEnableForce)
MRS R0 , CPSR
BIC R0 , R0, #0x80
MSR CPSR_c, R0
BX LR
FUNC_END()
/*********************************************************************************************************
ARM 处理器标准底层库
*********************************************************************************************************/
/*********************************************************************************************************
** 函数名称: archIntHandle
** 功能描述: bspIntHandle 需要调用此函数处理中断 (关闭中断情况被调用)
** 输 入 : ulVector 中断向量
** bPreemptive 中断是否可抢占
** 输 出 : NONE
** 全局变量:
** 调用模块:
** 注 意 : 此函数退出时必须为中断关闭状态.
*********************************************************************************************************/
LW_WEAK VOID archIntHandle (ULONG ulVector, BOOL bPreemptive)
{
REGISTER irqreturn_t irqret;
if (_Inter_Vector_Invalid(ulVector)) {
return; /* 向量号不正确 */
}
//判断对应中断是否配置了中断抢占标志位
if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_PREEMPTIVE) {
bPreemptive = LW_TRUE;
}
//如果中断可抢占,这里关闭本Vector对应中断,强制开启中断
if (bPreemptive) {
VECTOR_OP_LOCK();
__ARCH_INT_VECTOR_DISABLE(ulVector); /* 屏蔽 vector 中断 */
VECTOR_OP_UNLOCK();
KN_INT_ENABLE_FORCE(); /* 允许中断 */
}
irqret = API_InterVectorIsr(ulVector); /* 调用中断服务程序 */
KN_INT_DISABLE(); /* 禁能中断 */
if (bPreemptive) {
if (irqret != LW_IRQ_HANDLED_DISV) {
VECTOR_OP_LOCK();
__ARCH_INT_VECTOR_ENABLE(ulVector); /* 允许 vector 中断 */
VECTOR_OP_UNLOCK();
}
} else if (irqret == LW_IRQ_HANDLED_DISV) {
VECTOR_OP_LOCK();
__ARCH_INT_VECTOR_DISABLE(ulVector); /* 屏蔽 vector 中断 */
VECTOR_OP_UNLOCK();
}
}
/*********************************************************************************************************
系统中断返回值
*********************************************************************************************************/
/* 不是本设备中断 */
/* 中断已被正确处理 */
/* 中断处理结束并且屏蔽本中断 */
/* 中断已被处理, 但需要继续循环*/
/*********************************************************************************************************
中断服务
*********************************************************************************************************/
//这里真正调用Connect的中断服务函数
irqret = piaction->IACT_pfuncIsr(piaction->IACT_pvArg, ulVector);
//函数正常处理之后,中断触发计数++
piaction->IACT_iIntCnt[LW_CPU_GET_ID(pcpu)]++; \
if (piaction->IACT_pfuncClear) { \
piaction->IACT_pfuncClear(piaction->IACT_pvArg, ulVector); \
} \
//如果不是需要继续循环的中断处理函数,就可以break退出了
//线性队列类型的中断将会循环处理所有挂载的中断服务函数
if (LW_IRQ_RETBREAK(irqret)) { \
BREAK \
} \
} \
}
/*********************************************************************************************************
** 函数名称: API_InterVectorIsr
** 功能描述: 向量中断总服务
** 输 入 : ulVector 中断向量号 (arch 层函数需要保证此参数正确)
** 输 出 : 中断返回值
** 全局变量:
** 调用模块:
** 注 意 : 这里并不处理中断嵌套, 他需要 arch 层移植函数支持.
API 函数
*********************************************************************************************************/
LW_API
irqreturn_t API_InterVectorIsr (ULONG ulVector)
{
PLW_CLASS_CPU pcpu;
PLW_LIST_LINE plineTemp;
PLW_CLASS_INTDESC pidesc;
PLW_CLASS_INTACT piaction;
irqreturn_t irqret = LW_IRQ_NONE;
struct timespec tv;
pcpu = LW_CPU_GET_CUR(); /* 中断处理程序中, 不会改变 CPU*/
//中断前的hook函数
__LW_CPU_INT_ENTER_HOOK(ulVector, pcpu->CPU_ulInterNesting);
/* LW_CFG_CPU_INT_HOOK_EN > 0 */
//核间中断的处理
if (pcpu->CPU_ulIPIVector == ulVector) { /* 核间中断 */
_SmpProcIpi(pcpu);
if (pcpu->CPU_pfuncIPIClear) {
pcpu->CPU_pfuncIPIClear(pcpu->CPU_pvIPIArg, ulVector); /* 清除核间中断 */
}
} else
/* LW_CFG_SMP_EN */
{
pidesc = LW_IVEC_GET_IDESC(ulVector);
//如果中断类型是线性队列中断,例如外部中断
if (pidesc->IDESC_ulFlag & LW_IRQ_FLAG_QUEUE) {
LW_SPIN_LOCK(&pidesc->IDESC_slLock); /* 锁住 spinlock */
/* LW_CFG_SMP_EN > 0 */
for (plineTemp = pidesc->IDESC_plineAction;
plineTemp != LW_NULL;
plineTemp = _list_line_get_next(plineTemp)) {
//遍历运行所有在链表中的中断服务函数
piaction = (PLW_CLASS_INTACT)plineTemp;
INTER_VECTOR_SVC(break;);
}
LW_SPIN_UNLOCK(&pidesc->IDESC_slLock); /* 解锁 spinlock */
/* LW_CFG_SMP_EN > 0 */
} else {
//一般情况下直接找到中断向量对应的中断服务结构体,运行中断服务函数
piaction = (PLW_CLASS_INTACT)pidesc->IDESC_plineAction;
if (piaction) {
INTER_VECTOR_SVC(;);
} else {
_DebugFormat(__ERRORMESSAGE_LEVEL, "interrupt vector: %ld no service.\r\n", ulVector);
}
}
}
//中断服务之后的钩子函数
__LW_CPU_INT_EXIT_HOOK(ulVector, pcpu->CPU_ulInterNesting);
/* LW_CFG_CPU_INT_HOOK_EN > 0 */
return (irqret);
}