4. 设备树GPIO控制器

在嵌入式Linux系统中,GPIO(通用输入输出)是最基础且使用最频繁的硬件资源之一。设备树通过标准化的描述方式,将GPIO控制器的硬件拓扑、引脚编号规则以及电气属性传递给内核。本章深入解析设备树中GPIO控制器的节点定义、gpio-specifier的编码规则,以及GPIO号与标志位的映射关系。

4.1 GPIO控制器节点(gpio-controller)

设备树中,每个能够控制GPIO引脚的硬件模块都必须声明为GPIO控制器节点。该节点通过两个关键属性标识其身份:

属性 类型 说明
gpio-controller 空属性(布尔) 声明该节点是一个GPIO控制器
#gpio-cells u32 指定引用该控制器时需要的gpio-specifier单元数,通常为2
关键规则: 一个设备节点可以同时是GPIO控制器和中断控制器,但必须分别声明gpio-controller和interrupt-controller属性,并各自指定#gpio-cells和#interrupt-cells。

典型GPIO控制器节点示例:

gpio0: gpio@ff310000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xff310000 0x0 0x1000>;
    interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_gpio0>;
    
    gpio-controller;          // 声明为GPIO控制器
    #gpio-cells = <2>;        // 每个GPIO引用需要2个单元
    
    interrupt-controller;     // 同时作为中断控制器
    #interrupt-cells = <2>;
    
    gpio-ranges = <&pinctrl 0 0 32>;  // 引脚范围映射
};
实战提示: gpio-ranges属性将GPIO控制器的逻辑引脚号映射到pinctrl子系统的物理引脚号,确保GPIO和pinctrl的引脚管理一致。缺少该属性可能导致GPIO申请失败或引脚复用冲突。

4.2 gpio-specifier:GPIO引用编码规则

gpio-specifier是设备树中引用某个GPIO控制器的具体引脚时使用的编码序列。其长度由被引用控制器的#gpio-cells决定,通常为2个单元(cells)。

单元序号 名称 说明
第1个cell GPIO号 相对于该控制器的引脚偏移量,从0开始
第2个cell GPIO标志位 引脚配置标志,如高/低电平有效、上拉/下拉等

引用语法格式:

<&[GPIO控制器标签] [GPIO号] [标志位]>

实际设备树中的引用示例:

led-red {
    compatible = "gpio-leds";
    gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_LOW>;
};

sdmmc_pwr: sdmmc-pwr {
    compatible = "regulator-fixed";
    regulator-name = "sdmmc_vcc";
    gpio = <&gpio3 RK_PA2 GPIO_ACTIVE_HIGH>;
    enable-active-high;
};
避坑指南: 注意gpios(复数)和gpio(单数)属性的区别。gpios用于需要多个GPIO的场景(如GPIO LED组),gpio用于单个GPIO控制(如固定稳压器使能)。内核解析函数不同,混用会导致驱动加载失败。

4.3 GPIO号与标志位详解

4.3.1 GPIO号的编码方式

设备树中的GPIO号是相对于所属控制器的本地偏移量,而非系统全局编号。内核通过以下公式计算全局GPIO号:

全局GPIO号 = GPIO控制器基址 + 设备树中指定的GPIO号

其中,GPIO控制器基址由驱动注册时通过gpiochip_add或gpiochip_add_data指定,通常与设备树中gpio-ranges的起始引脚号一致。

GPIO控制器 基址(base) 设备树GPIO号 全局GPIO号
gpio0 0 12 12
gpio1 32 5 37
gpio2 64 20 84

4.3.2 GPIO标志位定义

标志位用于描述引脚的电气特性,内核预定义了以下标准标志(定义在dt-bindings/gpio/gpio.h中):

宏定义 值 说明
GPIO_ACTIVE_HIGH 0 高电平有效(默认)
GPIO_ACTIVE_LOW 1 低电平有效
GPIO_OPEN_DRAIN 2 开漏输出
GPIO_OPEN_SOURCE 4 开源输出
GPIO_PULL_UP 8 内部上拉使能
GPIO_PULL_DOWN 16 内部下拉使能
组合使用: 标志位可以通过按位或组合,例如 GPIO_ACTIVE_LOW | GPIO_PULL_UP 表示低电平有效且使能内部上拉。内核在of_get_named_gpio_flags解析时会自动合并标志位。

4.4 GPIO控制器与pinctrl的交互流程

下图展示了设备树GPIO控制器节点、pinctrl子系统以及V4L2驱动之间的引脚申请与配置流程:

设备树GPIO控制器与pinctrl交互流程 设备树GPIO控制器节点 gpio-controller / #gpio-cells of_get_named_gpio_flags 解析gpio-specifier GPIO号映射 本地号 → 全局号 pinctrl引脚复用配置 pinctrl-names / pinctrl-0 gpio_request / gpiod_get 申请GPIO资源 V4L2驱动使用GPIO 电源使能 / 复位 / 检测 流程说明: 1. 驱动通过 of_get_named_gpio_flags 解析设备树 gpio-specifier,获取GPIO号和标志位。 2. 内核将本地GPIO号映射为系统全局GPIO号,并检查gpio-ranges有效性。 3. pinctrl子系统根据设备树pinctrl-*属性配置引脚复用功能(GPIO模式)。 4. V4L2驱动通过gpio_request/gpiod_get申请GPIO,并根据标志位设置初始电平。

4.5 常见GPIO标志位使用场景

不同外设对GPIO标志位的需求差异很大,以下表格总结了典型场景的推荐配置:

外设类型 推荐标志位 说明
LED指示灯 GPIO_ACTIVE_LOW 共阳极LED,低电平点亮
按键输入 GPIO_ACTIVE_LOW | GPIO_PULL_UP 外部按下拉低,内部上拉保证空闲高电平
摄像头复位 GPIO_ACTIVE_LOW 复位信号通常低电平有效
SD卡检测 GPIO_ACTIVE_LOW | GPIO_PULL_UP 卡插入时引脚被拉低
电源使能 GPIO_ACTIVE_HIGH 高电平开启稳压器
I2C总线GPIO模拟 GPIO_OPEN_DRAIN 开漏输出,符合I2C总线规范

4.6 内核GPIO API与设备树标志位的对应关系

内核提供两套GPIO API:legacy GPIO(整数编号)和 descriptor-based GPIO(gpiod结构体)。设备树标志位对两者的影响不同:

API类型 函数 标志位处理方式
Legacy of_get_named_gpio_flags 返回原始标志位,驱动需自行调用gpio_direction_output并设置电平
Descriptor gpiod_get / devm_gpiod_get 自动根据GPIO_ACTIVE_LOW反转电平,驱动无需关心物理电平极性
Descriptor gpiod_get_index 支持多GPIO引脚的索引访问,标志位自动处理
最佳实践: 新驱动开发应优先使用descriptor-based GPIO API(gpiod_*系列函数)。该API自动处理GPIO_ACTIVE_LOW标志位反转,使驱动代码与硬件极性解耦,提高可移植性。Legacy API仅用于兼容旧代码。

4.7 调试技巧:验证GPIO控制器配置

在调试设备树GPIO配置时,以下方法可快速验证节点定义是否正确:

  1. 查看设备树解析结果:
    # 检查GPIO控制器是否被正确识别
    ls /sys/kernel/debug/gpio
    
    # 查看具体GPIO控制器的引脚映射
    cat /sys/kernel/debug/gpio | grep gpiochip
  2. 验证gpio-specifier解析:
    # 使用gpioinfo查看全局GPIO号与设备树标签的对应关系
    gpioinfo
    
    # 查看特定GPIO的消费方信息
    cat /sys/kernel/debug/gpio | grep "gpio-XXX"
  3. 检查pinctrl与GPIO的一致性:
    # 查看引脚复用状态
    cat /sys/kernel/debug/pinctrl/*/pinmux-pins
    
    # 确认GPIO对应的引脚没有被其他外设占用
    cat /sys/kernel/debug/pinctrl/*/pinmux-select
常见错误: 设备树中GPIO号超出控制器范围(如32引脚的控制器指定了GPIO号35),或gpio-ranges缺失导致pinctrl无法正确配置引脚复用。这类错误通常表现为gpio_request返回-EINVAL或-EBUSY。

4.8 本章小结

设备树GPIO控制器的核心概念可归纳为以下要点:

  • gpio-controller 和 #gpio-cells 是声明GPIO控制器的必备属性,缺一不可。
  • gpio-specifier 由GPIO号和标志位组成,标志位支持按位组合,用于描述引脚电气特性。
  • 设备树中的GPIO号是本地偏移量,内核通过控制器基址映射为全局编号。
  • gpio-ranges 属性是GPIO控制器与pinctrl子系统的桥梁,确保引脚复用配置正确。
  • V4L2驱动应使用descriptor-based GPIO API,利用设备树标志位自动处理电平极性。