平台
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 = ;
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 = ;
36 };
37 };
上面的节点interrupt_xeint26中引用了gpx3_2,而且在驱动中打算将这个gpio当作中断引脚来使用。
下面是对应的驱动程序:
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 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: %dn", __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 %dn", 43 data[i].name, dev->of_node->full_name, irq_gpio); 44 goto err1; 45 }