4. GPIO与PinMux配置:UFS相关引脚功能选择、上下拉、驱动强度、代码实现
4.1 原理概述
UFS控制器与UFS设备之间通过高速差分信号和控制信号进行通信。在SoC层面,这些信号必须通过多路复用器(PinMux)从通用I/O(GPIO)配置为专用功能。错误的PinMux配置会导致UFS链路无法训练、初始化失败或传输错误。
GPIO与PinMux配置的核心包括三个层次:
- 功能选择:将特定引脚从GPIO状态切换为UFS、I2C(用于UFS供电协商)或复位等专用功能。
- 电气特性:包括上下拉电阻、驱动强度(Drive Strength)、压摆率(Slew Rate)和开漏/推挽输出类型。
- 电源域:确认引脚电压域(e.g., 1.8V或3.3V)与UFS PHY要求匹配。
调试提示:大多数量产板UFS连接失败,根本原因不是控制器驱动问题,而是SoC外围引脚复用配置有误。建议在Target Config(TARGET_xxx)中优先确认所有UFS相关GPIO的TLMM(Top-Level Mode Multiplex)寄存器初始值。
4.2 UFS引脚信号清单
UFS硬件接口涉及以下关键引脚,每个引脚都需要在XBL早期(Timer/GPIO初始化阶段)完成复用配置:
| 信号名 | 方向 | 功能说明 | 典型电气要求 |
|---|---|---|---|
| UFS_REF_CLK | 输出 | UFS参考时钟(19.2MHz / 26MHz) | 1.8V,驱动强度>4mA |
| UFS_RST_N | 输出 | UFS设备硬件复位,低电平有效 | 1.8V,内置上拉 |
| UFS0/1_DP/DN | 双向 | MIPI M-PHY差分数据线 | H/S模式,AC耦合 |
| UFS_EN (或REGUFS_EN) | 输出 | UFS供电使能控制 | 1.8V/3.3V,推挽输出 |
| I2C_SCL/SDA | 双向 | 用于UFS VCCQ/VCC电压协商 | 1.8V,开漏+上拉 |
4.3 PinMux配置实现流程
- 查表确定对应GPIO号:根据芯片数据手册或TLMM映射表,找到UFS功能对应的GPIO BANK和引脚偏移。
- 配置功能选择寄存器:写入TLMM_PER_EN或GPIOx_CFG[FUNC_SEL]字段,将引脚切换为UFS模式。
- 配置上下拉:设置PULL_UP_EN/PULL_DOWN_EN位;UFS控制信号(RST_N)通常启用内部弱上拉,差分对通常不启用内部上下拉。
- 配置驱动强度:写入DRV_STR字段(2/4/6/8/12/16mA等档位);参考时钟和复位信号建议使用中等驱动强度(6~8mA)。
- 确认输出类型:I2C必须设为开漏(Open-Drain),其余信号设为推挽。
- 系统锁定:部分SoC写入TLMM后需置位LOCK寄存器防止Bootloader后期被意外篡改。
常见错误:UFS差分对DP/DN的AC耦合电容在SoC侧靠近PHY放置,不要在GPIO配置中为该对引脚启用内部上下拉,否则会破坏M-PHY端接阻抗(~50Ω),导致链路训练失败。
4.4 代码实现(Qualcomm平台示例)
以下代码片段展示在XBL平台代码中配置UFS相关GPIO的典型做法。以Qualcomm QCM6490为例,TLMM基地址通过芯片系统模块映射。
头文件定义:
// tlmm_ufs_cfg.h
#ifndef TLMM_UFS_CFG_H
#define TLMM_UFS_CFG_H
#define TLMM_BASE 0x0100F000
#define GPIO_UFS_REF_CLK (GPIO_BASE + 0x14 * 4) // 假设GPIO 20
#define GPIO_UFS_RST_N (GPIO_BASE + 0x16 * 4) // GPIO 22
#define GPIO_UFS_EN (GPIO_BASE + 0x18 * 4) // GPIO 24
// CFG寄存器偏移(TLMMv2风格)
#define CFG_OFFSET 0x00
#define INOUT_OFFSET 0x04
#define FUNC_SEL_UFS 0x02 // 功能代码:UFS模式
#define DRV_STR_8MA 0x03 // 8mA档位
#define PULL_UP_EN 0x01 // 使能内部上拉
#endif
实际配置代码:
// tlmm_ufs_init.c
#include "tlmm_ufs_cfg.h"
#include "hwio.h" // 提供 writel/readl 寄存器访问
void tlmm_configure_ufs_pins(void)
{
uint32_t reg_val;
// 1. 配置UFS参考时钟引脚(功能选择+驱动强度,无上拉)
reg_val = readl(GPIO_UFS_REF_CLK + CFG_OFFSET);
reg_val &= ~0x7F; // 清除低7位(功能+驱动+上拉)
reg_val |= (FUNC_SEL_UFS << 0); // FUNC_SEL位于bit[2:0]
reg_val |= (DRV_STR_8MA << 4); // 驱动强度位于bit[6:4]
reg_val &= ~(1 << 3); // 不启用上拉 (bit3 = 0)
writel(reg_val, GPIO_UFS_REF_CLK + CFG_OFFSET);
// 2. 配置UFS复位引脚(功能选择+内部上拉+中等驱动)
reg_val = readl(GPIO_UFS_RST_N + CFG_OFFSET);
reg_val &= ~0x7F;
reg_val |= (FUNC_SEL_UFS << 0);
reg_val |= (DRV_STR_8MA << 4);
reg_val |= (PULL_UP_EN << 3); // 启用内部上拉
writel(reg_val, GPIO_UFS_RST_N + CFG_OFFSET);
// 3. 配置UFS供电使能(功能选择+推挽输出,初始低电平)
reg_val = readl(GPIO_UFS_EN + CFG_OFFSET);
reg_val &= ~0x7F;
reg_val |= (FUNC_SEL_UFS << 0);
reg_val |= (DRV_STR_8MA << 4);
reg_val &= ~(1 << 3); // 不上拉
writel(reg_val, GPIO_UFS_EN + CFG_OFFSET);
// 设定输出为低电平(禁止供电)
writel(0x00, GPIO_UFS_EN + INOUT_OFFSET);
// 4. 锁定TLMM(防止后续误修改)
writel(0x1, TLMM_BASE + 0x1000); // TLMM LOCK寄存器
}
实现要点:
- 务必在XBL UFS核心时钟初始化 之前 调用
tlmm_configure_ufs_pins()。 - I2C引脚应单独处理为开漏(OD)模式:写GPIOx_CFG寄存器中的OD_EN位(bit 9)。
- 驱动强度不要超过PHY芯片规格,一般参考时钟和复位不超过8mA,普通GPIO不超过4mA。
4.5 调试与验证
完成配置后,可通过寄存器回读和逻辑分析仪验证引脚状态:
- 回读TLMM寄存器:通过JTAG或XBL的early serial dump,确认目标GPIO的FUNC_SEL字段是否为UFS代码。
- 测量引脚电平:UFS_RST_N应在XBL阶段被拉高(内部上拉),UFS_EN应保持低电平直到UFS供电就绪。
- Sysfs接口(Linux侧辅助验证):在后续操作系统启动后,通过
/sys/kernel/debug/gpio或pinctrl工具查看是否已正确占用。
调试命令速查:
通过XBL串口打印TLMM寄存器值:
观察UFS_RST_N电平:若为低电平持续>1秒,检查内部上拉是否使能或外部上拉电阻是否焊接。
通过XBL串口打印TLMM寄存器值:
APR> register_read 0x0100F054(举例地址,替换为实际GPIO偏移)。观察UFS_RST_N电平:若为低电平持续>1秒,检查内部上拉是否使能或外部上拉电阻是否焊接。
4.6 性能与稳定性优化
- 分组配置:将UFS相关引脚配置集中在同一个.c文件中,避免散落在不同的平台初始化模块中,方便审计。
- 寄存器缓存:在频繁访问的TLMM配置场景中,使用局部缓存变量而非重复readl/writel,减少对低速APB总线的访问延迟。
- 静电防护:UFS参考时钟引脚在PCB设计时应串联22Ω电阻衰减振铃;驱动强度不宜超过10mA,否则引入EMI干扰。
- 避免浮动:长期未使用但未屏蔽的UFS功能引脚应配置为GPIO输入+下拉,防止漏电和电平跳变。
平台差异提示:不同SoC的TLMM寄存器布局不同(例如QTI TLMMv1 vs TLMMv2),访问偏移和字段位宽差异大。务必以当前芯片的TRM/PinMux Excel表格为准,不可直接套用其他平台的偏移值。