天天看點

DMA(一) - 核心sysdev DMA控制器裝置

概念

差別兩個概念:DMA和DMA控制器

dma_alloc_coherent/dma_map_single/dma_pool_create

以上這3個函數隻是将核心虛拟位址映射得到實體位址

對于實體位址的使用則涉及到DMA控制器的操作了,需要将實體位址填寫到DMA控制器的對應寄存器,并啟動DMA控制器進行傳輸。核心對DMA控制器的操作也提供了一些函數。

大部分情況下,外設都有自己獨立的DMA控制器,如dwc_otg中usb使用的是usb相關的DMA控制器;而有的soc有公共的DMA控制器,如s3c2440,不同的外設使用公共的DMA控制器。

核心sysdev DMA控制器裝置

sysdev_class_register, sysdev_register, sysdev_driver_register函數位于drivers/base/sys.c檔案。

/* arch/arm/plat-s3c24xx/s3c244x.c */
struct sysdev_class s3c2440_sysclass = {
        .name           = "s3c2440-core",
        .suspend        = s3c244x_suspend,
        .resume         = s3c244x_resume
};
sysdev_class_register(&s3c2440_sysclass);

/* arch/arm/mach-s3c2440/s3c2440.c */
static struct sys_device s3c2440_sysdev = {
        .cls            = &s3c2440_sysclass,
};
sysdev_register(&s3c2440_sysdev);

/* arch/arm/mach-s3c2440/dma.c */
static struct sysdev_driver s3c2440_dma_driver = {
        .add    = s3c2440_dma_add,
};
sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);      

系統裝置與驅動模型有一點不同,他們不需要動态的驅動綁定,也不能被探測,并且不屬于任何類型的外圍總線。對系統裝置我們仍然有驅動的概念,因為我們仍想執行在這些裝置上執行基本的操作。

s3c2440_dma_driver注冊到s3c2440_sysclass中的driver連結清單後,在s3c2440_sysclass的device連結清單中發現了s3c2440_sysdev,比對,于是便執行s3c2440_dma_driver中的add函數s3c2440_dma_add.

/* arch/arm/plat-s3c24xx/dma.c */
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);
}      

接着調用

/* arch/arm/mach-s3c2440/dma.c */
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, (c) 2003-2004,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));   
                cp->number = channel;
                cp->irq    = channel + irq;/* dma channel irqs are in order.. */
                cp->regs   = dma_base + (channel * stride); 
                cp->stats  = &cp->stats_store;/* point current stats somewhere */
                cp->stats_store.timeout_shortest = LONG_MAX;
                cp->load_timeout = 1<<18;/* basic channel configuration */

                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;
}      
struct sysdev_class dma_sysclass = {
        .name           = "s3c24xx-dma",
        .suspend        = s3c2410_dma_suspend,
        .resume         = s3c2410_dma_resume,
};

static int __init s3c24xx_dma_sysclass_init(void)
{
        int ret = sysdev_class_register(&dma_sysclass);
        if (ret != 0)
                printk(KERN_ERR "dma sysclass registration failed\n");
        return ret;
}
core_initcall(s3c24xx_dma_sysclass_init);

static int __init s3c24xx_dma_sysdev_register(void)
{
        struct s3c2410_dma_chan *cp = s3c2410_chans;
        int channel, ret;
        for (channel = 0; channel < dma_channels; cp++, channel++) {
                cp->dev.cls = &dma_sysclass;
                cp->dev.id  = channel;
                ret = sysdev_register(&cp->dev);
                if (ret) {
                        printk(KERN_ERR "error registering dev for dma %d\n", channel);
                        return ret;
                }
        }
        return 0;
}
late_initcall(s3c24xx_dma_sysdev_register);