打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
mips一致性DMA映射、流式DMA映射的使用(轉)
mips一致性DMA映射、流式DMA映射的使用
一、流式DMA:
1、一般的使用方法是:
dma_buf = (void *)__get_free_pages(GFP_ATOMIC|GFP_DMA, get_order(s->fragsize));
desc->snd_buffer = dma_buf;
desc->snd_dma = dma_map_single(NULL, desc->snd_buffer, s->fragsize, DMA_FROM_DEVICE);
_desc->saddr = desc->snd_dma;
kmalloc 和 _get_free_pages分配内存空间, 返回的是cache段的虚拟地址(在/mm/page_alloc.c中定义);
在文件include/asm-generic/dma-mapping-common.h中有:
#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, NULL);
static inlinedma_addr_tdma_map_single_attrs(struct device*dev, void *ptr, size_tsize, enum dma_data_directiondir, struct dma_attrs*attrs)
{
struct dma_map_ops*ops= get_dma_ops(dev);
dma_addr_taddr;
kmemcheck_mark_initialized(ptr, size);
BUG_ON(!valid_dma_direction(dir));
addr= ops->map_page(dev, virt_to_page(ptr),
(unsigned long)ptr& ~PAGE_MASK, size,dir, attrs);
debug_dma_map_page(dev, virt_to_page(ptr),
(unsigned long)ptr& ~PAGE_MASK, size, dir, addr, true);
return addr;
}
先来看一下get_dma_ops()这个函数,在/arch/mips/include/asm/dma-mapping.h定义:
static inlinestruct dma_map_ops*get_dma_ops(struct device*dev)
{
if (dev&& dev->archdata.dma_ops)
return dev->archdata.dma_ops;
else
return mips_dma_map_ops;
}
因为dev为NULL,所以会调用mips_dma_map_ops---->
在arch/mips/mm/dma-default.c中定义:
struct dma_map_ops*mips_dma_map_ops= &mips_default_dma_map_ops;static struct dma_map_opsmips_default_dma_map_ops= {
.alloc_coherent= mips_dma_alloc_coherent,
.free_coherent= mips_dma_free_coherent,
.map_page= mips_dma_map_page,
.unmap_page= mips_dma_unmap_page,
.map_sg= mips_dma_map_sg,
.unmap_sg= mips_dma_unmap_sg,
.sync_single_for_cpu= mips_dma_sync_single_for_cpu,
.sync_single_for_device= mips_dma_sync_single_for_device,
.sync_sg_for_cpu= mips_dma_sync_sg_for_cpu,
.sync_sg_for_device= mips_dma_sync_sg_for_device,
.mapping_error= mips_dma_mapping_error,
.dma_supported= mips_dma_supported
};再回到ops->map_page,也就是会调用mips_dma_map_page:
static dma_addr_tmips_dma_map_page(struct device*dev, struct page*page,
unsigned long offset, size_tsize, enum dma_data_directiondirection, struct dma_attrs*attrs)
{
unsigned long addr;
addr= (unsigned long) page_address(page) + offset;
if (!plat_device_is_coherent(dev))
__dma_sync(addr, size, direction);
return plat_map_dma_mem(dev, (void *)addr, size);
}主要来看__dma_sync,定义在arch/mips/mm/dma-default.c:
static inlinevoid __dma_sync(unsigned long addr, size_tsize,
enum dma_data_directiondirection)
{
switch (direction) {
case DMA_TO_DEVICE:
dma_cache_wback(addr, size);
break;
case DMA_FROM_DEVICE:
dma_cache_inv(addr, size);
break;
case DMA_BIDIRECTIONAL:
dma_cache_wback_inv(addr, size);
break;
default:
BUG();
}
}这里我们看到分别对三种情况进行处理:
a、DMA_TO_DEVICE:把cache的数据刷回内存里,用于使能dma传输到外设之前。因为dma传输只会从内存拿数据,所以必须把cache的数据全部刷回到内存中;
b、DMA_FROM_DEVICE:把cache的数据置无效,用于dma已经传输完毕产生中断之后,准备从内存读取到驱动的buffer中。如果不把cache的数据置无效,
那么cpu就会直接从cache中取出旧的数据,不会到内存中去拿新的数据;
c、DMA_BIDIRECTIONAL跟DMA_TO_DEVICE的效果一样。
在文件中/arch/mips/include/asm/io.h定义:
#define dma_cache_wback_inv(start, size)        _dma_cache_wback_inv(start, size)
#define dma_cache_wback(start, size)            _dma_cache_wback(start, size)
#define dma_cache_inv(start, size)              _dma_cache_inv(start, size)
具体的函数在arch/mips/mm/c-r4k.c中定义如下:
void __cpuinitr4k_cache_init(void)
{
......_dma_cache_wback_inv= r4k_dma_cache_wback_inv;
_dma_cache_wback= r4k_dma_cache_wback_inv;
_dma_cache_inv= r4k_dma_cache_inv;
......
再来看plat_map_dma_mem,在/arch/mips/include/asm/mach-generic/dma-coherence.h中定义:
static inlinedma_addr_tplat_map_dma_mem(struct device*dev, void *addr, size_tsize)
{
return virt_to_phys(addr);
}
arch/mips/include/asm/io.h中定义:
static inlineunsigned long virt_to_phys(volatile const void *address)
{
return (unsigned long)address- PAGE_OFFSET+ PHYS_OFFSET;
}
在/arch/mips/include/asm/mach-generic/spaces.h中定义:
#define PAGE_OFFSET(CAC_BASE+ PHYS_OFFSET)
#define CAC_BASE_AC(0x80000000, UL)#define PHYS_OFFSET_AC(0, UL)所以plat_map_dma_mem返回的是物理地址,用于填写到DMA的saddr中。
二、一致性DMA映射:
desc = dma_alloc_coherent(NULL, sizeof(audio_dmadesc_t), (dma_addr_t *)&dma_phyaddr, GFP_KERNEL);在文件arch/mips/include/asm/dma-mapping.h中定义:
static inlinevoid
*dma_alloc_coherent(struct device*dev, size_tsize, dma_addr_t*dma_handle, gfp_tgfp)
{
void *ret;
struct dma_map_ops*ops= get_dma_ops(dev);
ret= ops->alloc_coherent(dev, size, dma_handle, gfp);
debug_dma_alloc_coherent(dev, size, *dma_handle, ret);
return ret;
}
从上面的分析可以知道ops->alloc_coherent会调用mips_dma_alloc_coherent();
在文件arch/mips/mm/dma-default.c中定义:
static void
*mips_dma_alloc_coherent(struct device*dev, size_tsize, dma_addr_t* dma_handle, gfp_tgfp)
{
void *ret;
if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
return ret;
gfp= massage_gfp_flags(dev, gfp);
ret= (void *) __get_free_pages(gfp, get_order(size));
if (ret) {
memset(ret, 0, size);
*dma_handle= plat_map_dma_mem(dev, ret, size);
if (!plat_device_is_coherent(dev)) {
dma_cache_wback_inv((unsigned long) ret, size);
ret= UNCAC_ADDR(ret);
}
}
return ret;
}
因为dev为NULL,所以dma_alloc_from_coherent直接返回0。接着通过__get_free_pages分配空间得到一个cache段虚拟地址,再通过plat_map_dma_mem得到一个物理地址,
再把这段地址在cache中的数据刷回到内存,最后返回一个uncache段的虚拟地址。
但是我们在驱动程序里面一般都会用cache段的地址,这样会快很多,所以通过CAC_ADDR来转换得到cache段的虚拟地址:
info->drcmr_dat = CAC_ADDR(info->drcmr_dat);
从上述可知,当用一致性DMA映射时,用得到的cache段的虚拟地址进行读写完,准备开始DMA或者DMA传输完成时,还要手动地调用dma_cache_wback、dma_cache_inv对缓冲区进行操作。
而流式DMA映射就可以一直调用dma_map_single(DMA_TO_DEVICE / DMA_FROM_DEVICE)就可以了,因为这个函数里面就带了对cache的操作。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
kernel如何保证cache数据一致性
DMA动态映射指南
Dynamic DMA mapping Guide
PCI设备的DMA映射操作详解
sata DMA 的建立
Arm linux dma mapping操作
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服