1. GPIO子系统概述:Linux GPIO子系统架构、GPIO硬件基础、GPIO控制器与引脚编号

1.1 GPIO硬件基础

GPIO(General Purpose Input/Output,通用输入输出)是嵌入式系统中最基础、最常用的外设接口。每个GPIO引脚可以独立配置为输入或输出模式,在输入模式下读取外部电平(高/低),在输出模式下驱动外部设备(LED、继电器等)。

核心硬件特性:
  • 方向控制:通过寄存器配置引脚为输入或输出
  • 电平检测:数字信号高电平(通常为3.3V/1.8V)或低电平(0V)
  • 内部上拉/下拉:防止引脚浮空,提供确定的默认电平
  • 中断触发:支持上升沿、下降沿、高电平、低电平触发
  • 驱动能力:通常为4mA~20mA,不同SoC有所差异

下图展示了一个典型的GPIO引脚内部结构,包含输入缓冲器、输出驱动器和可配置的上拉/下拉电阻:

GPIO引脚内部硬件结构 SoC 芯片边界 输出驱动器 (推挽/开漏) 输入缓冲器 (施密特触发) 上拉电阻 下拉电阻 中断检测 (边沿/电平) GPIO 引脚焊盘 (Pad) 内部上拉/下拉电阻 可通过寄存器配置使能

1.2 Linux GPIO子系统软件架构

Linux GPIO子系统采用分层设计,从硬件到用户空间共分为四层,每一层都封装了特定的职责:

Linux GPIO子系统软件架构 用户空间 sysfs接口 / libgpiod / 字符设备 GPIO核心层 (gpiolib) 引脚编号管理 / 中断映射 / 操作抽象 GPIO控制器驱动 (gpio_chip) request / free / direction_input / direction_output / get / set 硬件层 (SoC GPIO寄存器) 数据寄存器 / 方向寄存器 / 中断寄存器 系统调用 接口 回调函数 注册 寄存器 操作
架构设计要点:GPIO核心层(gpiolib)是Linux GPIO子系统的中枢,它向上提供统一的系统调用接口(字符设备、sysfs),向下定义gpio_chip结构体回调函数,驱动开发者只需实现硬件相关的寄存器操作即可。

1.3 GPIO控制器与引脚编号

在Linux系统中,每个GPIO引脚都有一个全局唯一的编号,称为GPIO编号。这个编号由系统启动时动态分配,或者通过设备树静态指定。GPIO控制器(gpio_chip)负责管理一组连续的GPIO引脚。

1.3.1 引脚编号分配机制

编号范围 所属控制器 典型SoC示例 说明
0 ~ 31 GPIO0 (P0组) i.MX6ULL 第一个GPIO控制器的32个引脚
32 ~ 63 GPIO1 (P1组) i.MX6ULL 第二个GPIO控制器的32个引脚
64 ~ 95 GPIO2 (P2组) i.MX6ULL 第三个GPIO控制器的32个引脚
96 ~ 127 GPIO3 (P3组) i.MX6ULL 第四个GPIO控制器的32个引脚
128 ~ 159 GPIO4 (P4组) i.MX6ULL 第五个GPIO控制器的32个引脚
注意事项:不同SoC的GPIO控制器数量和引脚编号范围差异很大。例如,STM32MP157有12个GPIO控制器(PA~PK),每个控制器16个引脚;而i.MX6ULL只有5个GPIO控制器,每个32个引脚。编写驱动时务必查阅芯片手册。

1.3.2 gpio_chip结构体核心字段

每个GPIO控制器在内核中对应一个struct gpio_chip实例,驱动开发者需要填充以下关键字段:

struct gpio_chip {
    const char              *label;         // 控制器名称,如 "GPIO1"
    struct device           *parent;        // 父设备
    struct module           *owner;         // THIS_MODULE

    int                     base;           // 引脚编号起始值
    u16                     ngpio;          // 本控制器管理的引脚数量

    // 方向控制回调
    int (*direction_input)(struct gpio_chip *gc, unsigned int offset);
    int (*direction_output)(struct gpio_chip *gc, unsigned int offset, int value);

    // 读写回调
    int (*get)(struct gpio_chip *gc, unsigned int offset);
    void (*set)(struct gpio_chip *gc, unsigned int offset, int value);

    // 中断相关回调
    int (*to_irq)(struct gpio_chip *gc, unsigned int offset);

    // 资源管理回调
    int (*request)(struct gpio_chip *gc, unsigned int offset);
    void (*free)(struct gpio_chip *gc, unsigned int offset);
};

1.3.3 设备树中的GPIO控制器描述

在设备树中,GPIO控制器通过gpio-controller属性标识,#gpio-cells指定引脚描述所需的参数个数:

// i.MX6ULL 设备树中的GPIO控制器节点示例
gpio1: gpio@0209c000 {
    compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
    reg = <0x0209c000 0x4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;          // 两个参数:引脚偏移 + 标志
    interrupt-controller;
    #interrupt-cells = <2>;     // 两个参数:中断类型 + 标志
};

// 引用GPIO引脚示例(LED控制)
led-red {
    compatible = "gpio-leds";
    gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;  // GPIO1_IO15
};

1.4 GPIO子系统核心数据结构关系

下图展示了GPIO子系统中核心数据结构之间的关联关系:

GPIO子系统核心数据结构关系 gpio_chip base: 起始编号 ngpio: 引脚数量 label: 控制器名称 ops: 操作回调 gpio_desc flags: 使用标志 label: 使用者名称 chip: 所属控制器 offset: 引脚偏移 irq_desc irq_data: 中断数据 action: 处理函数 status: 中断状态 chip: 中断控制器 数据流说明 1. gpio_chip 注册到 gpiolib 时,内核为每个引脚创建 gpio_desc 2. gpio_desc 通过 chip 指针关联到对应的 gpio_chip 3. 中断使能后,gpio_desc 通过 to_irq() 映射到 irq_desc

1.5 本章小结

  • GPIO硬件基础:每个引脚包含输入缓冲器、输出驱动器、可配置上拉/下拉电阻和中断检测逻辑。
  • 软件架构:Linux GPIO子系统分为用户空间接口、gpiolib核心层、gpio_chip驱动层和硬件寄存器层。
  • 引脚编号:全局唯一编号由base+offset组成,不同SoC的控制器数量和引脚范围差异较大。
  • 核心数据结构:gpio_chip描述控制器硬件能力,gpio_desc描述单个引脚状态,irq_desc管理中断映射。
工程实践提示:在开始编写GPIO驱动前,务必通过芯片手册确认以下信息:GPIO控制器数量、每个控制器的引脚范围、中断控制器连接方式、以及设备树中gpio-cells的格式。这些信息将直接影响驱动实现的正确性。