☰
coreboot 主板 GPIO 配置实战指南:从 Boot Stage 划分到 Pad 寄存器编程
2026/10/14 17:03:42 网站建设 项目流程
  • 固件
  • 嵌入式
  • 操作系统
  • 驱动开发

【免费下载链接】coreboot

Read-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We don't handle Pull Requests.

项目地址:https://gitcode.com/gh_mirrors/co/coreboot
点击查看免费下载

在 coreboot 中,每块主板(mainboard)都必须正确配置自己的通用输入/输出引脚(GPIO),而“在哪个启动阶段配置”往往和“怎么配置”同样关键。本文基于仓库中的官方文档 Documentation/getting_started/gpio.md,结合 intelblocks/gpio 的公共实现与真实主板的gpio.c代码,完整讲解 GPIO 配置文件的组织方式、各厂商 SoC 的配置数据结构、PAD_CFG_*宏族的用法、未连接引脚(No Connect)的处理原则,以及 Intel PCH 上 Pad Reset、锁定寄存器与 Soft Straps 等容易踩坑的细节。读完本文,你可以为任意一块新主板定位 early/ramstage GPIO 的配置位置,并写出可复现的 pad 配置表。

为什么每块主板都必须配置 GPIO

SoC 的 pad 在复位后只会处于芯片默认(或厂商通过固件配置好的)状态,这个状态很少与主板电路的实际用途一致。coreboot 需要在正确的时间点把每个 pad 设置为正确的方向、上下拉、终止电阻、中断路由等。文档开篇即指出:“Every mainboard needs to appropriately configure its GPIOs. There are many facets of this issue, including which boot stage a GPIO might need to be configured.”

GPIO 配置的两个核心维度:

  1. 配置内容:每个 pad 的寄存器值(功能选择、上下拉、方向、中断路由、去抖等);
  2. 配置时机:coreboot 的各个 boot stage(bootblock → romstage → ramstage → SMM 等)。

配置时机:boot stage 的划分

文档将 GPIO 配置时机划分为两大类:

  • ramstage(大多数 GPIO):coreboot 的绝大部分非内存相关初始化都在 DRAM 可用之后的 ramstage 完成,因此主板的大多数 GPIO 在此阶段配置;
  • bootblock / romstage(early GPIO):少数引脚必须在内存初始化之前就绪,最典型的例子是memory strapping pins——它们指示当前安装的是哪种 DRAM。这些引脚必须在初始化内存之前被读取,因此其配置要放在更早期的 bootblock 或 romstage。

文档称这类“在 bootblock 或 romstage 中配置、视需求时机而定”的 GPIO 为“early” GPIO。

配置文件组织:gpio.c 与 baseboard/variant 模型

基本约定

大多数主板的目录(src/mainboard/<vendor>/<board>/)下会有一个gpio.c文件,其中通常是一个描述 GPIO 寄存器配置的表。由于寄存器布局因 SoC 甚至因 SoC 家族而异,编写配置前需要查阅对应 SoC 的 datasheet。

baseboard/variant(基板/变体)模型

许多主板遵循“基板 + 变体”的设计:若干块变体主板共享大量电路和芯片,公共部分被抽象成一个虚拟的baseboard。此时:

  • 多块板共享的 GPIO 放在baseboard 的gpio.c中;
  • 各板专属的 GPIO 放在每个变体自己的gpio.c中。

仓库中的 src/mainboard/clevo/cml-u 就是典型实例:公共代码位于 baseboard 目录,具体引脚表位于 src/mainboard/clevo/cml-u/variants/l140cu/gpio.c,early GPIO 表位于 src/mainboard/clevo/cml-u/variants/l140cu/gpio_early.c。

Intel SoC:公共 GPIO 块与宏库

宏库 gpio_defs.h

较新的 Intel SoC 共享同一套 GPIO IP 块,coreboot 将其公共部分抽象到 src/soc/intel/common/block/include/intelblocks/gpio_defs.h,提供了一整套描述 pad 配置的宏。文档中列出的核心宏如下:

/* Native function configuration */ #define PAD_CFG_NF(pad, pull, rst, func) /* General purpose output, no pullup/down. */ #define PAD_CFG_GPO(pad, val, rst) /* General purpose output, with termination specified */ #define PAD_CFG_TERM_GPO(pad, val, pull, rst) /* General purpose output, no pullup/down. */ #define PAD_CFG_GPO_GPIO_DRIVER(pad, val, rst, pull) /* General purpose input */ #define PAD_CFG_GPI(pad, pull, rst)

从源码看,这些宏都展开为struct pad_config结构体字面量(通过内部的_PAD_CFG_STRUCT宏),填充 DW0/DW1 两个配置字。几个代表性实现:

/* src/soc/intel/common/block/include/intelblocks/gpio_defs.h#L218-L222 */ /* Native function configuration */ #define PAD_CFG_NF(pad, pull, rst, func) \ _PAD_CFG_STRUCT(pad, \ PAD_RESET(rst) | PAD_FUNC(func), \ PAD_PULL(pull) | PAD_IOSSTATE(TxLASTRxE)) /* General purpose output, no pullup/down. */ #define PAD_CFG_GPO(pad, val, rst) \ _PAD_CFG_STRUCT(pad, \ PAD_FUNC(GPIO) | PAD_RESET(rst) | \ PAD_TRIG(OFF) | PAD_BUF(RX_DISABLE) | !!val, \ PAD_PULL(NONE) | PAD_IOSSTATE(TxLASTRxE))

参数含义在源码中都有对应定义:

参数取值来源说明
pad各 SoC 头文件中的 pad 名(如GPP_B3)community 内的 pad 偏移,见struct pad_config.pad
pullgpio_defs.h#L86-L95NONE、DN_5K、DN_20K、UP_1K、UP_5K、UP_2K、UP_20K、UP_667、NATIVE等内部上下拉档位
rstgpio_defs.h#L60-L64逻辑复位源:PWROK、DEEP、PLTRST、RSMRST(经struct reset_mapping映射为芯片集实际值)
funcPAD_CFG0_MODE_NF1…NF7或PAD_CFG0_MODE_GPIOnative function 编号;GPIO即通用模式

此外宏库还覆盖更复杂的场景,例如带中断路由的输入(PAD_CFG_GPI_APIC、PAD_CFG_GPI_SMI、PAD_CFG_GPI_SCI、PAD_CFG_GPI_NMI)、带锁定的变体(*_LOCK)、双向 GPIO(PAD_CFG_GPIO_BIDIRECT),以及专门用于未连接引脚的PAD_NC(见 gpio_defs.h#L397-L401):

/* * No Connect configuration for unconnected or unused pad. * Both TX and RX are disabled. RX disabling is done to avoid unnecessary * setting of GPI_STS and to prevent triggering the internal logic by floating * pads. */ #define PAD_NC(pad, pull) \ _PAD_CFG_STRUCT(pad, \ PAD_FUNC(GPIO) | PAD_RESET(DEEP) | \ PAD_TRIG(OFF) | PAD_BUF(TX_RX_DISABLE), \ PAD_PULL(pull) | PAD_IOSSTATE(IGNORE))

老一代 Intel SoC:mainboard_get_gpios 回调

以 Bay Trail、Braswell 为代表的老 SoC 没有上述宏库,它们的约定是:主板提供一个回调mainboard_get_gpios,返回一个struct soc_gpio对象数组,逐个定义每个 pin 的配置。

AMD SoC:struct soc_amd_gpio

部分 AMD SoC 使用struct soc_amd_gpio对象列表定义每个 pin 的寄存器值,思路与 Intel 类似,其定义见 src/soc/amd/common/block/include/amdblocks/gpio.h#L11-L16:

struct soc_amd_gpio { gpio_t gpio; uint8_t function; uint32_t control; uint32_t flags; };

对应的事件(中断)触发方式(电平/边沿、高/低有效)由flags中的GPIO_FLAG_EVENT_*位描述,flags的取值语义同样对应下文“寄存器属性”一节。

Pad 配置寄存器控制哪些属性

文档列出 GPIO 配置寄存器通常控制 8 类属性,这些属性在 Intel 公共块的 DW0/DW1/DW2 位定义中都能找到一一对应(gpio_defs.h#L10-L142):

  1. Input / Output:DW0 的PAD_CFG0_TX_DISABLE/PAD_CFG0_RX_DISABLE位(TX/RX buffer 使能),例如PAD_CFG_GPO禁用 RX buffer,PAD_CFG_GPI禁用 TX buffer;
  2. Pullups / Pulldowns:DW1 的PAD_CFG1_PULL_MASK位域,含UP_1K、DN_20K等档位;
  3. Termination:DW1 的PAD_CFG1_IOSTERM_MASK(SAME/DISPUPD/ENPD/ENPU),由PAD_CFG_TERM_GPO等宏使用;
  4. Tx / Rx Disable:即属性 1 中对应的PAD_BUF(TX_DISABLE)/PAD_BUF(RX_DISABLE);
  5. Reset signal(复位源):DW0 的PAD_CFG0_RESET_MASK(bit 31:30),逻辑值PAD_CFG0_LOGICAL_RESET_PWROK/DEEP/PLTRST/RSMRST;
  6. Native Function / IO 选择:DW0 的PAD_CFG0_MODE_MASK,值为 0 是 GPIO,1~7(4 位模式下可到 15)是 NF1~NF7/NF15;
  7. Interrupts(中断):
    • IRQ 路由:DW0 的PAD_CFG0_ROUTE_MASK(NMI/SMI/SCI/IOAPIC四种,x86 上即 APIC/SCI/SMI/NMI 路由);
    • 边沿/电平触发:PAD_CFG0_TRIG_MASK(LEVEL/EDGE_SINGLE/EDGE_BOTH/OFF);
    • 高/低有效:PAD_CFG0_RX_POL_INVERT极性反转位;
  8. Debouncing(去抖):DW2 的PAD_CFG2_DEBEN及PAD_CFG2_DEBOUNCE_8_RTC…32K_RTC档位,时长为 2^N 个 RTC 时钟周期(gpio_defs.h#L127-L142)。

配置真正落地的入口是 src/soc/intel/common/block/gpio/gpio.c 中的gpio_configure_pad()(L350-L407):按 pad 号通过soc_gpio_get_community()找到所属 community,逐 DW 读改写寄存器(先经PAD_DWx_MASK过滤并做 logical→chipset 复位值映射,再调用可被 SoC 覆写的soc_gpio_pad_config_fixup()),随后依次完成 ITSS 中断极性设置(若路由到 APIC)、host ownership 位设置、SMI/NMI/GPE 使能以及可选的 pad 锁定。

配置 pre-ramstage 的 early GPIO

coreboot 在若干时间点提供 SoC 级和主板级回调:bootblock-early、bootblock、romstage entry、pre-silicon init、pre-RAM init、post-RAM init。文档建议从bootblock_mainboard_init()入手配置 early GPIO。

以 Intel 主板为例,GPIO 配置通常声明为结构体数组:

struct pad_config { /* offset of pad within community */ int pad; /* Pad config data corresponding to DW0, DW1,.... */ uint32_t pad_config[GPIO_NUM_PAD_CFG_REGS]; };

该结构在当前仓库中的实际定义(含新增的锁定动作字段)见 src/soc/intel/common/block/include/intelblocks/gpio.h#L75-L80:

struct pad_config { gpio_t pad;/* offset of pad within community */ uint32_t pad_config[GPIO_NUM_PAD_CFG_REGS];/* Pad config data corresponding to DW0, DW1,.... */ enum gpio_lock_action lock_action; /* Pad lock configuration */ };

一个真实用例:Clevo L140CU 的 early 表只配置了早期串口(供 bootblock 日志使用)及相邻的未用引脚,见 src/mainboard/clevo/cml-u/variants/l140cu/gpio_early.c:

static const struct pad_config early_gpio_table[] = { PAD_CFG_NF(GPP_C20, NONE, DEEP, NF1), /* UART2_RXD */ PAD_CFG_NF(GPP_C21, NONE, DEEP, NF1), /* UART2_TXD */ PAD_NC(GPP_C22, UP_20K), PAD_NC(GPP_C23, UP_20K), }; void variant_configure_early_gpios(void) { gpio_configure_pads(early_gpio_table, ARRAY_SIZE(early_gpio_table)); }

其挂接位置在 src/mainboard/clevo/cml-u/bootblock.c:bootblock_mainboard_early_init()调用variant_configure_early_gpios(),与文档“从bootblock_mainboard_init()开始”的建议一致(该板把 early GPIO 放在了更早的bootblock_mainboard_early_init()回调)。

ramstage 及之后阶段的 GPIO 配置

进入 ramstage 后,主板配置其余 GPIO 以进入设备正常工作状态。做法与 bootblock 相同:创建另一个static const数组,把剩余 GPIO 寄存器编程一遍。

仍以上述 Clevo CML-U 为例,变体级完整表 src/mainboard/clevo/cml-u/variants/l140cu/gpio.c#L8-L13 覆盖了 GPD、GPP_A~GPP_H、CPU 等全部 group,并按“pad 名 / 原理图网表名”双注释,例如:

PAD_CFG_NF(GPD3, UP_20K, DEEP, NF1), /* PWRBTN# / PWR_BTN# */ PAD_CFG_GPI_APIC_LOW(GPP_B3, NONE, PLTRST), /* GPP_B3 (touchpad interrupt) */ PAD_CFG_GPI_SMI_LOW(GPP_E15, NONE, DEEP, EDGE_SINGLE), /* EC SMI# */ PAD_CFG_GPI_SCI_LOW(GPP_E16, NONE, PLTRST, LEVEL), /* EC SCI# */ PAD_NC(GPD2, UP_20K),

表末尾通过variant_configure_gpios()→gpio_configure_pads(gpio_table, ARRAY_SIZE(gpio_table))(gpio.c#L241-L244)统一应用。

与 boot 流程的挂接

文档指出该配置通常挂接在主板的struct chip_operations回调(常为enable_dev)中。从源码结构看,不同主板选择的具体回调可能不同:上述 Clevo 主板是将其注册为mainboard_ops.init(init_mainboard中调用variant_configure_gpios(),见 src/mainboard/clevo/cml-u/ramstage.c#L6-L13),而 FSP 平台的 src/mainboard/clevo/tgl-u/ramstage.c 则同时提供mainboard_silicon_init_params与init两条路径。

baseboard 表 + variant override 表

在 baseboard/variant 模型下,baseboard 提供各变体完全相同的那部分 GPIO 配置,并提供一种让变体覆盖 baseboard 配置的机制——通常是两张表:baseboard 表与变体的 override 表。其底层实现即gpio_configure_pads_with_override()(src/soc/intel/common/block/gpio/gpio.c#L439-L452):

void gpio_configure_pads_with_override(const struct pad_config *base_cfg, size_t base_num_pads, const struct pad_config *override_cfg, size_t override_num_pads) { size_t i; const struct pad_config *c; for (i = 0; i < base_num_pads; i++) { c = gpio_get_config(base_cfg + i, override_cfg, override_num_pads); gpio_configure_pad(c); } }

行为语义(来自 intelblocks/gpio.h#L203-L217 的注释):按 baseboard 表逐个配置 pad;若某 pad 在 override 表中出现,则改用 override 的配置;仅存在于 override 表而 baseboard 表没有的 pad 会被忽略——变体只能修改、不能凭空新增 baseboard 的引脚配置。

未连接与未使用引脚(No Connect)的处理

文档给出了一系列处理 unconnected/unused pad 的工程原则:

  1. 通用电子学原则:建议把未连接的 GPIO 通过“设为输出方向 + 外部/内部上下拉”绑定到确定电平(VCC 或 GND),避免引脚悬空导致 EMI、以及逻辑持续翻转带来的额外功耗;

  2. Sunrise Point 之后的 Intel PCH:未连接 GPIO 的“termination”并非必须,只要置位GPIORXDIS(即PAD_CFG0_RX_DISABLE)禁用输入 buffer,pad 即从内部逻辑断开。所有默认 GPIO 模式的 pad 均已置该位。主板上可用宏PAD_NC(pad, NONE)显式把某 pad 配置为 unconnected;

  3. 无原理图时的经验判据:若厂商把某 pad 配成GPIORXDIS=1、GPIOTXDIS=1且带内部上下拉,可以推断它是未连接或未使用的 pad。此时建议保留该内部上下拉(外部电路可能依赖它);

  4. 默认为 native function(输入+输出)的未连接 pad:通常无需显式配成带GPIORXDIS的 GPIO,但为了清晰和文档作用,也可以照样使用宏标注;

  5. 例外——需要外部上拉的 native 输入 pad(按 PDG 规定):

    • eDP_HPD
    • SMBCLK/SMBDATA
    • SML0CLK/SML0DATA/SML0ALERT
    • SATAGP*

    若板子设计正确,板载外部上拉会接管这些 pad,软件无需额外动作,使用PAD_NC(pad, NONE)可作为文档化手段;若因设计缺陷缺少外部上拉,仍应配置PAD_NC(pad, NONE)将其内部断开。

PAD_NC的实现印证了这些原则:它同时禁用 TX/RX buffer(PAD_BUF(TX_RX_DISABLE))、关闭中断触发(PAD_TRIG(OFF))、复位源固定为DEEP,并通过PAD_IOSSTATE(IGNORE)忽略 IOSTANDBY(见 gpio_defs.h#L391-L401),注释明确说明禁用 RX 是为了避免悬空 pad 误置GPI_STS并触发内部逻辑。

常见坑(Gotchas)

把“设计为输入”的引脚配成输出

这是文档特别警告的第一号危险:若某引脚在设计上就是输入,你却把它配置为输出,存在短路对端器件的真实风险,可能导致灾难性故障——最坏情况是烧坏主板。编写 pad 表前务必核对原理图上的信号方向。

Intel PCH:四种 Pad Reset 与锁定寄存器的“隐性默认值”

按 Skylake 起的 Intel PCH EDS,GPIO PAD 寄存器支持四种 reset 类型,其在不同复位事件下是否复位如下(GPP 为 GPP_A~G 类 group,GPD 为 GPD group;“N”表示不被该复位影响,“Y”表示会复位):

Pad Reset ConfigPlatform ResetGPPGPD
00 — Power Good(GPP: RSMRST,GPD: DSW_PWROK)Warm ResetNN
Cold ResetNN
S3/S4/S5NN
Global ResetNN
Deep SxYN
G3YY
01 — DeepWarm ResetYY
Cold ResetYY
S3/S4/S5NN
Global ResetYY
Deep SxYY
G3YY
10 — Host Reset/PLTRSTWarm ResetYY
Cold ResetYY
S3/S4/S5YY
Global ResetYY
Deep SxYY
G3YY
11 — Resume Reset(GPP: Reserved,GPD: RSMRST)Warm Resetn/aN
Cold Resetn/aN
S3/S4/S5n/aN
Global Resetn/aN
Deep Sxn/aY
G3n/aY

由此引出文档强调的一个隐蔽问题:每个 GPIO Community 都有一个 Pad Configuration Lock 寄存器,可锁定 GPP 的 PAD 配置寄存器中的特定字段;而Pad Config Lock 寄存器自身的复位类型被硬编码为 Power Good,且不能用 PAD 配置寄存器 DW0 的 PadRstCfg 字段改变。后果是:

  • 若某 GPP 的 Pad Reset Config(DW0 bit 31:30)被配置成与Power Good不同的值(例如PLTRST或DEEP),复位后会出现“pad 配置回到了默认值、但 lock 位仍保持锁定”的不一致状态,阻止软件重新编程该 GPP;
  • 软件也不应依赖 GPIO 会被 PLTRST# 或进入 Sx 所复位。

因此,按 Intel GPIO BIOS Writers Guide(BWG)的建议:对带锁定的 GPP,应把其 Pad Reset Configuration 也设为Power Good,保证 pad 配置与 lock 位始终同步、同时被复位。在源码中这对应*_LOCK系列宏的行为——例如PAD_CFG_GPO_LOCK/PAD_CFG_GPI_LOCK等都将rst参数固定为PWROK(见 gpio_defs.h#L275-L281、L311-L316),正是这一建议的落地。

顺带说明:锁定操作本身(gpio_lock_pad()/gpio_lock_pads(),gpio.c#L661-L731)有环境约束——早期阶段(ENV_ROMSTAGE_OR_BEFORE)与 vboot recovery 模式会跳过锁定;批量锁定的gpio_lock_pads()只能在 SMM 中调用(需先 unhide P2SB),且 FSP-S 会解锁所有 GPIO pad,这是“锁定为何必须在 SMM 时机做”的实现背景。

Soft Straps:不要相信 datasheet 里的默认值

部分 pad 的默认模式可被厂商通过Intel Flash Image Tool(FIT)中的 soft straps 改变:在非服务器 SoC 上可为某些 pad 选择 native function 或 GPIO 模式,在服务器 SoC 上则绝大多数 pad 都可控制。因此文档的结论性建议是:始终显式配置每一个 pad,而不是依赖 datasheet 中描述的默认值——那些默认值未必反映厂商实际写入的内容。

这也解释了为何真实主板的gpio.c里连未使用的 pad 也会逐条列出PAD_NC(...):把全表写全,等于用软件配置覆盖一切上游不确定性。

已知 pad 问题:LPC_CLKRUNB 阻塞 S0ix

当主板使用eSPI时,承载LPC_CLKRUNB的 pad 必须设置为GPIO 模式;其他引脚设置(Rx/Tx 使能、上下拉等)会被忽略。若该引脚留在 native 模式,LPC Controller 会保持唤醒状态,阻止 S0ix(package C-state)进入。文档注明该问题至少在Apollolake和Geminilake上已知。

小结:一块主板 GPIO 配置的完整路径

综合文档与仓库源码,一次完整的 GPIO 配置工作可归纳为:

  1. 查阅 SoC datasheet/PDG,确定每个 pad 的功能、方向与是否悬空;
  2. 按 boot stage 拆分:内存 strapping、早期串口等“early” pin 放入 bootblock/romstage 回调(如bootblock_mainboard_init()或更早的bootblock_mainboard_early_init());其余放入 ramstage 的chip_operations回调;
  3. 使用struct pad_config数组 +PAD_CFG_*/PAD_NC宏(Intel 公共块)或struct soc_gpio/struct soc_amd_gpio(老 Intel/AMD SoC)描述配置;baseboard/variant 架构下用 baseboard 表 +gpio_configure_pads_with_override()处理变体差异;
  4. 悬空 pad 一律显式标注PAD_NC(pad, pull),保留厂商已设置的内部上下拉;对 PDG 要求外部上拉的 native 输入 pad 单独留意;
  5. 复核高危项:方向配反的短路风险、带 lock 的 GPP 必须用Power Good复位源、eSPI 板上的LPC_CLKRUNB必须 GPIO 化、以及任何 pad 都不要依赖“datasheet 默认值”(Soft Straps)。

参考起点:官方文档 Documentation/getting_started/gpio.md、Intel 公共块实现 src/soc/intel/common/block/gpio/gpio.c 与宏定义 src/soc/intel/common/block/include/intelblocks/gpio_defs.h、结构定义 src/soc/intel/common/block/include/intelblocks/gpio.h、AMD 块 src/soc/amd/common/block/include/amdblocks/gpio.h、真实主板示例 src/mainboard/clevo/cml-u。

  • 固件
  • 嵌入式
  • 操作系统
  • 驱动开发

【免费下载链接】coreboot

Read-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We don't handle Pull Requests.

项目地址:https://gitcode.com/gh_mirrors/co/coreboot
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询