1. RK3568 PCIe 硬件架构概述
各位同学好,我是老李。做嵌入式Linux驱动开发十几年了,RK3568这颗芯片我摸过不少。今天咱们开始第一讲——PCIe硬件架构。说白了,就是看看RK3568肚子里那个PCIe控制器到底长什么样,怎么工作的。
我个人习惯,做驱动之前一定要先把硬件摸透。你想想看,连硬件怎么跑的你都不清楚,写出来的驱动能稳吗?
1.1 控制器配置能力:Gen3x2 vs Gen2x1
RK3568内部集成了一个PCIe控制器,但它有两种工作模式:
| 模式 | 通道数 | 单通道速率 | 总带宽 | 典型场景 |
|---|---|---|---|---|
| Gen3 x2 | 2条 | 8 GT/s | ~16 GT/s | NVMe SSD、高速网卡 |
| Gen2 x1 | 1条 | 5 GT/s | ~5 GT/s | WiFi模块、低速外设 |
这里有个关键点:两种模式不能同时用。你只能在设备树里选一种。我在项目中遇到过,有人想同时接NVMe和WiFi,结果发现控制器只能选一种配置——嗯,踩坑了才知道。
注意:Gen3 x2模式下,PCB走线必须做阻抗匹配(85Ω差分对),长度差控制在5mil以内。否则链路训练会失败,我吃过这个亏。
1.2 内部DMA引擎
PCIe控制器内部集成了一个DMA引擎,负责搬运数据。它支持两种传输方式:
- PIO模式:CPU直接读写PCIe配置空间和BAR空间。适合小数据量,比如配置寄存器。
- DMA模式:硬件自动搬运,不占CPU。适合大数据量,比如NVMe读写。
DMA引擎的核心寄存器组如下:
// DMA控制寄存器(基地址偏移0x1000)
#define DMA_CTRL 0x00
#define DMA_SRC_ADDR 0x04
#define DMA_DST_ADDR 0x08
#define DMA_TRANS_SIZE 0x0C
#define DMA_STATUS 0x10
我个人建议,能用DMA就别用PIO。之前有个项目,用PIO传4K数据,CPU占用率直接飙到80%。换成DMA后,降到5%不到。
1.3 MSI中断控制器
RK3568的PCIe控制器支持MSI(Message Signaled Interrupts),也就是消息信号中断。它和传统的中断引脚(INTx)不一样:
| 特性 | MSI | INTx |
|---|---|---|
| 中断方式 | 写内存消息 | 拉低引脚电平 |
| 中断向量数 | 最多32个 | 4个(A/B/C/D) |
| 共享中断 | 不支持 | 支持 |
| 性能 | 高(无轮询) | 低(需电平检测) |
为什么MSI性能更好?因为它直接写内存触发中断,不需要CPU去检测引脚电平。我曾经在调试NVMe驱动时,发现中断响应延迟不稳定,后来查出来是INTx共享中断导致的。换成MSI后,延迟稳定在微秒级。
小技巧:在设备树中使能MSI,需要在PCIe节点中添加
msi-parent = <&gic>。否则内核会回退到INTx。
1.4 PHY层初始化流程
PHY层是PCIe的物理层,负责信号的串并转换和链路训练。初始化流程大致如下:
- 上电复位:PHY进入复位状态,等待电源稳定。
- PLL锁定:内部锁相环锁定参考时钟(100MHz)。
- 链路训练:发送训练序列(TS1/TS2),协商速率和通道数。
- 链路确认:双方确认链路宽度和速率,进入L0状态。
这里我画了一张流程图,帮你理清整个初始化过程:
链路训练这一步最容易出问题。我曾经调试一块板子,PCIe死活训练不过去。查了两天,最后发现是参考时钟的100MHz晶振精度不够,换了±25ppm的晶振就好了。
核心要点:
- RK3568 PCIe控制器支持Gen3 x2和Gen2 x1两种模式,二选一
- DMA引擎是性能关键,大数据传输必须用DMA
- MSI中断比INTx更高效,优先使用
- PHY初始化中链路训练是最大坑点,注意硬件设计质量