Linux驱动开发基础_在设备树中指定中断以及在代码中获得中断

2023-05-16

目录

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

1.1 设备树里的中断控制器

1.2 设备树里使用中断

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

3 在代码中获得中断

3.1 对于 platform_device 

3.2  对于 I2C 设备、SPI 设备

3.3  调用 of_irq_get 获得中断号

3.4 对于 GPIO 


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

参考文档:内核 Documentation\devicetree\bindings\interrupt-controller\interrupts.txt

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 的值一般有如下取值: 
⚫  #interrupt-cells=<1> 
别的节点要使用这个中断控制器时,只需要一个 cell 来表明使用“哪一个中断”。 
⚫  #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,低电平触发 

示例如下: 

vic: intc@10140000 { 
  compatible = "arm,versatile-vic"; 
  interrupt-controller; 
  #interrupt-cells = <1>; 
  reg = <0x10140000 0x1000>; 
}; 

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

1.2 设备树里使用中断

一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的? 
这 3 个问题,在设备树里使用中断时,都要有所体现。 
⚫  interrupt-parent=<&XXXX> 
你要用哪一个中断控制器里的中断? 
⚫  interrupts 
你要用哪一个中断? 
Interrupts 里要用几个 cell,由 interrupt-parent 对应的中断控制器决定。在中断控制器里有“#interrupt-cells”属性,它指明了要用几个 cell来描述中断。 
比如: 

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”,比如: 

interrupts-extended = <&intc1 5 1>, <&intc2 1 0>; 

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

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

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

3 在代码中获得中断

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

  • 根节点下含有 compatile 属性的子节点,会转换为 platform_device 
  • 含有特定 compatile 属性的节点的子节点,会转换为 platform_device 如果一个节点的 compatile 属性,它的值是这 4 者之一:"simple-bus","simple-mfd","isa","arm,amba-bus",  那么它的子结点(需含 compatile 属性)也可以转换为 platform_device。
  • 总线 I2C、SPI 节点下的子节点:不转换为 platform_device 某个总线下到子节点,应该交给对应的总线驱动程序来处理, 它们不应该被转换为 platform_device。 

3.1 对于 platform_device 

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

/** 
 * 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 

可以使用 gpio_to_irq 或 gpiod_to_irq 获得中断号。 举例,假设在设备树中有如下节点:

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: 
button->gpio = of_get_gpio_flags(pp, 0, &flags); 
bdata->gpiod = gpio_to_desc(button->gpio); 
再去使用 gpiod_to_irq 获得中断号: 
irq = gpiod_to_irq(bdata->gpiod); 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux驱动开发基础_在设备树中指定中断以及在代码中获得中断 的相关文章

随机推荐