文章目录
一、回顾
裸机开发中:
-
通用中断控制器(GIC)
中断类型、硬件中断号、分发器和cpu接口单元 -
中断向量表
一级查表、二级查表 -
中断处理流程
进入irq模式、保护现场、获取硬件中断编号、执行中断处理函数、还原现场
二、设备树中GIC中断控制器节点
arch/arm/boot/dts/imx6ull.dtsi
初始化中断控制器、设置其他中断控制器节点的描述格式
intc: interrupt-controller@a01000 {
compatible = "arm,cortex-a7-gic";
// 描述下一级中断信息节点所需要的单元个数
#interrupt-cells = <3>;
// 表示该设备是一个中断控制器,外设可以连接在该中断控制器上
interrupt-controller;
// GIC的分发器和cpu接口单元寄存器地址
reg = <0xa01000 0x1000>,
<0xa02000 0x100>;
};
因为中断控制器是linux系统中必不可少的,因此配置gic的代码在内核中并不是以驱动模块的形式存在的。因此搜索compatilble属性值匹配到的不是一个驱动模块文件。
三、外设中断控制器节点
arch/arm/boot/dts/imx6ull.dtsi
某个外设和中断密切相关
管理某一种具体中断
gpio5: gpio@20ac000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x20ac000 0x4000>;
// 负责管理的中断类型,上面父节点的#interrupt-cells = <3>从,此属性的每个单元用3个值来描述
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_GPIO5>;
// 和上面一样,有此属性表示此节点是一个中断控制器,负责管理某一种的中断
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
// 其它外设节点想要使用这个中断控制器上的中断时的描述格式。使用范例见下。
#interrupt-cells = <2>;
gpio-ranges = <&iomuxc 0 7 10>, <&iomuxc 10 5 2>;
};
- interrupts属性:
GIC_SPI:中断类型:0 表示 SPI 中断,1 表示 PPI 中断
74:中断号,对于 SPI 中断来说中断号的范围为 0 ~ 987,对于 PPI 中断来说中断号的范围为 0~15
IRQ_TYPE_LEVEL_HIGH:中断类型,高电平触发
三、其他设备使用中断节点
使用某一种具体中断(控制器)
button_interrupt {
compatible = "button_interrupt";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_button>;
button_gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
status = "okay";
// 表明归属的上一级中断,使用上面描述的gpio5中断控制器
interrupt-parent = <&gpio5>;
// gpio5上有32个外部中断,button节点只用到其中一个,1表示gpio5-1;上升沿触发中断
interrupts = <1 IRQ_TYPE_EDGE_RISING>;
};
1、中断类型
include/linux/irq.h
标志, bit[3:0]表示中断触发类型,为 1 的时候表示上升沿触发,为 2 的时候表示下降沿触发,为 4 的时候表示高电平触发,为 8 的时候表示低电平触发。 bit[15:8]为 PPI 中断的 CPU 掩码。
// 常用的五个触发类型
enum {
IRQ_TYPE_NONE = 0x00000000,
IRQ_TYPE_EDGE_RISING = 0x00000001,
IRQ_TYPE_EDGE_FALLING = 0x00000002,
IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
IRQ_TYPE_LEVEL_HIGH = 0x00000004,
IRQ_TYPE_LEVEL_LOW = 0x00000008,
...
}
四、常用函数
1、request_irq()函数
申请中断,若申请成功会自动激活(使能)中断,不再需要我们手动去使能中断。
此函数可能会导致睡眠。
include/linux/interrupt.h
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
-
参数:
irq:要申请的中断号
handler:函数指针,指向中断处理函数,详见下(1)
flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,详见下(2)
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev:传递给中断处理函数的第二个参数,一般指向 device结构体变量,区分不同设备共用同一中断。如果将 flags 设置为 IRQF_SHARED的话, dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。 -
返回值:
成功:0
失败:负数
(1)irq_handler_t
中断处理函数的数据类型
typedef irqreturn_t (*irq_handler_t)(int, void *);
参数1:中断处理函数对应的中断号
参数2:通用指针,需要与request_irq函数的dev参数保持一致
返回值详见下
irqreturn_t
enum irqreturn {
// 不是本驱动程序的中断,不处理
IRQ_NONE = (0 << 0),
// 正常处理
IRQ_HANDLED = (1 << 0),
// 不使用中断处理函数来处理,使用中断下半部处理
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
(2)flags
include/linux/interrupt.h
描述中断的特征
标志, bit[3:0]表示中断触发类型,为 1 的时候表示上升沿触发,为 2 的时候表示下降沿触发,为 4 的时候表示高电平触发,为 8 的时候表示低电平触发。 bit[15:8]为 PPI 中断的 CPU 掩码。
// 共享中断,可以有多个外设共同使用这个中断
#define IRQF_SHARED 0x00000080
// 此中断只出发一次
#define IRQF_ONESHOT 0x00002000
#define IRQF_TRIGGER_NONE 0x00000000
// 下面四个描述的就是中断的触发方式
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
2、free_irq()函数
释放中断
include/linux/interrupt.h
const void *free_irq(unsigned int irq, void *dev_id)
- 参数:
irq:要释放的中断号
dev:传递给中断处理函数的第二个参数。如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。
3、enable_irq()函数
使能中断
kernel/irq/manage.c
void enable_irq(unsigned int irq)
- 参数:
irq:要使能的中断号
4、disable_irq()函数
禁止中断,但是要等待中断执行完毕再禁止
kernel/irq/manage.c
void disable_irq(unsigned int irq)
- 参数:
irq:要禁止的中断号
5、disable_irq_nosync()函数
立即禁止中断,不等待中断执行完
kernel/irq/manage.c
void disable_irq_nosync(unsigned int irq)
- 参数:
irq:要禁止的中断号
6、local_irq_disable()宏
include/linux/irqflags.h
禁止处理器中断,就是禁止全部中断
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
7、local_irq_enable()宏
include/linux/irqflags.h
打开处理器中断,就是使能所有中断
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)