RK3588 kernel-6.1 — drivers/pci 设备枚举与配置空间探测流程

RK3588 kernel-6.1drivers/pci 设备枚举与配置空间探测流程

1. 文档目的与源码范围

项目 说明
基准路径 rk3588/kernel-6.1/drivers/pci/
核心文件 probe.c(扫描与 pci_dev 创建)、access.cpci_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.cprobe.c 的职责划分

3.1 search.c — 仅搜索已注册设备

实现 pci_find_buspci_get_slotpci_get_devicepci_dev_present 等。

  • 依赖 pci_bus_sem 与全局 / 总线上的 pci_dev 链表
  • 不进行 pci_bus_read_config_*,因此 不参与发现新设备

3.2 probe.c — 枚举与探测

  • pci_scan_child_bus:在一条 PCI 总线下扫描设备。
  • pci_scan_slotpci_scan_single_devicepci_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):

  1. pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)
  2. 若返回值表现为 错误响应全 0、或部分板级异常的 0x0000ffff / 0xffff0000,则视为 无设备
  3. Vendor ID 为 PCI‑SIG 保留值表示 CRS,则循环 msleep + 重读,直到超时或得到合法 VID。

对应源码位置:probe.cpci_bus_generic_read_dev_vendor_idpci_scan_device


5. 扫描调用链

1
2
3
4
5
6
7
8
9
10
pci_scan_child_bus(bus)
└─ … 对每个 devfn / slot …
pci_scan_slot(bus, devfn) # 从 function 0 起,必要时遍历多功能
└─ pci_scan_single_device(bus, devfn + fn)
├─ pci_get_slot() 若已在内核链表中则直接返回
└─ pci_scan_device()
├─ pci_bus_read_dev_vendor_id() # 关键:VID/DID 读
├─ pci_alloc_dev()
└─ pci_setup_device() # 继续读 Class、Header、BAR、Cap…
└─ pci_device_add()

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

文章互动

阅读 --

留言

0 条留言

正在加载留言…