arm linux dma_map_single原理

static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
size_t size, enum dma_data_direction dir)
{
dma_addr_t addr;

BUG_ON(!valid_dma_direction(dir));

addr = __dma_map_single(dev, cpu_addr, size, dir);
debug_dma_map_page(dev, virt_to_page(cpu_addr),
		(unsigned long)cpu_addr & ~PAGE_MASK, size,
		dir, addr, true);
return addr;

}

static inline dma_addr_t __dma_map_single(struct device *dev, void *cpu_addr,
size_t size, enum dma_data_direction dir)
{
__dma_single_cpu_to_dev(cpu_addr, size, dir);
return virt_to_dma(dev, cpu_addr);
}

void ___dma_single_cpu_to_dev(const void *kaddr, size_t size,
enum dma_data_direction dir)
{
unsigned long paddr;

BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1));

dmac_map_area(kaddr, size, dir);

paddr = __pa(kaddr);
if (dir == DMA_FROM_DEVICE) {
	outer_inv_range(paddr, paddr + size);
} else {
	outer_clean_range(paddr, paddr + size);
}
/* FIXME: non-speculating: flush on bidirectional mappings? */

}

void ___dma_single_dev_to_cpu(const void *kaddr, size_t size,
enum dma_data_direction dir)
{
BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1));

/* FIXME: non-speculating: not required */
/* don't bother invalidating if DMA to device */
if (dir != DMA_TO_DEVICE) {
	unsigned long paddr = __pa(kaddr);
	outer_inv_range(paddr, paddr + size);
}

dmac_unmap_area(kaddr, size, dir);

}

dma_map_single:流式DMA相比一致性dma,这段mem是会被cache的,为了防止cache一致性问题,在调用dma_map_single函数时需要指定DMA的direction,linux会根据direction的值invalid 或者write back cache。 一致性DMA与流式DMA映射分析, 当驱动主要去分配一个DMA缓冲区并且该缓冲区的存在周期与所在的驱动模块 一样长时,就用一致性DMA映射, 这种映射在一开始为DMA操作分配缓冲区时就解决了cache一致性的问题. 如果驱动需要使用从别的模块传进来的地址空间作为DMA缓冲区,那就需要考虑使用流式DMA映射,这种映射对传 入的地址空间要求是,必须位于内核窨的线性映射区中, 驱动的处理主要是确保每次DMA操作前后cache的一致性问题.

__dma_map_single:该函数中的virt_to_dma 会把虚拟地址转换成bus地址 如果对总线地址的概念有疑问的话可以参考博客 链接如下:https://blog.csdn.net/alada007/article/details/7616434

__dma_single_cpu_to_dev:该函数会根据direction 操作cache

     --> outer_inv_cache:如果dma数据流向是from device即dma把hw 寄存器的值放到dma映射的mem中,说明该段mem已经背着cpu更新了,所以要把这段mem的cache invalid掉

      --> outer_clean_cache:dma控制器从mem中取数据到hw register处理的时候,防止数据只被取到cache中

dma_unmap_single: umap dma mem 调用到___dma_single_dev_to_cpu 也是inv cache

dmac_map_area & dmac_unmap_are :在map和unmap的时候会调用这对函数,追踪到cacheflush.h 中的全局变量cpu_cache.dma_map_area,cpu_cache最终定义在在arm/mm/cache-v6.s

arm/mm/cache-v6.s
ENTRY(v6_cache_fns)
.long v6_flush_icache_all
.long v6_flush_kern_cache_all
.long v6_flush_user_cache_all
.long v6_flush_user_cache_range
.long v6_coherent_kern_range
.long v6_coherent_user_range
.long v6_flush_kern_dcache_area
.long v6_dma_map_area
.long v6_dma_unmap_area
.long v6_dma_flush_range
.size v6_cache_fns, . - v6_cache_fns
遇到过dma unmap 过后cpu访问这段mem的时候数据不是预期的值有很多0 和0x6b6b6b6b ,可是如果用codeviser stop cpu一段时间再去访问这段mem 拿到的值就没问题,怀疑umap没起作用,在cpu访问之前调用下flush_cache_all,果然问题就不存在了
————————————————
版权声明:本文为CSDN博主「shenhuxi_yu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shenhuxi_yu/article/details/81676704

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值