分类: LINUX
2010-02-03 16:37:46
一,前言:DMA是英文“Direct Memory Access”的缩写。顾名思义,它是一种不经过CPU而直接从内存存取数据的数据交换模式。进一步的我们可以想到它需要设置源地址,目标地址,存取字节数,存取开始的使能或者请求,还有存取完成后产生中断通知CPU这几块。
(以下分析基于版本:linux-
二,先看一下/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。
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 < 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)——>使能DMA(mxc_dma_enable)——>做其它工作或挂起——>最后释放DMA通道(mxc_dma_free)。