#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/irq.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <linux/poll.h>#include <linux/dma-mapping.h>#define MEM_CPY_NO_DMA 0#define MEM_CPY_DMA 1#define BUF_SIZE (512*1024)#define DMA0_BASE_ADDR 0x4B000000#define DMA1_BASE_ADDR 0x4B000040#define DMA2_BASE_ADDR 0x4B000080#define DMA3_BASE_ADDR 0x4B0000C0struct s3c_dma_regs { unsigned long disrc; unsigned long disrcc; unsigned long didst; unsigned long didstc; unsigned long dcon; unsigned long dstat; unsigned long dcsrc; unsigned long dcdst; unsigned long dmasktrig;};static int major = 0;static char *src;static u32 src_phys;static char *dst;static u32 dst_phys;static struct class *cls;static volatile struct s3c_dma_regs *dma_regs;static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);/* 中斷事件標誌, 中斷服務程序將它置1,ioctl將它清0 */static volatile int ev_dma = 0;static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int i; memset(src, 0xAA, BUF_SIZE); memset(dst, 0x55, BUF_SIZE); switch (cmd) { //這是非DMA模式 case MEM_CPY_NO_DMA : { for (i = 0; i < BUF_SIZE; i++) dst[i] = src[i]; //CPU直接將源拷貝到目的 if (memcmp(src, dst, BUF_SIZE) == 0)//這個函數見註釋2 { printk("MEM_CPY_NO_DMA OK\n"); } else { printk("MEM_CPY_DMA ERROR\n"); } break; } //這是DMA模式 case MEM_CPY_DMA : { ev_dma = 0; /* 把源,目的,長度告訴DMA */ /* 關於下面寄存器的具體狀況,咱們在註釋3裏面來詳細講一下 */ dma_regs->disrc = src_phys; /* 源的物理地址 */ dma_regs->disrcc = (0<<1) | (0<<0); /* 源位於AHB總線, 源地址遞增 */ dma_regs->didst = dst_phys; /* 目的的物理地址 */ dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* 目的位於AHB總線, 目的地址遞增 */ dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0); /* 使能中斷,單個傳輸,軟件觸發, */ /* 啓動DMA */ dma_regs->dmasktrig = (1<<1) | (1<<0); /* 如何知道DMA何時完成? */ /* 休眠 */ wait_event_interruptible(dma_waitq, ev_dma); if (memcmp(src, dst, BUF_SIZE) == 0) { printk("MEM_CPY_DMA OK\n"); } else { printk("MEM_CPY_DMA ERROR\n"); } break; } } return 0;}static struct file_operations dma_fops = { .owner = THIS_MODULE, .ioctl = s3c_dma_ioctl,};static irqreturn_t s3c_dma_irq(int irq, void *devid){ /* 喚醒 */ ev_dma = 1; wake_up_interruptible(&dma_waitq); /* 喚醒休眠的進程 */ return IRQ_HANDLED;}static int s3c_dma_init(void){ /* 這裏註冊一箇中斷,當DMA數據傳輸完畢以後會發生此中斷 */ if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1)) { printk("can't request_irq for DMA\n"); return -EBUSY; } /* 分配SRC, DST對應的緩衝區,關於此函數詳見註釋1 */ src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);//源 if (NULL == src) { printk("can't alloc buffer for src\n"); free_irq(IRQ_DMA3, 1); return -ENOMEM; } dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);//目的 if (NULL == dst) { free_irq(IRQ_DMA3, 1); dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); printk("can't alloc buffer for dst\n"); return -ENOMEM; } major = register_chrdev(0, "s3c_dma", &dma_fops);//註冊字符設備 /* 爲了自動建立設備節點 */ cls = class_create(THIS_MODULE, "s3c_dma"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */ dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));//這邊是將DMA控制寄存器映射到內核空間 return 0;}static void s3c_dma_exit(void){ iounmap(dma_regs); class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "s3c_dma"); dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys); free_irq(IRQ_DMA3, 1);}module_init(s3c_dma_init);module_exit(s3c_dma_exit);MODULE_LICENSE("GPL");
int memcmp(const void *cs, const void *ct, size_t count){ const unsigned char *su1, *su2; int res = 0; for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) if ((res = *su1 - *su2) != 0) break; return res;}
DISRCn | bit | Description | Initial State |
S_ADDR | [30:0] | 源起始地址 | 0x00000000 |
DISRCCn | bit | Description | Initial State |
LOC | [1] | 用於選擇源的位置 0:源在系統總線上 1:源在外設總線上 |
0 |
INC | [0] | 用於選擇地址是否自動增長 0:地址自動增長 1:地址固定不變(此時即使是burst 模式下,傳輸過程當中地址自動增長, 可是一旦傳輸完這一次數據,地址又變爲初值) |
0 |
DIDSTn | bit | Description | Initial State |
D_ADDR | [30:0] | 目的起始地址 | 0x00000000 |
DIDSTCn | Bit | Description | Initial State |
CHK_INT | [2] | 當設置爲自動加載時,用來選擇中斷髮生的時間 0:TC爲0是產生中斷 1:自動加載完成的時候產生中斷 |
0 |
LOC | [1] | 用於選擇目的設備的位置 0:目的設備在系統總線上 1:目的設備在外設總線上 |
0 |
INC | [0] | 用於選擇地址是否自動增長 0:地址自動增長 1:地址固定不變(此時即使是burst模式下,傳輸過程當中地址自動增長,可是一旦傳輸完這一次數據,地址又從新變爲初值) |
0 |
DCONn | Bit | Description | Initial State |
DMD_HS | [31] | 選擇爲Demand模式或者是握手模式 0:選擇爲Demand模式 1:選擇爲握手模式 這兩種模式下都是當發生請求時,DMA控制器開始傳輸數據而且發出 應 答信號,不一樣點是握手模式下,當DMA控制器收到請求撤銷信號,而且自 身發出應答撤銷信號以後才能接收下一次請求。而在Demand模式下,並 不須要等待請求撤銷信號,他只須要撤銷自身的應答信號,而後等待下一 次的請求。 |
0 |
SYNC | [30] | 選擇DREQ/DACK的同步 0:DREQ and DACK 與PCLK同步 1:DREQ and DACK 與HCLK同步 所以當設備在AHB系統總線時,這一位必須爲1,而當設備在APB系統 時,它應該被設爲0。當設備位於外部系統時,應根據具體狀況而定。 |
0 |
INT | [29] | 是否使能中斷 0:禁止中斷,用戶須要查看狀態寄存器來判斷傳輸是否完成 1:使能中斷,全部的傳輸完成以後產生中斷信號 |
0 |
TSZ | [28] | 選擇一個原子傳輸的大小 0:單元傳輸(一次傳輸一個單元) 1:突發傳輸(一次傳輸四個單元) |
0 |
SERVMODE | [27] | 選擇是單服務模式仍是總體服務模式 0:單服務模式,當一次原子傳輸完成後須要等待下一個DMA請求 1:總體服務模式,進行屢次原子傳輸,知道傳輸計數值到達0 |
0 |
HWSRCSEL | [26:24] | 爲每個DMA選擇DMA請求源 具體參見芯片手冊 |
000 |
SWHW_SEL | [23] | 選擇DMA源爲軟件請求模式仍是硬件請求模式 0:軟件請求模式,須要將寄存器DMASKTRIG的SW_TRIG置位 1:硬件請求模式 |
0 |
RELOAD | [22] | 是否自動從新裝載 0:自動重裝,當目前的傳輸計數值變爲0時,自動重裝 1:不自動重裝 RELOAD[1]被設置爲0以防無心識地進行新的DMA傳輸 |
0 |
DSZ | [21:20] | 要被傳輸的數據的大小 00 = Byte 01 = Half word 10 = Word 11 = reserved |
00 |
TC | [19:0] | 初始化傳輸計數 | 0000 |
DSTATn | Bit | Description | Initial State |
STAT | [21:20] | DMA控制器的狀態 00:DMA控制器已經準備好接收下一個DMA請求 01:DMA控制器正在處理DMA請求 |
00 |
CURR_TC | [19:0] | 傳輸計數的當前值 每一個原子傳輸減1 |
DCSRCn | Bit | Description | Initial State |
CURR_SRC | [30:0] | 當前的源地址 | 0x00000000 |
DCDSTn | Bit | Description | Initial State |
CURR_DST | [30:0] | 當前的目的地址 | 0x00000000 |
DMASKTRIGn | Bit | Description | Initial State |
STOP | [2] | 中止DMA操做 1:當前的原子傳輸完成以後,就中止DMA操做。若是當前沒有原子 傳輸正在進行,就當即結束。 |
|
ON_OFF | [1] | DMA通道的開/閉 0:DMA通道關閉 1:DMA通道打開,而且處理DMA請求 |
|
SW_TRIG | [0] | 1:在軟件請求模式時觸發DMA通道 |
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/ioctl.h>#include <string.h>/* ./dma_test nodma * ./dma_test dma */#define MEM_CPY_NO_DMA 0#define MEM_CPY_DMA 1void print_usage(char *name){ printf("Usage:\n"); printf("%s <nodma | dma>\n", name);}int main(int argc, char **argv){ int fd; if (argc != 2) { print_usage(argv[0]); return -1; } fd = open("/dev/dma", O_RDWR); if (fd < 0) { printf("can't open /dev/dma\n"); return -1; } if (strcmp(argv[1], "nodma") == 0) { while (1) { ioctl(fd, MEM_CPY_NO_DMA); } } else if (strcmp(argv[1], "dma") == 0) { while (1) { ioctl(fd, MEM_CPY_DMA); } } else { print_usage(argv[0]); return -1; } return 0; }