【Linux驱动】设备树中指定中断 | 驱动中获得中断 | 按键中断实验

发布时间:2024年01月15日

🐱作者:一只大喵咪1201
🐱专栏:《Linux驱动》
🔥格言:你只管努力,剩下的交给时间!
图

🏀在设备树中指定中断

图
继续拿这个中断流程图来说话。

在硬件上,中断控制器只有GIC这一个,但是我们在软件上可以把GPIO也归类为中断控制器。

芯片会有多个GPIO模块,所以软件上的中断控制器就会有很多个:GIC,GPIO1,GPIO2,GPIO3…等等。

其中GPIO1、GPIO2、GPIO3这些中断控制器模块都连接汇集到GIC模块,所以GIC模块就是GPIOx模块的父亲。

假设GPIO1有32个中断源,但是它把其中的16个汇聚起来向GIC发出一 个中断,把另外16个汇聚起来向GIC发出另一个中断。这就意味着GPIO1会用到 GIC 的两个中断,会涉及 GIC 里的 2 个 hwirq

  • 这些层级关系、中断号(hwirq),都会在设备树中有所体现。

dtsi:

图
如上图所示由BSP工程师提供的dtsi设备树文件:

  • intc:表示GIC中断控制器。
  • gpio1:表示GPIO1中断控制器。
  • gpio2:表示GPIO2中断控制器。

GIC是顶层中断控制器,所以它没有父亲,而GPIO1和GPIO2都是soc的子节点,子节点会继承父节点的属性

  • interrupt-parent = <&gpc>:表示父亲节点,soc节点的interrupt-parent = <&gpc>
  • GPIO1和GPIO2继承socinterrupt-parent = <&gpc>属性,所以它们的父节点也是gpc

本喵没有列出来,gpc的父节点是GIC中断控制器,所以从设备树反推出IMX6ULL的中断框图,它比之前多了一个GPC INTC

图
如上图所示新的中断框图,GPC INTC的功能是提供中断屏蔽、中断状态查询等功能。

  • 实际上这些功能在GIC中也实现了。
  • 之所以保留它是因为它还能提供唤醒功能。

继续回到dtsi设备树文件来看,每一个中断控制器中都有两个必须的属性:

  • interrupt-controller:该属性表明它是中断控制器。
  • #interrupt-cells:该属性表明引用这个中断控制器的话需要多少个cell
    • #interrupt-cells=<1>:别的节点要使用这个中断控制器时,只需要一个 cell来表明使用哪一个中断。
    • #interrupt-cells=<2>:别的节点要使用这个中断控制器时,需要两个cell,其中第一个来表明使用哪一个中断,第二个一般来描述中断的触发类型。

由于GIC规定了要引用该中断控制器需要使用3个cell,所以GPIO1和GPIO2就使用了<GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>这样的3个cell

  • GIC_SPI:GPIO模块向GIC发出中断的类型。
  • 66:GPIO模块向GIC发出中断的硬件中断号hirq
  • IRQ_TYPE_LEVEL_HIGH:GPIO模块向GIC发出中断的触发类型。

除了引用GIC中断控制器外,GPIOx模块也有自己的interrupt-controller属性和#interrupt-cells = <2>属性:

  • GPIO1和GPIO2中断控制器模块规定引用该控制器时需要使用两个cell

这是都是芯片厂家写好的,提供给我们使用的,我们只需要知道怎么去用它们就行。

dts:

图
如上图我们写的dts设备树文件中,这是一个spidev节点:

  • interrupt-parent = <&gpio1>:该设备的父节点是gpio1,表明该外部设备引用GPIO1控制器。
  • interrupts = <1 1>:表示该设备使用的硬件中断号hirq是1,触发类型是1。
    • 新写法interrupts-extend:一个这样的属性就可以既指定interrupt-parent又指定interrupts
    • 比如interrupts-extend = <&gpio1 1 1>

触发类型这里写的1,代表什么意思呢?有写什么类型呢?

tu
如上图所示,1就代表着low-to-high edge triggered上升沿触发。

如此一来,在设备树中指定了该外部设备的中断,在驱动程序中就可以直接使用了。

🏀代码中获得中断

我们知道,设备树中的节点有的会被内核转化为platform_device结构体,有些则不会。

对于能转化为platform_device结构体的节点,如果它在设备树里指定了中断属性,那么可以从platform_device中获得中断资源:

图
如上图所示platform_get_resource函数,用来获取中断资源:

  • dev:转化后的platform_device结构体指针。
  • type:获取哪类资源。
    • IORESOURCE_MEMIORESOURCE_REGIORESOURCE_IRQ 等。
  • num:这类资源中的哪一个。
    • 一个节点中使用不止一个中断源。

对于I2C设备、SPI设备,总线驱动在处理设备树里的I2C子节点时,也会处理其中的中断信息。

  • 一个I2C设备会被转换为一个i2c_client结构体,中断号会保存在i2c_clientirq成员里。

图
如上图代码所示,当I2C总线和驱动程序匹配以后,会自动调用probe函数,在该函数中使用of_irq_get函数,从它的子节点I2C设备中解析出中断号irq

  • 一个 SPI 设备会被转换为一个spi_device结构体,中断号会保存在spi_deviceirq成员里。

图
如上图,当SPI总线和驱动程序匹配后,在它的probe函数中,也会调用of_irq_get从子节点SPI设备中解析出中断号irq

如果设备节点既不能转换为platform_device,也不是I2C和SPI设备,那么在驱动程序中,可以自行调用of_irq_get函数去解析得到中断号。


对于GPIO引脚,芯片厂家提供了专门的接口函数来获取中断:

  • of_get_gpio_flags:使用该函数获取GPIO引脚,此时获取到的是使用老的方式描述GPIO引脚信息的那个整数。
  • gpio_to_desc:使用该函数获取GPIO引脚,获得是使用新方式描述GPIO引脚信息的那个desc结构体。
  • gpiod_to_irq:最后使用该函数从得到的GPIO引脚信息中获得软件中断号。

🏀按键中断

对于 GPIO 按键,我们并不需要去写驱动程序,使用内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c就可以,然后需要做的只是修改设备树指定引脚及键值。

但是本喵还是要从头写一遍按键驱动程序,特别是如何使用中断,因为中断是其他基础知识的前提,以后的休眠-唤醒,POLL机制,异步通知,定时器,中断的线程化处理等内容都离不开中断。

图
如上图所示本喵使用的IMX6ULL开发板原理图,按键KEY1和按键KEY2使用的是GPIO5_1GPIO4_14两个引脚,并且是低电平有效。

同样的,按键驱动程序也要分为驱动程序和设备树两部分。

?驱动程序

图
如上图所示gpio_key_drv.c文件中的驱动程序:

  • 创建platform_driver结构体gpio_keys_driver,并且进行初始化。
    • 使用gpio_key_probe初始化probe函数。
    • 使用gpio_key_remove初始化remove函数。
    • 使用of_device_id数组Big_Miaomi_keys初始化driver.of_match_table
      • 数组中compatible属性的值是"Big_Miaomi,gpio_keys",用来和设备节点匹配。
  • 在入口函数gpio_key_init中使用platform_driver_register函数向内核注册驱动程序。
  • 在出口函数gpio_key_exit中使用platform_driver_unregister从内核中取消驱动程序注册。
  • 完善设备驱动信息。
  • 由于这是按键中断,不需要应用层来调用,所以不用在/dev下创建设备节点,也不用注册file_operations结构体。

probe函数:

图
如上图所示probe函数的实现:

  • 定义全局一个结构体指针gpio_keys_Big_Miaomi,该结构体是struct gpio_key 类型,用来存放中断的信息。
  • 使用of_gpio_count获得中断源个数,因为一个设备节点可能有多个中断源。
    • 按键设备可能有多个按键,此时就有多个中断源。
  • 使用内核的kzalloc函数在堆区上开辟一段堆空间,来存放中断信息。
  • 每一个中断源,都需要获取它的详细信息:
    • 使用of_get_gpio_flags获取中断的引脚信息gpio和有效标志。
    • 将用整数描述的引脚信息转换成使用gpio_desc类型描述的引脚信息。
    • 将中断引脚设置成低电平有效,与OF_GPIO_ACTIVE_LOW相与。
      • 因为电路中按键是低电平有效,原本的引脚标志是输入GPIOF_IN
    • 再使用devm_gpio_request_one将引脚状态设置成逻辑值。
      • 按键按下后,读取到的引脚值是1,尽管物理值是0。
    • 使用gpio_to_irq获取中断引脚的软件中断号irq

将所有中断信息获取到以后,再为每一个中断注册中断函数:

  • 使用request_irq注册中断服务函数:
    • 第一个参数传入中断的软件中断号gpio_keys_Big_Miaomi[i].irq
    • 第二个参数传入中断服务函数gpio_key_isr
    • 第三个参数传入中断触发方式IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING双边沿触发。
    • 第四个参数传入中断名称"100ask_gpio_key",该参数不重要。
    • 第五个参数传入dev_id,也就是中断信息所在的结构体指针&gpio_keys_Big_Miaomi[i]

此时中断函数就注册完成了,接下来就是实现中断服务函数中要做什么:

图
如上图所示中断服务函数,在里面仅获取引脚电平的逻辑值,并且打印出描述引脚的那个整数编号和引脚状态。

remove函数:

tu
如上图所示remove函数,在卸载驱动程序时:

  • 使用free_irq将所有前面注册的中断释放掉。
  • 使用kfree将存放引脚信息的堆空间释放掉。

?设备树

图
如上图所示蓝色框中代码,使用图形化工具生成GPIO5_1GPIO4_14的两个pin-controller节点代码。

图
如上图所示,将生成的pin-controller节点代码复制到我们要写的dts设备树文件中:

  • GPIO5_1:节点名称为Big_Miaomi_key1,表示按键1。
  • GPIO4_14:节点名称为Big_Miaomi_key2,表示按键2。

图
如上图所示,在dts设备树文件的根节点下,增加按键外部设备节点gpio_keys_Big_Miaomi

  • 使用GPIO子系统指定按键引脚和有效电平。
  • 使用Pinctrl子系统将引脚复用为通用GPIO功能。
  • 在原本的gpio-keys节点中,使用status = "disabled"属性让该节点失能,防止影响我们自己创建的按键节点。
  • 这里并没有在设备树中指定按键的interrupts-extend
  • 因为对于GPIO,芯片厂家提供了驱动程序中的一些列接口,可以直接获取GPIO的中断信息,包括中断号以及中断控制器等。

?上机实验

图
如上图所示,使用上面的makefile文件编译驱动程序,生成gpio_key_drv.ko驱动模块。

图
如上图所示,将写好的gpio_key_drv.c驱动程序和设备树文件,以及Makefile上传到服务器上,分别进行编译,编程成功后将生成的gpio_key.kodtb文件都拷贝到网络文件系统中供开发板使用。

图
如上图所示,先让开发板使用新的dtb设备树文件,再使用insmod安装gpio_key_drv.ko驱动模块,可以看到安装成功后输出匹配porbe函数的调试信息。

图
如上图所示,按下开发板上的KEY1KEY2时:

  • 按键按下,按键值是1,松开按键值是0。
  • 按键值前面的整数就是描述按键引脚的编号。

🏀总结

要知道在设备树中是如何描述一个设备的中断的,包括父节点interrupt-parent属性,以及描述中断引脚的interrupts属性的用法。

还要知道在中断程序中是如何获取设备树中的中断信息的,以及如何使用这些中断信息。

最后要会实现按键中断程序。

文章来源:https://blog.csdn.net/weixin_63726869/article/details/135537927
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。