韦东山:在Linux设备树(DTS)中指定中断_在代码中获得中断

2020-09-30 11:28:45 浏览数 (2)

作者:韦东山 全文分为三大部分

1.设备树里中断节点的语法 2.设备树里中断节点的示例 3.在代码中获得中断

参考:

内核Documentationdevicetreebindingsinterrupt-controllerinterrupts.txt

1.设备树里中断节点的语法

1.1 设备树里的中断控制器

中断的硬件框图如下:

在硬件上,“中断控制器”只有GIC这一个,但是我们在软件上也可以把上图中的“GPIO”称为“中断控制器”。很了芯片有多个GPIO模块,比如GPIO1、GPIO2等等。所以软件上的“中断控制器”就有很多个:GIC、GPIO1、GPIO2等等。

GPIO1连接到GIC,GPIO2连接到GIC,所以GPIO1的父亲是GIC,GPIO2的父亲是GIC。

假设GPIO1有32个中断源,但是它把其中的16个汇聚起来向GIC发出一个中断,把另外16个汇聚起来向GIC发出另一个中断。这就意味着GPIO1会用到GIC的两个中断,会涉及GIC里的2个hwirq。

这些层级关系、中断号(hwirq),都会在设备树中有所体现。

在设备树中,中断控制器节点中必须有一个属性:interrupt-controller,表明它是“中断控制器”。

还必须有一个属性:#interrupt-cells,表明引用这个中断控制器的话需要多少个cell。

#interrupt-cells的值一般有如下取值:

代码语言:javascript复制
① #interrupt-cells=<1>

别的节点要使用这个中断控制器时,只需要一个cell来表明使用“哪一个中断”。

代码语言:javascript复制
② #interrupt-cells=<2>

别的节点要使用这个中断控制器时,需要一个cell来表明使用“哪一个中断”;

还需要另一个cell来描述中断,一般是表明触发类型:

第2个cell的bits[3:0] 用来表示中断触发类型(trigger type and level flags):

1 = low-to-high edge triggered,上升沿触发

2 = high-to-low edge triggered,下降沿触发

4 = active high level-sensitive,高电平触发

8 = active low level-sensitive,低电平触发

示例如下:

代码语言:javascript复制
vic: intc@10140000 {
  compatible = "arm,versatile-vic";
  interrupt-controller;
  #interrupt-cells = <1>;
  reg = <0x10140000 0x1000>;
};

如果中断控制器有级联关系,下级的中断控制器还需要表明它的“interrupt-parent”是谁,用了interrupt-parent”中的哪一个“interrupts”,请看下一小节。

1.2 设备树里使用中断

一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的?

这3个问题,在设备树里使用中断时,都要有所体现。

代码语言:javascript复制
① interrupt-parent=<&XXXX>

你要用哪一个中断控制器里的中断?

代码语言:javascript复制
② interrupts

你要用哪一个中断?

Interrupts里要用几个cell,由interrupt-parent对应的中断控制器决定。在中断控制器里有“#interrupt-cells”属性,它指明了要用几个cell来描述中断。

比如:

代码语言:javascript复制
i2c@7000c000 {
  gpioext: gpio-adnp@41 {
    compatible = "ad,gpio-adnp";


    interrupt-parent = <&gpio>;
    interrupts = <160 1>;


    gpio-controller;
    #gpio-cells = <1>;


    interrupt-controller;
    #interrupt-cells = <2>;
  };
......
};

③ 新写法:interrupts-extended

一个“interrupts-extended”属性就可以既指定“interrupt-parent”,也指定“interrupts”,比如:

代码语言:javascript复制
interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;

2.设备树里中断节点的示例

以100ASK_IMX6ULL开发板为例,在arch/arm/boot/dts目录下可以看到2个文件:imx6ull.dtsi、100ask_imx6ull-14x14.dts,把里面有关中断的部分内容抽取出来。

从设备树反推IMX6ULL的中断体系,如下,比之前的框图多了一个“GPC INTC”:

GPC INTC的英文是:General Power Controller, Interrupt Controller。它提供中断屏蔽、中断状态查询功能,实际上这些功能在GIC里也实现了,个人觉得有点多余。除此之外,它还提供唤醒功能,这才是保留它的原因。

3.在代码中获得中断

之前我们提到过,设备树中的节点有些能被转换为内核里的platform_device,有些不能,回顾如下:

A. 根节点下含有compatile属性的子节点,会转换为platform_device

B. 含有特定compatile属性的节点的子节点,会转换为platform_device

如果一个节点的compatile属性,它的值是这4者之一:“simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”,

那么它的子结点(需含compatile属性)也可以转换为platform_device。

C. 总线I2C、SPI节点下的子节点:不转换为platform_device

某个总线下到子节点,应该交给对应的总线驱动程序来处理, 它们不应该被转换为platform_device。

3.1 对于platform_device

一个节点能被转换为platform_device,如果它的设备树里指定了中断属性,那么可以从platform_device中获得“中断资源”,函数如下,可以使用下列函数获得IORESOURCE_IRQ资源,即中断号:

代码语言:javascript复制
/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type   // 取哪类资源?IORESOURCE_MEM、IORESOURCE_REG
*                      // IORESOURCE_IRQ等
* @num: resource index  // 这类资源中的哪一个?
*/
struct resource *platform_get_resource(struct platform_device *dev,
              unsigned int type, unsigned int num);

3.2 对于I2C设备、SPI设备

对于I2C设备节点,I2C总线驱动在处理设备树里的I2C子节点时,也会处理其中的中断信息。一个I2C设备会被转换为一个i2c_client结构体,中断号会保存在i2c_client的irq成员里,代码如下(drivers/i2c/i2c-core.c):

对于SPI设备节点,SPI总线驱动在处理设备树里的SPI子节点时,也会处理其中的中断信息。一个SPI设备会被转换为一个spi_device结构体,中断号会保存在spi_device的irq成员里,代码如下(drivers/spi/spi.c):

3.3. 调用of_irq_get获得中断号

如果你的设备节点既不能转换为platform_device,它也不是I2C设备,不是SPI设备,那么在驱动程序中可以自行调用of_irq_get函数去解析设备树,得到中断号。

3.4. 对于GPIO

参考:

drivers/input/keyboard/gpio_keys.c

可以使用gpio_to_irq或gpiod_to_irq获得中断号。

举例,假设在设备树中有如下节点:

代码语言:javascript复制
gpio-keys {
    compatible = "gpio-keys";
    pinctrl-names = "default";


    user {
        label = "User Button";
        gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
        gpio-key,wakeup;
        linux,code = <KEY_1>;
    };
};

那么可以使用下面的函数获得引脚和flag:

代码语言:javascript复制
button->gpio = of_get_gpio_flags(pp, 0, &flags);

bdata->gpiod = gpio_to_desc(button->gpio);

再去使用gpiod_to_irq获得中断号:

代码语言:javascript复制
irq = gpiod_to_irq(bdata->gpiod);

0 人点赞