在开始分析代码之前,先简要介绍一下DMA的基础知识。
DMA,Direct Memory Access,直接内存访问。
既然叫 直接内存访问,那么相对应地,应该就有 “间接的内存访问”。
间接的内存访问,我的理解是,就是指最常见的,我们利用CPU的指令,去从一个内存地址中读出数据,然后写到另外一个内存地址中,完成对应的赋值操作。
此过程,完全都是CPU去操作的,如果是单个这样的数据读取和写入,还没啥,但是如果数据量很大,比如我们用memcpy(addr1,addr2,1024)去从地址addr1地址开始,拷贝1024个字节到内存addr2处,那么CPU这段时间,就不要干别的事情了,就一直这么的给你读取,写入数据吧,另外的还有,常见于驱动中的,尤其是涉及到和外设打交道,我们让CPU从内存一个地址,读取了一个数据,然后写入到某个设备的FIFO或者DATA寄存器中,咋写入之前,常常会等待FIFO不是满的,然后才能写入数据,要从FIFO中读取数据,要等到FIFO不是空,才能读取,这样来来回回,会比较消耗资源。
鉴于此,才出现了DMA这个硬件设备,专门设计用来处理这些相对用CPU去操作这样的事情,效率很低,换做专门的硬件的DMA来负责数据的读取和写入,释放了CPU这个苦力,可以让,在DMA忙着数据传输的过程中,CPU去忙其他更重要的事情。而专门的DMA硬件负责这样的数据传输,效率也会更高。
之所以这样,才叫做内存直接访问的。
DMA传输,总的来说就是:
硬件上,会有对应的控制寄存器ctrl和配置寄存器config,
比如你想要从内存一个地址addr传输,N个字word(32bit)的数据到设备dev上,
那么你就要先去根据你的请求,去配置config寄存器,首先是传输方向,
是DMA_TO_DEVICE,然后是源地址source address是你的内存地址addr,
和目标destination address是你的dev的DATA寄存器地址,
然后要传输额transfer size是N个,每个位宽是32bit,
将源地址,目标地址,位宽,DMA传输方向设置好,
整理成一个结构,专有名称叫做LLI(Link List Item),把这个LLi设置到ctrl里面。
然后去enable DMA,DMA就可以按照你的要求,把数据传输过去了。
这样的DMA叫做single DMA传输,LLI中的next lli的域设置为空,表示就一个LLI要传输。
如果源地址或目标地址是多个分散的地址,叫做scatter/gather DMA,
就要将这些LLI组合一下,即将第一个LLI的next lli那个域,设置成下一个LLI的地址,
这样一个个链接起来,最后一个LLI的nex lli的域为空,这样设置好后,将第一个LLI的值写入到ctrl中,DMA就会自动地去执行第一个LLI的数据传输,传完后,发现next lli不为空,就找到next lli的位置,
找到对应的配置,开始这个lli的数据传送,直至传完所有的数据为止。
说完了DMA的由来和基本概念后,下面来分析一下,具体的ARM的PL08x驱动是如何实现的。