内核版本:2.6.35
主机平台:Ubuntu 11.04
内核版本:2.6.39
1、DMA的功能和工作原理这里就不多说了,可以查看s3c2440的手册
2、在正式分析DMA驱动之前,我们先来看一下DMA的注册和初始化过程
系统设备:(翻译自源码注释)
系统设备和系统模型有点不同,它不需要动态绑定驱动,不能被探测(probe),不归结为任何的系统总线,所以要区分对待。对待系统设备我们仍然要有设备驱动的观念,因为我们需要对设备进行基本的操作。
定义系统设备,在./arch/arm/mach-s3c2440/s3c244x.c中
- /* 定义系统设备类 */
- struct sysdev_class s3c2440_sysclass = {
- .name = "s3c2440-core",
- .suspend = s3c244x_suspend,
- .resume = s3c244x_resume
- };
- static int __init s3c2440_core_init(void)
- {
- return sysdev_class_register(&s3c2440_sysclass);
- }
下面就是系统设备类的注册函数,在./drivers/base/sys.c中
- int sysdev_class_register(struct sysdev_class *cls)
- {
- int retval;
- pr_debug("Registering sysdev class '%s'\n", cls->name);
- INIT_LIST_HEAD(&cls->drivers);
- memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
- cls->kset.kobj.parent = &system_kset->kobj;
- cls->kset.kobj.ktype = &ktype_sysdev_class;
- cls->kset.kobj.kset = system_kset;
- retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name);
- if (retval)
- return retval;
- retval = kset_register(&cls->kset);
- if (!retval && cls->attrs)
- retval = sysfs_create_files(&cls->kset.kobj,
- (const struct attribute **)cls->attrs);
- return retval;
- }
- /* 定义DMA系统设备驱动 */
- static struct sysdev_driver s3c2440_dma_driver = {
- .add = s3c2440_dma_add,/* 添加add函数 */
- };
- 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 int __init s3c2440_dma_init(void)
- {
- return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
- }
- /**
- * sysdev_driver_register - Register auxillary driver
- * @cls: Device class driver belongs to.
- * @drv: Driver.
- *
- * @drv is inserted into @cls->drivers to be
- * called on each operation on devices of that class. The refcount
- * of @cls is incremented.
- */
- int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
- {
- int err = 0;
- if (!cls) {
- WARN(1, KERN_WARNING "sysdev: invalid class passed to "
- "sysdev_driver_register!\n");
- return -EINVAL;
- }
- /* Check whether this driver has already been added to a class. */
- if (drv->entry.next && !list_empty(&drv->entry))
- WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
- " been registered to a class, something is wrong, but "
- "will forge on!\n", cls->name, drv);
- mutex_lock(&sysdev_drivers_lock);
- if (cls && kset_get(&cls->kset)) {
- list_add_tail(&drv->entry, &cls->drivers);/* 将设备驱动添加到系统设备类的链表中 */
- /* If devices of this class already exist, tell the driver */
- if (drv->add) {
- struct sys_device *dev;
- list_for_each_entry(dev, &cls->kset.list, kobj.entry)
- drv->add(dev);
- }
- } else {
- err = -EINVAL;
- WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
- }
- mutex_unlock(&sysdev_drivers_lock);
- return err;
- }
- static struct sys_device s3c2440_sysdev = {
- .cls = &s3c2440_sysclass,/* 定义系统设备的所属系统设备类,用于系统设备注册到指定设备类 */
- };
- /* S3C2440初始化 */
- int __init s3c2440_init(void)
- {
- printk("S3C2440: Initialising architecture\n");
- s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up;
- s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up;
- /* change irq for watchdog */
- s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;
- s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT;
- /* register our system device for everything else */
- return sysdev_register(&s3c2440_sysdev);/* 注册s3c2440的系统设备 */
- }
- /**
- * sysdev_register - add a system device to the tree
- * @sysdev: device in question
- *
- */
- /* 系统设备的注册 */
- int sysdev_register(struct sys_device *sysdev)
- {
- int error;
- struct sysdev_class *cls = sysdev->cls;/* 所属的系统设备类 */
- if (!cls)
- return -EINVAL;
- pr_debug("Registering sys device of class '%s'\n",
- kobject_name(&cls->kset.kobj));
- /* initialize the kobject to 0, in case it had previously been used */
- memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
- /* Make sure the kset is set */
- sysdev->kobj.kset = &cls->kset;
- /* Register the object */
- error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
- "%s%d", kobject_name(&cls->kset.kobj),
- sysdev->id);
- if (!error) {
- struct sysdev_driver *drv;
- pr_debug("Registering sys device '%s'\n",
- kobject_name(&sysdev->kobj));
- mutex_lock(&sysdev_drivers_lock);
- /* Generic notification is implicit, because it's that
- * code that should have called us.
- */
- /* Notify class auxillary drivers */
- list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->add)
- drv->add(sysdev);/* 遍历该设备所属同一个设备类的所有设备,并执行相应的add函数 */
- }
- mutex_unlock(&sysdev_drivers_lock);
- kobject_uevent(&sysdev->kobj, KOBJ_ADD);
- }
- return error;
- }
(1)首先看第一个函数int __init s3c2410_dma_init(void),在./arch/arm/plat-s3c24xx/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, 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告诉缓冲区 */
- 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;
- }
(2)然后是函数s3c24xx_dma_order_set(&s3c2440_dma_order);
- int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
- {
- struct s3c24xx_dma_order *nord = dma_order;
- if (nord == NULL)
- nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);
- if (nord == NULL) {
- printk(KERN_ERR "no memory to store dma channel order\n");
- return -ENOMEM;
- }
- dma_order = nord;
- memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));
- return 0;
- }
(3)最后一个函数s3c24xx_dma_init_map(&s3c2440_dma_sel)
该函数功能是建立DMA源与硬件通道的映射图
- int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
- {
- struct s3c24xx_dma_map *nmap;
- size_t map_sz = sizeof(*nmap) * sel->map_size;
- int ptr;
- nmap = kmalloc(map_sz, GFP_KERNEL);
- if (nmap == NULL)
- return -ENOMEM;
- memcpy(nmap, sel->map, map_sz);
- memcpy(&dma_sel, sel, sizeof(*sel));
- dma_sel.map = nmap;
- for (ptr = 0; ptr < sel->map_size; ptr++)
- s3c24xx_dma_check_entry(nmap+ptr, ptr);
- return 0;
- }
注:由于内核实在是太深了,这里只是表面上按流程大体了解了子同设备的注册和系统设备驱动的注册以及DMA设备的注册和初始化,函数中有很多细节有待进一步研究。
上一篇:ARM-Linux移植攻略--yaffs2 Partially written block xxx detected 问题解决
下一篇:ARM中的big-endian和little-endian
推荐阅读最新更新时间:2024-03-16 14:51
- 热门资源推荐
- 热门放大器推荐