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总线上的所有事务。初始化这一步,说白了就是让控制器从睡梦中醒过来,准备好干活。
我个人习惯把初始化分成三步走:
- 时钟和复位:先给PCIe控制器提供参考时钟(通常是100MHz),然后释放复位信号。
- 链路训练:控制器开始和下游设备握手,协商速率和通道数。
- 配置空间准备:分配总线号、设备号,让控制器进入可操作状态。
这里有个坑,我踩过好几次。有些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)喊名字。
具体流程是这样的:
- 扫描Bus 0:系统先扫描根总线(Bus 0),读取每个设备的Vendor ID和Device ID。如果读到0xFFFF,说明这个位置是空的。
- 发现桥设备:如果发现某个设备是PCIe桥(Type 1配置头),说明它下面还有二级总线。
- 递归扫描:给二级总线分配新的Bus号,然后继续扫描。直到所有总线都扫完。
- 分配资源:每个设备报告自己需要多少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驱动的核心——数据传输部分。