大家好,欢迎来到IT知识分享网。
DMA
DMA(Direct memory access) 特性允许在CPU参与的情况下外设访问DDR。如常见的ARM SOC,CPU core通过AXI master,经常NOC(Network on a chipe)路由到DDR AXI SLAVE接口,实现DDR访问。在SOC没有DMA特性下,如果DDR需要和外设之间搬移数据,只能通过CPU 指令,这样会耗费大量的CPU时间。如果有DMA,CPU配置好source address、destination address,还有搬移数据的长度,DMAC(DMA controller)会在CPU不参与下搬移数据,CPU可以做其他工作,在完成后有中断通知到CPU,进行相应的处理。有些DMAC还支持link listt模式,一次搬移多个不连续的DDR请求。
通常情况下SOC下的负载IP如SD/SDIO/eMMC controller[chapter 26], USB Controller[chapter 31]都自带专有的DMAC。其他相对简单的IP如串口、SPI共用内部的通用DMAC[chapter 19](general purpose direct memory access)。DMAC和CPU访问DDR大概架构如下。其中CPU和DMAC都有AXI master访问DDR,同时CPU可以通过APB总线配置DMAC相关寄存器。
图1 简单CPU和DMAC访问DDR架构图
通过图1可以看到CPU访问DDR和DMAC有一点不同时,路径上经过Cache。Cache是为了加速DDR访问的一种缓存机制。因为有cache存在,CPU和DMAC看到的同一个DDR地址空间可能不一致。有两种情况导致不一致,第一种是CPU写,DDR读。第二种是DDR写, CPU读。第一种的不一致例子如DDR 0x80000000地址处值为0,CPU往该地址写入值为2,因为cache为Write-back特性同时Cache可用,2只存在Cache中不会flush到DDR上,所以DMAC从DDR看到的值还是0。第二种不一致和类似。对于这种SOC需要CPU相关指令clean cache , invalid cache。ARM V7架构有一个Accelerator Coherency Port (ACP)接口,DMAC和CPU都通过Cache访问DDR达到缓存一致的效果,避免CPU操作cache。ACP位于图2中虚线箭头,为了区别ACP请求和普通DDR请求,经过ACP口的地址可以和普通的DDR地址不一致,NOC通过地址路由到相关的接口。这个地址的偏移ARM平台可以通过设备数的dma-ranges指定。如stm32mp151设备数的相关配置。
mlahb {
compatible = “simple-bus”;
#address-cells = <1>;
#size-cells = <1>;
dma-ranges = <0x00000000 0x38000000 0x10000>,
<0x10000000 0x10000000 0x60000>,
<0x30000000 0x30000000 0x60000>;
}
图2 带有ACP接口的DMAC
因为ARM PTE页表policy可以设置成Strongly-ordered(no-cache)特性,CPU访问绕过Cache,因此就没有数据不一致的问题。图3中虚线为CPU绕过Cache直接访问DDR,不存在数据不一致问题。
图3 stronger-order policy的DMAC和CPU访问路径
IOMMU
从上面的介绍可以看到,CPU和DMAC会同时访问内存,如果DMAC软件BUG造成越界写DDR,从CPU角度很难发现问题。对于不支持link list模式的DMAC搬移大量的数据时,可能由于系统内存碎片导致申请不到一大段连续内存,导致请求失败。鉴于这些原因在DMAC和DDR之间引入IOMMU。ARM也实现的IOMMU功能叫做SMMU。图4 带IOMMU的DMAC架构图。
图4 带有IOMMU的DMAC架构图
IOMMU和CPU侧的MMU类似机制,对于DDR请求会根据页表做相应的映射,再请求到页表对应DDR的物理地址。所以设定好DMAC IOMMU页表,可以固定DDR请求的物理地址,避免访问非法DDR物理地址。对于非法的范围,出发相应的异常中断。
Linux DMA架构
Linux下DMA的架构如下图5..对于内存申请,IOMMU映射,Cache的操作通过inlclude/linux/dma-mapping.h相关接口。include/linux/dmaengine.h为使用通用DMAC相关接口。对于自带DMAC的IP,只需要关注include/linux/dma-mapping.h相关接口。
图5 linux DMA架构
通用的DMAC会通过DMA Engine导出统一的API。通用的处理流程如下。具体可参考dmates.c。
Allocate a DMA slave channel
Set slave and controller specific parameters
Get a descriptor for transaction
Submit the transaction
Issue pending requests and wait for callback notification
其中申请给DMA使用的内存有两种类型,一种Consistent DMA mappings ,另外一种是Streaming DMA mappings 。Consistent DMA mappings也称作coherent DMA mapping,也就是驱动先申请一段stronger order内存,绕过cache达到数据一致性。Streaming DMA mappings使用的DDR normal policy带cache的内存,所以需要clean/invalid cache操作达到数据一致性。
Consistent DMA mapping API
这类接口适合驱动初始化时候分配好,后续的DMA相关操作固定使用申请的内存。申请接口为dma_alloc_coherent,释放接口为dma_free_coherent。
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
返回值为CPU 范围的地址, dma_handle为DMAC操作地址。主要的流程如下
dma_alloc_coherent ->dma_mmap_attrs->dev->dma_ops->alloc
dma_alloc_coherent
->dma_mmap_attrs
–>dev->dma_ops->alloc
其中dev->dma_ops是在platform bus绑定platform device 解析其device tree设置的,相应的流程如下
driver_probe_device
->platform_dma_configure
->of_dma_configure
—>of_dma_get_range //dma-ranges map
—>of_dma_is_coherent // coherent
—>of_iommu_configure_device // get iommu_ops
—>arch_setup_dma_ops
—->arm_setup_iommu_dma_ops
—–>arm_iommu_create_mapping
—–>arm_get_iommu_dma_map_ops // iommu_coherent_ops
其中arm_get_iommu_dma_map_ops会根据device tree中时候配置coherent,决定返回值。这里说的coherent也就是这个DMAC支持ACP,对于带有cache的DDR也不需要操作cache。这里分析不带coherent配置相关接口,也就是iommu_ops。继续上述的dev->dma_ops->alloc接口arm_iommu_alloc_attrs。
arm_iommu_alloc_attrs
->__arm_iommu_alloc_attrs
–>__iommu_alloc_buffer
–>__iommu_create_mapping //创建DMA address 到DDR地址映射,配置IOMMU 页表
–>dma_common_pages_remap //通过vmap接口创建stronger order内存映射
dma_common_pages_remap中的pte页表的配置通过__get_dma_pgprot。可以看到pte的配置是L_PTE_MT_UNCACHED。
static inline pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot)
{
prot = (attrs & DMA_ATTR_WRITE_COMBINE) ?
pgprot_writecombine(prot) :
pgprot_dmacoherent(prot);
return prot;
}
#define pgprot_dmacoherent(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED | L_PTE_XN)
#endif
Streaming DMA mapping API
Streaming DMA mapping适用于内存已分配好,操作相应的cache和IOMMU map给DMAC使用。例如用户态发送TCP/IP数据,数据包是协议栈通过alloc_skb分配的,是normal policy的带cache的内存,再发起DMA操作前需要操作cache。
dma_map_single 对内存分配操作cache,再进行IOMMU map操作返回DMA address。
static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
dma_map_single_attrs
->dev->dma_ops->alloc //如Consistent DMA mapping 在probe driver设定的
(arm_iommu_map_page)
–>__dma_page_cpu_to_dev //通过dir确定clean/invalid cache
–>arm_coherent_iommu_map_page //构建IOMMU映射,返回DMA address
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/29156.html