第十四章 中断自我整理(一)

中断的内容真的好多,课是听完了,但是电脑一合感觉啥都忘了,网上有很多写的很好的博文,借鉴,自己整理整理。 以按键中断为主线,当按键按下之后,会发生什么事?我自己想的:     硬件方面...

    中断的内容真的好多,课是听完了,但是电脑一合感觉啥都忘了,网上有很多写的很好的博文,借鉴,自己整理整理。

    以按键中断为主线,当按键按下之后,会发生什么事?我自己想的:

    硬件方面:怎么把Key这个中断源告诉CPU的?

    软件方面:CPU接收到中断后是怎么确定是Key中断的?确定了之后又是怎么去调用Key的中断处理函数的?中断处理函数在内核中是怎么运行的?怎么退出中断等等。

自己想的不全面,网上有文章写的很好:https://www.cnblogs.com/arnoldlu/p/8659972.html想想内容真的好多。

14.1.中断触发过程

    参考下STM32的中断触发过程:

attachments-2020-05-TY89LAUh5ecba16d70a4c.png

参考一篇博客:https://www.cnblogs.com/Cqlismy/p/12549087.html

    对于ARM v7架构的CPU最多支持的中断号可以到达1020个,为ID0~ID1019但是对于实际的芯片厂商并不会全部使用完这些中断号,例如:对于NXP研发的I.MX6ULL芯片就只使用了128个中断号,为ID0~ID127,其中前32个用于SoC内核私有PPI+SGI,并没有用到芯片外设上,ID32~ID127这些中断号就使用到了芯片外设上,部分IRQ号和中断源的描述如下:

attachments-2020-05-PWZYG9iZ5ecba1eb63322.png

    前面知道了按键使用的是GPIO5_1GPIO4_14,看下它们分别对应的中断ID是多少?GPIO4pin0~15对应ID72GPIO50~15对应的ID74,也就是GPIO5_1GPIO4_14和它们兄弟pins共用一个ID

attachments-2020-05-u1E7BREi5ecba22293b99.png

    当按键按下后,GPIO5_1GPIO4_14的电平发生变化,High->Low,这个电平变化怎么传递呢?硬件一路传递到GIC,具体细节就不清楚了。

attachments-2020-05-VGAlgB7N5ecba26f6562b.png

    GIC接收到芯片外部的中断信号后,就会上报到ARM Core,但是ARM Core只是提供了4个信号来给GIC汇报中断的情况,分别为:VFIQVIRQFIQIRQ,其中VFIQVIRQ是针对虚拟化,暂时不关注,FIQ是用于快速中断的,重点关注IRQGICARM Core中断汇报关系如下:

attachments-2020-05-6FuckbVG5ecba54048507.png

    GIC主要分为两个部分,分别为DistributorCPU interfacesGIC的框架示意图如下所示:

attachments-2020-05-bzU8EqyU5ecba560ca659.png

    在上面图中,可以看到,最左侧的就是中断源,中间就是GIC的内部框图,最右侧是GICARM Core进行中断信息汇报,GIC中断控制器将所有的中断源分成3类,分别为SGIPPISPI,解析如下:

        SGI:由软件触发产生的中断,可以通过向GICD_SGIR寄存器中写入数据进行触发,该中断通常用于内核间的通信;

        PPI:私有外设中断,GIC是支持多核心的CPU的,每个核心都有自己独有的中断,例如:每个核心的timer中断;

        SPI:共享外设中断,所有的ARM Core共享的中断,由SoC的外围设备所产生的中断。

    芯片的所有中断源被分成了3大类,可是中断源哪么多,GIC是怎么区分的呢?主要是通过Interrupt ID机制,GIC为每个中断源都分配一个ID号,每个CPU Interface最多支持1020个中断ID,为ID0~ID1019,分配如下:

        ID0~ID15:存在于GICDistributor中;

        ID16~ID31:存在于GICDistributor中;

        ID32~ID1019:分配给了SPI,用于SoC的外围设备中断,至于用多少个ID,然后外围设备怎么用,就要看各个芯片厂商根据实际情况去定义了。

    Distributor,主要是集中所有的中断源,负责各个中断的分发问题,确定每个中断的优先级,确定每个CPU Interface的优先级,将优先级最高的中断转发到CPU Interface,以进行优先级屏蔽和抢占处理

    CPU interfaces,该部分是和CPU Core进行连接,在每个CPU Core都可以找到一个对应的CPU Interface,它是DistributorCPU Core之间的桥梁

这些中断ID怎么和GIC关联呢?对于ARM Cortex-A7内核来说,中断ID使用了512个,GICD_ISENABLER0寄存器的bit[15:0]用来控制ID15~ID016SGI中断,GICD_ISENABLER0寄存器的bit[31:16]用来控制ID31~ID1616PPI中断,剩下的GICD_ISENABLER1~GICD_ISENABLER15寄存器用来控制SPI中断使能。

    GIC是怎么把ID传递给CPU的呢?参考:http://www.wowotech.net/irq_subsystem/gic_driver.html/comment-page-2,里面讲的很详细,大体ID传递到CPU interfaceGICC_IAR寄存器,并拉低nFIQCPU信号线,向CPU报告外设ID有中断请求发出。

    接下来CPU要做什么呢?会从当前模式切换到IRQ模式,即跳转到IRQ的中断向量表的位置,如下图,报存上下文。

attachments-2020-05-tfh92bqY5ecba5d01be68.png

    参考:Linux kernel的中断子系统之(六):ARM中断处理过程http://www.wowotech.net/irq_subsystem/irq_handler.html

“对于ARM平台而言,……,中断处理就是需要根据当前的硬件中断系统的状态,转换成一个IRQ number,然后调用该IRQ number的处理函数即可。ARM平台上的硬件中断系统已经是越来越复杂了,需要引入interrupt controller级联,irq domain等等概念。”

代码执行:__vectors_start->vector_irq -> __irq_usr -> usr_entry ->irq_handler->handle_arch_irq……archarch\arm\kernel\irq.c中设置handle_arch_irq = handle_irq

#ifdef CONFIG_MULTI_IRQ_HANDLER
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
handle_arch_irq = handle_irq;
}
#endif

__gic_init_bases->set_handle_irq(gic_handle_irq);是不是这个函数呢???

    到了gic这里会遇到gic_nr、gic_irqs、irq_desc、irq_demain这些变量,涉及的内容很多,这里先暂停一下,转到dts->node->platform_device的转化过程。前面知道了led设备树节点转化到内核的过程,现在看下key的gpio的属性是如何转化到内核中的?和led有什么区别?led gpio是不是也已被当做irq处理吧。


14.2.设备树中断节点分析


14.2.1.6ull设备树中断控制器的节点描述规范及层级

    先看下对应的参考文档,interrupt.txt主要是介绍作为interrupt-controller必须的几个属性。

    samsung,s3c24xx-irq.txt介绍s3c24xx系列芯片的中断控制器节点的属性。

attachments-2020-05-2L3rJtR45ecba6dab9469.png

    查看imx6ull.dtsi文件中只有7个节点中含有interrupt-controller属性,也就是只有7个中断控制器,看下它们的层级关系,只有gpc节点中声明了自己的interrupt-parent = <&intc>;,其他gpio1~5都没有声明自己的父中断是谁,它们的父节点中也没有声明父中断是谁,再往上找就是根节点,只有intc节点,即6ull的主中断控制器,需要3cells描述,第一个描述是属于上面3种中断的哪一种,第二个描述是哪个中断,第三个是触发类型,边缘or电平。

/*imx6ull.dtsi*/
/ {
	intc: interrupt-controller@00a01000 {
		compatible = "arm,cortex-a7-gic";
		#interrupt-cells = <3>;
		interrupt-controller;
		reg = <0x00a01000 0x1000>,
		      <0x00a02000 0x100>;
	};

	soc {2
		compatible = "simple-bus";
		interrupt-parent = <&gpc>;

		aips1: aips-bus@02000000 {
			compatible = "fsl,aips-bus", "simple-bus";

			gpio1: gpio@0209c000 {
				compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
				reg = <0x0209c000 0x4000>;
				interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
					     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
				interrupt-controller;
				#interrupt-cells = <2>;
			};gpio1

			gpc: gpc@020dc000 { //General Power Controller
				compatible = "fsl,imx6ul-gpc", "fsl,imx6q-gpc";
				reg = <0x020dc000 0x4000>;
				interrupt-controller;
				#interrupt-cells = <3>;
				interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
				interrupt-parent = <&intc>;
			};gpc
		};aips1
	};soc 
};root
/*100ask_imx6ull-14x14.dts*/

	spidev: icm20608@0{
		compatible = "invensense,icm20608";
		interrupt-parent = <&gpio1>;
		interrupts = <1 1>;
		spi-max-frequency = <8000000>; 
		reg = <0>; 
	}; 

    整理了imx6ull.dtsi100ask_imx6ull-14x14.dts中整理下主要的中断节点层级关系。在100ask_imx6ull-14x14.dts只需要引用就可以了。

attachments-2020-05-MK3b8USS5ecba7b011147.png


14.2.2.按键中断节点怎么描述的?

attachments-2020-05-TzNbnoag5ecba7d33073d.png

GPIO5_1GPIO4_14,查找参考手册看下对应的中断号是啥?P185,一个是72,一个是74

attachments-2020-05-Sox5CSYJ5ecba8ca63e54.png

    先编写pinctrl子系统,需要借用i.MX Pins Tools配置引脚,

&iomuxc_snvs {
    pinctrl-names = "default_snvs";
    imx6ull-board {
    pinctrl-0 = <&BOARD_InitPinsSnvs>;
        Key1_100ask: Key1_100ask {   //            fsl,pins = <
                MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01        0x000110A0
            >;
        };
    };
};
///////////////////////////////////////////////////////////////////////
&iomuxc {
    pinctrl-names = "default";
    pinctrl-0 = <&BOARD_InitPins>;
    imx6ull-board {
         Key2_100ask: Key2_100ask {                /*!< Function assigned for the core: Cortex-A7[ca7] */
            fsl,pins = <
                MX6UL_PAD_NAND_CE1_B__GPIO4_IO14           0x000010B0
            >;
        };
    };
};

2个子系统的配置添加到100ask_imx6ull-14x14.dts中。

gpio_keys_100ask {
    compatible = 100ask,gpio_key;
    gpios = < &gpio5 1 GPIO_ACTIVE_LOW
            &gpio4 14 GPIO_ACTIVE_LOW>;
    pinctrl-names = defualt;
    pincrtl-0 = <&key1_100ask &key2_100ask>;
}

    编写设备树节点,会发现节点中并没有用到interrupt的相关属性,因为内核对GPIO特殊优待,可以用专有函数(gpiod_to_irq())直接把gpios的属性转化为了中断号。

    在自带.dts文件中有一个gpio-keys节点,可以将其disabled

14.3.设备树节点到内核的转化

    问题,内核怎么获取设备树的中断节点呢?关于设备树节点到内核的转化,自己也写过一篇总结:https://blog.csdn.net/fendoulanyan/article/details/105635251

    只把小结拿过来:uboot把设备树编译成的.dtb文件的地址传递给内核,内核提取.dtb根节点的compatible属性来匹配machine_desc,解析.dtbchosen节点、memory节点、#address-cells#size-cells属性,为其分配内存。再去遍历整个.dtb文件的节点,将其转换成device_nodes,填充到各个device_nodesproperties,最终构建出device-tree。再根据compatible属性的转化原则将某些device_nodes转化成platform_devices,不能转化成device_nodes的节点内核提供专门的处理函数。platform_devices被挂载到不同的设备总线上,等待platform_drivers的注册。到这里设备树到内核的转化过程已经清楚了,回归到leddrv.c中。

    刚发现蜗窝科技一篇博文:http://www.wowotech.net/linux_kenrel/dt-code-analysis.html    interrupts属性最后通过drivers\of\fdt.c中的populate_properties()转化为了propertys。

    整个转化的过程可以参考:https://www.cnblogs.com/downey-blog/p/10485596.html




5 条评论&回复

请先 登录 后评论
zxq
zxq

11 篇文章

作家榜 »

  1. 百问网-周老师 19 文章
  2. st_ashang 14 文章
  3. 渐进 12 文章
  4. zxq 11 文章
  5. CMOneMO 8 文章
  6. helloworld 8 文章
  7. Infinite 5 文章
  8. 谢工 5 文章