Chinaunix首页 | 论坛 | 博客
  • 博客访问: 110457
  • 博文数量: 24
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 270
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-30 18:17
文章分类

全部博文(24)

文章存档

2010年(21)

2009年(3)

我的朋友

分类: LINUX

2010-02-03 16:37:46

一,前言:DMA是英文Direct Memory Access的缩写。顾名思义,它是一种不经过CPU而直接从内存存取数据的数据交换模式。进一步的我们可以想到它需要设置源地址,目标地址,存取字节数,存取开始的使能或者请求,还有存取完成后产生中断通知CPU这几块。

     (以下分析基于版本:linux-2.6.19.2

二,先看一下/arch/arm/plat-mxc/dma_mx2.c

int __init mxc_dma_init(void)

{

     int i;

     mxc_dma_channel_t *dma = g_dma_channels;

     mx2_dma_priv_t *private = g_dma_privates;

 

     memset(dma, 0, sizeof(mxc_dma_channel_t) * MXC_DMA_CHANNELS);

     for (i = 0; i < MXC_DMA_CHANNELS; i++, dma++, private++) {

         dma->channel = i;

         dma->private = private;

         private->dma_base =

             (unsigned int)(IO_ADDRESS(DMA_BASE_ADDR + DMA_CH_BASE(i)));

         private->dma_irq = i + MXC_DMA_INTR_0;    /*Dma channel interrupt number */

         private->bd_ring = &g_dma_bd_table[i][0];

     }

 

     mxc_dma_load_info(g_dma_channels);

 

     dma_clk = clk_get(NULL, "dma_clk");

     clk_enable(dma_clk);

 

     __raw_writel(0x2, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); /*reset DMA; */

 

     disable_dma_clk();

 

     /*use module init because create_proc after init_dma */

     g_proc_dir = create_proc_entry("dma", 0, NULL);

     g_proc_dir->read_proc = (read_proc_t *) mxc_get_dma_list;

     g_proc_dir->data = NULL;

     ……

     return 0;

}

上面这段代码做了几件事情:初始化了几个全局变量,获得DMA控制器的时钟,复位DMA,并在/proc下面加了一项dma

16DMA通道,16g_dma_channels成员,16g_dma_privates成员,16g_dma_bd_table[MAX_BD_SIZE]成员,而且g_dma_privatesg_dma_channelsprivate成员,g_dma_bd_table[MAX_BD_SIZE]又是g_dma_privatesbd_ring成员,一环接一环,连成一线。

Cat /proc/dma会产生一些DMA使用的信息。

wolver:/mnt/sd# cat /proc/dma

dma channel  0: unused

dma channel  1: unused

dma channel  2: unused

dma channel  3: unused

dma channel  4: unused

dma channel  5: unused

dma channel  6: unused

dma channel  7: unused

dma channel  8: unused

dma channel  9: unused

dma channel 10: unused

dma channel 11: unused

dma channel 12: unused

dma channel 13: unused

dma channel 14: unused

dma channel 15: unused

它执行的是下面这段代码:

static int mxc_get_dma_list(char *buf)

{

     mxc_dma_channel_t *dma;

     char *p = buf;

     int i;

 

     for (i = 0, dma = g_dma_channels; i < MAX_DMA_CHANNELS; i++, dma++) {

         if (dma->lock) {

              p += sprintf(p, "dma channel %2d: %s\n", i,

                        dma->dev_name ? dma->dev_name : "unknown");

         } else {

              p += sprintf(p, "dma channel %2d: unused\n", i);

         }

     }

 

     return p - buf;

}

三,分析其它代码前,先看一个使用DMA的简单驱动。

#include

#include

#include

#include

#include

#include

#include     

 

#include

#include

#include

 

#include

#include

 

#include

 

static volatile int g_dma_done = 0;

static int g_status = 0;

 

#define DMA_INTERVAL_MEM 0x2000

#define MEM_DATA 0x27

 

static void dma_complete_fn(void * args, int error, unsigned int count)

{

     if(error != MXC_DMA_DONE) {

         printk("==========dma transfer is failure[%x]==========\n", error);

     } else {

         printk("==========dma transfer is success==========\n");

     }

     g_status = error;

     g_dma_done = 1;

}

 

static int __init ram2ram_test_init(void)

{

     dmach_t channel;

     mxc_dma_requestbuf_t buf;

     int count = 0,i;

     char * memory_base, * src, * dest;

     channel = mxc_dma_request(MXC_DMA_TEST_RAM2RAM, "dma test driver");

     if ( channel < 0 ) {

         printk("request dma fail n");

         return -ENODEV;

     }

    

     memory_base = __get_free_pages(GFP_KERNEL|GFP_DMA, 4);

     if ( memory_base == NULL ) {

         mxc_dma_free(channel);

         return -ENOMEM;

     }

 

     mxc_dma_callback_set(channel, dma_complete_fn, NULL);

     src = memory_base;

     dest = src+DMA_INTERVAL_MEM;

     memset(src, MEM_DATA, DMA_INTERVAL_MEM);

     memset(dest, 0, DMA_INTERVAL_MEM);

     buf.src_addr = virt_to_phys(src);

     buf.dst_addr = virt_to_phys(dest);

     buf.num_of_bytes = DMA_INTERVAL_MEM;

     mxc_dma_config(channel, &buf, 1, DMA_MODE_READ);

     mxc_dma_enable(channel);

     for(count =0; count<100; count++) {

         if(g_dma_done) {

              printk("dma transfer complete: error =%x\n", g_status);

              break;

         }

         set_current_state(TASK_UNINTERRUPTIBLE);

         schedule_timeout(100*(HZ/1000));

         set_current_state(TASK_RUNNING);

     }

     if (count >= 100) {

         mxc_dump_dma_register(channel);

         printk("dma transfer timeout\n");

         mxc_dma_disable(channel);

     } else {

         for(i=0;i

              if(dest[i]!=MEM_DATA)

                   break;

         }

         if(i==DMA_INTERVAL_MEM)

              printk("dest data correct\n");

         else

              printk("dest data is not the same\n");

     }

     free_pages(memory_base, 4);

     mxc_dma_free(channel);

}

 

static void __exit ram2ram_test_exit(void)

{

     //to do the cleanup

}

module_init(ram2ram_test_init);

module_exit(ram2ram_test_exit);

 

MODULE_LICENSE("GPL");

 

测试结果如下:

wolver:/mnt/sd# insmod mxc_dma_test.ko

==========dma transfer is success==========

dma transfer complete: error =0

dest data correct

上面的驱动所做的工作就是把一块内存搬到另一块内存里,搬运结束后DMA控制器会产生中断,再调用它的回调函数dma_complete_fn(),通常这时候就会作一些判断。

从这个简单的驱动可以看到使用DMA的流程是这样的:申请一个DMA通道(mxc_dma_request)——>设置回调函数(mxc_dma_callback_set)——>填充mxc_dma_requestbuf_t结构体——>使用mxc_dma_requestbuf_t来配置DMA通道(mxc_dma_config)——>使能DMAmxc_dma_enable)——>做其它工作或挂起——>最后释放DMA通道(mxc_dma_free)。

 

------------------------------------------
本文乃原创!
转载请注明出处:http://sparklecliz.cublog.cn/
------------------------------------------
阅读(2233) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~