/*
* The AMBA DMA API is modelled on the ISA DMA API and performs
* single asynchronous transfers between a device and memory
* i.e. some platform fixed device address and a driver defined memory address
/*
此AMBA DMA驱动,基于ISA DMA的API,好像应该就是那个DMA engine的架构吧,对应的,是这两个相关文件:
\include\linux\dmaengine.h
\drivers\dma\dmaengine.c
主要实现了异步传输,细看内部实现,就是,你设置好所有的参数之后,就提交你的请求后,然后此dma驱动会去在合适的时候帮你实现你的dma请求。因此,不保证是立刻就去执行你的请求的,此之所以称作异步。
正如上面的解释,常见的应用就是,
对应某个外设有某个固定的设备地址,一般都是某个FIFO的地址,或者DATA之类的寄存器,然后你的DMA请求是,从内存某个地址传输一定数据到你这个设备的FIFO或者data寄存器,即往你设备里面写数据,或者相反,从你的设备的FIFO地址中,读取一定量数据到内存某个位置,即从你设备里面读取数据。
*/
*
* Memory to peripheral transfer may be visualized as
* Get data from memory to DMAC
* Until no data left
* On burst request from peripheral
* Destination burst from DMAC to peripheral
* Clear burst request
* Raise terminal count interrupt
*
* For peripherals with a FIFO:
* Source burst size == half the depth of the peripheral FIFO
* Destination burst size == width of the peripheral FIFO
/*
关于提交DMA传输请求的时候,对于突发传输大小(burst size)的设置,虽然此处建议对于source burst size,设置成你的FIFO大小的一半,而对于destination burst size,设置为你的FIFO大小等同,但是,实际一般是根据你的外设控制器的设置而去具体设置的,比如你的nand flash控制器有个fifo是36个word,但是,其nand flash controller中关于burst size的说明是,,DMA模式时候,当fifo中小于4个word并且将要写入数据大于32个word的时候,才会发送write
burst信号给dma,要求burst传输,期望一下子传输32个word,这样,一下子传输32个word,写入到nand flash的fifo里面,这样就比dma传输一次一个word,即single word transfer的效率高多了。此时,你的destination burst size,就应该设置成32个word,之前,我以为也可以设置成16,8之类比32小的值,但是除了理论上理解的,没有充分利用硬件的能力、效率低之外,,实际上,驱动并不能正常工作,数据总是少不部分,就是说,硬件上人家有burst的dma请求了,就是已经准备了32个数据让你传,结果你只传输了部分,所以数据就少了一些,就乱了。一般来说,source
burst size,多数和destination burst size相等。具体,还要去看你的设备的datasheet。
*/
*
* (Bursts are irrelevant for mem to mem transfers - there are no burst signals)
*
* Details of each tranfer on a particular channel
* are held in the DMA channel array
* A workqueue uses those details to initiate the actual transfer
* The DMAC interrupt clears the relevant transfer in the channel array
*
* ASSUMES only one DMAC device exists in the system
* ASSUMES default (little) endianness for DMA transfers
*
* Only DMAC flow control is implemented
*
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
/*
此处之所以定义成union类型,就是方便,在设置好了之后,将此32位的值,直接写入对应的32位的寄存器中。
*/
union _cctl{
struct _cctl_data bits;
unsigned int val;
};
/*
下面这个就是DMA里面最核心的概念,LLI,Link List Item,包括了
(1)源地址;
(2)目标地址;
(3)下一个LLI的地址:如果其为0/NULL/空,说明当前只需要传输一个信息,如果非空,传完当前的LLI,就会跳转到对应的地址,执行下一个LLI的传输;
(4)对应的控制信息cctrl - channel control。
这四个值,会分别写入到对应的四个寄存器。
这样配置好了之后,再去启用DMA,DMA就会按照你的要求,把数据从源地址传送到目的地址。
下面的那个英文解释的意思是,next域,即下一个LLI的地址,其中的bit0,是bus bit,即指示当前用哪个bus,现将datasheet中相关解释截图如下:
图 1 LLI寄存器定义
如图1,对应的bit[0],LM位,就表示了,当前使用哪个AHB master,而真正的LLI的地址,是存放在bit[2-31]。
/*
* An LLI struct - see pl08x TRM
* Note that next uses bit[0] as a bus bit,
* start & end do not - their bus bit info
* is in cctl
*/
struct _lli{
dma_addr_t src;
dma_addr_t dst;
dma_addr_t next;
union _cctl cctl;
};
# error "AMBA PL08X DMA CANNOT BE COMPILED AS A LOADABLE MODULE AT PRESENT"
/*
a) Some devices might make use of DMA during boot
(esp true for DMAENGINE implementation)
b) Memory allocation will need much more attention
before load/unload can be supported
*/
#endif
struct pl08x_driver_data pd;
/*
* PL08X specific defines
*/
/* Minimum period between work queue runs */
#define PL08X_WQ_PERIODMIN 20
/* 对于其他使用此DMA 的驱动中,会去提交DMA请求,此处就是指一次DMA请求中,最大所允许的传输大小,此处是0x2000= 8192 */
/* Size (bytes) of each buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000
/* Maximimum times we call dma_pool_alloc on this pool without freeing */
# define PL08X_MAX_ALLOCS 0x40
/* 后面代码中会看到,对于你的驱动提交的DMA请求,此DMA驱动内部会自动帮你转换成对应的一个个LLI,此数值就是限制一次DMA请求中,最大支持多少个LLI */
#define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct _lli))
#define PL08X_ALIGN 8
#define PL08X_ALLOC 0
/* 一些全局的寄存器的偏移地址,这些值都是根据datasheet中定义出来的 */
/* Register offsets */
#define PL08X_OS_ISR 0x00
#define PL08X_OS_ISR_TC 0x04
#define PL08X_OS_ICLR_TC 0x08
#define PL08X_OS_ISR_ERR 0x0C
#define PL08X_OS_ICLR_ERR 0x10
#define PL08X_OS_CFG 0x30
#define PL08X_OS_CCFG 0x10
#define PL08X_OS_ENCHNS 0x1C
#define PL08X_OS_CHAN 0x20
#define PL08X_OS_CHAN_BASE 0x100
/* DMA控制器,有很多个通道channel,每个channel都对应有自己的一些寄存器,下面就是这些寄存器的地址偏移和位域值的含义 */
/* Channel registers */
#define PL08X_OS_CSRC 0x00
#define PL08X_OS_CDST 0x04
#define PL08X_OS_CLLI 0x08
#define PL08X_OS_CCTL 0x0C
/* register masks */
#define PL08X_MASK_CFG 0xFFFFFFF1
#define PL08X_MASK_EN 0x00000001
#define PL08X_MASK_CLLI 0x00000002
#define PL08X_MASK_TSFR_SIZE 0x00000FFF
#define PL08X_MASK_INTTC 0x00008000
#define PL08X_MASK_INTERR 0x00004000
#define PL08X_MASK_CCFG 0x00000000
#define PL08X_MASK_HALT 0x00040000
#define PL08X_MASK_ACTIVE 0x00020000
#define PL08X_MASK_CEN 0x00000001
#define PL08X_MASK_ENCHNS 0x000000FF
#define PL08X_WIDTH_8BIT 0x00
#define PL08X_WIDTH_16BIT 0x01
#define PL08X_WIDTH_32BIT 0x02
/* 下面这几个宏定义,好像没用到 */
/*
* Transferring less than this number of bytes as bytes
* is faster than calculating the required LLIs....
* (8 is the real minimum
* >7 bytes must have a word alignable transfer somewhere)
*/
#define PL08X_BITESIZE 0x10
/* 下面这个宏定义,好像也没用到,具体的流控制flow control的设置以及宏定义,在对应头文件中:
#define PL08X_CCFG_FCTL_MEM_TO_MEM (0)
#define PL08X_CCFG_FCTL_MEM_TO_PERI (1)
…….
*/
/*
* Flow control bit masks
*/
#define PL08X_FCMASK_M2M_DMA 0x00000000
#define PL08X_FCMASK_M2P_DMA 0x00000800
#define PL08X_FCMASK_P2M_DMA 0x00001000
#define PL08X_FCMASK_P2P_DMA 0x00001800
#define PL08X_FCMASK_P2P_DST 0x00002000
#define PL08X_FCMASK_M2P_PER 0x00002800
#define PL08X_FCMASK_P2P_PER 0x00003000
#define PL08X_FCMASK_P2P_SRC 0x00003800
/* Max number of transfers which can be coded in the control register */
#define PL08X_MAX_TSFRS 0xFFF
#define PL08X_CODING_ERR 0xFFFFFFFF
/* 根据设置的值,解码出实际位的宽度 */
static unsigned int pl08x_decode_widthbits(unsigned int coded)
{
/*
根据datasheet中的解释:
所以,目前只支持8.,16,32字节,位域的值分别是0,1, 2,所以此处判断小于3,才是有效的,然后1<<coded,得到的结果分别是1,2,4个字节。
*/
if (coded < 3)
return 1 << coded;