首页 > 代码库 > 基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(2)

基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(2)

作者:彭东林

邮箱:pengdonglin137@163.com

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_chip10         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那个节点的node17         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  // 分配一个唯一的id18     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 这个bank27     gdev->data =http://www.mamicode.com/ 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)  // 第二个参数表示的是flag6         *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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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 = http://www.mamicode.com/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

    ---> 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‘来说就是gpx312     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_chip21     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_desc25     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 @ef205d006 [  152.085590] __irq_alloc_descs: alloc virq: 100, cnt: 17 [  152.090160] irq: irq 2 on domain /pinctrl@11000000/gpx3 mapped to virtual irq 1008 [  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: 1002 [  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.pdma10  53:          0          0          0          0     GIC-0  68 Edge      12690000.pdma11  54:          0          0          0          0     GIC-0  66 Edge      12850000.mdma12  67:          0          0          0          0     GIC-0 144 Edge      10830000.sss13  68:          0          0          0          0     GIC-0  79 Edge      11400000.pinctrl14  69:          0          0          0          0     GIC-0  78 Edge      11000000.pinctrl15  87:          0          0          0          0  COMBINER  80 Edge      3860000.pinctrl16  88:          0          0          0          0     GIC-0 104 Edge      106e0000.pinctrl17 100:          2          0          0          0  exynos4210_wkup_irq_chip   2 Edge      int-gpio18 IPI0:          0          1          1          1  CPU wakeup interrupts19 IPI1:          0          0          0          0  Timer broadcast interrupts20 IPI2:        896        894        374        149  Rescheduling interrupts21 IPI3:          0          2          3          2  Function call interrupts22 IPI4:          0          0          0          0  CPU stop interrupts23 IPI5:        212         45         91          8  IRQ work interrupts24 IPI6:          0          0          0          0  completion interrupts25 Err:          0

完。

基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(2)