Linux 中断
文章目录
- Linux 中断
- 简介
- 中断的种类
- 中断的触发方式
- 中断触发类型
- 中断路由
- 中断状态机
- Level sensitive interrupts
- Edge-triggered interrupts
- 中断目标
- 怎么表示中断控制器
简介
GIC(Generic Interrupt Controller) 也被称为中断控制器,是用来接收外部的中断,并将它们分为不同的优先级,按照优先级的高低将中断发送到目的处理器core中,如下图所示:
目前 GIC 有4个版本:V1~V4,V1 是最老的版本,已经被废弃了。V2~V4 目前正在大量的使用。GIC V2 是给 ARMv7-A架构使用的,比如 Cortex-A7、Cortex-A9、Cortex-A15 等, V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。
中断的种类
GIC可以处理4种中断类型:
SPl (Shared Peripheral Interrupt): 外部中断可以以SPI中断的形式发送到任何一个CPU core上。这种也是我们最常使用的中断类型。
PPI (Private Peripheral Interrupt): 该种类型的中断是每个CPU core私有的,比如Generic Timer产生的中断(每个CPU core上都有Generic Timer,其产生的中断只属于该core)。
SGl (Softrware Generated Interrupt): 该种类型的中断通常用来核间通信,软件通过写SGI寄存器在其他CPU core上产生该中断,也被称为IPI。常见的如 IPI_RESCHEDULE、IPI_CALL_FUNC 都属于该种类型的中断。
LPl (Locality-specific Peripheral Interrupt): 基于消息的中断,中断不通过中断线的电平变化来触发,而是通过写GIC寄存器或者ITS转化的方式,常用于PCI中
每个中断都是由中断ID(INTID)来区分的,并且INTID也表明了中断的类型,如下表所示:
对于GIC来说, 为每一个硬件中断源分配一个中断号, 这就是硬件中断号。GIC会为支持的中断类型分配中断号范围
中断的触发方式
传统的方法, 是一个外设通过硬件上的一根中断信号线, 把中断信号发送给 GIC, GIC 再把 IRQ/FIQ 发送给 CPU core中, 这种方式就需要给外设配置专用的中断
PE 指的是 Processing Element(处理单元)
在 GICv3 之后的版本, GIC 还额外支持外设通过中断消息(message-signaled interrupts, MSI)的方式将中断发送给 GIC. 这样做的好处是不需要给每个中断源配置专门的中断信号, 通过写寄存器的方式就可以将中断从外设发到中断控制器. 对于大型系统更是一大优点, 因为其中可能有成百上千个中断信号通过 SoC 路由汇聚到中断控制器上.
中断是作为消息发送还是使用中断信号线对中断处理的方式影响不大。可能需要对外围设备进行一些配置。例如,可能需要指定中断控制器的地址。
其中, SPI 可以是消息信号中断. LPI 始终是消息信号中断, 不同的中断类型使用的是不同的寄存器
中断触发类型
外设中断可以支持两种中断触发方式。
- 边沿触发(edge-triggered): 当中断源产生一个上升沿或者下降沿时, 触发一个中断
- 电平触发(level-sensitive): 当中断信号线产生一个高电平或者低电平时, 触发一个中断
中断路由
中断状态机
中断控制器为每个 SPI、PPI 和 SGI 中断源维护一个状态机。该状态机由四个状态组成
- **Inactive: **当前为声明中断源
- **Pending: ** 中断源已经被置位了, 但是没有被 PE 确认(GIC已经把信号发给CPU core了)
- **Active: **中断源已经被置位, 且中断已经被 PE 确认了(CPU core那边 ack 了这个中断信号)
- **Active and Pending: ** 这个中断的前一个信号被 ack 了, 但是后面又来了一个, 还需要再 ack, 但是先 pending起来
Level sensitive interrupts
中断的生命周期取决于其配置为电平敏感(高低电平触发) 还是边沿触发
- 对于电平敏感型中断(Level sensitive interrupts),中断输入上的上升沿会导致中断成为待处理中断,并保持中断状态,直到外设取消中断信号。
- 对于边缘触发中断(Edge-triggered interrupts),中断输入上的上升沿会导致中断挂起,但中断不会保持断言状态
- **Inactive -> pending: ** 当中断源被置位时,中断会从 Inactive 转为 pending。此时,如果中断已启用且优先级足够高,GIC 会向 PE 发出中断信号, 然后等待 PE 的 ack
- **pending -> Active and Pending: ** 当 PE 通过读取 CPU 接口中的一个IAR寄存器确认中断时发生状态转变. 此时只是处理了一个中断, GIC 会向 PE 取消中断信号
- **Active and Pending -> Active: **当外设取消中断信号时,中断会从激活和挂起状态转换为激活状态。这通常是在软件写入外设状态寄存器时发生的。
- **Active -> inactive: **当 PE 写入 CPU 接口中的一个中断结束寄存器(EOIR)时,中断将从激活状态转为非激活状态。这表明 PE 已完成中断处理。
Edge-triggered interrupts
- **Inactive -> pending: ** 当中断源被断言时,中断会从非活动状态转为挂起状态。此时,如果中断已启用且优先级足够高,GIC 会向 PE 发出中断信号
- **pending -> Active: **当 PE 通过读取 CPU 接口中的一个 IAR 确认中断时,中断就会从挂起状态转为活动状态。这种读取通常是中断异常发生后执行的中断处理例程的一部分。不过,软件也可以轮询 IAR。此时,GIC 会向 PE 取消中断信号。
- **Active -> Active and Pending: **如果外设重新发出中断信号,则中断会从激活状态变为激活状态并挂起
- **Active and Pending -> Pending: **当 PE 写入 CPU 接口中的一个 EOIR 时,中断将从活动和挂起状态转为挂起状态。这表明 PE 已处理完第一个中断实例。此时,GIC 会重新向 PE 发出中断信号。
中断目标
Arm 架构为每个 PE 分配一个分层标识符,称为 affinity。GIC 使用 affinity 将中断锁定在特定内核上。affinity 是一个 32 位值,分为四个字段
<affinity level3>.<affinity level2>.<affinity level1>.<affinity level0>不同 affinity 级别的确切含义由特定处理器和 SoC 确定。例如,Arm Cortex-A53 和 Arm Cortex-A57 处理器使用
<group of groups>.<group of processors>.<processor>.<core>后来的设计,例如 Arm Cortex-A55 和 Arm Cortex-A76 处理器中使用的设计,使用
<group of processors>.<processor>.<core>.<thread>举个例子
0.0.0.[0:3]Cores0to3of a Cortex-A53 processor0.0.1.[0:1]Cores0to1of a Cortex-A57 processor怎么表示中断控制器
在设备树中用四个属性就可以描述一个中断控制器
gic:interrupt-controller{#interrupt-cells=<3>;//当子设备要引用我时, 应该传入几个参数, 每个参数是什么意思...compatible="arm,gic-v3";//这个肯定要interrupt-controller;//表示是一个中断控制器reg=<0x00x29C000000x00x10000>,//地址也要的, 不然系统可找不到<0x00x29C800000x00x200000>;interrupts=<GIC_PPI9IRQ_TYPE_LEVEL_HIGH>;...};对于下一级的中断控制器, 比如 gpio uart 等, 这些控制器的属性就可能需要多一个interrupt-parent/interrupts, 表示使用上一级的中断控制的哪一个中断口
mipi_host0:mipi_host@0x37420000{compatible="hobot,mipi-host";//这个肯定要reg=<00x3742000000x10000>,<00x3720000000x40000>;...interrupt-controller;//表示是一个中断控制器...interrupt-parent=<&gic>;interrupts=<GIC_SPI CAMERASYS_CPE0_MIPI_RX_CSI_INTR CAMERASYS_CPE0_MIPI_RX_CSI_INTR_TRIG_TYPE>;status="okay";}在硬件上,“中断控制器”只有 GIC 这一个,但是我们在软件上也可以把上图中的“GPIO”称为“中断
控制器”。很多芯片有多个 GPIO 模块,比如 GPIO1、GPIO2 等等。所以软件上的“中断控制器”就有很多个:
GIC、GPIO1、GPIO2 等等。
GPIO1 连接到 GIC,GPIO2 连接到 GIC,所以 GPIO1 的父亲是 GIC,GPIO2 的父亲是 GIC。
假设 GPIO1 有 32 个中断源,但是它把其中的 16 个汇聚起来向 GIC 发出一个中断,把另外 16 个汇聚起
来向 GIC 发出另一个中断。这就意味着 GPIO1 会用到 GIC 的两个中断,会涉及 GIC 里的 2 个 hwirq。
上面说了这么多是为了表示
在设备树中,中断控制器节点中必须有一个属性:interrupt-controller,表明它是“中断控制器”。
还必须有一个属性:#interrupt-cells,表明引用这个中断控制器的话需要多少个 cell。
设备树里中断表示: