zoukankan      html  css  js  c++  java
  • 基於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_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
    ", __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.
    ", __func__);
     28     if (!dev->of_node) {
     29         dev_err(dev, "no platform data.
    ");
     30         goto err1;
     31     }
     32     data = devm_kmalloc(dev, sizeof(*data)*1, GFP_KERNEL);
     33     if (!data) {
     34         dev_err(dev, "no memory.
    ");
     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
    ",
     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
    ",
     51                 irq_gpio, irq);
     52             goto err1;
     53         }
     54         data[i].irq = irq;
     55         printk("%s: gpio: %d ---> irq (%d)
    ", __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
    ",
     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.
    ", __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
    ", 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'来说就是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

    完。

  • 相关阅读:
    tomcat 无法clean 的bug
    Open Type vs Open resource
    Cannot change version of project facet Dynamic Web Module to 2.4.
    股权稀释
    Java的各种打包方式
    记忆
    【转】给女儿的信
    买房费用
    工作职场
    装修-水电改造
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/6350308.html
Copyright © 2011-2022 走看看