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

作者:彭东林

邮箱:[email protected]

QQ:405728433

平台

tiny4412 ADK

Linux-4.9

概述

前面一篇博文基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)结合示例分析了一下新版kernel引入设备树和irq domain后中断幕后的一些知识,其中的示例只是使用gpio中断的一种方式,此外,还有一种,就像博文

基於tiny4412的Linux內核移植--- 中斷和GPIO學習(1)中描述的那样,这种实现方式又是如何进行的呢?下面还是结合示例的方式分析。

正文

框图可以参考前一篇博文。

在前一篇博文的第三部分 GPIO控制器驱动中有一个函数我们没有分析,就是samsung_gpiolib_register,把这函数看懂了,后面的分析就顺了,下面的分析最好结合前一篇博文的第三部分 GPIO控制器驱动一块看。

这里还是以[email protected]这个节点为例分析。

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) {  // 遍历[email protected]下的所有bank,我们关心的是gpx3这个bank
 9         bank->gpio_chip = samsung_gpiolib_chip;   // gpio_chip
10         gc = &bank->gpio_chip;
11  // 这个bank的gpio在系统中的逻辑起始号, 其中drvdata->pin_base是[email protected]的在系统中的逻辑gpio起始号,
12  // 而bank->pin_base是这个bank在[email protected]中的逻辑起始号(从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 = "/[email protected]";
 9     };
10     gic: [email protected]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     [email protected]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

---> 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 [[email protected] 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 /[email protected]11000000/gpx3 mapped to virtual irq 100
8 [  152.097376] xeint26_probe: gpio: 238 ---> irq (100)

可以看到在加载驱动的时候才创建了hwirq和virq之间的映射,分配到的virq是100.此时可以去按下tiny4412上面的key1,可以看到中断处理函数中打印出来的log

1 [[email protected] 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 [[email protected] 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

完。

时间: 2024-10-17 05:48:28

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

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

作者:彭东林 邮箱:[email protected] QQ:405728433 平台 tiny4412 ADK Linux-4.9 概述 前面几篇博文列举了在有设备树的时候,gpio中断的用法示例.下面我们尝试分析一下Linux内核是如何做到的,如果哪写的有问题,欢迎大家批评指正,谢谢. 还是以GPIO中断为例分析,对于tiny4412,gpio中断可以分为两种,外部中断和普通的GPIO中断 外部中断:按键中断分别使用了外部中断XEINT26.XEINT27.XEINT28以及XEINT29,

基於tiny4412的Linux內核移植--- 中斷和GPIO學習(2)

作者 彭東林 [email protected] 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uImage做了稍許改動 概述 這篇博客以一個簡單的led燈實驗演示一下在含有設備樹.pinctrl時的gpio控制方法. 正文 未完待續... ...

第一章 Linux內核簡介

1. Linux是類Unix系統,但他不是Unix. 儘管Linux借鑑了Unix的許多設計並且實現了Unix的API(由Posix標準和其他Single Unix Specification定義的),但Linux沒有像其他Unix變種那樣直接使用Unix的源代碼. 2. Linux系統的基礎是內核.C庫.工具集和系統的基本工具,如登錄程序和Shell. 3. 操作系統是指在整個系統中負責完成最基本功能和系統管理的那些部分.這些部分應該包括內核.設備驅動程序.啓動引導程序.命令行Shell或者其

《交互式计算机图形学 基於OpenGL着色器的自顶向下方法(第6版) 》代码运行问题及其解决

上一篇文章,笔者提到了红宝书第8版的代码编译运行问题,想必大家已经知道我看过红宝书了. 然而,笔者竟然看不懂,这是因为笔者并不知道OpenGL的历史.原来有古代OpenGL和现代OpenGL之分,古代OpenGL使用固定功能管线,现在已经被废弃(尽管还能用),而现代使用可编程管线. 由于笔者在网上搜索一通,见到怎么OpenGL的代码有两种风格,到了现在才搞懂.原来OpenGL在第8版之前都是讲的固定功能管线,到了第8版立即转成了可编程管线.然而红宝书已经认定了你有一定的计算机图形学基础,因此介绍

Coinbase 推出基於以太坊的聊天 APP「Token」

Coinbase CEO Brian Armstrong 透过他的推特(Twitter)宣布推出一个名为「Token」的新产品,这个产品是一个手机聊天 APP,应用底层是利用以太坊来建设,使用者除了可以在上面发讯息,还可以传送 ETH 币给別人. Token已经正式推出,iOS 和 Android 用戶都可以下载使用.技术上,Token利用了 WhisperSystem 的 Signal Protocol 来加密用用户间的信息,这个技术连著名前美国 CIA 技术员爱德华·史诺登(Edward S

Linux内核中的中断栈与内核栈的补充说明【转】

转自:http://blog.chinaunix.net/uid-12461657-id-3487463.html 原文地址:Linux内核中的中断栈与内核栈的补充说明 作者:MagicBoy2010 中断栈与内核栈的话题更多地属于内核的范畴,所以在<深入Linux设备驱动程序内核机制>第5章“中断处理”当中,基本上没怎么涉及到上述内容,只是在5.4节有些许的文字讨论中断栈在中断嵌套情形下可能的溢出问题. 本贴在这个基础上对内核栈与中断栈的话题做些补充,讨论基于x86 32位系统,因为64位系

linux内核学习:中断

编程相关 注册中断 int request_irq( unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) typedef irqreturn_t (*irq_handler_t)(int, void *); IRQF_DISABLED 会禁用除本本身以外的其它中断,一般是不用的 IRQF_SAMPLE_RANDOM 可以帮助内核随机数的产生.如果中断产生地毫无规律,可

linux内核学习:中断中推后执行的部分

软中断-softirq 特点 相同和不同的软中断都可以在不同处理器上同时执行 一个软中断不会抢占另一个软中断 何时执行 从中断程序返回时 ksoftirqd线程中 显示调用 软中断最多有32个,一个32位的整型数据可以被用来标记刮起的软中断 使用策略 软中断应用于确实需要的场合,目前只有网络驱动和SCSI驱动中使用.另外,内核定时器和tasklet建立在软中断之上. 使用方法 注册软中断 void open_softirq(int nr, void (*action)(struct softir

Linux Centos的Inode及Block相关知识

本经验均在CentOSrelease6.7(Final)下操作,如知识有欠缺之处 欢迎批评指正. linux 的inode及block的相关知识: 1>  Linux系统分区格式化文件系统之后,系统会分为Inode及Block两部分: 1)Inode为系统文件的属性信息(ls -l的结果)及指向文件实体的指针,但是没有存放文件名,一般在上级目录里的Block. 2)Block为存放数据的,ext3/ext4一般为1k,2k,4k,一般默认4k 3)一个文件不论多大至少占用一个Inode和一个Bl