imx6ull中断设备树以及对应驱动相关的知识点的理解

一、中断相关知识点回顾

1、中断向量表:

        中断向量是中断服务程序的入口地址或存放中断服务程序的首地址,而中断向量表就是存放着一系列中断服务程序入口地址的表。这些中断服务程序在中断向量表中的位置是半导体厂商确定好的。若某个中断被触发,则会自动跳转到中断向量表对应的中断服务程序的入口地址处。

        中断向量表在整个程序的最前面,但ARM处理器都是从0x00000000开始运行,但在stm32中代码是下载到0x80000000开始的区域,这里就会引入一个中断向量表偏移。在imx6ull也有也有相类似的概念,只是使用的寄存器不大一样。

2、NVIC与GIC

        对于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的七个中断:

  1. 复位中断(Rest),CPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化 SP 指针、DDR 等等。
  2. 未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
  3. 软中断(Software Interrupt,SWI),由 SWI 指令引起的中断,Linux 的系统调用会用 SWI指令来引起软中断,通过软中断来陷入到内核空间。
  4. 指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。
  5. 数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。
  6. IRQ 中断(IRQ Interrupt),外部中断,前面已经说了,芯片内部的外设中断都会引起此中断的发生。
  7. FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中断。

3、GIC控制器简介

        GIC V2是给ARMv8-A/R架构使用的,如imx6ull的芯片。当GIC接收到外部中断信号后就会上报给ARM内核,但ARM内核只提供了四个信号给GIC来汇报中断情况:VFIQ(虚拟快速FIQ)、VIRQ(虚拟外部IRQ)、FIQ(快速中断IRQ)、IRQ(外部中断IRQ)。所以我们使用外部中断的话,最终向内核上报的只有一个IRQ信号

imx6ull中断设备树以及对应驱动相关的知识点的理解_第1张图片

 1、GIC V2概述

下图的左侧为中断源,中间则是GIC控制器,右侧是向处理器内核发送的中断信息。GIC将众多中断源分为三个部分即红圈圈出的三个部分。

  1. SPI(Shared Peripheral Interrupt),共享中断:即所有Core共享的中断源。
  2. PPI(Private Peripheral Interrupt),私有中断:即每个Core自己独有的中断,进行指定的处理。
  3. SGI(Software generated Interrupt),软件中断:由软件触发引起的中断,通过向寄存器GICD_SGIR写入数据来触发,系统会通过使用SGI中断来进行多核之前通信。

imx6ull中断设备树以及对应驱动相关的知识点的理解_第2张图片

 2、GIC v2的中断号

        区分这些不同中断源的方式就是给他们分配一个唯一ID,即中断ID。每个CPU最多支持1020个中断ID。ID0~ID15分配给 SGI。 ID16~ID31分配给 PPI。ID32~ID1019988 ID 分配给 SPI。具体的某个ID对应某个中断就由半导体厂商自行定义。

        比如 I.MX6U 的总共 使用了 128 个中断 ID,加上前面属于 PPI SGI 32 IDI.MX6U 的中断源共有 128+32=160 个,这 128 个中断 ID 对应的中断在《I.MX6ULL 参考手册》的“3.2 CortexA7 interrupts”小节,

3、GIC逻辑分块

GIC架构分为两个逻辑块:Distributor和CPU Interface,如上图蓝圈所示。

        Distributor(分发器端):主要功能是处理各个中断事件的分发问题,他可以控每个中断的优先级,然后收集所有的中断源,将优先级最高的中断事件发送到CPU的接口端。主要职能如下:
  1. 全局中断使能控制。
  2. 控制每一个中断的使能或者关闭。
  3. 设置每个中断的优先级。
  4. 设置每个中断的目标处理器列表。
  5. 设置每个外部中断的触发模式:电平触发或边沿触发。
  6. 设置每个中断属于组 0 还是组 1

CPU Interface(CPU接口端):它是分发器和CPU Core之间的桥梁,主要职能如下:

  1. 使能或关闭发送到CPU Core的中断请求信号
  2. 应答中断
  3. 通知中断处理完成
  4. 设置优先级掩码,进而设置哪些中断不需要上报给CPU Core
  5. 定义抢断策略
  6. 当多个中断到来时选择优先级最高的中断通知给CPU Core   

4、CP15协处理器 

        这个可以查看相关裸机开发的文档去补充,第一张主要是为了弄明白imx6ull的中断设备树相关配置而写的。

二、中断设备树相关说明

一个硬件中断产生后,会依次经过GPIOX,SOC,GPC,GIC,最后再上报到CPU执行对应操作。

1、intc的描述

        intc描述的是整个GIC控制器,即中断总开关。由#interrupt-cells属性表示这个中断控制器下的其他子控制器设备的inturrupts属性cells大小,也就是子节点中用几个cells来描述中断。

  1. 第一个参数用于指定中断类型,如SPI、PPI、SGI。
  2. 第二个参数用于指定中断ID
  3. 第三个参数用于指定触发方式,参数为u32,后四位用来设置触发类型,可进行组合操作
	intc: interrupt-controller@a01000 {
		compatible = "arm,cortex-a9-gic";
		#interrupt-cells = <3>;
		interrupt-controller;
		reg = <0xa01000 0x1000>,
		      <0xa00100 0x100>;
		interrupt-parent = <&intc>;
	};

2、gpc的描述与GPIO中断节点的描述

        gpc是系统的虚拟中断控制器也称为二级子中断控制器,如果处理器是SOC,则此节点必须存在。顶级SOC节点包含的信息对此SOC上的所有设备可见。SOC节点还包含目标板使用的每个SOC设备子节点。

        由于顶级SOC节点的已包含interrupt-parent信息,所以gpio中断控制器的中断父级都是gpc(gpc节点的父级信息是intc,可以理解为被覆盖了)。

        由下方gpio5可知,他描述了一个SPI类的第74、75号的高电平触发。查阅手册可知,GPIO5只能产生两个中断,分配的中断号为106、107(前面属于PPISGI的共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>;
			};
};

3、实际设备的描述

由上可知,包含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博客

你可能感兴趣的:(野火i.MX6ULL,Pro开发板,单片机,嵌入式硬件,嵌入式,linux)