linux驱动:(22)中断节点和中断函数

时间:2024-10-07 19:19:46

目录

中断过程

中断上下文

注意

中断节点

步骤 

中断函数

获取设备节点的中断号函数

获取对应io口的终端号

申请中断函数

中断梳理函数

释放对应中断


中断过程

  • 中断请求
  • 中断响应
  • 保护现场
  • 中断处理
  • 恢复现场
  • 中断返回
  • 中断屏蔽

中断上下文

中断的存在可以极大的提高 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

释放对应中断

中断使用完成以后就要通过 free_irq 函数释放掉相应的中断。 如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断

void free_irq(unsigned int irq,void *dev)
  • 参数
    • irq:要释放的中断号
    • dev:如果中断设置为共享(IRQF_SHARED)的话, 此参数用来区分具体的中断。 共享中断只有在释放最后中断处理函数的时候才会被禁止掉。