Linux学习_设备树实现中断
- 中断层级结构
- 设备树_中断控制器
- 设备树_中断子节点
- 驱动程序
- 获取GPIO
- 获取中断号
- 申请中断
- 中断处理函数
中断层级结构
就硬件而言,中断控制器指的就是GIC,但是实际在软件上,图中的GPIO等我们也称之为中断控制器。
外部设备1~n共享着GPIO的B号中断,而GPIO的多个中断又汇总起来共享GIC的A号中断,这种像树一样的嵌套形式,自然也有树一样的关系,即父-子关系,我们在子节点中称之为parent
。
在实际处理时,底层会这样做:
- CPU发现是A号中断,调用GIC中断A的handle_A函数,
- handle_A函数去调用自己名下所有中断的handle_xxx函数
- handle_xxx们会判断是不是自己这里中断,不是就立刻结束,是就继续执行处理
- 最后B发现是自己,只有handle_B继续执行,去调用自己名下所有设备的中断函数,重复以上流程
- 最终对应设备的handle函数被执行
当然了,这是底层的执行逻辑我们需要了解,我们调用设备树有指定的规范
设备树_中断控制器
示例:
vic: intc@10140000 {
compatible = "arm,versatile-vic";
interrupt-controller; 表明自己控制器的身份
#interrupt-cells = <3>;表明需要几个cell描述自己
reg = <0x10140000 0x1000>;
};
其中interrupt-controller;
表明自己控制器的身份,#interrupt-cells = <3>;
表明需要几个cell描述自己,这俩是必须的
设备树_中断子节点
如果要使用该controller,那么必须将其声明为parent并加以描述
例如:
i2c@7000c000 {
gpioext: gpio-adnp@41 {
compatible = "ad,gpio-adnp";
interrupt-parent = <&gpio>; 声明自己的parent是哪个
interrupts = <160 1>; 代表160号,上升沿触发
gpio-controller;
#gpio-cells = <1>;
interrupt-controller;
#interrupt-cells = <2>;
};
1 = low-to-high edge triggered,上升沿触发
2 = high-to-low edge triggered,下降沿触发
4 = active high level-sensitive,高电平触发
8 = active low level-sensitive,低电平触发
这个玩意可以组合使用,例如3代表上升下降双边沿都触发
实际上,IMX6ULL在各模块和GIC之间还有个GPC INTC模块General Power Controller, Interrupt Controller,作用是提供中断屏蔽、中断状态查询、唤醒功能。
驱动程序
如果用GPIO实现中断的话,总的来说分四步:
- 获取GPIO
- 获取中断号
- 申请中断
- 中断处理函数
获取GPIO
利用以下函数可以从设备树中获取中断号
count = of_gpio_count(node);
for (i = 0; i < count; i++)
gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag);
获取中断号
之前学习设备树的时候提到过,设备树中只有两种会被转化为platform_device,这两种就是:
- 根节点下含有compatile属性的子节点
- 某节点compatile属性为:“simplebus”,“simple-mfd”,“isa”,"arm,amba-bus"四者之一的,其子节点全部可以
特别的,总线I2C、SPI节点会由总线驱动程序转化为i2c_client结构体和spi_device结构体,就不变成platform_device了;而GPIO太常用了,所以有一堆好用的函数
platform_device:使用platform_get_resource函数
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type,unsigned int num);
参数一,设备号
参数二,资源类型,IORESOURCE_MEM、 IORESOURCE_REG,以及我们这里要用的IORESOURCE_IRQ中断资源
参数三,这类资源中的哪一个
I2C、SPI:储存在i2c_client、spi_device结构体的irq成员中
GPIO:GPIO由于很常用,所以有特别的优待----->一堆函数,gpio_to_irq
啥也不是的:of_irq_get()
申请中断
err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys_100ask
[i]);
中断处理函数
static irqreturn_t gpio_key_isr(int irq, void *dev_id){
struct gpio_key *gpio_key = dev_id;
int val;
val = gpiod_get_value(gpio_key->gpiod);
printk("key %d %d\n", gpio_key->gpio, val);
return IRQ_HANDLED;
}