中断向量是中断服务程序的入口地址或存放中断服务程序的首地址,而中断向量表就是存放着一系列中断服务程序入口地址的表。这些中断服务程序在中断向量表中的位置是半导体厂商确定好的。若某个中断被触发,则会自动跳转到中断向量表对应的中断服务程序的入口地址处。
中断向量表在整个程序的最前面,但ARM处理器都是从0x00000000开始运行,但在stm32中代码是下载到0x80000000开始的区域,这里就会引入一个中断向量表偏移。在imx6ull也有也有相类似的概念,只是使用的寄存器不大一样。
对于STM32这种基于Cortex-M内核的设备,中断系统的管理机构称作NVIC,即Nested Vectored Interrupt Controller。使用时先进行中断使能,然后编写并注册中断服务函数。
对于imx6ull这种基于Cortex-A7内核的设备,中断系统的管理机构称为GIC,即General Interrupt Controller。(有V1~V4四个版本,A核用的是V2)
区别:GIC的中断向量表与NVIC的中断向量表相比少了一大堆,区别在于Cortex-M内核会将所有的中断向量,包括芯片外设的所有的中断给列举出来;而Cortex-A内核的则是所有的外部中断都属于IRQ这个中断,再通过IRQ服务函数中指定的寄存器来判断具体是什么中断。
Cortex-A的七个中断:
GIC V2是给ARMv8-A/R架构使用的,如imx6ull的芯片。当GIC接收到外部中断信号后就会上报给ARM内核,但ARM内核只提供了四个信号给GIC来汇报中断情况:VFIQ(虚拟快速FIQ)、VIRQ(虚拟外部IRQ)、FIQ(快速中断IRQ)、IRQ(外部中断IRQ)。所以我们使用外部中断的话,最终向内核上报的只有一个IRQ信号
下图的左侧为中断源,中间则是GIC控制器,右侧是向处理器内核发送的中断信息。GIC将众多中断源分为三个部分即红圈圈出的三个部分。
区分这些不同中断源的方式就是给他们分配一个唯一ID,即中断ID。每个CPU最多支持1020个中断ID。ID0~ID15分配给 SGI。 ID16~ID31分配给 PPI。ID32~ID1019这 988 个 ID 分配给 SPI。具体的某个ID对应某个中断就由半导体厂商自行定义。
比如 I.MX6U 的总共 使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID,I.MX6U 的中断源共有 128+32=160 个,这 128 个中断 ID 对应的中断在《I.MX6ULL 参考手册》的“3.2 CortexA7 interrupts”小节,
GIC架构分为两个逻辑块:Distributor和CPU Interface,如上图蓝圈所示。
CPU Interface(CPU接口端):它是分发器和CPU Core之间的桥梁,主要职能如下:
这个可以查看相关裸机开发的文档去补充,第一张主要是为了弄明白imx6ull的中断设备树相关配置而写的。
一个硬件中断产生后,会依次经过GPIOX,SOC,GPC,GIC,最后再上报到CPU执行对应操作。
intc描述的是整个GIC控制器,即中断总开关。由#interrupt-cells属性表示这个中断控制器下的其他子控制器设备的inturrupts属性cells大小,也就是子节点中用几个cells来描述中断。
intc: interrupt-controller@a01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0xa01000 0x1000>,
<0xa00100 0x100>;
interrupt-parent = <&intc>;
};
gpc是系统的虚拟中断控制器也称为二级子中断控制器,如果处理器是SOC,则此节点必须存在。顶级SOC节点包含的信息对此SOC上的所有设备可见。SOC节点还包含目标板使用的每个SOC设备子节点。
由于顶级SOC节点的已包含interrupt-parent信息,所以gpio中断控制器的中断父级都是gpc(gpc节点的父级信息是intc,可以理解为被覆盖了)。
由下方gpio5可知,他描述了一个SPI类的第74、75号的高电平触发。查阅手册可知,GPIO5只能产生两个中断,分配的中断号为106、107(前面属于PPI和SGI的共32个,即106-32=74)。
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&gpc>;
ranges;
gpc: interrupt-controller@20dc000 {
compatible = "fsl,imx6sll-gpc", "fsl,imx6q-gpc";
reg = <0x20dc000 0x4000>;
interrupt-controller;
#interrupt-cells = <3>;
interrupts = ;
interrupt-parent = <&intc>;
fsl,mf-mix-wakeup-irq = <0x7c00000 0x7d00 0x0 0x1400640>;
};
gpio5: gpio@20ac000 {
compatible = "fsl,imx6sll-gpio", "fsl,imx35-gpio";
reg = <0x20ac000 0x4000>;
interrupts = ,
;
clocks = <&clks IMX6SLL_CLK_GPIO5>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
gpio-ranges = <&iomuxc 0 135 1>, <&iomuxc 1 128 1>,
.......
<&iomuxc 21 137 1>;
};
};
由上可知,包含gpio5的子中断控制器的interrupts参数需要两个参数来进行描述。interrupts属性第一个为引脚编号、第二个为触发方式。button_gpio属性是gpio子系统表示使用gpio5的信息,引脚编号为1,低电平。
button_interrupt {
compatible = "button_interrupt";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_button>;
button_gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
status = "okay";
interrupt-parent = <&gpio5>;
interrupts = <1 IRQ_TYPE_EDGE_RISING>;
};
三、中断代码的调用及中断号的获取
1、中断号的获取
对于能够转化为平台设备的节点,如果它指定了中断属性,便可以使用下面的函数指定IORESOURCE_IRQ类型来获取中断号。
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num);
对于I2C、SPI设备节点,其对应的驱动会在处理节点时顺便处理其中断信息。并将其保存在i2c_device或spi_device的irq成员当中。
对于使用GPIO中断的设备而言,可以使用gpio_to_irq()来获得中断号。
对于除上述情况之外的某些设备节点,可以使用of_irq_get()或irq_of_parse_and_map()函数去获取。
2、调用
四、文章推荐
linux驱动2.1按键中断-中断过程分析和程序编写 - princepeng - 博客园 (cnblogs.com)
中断系统中的设备树_小嵌同学的博客-CSDN博客