硬件支持的數據傳輸方式:
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通道的一些固定參數,比如通道號、對應寄存器地址、中斷號等,有些參數需要使用的時候設置,比如傳輸數據大小、那個邏輯通道在使用它等等。