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)配置。两者的协作关系可以用下图清晰表达:
上图展示了两个子系统的典型协作流程:
- 设备树解析阶段:内核解析设备树节点中的
pinctrl-0、pinctrl-names属性,Pin Control 子系统将引脚配置为 GPIO 功能。 - GPIO 控制器注册:SoC 的 GPIO 驱动调用
gpiochip_add_data()注册 gpio_chip,此时引脚已处于 GPIO 复用状态。 - 驱动请求 GPIO:外设驱动通过
gpiod_get()获取描述符,Pin Control 子系统确保引脚状态正确。 - 运行时切换:若驱动需要动态切换引脚功能(如 GPIO 模拟 I2C),需通过
pinctrl_select_state()重新配置。
常见陷阱:如果 GPIO 驱动在注册时未正确配置 Pin Control 状态,可能导致引脚功能异常(如输出电平不受控、中断无法触发)。务必在设备树中显式指定 pinctrl 状态,或在驱动 probe 中调用
devm_pinctrl_get_select_default()。
1.4 内核中的 GPIO 软件架构
从软件分层角度看,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.
2.
3. 若缺少
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 状态;注意中断触发类型的硬件支持 |