GPIO Hog 机制详解 文章封面

GPIO Hog 机制详解

一句话:GPIO Hog 是一种 DTS 配置机制,让内核在注册 GPIO 控制器时自动预占指定引脚并设置初始状态,无需专门的驱动程序去申请它。


一、为什么需要 GPIO Hog

正常情况下,GPIO 引脚由对应的驱动在 probe() 时通过 gpiod_get() 申请,在 remove() 时释放。这套流程适合”有驱动管理的外设”。

但有一类 GPIO 比较特殊:

  • 硬件复位引脚(如 USB Hub 的 RESET_N、以太网 PHY 的 RST)——需要在系统启动极早期拉高/拉低,但没有独立驱动
  • 上电使能引脚(如某个电源轨的 EN 脚)——需要在 regulator 驱动加载之前就处于确定电平
  • 硬件配置引脚(如启动模式选择)——上电后需要立即固定状态,不能浮空

对这类引脚,如果等驱动 probe() 才去申请,时序可能已经来不及;如果专门为它写一个空壳驱动又太重。

GPIO Hog 解决的就是这个问题:在 GPIO 控制器注册完成的瞬间,内核立刻预占并配置这些引脚,早于任何外设驱动的 probe(),且不需要独立驱动。


二、核心概念

GPIO Hog(”占用”/”独占”)是 Linux 3.12 引入的机制,配置方式是 DTS,不是驱动代码。

关键特点:

特点说明
配置位置GPIO 控制器节点的子节点,DTS 中声明
触发时机GPIO chip 注册时(gpiochip_add_data()),早于外设驱动 probe
是否需要驱动不需要,内核自动处理
引脚状态被标记为 FLAG_IS_HOGGED,其他驱动无法再申请
电平控制支持输出高、输出低、输入三种模式

三、DTS 配置方法

3.1 基本语法

在 GPIO 控制器节点下添加子节点,子节点必须包含 gpio-hog 属性:

&gpio_controller {
    子节点名称 {
        gpio-hog;                        /* 标记这是一个 hog 节点,必须有 */
        gpios = <引脚编号  极性标志>;    /* 指定引脚 */
        output-high;                     /* 配置为输出,逻辑高电平 */
        /* output-low;  或输出低电平  */
        /* input;       或配置为输入  */
        line-name = "描述性名称";        /* 可选,用于标识该引脚 */
    };
};

3.2 DTS 属性说明

属性是否必须说明
gpio-hog✅ 必须声明这是一个 hog 节点,无值,仅凭存在触发
gpios✅ 必须<偏移量 极性>,极性:0=高有效,1=低有效
output-high三选一配置为输出,输出逻辑 1(高)
output-low三选一配置为输出,输出逻辑 0(低)
input三选一配置为输入,只占用不驱动
line-name可选引脚标识名,会显示在 /sys/kernel/debug/gpio

⚠️ 极性与电平的关系:gpios = <7 1> 表示 GPIO 7,低有效(active-low)。此时 output-high 输出的是逻辑高,但物理引脚实际为低电平(经过极性反转)。如果只是要控制物理引脚电平,建议将极性设为 0(高有效),直接用 output-high / output-low 控制物理电平,语义更清晰。

3.3 完整示例

/* iMX6ULL GPIO 控制器节点示例 */
&amp;gpio1 {
    /* USB Hub 复位引脚:输出低电平,保持 Hub 处于工作状态(RESET_N 低有效) */
    usb_hub_reset_hog: usb-hub-reset {
        gpio-hog;
        gpios = &lt;7 GPIO_ACTIVE_LOW&gt;;
        output-high;                     /* 逻辑高 → 物理低 → Hub 正常工作 */
        line-name = "usb-hub-reset";
    };
};

&amp;gpio3 {
    /* 以太网 PHY 复位:上电后立即释放复位(高电平 = 正常工作) */
    eth_phy_reset_hog: eth-phy-reset {
        gpio-hog;
        gpios = &lt;4 GPIO_ACTIVE_HIGH&gt;;
        output-high;
        line-name = "eth-phy-reset";
    };

    /* 某个硬件配置引脚:只需占用,配置为输入即可 */
    mode_sel_hog: mode-select {
        gpio-hog;
        gpios = &lt;12 GPIO_ACTIVE_HIGH&gt;;
        input;
        line-name = "mode-select";
    };
};


四、内核处理流程

GPIO Hog 的整个处理过程由内核自动完成,驱动开发者不需要写任何 C 代码:

gpiochip_add_data()           ← 驱动调用,注册 GPIO 控制器
    └─ of_gpiochip_add(gc)    ← 绑定 DTS 节点
        └─ of_gpiochip_scan_gpios(gc)
               │
               ├─ 遍历 GPIO 控制器节点的所有子节点
               │
               └─ 发现含 "gpio-hog" 属性的子节点
                      │
                      ├─ 解析 gpios、output-high/output-low/input、line-name
                      │
                      └─ gpiod_hog(desc, name, lflags, dflags)
                             ├─ gpiochip_request_own_desc()  ← 内部申请,不走正常请求流程
                             ├─ set_bit(FLAG_IS_HOGGED, &amp;desc-&gt;flags)  ← 标记为已 hog
                             └─ 按配置设置引脚方向和电平

时序意义:整个流程在 GPIO chip 注册完成时同步完成,早于总线扫描和外设驱动的 probe()。


五、gpiod_hog() 内部实现(仅供参考)

gpiod_hog() 是内核内部函数,驱动开发者不应直接调用,这里列出源码仅作理解参考:

/* drivers/gpio/gpiolib.c */
int gpiod_hog(struct gpio_desc *desc, const char *name,
              unsigned long lflags, enum gpiod_flags dflags)
{
    struct gpio_chip *gc;
    struct gpio_desc *local_desc;
    int hwnum;
    int ret;

    gc = gpiod_to_chip(desc);
    hwnum = gpio_chip_hwgpio(desc);

    /* 通过 chip 内部接口申请,绕过正常的 gpiod_get 流程 */
    local_desc = gpiochip_request_own_desc(gc, hwnum, name,
                                           lflags, dflags);
    if (IS_ERR(local_desc)) {
        ret = PTR_ERR(local_desc);
        pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %dn",
               name, gc-&gt;label, hwnum, ret);
        return ret;
    }

    /* 在描述符的 flags 中打上 hogged 标记 */
    set_bit(FLAG_IS_HOGGED, &amp;desc-&gt;flags);

    gpiod_info(desc, "hogged as %s%sn",
        (dflags &amp; GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
        (dflags &amp; GPIOD_FLAGS_BIT_DIR_OUT) ?
            (dflags &amp; GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : "");

    return 0;
}

关键点:

  • 使用 gpiochip_request_own_desc() 而非 gpiod_get(),这是 chip 内部的私有申请路径
  • 设置 FLAG_IS_HOGGED 后,其他驱动调用 gpiod_get() 申请同一引脚时会返回 -EBUSY

六、GPIO Hog vs 普通 GPIO 申请

对比项GPIO Hog(DTS)普通申请(gpiod_get())
配置位置GPIO 控制器子节点外设节点的 gpios 属性 + 驱动代码
申请时机GPIO chip 注册时,极早期外设驱动 probe() 时
需要驱动不需要需要外设驱动
引脚归属内核持有,无驱动主体由申请的驱动持有
释放时机GPIO chip 注销时驱动 remove() 或手动 gpiod_put()
适用场景无专属驱动的硬件引脚、早期上电时序有驱动管理的外设 GPIO
其他驱动能否申请❌ 不能,返回 -EBUSY✅ 释放后可被重新申请

七、适用场景与注意事项

适合用 GPIO Hog 的场景

  • 硬件复位引脚:USB Hub、以太网 PHY、音频 Codec 的复位脚,需要在驱动 probe 前已经就绪
  • 电源使能引脚:上电后需立即有效的 EN 脚,且没有对应的 regulator 驱动
  • 硬件模式配置引脚:只需要开机设置一次、保持固定状态的引脚

注意事项

  1. 不要用 GPIO Hog 代替 regulator 框架:如果引脚控制的是电源轨,应优先考虑 fixed-regulator + regulator 框架,而不是简单用 Hog 强制拉高
  2. Hog 的引脚无法被其他驱动动态控制:Hog 的设计意图是”固定状态”,如果引脚需要在运行时被驱动程序动态读写,应使用普通 GPIO 申请
  3. 调试查看:可通过 /sys/kernel/debug/gpio 查看哪些引脚被 hog,会显示 line-name 和方向/电平状态
# 查看 GPIO 状态,hog 的引脚会标注 [hogged]
cat /sys/kernel/debug/gpio
# 示例输出:
# gpio-7   (usb-hub-reset       ) out hi [hogged]
# gpio-52  (eth-phy-reset       ) out hi [hogged]


关联笔记

  • 平台设备驱动模型 — GPIO 在平台驱动 probe 中的常规申请方式(of_get_named_gpio、devm_gpio_request_one)
  • PWM驱动开发 — 同属 Linux 子系统分层架构,核心层 vs host 驱动的职责划分

延伸阅读

GPIO Hog 适合处理上电默认状态;运行期 GPIO 控制可继续参考 LED 和按键驱动。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部