3. 设备树中的GPIO描述:GPIO控制器节点、GPIO specifier、pinctrl绑定
在嵌入式Linux系统中,设备树(Device Tree)是描述硬件拓扑结构的核心机制。GPIO驱动开发的第一步,就是理解如何在设备树中正确描述GPIO控制器、GPIO引脚引用以及引脚复用控制。本章将深入剖析这三个关键概念,帮助你在实际项目中准确编写和解析设备树中的GPIO相关节点。
3.1 GPIO控制器节点
GPIO控制器节点是设备树中描述SoC内部GPIO外设的节点。它定义了该控制器支持的GPIO引脚数量、寄存器基址、中断控制器关联等属性。内核通过解析该节点来注册对应的GPIO芯片驱动。
| 属性 | 描述 | 示例值 |
|---|---|---|
compatible |
匹配驱动字符串,用于绑定对应的GPIO驱动 | "brcm,bcm2835-gpio" |
reg |
寄存器地址和长度,描述控制器的MMIO区域 | <0x7e200000 0xb4> |
gpio-controller |
空属性,标记该节点为GPIO控制器 | 无值 |
#gpio-cells |
指定GPIO specifier所需的cell数量 | 2 |
interrupt-controller |
空属性,标记该节点同时也是中断控制器 | 无值 |
#interrupt-cells |
指定中断specifier所需的cell数量 | 2 |
gpio-controller 和 #gpio-cells 是GPIO控制器节点的必备属性。缺少任何一个,其他设备节点将无法正确引用该控制器的GPIO引脚。
以下是一个典型的GPIO控制器节点示例(以BCM2835为例):
gpio: gpio@7e200000 {
compatible = "brcm,bcm2835-gpio";
reg = <0x7e200000 0xb4>;
interrupts = <2 17>, <2 18>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
3.2 GPIO specifier
GPIO specifier是设备树中用于引用特定GPIO引脚的编码方式。它由一组32位整数(cell)组成,格式由GPIO控制器的 #gpio-cells 属性决定。最常见的格式是2个cell:第一个cell表示引脚编号,第二个cell表示引脚标志(如高电平有效、低电平有效、开漏等)。
GPIO specifier的典型使用方式:
leds {
compatible = "gpio-leds";
led-red {
gpios = <&gpio 17 0>; // GPIO 17,低电平有效(0表示默认)
label = "red-led";
};
led-green {
gpios = <&gpio 18 1>; // GPIO 18,高电平有效(1表示active high)
label = "green-led";
};
};
第二个cell的常用标志位定义:
| 标志值 | 宏定义 | 含义 |
|---|---|---|
| 0 | GPIO_ACTIVE_HIGH |
高电平有效(默认) |
| 1 | GPIO_ACTIVE_LOW |
低电平有效 |
| 2 | GPIO_OPEN_DRAIN |
开漏输出 |
| 3 | GPIO_OPEN_SOURCE |
开源输出 |
gpios 属性中的 &gpio 引用必须指向一个已声明 gpio-controller 的节点。如果引用错误,内核在解析设备树时会报“GPIO lookup failed”错误。
3.3 pinctrl绑定
pinctrl(Pin Control)子系统负责管理SoC引脚的复用功能、电气特性和上下拉配置。在设备树中,通过 pinctrl-names 和 pinctrl-0 等属性将设备与特定的引脚配置状态绑定。
pinctrl绑定的核心概念:
- 状态(State): 设备在不同运行阶段(如默认、休眠、激活)需要不同的引脚配置。
- 配置组(Config Group): 一组引脚及其对应的复用功能、驱动强度、上下拉等设置。
- 绑定机制: 通过
pinctrl-names定义状态名称列表,pinctrl-0、pinctrl-1等属性引用对应的配置组节点。
// pinctrl控制器节点定义引脚配置组
pinctrl: pinctrl@7e300000 {
compatible = "brcm,bcm2835-pinctrl";
reg = <0x7e300000 0x20>;
uart0_pins: uart0_pins {
brcm,pins = <14 15>; // 引脚14(TXD)、15(RXD)
brcm,function = <2>; // 复用功能:ALT0(UART0)
brcm,pull = <0>; // 无上下拉
};
i2c1_pins: i2c1_pins {
brcm,pins = <2 3>; // 引脚2(SDA)、3(SCL)
brcm,function = <4>; // 复用功能:ALT0(I2C1)
brcm,pull = <2>; // 上拉
};
};
// 外设设备节点引用pinctrl配置
uart0: serial@7e201000 {
compatible = "brcm,bcm2835-uart";
reg = <0x7e201000 0x100>;
interrupts = <0 25 4>;
clocks = <&clocks 19>;
pinctrl-names = "default"; // 状态名称
pinctrl-0 = <&uart0_pins>; // 引用配置组
};
default 状态。如果设备支持电源管理,可以额外定义 sleep 状态,在系统休眠时将引脚配置为低功耗模式。
3.4 GPIO控制器、specifier与pinctrl的协作关系
为了更直观地理解这三者之间的协作关系,下面通过一张SVG架构图展示从设备树描述到内核驱动注册的完整链路。
3.5 实际工程中的设备树编写步骤
在嵌入式项目中,编写GPIO相关的设备树节点通常遵循以下步骤:
- 确认SoC的GPIO控制器信息: 查阅芯片手册,获取GPIO控制器的寄存器基址、中断号、引脚数量等参数。
- 定义GPIO控制器节点: 在设备树中创建或确认
gpio-controller节点,确保#gpio-cells与驱动期望值一致。 - 定义pinctrl配置组: 在pinctrl控制器节点下,为每个需要特定复用功能的外设定义配置组节点,指定引脚、功能和上下拉。
- 编写外设设备节点: 在外设节点中通过
gpios属性引用GPIO引脚,通过pinctrl-names和pinctrl-0绑定引脚配置。 - 编译并验证: 使用dtc工具编译设备树,通过
devicetree/unittest或内核启动日志验证节点解析是否正确。
dmesg | grep gpio 查看GPIO控制器的注册信息。使用 ls /sys/kernel/debug/gpio 可以查看当前系统中所有GPIO引脚的状态和用途。
3.6 常见问题与避坑指南
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| GPIO请求失败,返回-ENODEV | 设备树中 gpios 引用的控制器节点未声明 gpio-controller |
检查控制器节点是否包含 gpio-controller; 属性 |
| 引脚电平不受控制 | pinctrl配置未正确绑定,引脚仍处于默认复用状态 | 确认 pinctrl-names 和 pinctrl-0 引用路径正确 |
| 中断触发异常 | GPIO控制器节点缺少 interrupt-controller 属性 |
添加 interrupt-controller; 并正确设置 #interrupt-cells |
| 设备树编译报错 | 引用的节点标签(如 &gpio)未定义或拼写错误 |
使用 dtc -I dts -O dtb -o output.dtb input.dts 编译并查看错误行号 |
pinctrl-0 直接引用配置组,而有些厂商使用 pinctrl-0 = <&config1 &config2> 同时应用多个配置组。务必参考对应BSP的文档和示例设备树文件。
通过本章的学习,你应该已经掌握了设备树中GPIO控制器节点、GPIO specifier和pinctrl绑定的核心概念与编写方法。这些知识是后续实现GPIO输出控制和中断触发配置的基础,在实际工程中需要反复练习和验证。