天天看點

SDIO驅動(15)使用DMA傳輸資料1

硬體支援的資料傳輸方式:

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

SDIO驅動(15)使用DMA傳輸資料1

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

2、傳輸使用的channel

這裡的channel有兩個意思:一是實體的、硬體上的channel;一是虛拟的、邏輯意義上的channel。我們看下晶片的datasheet就一目了然:

SDIO驅動(15)使用DMA傳輸資料1

可以看出,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;
}
           

s 3 c 2 4 xx_dma_init ( )函數的第一個參數是硬體DMA控制器的通道數,S3C2440 supports four-channel DMA controller。第二個參數是DMA的通道0對應的中斷号,

第三個參數是寄存器位址之間的內插補點,專業的說法是步幅0x40:

SDIO驅動(15)使用DMA傳輸資料1

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通道的一些固定參數,比如通道号、對應寄存器位址、中斷号等,有些參數需要使用的時候設定,比如傳輸資料大小、那個邏輯通道在使用它等等。

繼續閱讀