驱动之路#67:PCIe驱动略读(基于RK3576)

科创之家 2026-09-26 共8836人围观

本合集分享的是,我当初学习Linux驱动的来时路——《《驱动之路》开篇:自序&前言》。

正文

前面几篇文章,我们已经陆续聊了 PCIe 的一些基础概念:

PCIe是什么;PCI 和 PCIe 有什么区别;RC 和 EP 是什么;PCIe 为什么和地址空间有关;PCIe 如何传输数据。

这些内容更多是概念层面的。

到真正做 Linux BSP 开发时,最后还是会落到驱动和设备树上。

比如在 RK3576 平台上,PCIe 控制器驱动大致在:

rk3576/kernel-6.1/drivers/pci/controller/dwc/pcie-dw-rockchip.c

这篇文章标题里特意写了“略读”。

为什么是略读?

原因也很简单:

一是我个人水平有限,PCIe 驱动源码真要逐行啃,确实费劲;二是当时没有特别强的项目需求,硬啃源码属于费力不讨好;三是对我来说,先建立框架索引,比一上来死磕细节更有价值。

所以这篇不做源码逐行解析,只做一次“蜻蜓点水式”的阅读。

目标很明确:

知道 RK3576 PCIe 驱动入口在哪里,probe 大概做了什么,后面真遇到问题应该从哪几个点继续追。

1. 蜻蜓点水

RK3576 PCIe 控制器驱动路径:

drivers/pci/controller/dwc/pcie-dw-rockchip.c

从路径也能看出几个信息:

drivers/pci/controller:PCI 控制器驱动目录。dwc:基于 Synopsys DesignWare PCIe IP。pcie-dw-rockchip.c:Rockchip 平台对 DesignWare PCIe 控制器的适配代码。

也就是说,这个驱动不是从零实现一套 PCIe 协议栈。

更准确地说,它是在 Linux PCI 子系统和 DesignWare PCIe 框架之上,完成 Rockchip 平台相关的资源获取、寄存器配置、PHY/Reset/Clock 控制、链路建立、中断处理等工作。

可以简单理解为:

LinuxPCI Core    ↓DesignWare PCIe Framework    ↓Rockchip PCIe Glue Driver    ↓RK3576 PCIe Controller    ↓PCIe EP Device

这层关系先理清,后面看源码才不会迷路。

还是那句老话,看一个驱动先从入口函数开始, 当 driver 与 device 匹配成功会调用 probe 函数。

wKgZO2q0esKAOX8VAAHiN6hIN9s607.png

所以阅读这类驱动,第一步不是盯着一堆函数乱看,而是先找到:

platform_driverof_match_tableprobe

这三个位置。

从调用关系看,probe 里一般不会把所有事情都堆在一起。

RK 这类驱动里,probe 通常会继续调用内部初始化函数。

通过以上函数调用关系得知,rk_pcie_really_probe()才是实际进行初始化的函数。因此,我们接下来重点看它做了什么。

从源码可以看出,rk_pcie_really_probe() 主要做了如下几件事(源码有注释):

wKgZO2q0esKAM2cuAABOoPoegFU100.pngwKgZO2q0esOAOA49AAGJ9Q9mPMI227.pngwKgZO2q0esOARoSjAABLtzpAs58903.png

根据以上 rk_pcie_really_probe() 的内容,我们就可以顺藤摸瓜逐步分析源码。但我没有这样做,原因前面已经说了。我只是蜻蜓点水,点了下面几下。

第 1 下:驱动如何从 dts 中获取相关资源的。

wKgZO2q0esOAGXNCAAI0pkmQ-U4311.pngwKgZO2q0esOAdt0pAAIzVl5NFpU446.png

第 2 下:PCIe 链路建立(rk_pcie_establish_link)

wKgZO2q0esOASYBeAAFB8TbFLlo937.png

PCIe 链路检查(rk_pcie_link_up)

wKgZO2q0esSAJjFdAABaN04VTN8550.png

第 3 下:DMA 初始化(rk_pcie_init_dma_trx)

wKgZO2q0esSAK3XJAAFDHU0DaAA655.png

DMA 读传输接口(rk_pcie_start_dma_rd)

wKgZO2q0esSAPNkXAAFrje04mTU960.png

DMA 写传输接口(rk_pcie_start_dma_wr)

wKgZO2q0esSAP5UrAAGTUNC13V4778.png

第 4 下:系统中断(rk_pcie_sys_irq_handler)

wKgZO2q0esSAbuH5AAJHCHCCi7M915.png

第 5 下:热插拔支持

wKgZO2q0esSAeTRrAABWe4lOuNU523.png

2. 我的学习建议

最后说点个人感受。

PCIe 驱动源码确实比 I2C、SPI 难不少。

它不只是一个控制器驱动,还牵涉:

PCI子系统;DesignWare 框架;设备树资源;PHY;Clock / Reset;地址映射;BAR;DMA;MSI;链路训练;错误处理。

如果没有实际项目牵引,直接硬啃源码,很容易看半天只记住一堆函数名。

所以我现在更倾向于:

先看框架;再建索引;有问题再深挖;用项目驱动学习。

比如这次略读,我只点了几下:

第 1 下:资源怎么从 DTS 获取;第 2 下:链路怎么建立;第 3 下:DMA 怎么初始化和启动;第 4 下:系统中断怎么处理;第 5 下:热插拔相关逻辑在哪里。

蜻蜓点水,总共点了 5 下。

多一下我都不会了。

但对我来说,这已经够用了。

至少下次遇到 PCIe 设备起不来,不会再完全没方向。

3. 总结

RK3576 PCIe 控制器驱动可以先这样理解:

pcie-dw-rockchip.c  是 Rockchip 基于 DesignWare PCIe IP 的平台适配驱动。probe  是驱动入口。rk_pcie_really_probe()  是实际初始化主线。DTS 资源获取  决定 clock、reset、phy、reg、irq、lane 等基础条件。rk_pcie_establish_link()  负责尝试建立 PCIe Link。rk_pcie_link_up()  用于检查链路是否起来。DMA 相关函数  对应 PCIe 控制器的数据搬运能力。irq handler  对应控制器中断和异常处理。

一句话总结:读 PCIe 控制器驱动,不要一上来逐行死磕,先抓 probe 主线,再抓资源、Link、Host、DMA、IRQ 这几条线。

等真正遇到项目问题,再顺着对应链路往下追。

这样学起来没那么痛苦,也更符合实际工作节奏。

(完)

欢迎关注,每周更新!

审核编辑 黄宇

  • 随机文章
  • 热门文章
  • 热评文章
不容错过
Powered By Z-BlogPHP