PCIe_Driver
目录
- 学习方法与调试准备
- PCI 子系统核心文件详解
- PCI 设备扫描完整流程 (逐层解析)
- 驱动匹配机制深度剖析
- Host 控制器驱动完全解析 (DesignWare)
- i.MX6 PCIe 驱动完整源码分析
- PCI 配置空间访问机制详解
- ATU 地址转换单元完全解析
- MSI/MSI-X 中断机制完全掌握
- PCIe 链路训练与状态检测
- 电源管理与高级特性
- 调试方法与实战技巧
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_dev | include/linux/pci.h:700+ | PCI 设备描述符 |
struct pci_driver | include/linux/pci.h:900+ | PCI 驱动描述符 |
struct pci_bus | include/linux/pci.h:400+ | PCI 总线描述符 |
struct pci_ops | include/linux/pci.h:350+ | 配置空间访问回调 |
struct pci_device_id | include/linux/pci.h:200+ | 设备 ID 表条目 |
struct pcie_port | pcie-designware.h | DesignWare 端口 |
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
总结
核心要点回顾
-
设备扫描:
pci_scan_root_bus()→pci_scan_child_bus()→pci_scan_slot()→pci_scan_device()→pci_setup_device() -
驱动匹配:
device_attach()→pci_bus_match()→pci_match_device()→pci_match_one_device()→drv->probe() -
Host 驱动:
platform_driver_register()→imx_pcie_probe()→dw_pcie_host_init()→pci_scan_root_bus() -
配置空间: x86 通过 IO 端口 (0xCF8/0xCFC), ARM 通过 ECAM/MMIO + ATU
-
MSI 中断: 通过内存写请求触发,支持更多中断向量