1. GPIO子系统概述

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

GPIO(General Purpose Input/Output)子系统是Linux内核中负责管理通用输入输出引脚的核心框架。它为内核驱动和用户空间程序提供了一套统一、抽象的API,用于控制SoC或外设芯片上的数字引脚。

GPIO子系统在内核中的核心角色包括:

  • 引脚抽象:将物理引脚映射为逻辑编号,屏蔽不同SoC的寄存器差异。
  • 方向控制:统一管理引脚的输入/输出方向切换。
  • 电平读写:提供安全的读/写接口,避免竞态条件。
  • 中断管理:将GPIO引脚变化映射为内核中断,支持边沿触发和电平触发。
  • 用户空间访问:通过sysfs或chardev接口,允许用户态程序控制GPIO。
关键概念: GPIO子系统是"引脚功能"的最终使用者,它不直接操作引脚复用寄存器,而是通过Pin Control子系统完成引脚功能配置。

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

Pin Control子系统(pinctrl)是GPIO子系统的上游依赖。两者分工明确:

维度 GPIO子系统 Pin Control子系统
核心职责 引脚电平读写、方向控制、中断触发 引脚复用(mux)、电气特性(drive strength、pull-up/down)
操作对象 GPIO编号(逻辑抽象) 引脚编号(物理bank + pin)
配置时机 运行时动态切换 设备初始化时静态配置
依赖关系 依赖pinctrl完成引脚功能选择 不依赖GPIO子系统
用户接口 sysfs、chardev、gpiod API 设备树pinctrl-*属性、debugfs

典型的配置流程是:Pin Control子系统先将引脚复用为GPIO功能,然后GPIO子系统接管该引脚的电平读写和中断管理。两者通过设备树中的pinctrl-0属性和gpio-controller标识建立关联。

实践要点: 在设备树中,一个引脚节点通常同时包含pinctrl-0 = <&pinmux_gpio>和gpio-controller属性,前者由pinctrl解析,后者由GPIO子系统识别。

1.3 GPIO子系统架构图

下图展示了GPIO子系统在内核中的分层架构及其与Pin Control子系统的协作关系:

GPIO子系统架构图 用户空间 sysfs (/sys/class/gpio) | chardev (/dev/gpiochip*) | libgpiod 内核GPIO核心层 (gpiolib) gpiochip_add_data() | gpiod_get() | gpiod_direction_output() | gpiod_set_value() GPIO芯片驱动 gpio_chip结构体 | request() | set() | get() | direction_input() GPIO中断控制器 irq_chip结构体 | irq_request_resources() | irq_set_type() SoC GPIO寄存器 GPIO_BASE + offset | DATA/OUT/SET/CLR寄存器 中断控制器硬件 GIC | INTC | 边沿检测寄存器 to_irq() Pin Control子系统 (pinctrl) 引脚复用配置 | 电气特性设置 | 设备树pinctrl-*解析 依赖

1.4 架构分层说明

GPIO子系统的分层设计遵循Linux内核的通用驱动模型,从上到下依次为:

  1. 用户空间接口层:提供sysfs和chardev两种访问方式。sysfs适用于简单场景,chardev(libgpiod)是推荐的新接口,支持批量操作和事件监听。
  2. 内核GPIO核心层(gpiolib):实现通用的GPIO管理逻辑,包括编号分配、请求/释放、方向控制、值读写、中断映射等。所有GPIO芯片驱动都注册到这一层。
  3. GPIO芯片驱动层:由具体SoC厂商实现,填充gpio_chip结构体中的回调函数,操作硬件寄存器。
  4. GPIO中断控制器层:将GPIO引脚变化映射为内核中断,通常与GPIO芯片驱动紧密耦合,通过irq_chip结构体实现。
  5. 硬件抽象层:直接操作SoC的GPIO寄存器组和中断控制器寄存器。
常见误区: GPIO子系统不负责引脚复用配置。如果设备树中未正确配置pinctrl,即使GPIO驱动加载成功,引脚也可能无法正常工作(例如输出无电平变化)。务必先确认pinctrl状态。

1.5 关键数据结构关系

GPIO子系统的核心数据结构及其关系如下表所示:

数据结构 所在层 主要字段/作用
gpio_chip 芯片驱动 base(起始编号)、ngpio(引脚数)、request/set/get/direction_input回调
gpio_desc 核心层 描述一个GPIO引脚的描述符,包含flags、label、chip引用
irq_chip 中断控制器 irq_request_resources、irq_set_type、irq_mask、irq_unmask回调
gpio_device 核心层 封装gpio_chip和chardev设备,管理用户空间访问
pinctrl_dev Pin Control 引脚控制器设备,管理复用和电气配置

在驱动开发中,最常见的操作流程是:

// 1. 定义gpio_chip结构体
static struct gpio_chip my_gpio_chip = {
    .label          = "my-gpio",
    .base           = -1,          // 动态分配
    .ngpio          = 32,
    .request        = my_gpio_request,
    .free           = my_gpio_free,
    .direction_input = my_gpio_direction_input,
    .direction_output = my_gpio_direction_output,
    .get            = my_gpio_get,
    .set            = my_gpio_set,
    .to_irq         = my_gpio_to_irq,
};

// 2. 注册到gpiolib
int ret = gpiochip_add_data(&my_gpio_chip, NULL);
if (ret) {
    pr_err("Failed to add GPIO chip\n");
    return ret;
}

// 3. 关联中断控制器(可选)
ret = gpiochip_irqchip_add(&my_gpio_chip, &my_irq_chip, 0,
                           handle_edge_irq, IRQ_TYPE_NONE);
if (ret) {
    pr_err("Failed to add irq chip\n");
    gpiochip_remove(&my_gpio_chip);
    return ret;
}
开发建议: 新驱动应优先使用gpiod API(基于描述符)而非旧的gpio_* API(基于整数编号)。描述符API支持设备树映射、引用计数和更安全的错误处理。