4. 设备树中GPIO的绑定:gpio-controller与#gpio-cells属性、GPIO specifier格式、标准GPIO绑定示例
在嵌入式Linux网关硬件抽象层中,GPIO是最基础且使用最频繁的硬件资源。设备树(Device Tree)通过一套标准化的绑定机制,将GPIO控制器的硬件描述与驱动程序的资源获取解耦。本章节深入解析GPIO绑定的核心属性、GPIO specifier的编码格式,并通过完整的绑定示例展示如何在网关主板上描述GPIO资源。
4.1 GPIO绑定的核心属性
设备树中,任何一个能够控制GPIO引脚的硬件模块(如GPIO控制器、GPIO扩展芯片、SoC内部的GPIO Bank)都必须声明两个关键属性,以告知内核该节点是一个GPIO提供者。
| 属性名 | 类型 | 必需性 | 说明 |
|---|---|---|---|
gpio-controller |
空属性(boolean) | 必需 | 标记该节点是一个GPIO控制器,可以对外提供GPIO引脚 |
#gpio-cells |
u32 | 必需 | 指定引用该控制器时,GPIO specifier中需要多少个cell(32位整数)来描述一个GPIO |
关键理解: gpio-controller 是“提供者”的声明,#gpio-cells 是“接口契约”的定义。消费者节点通过GPIO specifier引用引脚时,必须按照 #gpio-cells 规定的格式提供参数。
4.2 GPIO specifier格式
GPIO specifier是消费者节点中用于引用一个具体GPIO引用的参数序列。其格式由GPIO控制器的 #gpio-cells 值决定。最常见的格式是 #gpio-cells = <2>,即两个cell的格式。
4.2.1 标准两cell格式(#gpio-cells = <2>)
| Cell序号 | 名称 | 说明 |
|---|---|---|
| cell 0 | GPIO编号 | 控制器内部的引脚索引号,从0开始 |
| cell 1 | GPIO标志位 | 位掩码,定义引脚的电气特性与逻辑行为 |
GPIO标志位(cell 1)的常用位定义:
| 位 | 宏定义 | 值 | 含义 |
|---|---|---|---|
| bit 0 | GPIO_ACTIVE_LOW |
1 | 引脚低电平有效(逻辑1对应低电平) |
| bit 1 | GPIO_OPEN_DRAIN |
2 | 引脚配置为开漏输出 |
| bit 2 | GPIO_OPEN_SOURCE |
4 | 引脚配置为开源输出 |
| bit 3 | GPIO_PULL_UP |
8 | 使能内部上拉电阻 |
| bit 4 | GPIO_PULL_DOWN |
16 | 使能内部下拉电阻 |
实践建议: 在网关硬件抽象层中,对于复位信号、中断请求等关键控制线,务必在设备树中明确设置 GPIO_ACTIVE_LOW 标志,使驱动逻辑与硬件原理图保持一致,避免因电平极性误解导致的驱动bug。
4.2.2 单cell格式(#gpio-cells = <1>)
部分简单的GPIO控制器(如某些GPIO扩展芯片)可能只使用单cell格式,此时cell 0仅表示引脚编号,不携带标志位。驱动中默认使用高电平有效、推挽输出。
4.3 GPIO绑定关系SVG架构图
下图展示了设备树中GPIO提供者与消费者之间的绑定关系,以及GPIO specifier在其中的传递路径。
4.4 标准GPIO绑定示例
以下是一个完整的网关主板设备树片段,展示了从GPIO控制器声明到多个消费者节点引用的全过程。该示例基于典型的ARM Cortex-A7网关SoC。
4.4.1 GPIO控制器声明
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
gpio0: gpio@1c20800 {
compatible = "allwinner,sun8i-h3-gpio";
reg = <0x01c20800 0x400>;
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_APB1>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
gpio1: gpio@1c20c00 {
compatible = "allwinner,sun8i-h3-gpio";
reg = <0x01c20c00 0x400>;
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_APB1>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
};
4.4.2 消费者节点引用GPIO
/* 网关状态指示灯:使用gpio0的引脚12,高电平有效 */
leds {
compatible = "gpio-leds";
status-led {
label = "gateway:green:status";
gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
alarm-led {
label = "gateway:red:alarm";
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
default-state = "off";
linux,default-trigger = "none";
};
};
/* 网关物理按键:使用gpio1的引脚7,低电平有效,内部上拉 */
keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
reset-key {
label = "Reset Button";
linux,code = <KEY_RESTART>;
gpios = <&gpio1 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
debounce-interval = <50>;
};
};
/* 网关4G模块电源控制:使用gpio0的引脚3,高电平有效 */
&pinctrl {
modem_power_pin: modem_power_pin {
pins = "PA3";
function = "gpio_out";
};
};
&gpio0 {
modem-power {
gpio-hog;
gpios = <3 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "modem-power";
};
};
常见错误: 在消费者节点中引用GPIO时,gpios 属性的第一个参数是GPIO控制器的phandle,第二个参数是引脚编号,第三个参数是标志位。初学者容易混淆引脚编号与物理引脚序号——设备树中的编号是控制器内部的软件索引,并非芯片封装的物理引脚号。务必查阅SoC数据手册的GPIO Bank映射表。
4.5 GPIO绑定中的高级用法
4.5.1 GPIO Hogging(引脚预配置)
GPIO Hogging允许在设备树中直接配置GPIO引脚的初始状态(输出高/低、输入),而无需编写额外的驱动代码。这对于网关中那些只需要固定电平的控制信号(如复位、使能、电源控制)非常有用。
&gpio0 {
/* 在GPIO控制器节点内部定义hog子节点 */
usb-hub-reset {
gpio-hog;
gpios = <5 GPIO_ACTIVE_LOW>;
output-high; /* 初始输出高电平,使USB HUB正常工作 */
line-name = "usb-hub-reset";
};
eth-phy-power {
gpio-hog;
gpios = <8 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "eth-phy-power";
};
};
4.5.2 多控制器引用
在复杂的网关设计中,可能同时使用SoC内置GPIO和外部I2C/SPI扩展GPIO。消费者节点可以引用不同控制器的引脚。
/* 外部GPIO扩展芯片:基于I2C的PCA9535 */
&i2c0 {
gpio_expander: gpio@20 {
compatible = "nxp,pca9535";
reg = <0x20>;
gpio-controller;
#gpio-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <10 IRQ_TYPE_EDGE_FALLING>;
};
};
/* 消费者同时使用SoC GPIO和扩展GPIO */
sensor-enable {
compatible = "gpio-enable";
enable-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>,
<&gpio_expander 3 GPIO_ACTIVE_LOW>;
};
4.6 GPIO绑定验证流程
在将设备树提交给硬件抽象层之前,建议按照以下步骤验证GPIO绑定的正确性:
- 语法检查: 使用
dtc -I dts -O dtb编译设备树源文件,确保无语法错误。 - 绑定检查: 使用
dt-validate工具检查是否满足内核的GPIO绑定schema。 - phandle解析: 确认所有
&gpio0、&gpio1等phandle引用正确指向已声明的GPIO控制器节点。 - 引脚冲突检测: 检查是否有多个消费者节点引用了同一个GPIO引脚(除非是共享输入场景)。
- 标志位验证: 确认
GPIO_ACTIVE_LOW等标志位与硬件原理图一致。
网关HAL层最佳实践: 在硬件抽象层中,建议将所有GPIO的命名与功能定义集中在设备树的一个单独包含文件中(如 gateway-gpio-map.dtsi),便于硬件改版时集中修改,避免在多个驱动文件中分散定义。
4.7 GPIO绑定与PWM绑定的对比
| 对比维度 | GPIO绑定 | PWM绑定 |
|---|---|---|
| 提供者标记 | gpio-controller |
#pwm-cells + 无特定标记(通过compatible识别) |
| cells数量 | 通常为2(引脚号 + 标志位) | 通常为3(通道号 + 周期 + 极性) |
| 消费者属性名 | gpios 或 xxx-gpios |
pwms 或 xxx-pwms |
| 标志位含义 | 电气特性(极性、开漏、上拉) | 极性(PWM_POLARITY_INVERTED) |
| 典型应用 | 数字输入/输出、中断、复位 | 背光调节、电机速度、蜂鸣器 |