- 了解DTS Interrupt 设置方式。
1.DTS 中 interrupt 描述
-
interrupt-controller - 一个空的属性定义, 该节点作为一个接收中断信号的设备。
-
#interrupt-cells - 这是一个中断控制器节点的属性。它声明了该中断控制器的中断指示符中 cell 的个数(类似于 #address-cells 和 #size-cells)。
-
interrupt-parent - 这是一个设备节点的属性,包含一个指向该设备连接的中断控制器的 phandle。那些没有 interrupt-parent 的节点则从它们的父节点中继承该属性。
-
interrupts - 一个设备节点属性,包含一个中断指示符的列表,对应于该设备上的每个中断输出信号。
2.中断控制器interrupt-controller
一个中断控制器节点必须含有 interrupt-controller 属性,该属性是一个 bool 属性, 其值必须为空。也就是说,只要节点中含有 interrupt-controller 属性,那么这个节点 就是中断控制器。中断控制器节点必须含有 #interrupt-cells 属性,该属性定义节点中中断标识的 cell 数量。以下是三种 cell 中断表示的例子:
2.1.One cell
中断控制器中,#interrupt-cells 属性设置为 1,此时节点中的中断标识只含有一个 cell,该 cell 用于指明中断在中断控制器内的偏移。
Example:
vic: interrupt@10140000 {
compatible = "arm,versatile-vic";
interrupt-controller; //此设备是一个中断控制器
#interrupt-cells = <1>;
reg = <0x10140000 0x1000>;
};
sic: intc@10003000 {
compatible = "arm,versatile-sic";
interrupt-controller; //此设备是一个中断控制器
#interrupt-cells = <1>;
reg = <0x10003000 0x1000>;
interrupt-parent = <&vic>;
interrupts = <31>; /* Cascaded to vic */
};
本例中 vic,sic 都是中断控制器的节点,其中 vic 控制器定义了 cell 数为 1,那么 所有以它为父节点的节点,中断标识只含有一个 cell。如上 sic 也是一个中断控制器, 其以 vic 为父节点,所有 sic 的 interrupts 属性就只能含有一个 cell,sic 使用的 中断在 vic 中的偏移是 31。
2.2.Two Cell
中断控制器中,#interrupt-cell 属性设置为 2,此时节点中的中断标识含有两个 cell:
- 第一个 cell 用于指明在中断控制器内的偏移;
- 第二个 cell 用于指明触发方式和级别;
bits[3:0] 触发类型和级别标志:
1 = low-to-high edge triggered
2 = high-to-low edge triggered
4 = active high level-sensitive
8 = active low level-sensitive
Example:
i2c@7000c000 {
gpioext: gpio-adnp@41 {
compatible = "ad,gpio-adnp";
reg = <0x41>;
intrrupt-parent = <&gpio>;
interrupts = <160 1>;
gpio-controller;
#gpio-cells = <1>;
interrupt-controller;
#interrupt-cells = <2>;
nr-gpios = <64>;
};
sx8634@2b {
compatible = "smtc,sx8634";
reg = <0x2b>;
interrupt-parent = <&gpioext>;
interrupts = <3 0x8>;
#address-cells = <1>;
#size-cells = <0>;
threshold = <0x40>;
sensitivity = <7>;
};
};
2.3.Three Cell
GIC中断控制器:
Example:
gic: interrupt-controller@d000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
#interrupt-cells = <3>;
#size-cells = <0>;
reg = <0xd000 0x1000>,
<0xc100 0x1000>;
};
#interrupt-cells 属性由于指明中断源中 cell 的数量,GIC 中 interrupt-cells 的值为 3。每个 cell 的含义如下:
- 第一个 cell 代表中断类型。
- GIC_SPI:0;
- GIC_PPI:1 ;
- 第二个 cell 代表不同类型中断的中断号。
- SPI 中断号的范围从 0 到 987;
- PPI 中断号 从 0 到 15。
- 第三个 cell 代表中断标识。
各标识含义如下:
bits[3:0] 代表触发类型
1 = 上升沿 IRQ_TYPE_EDGE_RISING
2 = 下降沿 IRQ_TYPE_EDGE_FALLING
4 = 高电平有效 IRQ_TYPE_LEVEL_HGIH
8 = 低电平有效 IRQ_TYPE_LEVEL_LOW
bits[15:8] PPI 中断 CPU 掩码
3.操作interrupt APIs
include/linux/of_irq.h:
45 extern int of_irq_count(struct device_node *dev);
46 extern int of_irq_get(struct device_node *dev, int index);
47 extern int of_irq_get_byname(struct device_node *dev, const char *name);
48 extern int of_irq_to_resource_table(struct device_node *dev,
49 struct resource *res, int nr_irqs);
50 extern struct device_node *of_irq_find_parent(struct device_node *child);
51 extern struct irq_domain *of_msi_get_domain(struct device *dev,
52 struct device_node *np,
53 enum irq_domain_bus_token token);
54 extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
55 u32 rid);
56 extern void of_msi_configure(struct device *dev, struct device_node *np);
57 u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in);
4.中断调试方法
- /sys/kernel/debug/irq
- /sys/kernel/debugirq_domain_mapping
4.1.查看系统信息
开机后,从/proc/interrupts中看到当前的中断资源申请信息:
[root@tq2440 ]# cat /proc/interrupts
CPU0
7: 926 s3c-eint 7 Edge eth0
8: 0 s3c 8 Edge s3c2410-rtc tick
13: 567308 s3c 13 Edge samsung_time_irq
26: 0 s3c 26 Edge ohci_hcd:usb1
27: 4 s3c 27 Edge 54000000.i2c //I2C
30: 0 s3c 30 Edge s3c2410-rtc alarm
32: 53 s3c-level 32 Level 50000000.serial //UART0 RXD
33: 230 s3c-level 33 Level 50000000.serial //UART0 TXD
59: 0 s3c-level 59 Edge 53000000.watchdog
上面这些参数的含义:
相关代码:
- kernel/irq/debugfs.c
- kernel/irq/irqdomain.c
- kernel/fs/proc.c
- fs/proc/interrupts.c
查看具体irq:
- cat /proc/irq/27
5.案例
5.1.内核中断
/*
* Interrupt on DTS
*
* (C) 2018.11.14 <buddy.zhang@aliyun.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* demo: demo2@2 {
* compatible = "demo,demo_intr";
* reg = <2>;
* interrupt-parent = <&gpio0>;
* interrupts = <11 2>;
* status = "okay";
* };
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
/* define name for device and driver */
#define DEV_NAME "demo_intr"
static int irq;
static irqreturn_t demo_irq_handler(int irq, void *dev_id)
{
printk("Hello World\n");
return IRQ_HANDLED;
}
/* probe platform driver */
static int demo_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int ret;
/* Obtain interrupt ID from DTS */
irq = of_irq_get(np, 0);
/* Request irq handler */
ret = request_threaded_irq(irq, NULL, demo_irq_handler,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING, "demo", NULL);
if (ret) {
printk("Failed to acquire irq %d\n", irq);
return -EINVAL;
}
return 0;
}
/* remove platform driver */
static int demo_remove(struct platform_device *pdev)
{
/* Release Interrupt */
free_irq(irq, NULL);
return 0;
}
static const struct of_device_id demo_of_match[] = {
{ .compatible = "demo,demo_intr", },
{ },
};
MODULE_DEVICE_TABLE(of, demo_of_match);
/* platform driver information */
static struct platform_driver demo_driver = {
.probe = demo_probe,
.remove = demo_remove,
.driver = {
.owner = THIS_MODULE,
.name = DEV_NAME,
.of_match_table = demo_of_match,
},
};
module_platform_driver(demo_driver);
5.2.gpio中断案例
arch/arm/boot/dts/imx6q-novena.dts:
268 touch: stmpe811@44 {
269 compatible = "st,stmpe811";
270 reg = <0x44>;
271 irq-gpio = <&gpio5 13 GPIO_ACTIVE_HIGH>;
drivers/mfd/stmpe.c:
1292 pdata->irq_gpio = of_get_named_gpio_flags(np, "irq-gpio", 0,
1293 &pdata->irq_trigger);
1371 ret = devm_gpio_request_one(ci->dev, pdata->irq_gpio, GPIOF_DIR_IN, "stmpe");
1378 stmpe->irq = gpio_to_irq(pdata->irq_gpio);
1404 if (stmpe->irq >= 0) {
1405 ret = stmpe_irq_init(stmpe, np);
1408
1409 ret = devm_request_threaded_irq(ci->dev, stmpe->irq, NULL,
1410 stmpe_irq, pdata->irq_trigger | IRQF_ONESHOT,
1411 "stmpe", stmpe);
1417 }
参考:
https://biscuitos.github.io/blog/DTS-interrupt/