中断号由内核统一分配和管理,确保不同硬件设备的中断请求互不冲突,维护系统稳定运行。
在嵌入式 Linux 驱动开发中,确定设备使用的中断号(IRQ number)是让设备能够响应硬件事件(如数据到达、状态改变)的关键步骤,这个中断号并非一个固定不变的硬件物理编号,而是 Linux 内核在运行时动态分配或映射的一个虚拟中断号,以下是确定中断号的详细过程和主要方法:
硬件设备通常有一个物理中断线(或称为硬件中断号、中断引脚),在 Linux 内核中,这个物理中断线会被映射到一个内核管理的虚拟中断号(irq
),驱动开发者主要关心和使用的是这个内核分配的 irq
值。
确定中断号的主要方法:
现代嵌入式 Linux 开发(尤其是使用设备树 Device Tree
的体系结构)中,设备树(Device Tree) 是最主要、最推荐的方式。
-
通过设备树(Device Tree – 首选方法)
- 原理: 设备树(
.dts
/.dtsi
文件)是一个描述硬件平台结构和资源(包括中断)的数据结构,它独立于内核源码,在系统启动时由 Bootloader(如 U-Boot)传递给内核。 - 过程:
- 设备节点定义中断属性: 在描述你的硬件设备的设备树节点中,需要包含一个或多个
interrupts
属性,这个属性指定了该设备连接到哪个中断控制器(interrupt-parent
)以及在该中断控制器上的哪个(或哪些)物理中断线/信号。 - 中断控制器节点: 系统中会有一个或多个中断控制器节点(如
intc
,gic
等),它们定义了#interrupt-cells
属性,说明其interrupts
属性需要多少个u32
单元来描述一个中断源,常见的值有 1, 2, 3。 - 内核解析与映射: 内核在启动时解析设备树,当它遇到一个带有
interrupts
属性的设备节点时:- 根据
interrupt-parent
(或继承父节点的)找到对应的中断控制器节点。 - 根据该中断控制器的
#interrupt-cells
值,读取interrupts
属性中相应数量的u32
值。 - 这些
u32
值通常包含:物理中断号(硬件中断线编号)、中断触发类型(如边沿触发、电平触发、高/低有效等)、有时还有中断优先级/类型(取决于具体中断控制器)。 - 内核根据这些信息,在内部为该物理中断线分配一个唯一的虚拟中断号 (
irq
),并建立映射关系。
- 根据
- 设备节点定义中断属性: 在描述你的硬件设备的设备树节点中,需要包含一个或多个
- 驱动获取中断号:
- 在驱动代码的
probe
函数(当设备与驱动匹配成功时调用)中,使用platform_get_irq()
或platform_get_irq_byname()
函数。 - *`platform_get_irq(struct platform_device pdev, unsigned int num)`**:
pdev
: 指向与该设备关联的platform_device
结构体的指针(通常由probe
函数参数传入)。num
: 设备树中该设备interrupts
属性里定义的第几个中断(索引,从 0 开始),如果设备只有一个中断源,通常为 0。- 返回值: 成功时返回内核分配的虚拟中断号 (
irq > 0
);失败时返回负的错误码(如-ENXIO
表示中断未定义)。
platform_get_irq_byname(struct platform_device *pdev, const char *name)
:- 如果设备树节点中的中断使用了
interrupt-names
属性给中断命名("tx"
,"rx"
,"int0"
),则可以使用此函数按名称获取对应的中断号。 name
: 要获取的中断在interrupt-names
中指定的名称。- 返回值: 同上。
- 如果设备树节点中的中断使用了
- 在驱动代码的
- 示例 (设备树片段):
&my_device { // 假设这是一个挂载在总线上的设备节点 compatible = "vendor,my-device"; reg = <0x10000000 0x1000>; // 寄存器地址范围 interrupt-parent = <&intc>; // 指向中断控制器节点 interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>; // 物理中断号=42, 高电平触发 // 如果支持多个中断或有名字: // interrupts = <0 42 IRQ_TYPE_EDGE_RISING>, <0 43 IRQ_TYPE_LEVEL_LOW>; // interrupt-names = "primary", "secondary"; };
- 示例 (驱动代码片段):
static int my_driver_probe(struct platform_device *pdev) { int irq, ret; ... // 获取第一个(索引0)中断号 irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); return irq; // 返回错误 } // 或者按名称获取 (如果定义了 interrupt-names) // irq = platform_get_irq_byname(pdev, "primary"); ... // 使用获取到的 irq 注册中断处理函数 ret = devm_request_irq(&pdev->dev, irq, my_interrupt_handler, IRQF_SHARED, dev_name(&pdev->dev), my_private_data); if (ret) { dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret); return ret; } ... return 0; }
- 原理: 设备树(
-
传统/旧方法(不推荐,仅用于遗留系统或无设备树)
- 板级特定文件(Board File): 在内核源码的板级支持包(BSP)或特定机器的文件(如
arch/arm/mach-xxx/board-yyy.c
)中,通常会硬编码地为设备定义资源,包括中断号,驱动通过platform_device
结构体获取这些资源。 - 驱动获取中断号:
- 在
probe
函数中,使用platform_get_resource(pdev, IORESOURCE_IRQ, index)
获取struct resource *
。 - 然后从
res->start
中提取中断号 (irq = res->start
)。
- 在
- 缺点: 高度依赖特定内核版本和板级代码,可移植性差,难以维护,现代嵌入式开发强烈建议使用设备树替代。
- 板级特定文件(Board File): 在内核源码的板级支持包(BSP)或特定机器的文件(如
-
直接硬件查询(极其罕见,不推荐)
- 理论上,驱动可以读取设备的某个配置寄存器来获取它使用的中断线(物理号),但这需要:
- 精确知道该寄存器位置和格式。
- 知道如何将该物理中断号映射到内核虚拟中断号(这通常需要访问特定于平台/中断控制器的内部数据结构,非常复杂且易变)。
- 强烈不推荐: 这种方法破坏了内核的中断抽象层,可移植性极差,极易出错,几乎只在非常早期的内核或特殊裸机场景中使用。在标准 Linux 驱动开发中应避免。
- 理论上,驱动可以读取设备的某个配置寄存器来获取它使用的中断线(物理号),但这需要:
关键点总结:
- 内核分配: 驱动使用的
irq
是内核管理的虚拟中断号,不是硬件的物理中断线号。 - 设备树是标准: 对于现代嵌入式 Linux 开发,设备树 (
Device Tree
) 是定义和获取中断号的绝对首选和标准方法。 使用platform_get_irq()
或platform_get_irq_byname()
在驱动的probe
函数中安全获取。 - 避免硬编码: 绝不要在驱动代码中硬编码中断号 (
irq
),这会导致驱动无法在不同硬件或不同配置上运行。 - 理解设备树绑定: 正确编写设备树节点是基础,需要查阅:
- 设备的数据手册,了解其物理中断输出。
- 所用中断控制器(如 GIC, NVIC, 外部中断控制器芯片)的文档和 Linux 内核绑定文档 (
Documentation/devicetree/bindings/interrupt-controller/
),了解其#interrupt-cells
含义和interrupts
属性的编码方式。 - 硬件平台(SoC/板级)的参考设备树 (
arch/arm64/boot/dts/vendor/
等)。
- 调试工具: 系统启动后,可以通过
/proc/interrupts
文件查看所有已注册中断的统计信息,包括中断号、所属设备、触发次数等,是验证中断是否成功注册和触发的有力工具。
在嵌入式 Linux 驱动开发中,确定中断号的正确且现代的方式是:在设备树 (.dts
) 中为你的设备节点明确定义 interrupt-parent
和 interrupts
属性,然后在驱动的 probe
函数中使用 platform_get_irq()
或 platform_get_irq_byname()
函数获取内核分配的实际虚拟中断号 (irq
)。 摒弃硬编码和直接操作硬件寄存器的方法,确保驱动的可移植性和可维护性。
引用说明:
- 主要基于 Linux 内核文档 (尤其是关于设备树、中断和平台设备的文档,可在
Documentation/
目录下找到)。 platform_get_irq()
等函数原型和用法参考 Linux 内核源码 (include/linux/platform_device.h
,drivers/base/platform.c
)。- 设备树绑定规范参考内核源码
Documentation/devicetree/bindings/
下的相关文件。 - 经典参考书籍如《Linux Device Drivers, 3rd Edition》(LDD3) 和《Essential Linux Device Drivers》提供了中断处理的基础知识,但需注意其中关于中断号获取的部分可能偏向旧方法,现代实践以设备树为主。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/6586.html