免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1650 | 回复: 0
打印 上一主题 下一主题

i.mx27下的DMA浅析(一) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-02-03 16:37 |只看该作者 |倒序浏览

一,前言: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
         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[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。
16个DMA通道,16个g_dma_channels成员,16个g_dma_privates成员,16个g_dma_bd_table[MAX_BD_SIZE]成员,而且g_dma_privates是g_dma_channels的private成员,g_dma_bd_table[MAX_BD_SIZE]又是g_dma_privates的bd_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
         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
         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
         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!=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)——>使能DMA(mxc_dma_enable)——>做其它工作或挂起——>最后释放DMA通道(mxc_dma_free)。

------------------------------------------
本文乃原创!
转载请注明出处:http://sparklecliz.cublog.cn/
------------------------------------------


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/107046/showart_2170400.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP