1. GPIO 子系统概述:角色、概念与 Pin Control 关系

GPIO(General Purpose Input/Output,通用输入输出)是嵌入式系统中最基础、最常用的外设接口。在 Linux 内核中,GPIO 子系统承担着统一管理 SoC 引脚复用、方向控制、电平读写及中断触发的核心职责。本章从宏观视角梳理 GPIO 子系统的定位、基本概念及其与 Pin Control 子系统的协作关系,为后续的驱动开发打下理论基础。

1.1 GPIO 子系统在内核中的角色

Linux 内核采用分层、模块化的设计思想,GPIO 子系统位于硬件与内核其他子系统之间,主要承担以下角色:

角色 说明 典型场景
硬件抽象层 屏蔽不同 SoC 的 GPIO 寄存器差异,向上提供统一的 API 驱动调用 gpiod_set_value() 无需关心底层寄存器地址
资源管理者 管理引脚的分配、释放、方向、复用状态,防止冲突 两个驱动同时请求同一 GPIO 时返回 -EBUSY
中断源提供者 将 GPIO 引脚的中断能力封装为标准 irq_chip,供中断子系统使用 按键驱动通过 gpiod_to_irq() 获取中断号
用户空间接口 通过 sysfs(旧)或 cdev(新)暴露给用户态程序 用户态通过 /dev/gpiochipX 操作 GPIO
关键认知:GPIO 子系统不是孤立的模块,它向下依赖 Pin Control 子系统完成引脚复用和电气属性配置,向上为 LED、按键、I2C 模拟、SPI 片选等驱动提供标准化的 GPIO 操作接口。

1.2 基本概念与术语

在深入代码之前,必须厘清 GPIO 子系统中的核心概念:

  • GPIO 控制器(gpio_chip):代表一个硬件 GPIO 控制器实例,包含一组引脚(通常 8/16/32 个),提供方向设置、电平读写、中断控制等底层操作函数。
  • GPIO 描述符(gpio_desc):内核内部对单个 GPIO 引脚的抽象描述,包含所属控制器、偏移量、标志位(方向、已申请、中断使能等)。
  • GPIO 编号(gpio number):全局唯一的整数标识符,早期内核使用静态编号(0~MAX),现代内核推荐使用描述符 API 避免编号混乱。
  • GPIO 映射(gpio mapping):通过设备树(Device Tree)或 ACPI 将物理引脚与驱动需要的逻辑名称绑定。
  • GPIO 中断(gpio interrupt):将 GPIO 引脚配置为中断源,支持上升沿、下降沿、高电平、低电平触发模式。
开发建议:新驱动应优先使用基于描述符的 API(gpiod_* 系列),避免使用已废弃的整数编号 API(gpio_* 系列)。描述符 API 提供更好的类型安全和资源跟踪。

1.3 GPIO 子系统与 Pin Control 子系统的关系

Pin Control 子系统(pinctrl)是 GPIO 子系统的“前置依赖”。一个物理引脚在作为 GPIO 使用之前,必须先通过 Pin Control 子系统完成引脚复用(pinmux)和电气属性(pinconf)配置。两者的协作关系可以用下图清晰表达:

GPIO 子系统与 Pin Control 子系统协作关系图 GPIO 子系统与 Pin Control 子系统协作关系 Pin Control 子系统 引脚复用 (Pinmux) 选择功能:GPIO / UART / I2C 引脚配置 (Pinconf) 上拉/下拉/驱动强度/施密特 引脚状态 (Pin State) default / sleep / idle pinctrl_ops / pinmux_ops / pinconf_ops 配置完成 引脚就绪 GPIO 子系统 gpio_chip 控制器注册 / 方向 / 读写 gpio_desc 描述符管理 / 引用计数 GPIO 中断 irq_chip / 触发类型 用户空间接口 cdev / libgpiod 核心 API:gpiod_get / gpiod_direction_input gpiod_set_value / gpiod_to_irq gpiolib 核心层 + 控制器驱动

上图展示了两个子系统的典型协作流程:

  1. 设备树解析阶段:内核解析设备树节点中的 pinctrl-0、pinctrl-names 属性,Pin Control 子系统将引脚配置为 GPIO 功能。
  2. GPIO 控制器注册:SoC 的 GPIO 驱动调用 gpiochip_add_data() 注册 gpio_chip,此时引脚已处于 GPIO 复用状态。
  3. 驱动请求 GPIO:外设驱动通过 gpiod_get() 获取描述符,Pin Control 子系统确保引脚状态正确。
  4. 运行时切换:若驱动需要动态切换引脚功能(如 GPIO 模拟 I2C),需通过 pinctrl_select_state() 重新配置。
常见陷阱:如果 GPIO 驱动在注册时未正确配置 Pin Control 状态,可能导致引脚功能异常(如输出电平不受控、中断无法触发)。务必在设备树中显式指定 pinctrl 状态,或在驱动 probe 中调用 devm_pinctrl_get_select_default()。

1.4 内核中的 GPIO 软件架构

从软件分层角度看,GPIO 子系统在内核中的位置如下:

Linux GPIO 子系统软件架构分层图 Linux GPIO 子系统软件架构分层 用户空间 libgpiod / sysfs (旧) 系统调用接口 (VFS / chrdev) GPIO 核心层 (gpiolib) 描述符管理 / 中断映射 / 设备树解析 / 用户空间接口 GPIO 控制器驱动 (gpio_chip ops) request / free / direction_input / direction_output / get / set / to_irq 硬件 (SoC GPIO 寄存器 / 中断控制器) 抽象层次递增

各层职责简述:

  • 用户空间:通过 libgpiod 库或直接操作 /dev/gpiochipX 字符设备访问 GPIO,适用于快速原型或调试。
  • 系统调用接口:字符设备驱动层将用户空间的 ioctl、read、write 转换为内核 GPIO API 调用。
  • GPIO 核心层:实现描述符分配、引用计数、中断映射、设备树解析、sysfs/cdev 接口等通用逻辑,是 gpiolib 的主体。
  • 控制器驱动:由 SoC 厂商实现,填充 gpio_chip 结构体的操作函数,直接操作硬件寄存器。
  • 硬件层:物理 GPIO 引脚对应的寄存器组,通常包含方向寄存器、数据寄存器、中断控制寄存器。

1.5 设备树中的 GPIO 与 Pin Control 绑定示例

实际开发中,GPIO 与 Pin Control 的协作通过设备树描述。以下是一个典型示例:

/* 设备树节点:GPIO 控制器 */
gpio0: gpio@ff310000 {
    compatible = "rockchip,gpio-bank";
    reg = <0xff310000 0x1000>;
    interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_gpio0>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
};

/* 设备树节点:使用 GPIO 的外设 */
led_user {
    compatible = "gpio-leds";
    pinctrl-names = "default";
    pinctrl-0 = <&led_pin>;          /* 引用 Pin Control 配置 */
    
    led0 {
        label = "user_led";
        gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>;  /* GPIO 0, pin A5 */
        linux,default-trigger = "heartbeat";
    };
};

/* Pin Control 子节点:将引脚配置为 GPIO 功能 */
&pinctrl {
    led_pin: led-pin {
        rockchip,pins = <0 RK_PA5 &fun_gpio>;  /* bank0, pinA5, 功能=GPIO */
    };
};
解析要点:
1. pinctrl-0 引用 led_pin 节点,Pin Control 子系统在驱动 probe 时自动将 RK_PA5 配置为 GPIO 功能。
2. gpios 属性中的 &gpio0 指向 GPIO 控制器,RK_PA5 是引脚偏移,GPIO_ACTIVE_HIGH 是电平极性。
3. 若缺少 pinctrl-0,内核不会自动配置引脚复用,可能导致 GPIO 操作无效。

1.6 本章小结

知识点 核心要点
GPIO 子系统角色 硬件抽象、资源管理、中断源提供、用户空间接口
核心概念 gpio_chip、gpio_desc、GPIO 编号、GPIO 映射、GPIO 中断
与 Pin Control 关系 Pin Control 负责引脚复用和电气配置,GPIO 负责方向与电平操作;两者通过设备树协作
软件架构 用户空间 → 系统调用 → gpiolib 核心 → 控制器驱动 → 硬件寄存器
开发注意事项 优先使用描述符 API;设备树中必须指定 pinctrl 状态;注意中断触发类型的硬件支持