DMA控制器接口函数

主要作用是配置DMA控制器并启动相应传输
s3c2440中关于公共DMA控制器的函数提供有:
s3c2410_dma_config()
s3c2410_dma_ctrl()
s3c2410_dma_enqueue()
s3c2410_dma_devconfig()
s3c2410_dma_set_buffdone_fn()
s3c2410_dma_request()
s3c2410_dma_free()
一般使用的顺序如下:
request =>set_buffdone_fn=> devconfig => config => enqueue => ctrl => free
1、int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev)

2、static inline void s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf, enum s3c2410_dma_buffresult result)
设置相应的dma通道完成一次dma传输后的回调函数

3、int s3c2410_dma_devconfig(int channel, enum s3c2410_dmasrc source, unsigned long devaddr)
source: S3C2410_DMASRC_HW: source is hardware
S3C2410_DMASRC_MEM: source is memory
devaddr: physical addr of source

4、int s3c2410_dma_config(unsigned int channel, int xferunit)
根据xferunit设置通道的控制寄存器DCONx
xferunit为每次传输的数据大小:0:byte 1:half word 2:word

5、dma_alloc_coherent
DMA要求使用non-cached, 物理地址连续的内存。
将对应的内核虚拟地址转化为物理地址,供给后面的s3c2410_dma_enqueue函数使用。

6、int s3c2410_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size)

7、int s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)
8、int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *client)

参考文章:
1. ​​​ s3c2410的dma操作的一般步骤​


DMA控制器接口函数使用实例

s3c2440 DMA datasheet

s3c2440 DMA控制器支持4种情况的DMA传输:
a. source and destination are in the system bus
b. source in the system bus while destination in the peripheral bus
c. source in the peripheral bus while destination in the system bus
d. source and destination are in the peripheral bus
但是在arch/arm/plat-s3c24xx/dma.c的函数s3c2410_dma_devconfig中却只实现了b和c两种情况。

s3c2440有4个DMA channel,每个channel都有DISC/DISCC/DIDST/DIDSTC寄存器,DISC和DIST可以填入不同source类型(具体参考S3c2440 Datasheet Table 8-1),除此之外还能填入内存的物理地址作为source或destination。

和传输相关的3个参数:
TSZ:DCON[28],0:unit模式:一次transfer1个data size,burst模式:一次transfer4个data size
DSZ:DCON[21:20],0:data size is BYTE,1:data size is Half WORLD,0:data size is WORLD
TC:DCON[19:0],transfer count
传输的数据量=TC * (DSZ * 8) * TSZ

request source: DCON[23]
0:S/W request mode, DMA is triggered by setting SW_TRIG bit of DMASKTRIG control register
1:DMA source selected by bit[26:24] triggers the DMA operation

service mode:DCON[27]
0:single service mode,一次DMA请求完成一次原子操作,等待下一次请求
1:whole service mode,一次DMA请求完成一批原子操作,当TC=0表示完成一次whole service

参考文章:
1. ​​​ S3C2410:DMA介紹​


使用cpu搬运内存与使用DMA搬运内存

下面这个例子我使用了s3c2440的公共DMA控制器,直接去配置寄存器并没有使用内核提供的arch/arm/plat-s3c24xx/dma.c文件中的DMA控制器接口函数,虽然效果是一样的,但最好应调用DMA控制器接口函数。
完整代码位置:​​dma_operation.c​​

static ssize_t s3c_mem_copy_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int i;
unsigned long time_s = 0, time_e = 0;

if (!buf && count < 1)
return -1;

printk("buf is: %s", buf);

memset(s3c_dma_dev->src, 0xaa, BUF_SIZE);
memset(s3c_dma_dev->dst, 0x55, BUF_SIZE);

switch (buf[0]) {
case MEM_CPY_NO_DMA:
time_s = jiffies;
for(i = 0;i < BUF_SIZE;i++)
s3c_dma_dev->dst[i] = s3c_dma_dev->src[i];
time_e = jiffies;

if (memcmp(s3c_dma_dev->src, s3c_dma_dev->dst, BUF_SIZE) == 0)
printk("MEM_CPY_NO_DMA OK\n");
else
printk("MEM_CPY_NO_DMA ERROR\n");
break;
case MEM_CPY_DMA:
s3c_dma_dev->dma_regs->disrc = s3c_dma_dev->src_phy; //src physical addr
s3c_dma_dev->dma_regs->disrcc = (0 << 1) | (0 << 0); //src in the AHB bus, addr INC
s3c_dma_dev->dma_regs->didst = s3c_dma_dev->dst_phy; //dst physical addr
s3c_dma_dev->dma_regs->didstc = (0 << 2) | (0 << 1) | (0 << 0); //intr when TC=0, dst in AHB bus, addr INC
s3c_dma_dev->dma_regs->dcon = (1 << 30) | (1 << 29) | (0 << 28) | (1 << 27) \
| (0 << 23) | (0 << 20) | (BUF_SIZE << 0); // enable intr, unit mode, soft trig

/* start DMA transfer */
time_s = jiffies;
s3c_dma_dev->dma_regs->dmasktrig = (1 << 1) | (1 << 0);
wait_for_completion(&s3c_dma_dev->comp);
time_e = jiffies;

if (memcmp(s3c_dma_dev->src, s3c_dma_dev->dst, BUF_SIZE) == 0)
printk("MEM_CPY_DMA OK\n");
else
printk("MEM_CPY_DMA ERROR\n");

break;

default:
break;
}

printk("use time: %ld\n", time_e - time_s);
return count;
}
static DEVICE_ATTR(oper, 0666, NULL, s3c_mem_copy_store);

static irqreturn_t s3c_dma_irq(int irq, void *devid)
{
complete(&s3c_dma_dev->comp);

return IRQ_HANDLED;
}

参考文章:
1. ​​​ S3C实现DMA驱动程序编写​