DMA是一个独立的模块存在于处理器,DMA是不通过CPU而是直接访问内存,使用DMA,可以释放CPU的"压力",使得它不会一直在做一件事,使用了DMA也能达到直接使用CPU的效果
DMA的编写套路:
1. 注册DMA中断,分配缓冲区
2. 注册字符设备,并提供文件操作集合fops
3. 硬件相关操作
几点:
1.cat /proc/interrupts 发现DMA0和DMA1都被使用,所以这里使用SDMA1,需要设置SDMA_SEL寄存器来使能SDMA
2.使用SDMA的寄存器要先使能时钟,否则会造成寄存器不能读写
3.本例子只是用简单程序的拷贝来演示DMA的作用,所以采用字符设备方式编写
驱动代码如下
-
//moudle.h 包含了大量加载模块需要的函数和符号的定义
-
#include <linux/module.h>
-
//kernel.h以便使用printk()等函数
-
#include <linux/kernel.h>
-
//fs.h包含常用的数据结构,如struct file等
-
#include <linux/fs.h>
-
//uaccess.h 包含copy_to_user(),copy_from_user()等函数
-
#include <linux/uaccess.h>
-
//io.h 包含inl(),outl(),readl(),writel()等IO口操作函数
-
#include <linux/io.h>
-
#include <linux/miscdevice.h>
-
#include <linux/pci.h>
-
//init.h来指定你的初始化和清理函数,例如:module_init(init_function)、module_exit(cleanup_function)
-
#include <linux/init.h>
-
#include <linux/delay.h>
-
#include <linux/device.h>
-
#include <linux/cdev.h>
-
#include <linux/gpio.h>
-
#include <linux/irq.h>
-
#include <linux/sched.h>
-
#include <linux/interrupt.h>
-
#include <linux/poll.h>
-
//irq.h中断与并发请求事件
-
#include <asm/irq.h>
-
//下面这些头文件是IO口在内核的虚拟映射地址,涉及IO口的操作所必须包含
-
//#include <mach/gpio.h>
-
#include <mach/regs-gpio.h>
-
#include <plat/gpio-cfg.h>
-
#include <mach/hardware.h>
-
#include <mach/map.h>
-
#include <linux/clk.h>
-
-
#define MEM_CPY_NO_DMA 0
-
#define MEM_CPY_DMA 1
-
-
static int major = 0;
-
static struct class *cls;
-
-
static char *src; //源
-
static u32 src_phys; //源的物理地址
-
-
static char *dst; //目的
-
static u32 dst_phys; //目的的物理地址
-
-
#define BUF_SIZE (512*1024)
-
-
#define DMAC0_BASE_ADDR 0x75000000
-
#define DMAC1_BASE_ADDR 0x75100000
-
#define SDMAC0_BASE_ADDR 0x7DB00000
-
#define SDMAC1_BASE_ADDR 0x7DC00000
-
struct s3c_dma_regs { //dma寄存器
-
unsigned long DMACIntStatus;
-
unsigned long DMACIntTCStatus;
-
unsigned long DMACIntTCClear;
-
unsigned long DMACIntErrorStatus;
-
unsigned long DMACIntErrClr;
-
unsigned long DMACRawIntTCStatus;
-
unsigned long DMACRawIntErrorStatus;
-
unsigned long DMACEnbldChns;
-
unsigned long DMACSoftBReq;
-
unsigned long DMACSoftSReq;
-
unsigned long reserved1[2];
-
unsigned long DMACConfiguration;
-
unsigned long DMACSync;
-
unsigned long reserved2[50];
-
unsigned long DMACC0SrcAddr;
-
unsigned long DMACC0DestAddr;
-
unsigned long DMACC0LLI;
-
unsigned long DMACC0Control0;
-
unsigned long DMACC0Control1;
-
unsigned long DMACC0Configuration;
-
unsigned long DMACC0ConfigurationExp;
-
unsigned long reserved3;
-
unsigned long DMACC1SrcAddr;
-
unsigned long DMACC1DestAddr;
-
unsigned long DMACC1LLI;
-
unsigned long DMACC1Control0;
-
unsigned long DMACC1Control1;
-
unsigned long DMACC1Configuration;
-
unsigned long DMACC1ConfigurationExp;
-
unsigned long reserved4;
-
unsigned long DMACC2SrcAddr;
-
unsigned long DMACC2DestAddr;
-
unsigned long DMACC2LLI;
-
unsigned long DMACC2Control0;
-
unsigned long DMACC2Control1;
-
unsigned long DMACC2Configuration;
-
unsigned long DMACC2ConfigurationExp;
-
unsigned long reserved5;
-
unsigned long DMACC3SrcAddr;
-
unsigned long DMACC3DestAddr;
-
unsigned long DMACC3LLI;
-
unsigned long DMACC3Control0;
-
unsigned long DMACC3Control1;
-
unsigned long DMACC3Configuration;
-
unsigned long DMACC3ConfigurationExp;
-
unsigned long reserved6;
-
unsigned long DMACC4SrcAddr;
-
unsigned long DMACC4DestAddr;
-
unsigned long DMACC4LLI;
-
unsigned long DMACC4Control0;
-
unsigned long DMACC4Control1;
-
unsigned long DMACC4Configuration;
-
unsigned long DMACC4ConfigurationExp;
-
unsigned long reserved7;
-
unsigned long DMACC5SrcAddr;
-
unsigned long DMACC5DestAddr;
-
unsigned long DMACC5LLI;
-
unsigned long DMACC5Control0;
-
unsigned long DMACC5Control1;
-
unsigned long DMACC5Configuration;
-
unsigned long DMACC5ConfigurationExp;
-
unsigned long reserved8;
-
unsigned long DMACC6SrcAddr;
-
unsigned long DMACC6DestAddr;
-
unsigned long DMACC6LLI;
-
unsigned long DMACC6Control0;
-
unsigned long DMACC6Control1;
-
unsigned long DMACC6Configuration;
-
unsigned long DMACC6ConfigurationExp;
-
unsigned long reserved9;
-
unsigned long DMACC7SrcAddr;
-
unsigned long DMACC7DestAddr;
-
unsigned long DMACC7LLI;
-
unsigned long DMACC7Control0;
-
unsigned long DMACC7Control1;
-
unsigned long DMACC7Configuration;
-
unsigned long DMACC7ConfigurationExp;
-
};
-
-
static volatile struct s3c_dma_regs *dma_regs;
-
static volatile unsigned long *SDMA_SEL = NULL; //系统控制器的安全 DMA 控制寄存器
-
-
static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);
-
/* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */
-
static volatile int ev_dma = 0;
-
-
static long s3c_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-
{
-
int i;
-
-
memset(src, 0xAA, BUF_SIZE);//把源地址设为0xAA,长度BUF_SIZE
-
memset(dst, 0x55, BUF_SIZE);//把目的地址设为0x55,长度BUF_SIZE
-
-
switch (cmd)
-
{
-
case MEM_CPY_NO_DMA://不使用DMA
-
{
-
for (i = 0; i < BUF_SIZE; i++)
-
dst[i] = src[i];
-
if (memcmp(src, dst, BUF_SIZE) == 0)
-
printk("MEM_CPY_NO_DMA OK\n");
-
else
-
printk("MEM_CPY_NO_DMA ERROR\n");
-
break;
-
}
-
-
case MEM_CPY_DMA://使用DMA
-
{
-
ev_dma = 0;
-
-
/*开总的DMA 使能*/
-
dma_regs->DMACConfiguration = (1<<0);
-
-
/*通过写 DMACIntTCClr 和 DMACIntErrClr 寄存器,清除通道中要用到的未处理的中断。先前的通道操作可能使剩余的中断有效。*/
-
dma_regs->DMACIntTCClear = 0x11;
-
dma_regs->DMACIntErrClr = 0x11;
-
-
/* 如果外设的工作时钟与DMA控制器的时钟不相同, 要使能"同步逻辑" */
-
dma_regs->DMACSync = 0x0;
-
-
/*选用通道7*/
-
/*写源地址到 DMACCxSrcAddr 寄存器中*/
-
dma_regs->DMACC7SrcAddr = src_phys;
-
-
/*写目标址到 DMACCxDestAddr 寄存器中*/
-
dma_regs->DMACC7DestAddr = dst_phys;
-
-
/* 不使用 linked list */
-
dma_regs->DMACC7LLI = 0;
-
-
/*写DMACC7Control0和DMACC7Control1寄存器,设置DMA通道控制信息*/
-
dma_regs->DMACC7Control0 = (2 << 18) //源传输宽度,一次传输4字节(32bit)数据
-
|(2 << 21) //目的传输宽度,一次传输4字节(32bit)数据
-
|(1 << 26) //源增量。每个传输后,随设置的源地址递增
-
|(1 << 27) //目标增量。每个传输后,随设置的目标地址递增。
-
|(1 << 28) //保护
-
|(1 << 31); //终端计数中断启动位。控制是否当前的 LLI 期望引发终端计数中断
-
-
dma_regs->DMACC7Control1 = BUF_SIZE; //写入拷贝的size;
-
-
dma_regs->DMACC7Configuration |= (0<<18) // enable DMA requests
-
| (0<<16) // disables locked transfers
-
| (1<<15) // Teminal count interrupt enable
-
| (1<<14) // Interrupt error mask
-
| (0<<13)
-
| (0<<12)
-
| (0<<11) //传输模式,000表示内存到内存
-
| (1<<0); //开channel7 DMA
-
-
/* 如何知道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");
-
//s3c_dma_ioctl(file, cmd, arg);
-
}
-
-
break;
-
}
-
}
-
-
return 0;
-
}
-
-
static irqreturn_t s3c_dma_irq(int irq, void *devid)
-
{
-
/* 唤醒 */
-
ev_dma = 1;
-
dma_regs->DMACC7Configuration &= ~(1<<15);//屏蔽终端计数中断
-
wake_up_interruptible(&dma_waitq); /* 唤醒休眠的进程 */
-
-
return IRQ_HANDLED;
-
}
-
-
static struct file_operations dma_fops = {
-
.owner = THIS_MODULE,
-
.unlocked_ioctl = s3c_dma_ioctl,
-
};
-
-
static int s3c_dma_init(void)
-
{
-
struct clk *clk;
-
-
if (request_irq(IRQ_SDMA1, s3c_dma_irq, 0, "s3c_dma", 1))
-
{
-
printk("can't request_irq for DMA\n");
-
return -EBUSY;
-
}
-
-
/*分配SRC, DST对应的缓冲区*/
-
src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);
-
/*不能用kmalloc函数,因为kmalloc函数返回的虚拟地址虽然是连续的,但物理地址不一定连续,而dma不能处理不连续的物理地址*/
-
-
if (NULL == src)
-
{
-
printk("can't alloc buffer for src\n");
-
return -ENOMEM;
-
}
-
-
dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);
-
if (NULL == dst)
-
{
-
dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);//免得内存泄漏
-
printk("can't alloc buffer for dst\n");
-
return -ENOMEM;
-
}
-
-
SDMA_SEL = (volatile unsigned long *)ioremap(0x7E00F110 , 16);//系统控制器的安全 DMA 控制寄存器
-
/* 使用SDMA */
-
*SDMA_SEL = 0x0;
-
-
clk = clk_get(NULL, "sdma1");//struct clk *clk_get(struct device *dev, const char *id)
-
clk_enable(clk);//使能时钟,否则dma_regs寄存器不能读写
-
dma_regs = ioremap(SDMAC1_BASE_ADDR, sizeof(struct s3c_dma_regs));//使用SDMA1
-
-
major = register_chrdev(0, "s3c_dma", &dma_fops);
-
/* 为了自动创建设备节点 */
-
cls = class_create(THIS_MODULE, "s3c_dma");
-
device_create(cls, NULL, MKDEV(major, 0), NULL, "mydma"); /* /dev/mydma */
-
-
return 0;
-
}
-
-
static void s3c_dma_exit(void)
-
{
-
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);
-
iounmap(dma_regs);
-
iounmap(SDMA_SEL);
-
free_irq(IRQ_SDMA1, 1);
-
}
-
-
module_init(s3c_dma_init);
-
module_exit(s3c_dma_exit);
-
MODULE_LICENSE("GPL");
测试程序代码
-
#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 1
-
-
void print_usage(char *name)
-
{
-
printf("Usage:\n");
-
printf("%s \n", name);
-
}
-
-
-
int main(int argc, char **argv)
-
{
-
int fd;
-
-
if (argc != 2)
-
{
-
print_usage(argv[0]);
-
return -1;
-
}
-
-
fd = open("/dev/mydma", O_RDWR);
-
if (fd < 0)
-
{
-
printf("can't open /dev/mydma\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;
-
}
阅读(1850) | 评论(1) | 转发(0) |