linux arm dma初始化,Arm linux dma mapping操作

Arm linux dma mapping

目 录

概述

由于处理器存在cache,cache和内存中数据可能不一致,所以驱动在使用dma在内存和device之间搬移数据前后需要cpu对cache和内存中数据进行同步。有些dma寻址能力有限,比如只能寻址内存低128m,但数据在内存的1G地址处,这时需要进行数据转移。

基本概念

cpu读取数据时先查询l1 cache,如果没有再向l2 cache

查询,如果没有在向内存查询,然后把数据从ddr 内存搬移到l2 cache和l1

cache。Cpu写数据,改变了l1 cache中内容,这时l1 cache可能把数据写会到ddr

内存,也可能过段时间再写。两个l1 cache和l2

cache之间数据同步,arm中按照moesi协议同步,这里不进行介绍。

基本概念:

PoU:在单核中在某一存储层级上,指令cache,数据cache和TLB在某一点上能够看到一致内容或相同copy,称为Pou;或者在an

Inner Shareable shareability

domain中,指令cache,数据cache和TLB在某一点上能够看到一致内容或相同copy,称为Pou;

PoC:系统中所有agents(系统中master,或者observer,例如,cpu,dma)看到的memory

一致点, 称为PoC.

例如,图中CPU core0和CPU core1为一组cluster0,CPU core2和CPU

core3为一组cluster1,

L2 cache为PoU,DDR memory为PoC。

Clean:把cache中指定地址内容写会到PoU或PoC。

Invalidate:

使cache中指定地址内容无效,如果cache中修改没有写回内存,那么这次修改会丢失。

Clean and invalidate:clean指令后执行invalidate指令。

写回(write back):修改了cache中内容后,并不把更新立即写到内存,把修改的cache

line标记为dirty。但执行clean操作时,把cache修改内容写的内存,或者,替换cache

line时,把cache修改内容写的内存。

写直通(write through):修改了cache中内容后,立即写到内存。

174350255_1_20191029101943534

Cache操作

写数据到device

cpu执行clean指令,把cache中写到内存

cpu配置dma,dma搬移内存中数据到device

从device读数据

cpu执行 invalidate指令,使cache中内容无效;

dma搬移device数据到内存

cpu处理数据,由于之前设置了cache无效,所以回从内存读取数据到cache

swio技术

swio技术,当dma寻址能力小于cpu分配的物理地址时,swio技术会从低端地址,比如0-64m中分配一块物理地址,建立低端物理地址和cpu分配的物理地址映射,并且后续dma使用低端dma地址进行数据搬移,如果是dma从物理内存搬移数据到设备

(DMA TO DEVICE

),那么cpu先把数据从cpu分配物理地址复制到低端物理地址,后续dma从低端物理地址搬移到设备。如果是dma从设备搬移数据到物理内存(DMA

FROM DEVICE

),那么dma从设备搬移到低端物理地址,然后通知cpu,cpu从低端物理地址复制数据到原高端物理地址,

但如果dma寻址能力够,就不需要这一步。

代码分析

arm64_dma_init

\arch\arm64\mm\dma-mapping.c 中设置dma_ops

static int \__init arm64_dma_init(void)

{

int ret = 0;

ret \|= swiotlb_late_init();

ret \|= atomic_pool_init();

return ret;

}

arch_initcall(arm64_dma_init);

static int \__init swiotlb_late_init(void)

{

size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES \

/\*

\* These must be registered before of_platform_populate().

\*/

bus_register_notifier(&platform_bus_type, \&platform_bus_nb);

注册notifier,当有bus注册到系统时,会回调这个bus_nb函数

bus_register_notifier(&amba_bustype, \&amba_bus_nb);

dma_ops = \&noncoherent_swiotlb_dma_ops; 设置了noncoherent ops

return swiotlb_late_init_with_default_size(swiotlb_size);

}

static int dma_bus_notifier(struct notifier_block \*nb,

unsigned long event, void \*_dev)

{

struct device \*dev = \_dev;

if (event != BUS_NOTIFY_ADD_DEVICE)

return NOTIFY_DONE;

如果dts中指定了bus dma是dma-coherent,那么设置ops为coherent_swiotlb_dma_ops。

if (of_property_read_bool(dev-\>of_node, "dma-coherent"))

set_dma_ops(dev, \&coherent_swiotlb_dma_ops);

return NOTIFY_OK;

}

看一下noncoherent dma ops

struct dma_map_ops noncoherent_swiotlb_dma_ops = {

.alloc = \__dma_alloc_noncoherent,

.free = \__dma_free_noncoherent,

.mmap = \__swiotlb_mmap_noncoherent,

.map_page = \__swiotlb_map_page,

.unmap_page = \__swiotlb_unmap_page,

.map_sg = \__swiotlb_map_sg_attrs,

.unmap_sg = \__swiotlb_unmap_sg_attrs,

.sync_single_for_cpu = \__swiotlb_sync_single_for_cpu,

.sync_single_for_device = \__swiotlb_sync_single_for_device,

.sync_sg_for_cpu = \__swiotlb_sync_sg_for_cpu,

.sync_sg_for_device = \__swiotlb_sync_sg_for_device,

.dma_supported = swiotlb_dma_supported,

.mapping_error = swiotlb_dma_mapping_error,

};

EXPORT_SYMBOL(noncoherent_swiotlb_dma_ops);

Cpu会调用dma_map_single

dma_map_single

例如:usb驱动收发数据

Cpu会调用dma_map_single进行clean或invalid

int usb_gadget_map_request(struct usb_gadget \*gadget,

struct usb_request \*req, int is_in)

{

req-\>dma = dma_map_single(&gadget-\>dev, req-\>buf, req-\>length,

is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

DMA_TO_DEVICE从内存搬移数据到device,DMA_FROM_DEVICE从device搬移数据到内存

return 0;

}

dma_map_single—》dma_map_single_attrs—》dma_map_single—》ops-\>map_page-》__swiotlb_map_page

static dma_addr_t \__swiotlb_map_page(struct device \*dev, struct page \*page,

unsigned long offset, size_t size,

enum dma_data_direction dir,

struct dma_attrs \*attrs)

{

dma_addr_t dev_addr;

dev_addr = swiotlb_map_page(dev, page, offset, size, dir, attrs);

swio技术,这里不再介绍,认为dma寻址能力足够;

这里先进行了dma地址和cpu内存物理地址,cpu虚拟地址转换

这里dma地址和cpu内存物理地址相同。这个由soc总线设计决定。

Cpu看到的内存地址和dma看到的内存地址可能不同。

\__dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);

return dev_addr;

}

\\arch\\arm64\\mm\\cache.S

/\*

\* \__dma_map_area(start, size, dir)

\* - start - kernel virtual start address

\* - size - size of region

\* - dir - DMA direction

\*/

ENTRY(__dma_map_area)

add x1, x1, x0

cmp w2, \#DMA_FROM_DEVICE 来自设备,就是从设备搬移数据到ap ddr中。

b.eq \__dma_inv_range

走这里,所以要先使cache中数据无效,后续dma搬移后,从ddr中直接读取到cache中。

b \__dma_clean_range 接着把cache中内容刷到内存中,

clean。确保之前cache修改内容,写回内存。

实际这里有没有必要执行,可以根据场景进行优化。

\__dma_inv_range: invalid就是使cache中内容无效,下次使用时需要从内存中重新读取

dcache_line_size x2, x3

sub x3, x2, \#1

tst x1, x3 // end cache line aligned?

bic x1, x1, x3

b.eq 1f

dc civac, x1 // clean & invalidate D / U line这里检查结束地址是否cache line

aligned,如果不对齐,需要把cacheline数据写到内存。因为cache

是按line处理,否则其它(非本次dma搬移地址)地址数据也变为无效,导致数据无法写会内存。

1: tst x0, x3 // start cache line aligned?

bic x0, x0, x3

b.eq 2f

dc civac, x0 // clean & invalidate D / U line 同上,起时地址

b 3f

2: dc ivac, x0 // invalidate D / U line 使无效

3: add x0, x0, x2

cmp x0, x1

b.lo 2b

dsb sy

ret

ENDPROC(__inval_cache_range)

ENDPROC(__dma_inv_range)

/\*

\* \__dma_clean_range(start, end)

\* - start - virtual start address of region

\* - end - virtual end address of region

\*/

\__dma_clean_range:

dcache_line_size x2, x3

sub x3, x2, \#1

bic x0, x0, x3

1: dc cvac, x0 // clean D / U line

add x0, x0, x2

cmp x0, x1

b.lo 1b

dsb sy

ret

ENDPROC(__dma_clean_range)

上面用到的invalidate指令,都是到PoC的。civac ,ivac

DC CIVAC, Xt Clean and invalidate by virtual address to Point of Coherency

DC IVAC, Xt Invalidate by virtual address to Point of Coherency

dma_unmap_single

void usb_gadget_unmap_request(struct usb_gadget \*gadget,

struct usb_request \*req, int is_in)

{

if (req-\>length == 0)

return;

if (req-\>num_mapped_sgs) {

dma_unmap_sg(&gadget-\>dev, req-\>sg, req-\>num_mapped_sgs,

is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

req-\>num_mapped_sgs = 0;

} else {

dma_unmap_single(&gadget-\>dev, req-\>dma, req-\>length,

is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

}

}

上面map_page,然后dma搬移,现在进行unmap_page

static void \__swiotlb_unmap_page(struct device \*dev, dma_addr_t dev_addr,

size_t size, enum dma_data_direction dir,

struct dma_attrs \*attrs)

{

\__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);

先进行cache操作

swiotlb_unmap_page(dev, dev_addr, size, dir, attrs); swio技术

}

/\*

\* \__dma_unmap_area(start, size, dir)

\* - start - kernel virtual start address

\* - size - size of region

\* - dir - DMA direction

\*/

ENTRY(__dma_unmap_area)

add x1, x1, x0

cmp w2, \#DMA_TO_DEVICE 如果是写数据到device,这里不需要再操作,

b.ne \__dma_inv_range 从device搬移到内存走这里,再次invalid

cache,防止dma搬移期间,cpu操作过这段地址? 特别是dma搬移的地址不是cache

line对齐场景。

ret

ENDPROC(__dma_unmap_area)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值