RK3588 kernel-6.1 — drivers/pci 设备枚举与配置空间探测流程
1. 文档目的与源码范围
| 项目 | 说明 |
|---|---|
| 基准路径 | rk3588/kernel-6.1/drivers/pci/ |
| 核心文件 | probe.c(扫描与 pci_dev 创建)、access.c(pci_bus_read_config_* → bus->ops->read) |
| 易混淆文件 | search.c(仅在内核已枚举设备链表中查找,不做硬件探测) |
本文回答:内核如何判定某个 (bus, devfn) 上是否存在 PCI 设备、“扫描”对应的软件操作是什么,以及与硬件侧 Configuration Read 的关系。
2. 结论摘要(协议视角)
- Linux PCI 核心 不会 单独发送一条高层“扫描命令”再由设备回报;枚举的本质是对 配置空间(Configuration Space)的读(及后续对桥的写)。
- 判断设备是否存在的第一道检查:对
PCI_VENDOR_ID(配置空间偏移 0) 做一次dword读,同时得到 Vendor ID(低 16 位) 与 Device ID(高 16 位)。 - 空槽或无响应时,读结果常为
0xFFFFFFFF(或错误完成、PCI_POSSIBLE_ERROR等);内核据此认为 无有效设备。 - CPU 侧调用
pci_bus_read_config_*;具体如何变成链路上的 PCIe Configuration TLP(Type 0 / Type 1)由 Host 控制器 / ECAM / SoC 路由 完成(如 RK3588 上 DesignWare PCIe RC 驱动注册的bus->ops)。
3. search.c 与 probe.c 的职责划分
3.1 search.c — 仅搜索已注册设备
实现 pci_find_bus、pci_get_slot、pci_get_device、pci_dev_present 等。
- 依赖
pci_bus_sem与全局 / 总线上的pci_dev链表。 - 不进行
pci_bus_read_config_*,因此 不参与发现新设备。
3.2 probe.c — 枚举与探测
pci_scan_child_bus:在一条 PCI 总线下扫描设备。pci_scan_slot→pci_scan_single_device→pci_scan_device:对给定(bus, devfn)做存在性判断并pci_device_add。
4. 判定设备是否存在:pci_bus_read_dev_vendor_id
入口:pci_scan_device() 调用 pci_bus_read_dev_vendor_id(bus, devfn, &l, timeout);其中 timeout 用于 CRS(Completion Retry Status) 重试场景。
逻辑要点(pci_bus_generic_read_dev_vendor_id):
pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)。- 若返回值表现为 错误响应、全 0、或部分板级异常的
0x0000ffff/0xffff0000,则视为 无设备。 - 若 Vendor ID 为 PCI‑SIG 保留值表示 CRS,则循环
msleep+ 重读,直到超时或得到合法 VID。
对应源码位置:probe.c 中 pci_bus_generic_read_dev_vendor_id、pci_scan_device。
5. 扫描调用链
1 | pci_scan_child_bus(bus) |
5.1 pci_scan_slot 与多功能
- 从
fn = 0开始pci_scan_single_device。 - 若 function 0 不存在且非虚拟机隔离等多函数场景,通常 不再扫描更高 function。
- 多功能设备通过
next_fn()/ ARI 扩展后续 function 号。
5.2 PCIe「仅 Device 0」优化
对典型 PCIe Downstream Port 下联链路,规范上通常为 单设备、logical Device 0。内核 only_one_child() 为真时 只扫描 devfn == 0,避免对 devfn 1..31 的无谓配置访问(除非 PCI_SCAN_ALL_PCIE_DEVS 等特殊拓扑)。
6. 配置访问如何落到硬件:access.c
pci_bus_read_config_dword(bus, devfn, pos, value)(及 byte/word 变体)在持 pci_lock(或可配置的 lockless 路径)后调用:
1 | bus->ops->read(bus, devfn, pos, len, &data) |
即:每条枚举用的配置读最终由 该 pci_bus 注册的 pci_ops 实现(ECAM MMIO、Rockchip/DesignWare 封装等均在此处对接)。
文件:drivers/pci/access.c(宏 PCI_OP_READ)。
7. 发现桥之后的扫描
当 pci_setup_device() 根据 Header Type 识别 PCI‑PCI / PCIe 桥 或 Root Port 后,会继续:
- 读写 总线号、窗口寄存器 等;
- 创建 子总线
pci_bus; - *递归
pci_scan_child_bus*。
此后对新一级 bus 上的设备再次重复 VID 读 → pci_setup_device。
8. RK3588 侧提示
- RK3588 常见为 DT + DesignWare PCIe Host,配置访问经
pci_ops映射到 控制器 MMIO(含 ECAM 区间)。 - 若
lspci看不到设备:除链路训练、时钟、复位管脚外,可从 首次 VID 读是否返回0xFFFFFFFF推断配置事务是否到达对端(结合earlycon/pci=earlydump等内核调试手段,若启用)。
9. 与其它文档的关系
| 文档 | 内容 |
|---|---|
| rk3588-kernel-6.1-pci总线架构分析.md | Host probe、pci_scan_root_bus_bridge 整体流程 |
| rk3588-kernel-6.1-pci核心层-probe-bus-setup-driver分析.md | probe/bus/setup/driver 分层 |
本文聚焦 「是否存在设备」的配置读判据与 pci_scan_* 调用链。
10. 修订记录
| 日期 | 说明 |
|---|---|
| 2026-05-08 | 初版:VID 探测、pci_scan_*、search.c 区分、bus->ops |
正在加载留言…