作者

彭東林

概述

這篇博客以一個簡單的按鍵中斷來演示一下有了設備樹後的中斷的使用,其中涉及到新版kernel的pinctrl和gpio子系統。

正文

在tiny4412的底板上有四個key,如下:

 

上圖中,在沒有按鍵的時候,對應的GPIO是被拉高的,當按下鍵的時候,對應的GPIO被拉低,從而產生一個下降沿中斷。

有了上面的準備,首先我們需要修改設備樹,添加相應的節點和相關的屬性:



1 diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts
2 index 610202a..a130047 100644
3 --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts
4 +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts
5 @@ -136,6 +136,14 @@
6 };
7 };
8 #endif
9 +
10 + interrupt_demo: interrupt_demo {
11 + compatible = "tiny4412,interrupt_demo";
12 + tiny4412,int_gpio1 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
13 + tiny4412,int_gpio2 = <&gpx3 3 GPIO_ACTIVE_HIGH>;
14 + tiny4412,int_gpio3 = <&gpx3 4 GPIO_ACTIVE_HIGH>;
15 + tiny4412,int_gpio4 = <&gpx3 5 GPIO_ACTIVE_HIGH>;
16 + };
17 };


 上面的代碼中,我們添加了四個屬性,對應的就是那四個key對應的gpio,參考gpx3的實現(exynos4x12-pinctrl.dtsi):



gpx3: gpx3 {
gpio-controller;
#gpio-cells = <2>;

interrupt-controller;
#interrupt-cells = <2>;
};


 可以看到,gpx3含有gpio-controller和interrupt-controller屬性,表示它是一個gpio控制器和中斷控制器,它的gpio-cell爲2,意味着應該給這個gpx3傳遞兩個參數,以

tiny4412,int_gpio1 = <&gpx3 2 GPIO_ACTIVE_HIGH>

爲例,這兩個參數就是2和GPIO_ACTIVE_HIGH,如果參數不對的話,比如只傳了一個參數2,那麼在調用of_get_named_gpio會出現如下錯誤:



[   31.133799] /interrupt_demo: arguments longer than property
[ 31.133935] interrupt_demo interrupt_demo: Looking up tiny4412,int_gpio4 property in node /interrupt_demo failed -22
[ 31.144562] interrupt_demo: probe of interrupt_demo failed with error -22


 使用 make dtbs 編譯完設備樹。

在Samsung的pinctrl驅動中加一些log:



@@ -622,6 +626,8 @@ static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
unsigned int virq;

+ printk("%s enter.\n", __func__);
+
if (!bank->irq_domain)
return -ENXIO;


 然後我們編寫對應的驅動程序:



1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/platform_device.h>
4 #include <linux/gpio.h>
5 #include <linux/of.h>
6 #include <linux/of_gpio.h>
7 #include <linux/interrupt.h>
8
9 typedef struct
10 {
11 int gpio;
12 int irq;
13 char name[20];
14 }int_demo_data_t;
15
16 static irqreturn_t int_demo_isr(int irq, void *dev_id)
17 {
18 int_demo_data_t *data = dev_id;
19
20 printk("%s enter, %s: gpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq);
21
22 return IRQ_HANDLED;
23 }
24
25 static int int_demo_probe(struct platform_device *pdev) {
26 struct device *dev = &pdev->dev;
27 int irq_gpio = -1;
28 int irq = -1;
29 int ret = 0;
30 int i = 0;
31 int_demo_data_t *data = NULL;
32
33 printk("%s enter.\n", __func__);
34
35 if (!dev->of_node) {
36 dev_err(dev, "no platform data.\n");
37 goto err1;
38 }
39
40 data = devm_kmalloc(dev, sizeof(*data)*4, GFP_KERNEL);
41 if (!data) {
42 dev_err(dev, "no memory.\n");
43 goto err0;
44 }
45
46 #if 0
47 for (i = 3; i >= 0; i--) {
48 sprintf(data[i].name, "tiny4412,int_gpio%d", i+1);
49 #else
50 for (i = 0; i < 4; i++) {
51 #endif
52 irq_gpio = of_get_named_gpio(dev->of_node,
53 data[i].name, 0);
54 if (irq_gpio < 0) {
55 dev_err(dev, "Looking up %s property in node %s failed %d\n",
56 data[i].name, dev->of_node->full_name, irq_gpio);
57 goto err1;
58 }
59
60 data[i].gpio = irq_gpio;
61
62 irq = gpio_to_irq(irq_gpio);
63 if (irq < 0) {
64 dev_err(dev,
65 "Unable to get irq number for GPIO %d, error %d\n",
66 irq_gpio, irq);
67 goto err1;
68 }
69
70 data[i].irq = irq;
71
72 printk("%s: gpio: %d ---> irq (%d)\n", __func__, irq_gpio, irq);
73
74 ret = devm_request_any_context_irq(dev, irq,
75 int_demo_isr, IRQF_TRIGGER_FALLING, data[i].name, data+i);
76 if (ret < 0) {
77 dev_err(dev, "Unable to claim irq %d; error %d\n",
78 irq, ret);
79 goto err1;
80 }
81 }
82
83 return 0;
84
85 err1:
86 devm_kfree(dev, data);
87 err0:
88 return -EINVAL;
89 }
90
91 static int int_demo_remove(struct platform_device *pdev) {
92
93 printk("%s enter.\n", __func__);
94
95 return 0;
96 }
97
98 static const struct of_device_id int_demo_dt_ids[] = {
99 { .compatible = "tiny4412,interrupt_demo", },
100 {},
101 };
102
103 MODULE_DEVICE_TABLE(of, int_demo_dt_ids);
104
105 static struct platform_driver int_demo_driver = {
106 .driver = {
107 .name = "interrupt_demo",
108 .of_match_table = of_match_ptr(int_demo_dt_ids),
109 },
110 .probe = int_demo_probe,
111 .remove = int_demo_remove,
112 };
113
114 static int __init int_demo_init(void)
115 {
116 int ret;
117
118 ret = platform_driver_register(&int_demo_driver);
119 if (ret)
120 printk(KERN_ERR "int demo: probe failed: %d\n", ret);
121
122 return ret;
123 }
124 module_init(int_demo_init);
125
126 static void __exit int_demo_exit(void)
127 {
128 platform_driver_unregister(&int_demo_driver);
129 }
130 module_exit(int_demo_exit);
131
132 MODULE_LICENSE("GPL");


 編譯驅動後,將ko文件拷貝到nfs目錄下,然後在開發板上執行 mount -t nfs -o nolock 192.168.2.6:/nfsroot /mnt 將共享目錄從PC上掛載到開發板上,然後insmod這個驅動:



[root@tiny4412 ]# mount -t nfs -o nolock 192.168.2.6:/nfsroot /mnt
[root@tiny4412 ]# cd /mnt
[root@tiny4412 mnt]# ls
fdt interrupt_demo.ko tiny4412.dts
[root@tiny4412 mnt]# insmod interrupt_demo.ko
[ 1655.872546] int_demo_probe enter.
[ 1655.872841] samsung_gpio_to_irq enter.
[ 1655.873061] int_demo_probe: gpio: 238 ---> irq (105)
[ 1655.873716] samsung_gpio_to_irq enter.
[ 1655.873906] int_demo_probe: gpio: 239 ---> irq (106)
[ 1655.874424] samsung_gpio_to_irq enter.
[ 1655.874773] int_demo_probe: gpio: 240 ---> irq (107)
[ 1655.879981] samsung_gpio_to_irq enter.
[ 1655.883485] int_demo_probe: gpio: 241 ---> irq (108)
[root@tiny4412 mnt]#
// 然後我們嘗試按底板上的按鍵,會看到相應的中斷log
[root@tiny4412 mnt]# [ 33.462207] int_demo_isr enter, tiny4412,int_gpio1: gpio:238, irq: 105
[ 33.657304] int_demo_isr enter, tiny4412,int_gpio1: gpio:238, irq: 105
[ 35.769955] int_demo_isr enter, tiny4412,int_gpio3: gpio:240, irq: 107
[ 35.951373] int_demo_isr enter, tiny4412,int_gpio3: gpio:240, irq: 107
[ 36.525804] int_demo_isr enter, tiny4412,int_gpio4: gpio:241, irq: 108
[ 36.698501] int_demo_isr enter, tiny4412,int_gpio4: gpio:241, irq: 108
[ 41.710481] int_demo_isr enter, tiny4412,int_gpio2: gpio:239, irq: 106
[ 41.857190] int_demo_isr enter, tiny4412,int_gpio2: gpio:239, irq: 106

[root@tiny4412 mnt]# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
36: 0 0 0 0 GIC 89 Edge mct_comp_irq
37: 1054 566 579 339 GIC 28 Edge MCT
44: 34 0 0 0 GIC 107 Edge mmc0
45: 1 0 0 0 GIC 103 Edge 12480000.hsotg, 12480000.hsotg, dwc2_hsotg:usb1
46: 508 0 0 0 GIC 102 Edge ehci_hcd:usb2, ohci_hcd:usb3
47: 197 0 0 0 GIC 84 Edge 13800000.serial
51: 52 0 0 0 GIC 93 Edge 13890000.i2c
57: 0 0 0 0 GIC 67 Edge 12680000.pdma
58: 0 0 0 0 GIC 68 Edge 12690000.pdma
59: 0 0 0 0 GIC 66 Edge 12850000.mdma
71: 0 0 0 0 GIC 79 Edge 11400000.pinctrl
72: 1 0 0 0 GIC 78 Edge 11000000.pinctrl
90: 0 0 0 0 COMBINER 80 Edge 3860000.pinctrl
91: 0 0 0 0 GIC 104 Edge 106e0000.pinctrl
95: 48 0 0 0 GIC 109 Edge dw-mci
103: 2 0 0 0 exynos4210_wkup_irq_chip 1 Edge mma7660
104: 1 0 0 0 exynos_gpio_irq_chip 2 Edge 12530000.sdhci cd
105: 2 0 0 0 exynos4210_wkup_irq_chip 2 Edge tiny4412,int_gpio1
106: 2 0 0 0 exynos4210_wkup_irq_chip 3 Edge tiny4412,int_gpio2
107: 2 0 0 0 exynos4210_wkup_irq_chip 4 Edge tiny4412,int_gpio3
108: 2 0 0 0 exynos4210_wkup_irq_chip 5 Edge tiny4412,int_gpio4
IPI0: 0 1 1 1 CPU wakeup interrupts
IPI1: 0 0 0 0 Timer broadcast interrupts
IPI2: 852 1809 422 537 Rescheduling interrupts
IPI3: 0 2 2 2 Function call interrupts
IPI4: 0 1 2 1 Single function call interrupts
IPI5: 0 0 0 0 CPU stop interrupts
IPI6: 0 0 0 0 IRQ work interrupts
IPI7: 0 0 0 0 completion interrupts
Err: 0


 如果我們修改驅動驅動,改變一下中斷的申請順序,然後重啓系統(測試發現,如果不重啓系統,而是重新加載新的驅動,gpio跟irq的對應關系沒有發生改變)



#if 1
for (i = 3; i >= 0; i--) {
sprintf(data[i].name, "tiny4412,int_gpio%d", i+1);
#else
for (i = 0; i < 4; i++) {
#endif


 此時,會看到如下的log:



[root@tiny4412 mnt]# insmod interrupt_demo.ko 
[ 119.735726] int_demo_probe enter.
[ 119.736295] samsung_gpio_to_irq enter.
[ 119.736417] int_demo_probe: gpio: 241 ---> irq (105)
[ 119.736852] samsung_gpio_to_irq enter.
[ 119.736985] int_demo_probe: gpio: 240 ---> irq (106)
[ 119.737254] samsung_gpio_to_irq enter.
[ 119.737893] int_demo_probe: gpio: 239 ---> irq (107)
[ 119.743029] samsung_gpio_to_irq enter.
[ 119.746631] int_demo_probe: gpio: 238 ---> irq (108)
[root@tiny4412 mnt]# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
36: 0 0 0 0 GIC 89 Edge mct_comp_irq
37: 1799 966 883 145 GIC 28 Edge MCT
... ...
105: 0 0 0 0 exynos4210_wkup_irq_chip 5 Edge tiny4412,int_gpio4
106: 0 0 0 0 exynos4210_wkup_irq_chip 4 Edge tiny4412,int_gpio3
107: 0 0 0 0 exynos4210_wkup_irq_chip 3 Edge tiny4412,int_gpio2
108: 0 0 0 0 exynos4210_wkup_irq_chip 2 Edge tiny4412,int_gpio1
... ...


 

未完待續... ...