Linux PCIe_Driver

4 阅读32分钟

PCIe_Driver


目录

  1. 学习方法与调试准备
  2. PCI 子系统核心文件详解
  3. PCI 设备扫描完整流程 (逐层解析)
  4. 驱动匹配机制深度剖析
  5. Host 控制器驱动完全解析 (DesignWare)
  6. i.MX6 PCIe 驱动完整源码分析
  7. PCI 配置空间访问机制详解
  8. ATU 地址转换单元完全解析
  9. MSI/MSI-X 中断机制完全掌握
  10. PCIe 链路训练与状态检测
  11. 电源管理与高级特性
  12. 调试方法与实战技巧

1. 学习方法与调试准备

1.1 核心学习路径

┌─────────────────────────────────────────────────────────────────────────────┐
│                          PCI 驱动学习全景图                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    第一阶段: 理解整体架构                              │   │
│  │  1.1 什么是 PCI/PCIe?                                             │   │
│  │  1.2 PCI 总线拓扑结构                                              │   │
│  │  1.3 配置空间作用                                                  │   │
│  │  1.4 Linux PCI 子系统角色                                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│                                    ▼                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    第二阶段: 设备扫描流程                             │   │
│  │  2.1 Host 控制器初始化                                             │   │
│  │  2.2 总线扫描入口                                                  │   │
│  │  2.3 pci_dev 创建过程                                              │   │
│  │  2.4 设备初始化                                                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│                                    ▼                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    第三阶段: 驱动匹配绑定                             │   │
│  │  3.1 设备添加到总线                                                 │   │
│  │  3.2 驱动匹配算法                                                  │   │
│  │  3.3 probe 调用流程                                                │   │
│  │  3.4 设备使能                                                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│                                    ▼                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    第四阶段: 底层机制深入                             │   │
│  │  4.1 配置空间访问 (IO 端口 vs ECAM)                               │   │
│  │  4.2 ATU 地址转换                                                  │   │
│  │  4.3 MSI 中断机制                                                  │   │
│  │  4.4 电源管理                                                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

1.2 源码追踪工具准备

# 推荐使用 cscope 建立索引
cd /path/to/linux-4.9.88
cscope -Rbq

# 或者使用 grep 定位
grep -rn "pci_scan_root_bus" drivers/pci/
grep -rn "pci_bus_match" drivers/pci/
grep -rn "pci_match_device" drivers/pci/

1.3 调试开关启用

// 在代码中添加调试
#define DEBUG

// 或者运行时启用
echo "file drivers/pci/probe.c +p" > /sys/kernel/debug/dynamic_debug/control
echo "file drivers/pci/pci-driver.c +p" > /sys/kernel/debug/dynamic_debug/control

// 查看日志
dmesg | grep -E "pci|PCIe" | tail -50

2. PCI 子系统核心文件详解

2.1 文件层级关系

drivers/pci/
├── pci.h                    # PCI 核心头文件 (定义 pci_dev, pci_driver 等)
├── pci.c                    # PCI 核心功能 (初始化、电源管理)
├── probe.c                  # ★ 设备扫描与探测 (最重要)
├── pci-driver.c             # ★ 驱动匹配与绑定
├── bus.c                    # 总线管理
├── access.c                 # ★ 配置空间访问接口
├── setup-bus.c              # ★ 资源分配
├── setup-irq.c              # 中断设置
├── irq.c                    # 中断路由
├── msi.c                    # ★ MSI 中断
├── quirks.c                 # 设备特殊处理 (bug workaround)
├── host-bridge.c            # Host Bridge 管理
├── of.c                     # OF (设备树) 支持
├── pci-acpi.c               # ACPI 支持
├── pci-sysfs.c              # sysfs 接口
├── proc.c                   # /proc 接口
└── pci-label.c              # 设备标签

drivers/pci/host/                          # ★ Host 控制器驱动
├── pci-imx6.c                            # i.MX6/7/8 PCIe
├── pcie-designware.c                     # ★ DesignWare 通用驱动
├── pcie-designware.h                      # DesignWare 头文件
├── pcie-rockchip.c                        # Rockchip RK3399
├── pcie-rcar.c                            # Renesas R-Car
├── pci-tegra.c                            # NVIDIA Tegra
└── pci-host-common.c                      # 通用 Host 初始化

drivers/pci/pcie/                          # PCIe 高级功能
├── aspm.c                                 # Active State Power Management
├── pme.c                                  # Power Management Events
└── aer/                                   # Advanced Error Reporting
    ├── core.c
    ├── err_global.c
    └── err.c

2.2 关键数据结构位置

数据结构定义位置用途
struct pci_devinclude/linux/pci.h:700+PCI 设备描述符
struct pci_driverinclude/linux/pci.h:900+PCI 驱动描述符
struct pci_businclude/linux/pci.h:400+PCI 总线描述符
struct pci_opsinclude/linux/pci.h:350+配置空间访问回调
struct pci_device_idinclude/linux/pci.h:200+设备 ID 表条目
struct pcie_portpcie-designware.hDesignWare 端口

3. PCI 设备扫描完整流程 (逐层解析)

3.1 整体调用流程图

┌──────────────────────────────────────────────────────────────────────────────┐
│                        PCI 设备扫描完整调用链                                 │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【阶段1: Host 控制器初始化】                                                │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ platform_driver_register(&imx_pcie_driver)                          │   │
│  │       │                                                               │   │
│  │       ▼                                                               │   │
│  │ imx_pcie_probe()                                                    │   │
│  │       │                                                               │   │
│  │       ├── imx_pcie_parse_dt()        // 解析设备树                  │   │
│  │       ├── imx_pcie_clk_enable()      // 使能时钟                   │   │
│  │       ├── imx_pcie_init_phy()        // 初始化 PHY                 │   │
│  │       ├── imx_pcie_deassert_core_reset()  // 复位                  │   │
│  │       │                                                               │   │
│  │       └── dw_pcie_host_init()  ◄─────────── 核心入口               │   │
│  │               │                                                       │   │
│  │               ├── of_pci_get_host_bridge_resources()  // 解析资源  │   │
│  │               ├── devm_ioremap()              // 映射寄存器         │   │
│  │               ├── irq_domain_add_linear()    // MSI 初始化         │   │
│  │               │                                                       │   │
│  │               └── pci_scan_root_bus()  ◄────────── 扫描开始         │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                    │                                          │
│  【阶段2: 总线扫描】        ▼                                                │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ pci_scan_root_bus()                                                │   │
│  │       │                                                               │   │
│  │       └── pci_scan_root_bus_msi()                                   │   │
│  │               │                                                       │   │
│  │               ├── pci_create_root_bus()    // 创建根总线            │   │  │               │                                                       │   │
│  │               └── pci_scan_child_bus()  ◄────────── 递归扫描        │   │
│  │                       │                                               │   │
│  │                       ├── for(devfn=0; devfn<0x100; devfn+=8)       │   │
│  │                       │     │                                               │   │
│  │                       │     └── pci_scan_slot(bus, devfn)            │   │
│  │                       │             │                                   │   │
│  │                       │             ├── pci_scan_single_device()     │   │
│  │                       │             │     │                           │   │
│  │                       │             │     ├── pci_get_slot()         │   │
│  │                       │             │     ├── pci_scan_device()      │   │
│  │                       │             │     │     │                    │   │
│  │                       │             │     │     ├── pci_bus_read_dev_vendor_id()  // 读 Vendor ID
│  │                       │             │     │     ├── pci_alloc_dev()              // 分配 pci_dev
│  │                       │             │     │     ├── pci_setup_device()         // 初始化设备
│  │                       │             │     │     │    ├── 读 Class/Revision
│  │                       │             │     │     │    ├── 读 BAR
│  │                       │             │     │     │    ├── 读 IRQ
│  │                       │             │     │     │    └── pci_fixup_device()
│  │                       │             │     │     └── 返回 pci_dev
│  │                       │             │     │                               │   │
│  │                       │             │     └── pci_device_add()           │   │
│  │                       │             │           │                         │   │
│  │                       │             │           └── device_add()         │   │
│  │                       │             │               │                     │   │
│  │                       │             │               ▼                     │   │
│  │                       │             └── (继续扫描下一个功能)             │   │
│  │                       │                                                       │   │
│  │                       ├── pcibios_fixup_bus()     // 架构修复             │   │
│  │                       │                                                       │   │
│  │                       └── for(pass=0; pass<2; pass++)                     │   │
│  │                             │                                               │   │
│  │                             └── pci_scan_bridge()  // 扫描 PCI 桥背后的总线│   │
│  │                                                                           │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                    │                                          │
│  【阶段3: 设备添加与驱动匹配】  ▼                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ pci_device_add()                                                    │   │
│  │       │                                                               │   │
│  │       └── device_add()                                              │   │
│  │               │                                                       │   │
│  │               └── bus_probe_device()                                │   │
│  │                       │                                               │   │
│  │                       └── device_attach()                            │   │
│  │                               │                                       │   │
│  │                               ├── for each driver on pci_bus_type:  │   │
│  │                               │                                       │   │
│  │                               └── pci_bus_match()  ◄── 匹配检查      │   │
│  │                                       │                               │   │
│  │                                       ├── pci_match_device()        │   │
│  │                                       │     │                        │   │
│  │                                       │     ├── driver_override 检查  │   │
│  │                                       │     ├── 动态 ID 匹配         │   │
│  │                                       │     └── 静态 ID 表匹配      │   │
│  │                                       │           │                  │   │
│  │                                       │           └── pci_match_one_device()
│  │                                       │                │               │   │
│  │                                       │                └── Vendor/Device/Class 匹配
│  │                                       │                                   │   │
│  │                                       └── if (匹配) → pci_device_probe()
│  │                                                               │           │
│  │                                                               ▼           │
│  │                                                    ┌─────────────────────┐│
│  │                                                    │ __pci_device_probe()││
│  │                                                    │     │               ││
│  │                                                    │     ├── pci_match_device()
│  │                                                    │     └── pci_call_probe()
│  │                                                    │           │          ││
│  │                                                    │           ▼          ││
│  │                                                    │    drv->probe()  ◄──┘│
│  │                                                    │    (具体驱动初始化)   ││
│  │                                                    └─────────────────────┘│
│  │                                                                              │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

3.2 源码详细分析: pci_scan_root_bus()

位置: drivers/pci/probe.c:2386

/**
 * pci_scan_root_bus - 创建并扫描 PCI 根总线
 * @parent: 父设备 (通常是 host bridge device)
 * @bus: 根总线号
 * @ops: 配置空间访问回调
 * @sysdata: 传递给 ops 的私有数据
 * @resources: 地址资源列表
 * 
 * 返回: 扫描后的 pci_bus 结构
 */
struct pci_bus *pci_scan_root_bus(struct device *parent, int bus,
        struct pci_ops *ops, void *sysdata, struct list_head *resources)
{
    return pci_scan_root_bus_msi(parent, bus, ops, sysdata, resources, NULL);
}
EXPORT_SYMBOL(pci_scan_root_bus);

调用链追踪:

pci_scan_root_bus()
    │
    └── pci_scan_root_bus_msi()  [probe.c:2350]
            │
            ├── list_splice_init(&resources, &res)  // 初始化资源列表
            │
            ├── pci_create_root_bus()  [probe.c:2195]
            │       │
            │       ├── pci_alloc_bus()              // 分配总线结构
            │       ├── bus->ops = ops               // 设置操作回调
            │       ├── bus->sysdata = sysdata       // 私有数据
            │       ├── pci_bus_add_bus()            // 添加到总线列表
            │       ├── pcibios_root_bridge_prepare() // 平台特定准备
            │       └── pci_create_bridge()          // 创建 host bridge
            │
            └── pci_scan_child_bus()  ◄─────────── 递归扫描开始 [2103]

3.3 源码详细分析: pci_scan_child_bus()

位置: drivers/pci/probe.c:2103

/**
 * pci_scan_child_bus - 递归扫描总线上的所有设备
 * @bus: 要扫描的 PCI 总线
 * 
 * 扫描流程:
 * 1. 扫描所有设备/功能 (devfn 0-255)
 * 2. 处理 SR-IOV 虚拟功能
 * 3. 执行架构修复
 * 4. 扫描 PCI-PCI 桥背后的子总线
 */
unsigned int pci_scan_child_bus(struct pci_bus *bus)
{
    unsigned int devfn, pass, max = bus->busn_res.start;
    struct pci_dev *dev;

    dev_dbg(&bus->dev, "scanning bus\n");

    /*
     * ============================================================
     * 步骤 1: 扫描总线上的所有设备槽
     * ============================================================
     * 
     * PCI 设备编号: 0-31 (5位)
     * 每个设备最多 8 个功能 (3位)
     * devfn 格式: [device(5位) | function(3位)]
     * 
     * 循环步长为 8, 因为:
     * - 首先扫描功能 0 (基础功能)
     * - 然后检查是否有其他功能 (多功能设备)
     */
    for (devfn = 0; devfn < 0x100; devfn += 8)
        pci_scan_slot(bus, devfn);

    /*
     * ============================================================
     * 步骤 2: 预留 SR-IOV 虚拟功能总线
     * ============================================================
     */
    max += pci_iov_bus_range(bus);

    /*
     * ============================================================
     * 步骤 3: 执行架构相关的总线修复
     * ============================================================
     */
    if (!bus->is_added) {
        dev_dbg(&bus->dev, "fixups for bus\n");
        pcibios_fixup_bus(bus);      // [arch-specific]
        bus->is_added = 1;
    }

    /*
     * ============================================================
     * 步骤 4: 扫描 PCI-PCI 桥背后的子总线
     * ============================================================
     * 
     * 需要两次扫描:
     * - pass 0 (第一遍): 收集信息,确定每个桥需要多少子总线
     * - pass 1 (第二遍): 实际扫描子总线
     * 
     * 这是递归的过程: 如果发现新的子总线,
     * 会对新的子总线调用 pci_scan_child_bus()
     */
    for (pass = 0; pass < 2; pass++) {
        list_for_each_entry(dev, &bus->devices, bus_list) {
            if (pci_is_bridge(dev))
                max = pci_scan_bridge(bus, dev, max, pass);
        }
    }

    /*
     * ============================================================
     * 步骤 5: 处理热插插桥的最小总线数
     * ============================================================
     */
    if (bus->self && bus->self->is_hotplug_bridge && pci_hotplug_bus_size) {
        if (max - bus->busn_res.start < pci_hotplug_bus_size - 1)
            max = bus->busn_res.start + pci_hotplug_bus_size - 1;
        if (max > bus->busn_res.end)
            max = bus->busn_res.end;
    }

    dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);
    return max;
}
EXPORT_SYMBOL_GPL(pci_scan_child_bus);

3.4 源码详细分析: pci_scan_slot()

位置: drivers/pci/probe.c:1919

/**
 * pci_scan_slot - 扫描 PCI 设备槽
 * @bus: PCI 总线
 * @devfn: 设备功能号 (必须是 8 的倍数,即功能 0)
 * 
 * 扫描流程:
 * 1. 首先扫描设备的第一个功能 (功能 0)
 * 2. 如果是多功能设备,扫描其他功能
 * 3. 初始化 PCIe ASPM 链路状态
 */
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
    unsigned fn, nr = 0;
    struct pci_dev *dev;

    /*
     * ============================================================
     * PCIe 优化: 对于只有一个设备的端口,只需扫描 devfn 0
     * ============================================================
     * 
     * only_one_child() 检查:
     * - 如果是 PCIe Root Port 或 Downstream Port
     * - 且没有设置 PCI_SCAN_ALL_PCIE_DEVS 标志
     * - 则只需扫描设备 0
     */
    if (only_one_child(bus) && (devfn > 0))
        return 0;

    /*
     * ============================================================
     * 步骤 1: 扫描设备 (功能 0)
     * ============================================================
     */
    dev = pci_scan_single_device(bus, devfn);
    if (!dev)
        return 0;
    if (!dev->is_added)
        nr++;

    /*
     * ============================================================
     * 步骤 2: 扫描多功能设备的其他功能
     * ============================================================
     * 
     * next_fn() 处理两种情况:
     * 1. ARI (Alternative Routing-ID Forwarding): 
     *    支持更灵活的功能编号
     * 2. 传统模式: 功能 1-7
     */
    for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
        dev = pci_scan_single_device(bus, devfn + fn);
        if (dev) {
            if (!dev->is_added)
                nr++;
            dev->multifunction = 1;  // 标记为多功能设备
        }
    }

    /*
     * ============================================================
     * 步骤 3: 初始化 PCIe ASPM
     * ============================================================
     */
    if (bus->self && nr)
        pcie_aspm_init_link_state(bus->self);

    return nr;
}
EXPORT_SYMBOL(pci_scan_slot);

3.5 源码详细分析: pci_scan_single_device()

位置: drivers/pci/probe.c:1839

/**
 * pci_scan_single_device - 扫描单个 PCI 设备 (某个功能)
 * @bus: PCI 总线
 * @devfn: 设备功能号
 * 
 * 核心流程:
 * 1. 检查设备是否已存在 (避免重复)
 * 2. 调用 pci_scan_device() 创建新设备
 * 3. 调用 pci_device_add() 将设备添加到总线
 */
struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;

    /* 
     * ============================================================
     * 步骤 1: 检查设备是否已存在
     * ============================================================
     * 
     * pci_get_slot() 遍历总线上的设备列表,
     * 查找是否有相同 bus+devfn 的设备
     */
    dev = pci_get_slot(bus, devfn);
    if (dev) {
        /*
         * 设备已存在 (可能是之前扫描创建的)
         * 增加引用计数并返回
         */
        pci_dev_put(dev);
        return dev;
    }

    /*
     * ============================================================
     * 步骤 2: 创建新设备
     * ============================================================
     * 
     * 这是核心函数,完成:
     * - 读取配置空间
     * - 分配 pci_dev 结构
     * - 初始化设备参数
     */
    dev = pci_scan_device(bus, devfn);
    if (!dev)
        return NULL;

    /*
     * ============================================================
     * 步骤 3: 将设备添加到总线
     * ============================================================
     * 
     * 这一步会触发驱动匹配!
     * 参见第 4 章"驱动匹配机制"
     */
    pci_device_add(dev, bus);

    return dev;
}
EXPORT_SYMBOL(pci_scan_single_device);

3.6 源码详细分析: pci_scan_device() - 核心创建设备

位置: drivers/pci/probe.c:1654

/**
 * pci_scan_device - 扫描并创建 PCI 设备结构
 * @bus: PCI 总线
 * @devfn: 设备功能号
 * 
 * 返回: 新创建的 pci_dev 结构,或 NULL
 * 
 * 关键步骤:
 * 1. 读取 Vendor ID (检测设备是否存在)
 * 2. 分配 pci_dev 结构
 * 3. 读取配置空间信息
 * 4. 设置设备参数
 */
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;
    u32 l;

    /*
     * ============================================================
     * 步骤 1: 读取 Vendor ID 检测设备是否存在
     * ============================================================
     * 
     * 这是第一次配置空间访问!
     * 读取偏移 0x00 (Vendor ID)
     * 
     * 返回值:
     * - l = 0xFFFFFFFF: 设备不存在
     * - l = 0x00000000: 设备不存在或未配置
     * - 其他: 有效的 Vendor ID
     * 
     * 60*1000 ms = 60 秒超时
     * (有些设备响应较慢)
     */
    if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
        return NULL;

    /*
     * ============================================================
     * 步骤 2: 分配 pci_dev 结构
     * ============================================================
     * 
     * pci_alloc_dev() 分配结构并初始化链表节点
     */
    dev = pci_alloc_dev(bus);
    if (!dev)
        return NULL;

    /*
     * ============================================================
     * 步骤 3: 填充基本信息
     * ============================================================
     */
    dev->devfn = devfn;
    dev->vendor = l & 0xffff;           // Vendor ID (低16位)
    dev->device = (l >> 16) & 0xffff;   // Device ID (高16位)

    /*
     * ============================================================
     * 步骤 4: 设置 OF 节点 (如果使用设备树)
     * ============================================================
     */
    pci_set_of_node(dev);

    /*
     * ============================================================
     * 步骤 5: 完整设备初始化
     * ============================================================
     * 
     * pci_setup_device() 完成:
     * - 读取 Class/Revision
     * - 读取所有 BAR
     * - 读取 IRQ 信息
     * - 读取 Subsystem ID
     * - 执行 quirk 修复
     */
    if (pci_setup_device(dev)) {
        pci_bus_put(dev->bus);
        kfree(dev);
        return NULL;
    }

    return dev;
}

3.7 源码详细分析: pci_setup_device() - 设备完整初始化

位置: drivers/pci/probe.c:1207

/**
 * pci_setup_device - 完整初始化 PCI 设备
 * @dev: pci_dev 结构
 * 
 * 初始化内容:
 * 1. 基本信息 (class, revision, header type)
 * 2. BAR (Base Address Register) 资源
 * 3. IRQ 信息
 * 4. Subsystem ID
 * 5. 执行设备特定修复 (quirks)
 */
int pci_setup_device(struct pci_dev *dev)
{
    u32 class;
    u16 cmd;
    u8 hdr_type;
    int pos = 0;
    struct pci_bus_region region;
    struct resource *res;

    /*
     * ============================================================
     * 步骤 1: 读取 Header Type
     * ============================================================
     * 
     * Header Type 格式:
     * - [6:0]: 头部类型
     *   * 0x00 = 标准设备 (普通 PCI 设备)
     *   * 0x01 = PCI-PCI 桥
     *   * 0x02 = CardBus 桥
     * - [7]:   多功能标志 (1=多功能)
     */
    if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
        return -EIO;

    dev->sysdata = dev->bus->sysdata;
    dev->dev.parent = dev->bus->bridge;
    dev->dev.bus = &pci_bus_type;
    dev->hdr_type = hdr_type & 0x7f;           // 头部类型
    dev->multifunction = !!(hdr_type & 0x80);   // 多功能标志

    dev->error_state = pci_channel_io_normal;
    set_pcie_port_type(dev);

    pci_dev_assign_slot(dev);

    /* 默认 32 位 DMA */
    dev->dma_mask = 0xffffffff;

    /*
     * ============================================================
     * 步骤 2: 设置设备名称
     * ============================================================
     * 
     * 格式: domain:bus:slot.func
     * 例如: 0000:00:1f.2
     */
    dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", 
                 pci_domain_nr(dev->bus),
                 dev->bus->number, 
                 PCI_SLOT(dev->devfn),
                 PCI_FUNC(dev->devfn));

    /*
     * ============================================================
     * 步骤 3: 读取 Class 和 Revision
     * ============================================================
     * 
     * PCI_CLASS_REVISION (0x08):
     * - [31:24]: Class Code (3字节)
     * - [23:16]: Prog IF
     * - [15:8]:  Subclass
     * - [7:0]:  Revision ID
     */
    pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
    dev->revision = class & 0xff;
    dev->class = class >> 8;    // 上3字节是 class

    dev_printk(KERN_DEBUG, &dev->dev, 
               "[%04x:%04x] type %02x class %#08x\n",
               dev->vendor, dev->device, dev->hdr_type, dev->class);

    /*
     * ============================================================
     * 步骤 4: 获取配置空间大小
     * ============================================================
     * 
     * PCI 配置空间:
     * - 256 字节 (标准)
     * - 4096 字节 (PCIe 扩展)
     */
    dev->cfg_size = pci_cfg_space_size(dev);

    dev->current_state = PCI_UNKNOWN;

    /*
     * ============================================================
     * 步骤 5: 执行早期修复 (quirk)
     * ============================================================
     * 
     * pci_fixup_early 在设备驱动加载前执行
     * 用于修复已知硬件问题
     */
    pci_fixup_device(pci_fixup_early, dev);
    class = dev->class >> 8;

    /*
     * ============================================================
     * 步骤 6: 处理非标准 BAR
     * ============================================================
     */
    if (dev->non_compliant_bars) {
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
            dev_info(&dev->dev, "disabling IO/MEM decoding\n");
            cmd &= ~PCI_COMMAND_IO;
            cmd &= ~PCI_COMMAND_MEMORY;
            pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
    }

    /*
     * ============================================================
     * 步骤 7: 根据 Header Type 初始化
     * ============================================================
     */
    switch (dev->hdr_type) {
    case PCI_HEADER_TYPE_NORMAL:    /* 标准 PCI 设备 */
        if (class == PCI_CLASS_BRIDGE_PCI)
            goto bad;
        
        pci_read_irq(dev);                              // 读取 IRQ
        pci_read_bases(dev, 6, PCI_ROM_ADDRESS);       // 读取 6 个 BAR + ROM
        pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, 
                           &dev->subsystem_vendor);
        pci_read_config_word(dev, PCI_SUBSYSTEM_ID, 
                           &dev->subsystem_device);
        
        /* 传统 IDE 控制器特殊处理 */
        if (class == PCI_CLASS_STORAGE_IDE) {
            u8 progif;
            pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
            /* ... IDE 控制器固定地址 ... */
        }
        break;

    case PCI_HEADER_TYPE_BRIDGE:    /* PCI-PCI 桥 */
        if (class != PCI_CLASS_BRIDGE_PCI)
            goto bad;
        
        pci_read_irq(dev);
        dev->transparent = ((dev->class & 0xff) == 1);
        pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
        set_pcie_hotplug_bridge(dev);
        
        pos = pci_find_capability(dev, PCI_CAP_ID_SSID);
        if (pos) {
            pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, 
                               &dev->subsystem_vendor);
            pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, 
                               &dev->subsystem_device);
        }
        break;

    case PCI_HEADER_TYPE_CARDBUS:   /* CardBus */
        if (class != PCI_CLASS_BRIDGE_CARD)
            goto bad;
        
        pci_read_irq(dev);
        pci_read_bases(dev, 1, 0);
        pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, 
                           &dev->subsystem_vendor);
        pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, 
                           &dev->subsystem_device);
        break;

    default:
        dev_err(&dev->dev, "unknown header type %02x\n", dev->hdr_type);
        return -EIO;
    }

    return 0;

bad:
    dev_err(&dev->dev, "ignoring class %#08x\n", dev->class);
    dev->class = PCI_CLASS_NOT_DEFINED << 8;
    return -EIO;
}

4. 驱动匹配机制深度剖析

4.1 匹配机制完整流程图

┌──────────────────────────────────────────────────────────────────────────────┐
│                          驱动匹配完整流程                                     │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  pci_device_add()                                                           │
│    │                                                                        │
│    ▼                                                                        │
│  device_add()  ◄─────────────────────────────────────────┐                 │
│    │                                                          │                 │
│    │  [核心: 将设备添加到总线,触发驱动匹配]                    │                 │
│    ▼                                                          │                 │
│  bus_probe_device()  ◄──────────────────────────┐           │                 │
│    │                                        │           │                 │
│    │  [调用总线的 probe 函数]                 │           │                 │
│    ▼                                        │           │                 │
│  device_attach()  ◄─────────────────────┐   │           │                 │
│    │                                  │   │           │                 │
│    │  [遍历总线上所有驱动,尝试绑定]        │   │           │                 │
│    ▼                                  │   │           │                 │
│  ┌─────────────────────────────────────┴───┴───────────────────────────┐    │
│  │ for each driver in &pci_bus_type.klist_drivers:                   │    │
│  │                                                                   │    │
│  │   ┌─────────────────────────────────────────────────────────────┐ │    │
│  │   │ driver_probe_device()                                      │ │    │
│  │   │   │                                                         │ │    │
│  │   │   ▼                                                         │ │    │
│  │   │ pci_bus_match()  ◄────────────────────────────┐           │ │    │
│  │   │   │                                      │               │ │    │
│  │   │   │  [检查设备是否与驱动匹配]                │               │ │    │
│  │   │   ▼                                      │               │ │    │
│  │   │ pci_match_device()  ◄──────────────┐      │               │ │    │
│  │   │   │                             │      │               │ │    │
│  │   │   │  [在驱动的 ID 表中查找匹配]     │      │               │ │    │
│  │   │   ▼                             │      │               │ │    │
│  │   │ 1. driver_override 检查         │      │               │ │    │
│  │   │    (如果设置了,只能绑定指定驱动)    │      │               │ │    │
│  │   │                               │      │               │ │    │
│  │   │ 2. 遍历动态 ID 列表              │      │               │ │    │
│  │   │    (运行时添加的 ID)             │      │               │ │    │
│  │   │                               │      │               │ │    │
│  │   │ 3. 遍历静态 ID 表                │      │               │ │    │
│  │   │    (驱动定义的 pci_device_id[])   │      │               │ │    │
│  │   │                               │      │               │ │    │
│  │   │ 4. pci_match_one_device()      │      │               │ │    │
│  │   │      │                          │      │               │ │    │
│  │   │      │  [核心匹配算法]            │      │               │ │    │
│  │   │      ▼                          │      │               │ │    │
│  │   │  返回匹配的 pci_device_id        │      │               │ │    │
│  │   │                               │      │               │ │    │
│  │   └───────────────────────────────┘      │               │ │    │
│  │                                           │               │ │    │
│  │  if (匹配成功):                            │               │ │    │
│  │      │                                    │               │ │    │
│  │      ▼                                    │               │ │    │
│  │  pci_device_probe()                       │               │ │    │
│  │      │                                    │               │ │    │
│  │      ├── pcibios_alloc_irq()             │               │ │    │
│  │      ├── pci_dev_get()                  │               │ │    │
│  │      │                                    │               │ │    │
│  │      ├── __pci_device_probe()  ◄─────────┴───────────────┘ │    │
│  │      │    │                                              │    │
│  │      │    │  [调用驱动的 probe 函数]                      │    │
│  │      │    ▼                                              │    │
│  │      │  drv->probe(pci_dev, matched_id) ◄───────────────┘    │
│  │      │                                                         │
│  │      └── (成功绑定!)                                           │
│  │                                                                   │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  如果没有驱动匹配:                                                          │
│    设备会保留在总线设备列表中                                                │
│    等待后续热插拔或驱动加载                                                 │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

4.2 源码分析: pci_bus_match()

位置: drivers/pci/pci-driver.c:1358

/**
 * pci_bus_match - PCI 设备与驱动匹配检查
 * @dev: PCI 设备
 * @drv: 设备驱动
 * 
 * 这是总线类型的 match 回调函数
 * 当 device_attach() 遍历驱动时调用
 * 
 * 返回: 1 = 匹配成功, 0 = 匹配失败
 */
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
    struct pci_dev *pci_dev = to_pci_dev(dev);
    struct pci_driver *pci_drv;
    const struct pci_device_id *found_id;

    /*
     * ============================================================
     * 检查设备是否允许驱动匹配
     * ============================================================
     * 
     * match_driver 标志:
     * - 由系统设置,可以阻止驱动匹配
     * - 例如: 在某些错误情况下
     */
    if (!pci_dev->match_driver)
        return 0;

    /* 转换为 pci_driver 结构 */
    pci_drv = to_pci_driver(drv);

    /*
     * ============================================================
     * 执行设备与驱动匹配
     * ============================================================
     */
    found_id = pci_match_device(pci_drv, pci_dev);
    if (found_id)
        return 1;    // 匹配成功!

    return 0;       // 匹配失败
}

4.3 源码分析: pci_match_device()

位置: drivers/pci/pci-driver.c:252

/**
 * pci_match_device - 在驱动 ID 表中查找匹配
 * @drv: PCI 驱动
 * @dev: PCI 设备
 * 
 * 匹配优先级:
 * 1. driver_override (最高优先级)
 * 2. 动态 ID (运行时添加)
 * 3. 静态 ID 表 (驱动定义)
 * 
 * 返回: 匹配的 pci_device_id,或 NULL
 */
static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
                                                    struct pci_dev *dev)
{
    struct pci_dynid *dynid;
    const struct pci_device_id *found_id = NULL;

    /*
     * ============================================================
     * 优先级 1: driver_override
     * ============================================================
     * 
     * 可以通过 sysfs 强制指定驱动:
     * echo "driver_name" > /sys/bus/pci/devices/.../driver_override
     * 
     * 这样只会绑定指定驱动,忽略其他匹配
     */
    if (dev->driver_override && strcmp(dev->driver_override, drv->name))
        return NULL;

    /*
     * ============================================================
     * 优先级 2: 动态 ID
     * ============================================================
     * 
     * 动态 ID 可以在运行时添加:
     * echo "vendor device" > /sys/bus/pci/drivers/.../new_id
     * 
     * 这些 ID 存储在驱动的 dynids 列表中
     */
    spin_lock(&drv->dynids.lock);
    list_for_each_entry(dynid, &drv->dynids.list, node) {
        if (pci_match_one_device(&dynid->id, dev)) {
            found_id = &dynid->id;
            break;
        }
    }
    spin_unlock(&drv->dynids.lock);

    if (found_id)
        return found_id;

    /*
     * ============================================================
     * 优先级 3: 静态 ID 表
     * ============================================================
     * 
     * 驱动定义的 static const struct pci_device_id[]
     * 例如:
     *   static const struct pci_device_id ids[] = {
     *       { PCI_DEVICE(0x8086, 0x1234) },
     *       { 0, }
     *   };
     */
    found_id = pci_match_id(drv->id_table, dev);

    /*
     * ============================================================
     * driver_override 特殊处理
     * ============================================================
     * 
     * 如果设置了 driver_override 但没有找到匹配,
     * 返回一个 dummy ID 让驱动尝试绑定
     */
    if (!found_id && dev->driver_override)
        found_id = &pci_device_id_any;

    return found_id;
}

4.4 源码分析: pci_match_one_device() - 核心匹配算法

位置: include/linux/pci.h (内联函数)

/**
 * pci_match_one_device - 单个设备与单个 ID 匹配
 * @id: pci_device_id 条目
 * @dev: pci_dev 设备
 * 
 * 匹配规则:
 * - id->vendor = PCI_ANY_ID (0xFFFF) 匹配任何厂商
 * - id->device = PCI_ANY_ID 匹配任何设备
 * - id->subvendor/subdevice 同上
 * - class_mask: 类代码掩码,0 表示精确匹配
 * 
 * 返回: 匹配返回 id,不匹配返回 NULL
 */
static inline const struct pci_device_id *
pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
{
    /*
     * ============================================================
     * 逐字段匹配检查
     * ============================================================
     */
    if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
        (id->device == PCI_ANY_ID || id->device == dev->device) &&
        (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
        (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
        !((id->class ^ dev->class) & id->class_mask))
        return id;    // 匹配成功!
    
    return NULL;     // 匹配失败
}

4.5 PCI 设备 ID 定义详解

/*
 * ============================================================
 * PCI 设备 ID 定义宏
 * ============================================================
 */

/* 通用定义 */
#define PCI_VENDOR_ID_ANY            0xffff
#define PCI_DEVICE_ID_ANY           0xffff
#define PCI_ANY_ID                   (~0)

/*
 * PCI 类代码
 * Class Code = 3 字节: [Base Class][Sub Class][Prog IF]
 */
#define PCI_CLASS(class, subclass, progif) \
    (((class) << 16) | ((subclass) << 8) | (progif))

#define PCI_CLASS_MASK               0xffffff

/* 常用类代码 */
#define PCI_CLASS_NOT_DEFINED         0x00
#define PCI_CLASS_STORAGE_IDE         0x0101
#define PCI_CLASS_NETWORK_ETHERNET    0x0200
#define PCI_CLASS_DISPLAY_VGA         0x0300
#define PCI_CLASS_MEDIA               0x04
#define PCI_CLASS_MEMORY_RAM          0x0500
#define PCI_CLASS_BRIDGE_PCI         0x0604

/*
 * ============================================================
 * 设备 ID 表定义宏
 * ============================================================
 */

/* 精确匹配单个设备 */
#define PCI_DEVICE(vendor, device) \
    .vendor = (vendor), .device = (device), \
    .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
    .class = 0, .class_mask = 0

/* 匹配特定厂商的所有设备 */
#define PCI_VDEVICE(vendor) \
    .vendor = (vendor), .device = PCI_ANY_ID, \
    .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
    .class = 0, .class_mask = 0

/* 匹配特定类别 (类代码) 的所有设备 */
#define PCI_DEVICE_CLASS(device_class, class_mask) \
    .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
    .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
    .class = (device_class), .class_mask = (class_mask)

/*
 * ============================================================
 * 驱动中的使用示例
 * ============================================================
 */

/* e1000 网卡驱动 */
static const struct pci_device_id e1000_pci_tbl[] = {
    /* 精确匹配: 特定厂商的特定设备 */
    { PCI_DEVICE(0x8086, 0x100E) },  /* Intel 82540EM */
    { PCI_DEVICE(0x8086, 0x100F) },  /* Intel 82540EM (LOM) */
    { PCI_DEVICE(0x8086, 0x1010) },  /* Intel 82540EP */
    
    /* 匹配厂商: Intel 的所有设备 */
    { PCI_VDEVICE(Intel) },
    
    /* 匹配类别: 所有以太网控制器 */
    { PCI_DEVICE_CLASS(PCI_CLASS_NETWORK_ETHERNET, ~0) },
    
    /* 结束标志 */
    { 0, }
};
MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);

/* NVMe 驱动 */
static const struct pci_device_id nvme_id_table[] = {
    { PCI_VDEVICE(Intel) },                    /* Intel NVMe */
    { PCI_VDEVICE(Samsung) },                  /* Samsung NVMe */
    { PCI_VDEVICE(WD) },                       /* Western Digital */
    { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_NVME, ~0) }, /* 所有 NVMe */
    { 0 }
};

4.6 源码分析: __pci_device_probe()

位置: drivers/pci/pci-driver.c:373

/**
 * __pci_device_probe - 检查驱动是否想绑定设备,并调用 probe
 * @drv: 驱动
 * @pci_dev: 设备
 * 
 * 这是驱动绑定的核心函数
 * 
 * 返回: 0 = 成功, 负数 = 错误
 */
static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
{
    const struct pci_device_id *id;
    int error = 0;

    /*
     * ============================================================
     * 前置检查
     * ============================================================
     * 
     * 1. 设备还没有驱动
     * 2. 驱动有 probe 函数
     */
    if (!pci_dev->driver && drv->probe) {
        error = -ENODEV;

        /*
         * ============================================================
         * 查找匹配的设备 ID
         * ============================================================
         */
        id = pci_match_device(drv, pci_dev);
        if (id) {
            /*
             * ============================================================
             * 调用驱动的 probe 函数
             * ============================================================
             * 
             * pci_call_probe() 处理:
             * - 电源管理 (pm_runtime)
             * - 调用 drv->probe()
             */
            error = pci_call_probe(drv, pci_dev, id);
            
            /*
             * probe 成功返回 0 或正数
             * 错误返回负数
             * 将正数转换为 0 (成功)
             */
            if (error >= 0)
                error = 0;
        }
    }
    return error;
}

5. Host 控制器驱动完全解析 (DesignWare)

5.1 DesignWare PCIe 架构概述

┌──────────────────────────────────────────────────────────────────────────────┐
│                        DesignWare PCIe 架构                                   │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                         Linux 系统层                                   │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                    │                                          │
│                                    ▼                                          │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                    平台驱动层 (pci-imx6.c 等)                          │   │
│  │                                                                      │   │
│  │  imx_pcie_probe()                                                   │   │
│  │    ├── 解析设备树                                                    │   │
│  │    ├── 时钟/PHY/复位                                                │   │
│  │    └── dw_pcie_host_init()  ◄──────────── 入口                     │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                    │                                          │
│                                    ▼                                          │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                 DesignWare 核心层 (pcie-designware.c)                 │   │
│  │                                                                      │   │
│  │  dw_pcie_host_init()         - 主机初始化                           │   │
│  │  dw_pcie_rd_conf()           - 读配置空间                           │   │
│  │  dw_pcie_wr_conf()           - 写配置空间                           │   │
│  │  dw_pcie_prog_outbound_atu() - 编程 ATU                             │   │
│  │                                                                      │   │
│  │  ┌──────────────────────────────────────────────────────────────┐   │   │
│  │  │ struct pcie_port {                                         │   │   │
│  │  │     struct device *dev;           // 平台设备               │   │   │
│  │  │     void __iomem *dbi_base;      // DBI 寄存器基地址        │   │   │
│  │  │     void __iomem *cfg_base;      // 配置空间基地址          │   │   │
│  │  │     void __iomem *va_cfg0_base;  // 配置空间虚拟地址        │   │   │
│  │  │     struct pci_ops *ops;         // 配置空间操作            │   │   │
│  │  │     struct irq_domain *irq_domain;  // MSI 中断域           │   │   │
│  │  │     struct list_head resources;   // 地址资源               │   │   │
│  │  │     ...                                                    │   │   │
│  │  │ };                                                         │   │   │
│  │  └──────────────────────────────────────────────────────────────┘   │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                    │                                          │
│                                    ▼                                          │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                         PCIe 硬件 IP 核                               │   │
│  │                                                                      │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐               │   │
│  │  │    DBI     │  │    ATU      │  │    PHY     │               │   │
│  │  │ (配置寄存器) │  │ (地址转换)   │  │ (物理层)    │               │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘               │   │
│  │                                                                      │   │
│  │  寄存器映射:                                                         │   │
│  │  基地址 + 0x0000 - 0x00FF:  DBI 寄存器 (配置和控制)                │   │
│  │  基地址 + 0x0100 - 0x0FFF:  ATU (地址转换窗口)                    │   │
│  │  基地址 + 0x1000 - ...  :  端口配置空间 (标准 PCI 配置)           │   │
│  │                                                                      │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

5.2 源码分析: dw_pcie_host_init() - 主机初始化核心

位置: drivers/pci/host/pcie-designware.c:517

/**
 * dw_pcie_host_init - DesignWare PCIe 主机初始化
 * @pp: pcie_port 结构
 * 
 * 初始化流程:
 * 1. 获取设备树资源
 * 2. ioremap 寄存器
 * 3. 初始化 MSI
 * 4. 扫描 PCI 总线
 * 5. 分配资源
 * 
 * 返回: 0 = 成功, 负数 = 错误
 */
int dw_pcie_host_init(struct pcie_port *pp)
{
    struct device_node *np = pp->dev->of_node;
    struct platform_device *pwd = to_platform_device(pp->dev);
    struct pci_bus *bus, *child;
    struct resource *cfg_res;
    int i, ret;
    LIST_HEAD(res);
    struct resource_entry *win, *tmp;

    /*
     * ============================================================
     * 步骤 1: 获取配置空间资源
     * ============================================================
     * 
     * 从设备树读取 "config" 寄存器
     * 通常是 1MB 大小,分为两半:
     * - cfg0: 根端口配置空间
     * - cfg1: 下游端口配置空间
     */
    cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
    if (cfg_res) {
        pp->cfg0_size = resource_size(cfg_res)/2;
        pp->cfg1_size = resource_size(cfg_res)/2;
        pp->cfg0_base = cfg_res->start;
        pp->cfg1_base = cfg_res->start + pp->cfg0_size;
    } else if (!pp->va_cfg0_base) {
        dev_err(pp->dev, "missing *config* reg space\n");
    }

    /*
     * ============================================================
     * 步骤 2: 从设备树获取地址资源
     * ============================================================
     * 
     * 解析 "ranges" 属性,获取:
     * - I/O 空间
     * - MEM 空间
     * - 总线号范围
     */
    ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base);
    if (ret)
        return ret;

    /*
     * ============================================================
     * 步骤 3: 请求 PCI 总线资源
     * ============================================================
     */
    ret = devm_request_pci_bus_resources(&pwd->dev, &res);
    if (ret)
        goto error;

    /*
     * ============================================================
     * 步骤 4: 解析并映射资源
     * ============================================================
     */
    resource_list_for_each_entry_safe(win, tmp, &res) {
        switch (resource_type(win->res)) {
        case IORESOURCE_IO:
            /*
             * 映射 I/O 空间到 CPU 地址
             * 使用 pci_remap_iospace()
             */
            ret = pci_remap_iospace(win->res, pp->io_base);
            if (ret) {
                dev_warn(pp->dev, "failed to map IO resource\n");
                resource_list_destroy_entry(win);
            } else {
                pp->io = win->res;
                pp->io_size = resource_size(pp->io);
                pp->io_bus_addr = pp->io->start - win->offset;
            }
            break;

        case IORESOURCE_MEM:
            /*
             * 映射 MEM 空间
             */
            pp->mem = win->res;
            pp->mem_size = resource_size(pp->mem);
            pp->mem_bus_addr = pp->mem->start - win->offset;
            break;

        case 0:  /* IORESOURCE_PREFETCH */
            /*
             * 可预取内存 / 配置空间
             */
            pp->cfg = win->res;
            pp->cfg0_size = resource_size(pp->cfg)/2;
            pp->cfg1_size = resource_size(pp->cfg)/2;
            pp->cfg0_base = pp->cfg->start;
            pp->cfg1_base = pp->cfg->start + pp->cfg0_size;
            break;

        case IORESOURCE_BUS:
            /*
             * 总线号范围
             */
            pp->busn = win->res;
            break;
        }
    }

    /*
     * ============================================================
     * 步骤 5: ioremap DBI 寄存器空间
     * ============================================================
     * 
     * DBI (Device Bus Interface) 寄存器:
     * - 用于配置 PCIe 控制器
     * - 包括 ATU、MSI、DMA 等控制寄存器
     */
    if (!pp->dbi_base) {
        pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start,
                                    resource_size(pp->cfg));
        if (!pp->dbi_base) {
            dev_err(pp->dev, "error with ioremap\n");
            ret = -ENOMEM;
            goto error;
        }
    }

    pp->mem_base = pp->mem->start;

    /*
     * ============================================================
     * 步骤 6: ioremap 配置空间
     * ============================================================
     */
    if (!pp->va_cfg0_base) {
        pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
                                        pp->cfg0_size);
    }
    if (!pp->va_cfg1_base) {
        pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
                                        pp->cfg1_size);
    }

    /*
     * ============================================================
     * 步骤 7: 解析设备树属性
     * ============================================================
     */
    of_property_read_u32(np, "num-lanes", &pp->lanes);
    of_property_read_u32(np, "num-viewport", &pp->num_viewport);

    /*
     * ============================================================
     * 步骤 8: 初始化 MSI 中断
     * ============================================================
     */
    if (IS_ENABLED(CONFIG_PCI_MSI)) {
        if (!pp->ops->msi_host_init) {
            /*
             * 创建 MSI 中断域
             */
            pp->irq_domain = irq_domain_add_linear(
                pp->dev->of_node,
                MAX_MSI_IRQS,
                &msi_domain_ops,
                &dw_pcie_msi_chip);

            if (!pp->irq_domain) {
                dev_err(pp->dev, "irq domain init failed\n");
                ret = -ENXIO;
                goto error;
            }

            /*
             * 为每个 MSI 向量创建映射
             */
            for (i = 0; i < MAX_MSI_IRQS; i++)
                irq_create_mapping(pp->irq_domain, i);
        } else {
            ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
            if (ret < 0)
                goto error;
        }
    }

    /*
     * ============================================================
     * 步骤 9: 平台特定初始化回调
     * ============================================================
     */
    if (pp->ops->host_init)
        pp->ops->host_init(pp);

    /*
     * ============================================================
     * 步骤 10: 扫描 PCI 总线  ◄────────────── 关键!
     * ============================================================
     * 
     * 这会触发设备枚举和驱动匹配
     */
    pp->root_bus_nr = pp->busn->start;
    if (IS_ENABLED(CONFIG_PCI_MSI))
        bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr,
                                    &dw_pcie_ops, pp, &res,
                                    &dw_pcie_msi_chip);
    else
        bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops,
                                pp, &res);
    if (!bus) {
        ret = -ENOMEM;
        goto error;
    }

    /*
     * ============================================================
     * 步骤 11: 平台特定扫描回调
     * ============================================================
     */
    if (pp->ops->scan_bus)
        pp->ops->scan_bus(pp);

    /*
     * ============================================================
     * 步骤 12: 修复和分配资源
     * ============================================================
     */
#ifdef CONFIG_ARM
    pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
#endif

    /*
     * 计算桥设备需要分配的资源大小
     */
    pci_bus_size_bridges(bus);

    /*
     * 分配资源 (BAR、IO、MEM)
     */
    pci_bus_assign_resources(bus);

    /*
     * ============================================================
     * 步骤 13: 配置 PCIe 链路
     * ============================================================
     */
    list_for_each_entry(child, &bus->children, node)
        pcie_bus_configure_settings(child);

    /*
     * ============================================================
     * 步骤 14: 添加设备到 sysfs
     * ============================================================
     */
    pci_bus_add_devices(bus);

    return 0;

error:
    pci_free_resource_list(&res);
    return ret;
}

6. i.MX6 PCIe 驱动完整源码分析

6.1 驱动结构定义

位置: drivers/pci/host/pci-imx6.c

/*
 * ============================================================
 * i.MX6 PCIe 控制器私有数据结构
 * ============================================================
 */
struct imx_pcie {
    struct pcie_port pp;                // DesignWare 端口结构
    
    /* SoC 变体 */
    u32 variant;                        // 变体 ID
    enum imx_pcie_variant type;        // 类型 (IMX6Q/IMX6DL/IMX7D 等)
    
    /* 时钟 */
    struct clk *pcie_bus;              // PCIe 总线时钟
    struct clk *pcie_phy;               // PHY 时钟
    struct clk *pcie_ref;               // 参考时钟 (可选)
    struct clk *pcie_ext;               // 外部时钟 (可选)
    
    /* 复位 */
    struct reset_control *pciephy_rst; // PHY 复位
    struct reset_control *app_rst;      // 应用层复位
    
    /* GPIO */
    int reset_gpio;                     // 复位 GPIO (可选)
    enum of_gpio_flags reset_gpio_flags;
    
    /* 寄存器 */
    struct regmap *iomuxc_gpr;         // IOMUX GPR 寄存器
    void __iomem *phys_base;            // 物理基地址
    resource_size_t phys_size;         // 大小
    
    /* 电源 */
    bool power_on;                     // 电源状态
};

/*
 * ============================================================
 * i.MX6 PCIe 平台驱动定义
 * ============================================================
 */
static struct platform_driver imx_pcie_driver = {
    .driver = {
        .name   = "imx6q-pcie",
        .of_match_table = imx_pcie_dt_ids,
        .pm     = IMX_PCIE_PM_OPS,
    },
    .probe  = imx_pcie_probe,
    .remove = imx_pcie_remove,
};

6.2 Probe 函数完整源码分析

/*
 * ============================================================
 * imx_pcie_probe - i.MX6 PCIe 驱动 probe 函数
 * ============================================================
 * 
 * 初始化流程:
 * 1. 分配私有数据
 * 2. 解析设备树
 * 3. 使能时钟
 * 4. 初始化 PHY
 * 5. 复位控制器
 * 6. 初始化 DesignWare 核心
 * 7. 使能 MSI
 */
static int imx_pcie_probe(struct platform_device *pwd)
{
    struct imx_pcie *imx_pcie;
    struct pcie_port *pp;
    struct device *dev = &pwd->dev;
    int ret;

    /*
     * ============================================================
     * 步骤 1: 分配私有数据
     * ============================================================
     */
    imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL);
    if (!imx_pcie)
        return -ENOMEM;

    pp = &imx_pcie->pp;
    pp->dev = dev;

    /*
     * ============================================================
     * 步骤 2: 解析设备树
     * ============================================================
     */
    ret = imx_pcie_parse_dt(imx_pcie);
    if (ret) {
        dev_err(dev, "failed to parse device tree\n");
        return ret;
    }

    /*
     * ============================================================
     * 步骤 3: 使能时钟
     * ============================================================
     * 
     * i.MX6 PCIe 需要多个时钟:
     * - pcie_bus: PCIe 总线时钟
     * - pcie_phy: PHY 时钟
     * - pcie_ref: 参考时钟
     */
    ret = imx_pcie_clk_enable(imx_pcie);
    if (ret) {
        dev_err(dev, "failed to enable clocks\n");
        return ret;
    }

    /*
     * ============================================================
     * 步骤 4: 初始化 PHY
     * ============================================================
     * 
     * PHY 初始化包括:
     * - 配置 PHY 参数
     * - 等待 PHY 稳定
     */
    ret = imx_pcie_init_phy(imx_pcie);
    if (ret) {
        dev_err(dev, "failed to init PHY\n");
        goto err_clk;
    }

    /*
     * ============================================================
     * 步骤 5: 复位 PCIe 控制器
     * ============================================================
     * 
     * 复位顺序:
     * 1. 先复位 PHY
     * 2. 释放 PHY
     * 3. 复位应用层
     * 4. 释放应用层
     */
    ret = imx_pcie_deassert_core_reset(imx_pcie);
    if (ret) {
        dev_err(dev, "failed to deassert reset\n");
        goto err_phy;
    }

    /*
     * ============================================================
     * 步骤 6: 初始化 DesignWare 核心  ◄────────────── 关键!
     * ============================================================
     * 
     * 这会:
     * - ioremap 寄存器
     * - 初始化 MSI
     * - 扫描 PCI 总线 ← 触发设备枚举
     * - 分配资源
     */
    ret = dw_pcie_host_init(pp);
    if (ret) {
        dev_err(dev, "failed to initialize host\n");
        goto err_reset;
    }

    /*
     * ============================================================
     * 步骤 7: 使能 MSI
     * ============================================================
     */
    imx_pcie_enable_msi(imx_pcie);

    return 0;

err_reset:
    imx_pcie_assert_core_reset(imx_pcie);
err_phy:
    imx_pcie_phy_disable(imx_pcie);
err_clk:
    imx_pcie_clk_disable(imx_pcie);
    return ret;
}

7. PCI 配置空间访问机制详解

7.1 x86 端口 IO 方式 (传统 PCI)

┌──────────────────────────────────────────────────────────────────────────────┐
│                      x86 PCI 配置空间访问机制                                 │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  IO 端口:                                                                   │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ 0xCF8 (32位) - 配置地址端口                                          │   │
│  │ 0xCFC (32位) - 配置数据端口                                          │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  配置地址格式:                                                              │
│  ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐│
│  │ 31 │ 30 │ 29 │ 28 │ 27 │ 26 │ 25 │ 24 │ 23 │ 22 │ 21 │ 20 │ 19 │ 18 │ 17 │ 16 ││
│  ├────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┤│
│  │  1  │                      Reserved                    │   Bus Number       ││
│  ├─────┼────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬───────────┤│
│  │ 15  │ 14  │ 13 │ 12 │ 11 │ 10 │  9 │  8 │  7 │  6 │  5 │  4 │  3 │  2 │  1 │  0 ││
│  ├─────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┤│
│  │         Device Number        │  Func  │         Register Offset        │使能││
│  │          (5位)               │ (3位)  │          (6位,4字节对齐)       │    ││
│  └─────────────────────────────┴────────┴──────────────────────────────────┘ │
│                                                                              │
│  访问示例 (读取总线0,设备0,功能0,Vendor ID):                                 │
│  ┌──────────────────────────────────────────────────────────────────────┐     │
│  │ 1. 写配置地址:                                                       │     │
│  │    addr = (1 << 31) | (0 << 16) | (0 << 8) | (0 & 0xFC)           │     │
│  │    outl(addr, 0xCF8)                                               │     │
│  │                                                                      │     │
│  │ 2. 读配置数据:                                                       │     │
│  │    val = inl(0xCFC)                                                │     │
│  │    // val = 0x8086 (Intel Vendor ID)                               │     │
│  └──────────────────────────────────────────────────────────────────────┘     │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

7.2 ECAM 方式 (PCIe 标准)

┌──────────────────────────────────────────────────────────────────────────────┐
│                      PCIe ECAM 配置空间访问机制                               │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ECAM (Enhanced Configuration Access Mechanism):                            │
│  - 通过 MMIO 访问 PCIe 配置空间                                              │
│  - ACPI MCFG 表提供 ECAM 基地址                                             │
│                                                                              │
│  地址格式:                                                                  │
│  ┌─────────────┬─────────────┬─────────────┬─────────────┐                │
│  │ [63:20][19:15][14:12][11:0]     │                │
│  │ ECAM Base  │ Bus Number │ Dev:Func   │ Register   │                │
│  │ (从 MCFG)  │ (0-255)    │ (设备+功能) │ Offset     │                │
│  └─────────────┴─────────────┴─────────────┴─────────────┘                │
│                                                                              │
│  示例:                                                                      │
│  ECAM Base = 0xE0000000 (从 ACPI MCFG 获取)                                 │
│  访问总线 1, 设备 0, 功能 0, 寄存器 0:                                       │
│  地址 = 0xE0000000 + (1 << 15) + (0 << 12) + 0                             │
│       = 0xE0000000 + 0x8000                                                 │
│       = 0xE0008000                                                           │
│                                                                              │
│  ARM64/ARM 实现:                                                             │
│  ┌──────────────────────────────────────────────────────────────────────┐     │
│  │ 1. 在设备树中指定 "config" 寄存器:                                     │     │
│  │    reg = <0x01ffc000 0x4000>;                                        │     │
│  │                                                                      │     │
│  │ 2. ioremap 到虚拟地址:                                               │     │
│  │    pp->va_cfg0_base = ioremap(cfg0_base, cfg0_size);                │     │
│  │                                                                      │     │
│  │ 3. 直接读写:                                                         │     │
│  │    val = readl(va_cfg0_base + offset);                              │     │
│  └──────────────────────────────────────────────────────────────────────┘     │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

8. ATU 地址转换单元完全解析

8.1 ATU 作用与类型

┌──────────────────────────────────────────────────────────────────────────────┐
                          ATU 地址转换单元                                     
├──────────────────────────────────────────────────────────────────────────────┤
                                                                              
  ATU (Address Translation Unit) 的作用:                                     
  ┌──────────────────────────────────────────────────────────────────────┐   
                                                                           
     CPU 地址 (虚拟/物理)   ─────────►   PCIe 地址                        
                                                                           
     例如:                                                                 
     0x80000000 (CPU)  ───────►  0x80000000 (PCIe)                      
     0x20000000 (CPU)  ───────►  0x10000000 (PCIe)                      
                                                                           
  └──────────────────────────────────────────────────────────────────────┘   
                                                                              
  ATU 区域:                                                                  
  ┌──────────────────────────────────────────────────────────────────────┐   
   Region 0: 通常用于 MEM 空间                                             
   Region 1: 通常用于 I/O 空间                                             
   Region 2: 通常用于配置空间访问 (CFG0/CFG1)                              
   ...                                                                      
   Region N: 可配置                                                         
  └──────────────────────────────────────────────────────────────────────┘   
                                                                              
  ATU 类型 ( outbound 传输):                                                  
  ┌──────────────────────────────────────────────────────────────────────┐   
   Type 0: MEM (内存事务)                                                 
   Type 1: I/O (I/O 事务)                                                 
   Type 2: CFG0 (配置类型0, 访问端点设备配置空间)                         
   Type 3: CFG1 (配置类型1, 访问桥后的设备配置空间)                       
   Type 4: MSG (消息)                                                      
  └──────────────────────────────────────────────────────────────────────┘   
                                                                              
└──────────────────────────────────────────────────────────────────────────────┘

8.2 ATU 寄存器详解

┌──────────────────────────────────────────────────────────────────────────────┐
│                          ATU 寄存器映射                                       │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ATU 寄存器偏移 (相对于 DBI 基地址):                                          │
│                                                                              │
│  Region 0: 0x000 - 0xFF                                                     │
│  Region 1: 0x100 - 0x1FF                                                    │
│  ...                                                                         │
│  Region N: N*0x100 - N*0x100 + 0xFF                                        │
│                                                                              │
│  每个区域的寄存器:                                                           │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ Offset + 0x00: ATU_VIEWPORT    - 区域选择 (写区域号激活)            │   │
│  │ Offset + 0x04: ATU_BASE_ADDR   - CPU 侧基地址 (低32位)              │   │
│  │ Offset + 0x08: ATU_BASE_ADDR_UPPER - CPU 侧基地址 (高32位)          │   │
│  │ Offset + 0x0C: ATU_LIMIT_ADDR  - CPU 侧限制地址                     │   │
│  │ Offset + 0x10: ATU_TARGET_ADDR - PCIe 侧目标地址 (低32位)            │   │
│  │ Offset + 0x14: ATU_TARGET_ADDR_UPPER - PCIe 侧目标地址 (高32位)     │   │
│  │ Offset + 0x18: ATU_ATR_SIZE   - 地址转换大小                        │   │
│  │ Offset + 0x1C: ATU_CONTROL   - 控制寄存器                          │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  ATU_CONTROL 位定义:                                                         │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ [31:20]: Reserved                                                   │   │
│  │ [19:18]: Type - 事务类型 (00=MEM, 01=IO, 10=CFG0, 11=CFG1)        │   │
│  │ [17]   : Attr - 属性 (0=No-snoop, 1=No-snoop)                     │   │
│  │ [16]   : TC - Traffic Class                                        │   │
│  │ [15:14]: Reserved                                                   │   │
│  │ [13:8] : Func_Bit - 功能位                                         │   │
│  │ [7]    : Reserved                                                   │   │
│  │ [6]    : AT - 地址类型 (0=36位, 1=64位)                             │   │
│  │ [5]    : Reserved                                                   │   │
│  │ [4]    : BAR_NUMBER - BAR 编号                                     │   │
│  │ [3]    : Reserved                                                   │   │
│  │ [2]    : Response_CODE - 响应码                                    │   │
│  │ [1]    : HEADER_TYPE - 头部类型                                    │   │
│  │ [0]    : ENABLE - 区域使能                                         │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

8.3 ATU 编程示例

/*
 * ============================================================
 * dw_pcie_prog_outbound_atu - 编程 outbound ATU
 * ============================================================
 * 
 * 将 CPU 地址范围映射到 PCIe 地址
 */
void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
                                int type, u64 cpu_addr,
                                u64 pci_addr, u32 size)
{
    /*
     * ============================================================
     * 步骤 1: 选择 ATU 区域
     * ============================================================
     * 
     * 写入区域号到 VIEWPORT 寄存器
     * 写入后,该区域的寄存器就可以访问了
     */
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_VIEWPORT, index);

    /*
     * ============================================================
     * 步骤 2: 设置 CPU 侧地址范围
     * ============================================================
     */
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_BASE_ADDR, lower_32_bits(cpu_addr));
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_BASE_ADDR_UPPER, upper_32_bits(cpu_addr));
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_LIMIT_ADDR, 
                       lower_32_bits(cpu_addr + size - 1));

    /*
     * ============================================================
     * 步骤 3: 设置 PCIe 侧目标地址
     * ============================================================
     */
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_TARGET_ADDR, lower_32_bits(pci_addr));
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_TARGET_ADDR_UPPER, upper_32_bits(pci_addr));

    /*
     * ============================================================
     * 步骤 4: 使能区域
     * ============================================================
     */
    dw_pcie_wr_own_dbi(pp, PCIE_ATU_CONTROL, 
                       type | PCIE_ATU_ENABLE);
}

/*
 * 使用示例: 配置 ATU 用于配置空间访问
 */
static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        u32 devfn, int where, int size, u32 *val)
{
    u32 busdev, cfg_size;
    u64 cpu_addr;
    void __iomem *va_cfg_base;
    int ret;

    /*
     * 计算目标设备地址
     * Bus[7:0] | Dev[4:0] | Func[2:0]
     */
    busdev = PCIE_ATU_BUS(bus->number) | 
             PCIE_ATU_DEV(PCI_SLOT(devfn)) |
             PCIE_ATU_FUNC(PCI_FUNC(devfn));

    /*
     * 根据总线类型选择配置空间
     */
    if (bus->parent->number == pp->root_bus_nr) {
        /* 根总线下的设备 */
        type = PCIE_ATU_TYPE_CFG0;
        cpu_addr = pp->cfg0_base;
        cfg_size = pp->cfg0_size;
        va_cfg_base = pp->va_cfg0_base;
    } else {
        /* 非根总线下的设备 */
        type = PCIE_ATU_TYPE_CFG1;
        cpu_addr = pp->cfg1_base;
        cfg_size = pp->cfg1_size;
        va_cfg_base = pp->va_cfg1_base;
    }

    /*
     * 配置 ATU 将 CPU 地址映射到目标设备配置空间
     */
    dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
                              type, cpu_addr,
                              busdev, cfg_size);

    /*
     * 读取配置空间
     */
    ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);

    /*
     * 清除 ATU 映射 (如果需要)
     */
    if (pp->num_viewport <= 2)
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
                                  PCIE_ATU_TYPE_IO, pp->io_base,
                                  pp->io_bus_addr, pp->io_size);

    return ret;
}

9. MSI/MSI-X 中断机制完全掌握

9.1 MSI vs 传统中断对比

┌──────────────────────────────────────────────────────────────────────────────┐
│                     MSI 中断 vs 传统中断对比                                 │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────┬──────────────────────┬─────────────────────────────┐   │
│  │     特性        │   传统中断 (INTx)    │         MSI / MSI-X          │   │
│  ├─────────────────┼──────────────────────┼─────────────────────────────┤   │
│  │ 中断引脚        │ 需要专用引脚         │ 无需引脚                     │   │
│  │                 │ (INTA#, INTB# 等)    │                             │   │
│  ├─────────────────┼──────────────────────┼─────────────────────────────┤   │
│  │ 中断数量        │ 最多 4 个            │ MSI: 1-32 个               │   │
│  │                 │                      │ MSI-X: 1-2048 个           │   │
│  ├─────────────────┼──────────────────────┼─────────────────────────────┤   │
│  │ 中断路由        │ 复杂 (PIRQ)          │ 简单 (内存写)               │   │
│  │                 │ 需要 ACPI/BIOS      │ 直接发送到指定地址          │   │
│  ├─────────────────┼──────────────────────┼─────────────────────────────┤   │
│  │ 性能            │ 较低                 │ 高                          │   │
│  │                 │ 有延迟               │ 低延迟                      │   │
│  ├─────────────────┼──────────────────────┼─────────────────────────────┤   │
│  │ PCIe 支持       │ 兼容                 │ PCIe 推荐                   │   │
│  │                 │                      │ (PCIe 必支持)               │   │
│  └─────────────────┴──────────────────────┴─────────────────────────────┘   │
│                                                                              │
│  MSI 中断触发机制:                                                           │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                                                                      │   │
│  │   PCIe 设备                                                        │   │
│  │       │                                                            │   │
│  │       │  1. 写 MSI 地址 (固定值)                                   │   │
│  │       │     ───────────────────────────────────────────            │   │
│  │       │     │  x86: 0xFEE00000 + (APIC ID << 12)  │               │   │
│  │       │     │  ARM: 平台特定                            │            │   │
│  │       │     ───────────────────────────────────────────            │   │
│  │       ▼                                                            │   │
│  │       │  2. 写 MSI 数据 (向量号)                                   │   │
│  │       │     ───────────────────────────────────────────            │   │
│  │       │     │  [15:14] Reserved                                    │   │
│  │       │     │  [13:11] Trigger Mode                               │   │
│  │       │     │  [10:8]  Delivery Mode                              │   │
│  │       │     │  [7:0]   Vector (中断向量号)                        │   │
│  │       │     ───────────────────────────────────────────            │   │
│  │       │                                                            │   │
│  │       ▼                                                            │   │
│  │   ┌────────────────────────────────────────────────────────────┐   │   │
│  │   │              PCIe 交换机 / Root Complex                    │   │   │
│  │   │                      │                                      │   │   │
│  │   │                      ▼                                      │   │   │
│  │   │              中断控制器 (APIC / GIC)                         │   │   │
│  │   │                      │                                      │   │   │
│  │   │                      ▼                                      │   │   │
│  │   │                      CPU                                     │   │   │
│  │   └────────────────────────────────────────────────────────────┘   │   │
│  │                                                                      │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

9.2 MSI 初始化流程

/*
 * ============================================================
 * MSI 中断初始化 (在 dw_pcie_host_init 中)
 * ============================================================
 */

// 步骤 1: 创建 MSI 中断域
if (IS_ENABLED(CONFIG_PCI_MSI)) {
    pp->irq_domain = irq_domain_add_linear(
        pp->dev->of_node,         // 设备树节点
        MAX_MSI_IRQS,             // MSI 向量数量
        &msi_domain_ops,          // 中断域操作
        &dw_pcie_msi_chip);      // MSI 芯片数据
    
    if (!pp->irq_domain) {
        dev_err(pp->dev, "irq domain init failed\n");
        return -ENXIO;
    }
    
    // 步骤 2: 为每个 MSI 向量创建映射
    for (i = 0; i < MAX_MSI_IRQS; i++)
        irq_create_mapping(pp->irq_domain, i);
}

/*
 * MSI 地址格式 (x86):
 * ┌─────────────────────────────────────────────┐
 * │ [63:32] = 0x00000000                        │
 * │ [31:20] = 0xFEE (固定)                     │
 * │ [19:12] = 0x00 (RH - Redirection Hint)    │
 * │ [11:4]  = 0x00 (DM - Destination Mode)   │
 * │ [3]     = 0                                │
 * │ [2]     = Trigger Mode (0=edge,1=level)   │
 * │ [1]     = Delivery (0=fixed,1=lowest)     │
 * │ [0]     = 0                                │
 * └─────────────────────────────────────────────┘
 *
 * MSI 数据格式:
 * ┌─────────────────────────────────────────────┐
 * │ [15:14] = 0                                │
 * │ [13:11] = Trigger Mode                     │
 * │ [10:8]  = Delivery Mode                    │
 * │ [7:0]   = Vector (中断向量号)               │
 * └─────────────────────────────────────────────┘
 */

10. PCIe 链路训练与状态检测

10.1 链路训练过程

┌──────────────────────────────────────────────────────────────────────────────┐
│                        PCIe 链路训练过程                                      │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  链路训练 (Link Training):                                                   │
│  - 上电后自动执行                                                            │
│  - 协商链路宽度和速度                                                        │
│  - 检测并纠正物理连接问题                                                    │
│                                                                              │
│  训练序列:                                                                   │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                                                                      │   │
│  │  1. Detect                                                         │   │
│  │     │  检测是否有 PCIe 设备连接                                      │   │
│  │     ▼                                                               │   │
│  │  2. Polling                                                         │   │
│  │     │  发送/接收 Training Sequences (TS1/TS2)                       │   │
│  │     │  协商链路宽度和速度                                            │   │
│  │     ▼                                                               │   │
│  │  3. Configuration                                                   │   │
│  │     │  确定链路参数                                                 │   │
│  │     │  设置 Lane 映射                                               │   │
│  │     ▼                                                               │   │
│  │  4. Recovery                                                         │   │
│  │     │  (可选) 错误恢复或速度更改                                     │   │
│  │     ▼                                                               │   │
│  │  5. L0 (正常操作状态)                                                │   │
│  │                                                                      │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  链路速度:                                                                   │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │  Gen1: 2.5 GT/s  (x1: 250 MB/s, x16: 4 GB/s)                        │   │
│  │  Gen2: 5.0 GT/s  (x1: 500 MB/s, x16: 8 GB/s)                        │   │
│  │  Gen3: 8.0 GT/s  (x1: ~1 GB/s, x16: ~16 GB/s)                       │   │
│  │  Gen4: 16.0 GT/s (x1: ~2 GB/s, x16: ~32 GB/s)                       │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

10.2 链路状态读取

/*
 * ============================================================
 * 读取 PCIe 链路状态
 * ============================================================
 */
static void pcie_get_link_status(struct pci_dev *pwd)
{
    u16 link_status;
    
    /* 读取 PCIe Capability + 0x12 (Link Status) */
    pcie_capability_read_word(pwd, PCI_EXP_LNKSTA, &link_status);
    
    /* 提取链路速度 */
    int speed = link_status & PCI_EXP_LNKSTA_SPEED;
    // PCI_EXP_LNKSTA_SPEED_2_5GT = 1 (Gen1)
    // PCI_EXP_LNKSTA_SPEED_5_0GT = 2 (Gen2)
    // PCI_EXP_LNKSTA_SPEED_8_0GT = 3 (Gen3)
    // PCI_EXP_LNKSTA_SPEED_16_0GT = 4 (Gen4)
    
    /* 提取链路宽度 */
    int width = (link_status & PCI_EXP_LNKSTA_NLW) >> 4;
    // 1=x1, 2=x2, 4=x4, 8=x8, 16=x16
    
    dev_info(&pwd->dev, "PCIe %s x%d\n", 
             speed == 3 ? "Gen3" : 
             speed == 2 ? "Gen2" : 
             speed == 1 ? "Gen1" : "Unknown",
             width);
}

11. 电源管理与高级特性

11.1 PCIe 电源状态

┌──────────────────────────────────────────────────────────────────────────────┐
                        PCIe 电源状态                                          
├──────────────────────────────────────────────────────────────────────────────┤
                                                                              
  PCI PM 状态 (D0-D3):                                                        
  ┌──────────────────────────────────────────────────────────────────────┐   
   D0: Active        - 全功率,完全正常工作                                 
   D1: Sleep         - 低功耗,唤醒较快                                     
   D2: Deep Sleep    - 更低功耗,唤醒较慢                                  
   D3hot: Hot        - 接近关闭,部分电源,可热插拔                         
   D3cold: Cold      - 完全关闭,无电源                                    
  └──────────────────────────────────────────────────────────────────────┘   
                                                                              
  ASPM (Active State Power Management):                                      
  ┌──────────────────────────────────────────────────────────────────────┐   
   L0s: Partial    - 轻量级低功耗状态,快速唤醒                             
   L1:  Sleep      - 中等低功耗状态,较慢唤醒                              
   L1.x: Sub-States - 更细粒度的 L1 控制                                 
   L2/L3 Ready:   - 完全低功耗状态                                        
  └──────────────────────────────────────────────────────────────────────┘   
                                                                              
└──────────────────────────────────────────────────────────────────────────────┘

12. 调试方法与实战技巧

12.1 常用调试命令

# 查看所有 PCI 设备
lspci -nn                      # 显示 ID
lspci -vvv                     # 详细输出
lspci -k                       # 显示驱动

# 查看设备详细信息
lspci -s 01:00.0 -vvv

# 查看完整配置空间
lspci -xxxx -s 01:00.0

# 查看 PCI 总线
cat /proc/bus/pci/devices

# 查看 sysfs
ls -la /sys/bus/pci/devices/
ls -la /sys/bus/pci/drivers/

# 读/写配置空间
setpci -s 00:1f.2 0.l         # 读
setpci -s 00:1f.2 4.w=7        # 写

12.2 内核调试

# 启用 PCI 调试
CONFIG_PCI_DEBUG=y
CONFIG_PCI_MSI=y

# 动态调试
echo "file drivers/pci/probe.c +p" > /sys/kernel/debug/dynamic_debug/control
echo "file drivers/pci/pci-driver.c +p" > /sys/kernel/debug/dynamic_debug/control

# 查看日志
dmesg | grep -iE "pci|pcie" | tail -100

总结

核心要点回顾

  1. 设备扫描: pci_scan_root_bus()pci_scan_child_bus()pci_scan_slot()pci_scan_device()pci_setup_device()

  2. 驱动匹配: device_attach()pci_bus_match()pci_match_device()pci_match_one_device()drv->probe()

  3. Host 驱动: platform_driver_register()imx_pcie_probe()dw_pcie_host_init()pci_scan_root_bus()

  4. 配置空间: x86 通过 IO 端口 (0xCF8/0xCFC), ARM 通过 ECAM/MMIO + ATU

  5. MSI 中断: 通过内存写请求触发,支持更多中断向量