一. Linux DMA简介
我们知道DMA是Direct Memory Access, 不需要CPU的参与,也可以直接访问内存中的数据。
CPU 虚拟地址:内核空间由于有MMU内存管理,正常使用的是虚拟地址
CPU物理地址:virtual memory system (TLB, page tables, etc) 会将CPU虚拟地址转换为物理地址。
总线地址: IO设备主要使用的是总线地址,如果一个设备在MMIO地址上有寄存器,或者如果它执行DMA来读取或写入系统存储器,那么该设备使用的地址就是总线地址。在一些系统中,总线地址与CPU物理地址相同,但通常情况下并非如此。IOMMU和主机桥可以在物理地址和总线地址之间产生任意映射。
在一些简单的系统中,设备可以直接对物理地址Y进行DMA。但在许多其他系统中,有IOMMU硬件可以将DMA地址转换为物理地址,例如,它将Z转换为Y。这是DMA API的部分原因:驱动程序可以向DMA_map_single()等接口提供虚拟地址X,它设置任何所需的IOMMU映射并返回DMA地址Z。然后驱动程序告诉设备进行DMA到Z,IOMMU将其映射到系统RAM中地址Y处的缓冲区。
为了让Linux能够使用动态DMA映射,它需要来自驱动程序的一些帮助,也就是说,它必须考虑到DMA地址应该只在实际使用时映射,并且在DMA传输后取消映射。
二. UFS DMA 概述
UFS作为块设备,读写操作进行时,数据传输的时候还是非常耗时的,如果一直需要靠CPU参与,势必会影响系统整体的调度,所有UFS子系统也会引入DMA机制,
(1)在数据准备阶段CPU参与,
(2)数据传输阶段CPU不参与,移交控制器交给DMA处理,
(3)数据传输处理完成后,DMA返还控制权给CPU。
三. UFS DMA 详解
UFS作为块设备传输,非常占用CPU的时间,为了解放CPU,UFS数据传输采用DMA的方式。
1 . 设置DMA的寻址能力
目前现在都是ARM64架构,64位系统,所以将DMA寻址能力设置为64位寻址,能够最大化DMA的寻址能力
ufshcd_set_dma_mask
/**
* ufshcd_set_dma_mask - Set dma mask based on the controller
* addressing capability
* @hba: per adapter instance
*
* Returns 0 for success, non-zero for failure
*/
static int ufshcd_set_dma_mask(struct ufs_hba *hba)
{
if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT) {
if (!dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(64)))
return 0;
}
return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32));
}
2 .分配UFS Host 数据结构的DMA内存
主要是包括UCD(Command UPIU, Response UPIU, PRDT), UTP Transfer Request Descriptor(UTRDL), UTP Task Management Request Descriprtor(UTMRDL)
(1) UTP Command Descriptor Base Address: 128Byte内存对齐
(2) UTP Transfer Request List Base Addr: 1KB内存对齐