SDIO驅動(15)使用DMA傳輸數據1

硬件支持的數據傳輸方式:


Normal:

tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
最終調用pio_tasklet()函數完成數據的收發。

DMA:即Direct Memory Access,在數據的傳輸的過程中不需要CPU直接參與。CPU坐鎮中央、發號施令,具體的數據“搬運”工作由DMA這一硬件模塊完成。接下來通過SDIO/SD這一實例,分析下DMA的實現及使用。


一、DMA存在的必要性

衆所周知CPU的速度相比外設來說是相當相當快的,所以如果讓CPU直接參與數據傳輸事務,CPU效率必定大打折扣,而且影響Event處理的實時性,於是DMA就應運而生了:解放CPU,專門搬運數據。


二、DMA介紹

這麼看來,DMA的意圖很單純;那要完成這個目的,DMA需要什麼被告知什麼信息呢?

1、數據傳輸方向

mem<---->mem

mem----->dev

dev----->mem


圖片來源:http://www.wowotech.net/linux_kenrel/dma_engine_overview.html

2、傳輸使用的channel

這裏的channel有兩個意思:一是物理的、硬件上的channel;一是虛擬的、邏輯意義上的channel。我們看下芯片的datasheet就一目瞭然:


可以看出,2440的DMA控制器有4條物理通道,每一條通道可共6個源通道複用。

3、傳輸的數據大小、位寬

4、傳輸控制,控制數據的開始、停止等

5、傳輸狀態,DMA控制器需要把一次傳輸的情況通知CPU


三、DMA在軟件上的呈現

在kernel中,DMA框架分兩個方向:DMA控制器的驅動實現,對應Provider;DMA控制器的使用,對應Consumer。這裏SDIO驅動中,自然就是Consumer了。

1、DMA控制器驅動實現分析

DMA控制器驅動框架:

static int __init s3c2440_dma_add(struct sys_device *sysdev)
{
	s3c2410_dma_init();
	s3c24xx_dma_order_set(&s3c2440_dma_order);
	return s3c24xx_dma_init_map(&s3c2440_dma_sel);
}

static struct sysdev_driver s3c2440_dma_driver = {
	.add	= s3c2440_dma_add,
};

static int __init s3c2440_dma_init(void)
{
	return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
}

arch_initcall(s3c2440_dma_init);
還是熟悉的味道。第一個函數s3c2410_dma_init():

int __init s3c2410_dma_init(void)
{
	return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
}

int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
			    unsigned int stride)
{
	struct s3c2410_dma_chan *cp;
	int channel;
	int ret;

	printk("S3C24XX DMA Driver, Copyright 2003-2006 Simtec Electronics\n");

	dma_channels = channels;

	dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
	if (dma_base == NULL) {
		printk(KERN_ERR "dma failed to remap register block\n");
		return -ENOMEM;
	}

	dma_kmem = kmem_cache_create("dma_desc",
				     sizeof(struct s3c2410_dma_buf), 0,
				     SLAB_HWCACHE_ALIGN,
				     s3c2410_dma_cache_ctor);

	if (dma_kmem == NULL) {
		printk(KERN_ERR "dma failed to make kmem cache\n");
		ret = -ENOMEM;
		goto err;
	}

	for (channel = 0; channel < channels;  channel++) {
		cp = &s3c2410_chans[channel];

		memset(cp, 0, sizeof(struct s3c2410_dma_chan));

		/* dma channel irqs are in order.. */
		cp->number = channel;
		cp->irq    = channel + irq;
		cp->regs   = dma_base + (channel * stride);

		/* point current stats somewhere */
		cp->stats  = &cp->stats_store;
		cp->stats_store.timeout_shortest = LONG_MAX;

		/* basic channel configuration */

		cp->load_timeout = 1<<18;

		printk("DMA channel %d at %p, irq %d\n",
		       cp->number, cp->regs, cp->irq);
	}

	return 0;

 err:
	kmem_cache_destroy(dma_kmem);
	iounmap(dma_base);
	dma_base = NULL;
	return ret;
}
s3c24xx_dma_init()函數的第一個參數是硬件DMA控制器的通道數,S3C2440 supports four-channel DMA controller。第二個參數是DMA的通道0對應的中斷號,
第三個參數是寄存器地址之間的差值,專業的說法是步幅0x40:


15行,dma_channels的值爲4,17行,有上圖可知S3C24XX_PA_DMA爲0x4B000000,dma_base爲重映射之後的地址。

23行,kmem_cache_create()函數用於創建專用的高速緩存,屬於內存管理的範疇。

34~54行的for循環用來初始化DMA控制器的4個物理通道,s3c2410_chans的定義:

struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];
這裏只初始化了DMA通道的一些固定參數,比如通道號、對應寄存器地址、中斷號等,有些參數需要使用的時候設置,比如傳輸數據大小、那個邏輯通道在使用它等等。
發佈了168 篇原創文章 · 獲贊 150 · 訪問量 56萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章