3. GPIO 子系统:Linux GPIO 子系统架构、gpio-controller、gpio-cells、gpio-hog
在嵌入式 Linux 系统中,GPIO(General Purpose Input/Output)是最基础且使用最频繁的硬件资源之一。对于骁龙平台的 Sensor 上电时序控制,GPIO 的配置正确与否直接决定了 Sensor 能否在正确的时刻获得电源和复位信号。本章将深入剖析 Linux GPIO 子系统的软件架构,并重点讲解设备树中与 GPIO 相关的三个核心绑定:gpio-controller、#gpio-cells 和 gpio-hog。
3.1 Linux GPIO 子系统软件架构
Linux GPIO 子系统采用分层设计,从上到下依次为:GPIO 消费者(Consumer)、GPIO 核心层(Core) 和 GPIO 控制器驱动(Controller Driver)。这种分层架构使得驱动开发者无需关心底层硬件寄存器细节,只需通过标准 API 即可操作 GPIO。
gpiod_get() 等 API 获取 GPIO 描述符,核心层负责管理 GPIO 编号映射和引脚复用,控制器驱动则直接操作硬件寄存器。
3.2 设备树中的 GPIO 绑定
设备树是描述硬件拓扑的静态数据结构。GPIO 相关的绑定主要涉及三个属性:gpio-controller、#gpio-cells 和 gpio-hog。理解这三个属性是正确配置 Sensor 上电时序的前提。
3.2.1 gpio-controller
gpio-controller 是一个布尔属性,用于标记该设备节点是一个 GPIO 控制器。当一个节点包含此属性时,Linux 内核会将其注册为 GPIO 芯片,并纳入 gpiolib 框架管理。
gpio-controller 的节点才能被其他设备节点通过 <&gpioX ...> 方式引用。骁龙 SoC 通常有多个 GPIO 控制器,如 tlmm(Top Level Mode Multiplexer)和 pmic-gpio。
3.2.2 #gpio-cells
#gpio-cells 指定了 GPIO 描述符中需要的单元格数量。它决定了消费者在引用 GPIO 时必须提供的参数个数。最常见的值是 2,表示需要两个参数:GPIO 编号 和 GPIO 标志。
| #gpio-cells 值 | 参数格式 | 典型场景 |
|---|---|---|
| 1 | <GPIO_NUM> | 仅指定引脚编号,无额外标志 |
| 2 | <GPIO_NUM FLAGS> | 指定编号 + 极性/驱动类型(最常用) |
| 3 | <GPIO_NUM FLAGS EXTRA> | 某些特殊控制器需要额外参数 |
#gpio-cells 不匹配,内核会报错并导致 GPIO 申请失败。例如,#gpio-cells = <2> 的控制器,消费者必须写成 <&gpio1 5 0>,不能省略标志位。
3.2.3 gpio-hog
gpio-hog 是一种特殊的 GPIO 配置机制,允许在设备树中直接指定某个 GPIO 的初始状态(输出高/低、输入等),而无需编写额外的驱动代码。这对于 Sensor 上电时序中的电源使能引脚非常有用——可以在系统启动早期就完成 GPIO 初始化。
gpio-hog 子节点通常包含以下属性:
- gpio-hog:布尔属性,标记该节点为 hog 节点。
- gpios:指定要控制的 GPIO 引脚和标志。
- output-high / output-low:设置输出电平。
- input:设置为输入模式。
- line-name:为这条 GPIO 线命名,便于调试。
3.3 完整设备树示例:Sensor 上电 GPIO 配置
以下是一个针对骁龙平台的典型 Sensor 上电 GPIO 配置片段。该配置使用 gpio-hog 在系统启动早期将 Sensor 的电源使能引脚拉高,确保后续 Sensor 驱动 probe 时电源已经稳定。
/* 骁龙平台 TLMM GPIO 控制器节点 */
tlmm: pinctrl@f100000 {
compatible = "qcom,msm8998-pinctrl";
reg = <0x0f100000 0x300000>;
interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
/* Sensor 电源使能:使用 gpio-hog 提前初始化 */
sensor_pwr_hog {
gpio-hog;
gpios = <92 0>; /* GPIO 92,标志 0 表示高电平有效 */
output-high; /* 输出高电平,使能 Sensor 电源 */
line-name = "sensor-pwr-enable";
};
/* Sensor 复位引脚:使用 gpio-hog 保持非复位状态 */
sensor_rst_hog {
gpio-hog;
gpios = <93 0>;
output-high; /* 拉高复位引脚,Sensor 正常工作 */
line-name = "sensor-reset";
};
};
/* Sensor 设备节点:引用 GPIO 进行后续控制 */
i2c@c175000 {
status = "okay";
sensor@48 {
compatible = "vendor,sensor-xyz";
reg = <0x48>;
/* 这里引用 GPIO 93 作为复位控制,驱动中可动态操作 */
reset-gpios = <&tlmm 93 GPIO_ACTIVE_LOW>;
vdd-supply = <&pm8994_lvs1>;
};
};
gpio-hog节点必须放在 GPIO 控制器节点内部,作为其子节点。gpios中的标志位0表示高电平有效,GPIO_ACTIVE_LOW在消费者节点中使用。line-name在/sys/kernel/debug/gpio中可见,便于调试。- hog 节点的初始化发生在
pinctrl设置之后、其他驱动 probe 之前,非常适合时序敏感场景。
3.4 调试与验证
配置完成后,可以通过以下方法验证 GPIO 状态是否符合预期:
| 调试方法 | 命令/路径 | 预期输出 |
|---|---|---|
| 查看 GPIO 概览 | cat /sys/kernel/debug/gpio |
显示所有 GPIO 芯片、引脚、标签和当前值 |
| 检查特定引脚 | cat /sys/class/gpio/gpio92/value |
应为 1(输出高) |
| 查看设备树解析 | dtc -I fs -O dts /proc/device-tree |
确认 hog 节点已展开且属性正确 |
| 内核日志 | dmesg | grep gpio |
显示 "hogging GPIO 92 as sensor-pwr-enable" |
dmesg 中是否有 "gpio-hog" 相关的错误信息,并确认 #gpio-cells 与消费者参数个数一致。
通过本章的学习,你应该能够理解 Linux GPIO 子系统的分层设计,掌握设备树中 gpio-controller、#gpio-cells 和 gpio-hog 的配置方法,并能够利用这些知识解决骁龙平台上 Sensor 上电时序的 GPIO 初始化问题。