目录
中断过程
- 中断请求
- 中断响应
- 保护现场
- 中断处理
- 恢复现场
- 中断返回
- 中断屏蔽
中断上下文
中断的存在可以极大的提高 CPU 的运行效率,但是中断会打断内核进程中的正常调度和运行,所以为保证系统实时性,中断服务程序必须足够简短,但实际应用中某些时候发生中断时必须处理大量的事物,这时候如果都在中断服务程序中完成,则会严重降低中断的实时性基于这个原因,linux 系统提出了一个概念:把中断服务程序分为两部分:中断上文和中断下
- 中断上文:完成尽可能少切比较急的任务,中断上文的特点就是响应速度快。
- 中断下文:处理中断剩余的大量比较耗时间的任务,而且可以被新的中断打断
注意
- 中断上文越快越好,中断下文可以做比较耗时间的事情,但是不能死循环。
- 以前的可以嵌套,现在的不可以嵌套
- 如果中断简单,可以只写上文
中断节点
如果一个设备需要用到中断功能,开发人员就需要在设备树中配置好中断属性信息,因为设备树是用来描述硬件信息的,然后Linux内核通过设备树配置的中断属性来配置中断功能。
我们只需要关系怎么在设备树中指定中断,怎么在代码中获得中断就可以。其他的事情,比如设备树中的中断控制器,这些都是由原的 BSP 工程师帮我们写好了,我们不需要来修改他。
比如,在 xxxx.dtsi,文件,其中的 inc 节点就是xxxx 的中断控制器节点:
intc: interrupt-controller@00a0100{
compatible = "arm,cortex-a7-gic";
#interrupt-cells = <3>;//表示他的子节点是用3个cells来描述中断的
interrupt-controller;//表示中断控制器
reg = < 0x00a0100 0x1000>,
< 0x00a02000 0x100>;
};
- intc就是中断控制器,因为节点属性中有一个interrupt-controller;
-
#interrupt-cells = <3>表示他的子节点是用3个cells来描述中断的
gpio1: gpio@0x09c000{
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>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
- 上面的中断节点的子节点可以为gpio中断,我们分析一下gpio中断
- gpio中断节点里面也有interrupt-controller;中断控制器属性
- interrupts 属性中一个<>括号里面有三个参数,因为他的上一个节点intc中#interrupt-cells属性等于 <3>,这是对应的
- 第一个参数;中断类型,共享中断和私有中断
- 第二个参数;中断编号
- 第三个参数;触发类型 在linux/irq.h中定义
-
#interrupt-cells = <2>;表示如果想用这个gpio节点就要用两个cells来描述这个节点
key{
#address-cells =<1>;
#size-cells = <1>;
compatible = "key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;//KEY 0
interrupt_parent = <&gpio1>
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;//双边沿触发
status = "okay";
}
- 现在这个是接在上面gpio中断节点上面的外设,是一个按键中断,通过中断来获取按键的键值
- compatible是与驱动名字name匹配的
- pinctrl和gpio子系统
- 第5,6,7行设置为了gpio功能,按键的输入
- key-gpio要设置为引脚复用gpio
- 描述中断
- 第8,9行是描述中断的
- interrupt_parent表示我们要使用gpio1的中断控制器,因为我们这个按键属于gpio1组
- interrupts表示设置中断源,这个<>括号里面的参数就要根据中断控制器中的interrupt-cells属性来决定要多少个值
- 第一个参数;要用的中断,也就是io口号
- 第二个参数;触发类型 在linux/irq.h中定义
步骤
根据上面分析,如果我们要设置gpio中断,就只需要两步,
- 第一步是将io设置成gpio
- 第二步是用interrupt_parent和interrupts来描述中断并绑定什么中断控制器
中断函数
获取设备节点的中断号函数
编写驱动的时候需要用到中断号,每一个中断都有中断号,我们用到中断号,中断信息已经写到了设备树里面,因此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号,函数原型如下:
unsigned int irq_of_parse_and_map(sturct device_node *dev,int index)
- 参数
- dev:设备节点
- index:索引号,interupts可能包含多个中断信息,通过index指定要获取的信息
- 返回值
- 中断号
获取对应io口的终端号
int gpio_to_irq(unsigned int gpio)
- 参数
- gpio:要获取的gpio编号
- 返回值
- gpio对应的中断号
申请中断函数
同 GPI0 一样,在Linux内核里面,如果我们要使用某个中断也是需要申请的
int reguest_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)
- 参数
- irq:要申请中断的中断号
- handler:中断处理函数
- flags:中断标志位,在linux/interrupt.h中定义
- name:自定义中断名字,启动后可以在/proc/interrupts中看到
- dev:传递给中断函数的第二个参数
- 返回值
- 0成功,负值失败,-EBUSY已经被申请了
中断梳理函数
irqreturn_t(*irq_handler_t)(int,void *)
- 参数
- 第一个参数:要中断处理函数要相应的中断号
- 第二个参数:通用指针,要与传递进来参数的类型保持一致,用于区分共享中断的不同设备,也可以指向设备数据结构
- 返回值
- irqreturn_t是一个枚举类型,返回一般形式是return IRQ_RETVAL(IRQ_HANDLED)
enum irqreturn{ IRQ_NONE = (0 << 0), IRQ_HANDLED = (1 << 0), IRQ_WAKE_THREAD = (1 << 1), }; typedef enum irqreturn irqreturn_t
- irqreturn_t是一个枚举类型,返回一般形式是return IRQ_RETVAL(IRQ_HANDLED)
释放对应中断
中断使用完成以后就要通过 free_irq 函数释放掉相应的中断。 如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断
void free_irq(unsigned int irq,void *dev)
- 参数
- irq:要释放的中断号
- dev:如果中断设置为共享(IRQF_SHARED)的话, 此参数用来区分具体的中断。 共享中断只有在释放最后中断处理函数的时候才会被禁止掉。