当前位置:   article > 正文

[IMX6ULL驱动开发]-Linux对中断的处理(三)

[IMX6ULL驱动开发]-Linux对中断的处理(三)

前两篇文章主要是理论上的知识,此文章主要内容为代码上的编写以及思路等

[IMX6ULL驱动开发]-Linux对中断的处理(一)

[IMX6ULL驱动开发]-Linux对中断的处理(二)

目录

设备树中的操作

中断控制器

设备树中使用中断

编程思路

设备树相关

驱动程序相关


设备树中的操作

中断控制器

我们首先要在设备树当中创建对应的中断节点,首先我们需要了解一下中断控制器这个概念,在硬件的概念上,中断控制器其实只有GIC,但是在软件上,我们也可以把GPIO看为一个中断控制器,所以,从软件层面上看,中断控制器包括GIC、GPIO1、GPIO2等,一个GPIO上可能连接多个外部设备,它们共用一个中断号,此GPIO控制这些中断号。

GPIO1 连接到 GIC,GPIO2 连接到 GIC,所以 GPIO1 的父亲是 GIC,GPIO2 的父亲是 GIC。

假设GPIO1总共控制32个中断外部设备,假设其中16个共用一个中断号,另外16个共用另一个中断号,那么GPIO1总共会使用到GIC的两个中断号,会涉及 GIC 里的 2 个 hwirq。这些都会在设备树当中体现出来。

在设备树当中,如果存在属性:interrupt-controller,则表示该节点为中断控制器,除此之外,还需要另外一个属性,#interrupt-cells,表示使用这个中断控制器需要多少个cell

如果中断控制器存在级联关系,那么子级的中断控制器需要指定自己的父级中断控制器为哪个,比如上级的GPIO中断控制器需要指定自己的 interrupt-parent 为GIC。


设备树中使用中断

一个外设,使用哪一个中断控制器,使用中断控制器的哪个引脚,中断的触发方式在设备树中是如何体现出来的。

interrupt-parent=< &xxxx >       使用的父级中断控制器为哪个

interrupts                                   使用的父级中断控制器的引脚,具体几个cell表示由父级决定

为了方便,通常interrupt-parent和interrupts两个属性可以合起来,使用 interrupts-extended来进行表示。

interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;

  • &intc1&intc2:这些是中断控制器的引用,它们是设备树中定义的节点,负责管理中断请求。

  • 51:这些是中断号,它们是中断控制器分配给特定设备的中断请求号。

  • 10:这些是中断的触发类型。


编程思路

设备树相关

在这里,我们需要使用两个按键来触发中断,我们首先查阅引脚图,来判断使用的引脚以及触发的方式。

我们可以依照这些信息编写出如下设备树信息:

节点的名称为:gpio_key_interrupt 

compatible的值为"100ask_interrupt_test",用来适配驱动程序

gpios属性指定了引脚GPIO5 1和 GPIO4 14为中断源,当它们为低电平的时候看为中断触发

  1. gpio_key_interrupt {
  2. compatible = "100ask_interrupt_test";
  3. gpios = <&gpio5 1 GPIO_ACTIVE_LOW
  4. &gpio4 14 GPIO_ACTIVE_LOW >;
  5. };

但是这些还是不够完整的,我们需要使用pinctrl来指定这两个引脚的为GPIO功能,表明自身是哪一个 GPIO 模块里的哪一个引脚。

使用引脚配置工具按照如下方式指定,然后把对应生成代码拷贝到dts文件当中

GPIO5 1引脚的配置如上 GPIO4 14所示,但是需要注意的是,存在的节点GPIO4为iomuxc,但是GPIO5为iomuxc_snvs。

  1. gpio_key_interrupt {
  2. compatible = "100ask_interrupt_test";
  3. gpios = <&gpio5 1 GPIO_ACTIVE_LOW
  4. &gpio4 14 GPIO_ACTIVE_LOW >;
  5. pinctrl-names = "default";
  6. pinctrl-0 = <&KEY1_interrupt
  7. &KEY2_interrupt>;
  8. };

设备树文件就完成了,把它编译后,放置于开发板的 /boot/ 目录下。


驱动程序相关

驱动程序的主要思路如下:

一、编写驱动的出口函数和入口函数

二、定义 platform_driver 结构体,重点填充 probe 以及 remove

三、在probe函数中获取设备树中节点的各种GPIO信息,并为它们创建中断号,remove主要用于清除这些信息

四、中断处理函数的编写

五、驱动卸载的一些操作函数

如下结构体为 platform_driver结构体的填充,主要就是完成probe函数,在该结构体的driver成员的of_match_table的类型为 of_device_id 结构体,我们需要再定义一个此结构体,然后填充此结构体的compatible属性,用来匹配设备树。

probe函数主要实现在设备树中获取对应的引脚信息,然后通过对应的引脚信息来创建中断,创建中断的时候,需要创建中断处理函数,在这里我们的中断处理函数负责读取当前按键的值

remove函数主要用来通过对应的中断号来清除对应的中断,在这里我们还需要释放kzalloc申请的内核态的堆区空间

如果内核没有打印日志信息,使用如下命令:

echo "7 4 1 7" > /proc/sys/kernel/printk 

Makefile如下

  1. KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
  2. all:
  3. make -C $(KERN_DIR) M=`pwd` modules
  4. clean:
  5. make -C $(KERN_DIR) M=`pwd` modules clean
  6. rm -rf modules.order
  7. obj-m += interrupt_test.o

  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/errno.h>
  4. #include <linux/miscdevice.h>
  5. #include <linux/kernel.h>
  6. #include <linux/major.h>
  7. #include <linux/mutex.h>
  8. #include <linux/proc_fs.h>
  9. #include <linux/seq_file.h>
  10. #include <linux/stat.h>
  11. #include <linux/init.h>
  12. #include <linux/device.h>
  13. #include <linux/tty.h>
  14. #include <linux/kmod.h>
  15. #include <linux/gfp.h>
  16. #include <linux/gpio/consumer.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/of_gpio.h>
  19. #include <linux/of_irq.h>
  20. #include <linux/interrupt.h>
  21. #include <linux/irq.h>
  22. #include <linux/slab.h>
  23. /* 中断结构体,存储按键中断的GPIO等信息 */
  24. struct key_gpio{
  25. int gpio;
  26. int irq;
  27. enum of_gpio_flags flag;
  28. };
  29. static struct key_gpio* ket_interrupt_res;
  30. /* 中断函数 */
  31. static irqreturn_t irq_handler(int irq , void *dev_id)
  32. {
  33. struct key_gpio* p_key_gpio = (struct key_gpio*)dev_id;
  34. int val;
  35. val = gpio_get_value(p_key_gpio->gpio);
  36. printk("key %d %d\n", p_key_gpio->irq , val);
  37. return IRQ_HANDLED;
  38. }
  39. int interrupt_test_probe(struct platform_device *pdev)
  40. {
  41. int count = 0;
  42. int i = 0;
  43. struct device_node *node = pdev->dev.of_node;
  44. enum of_gpio_flags flag;
  45. int err;
  46. /* 获取GPIO中断个数 */
  47. count = of_gpio_count(node);
  48. ket_interrupt_res = kzalloc(count * sizeof(struct key_gpio), GFP_KERNEL);
  49. if( !count )
  50. {
  51. printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
  52. return -1;
  53. }
  54. for( i = 0 ; i < count ; i++ )
  55. {
  56. ket_interrupt_res[i].gpio = of_get_gpio_flags(node , i , &flag);
  57. if( ket_interrupt_res[i].gpio < 0 )
  58. {
  59. printk("%s %s line %d, of_get_gpio_flags fail \n", __FILE__, __FUNCTION__, __LINE__);
  60. }
  61. ket_interrupt_res[i].irq = gpio_to_irq(ket_interrupt_res[i].gpio);
  62. ket_interrupt_res[i].flag = flag & OF_GPIO_ACTIVE_LOW;
  63. err = request_irq(ket_interrupt_res[i].irq, irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, \
  64. "ket_interrupt", &ket_interrupt_res[i]);
  65. }
  66. return 0;
  67. }
  68. int interrupt_test_remove(struct platform_device *pdev)
  69. {
  70. struct device_node *node = pdev->dev.of_node;
  71. int count = 0;
  72. int i = 0;
  73. count = of_gpio_count(node);
  74. for( i = 0 ; i < count ; i++ )
  75. {
  76. free_irq(ket_interrupt_res[i].irq,&ket_interrupt_res[i]);
  77. }
  78. kfree(ket_interrupt_res);
  79. return 0;
  80. }
  81. static const struct of_device_id ask100_interrupt[] = {
  82. { .compatible = "100ask_interrupt_test" },
  83. { },
  84. };
  85. static struct platform_driver interrupt_test_driver = {
  86. .probe = interrupt_test_probe,
  87. .remove = interrupt_test_remove,
  88. .driver = {
  89. .name = "100ask_interrupt",
  90. .of_match_table = ask100_interrupt,
  91. },
  92. };
  93. static int __init interrupt_test_init(void)
  94. {
  95. int err;
  96. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  97. printk(" interrupt_test_driver register \n");
  98. err = platform_driver_register(&interrupt_test_driver);
  99. return err;
  100. }
  101. static void __exit interrupt_test_exit(void)
  102. {
  103. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  104. printk(" interrupt_test_driver unregister \n");
  105. platform_driver_unregister(&interrupt_test_driver);
  106. }
  107. module_init(interrupt_test_init);
  108. module_exit(interrupt_test_exit);
  109. MODULE_LICENSE("GPL");
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/744569
推荐阅读
相关标签
  

闽ICP备14008679号