之前一直没有接触过linux下的dma编程,总感觉很难很难,没敢接触过。最近公司事情少点 ,索性就了解了一下dma的编程,因为我对比较喜欢对寄存器进行操作 ,所以就以寄存器编程来说。
其实也就是一个GDMA_SA_X一个GDMA_DA_X 两个控制寄存器GDMA_CT0_X 和 GDMA_CT1_X MT7688 generic DMA controller 在datasheet开篇就这样介绍:
? Supports 16 DMA channels //支持16个信道
? Supports 32 bit address// 支持32位的地址
? Maximum 65535 byte transfer 最多支持65535字节的传输量
? Programmable DMA burst size (1, 2, 4, 8, 16 double word burst) 每次触发可以传输的双字数(双字单位)
? Supports memory to memory, memory to peripheral, peripheral to memory, peripheral to peripheral transfers.//不用解释吧
? Supports continuous mode.
? Supports division of target transfer count into 1 to 256 segments//每次传输可以最多分成256段
? Support for combining different channels into a chain.//上面说了一次数据最多传16双字也就是64字节
? Programmable hardware channel priority. //而数据传输可以最多分成256段 所以一个信道用? --Interrupts for each channel. //dma最多传16384字节的buffer大小。这里的一个 //chain估计可以装好几个channel 但是没用过!!!!
一:几个比较重要的寄存器。
1.1:GDMA_SA_X和GDMA_DA_X
这两个寄存器存放的是两个映射过后的物理地址,比如我使用dma是用的简单的mem to mem的功能,那么这两个地址是存放的我内存虚拟地址所对应的物理地址。
那问题来了?怎么确定对应的物理地址。
dst_vir = dma_alloc_coherent(NULL,100,&dst_phy,GFP_ATOMIC);//一致性映射 和流式映射关系不在此赘述
函数返回值dst_vir 就是向内存申请的100字节缓冲区的首地址。
100 缓冲区大小
dst_phy 申请下来的虚拟缓冲区dst_vir所对应的物理地址,这个 是申请的时候底层给的。
GFP_ATOMIC 这个是申请常用的符号 还有GFP_KERNEL
1.2:GDMA_CT0_X 和GDMA_CT1_X
这两个寄存器比较麻烦主要就是初始化上面介绍的时候的东西。
CT0
*reg_ct0 |=(0x01|0x01<<2|0x4<<3|0x100<<16);
*reg_ct1 |=(Channel <<3|32<<8|32<<16);
三:dma中断的申请
request_irq(7,mydma_handler,IRQF_DISABLED,"mydma",NULL)
第一个是dma中断对应芯片的num号
mydma_handle 是处理函数
。。。。。。。
中断里面一般要做的东西是
1、清除中断
2、查看dma是否运行成功
3、返回
我的是这样处理的
static irqreturn_t mydma_handler(int irq, void *dev_id)
{
int i =0;
*reg_int |=(0x01 << Channel);//清除中断 对应的channel 位写1就可
printk("in irq!\n");
for(i=0;i<100;i++)
{
printk("%2x ",dst_vir[i]);
}
printk("\n");
return IRQ_HANDLED;
}
全部的代码如下:
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "my_dma"
static dma_addr_t src_phy;
static dma_addr_t dst_phy;
static unsigned char* src_vir;
static unsigned char* dst_vir;
#define CMD_INIT_DMA 0x01
#define CMD_ENABLE_DMA 0x02
#define MY_DMA_BASE 0x10002800
#define Channel 0
unsigned long *reg_src,*reg_dst,*reg_ct0,*reg_ct1,*reg_int;
void ralink_regset(unsigned myreg,int data)
{
myreg |= data;
}
static int ralink_dma_init(void)
{
int i = 0;
*reg_src = src_phy;
*reg_dst = dst_phy;
*reg_ct0 |=(0x01|0x01<<2|0x4<<3|0x100<<16);
*reg_ct1 |=(Channel <<3|32<<8|32<<16);
*reg_int |=(0x01 << Channel);
for(i=0;i<100;i++)
{
src_vir[i]=i;
}
return 0;
}
void ralink_dma_enable(void)
{
*reg_ct0 |=(0x01 << 1);
}
static long my_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case CMD_INIT_DMA:
ralink_dma_init();
break;
case CMD_ENABLE_DMA:
ralink_dma_enable();
break;
default:
break;
}
return 0;
}
struct file_operations my_dma =
{
.owner = THIS_MODULE,
.unlocked_ioctl = my_dma_ioctl,
};
static irqreturn_t mydma_handler(int irq, void *dev_id)
{
int i =0;
*reg_int |=(0x01 << Channel);
printk("in irq!\n");
for(i=0;i<100;i++)
{
printk("%2x ",dst_vir[i]);
}
printk("\n");
return IRQ_HANDLED;
}
static int my_dma_init(void)
{
int result = register_chrdev(888,DEVICE_NAME,&my_dma);
if (result < 0) {
printk("register failed!liujianlong \n");
return -1;
}
src_vir = dma_alloc_coherent(NULL,100,&src_phy,GFP_ATOMIC);
if(src_vir == NULL)
{
unregister_chrdev(888, DEVICE_NAME);
printk("src_vir is NULL!\n");
return -1;
}
dst_vir = dma_alloc_coherent(NULL,100,&dst_phy,GFP_ATOMIC);
if(src_vir == NULL)
{
dma_free_coherent(NULL,100,src_vir,src_phy);
unregister_chrdev(888, DEVICE_NAME);
printk("dst_vir is NULL!\n");
return -1;
}
if(request_irq(7,mydma_handler,IRQF_DISABLED,"mydma",NULL)<0)
{
printk("request_irq failed!\n");
dma_free_coherent(NULL,100,src_vir,src_phy);
dma_free_coherent(NULL,100,dst_vir,dst_phy);
unregister_chrdev(888, DEVICE_NAME);
return -1;
}
reg_src = (unsigned long*)ioremap(MY_DMA_BASE+Channel * 0x1f,4);
if(reg_src == NULL)
{
printk("reg_src ioremap() failed!\n");
}
reg_dst = (unsigned long*)ioremap(MY_DMA_BASE+Channel * 0x1f+0x04,4);
if(reg_dst == NULL)
{
printk("reg_dst ioremap() failed!\n");
}
reg_ct0 = (unsigned long*)ioremap(MY_DMA_BASE+Channel * 0x1f+0x08,4);
if(reg_ct0 == NULL)
{
printk("reg_ct0 ioremap() failed!\n");
}
reg_ct1 = (unsigned long*)ioremap(MY_DMA_BASE+Channel * 0x1f +0x0c,4);
if(reg_ct1 == NULL)
{
printk("reg_ct1 ioremap() failed!\n");
}
reg_int = (unsigned long*)ioremap(0x10002a04 ,4);
if(reg_int == NULL)
{
printk("reg_int ioremap() failed!\n");
}
printk("address map successfully!\n");
return 0;
}
static void my_dma_exit(void)
{
#if 1
dma_free_coherent(NULL,100,src_vir,src_phy);
dma_free_coherent(NULL,100,dst_vir,dst_phy);
#endif
unregister_chrdev(888, DEVICE_NAME);
iounmap(reg_ct0);
iounmap(reg_ct1);
iounmap(reg_dst);
iounmap(reg_src);
}
module_init(my_dma_init);
module_exit(my_dma_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Liu Long");