4、PCIe接口基础:PCIe协议简介、PCIe控制器初始化、PCIe设备枚举流程

好,咱们开始聊PCIe。说实话,做机顶盒WiFi驱动,PCIe接口是你绕不开的一道坎。市面上绝大多数的WiFi 6芯片,像MT7921、RTL8852这些,走的都是PCIe总线。你搞不定PCIe,WiFi驱动就根本跑不起来。

这一章,我带你把PCIe的底裤扒干净。咱们不讲那些虚头巴脑的理论,直接聚焦三个核心问题:PCIe到底是什么?控制器怎么初始化?设备怎么被找到?

4.1 PCIe协议简介:从并行到串行的进化

PCIe,全称是Peripheral Component Interconnect Express。说白了,就是用来连接CPU和外设的高速总线。

早年间大家用的是PCI,并行总线,32位或64位数据线一起跑。但并行总线有个硬伤——频率一高,信号之间互相干扰,根本稳不住。我记得十年前做老平台时,PCI总线上挂个千兆网卡,偶尔就会丢包,查了半天是信号质量问题。

PCIe改成了串行,用差分信号传输。一条lane(通道)就是一对发送线加一对接收线,共4根线。速度从Gen1的2.5GT/s,一路干到Gen5的32GT/s。你想想看,这个进步有多大。

这里有个关键点要记住:PCIe是点对点连接。不像PCI那样所有设备挂在一根总线上共享带宽。每个PCIe设备都有自己的专用通道,带宽独享。这对WiFi这种高吞吐设备来说,太重要了。

PCIe速率对照表(常用)

版本 单 Lane 速率 x1 带宽 x4 带宽
PCIe 2.0 5 GT/s 约 500 MB/s 约 2 GB/s
PCIe 3.0 8 GT/s 约 1 GB/s 约 4 GB/s
PCIe 4.0 16 GT/s 约 2 GB/s 约 8 GB/s

机顶盒里用的WiFi芯片,大部分是PCIe 2.0或3.0,x1或x2通道。够用了。但如果你做的是高端路由器方案,那得上PCIe 4.0甚至5.0。

4.2 PCIe控制器初始化:让硬件先醒过来

PCIe控制器,一般集成在SoC内部。它的任务就是管理PCIe总线上的所有事务。初始化这一步,说白了就是让控制器从睡梦中醒过来,准备好干活。

我个人习惯把初始化分成三步走:

  1. 时钟和复位:先给PCIe控制器提供参考时钟(通常是100MHz),然后释放复位信号。
  2. 链路训练:控制器开始和下游设备握手,协商速率和通道数。
  3. 配置空间准备:分配总线号、设备号,让控制器进入可操作状态。

这里有个坑,我踩过好几次。有些SoC的PCIe控制器默认是关闭的,你得先操作对应的GPIO或者Power Domain,把电源给上去。否则你读配置空间,永远返回0xFF。

实战小技巧

初始化完成后,可以用示波器抓PCIe的差分对。正常链路训练时,你会看到一连串的电气空闲退出序列(EIOS)。如果看不到,说明物理层就没通,先查硬件。

代码层面,Linux内核里PCIe控制器的初始化通常在pcie-rockchip.c或pcie-designware.c这类文件中。核心函数就是probe(),里面会调用phy_init()、dw_pcie_setup()这些接口。嗯,这里要注意,不同厂家的SoC,初始化流程大同小异,但寄存器地址千差万别。

4.3 PCIe设备枚举流程:系统怎么找到WiFi芯片

控制器初始化好了,接下来就是枚举。枚举,就是系统去扫描PCIe总线上到底挂了哪些设备。

这个过程,我建议你把它想象成一次点名。老师(CPU)拿着花名册(配置空间),挨个座位(Bus:Device:Function)喊名字。

具体流程是这样的:

  1. 扫描Bus 0:系统先扫描根总线(Bus 0),读取每个设备的Vendor ID和Device ID。如果读到0xFFFF,说明这个位置是空的。
  2. 发现桥设备:如果发现某个设备是PCIe桥(Type 1配置头),说明它下面还有二级总线。
  3. 递归扫描:给二级总线分配新的Bus号,然后继续扫描。直到所有总线都扫完。
  4. 分配资源:每个设备报告自己需要多少MMIO空间、多少I/O空间。系统统一分配基地址。

你可能会问,WiFi芯片在枚举时扮演什么角色?它就是个Endpoint(端点设备),老老实实待在某个Bus:Device下面,等着被系统发现。

注意:枚举失败常见原因

  • PCIe参考时钟不稳定,导致链路训练失败。
  • WiFi芯片的复位引脚没拉高,芯片处于复位状态。
  • 供电不足,芯片无法完成内部初始化。

我曾经遇到一个案子,WiFi芯片在低温下枚举失败。查了两天,最后发现是电源纹波太大,芯片内部PLL锁不住。换了颗LDO就解决了。

枚举完成后,你可以在终端用lspci命令查看结果。比如:

# lspci -vvv
01:00.0 Network controller: MEDIATEK Corp. MT7921 802.11ax Wireless Adapter
        Subsystem: MEDIATEK Corp. MT7921
        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Latency: 0, Cache Line Size: 64 bytes
        Interrupt: pin A routed to IRQ 38
        Region 0: Memory at 10000000 (64-bit, non-prefetchable) [size=1M]
        Capabilities: [c0] Power Management version 3
        Capabilities: [100] Advanced Error Reporting
        Kernel driver in use: mt7921e

看到这一行,说明你的WiFi芯片已经被系统认出来了。接下来,驱动就可以开始干活了。

4.4 避坑指南:我踩过的三个PCIe大坑

做PCIe驱动这么多年,有些坑我至今记忆犹新。分享给你,希望能帮你少走弯路。

  • 坑一:ASPM导致WiFi断流。ASPM是PCIe的电源管理功能,省电模式下会降低链路速率。但有些WiFi芯片对ASPM支持不好,进入L1状态后就醒不过来。我建议在调试阶段,先在BIOS或设备树里把ASPM关掉。
  • 坑二:MSI中断配置不对。PCIe设备支持MSI中断,比传统INTx效率高。但有些SoC的MSI控制器实现有bug,导致中断丢失。我曾经在某个平台上,WiFi吞吐量一高就卡死,最后发现是MSI中断向量分配冲突。
  • 坑三:DMA地址空间限制。32位PCIe设备只能访问4GB以下的物理内存。如果你的机顶盒内存大于4GB,WiFi芯片做DMA时可能会写到错误地址。解决办法是启用SWIOTLB或者IOMMU。

嗯,PCIe这块内容确实不少,但只要你把协议、初始化、枚举这三个核心搞明白,后面写驱动就会顺手很多。下一章,咱们就正式进入WiFi驱动的核心——数据传输部分。