基于设备树编写按键中断驱动程序

  Linux内核版本:4.14.2

  本文基于itop4412开发板,编写驱动程序响应HOME按键中断,编写这个按键驱动程序需要做如下几个工作:

    1. 在原理图中确定HOME按键的引脚

    2. 在设备树文件中添加节点描述HOME引脚

    3. 重新编译烧写设备树

    4. 编写驱动程序,调用设备树接口函数获取HOME引脚的中断号,使用中断号注册按键中断处理程序

1. 在原理图中确定HOME按键的引脚

基于设备树编写按键中断驱动程序

基于设备树编写按键中断驱动程序

  在原理图中找到HOME按键对应的引脚是GPX1_1

2. 在设备树文件中添加节点描述HOME引脚

  itop4412开发板中将GPIO分成多个组,GPX1_1在gpx1组中,设备树文件exynos4412-pinctrl.dtsi中用如下节点描述gpx1组:

基于设备树编写按键中断驱动程序

  我们需要在设备树文件exynos4412-itop-elite.dts中,根据exynos4412-pinctrl.dtsi文件对gpx1组的描述添加HOME引脚的描述节点,如下

基于设备树编写按键中断驱动程序

3. 重新编译烧写设备树

4. 编写驱动程序,调用设备树接口函数获取HOME引脚的中断号,使用中断号注册按键中断处理程序

  驱动程序按照平台总线的架构编写,用platform_driver_register注册驱动;

  4.1 设备和驱动匹配实现过程

         在设备树文件中添加了HOME按键的描述节点后,内核在解析设备树时,就会将上述节点转换成一个平台设备platform_device;在struct platform_driver结构体中,将compatible属性设置为”irq-keys”,那么,加载驱动后,就会和内核生成的这个平台设备platform_device匹配,从而进入probe函数中

驱动程序中指定compatible的程序如下:

static const struct of_device_id irq_test_of_match[] = {

    { .compatible = "irq-keys", },

    { },

};

MODULE_DEVICE_TABLE(of, irq_test_of_match);

static struct platform_driver irq_test_device_driver = {

    .probe      = irq_test_probe,

    .driver     = {

        .name   = "irqtest_keys",

        .of_match_table = irq_test_of_match,

    }

};

  4.2 HOME引脚信息读取过程

    上述驱动和设备匹配后,程序就进入probe函数中,在probe函数中,可以使用设备树相关的接口读取HOME引脚的信息:

           读取子节点的个数,irqtest_keys只有一个子节点key-home

                  device_get_child_node_count

           获取每个子节点的结构体

                  device_for_each_child_node(dev, child)

           获取子节点中引脚的虚拟中断号,这个虚拟中断号是内核自动生成的

                  irq_of_parse_and_map(to_of_node(child), 0)

           获取子节点gpio描述符gpiod

                  devm_fwnode_get_gpiod_from_child

           使用虚拟中断号注册中断处理函数

                  devm_request_any_context_irq

测试驱动程序如下:

  1 #include <linux/module.h>
  2 #include <linux/init.h>
  3 #include <linux/fs.h>
  4 #include <linux/interrupt.h>
  5 #include <linux/irq.h>
  6 #include <linux/sched.h>
  7 #include <linux/pm.h>
  8 #include <linux/slab.h>
  9 #include <linux/sysctl.h>
 10 #include <linux/proc_fs.h>
 11 #include <linux/delay.h>
 12 #include <linux/platform_device.h>
 13 #include <linux/input.h>
 14 #include <linux/gpio_keys.h>
 15 #include <linux/workqueue.h>
 16 #include <linux/gpio.h>
 17 #include <linux/gpio/consumer.h>
 18 #include <linux/of.h>
 19 #include <linux/of_irq.h>
 20 #include <linux/spinlock.h>
 21 
 22 /* 设备树节点 */
 23 #if 0
 24 irqtest_keys {
 25     compatible = "irq-keys";
 26     key-home {
 27         label = "GPIO key-home";
 28         gpios = <&gpx1 1 GPIO_ACTIVE_LOW>;
 29               pinctrl-0 = <&my_irq_key>;
 30               pinctrl-names = "default";
 31     };
 32 };
 33 
 34 my_irq_key: my-irq-key {
 35     samsung,pins = "gpx1-1";
 36     samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
 37 //    samsung,pin-drv = <EXYNOS4_PIN_DRV_LV4>;
 38 };
 39 #endif
 40 
 41 struct my_gpio_keys_button {
 42     unsigned int code;
 43     int gpio;
 44     int active_low;
 45     const char *desc;
 46     unsigned int type;
 47     int wakeup;
 48     int debounce_interval;
 49     bool can_disable;
 50     int value;
 51     unsigned int irq;
 52     struct gpio_desc *gpiod;
 53 };
 54 
 55 static char *label[2];
 56 static struct device *dev;
 57 static struct my_gpio_keys_button *button;
 58 
 59 static irqreturn_t irq_test_irq_isr(int irq, void *dev_id)
 60 {
 61     printk(KERN_INFO "get irq --> irq_test_irq_isr.
");
 62 
 63     return IRQ_HANDLED;
 64 }
 65 
 66 static int irq_test_probe(struct platform_device *pdev)
 67 {
 68     /* 获取节点信息,注册中断 */
 69     dev = &pdev->dev;
 70     struct fwnode_handle *child = NULL;
 71     int nbuttons;
 72     int irq, error;
 73     irq_handler_t isr;
 74     unsigned long irqflags;
 75 
 76     nbuttons = device_get_child_node_count(dev);
 77     if (nbuttons == 0) {
 78         printk(KERN_INFO "no child exist, return
");
 79         return ERR_PTR(-ENODEV);
 80     }
 81 
 82     printk(KERN_INFO "child num is %d.
", nbuttons);
 83     button = devm_kzalloc(dev, sizeof(struct my_gpio_keys_button) * nbuttons, GFP_KERNEL);
 84 
 85     /* 获取lable参数,父节点没有lable属性 */
 86     device_property_read_string(dev, "label", label[0]);
 87     printk(KERN_INFO "parent lable %s
", label[0]);
 88 
 89     /* 扫描处理每个子节点 */
 90     device_for_each_child_node(dev, child) {
 91         /* 获取虚拟中断号virq */
 92         if (is_of_node(child)) {
 93             button->irq = irq_of_parse_and_map(to_of_node(child), 0);
 94         }
 95 
 96         fwnode_property_read_string(child, "label", &button->desc);
 97         /* 获取gpio描述符gpiod */
 98         button->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL,
 99                                 child,
100                                 GPIOD_IN,
101                                 button->desc);
102         if (IS_ERR(button->gpiod)) {
103             printk(KERN_INFO "get gpiod failed, return.
");
104             return -ENOENT;
105         }
106 
107         /* 检查虚拟中断号,可不使用 */
108         if (!button->irq) {
109             irq = gpiod_to_irq(button->gpiod);
110             if (irq < 0) {
111                 error = irq;
112                 dev_err(dev,
113                     "Unable to get irq number for GPIO %d, error %d
",
114                     button->gpio, error);
115                 return error;
116             }
117             button->irq = irq;
118         }
119 
120         printk(KERN_INFO "get virq %d for key.
", button->irq);
121         isr = irq_test_irq_isr;
122         irqflags = 0;
123         irqflags |= IRQF_SHARED;
124 //devm_request_any_context_irq(class_dev, data->irq,int26_irq, IRQF_TRIGGER_FALLING, data->name, data);
125 
126         /* 设置引脚为输入模式 */
127         gpiod_set_value(button->gpiod, 1);
128         gpiod_direction_input(button->gpiod);
129 
130         /* 注册中断 */
131         /* 最后一个参数是传给中断函数的参数 */
132         error = devm_request_any_context_irq(dev, button->irq, isr, IRQF_TRIGGER_FALLING, button->desc, NULL);
133         if (error < 0) {
134             dev_err(dev, "Unable to claim irq %d; error %d
", button->irq, error);
135             return error;
136         }
137     }
138 
139     return 0;
140 }
141 
142 static const struct of_device_id irq_test_of_match[] = {
143     { .compatible = "irq-keys", },
144     { },
145 };
146 
147 MODULE_DEVICE_TABLE(of, irq_test_of_match);
148 
149 static struct platform_driver irq_test_device_driver = {
150     .probe      = irq_test_probe,
151     .driver     = {
152         .name   = "irqtest_keys",
153         .of_match_table = irq_test_of_match,
154     }
155 };
156 
157 static int __init irq_test_init(void)
158 {
159     return platform_driver_register(&irq_test_device_driver);
160 }
161 
162 static void __exit irq_test_exit(void)
163 {
164     devm_free_irq(dev, button->irq, NULL);
165     platform_driver_unregister(&irq_test_device_driver);
166 }
167 
168 module_init(irq_test_init);
169 module_exit(irq_test_exit);
170 
171 MODULE_LICENSE("GPL");