**欧博嵌入式Linux设备树中断配置**
在嵌入式系统开发领域,特别是基于Linux操作系统的设备驱动开发中,设备树(Device Tree)扮演着至关重要的角色。它作为一种数据结构,描述了硬件平台的配置信息,使得Linux内核能够“了解”它所运行的硬件环境,从而加载相应的驱动程序并进行配置。其中,中断(Interrupt)的配置是设备树描述中的一个核心部分,它直接关系到设备如何与CPU进行异步通信,以及驱动程序如何响应这些异步事件。本文将聚焦于欧博(EuRob)嵌入式平台,深入探讨Linux设备树中中断配置的原理、方法、常见中断控制器类型以及配置中的注意事项。
**一、 设备树与中断:为何重要?**
传统的嵌入式系统开发中,硬件信息往往直接硬编码在内核源码中。这种方法存在诸多弊端,如代码可移植性差、修改硬件配置需要重新编译内核等。设备树的引入,将硬件描述与内核代码分离,实现了内核与硬件描述的解耦。
对于中断而言,设备树需要明确告知内核:
1. **中断源(Interrupt Source)**:哪个硬件设备会产生中断。
2. **中断控制器(Interrupt Controller)**:该中断信号最终由哪个中断控制器来管理。
3. **中断标识(Interrupt Identifier)**:在中断控制器内部,该中断的编号或标识符是什么。
4. **中断触发方式(Interrupt Trigger Type)**:该中断是电平触发(Level-sensitive)还是边沿触发(Edge-sensitive),是高电平/上升沿有效,还是低电平/下降沿有效。
5. **中断优先级(Interrupt Priority)**:在某些支持优先级的中断控制器中,该中断的优先级是多少。
只有正确配置了这些信息,Linux内核才能正确地注册中断处理程序,管理中断状态(使能、屏蔽),并在中断发生时准确地调度相应的处理函数,从而实现设备与系统的有效交互。
**二、 设备树中断属性详解**
在设备树源文件(.dts或.dtsi)中,中断相关的信息通常通过特定的属性来描述。最核心的属性是 `interrupts` 和 `interrupt-parent`。
1. **interrupt-parent**:
* 该属性用于指定当前节点(设备)的中断控制器父节点。因为一个SoC上可能存在多个中断控制器,所以需要明确指定设备的中断信号最终路由到哪个控制器。
* 其值通常是一个指向设备树中另一个节点的引用(例如,使用单位址引用 `&` 或 `&`)。
* 如果一个节点没有显式指定 `interrupt-parent`,它会继承其父节点的 `interrupt-parent` 值。通常,根节点或某个顶层节点会定义一个默认的 `interrupt-parent`,指向系统主用的中断控制器。
2. **interrupts**:
* 这是描述单个设备中断属性的核心属性。它的值是一个或多个中断描述符的数组,每个描述符具体定义了中断控制器如何识别和处理该设备的中断。
* **中断描述符的格式**:具体格式高度依赖于所使用的 `interrupt-controller` 节点中定义的 `#interrupt-cells` 属性值。`#interrupt-cells` 告诉设备树的解析器,每个中断描述符需要多少个单元格(cell)来表示。
* **常见格式(以GIC为例)**:对于ARM架构常用的GIC(Generic Interrupt Controller),`#interrupt-cells` 通常设置为2。因此,`interrupts` 属性的每个值通常由两个单元格组成:
* **单元格0(中断号)**:在中断控制器内部注册的中断编号。
* **单元格1(中断标志)**:包含触发类型、极性等信息。常见的标志位定义在 `` 头文件中,例如:
* `IRQ_TYPE_LEVEL_HIGH` (0x2):高电平触发
* `IRQ_TYPE_LEVEL_LOW` (0x3):低电平触发
* `IRQ_TYPE_EDGE_RISING` (0x4):上升沿触发
* `IRQ_TYPE_EDGE_FALLING` (0x5):下降沿触发
* `IRQ_TYPE_EDGE_BOTH` (0xA):双边沿触发
* `IRQ_TYPE_NONE` (0x0):无特定触发类型(通常由控制器默认)
* 这些标志位可能通过位操作组合使用。
* **多中断源**:如果一个设备有多个中断输出(例如,状态中断和错误中断),`interrupts` 属性可以包含多个中断描述符,用空格分隔。驱动程序需要根据需要注册多个中断处理程序。
**三、 欧博平台常见中断控制器及配置示例**
欧博嵌入式平台可能采用不同的SoC,从而使用不同类型的中断控制器。以下列举几种常见类型及其在设备树中的配置示例:
1. **ARM GIC (Generic Interrupt Controller)**:
* **特点**:ARM架构的标准中断控制器,支持多核,功能强大。
* **`#interrupt-cells`**:通常为2。
* **配置示例**:
```dts
// 中断控制器节点定义(通常在 soc节点下)
interrupt-parent = ; // 设定为默认中断父节点
&gic {
compatible = "arm,gic-400", "arm,cortex-a15-gic";
interrupt-controller;
#interrupt-cells = ; // 告知每个中断需要2个cell
// ... 其他GIC特定属性
};
// 某个外设节点(例如UART)
&uart0 {
compatible = "arm,pl011", "arm,primecell";
reg = ;
interrupts = , // SPI中断域,中断号42,电平触发
; // PPI中断域,中断号10,边沿触发
// ... 其他属性
};
```
* **注意**:`GIC_SPI` 和 `GIC_PPI` 等宏通常定义在 SoC 特定的头文件或 bindings 文件中,用于生成正确的中断号和标志组合。
2. **STM32 EXTI (External Interrupt) / NVIC (Nested Vectored Interrupt Controller)**:
* **特点**:常见于STM32系列MCU,EXTI用于外部线中断,NVIC是Cortex-M核的中断控制器。
* **`#interrupt-cells`**:对于NVIC,通常是1;对于EXTI,可能是1或2,取决于具体实现和描述方式。
* **配置示例(假设使用EXTI)**:
```dts
// 中断控制器节点定义
&exti {
compatible = "st,stm32-exti";
interrupt-controller;
#interrupt-cells = ; // 告知每个中断需要1个cell(通常是EXTI线号)
// ... 其他属性
};
// 某个GPIO节点配置中断
&gpioa {
gpioa_pin5: gpioa_pin5 {
st,pin = ; // GPIO A的第5引脚
st,function = ;
st,mode = ;
st,alt-function = ;
st,pull = ;
st,open-drain = ;
st,edge-trigger = ; // 触发方式
interrupts = ; // 中断描述符,值为EXTI线号5
interrupt-parent = ; // 指定EXTI为中断父节点
};
};
```
3. **其他中断控制器**:如Intel的IOAPIC、PIC,或其他定制的中断控制器,其 `#interrupt-cells` 和中断描述符格式会有所不同,需要参考具体的SoC文档和设备树绑定(binding)文档。
**四、 配置注意事项与调试技巧**
1. **准确性**:中断号、触发方式等必须与硬件设计完全一致。任何错误都可能导致中断无法触发、错误触发或系统不稳定。
2. **中断父节点**:务必正确设置 `interrupt-parent`,确保中断信号被路由到正确的控制器。
3. **`#interrupt-cells`**:这是理解 `interrupts` 属性格式的关键,必须查阅对应中断控制器的设备树绑定文档。
4. **驱动程序配合**:设备树配置只是第一步,驱动程序(driver)需要正确解析 `interrupts` 属性,并使用 `request_irq()` 或 `devm_request_irq()` 等函数注册中断处理程序。
5. **调试工具**:
* **`dtc` (Device Tree Compiler)**:编译设备树源文件,检查语法错误。
* **`cat /proc/interrupts`**:查看系统中所有中断的统计信息,包括中断号、触发次数、处理程序等,有助于判断中断是否被内核识别和触发。
* **`cat /proc/device-tree//`**:直接读取设备树节点中的属性值,验证