作者:彭東林
QQ:405728433
平台
tiny4412 ADK
Linux-4.9
概述
前面一篇博文基於tiny4412的Linux內核移植 --- 執行個體學習中斷背後的知識(1)結合示例分析了一下新版kernel引入裝置樹和irq domain後中斷幕後的一些知識,其中的示例隻是使用gpio中斷的一種方式,此外,還有一種,就像博文
基於tiny4412的Linux內核移植--- 中斷和GPIO學習(1)中描述的那樣,這種實作方式又是如何進行的呢?下面還是結合示例的方式分析。
正文
框圖可以參考前一篇博文。
在前一篇博文的第三部分 GPIO控制器驅動中有一個函數我們沒有分析,就是samsung_gpiolib_register,把這函數看懂了,後面的分析就順了,下面的分析最好結合前一篇博文的第三部分 GPIO控制器驅動一塊看。
這裡還是以pinctrl@11000000這個節點為例分析。
samsung_gpiolib_register
1 static int samsung_gpiolib_register(struct platform_device *pdev,
2 struct samsung_pinctrl_drv_data *drvdata)
3 {
4 struct samsung_pin_bank *bank = drvdata->pin_banks;
5 struct gpio_chip *gc;
6 int ret;
7 int i;
8 for (i = 0; i < drvdata->nr_banks; ++i, ++bank) { // 周遊pinctrl@11000000下的所有bank,我們關心的是gpx3這個bank
9 bank->gpio_chip = samsung_gpiolib_chip; // gpio_chip
10 gc = &bank->gpio_chip;
11 // 這個bank的gpio在系統中的邏輯起始号, 其中drvdata->pin_base是pinctrl@11000000的在系統中的邏輯gpio起始号,
12 // 而bank->pin_base是這個bank在pinctrl@11000000中的邏輯起始号(從0開始)
13 gc->base = drvdata->pin_base + bank->pin_base;
14 gc->ngpio = bank->nr_pins; // 這個bank中含有的gpio的個數
15 gc->parent = &pdev->dev;
16 gc->of_node = bank->of_node; //對于gpx3來說,就是gpx3那個節點的node
17 gc->label = bank->name;
18 ret = gpiochip_add_data(gc, bank);
19 ...
20 }
21 return 0;
22 ...
23 }
---> gpiochip_add_data(struct gpio_chip *chip, void *data)
1 int gpiochip_add_data(struct gpio_chip *chip, void *data)
2 {
3 unsigned long flags;
4 int status = 0;
5 unsigned i;
6 int base = chip->base;
7 struct gpio_device *gdev;
8 // 每一個bank都都應一個唯一的gpio_device和gpio_chip
9 gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
10 gdev->dev.bus = &gpio_bus_type;
11 gdev->chip = chip;
12 chip->gpiodev = gdev;
13 ... ...
14 if (chip->of_node)
15 gdev->dev.of_node = chip->of_node;
16
17 // 配置設定一個唯一的id
18 gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
19 dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
20 ... ...
21 // 為這個chip下的每一個gpio都要配置設定一個gpio_desc結構體
22 gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
23 ... ...
24 // 這個chip中含有的gpio的個數
25 gdev->ngpio = chip->ngpio;
26 // gpx3 這個bank
27 gdev->data = data;
28 ... ...
29 // base表示的是這個bank在系統中的邏輯gpio号
30 gdev->base = base;
31 // 将這個bank對應的gpio_device添加到全局連結清單gpio_devices中
32 // 在添加的時候會根據gdev->base和ngpio在gpio_devices連結清單中找到合适的位置
33 status = gpiodev_add_to_list(gdev);
34 ... ...
35 for (i = 0; i < chip->ngpio; i++) {
36 struct gpio_desc *desc = &gdev->descs[i];
37 desc->gdev = gdev;
38 ... ...
39 }
40 ... ...
41 // 預設這個chip下的所有gpio都是可以産生中斷
42 status = gpiochip_irqchip_init_valid_mask(chip);
43 status = of_gpiochip_add(chip);
44 ... ...
45 return 0;
46 ... ...
47 }
---> of_gpiochip_add(struct gpio_chip *chip)
1 int of_gpiochip_add(struct gpio_chip *chip)
2 {
3 int status;
4 ... ...
5 if (!chip->of_xlate) {
6 chip->of_gpio_n_cells = 2;
7 chip->of_xlate = of_gpio_simple_xlate;
8 }
9 ... ...
10 }
這裡需要看一下of_gpio_simple_xlate的實作,這個在下面的分析中會被回調
1 int of_gpio_simple_xlate(struct gpio_chip *gc,
2 const struct of_phandle_args *gpiospec, u32 *flags)
3 {
4 .. ...
5 if (flags) // 第二個參數表示的是flag
6 *flags = gpiospec->args[1];
7 // 第一個參數表示的是gpio号
8 return gpiospec->args[0];
9 }
從上面的分析中我們知道了如下幾點:
1. 每一個bank(如gpx3)都對應一個gpio_chip和gpio_device
2. 這個bank下的每一個gpio都會對應一個唯一的gpio_desc結構體,這些結構提的首位址存放在gpio_device的desc中
3. 上面的gpio_device會加入到全局gpio_devices連結清單中
4. gpio_chip的of_gpio_n_cells被指派為2,表示引用一個gpio資源需要兩個參數,負責解析這兩個參數函數以的of_xlate函數為of_gpio_simple_xlate,其中第一個參數表示gpio号(在對應的bank中),第二個表示flag
這裡還是先把裝置樹中涉及到的節點列在這裡:
1 / {
2 interrupt-parent = <&gic>;
3 #address-cells = <0x1>;
4 #size-cells = <0x1>;
5 compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
6 model = "FriendlyARM TINY4412 board based on Exynos4412";
7 aliases {
8 pinctrl1 = "/pinctrl@11000000";
9 };
10 gic: interrupt-controller@10490000 {
11 compatible = "arm,cortex-a9-gic";
12 #interrupt-cells = <0x3>;
13 interrupt-controller;
14 reg = <0x10490000 0x10000>, <0x10480000 0x10000>;
15 cpu-offset = <0x4000>;
16 };
17 pinctrl@11000000 {
18 compatible = "samsung,exynos4x12-pinctrl";
19 reg = <0x11000000 0x1000>;
20 interrupts = <0x0 0x2e 0x0>;
21 gpx3: gpx3 {
22 gpio-controller;
23 #gpio-cells = <0x2>;
24 interrupt-controller;
25 #interrupt-cells = <0x2>;
26 };
27 wakeup-interrupt-controller {
28 compatible = "samsung,exynos4210-wakeup-eint";
29 interrupt-parent = <0x1>;
30 interrupts = <0x0 0x20 0x0>;
31 };
32 };
33 interrupt_xeint26: interrupt_xeint26 {
34 compatible = "tiny4412,interrupt_xeint26";
35 int-gpio = <&gpx3 2 GPIO_ACTIVE_HIGH>;
36 };
37 };
上面的節點interrupt_xeint26中引用了gpx3_2,而且在驅動中打算将這個gpio當作中斷引腳來使用。
下面是對應的驅動程式:
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 typedef struct
9 {
10 int gpio;
11 int irq;
12 char name[20];
13 }xeint26_data_t;
14 static irqreturn_t xeint26_isr(int irq, void *dev_id)
15 {
16 xeint26_data_t *data = dev_id;
17 printk("%s enter, %s: gpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq);
18 return IRQ_HANDLED;
19 }
20 static int xeint26_probe(struct platform_device *pdev) {
21 struct device *dev = &pdev->dev;
22 int irq_gpio = -1;
23 int irq = -1;
24 int ret = 0;
25 int i = 0;
26 xeint26_data_t *data = NULL;
27 printk("%s enter.\n", __func__);
28 if (!dev->of_node) {
29 dev_err(dev, "no platform data.\n");
30 goto err1;
31 }
32 data = devm_kmalloc(dev, sizeof(*data)*1, GFP_KERNEL);
33 if (!data) {
34 dev_err(dev, "no memory.\n");
35 goto err0;
36 }
37 for (i = 0; i < 1; i++) {
38 sprintf(data[i].name, "int-gpio");
39 irq_gpio = of_get_named_gpio(dev->of_node,
40 data[i].name, 0);
41 if (irq_gpio < 0) {
42 dev_err(dev, "Looking up %s property in node %s failed %d\n",
43 data[i].name, dev->of_node->full_name, irq_gpio);
44 goto err1;
45 }
46 data[i].gpio = irq_gpio;
47 irq = gpio_to_irq(irq_gpio);
48 if (irq < 0) {
49 dev_err(dev,
50 "Unable to get irq number for GPIO %d, error %d\n",
51 irq_gpio, irq);
52 goto err1;
53 }
54 data[i].irq = irq;
55 printk("%s: gpio: %d ---> irq (%d)\n", __func__, irq_gpio, irq);
56 ret = devm_request_any_context_irq(dev, irq,
57 xeint26_isr, IRQF_TRIGGER_FALLING, data[i].name, data+i);
58 if (ret < 0) {
59 dev_err(dev, "Unable to claim irq %d; error %d\n",
60 irq, ret);
61 goto err1;
62 }
63 }
64 return 0;
65 err1:
66 devm_kfree(dev, data);
67 err0:
68 return -EINVAL;
69 }
70 static int xeint26_remove(struct platform_device *pdev) {
71 printk("%s enter.\n", __func__);
72 return 0;
73 }
74 static const struct of_device_id xeint26_dt_ids[] = {
75 { .compatible = "tiny4412,interrupt_xeint26", },
76 {},
77 };
78 MODULE_DEVICE_TABLE(of, xeint26_dt_ids);
79 static struct platform_driver xeint26_driver = {
80 .driver = {
81 .name = "interrupt_xeint26",
82 .of_match_table = of_match_ptr(xeint26_dt_ids),
83 },
84 .probe = xeint26_probe,
85 .remove = xeint26_remove,
86 };
87 static int __init xeint26_init(void)
88 {
89 int ret;
90 ret = platform_driver_register(&xeint26_driver);
91 if (ret)
92 printk(KERN_ERR "xeint26: probe failed: %d\n", ret);
93 return ret;
94 }
95 module_init(xeint26_init);
96 static void __exit xeint26_exit(void)
97 {
98 platform_driver_unregister(&xeint26_driver);
99 }
100 module_exit(xeint26_exit);
101 MODULE_LICENSE("GPL");
其中我們隻需要分析兩個關鍵的函數:of_get_named_gpio 和 gpio_to_irq.
of_get_named_gpio
這個函數的作用是根據傳遞的屬性的name和索引号,得到一個gpio号
---> of_get_named_gpio_flags(np, propname, index, NULL)
1 int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
2 int index, enum of_gpio_flags *flags)
3 {
4 struct gpio_desc *desc;
5 desc = of_get_named_gpiod_flags(np, list_name, index, flags);
6 ... ...
7 return desc_to_gpio(desc);
8 }
---> of_get_named_gpiod_flags
1 struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
2 const char *propname, int index, enum of_gpio_flags *flags)
3 {
4 struct of_phandle_args gpiospec;
5 struct gpio_chip *chip;
6 struct gpio_desc *desc;
7 int ret;
8 // 解析"int-gpio"屬性中第index字段,将解析結果存放到gpiospec中
9 /*
10 struct of_phandle_args {
11 struct device_node *np; // int-gpio屬性所引用的gpio-controller的node,對于'int-gpio'來說就是gpx3
12 int args_count; // gpx3這個gpio-controller的#gpio-cells屬性的值
13 uint32_t args[MAX_PHANDLE_ARGS]; // 具體描述這個gpio屬性的每一個參數
14 };
15 */
16 ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
17 &gpiospec);
18
19 // 上面gpiospec的np存放的索引用的gpio-controller的node,
20 // 周遊gpio_devices連結清單,找到對應的gpio_device,也就找到了gpio_chip
21 chip = of_find_gpiochip_by_xlate(&gpiospec);
22 // 調用chip->of_xlate解析gpiospec,傳回gpiospec的args中的第一個參數args[0],
23 // 也就是前面分析的在bank中的邏輯gpio号
24 // 知道了gpio号,就可以在gpio_device->desc中索引到對應的gpio_desc
25 desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
26 return desc;
27 }
---> desc_to_gpio
1 int desc_to_gpio(const struct gpio_desc *desc)
2 {
3 // 獲得這個gpio_desc對應的gpio在系統中的邏輯gpio号
4 return desc->gdev->base + (desc - &desc->gdev->descs[0]);
5 }
gpio_to_irq
将這個gpio轉換成對應的virq
gpio_to_irq(irq_gpio)
---> __gpio_to_irq(gpio)
---> gpiod_to_irq(gpio_to_desc(gpio))
這裡調用了兩個函數,函數gpio_to_desc根據傳入的全局邏輯gpio号找到對應的gpio_desc,原理是:周遊gpio_devices連結清單,根據傳入的邏輯gpio号,就可以定位到所屬的gpio_device,前面說過,在将gpio_device加入到gpio_devices連結清單的時候,不是亂加的,而是根據gpio_device的base和ngpio找到一個合适的位置。找到了gpio_device,那麼通過索引它的desc成員,就可以找到對應的gpio_desc
gpio_to_desc
1 struct gpio_desc *gpio_to_desc(unsigned gpio) // 傳入的是全局邏輯gpio号
2 {
3 struct gpio_device *gdev;
4 unsigned long flags;
5 list_for_each_entry(gdev, &gpio_devices, list) {
6 if (gdev->base <= gpio &&
7 gdev->base + gdev->ngpio > gpio) {
8 return &gdev->descs[gpio - gdev->base]; // 獲得gpio_desc
9 }
10 }
11 ... ...
12 }
gpiod_to_irq
1 int gpiod_to_irq(const struct gpio_desc *desc)
2 {
3 struct gpio_chip *chip;
4 int offset;
5 ... ...
6 chip = desc->gdev->chip;
7 // 這個函數通過desc - &desc->gdev->descs[0]就可以計算出,對于gpx3_2,就是2
8 // 這個gpio_desc在所屬的bank中的邏輯gpio号
9 offset = gpio_chip_hwgpio(desc);
10 int retirq = chip->to_irq(chip, offset);
11 ... ...
12 return retirq;
13 ... ...
14 }
上面的第11行就是前面samsung_gpiolib_register中設定的samsung_gpiolib_chip,其to_irq定義如下
1 static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
2 {
3 struct samsung_pin_bank *bank = gpiochip_get_data(gc);
4 unsigned int virq;
5 .. ..
6 virq = irq_create_mapping(bank->irq_domain, offset);
7 return (virq) ? : -ENXIO;
8 }
需要注意的是offset,比如對于gpx3_2,那麼offset就是2, 結合前一篇的博文,這裡的offset就是hwirq,調用irq_create_mapping可以為該hwirq在kernel中配置設定一個唯一的virq,同時将hwirq和virq的映射關系存放到bank->irq_domain中。
實驗
加載驅動
1 [root@tiny4412 mnt]# insmod xeint26.ko
2 [ 152.084809] xeint26_probe enter.
3 [ 152.085104] of_get_named_gpiod_flags: parsed 'int-gpio' property of node '/interrupt_xeint26[0]' - status (0)
4 [ 152.085286] irq: irq_create_mapping(0xef205d00, 0x2)
5 [ 152.085423] irq: -> using domain @ef205d00
6 [ 152.085590] __irq_alloc_descs: alloc virq: 100, cnt: 1
7 [ 152.090160] irq: irq 2 on domain /pinctrl@11000000/gpx3 mapped to virtual irq 100
8 [ 152.097376] xeint26_probe: gpio: 238 ---> irq (100)
可以看到在加載驅動的時候才建立了hwirq和virq之間的映射,配置設定到的virq是100.此時可以去按下tiny4412上面的key1,可以看到中斷處理函數中列印出來的log
1 [root@tiny4412 mnt]# [ 170.718118] xeint26_isr enter, int-gpio: gpio:238, irq: 100
2 [ 170.910928] xeint26_isr enter, int-gpio: gpio:238, irq: 100
可以看看目前的中斷觸發情況:
1 [root@tiny4412 mnt]# cat /proc/interrupts
2 CPU0 CPU1 CPU2 CPU3
3 36: 0 0 0 0 GIC-0 89 Edge mct_comp_irq
4 37: 4702 2840 1176 787 GIC-0 28 Edge MCT
5 44: 34 0 0 0 GIC-0 107 Edge mmc0
6 45: 1 0 0 0 GIC-0 103 Edge 12480000.hsotg, 12480000.hsotg, dwc2_hsotg:usb1
7 46: 881 0 0 0 GIC-0 102 Edge ehci_hcd:usb2, ohci_hcd:usb3
8 48: 341 0 0 0 GIC-0 84 Edge 13800000.serial
9 52: 4 0 0 0 GIC-0 67 Edge 12680000.pdma
10 53: 0 0 0 0 GIC-0 68 Edge 12690000.pdma
11 54: 0 0 0 0 GIC-0 66 Edge 12850000.mdma
12 67: 0 0 0 0 GIC-0 144 Edge 10830000.sss
13 68: 0 0 0 0 GIC-0 79 Edge 11400000.pinctrl
14 69: 0 0 0 0 GIC-0 78 Edge 11000000.pinctrl
15 87: 0 0 0 0 COMBINER 80 Edge 3860000.pinctrl
16 88: 0 0 0 0 GIC-0 104 Edge 106e0000.pinctrl
17 100: 2 0 0 0 exynos4210_wkup_irq_chip 2 Edge int-gpio
18 IPI0: 0 1 1 1 CPU wakeup interrupts
19 IPI1: 0 0 0 0 Timer broadcast interrupts
20 IPI2: 896 894 374 149 Rescheduling interrupts
21 IPI3: 0 2 3 2 Function call interrupts
22 IPI4: 0 0 0 0 CPU stop interrupts
23 IPI5: 212 45 91 8 IRQ work interrupts
24 IPI6: 0 0 0 0 completion interrupts
25 Err: 0
完。