现在的位置: 首页 > 综合 > 正文

mips一致性DMA映射、流式DMA映射的使用

2018年04月16日 ⁄ 综合 ⁄ 共 5247字 ⁄ 字号 评论关闭

一、流式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 inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
  10                                              size_t size,
  11                                              enum dma_data_direction dir,
  12                                              struct dma_attrs *attrs)
  13{
  14        struct dma_map_ops *ops = get_dma_ops(dev);
  15        dma_addr_t addr;
  16
  17        kmemcheck_mark_initialized(ptr, size);
  18        BUG_ON(!valid_dma_direction(dir));
  19        addr = ops->map_page(dev, virt_to_page(ptr),
  20                             (unsigned long)ptr & ~PAGE_MASK, size,
  21                             dir, attrs);
  22        debug_dma_map_page(dev, virt_to_page(ptr),
  23                           (unsigned long)ptr & ~PAGE_MASK, size,
  24                           dir, addr, true);
  25        return addr;
  26}

先来看一下get_dma_ops()这个函数,在/arch/mips/include/asm/dma-mapping.h定义:
static inline struct dma_map_ops *get_dma_ops(struct device *dev)
  15{
  16        if (dev && dev->archdata.dma_ops)
  17                return dev->archdata.dma_ops;
  18        else
  19                return mips_dma_map_ops;
  20}

因为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_ops mips_default_dma_map_ops = {
 304        .alloc_coherent = mips_dma_alloc_coherent,
 305        .free_coherent = mips_dma_free_coherent,
 306        .map_page = mips_dma_map_page,
 307        .unmap_page = mips_dma_unmap_page,
 308        .map_sg = mips_dma_map_sg,
 309        .unmap_sg = mips_dma_unmap_sg,
 310        .sync_single_for_cpu = mips_dma_sync_single_for_cpu,
 311        .sync_single_for_device = mips_dma_sync_single_for_device,
 312        .sync_sg_for_cpu = mips_dma_sync_sg_for_cpu,
 313        .sync_sg_for_device = mips_dma_sync_sg_for_device,
 314        .mapping_error = mips_dma_mapping_error,
 315        .dma_supported = mips_dma_supported
 316};

再回到ops->map_page,也就是会调用mips_dma_map_page

static dma_addr_t mips_dma_map_page(struct device *dev, struct page *page,
 201        unsigned long offset, size_t size, enum dma_data_direction direction,
 202        struct dma_attrs *attrs)
 203{
 204        unsigned long addr;
 205
 206        addr = (unsigned long) page_address(page) + offset;
 207
 208        if (!plat_device_is_coherent(dev))
 209                __dma_sync(addr, size, direction);
 210
 211        return plat_map_dma_mem(dev, (void *)addr, size);
 212}

主要来看__dma_sync,定义在arch/mips/mm/dma-default.c:

static inline void __dma_sync(unsigned long addr, size_t size,
 152        enum dma_data_direction direction)
 153{
 154        switch (direction) {
 155        case DMA_TO_DEVICE:
 156                dma_cache_wback(addr, size);
 157                break;
 158
 159        case DMA_FROM_DEVICE:
 160                dma_cache_inv(addr, size);
 161                break;
 162
 163        case DMA_BIDIRECTIONAL:
 164                dma_cache_wback_inv(addr, size);
 165                break;
 166
 167        default:
 168                BUG();
 169        }
 170}

这里我们看到分别对三种情况进行处理:
a、DMA_TO_DEVICE:把cache的数据刷回内存里,用于使能dma传输到外设之前。因为dma传输只会从内存拿数据,所以必须把cache的数据全部刷回到内存中;
b、DMA_FROM_DEVICE:把cache的数据置无效,用于dma已经传输完毕产生中断之后,准备从内存读取到驱动的buffer中。如果不把cache的数据置无效,
那么cpu就会直接从cache中取出旧的数据,不会到内存中去拿新的数据;
c、DMA_BIDIRECTIONALDMA_TO_DEVICE的效果一样。

在文件中/arch/mips/include/asm/io.h定义:

 589#define dma_cache_wback_inv(start, size)        _dma_cache_wback_inv(start, size)
 590#define dma_cache_wback(start, size)            _dma_cache_wback(start, size)
 591#define dma_cache_inv(start, size)              _dma_cache_inv(start, size)

具体的函数在arch/mips/mm/c-r4k.c中定义如下:

void __cpuinit r4k_cache_init(void)
{
     ......
                _dma_cache_wback_inv    = r4k_dma_cache_wback_inv;
1416            _dma_cache_wback        = r4k_dma_cache_wback_inv;
1417            _dma_cache_inv          = r4k_dma_cache_inv;
     ......
}

再来看plat_map_dma_mem,在/arch/mips/include/asm/mach-generic/dma-coherence.h中定义:

static inline dma_addr_t plat_map_dma_mem(struct device *dev, void *addr,
  15        size_t size)
  16{
  17        return virt_to_phys(addr);
  18}

arch/mips/include/asm/io.h中定义:

static inline unsigned 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 inline void *dma_alloc_coherent(struct device *dev, size_t size,
  61                                       dma_addr_t *dma_handle, gfp_t gfp)
  62{
  63        void *ret;
  64        struct dma_map_ops *ops = get_dma_ops(dev);
  65
  66        ret = ops->alloc_coherent(dev, size, dma_handle, gfp);
  67
  68        debug_dma_alloc_coherent(dev, size, *dma_handle, ret);
  69
  70        return ret;
  71}

从上面的分析可以知道ops->alloc_coherent会调用mips_dma_alloc_coherent();
在文件arch/mips/mm/dma-default.c中定义:
static void *mips_dma_alloc_coherent(struct device *dev, size_t size,
 101        dma_addr_t * dma_handle, gfp_t gfp)
 102{
 103        void *ret;
 104
 105        if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
 106                return ret;
 107
 108        gfp = massage_gfp_flags(dev, gfp);
 109
 110        ret = (void *) __get_free_pages(gfp, get_order(size));
 111
 112        if (ret) {
 113                memset(ret, 0, size);
 114                *dma_handle = plat_map_dma_mem(dev, ret, size);
 115
 116                if (!plat_device_is_coherent(dev)) {
 117                        dma_cache_wback_inv((unsigned long) ret, size);
 118                        ret = UNCAC_ADDR(ret);
 119                }
 120        }
 121
 122        return ret;
 123}
因为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的操作。

抱歉!评论已关闭.