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,则需检查信号完整性和均衡设置。

Layerscape PCIe 控制器初始化与 LTSSM 状态关系 软件初始化路径 1. 使能时钟与释放复位 2. 配置 DBI 与 BAR 3. 设置 ATU 地址窗口 4. 启动链路训练 5. 枚举下游设备 6. 分配总线号与资源 LTSSM 状态机 DETECT POLLING CONFIG L0 RECOVERY 错误恢复 寄存器观察点 DBI: Device ID / BAR 0x03400000 + 偏移 Port Logic: LTSSM_STATE 链路状态与错误计数 ATU: 出站/入站窗口 CPU 地址 ↔ PCIe 地址 CFG_ADDR / CFG_DATA 下游配置空间访问 PEX_CTRL: 复位控制 热复位与链路复位 错误状态与中断标志 触发 反映 注:地址与状态字段为 LS1046A 典型示例,实际以芯片参考手册为准

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 或训练失败。

调试要点与常见问题

实际调试中,建议按以下顺序排查:

  1. 确认参考时钟存在且频率正确(100MHz ±300ppm)。
  2. 读取 LTSSM_STATE,判断卡在哪个状态。
  3. 检查 DBI 中的 LINK_CAP 和 LINK_CTRL,确认速率和宽度配置。
  4. 检查 ATU 窗口是否覆盖了目标地址范围。
  5. 若链路已到 L0 但枚举失败,检查 CFG_ADDR 访问是否返回 0xFFFFFFFF。

一个典型现象是:LTSSM 显示 L0,但配置空间读取全为 0xFFFFFFFF。这通常意味着 ATU 出站窗口未正确配置,或者下游设备未上电。此时应优先检查 ATU 的 CPU_ADDR_BASE 和 PCIE_BUS_ADDR 是否匹配,以及下游设备的电源和复位状态。

调试 PCIe 链路时,LTSSM 状态是最直接的“体温计”。先看状态,再看寄存器,最后看配置空间,可以避免在错误的方向上浪费时间。