3. Layerscape PCIe控制器寄存器与初始化流程
Layerscape 系列处理器的 PCIe 控制器并不是一个单一外设,而是由 PCIe 控制器核心、SerDes 物理层、CCSR 寄存器空间、配置空间访问通道以及中断/错误上报逻辑共同组成的复合体。理解它的第一步,是把“寄存器映射—复位与时钟—LTSSM 链路训练—软件初始化”这条链条拆开来看:寄存器决定软件能控制什么,复位与时钟决定控制器何时可用,LTSSM 决定链路能否真正建立,而 U-Boot/Linux 的代码路径则决定这些动作在启动流程中的先后顺序。
寄存器映射:从 CCSR 到 PCIe 配置空间
在 Layerscape 中,PCIe 控制器寄存器通常位于 CCSR(Central Complex System Register)地址空间内,每个控制器占用一段连续的 64KB 或 128KB 窗口。以 LS1046A 为例,PCIe1 的 DBI(DesignWare Bridge Interface)基址常位于 0x03400000,PCIe2 位于 0x03500000,PCIe3 位于 0x03600000。这些地址在芯片参考手册的“PCI Express Controller”章节中给出,软件通过 ioremap 或直接物理地址访问。
控制器内部寄存器可分为几类:
- DBI 寄存器:包括设备 ID、厂商 ID、命令/状态、BAR 配置、链路能力与链路控制等,用于配置控制器自身作为 RC 或 EP 的行为。
- Port Logic 寄存器:控制 LTSSM、链路训练参数、错误上报、中断使能等,是调试链路问题的关键。
- 配置空间访问寄存器:RC 模式下通过
CFG_ADDR和CFG_DATA间接访问下游设备的配置空间。 - ATU(Address Translation Unit)寄存器:负责 CPU 地址与 PCIe 总线地址之间的转换,出站和入站窗口分别配置。
需要特别注意的是,DBI 寄存器与配置空间头部的前 64 字节在 RC 模式下是重叠的,但访问方式不同:DBI 访问不产生总线事务,而配置空间访问会通过 PCIe 链路发出配置读写 TLP。调试时若发现 BAR 写入无效,应优先确认是写到了 DBI 还是配置空间。
复位与时钟:控制器可用的前提
PCIe 控制器的复位分为三类:上电复位、热复位和链路复位。上电复位由芯片复位控制器统一释放,软件通常无需干预;热复位通过 PEX_CTRL 寄存器中的复位位触发,用于重新初始化控制器;链路复位则通过 LTSSM 进入 DETECT 状态实现,用于重新训练链路。
时钟方面,PCIe 控制器需要两类时钟:核心时钟(通常来自平台 PLL)和参考时钟(100MHz 差分,来自外部晶振或 SerDes PLL)。参考时钟的稳定性直接决定链路能否训练成功。在 U-Boot 中,SerDes 的参考时钟配置通常在 board_init 或 serdes_init 中完成,若参考时钟缺失或频率偏差过大,LTSSM 会卡在 POLLING 或 DETECT 状态。
LTSSM 状态机:链路训练的核心
LTSSM(Link Training and Status State Machine)是 PCIe 物理层的核心状态机,负责链路初始化、训练和状态维护。其关键状态包括:
- DETECT:检测接收端是否存在,通过测量差分对上的终端电阻判断。
- POLLING:发送 TS1/TS2 有序集,协商链路宽度和速率。
- CONFIG:确定链路号和通道映射,完成 lane 到 lane 的对应。
- L0:正常工作状态,TLP 和 DLLP 可以正常传输。
- RECOVERY:链路出现错误或需要重新协商时进入,尝试回到 L0。
在调试中,读取 Port Logic 寄存器中的 LTSSM_STATE 字段即可知道当前状态。若长期停留在 DETECT,通常是参考时钟或差分对连接问题;若停留在 POLLING,则可能是对端设备未上电或速率不匹配;若反复进入 RECOVERY,则需检查信号完整性和均衡设置。
U-Boot 下的初始化代码路径
在 U-Boot 中,PCIe 初始化通常由 board_init 调用 pci_init 触发,最终进入 fsl_pcie_init 或 ls_pcie_probe。以 LS1046A 为例,关键步骤包括:
/* 典型 U-Boot PCIe 初始化片段(示意) */
void ls_pcie_init(void)
{
/* 1. 使能 SerDes 参考时钟 */
setbits_le32(&scfg->serdes_cfg, SERDES_REFCLK_EN);
/* 2. 释放 PCIe 控制器复位 */
setbits_le32(&scfg->pex_ctrl, PEX_CTRL_RST_RELEASE);
/* 3. 配置 DBI 寄存器 */
dbi_write32(PCI_HEADER_TYPE, PCI_HEADER_TYPE_NORMAL);
dbi_write32(PCI_CLASS_REVISION, PCI_CLASS_BRIDGE_PCI);
/* 4. 设置 ATU 出站窗口 */
atu_outbound_config(0, CPU_ADDR_BASE, PCIE_BUS_ADDR, WINDOW_SIZE);
/* 5. 等待 LTSSM 进入 L0 */
while (ltssm_state() != LTSSM_L0)
udelay(100);
/* 6. 枚举下游设备 */
pci_scan_bus();
}
这段代码的关键在于第 5 步:软件必须等待 LTSSM 进入 L0 后才能进行配置空间访问,否则配置读写 TLP 会因链路未就绪而超时。实际调试中,若 ltssm_state() 长时间不返回 L0,应检查参考时钟、差分对连接和 SerDes 配置。
Linux 下的初始化代码路径
Linux 内核中,Layerscape PCIe 控制器由 drivers/pci/controller/dwc/pci-layerscape.c 驱动,基于 DesignWare DWC 框架。初始化流程为:
ls_pcie_probe:匹配设备树节点,获取寄存器基址、时钟、复位和中断资源。dw_pcie_host_init:初始化 RC 模式,配置 DBI、ATU 和中断。ls_pcie_host_init:Layerscape 特有的初始化,包括设置PEX_CTRL和等待链路训练。pci_host_probe:枚举总线,分配资源,注册 PCI 主机桥。
与 U-Boot 不同,Linux 驱动会通过设备树获取 num-lanes、max-link-speed 等参数,并在 ls_pcie_host_init 中写入 Port Logic 寄存器。若设备树中 num-lanes 配置为 4 而实际硬件只连接了 2 条 lane,LTSSM 会在 CONFIG 状态反复尝试,最终可能降级到 x2 或训练失败。
调试要点与常见问题
实际调试中,建议按以下顺序排查:
- 确认参考时钟存在且频率正确(100MHz ±300ppm)。
- 读取
LTSSM_STATE,判断卡在哪个状态。 - 检查 DBI 中的
LINK_CAP和LINK_CTRL,确认速率和宽度配置。 - 检查 ATU 窗口是否覆盖了目标地址范围。
- 若链路已到 L0 但枚举失败,检查
CFG_ADDR访问是否返回0xFFFFFFFF。
一个典型现象是:LTSSM 显示 L0,但配置空间读取全为 0xFFFFFFFF。这通常意味着 ATU 出站窗口未正确配置,或者下游设备未上电。此时应优先检查 ATU 的 CPU_ADDR_BASE 和 PCIE_BUS_ADDR 是否匹配,以及下游设备的电源和复位状态。