Rockchip RK3588 PCIe驱动开发:arm64 DMA映射操作指南 - 详解
Rockchip RK3588 PCIe驱动开发实战:arm64 DMA映射避坑与性能调优指南
你有没有遇到过这样的情况?
在RK3588上接了一块PCIe NVMe SSD,系统能识别,但跑 dd 命令时速度只有理论值的一半;
或者写了个FPGA采集卡驱动,DMA传输后读回来的数据全是0xAA——明明设备已经发了有效数据;
更糟的是,偶尔还会触发内核Oops,定位到 dma_map_single 返回了一个非法地址……
别急,这些“玄学”问题背后,几乎都指向同一个元凶: DMA映射配置不当 。
尤其是在Rockchip RK3588这类集成了SMMU的arm64平台中,传统的“物理地址直连”思维早已失效。本文将带你穿透Linux内核、IOMMU和硬件协同的迷雾,用一线调试经验告诉你: 为什么你的DMA不工作?怎么让它既稳定又高效?
一、从一次失败的DMA说起:你以为的“物理地址”,其实是个虚拟IO地址
我们先来看一个真实案例。
有位开发者在RK3588板子上接入一块自研的PCIe数据采集卡,驱动逻辑看似无懈可击:
void *buf = kmalloc(4096, GFP_KERNEL);
dma_addr_t phys = virt_to_phys(buf); // 错!大错特错!
pcie_write_reg(pdev, DMA_ADDR_REG, phys);
结果呢?设备压根收不到数据, dmesg 里还飘着一行警告:
[ 5.123456] dma-direct remap: failed to allocate at 0x00000000f0000000
问题出在哪?
关键就在于这句 virt_to_phys(buf) —— 它拿到的是 系统物理地址(PA) ,但在启用了SMMU的RK3588平台上,PCIe设备看到的根本不是这个地址!
实际流程是这样的:
CPU虚拟地址 (VA)
↓ (mmu)
CPU物理地址 (PA)
↓ (iommu/smmu)
IO虚拟地址 (IOVA) ← PCIe设备唯一可见的“地址”
↓ (页表翻译)
最终物理地址 (IPA → PA)
也就是说, 你必须通过内核提供的DMA API来获取那个设备真正能访问的IOVA ,而不是自己硬算。
✅ 正确做法:永远使用
dma_map_single()或dma_alloc_coherent()获取dma_addr_t,这才是设备该用的地址。
二、两种DMA映射模式:什么时候用Coherent?什么时候用Streaming?
Linux为arm64提供了两类DMA映射方式,选错一个,轻则性能下降50%,重则数据错乱。
1. 一致性DMA映射(Consistent Mapping)
适用于:控制寄存器共享内存、小块频繁交互缓冲区(≤4KB)
特点:
- 分配时自动保证cache一致性
- 内存通常来自CMA区域,分配稍慢
- 映射长期有效,适合常驻场景
典型调用:
void *vaddr;
dma_addr_t dma_handle;
vaddr = dma_alloc_coherent(&pdev-&g

浙公网安备 33010602011771号