硬體支援的資料傳輸方式:
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;
}
s 3 c 2 4 xx_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通道的一些固定參數,比如通道号、對應寄存器位址、中斷号等,有些參數需要使用的時候設定,比如傳輸資料大小、那個邏輯通道在使用它等等。