`
thecloud
  • 浏览: 880578 次
文章分类
社区版块
存档分类
最新评论

ARM-Linux驱动--DMA驱动分析(一)

 
阅读更多

硬件平台:FL2440 (s3c2440)

内核版本:2.6.35

主机平台:Ubuntu 11.04

内核版本:2.6.39

原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6645821

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函数 */
};
下面是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);
}
注册DMA驱动到系统设备

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;
}
在./arch/arm/mach-s3c2440/s3c2440.c中定义s3c2440的系统设备和注册

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;
}
那DMA系统设备驱动中的add函数中到底是什么呢?

(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);
}
实际上就是初始化DMA为4通道,设置中断号,设置寄存器的覆盖范围

下面是该函数的实现

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;
}
我们注意到函数中使用了kmalloc给结构体重新分配了内存,这是由于__initdata修饰的变量表示初始化用的变量,初始化完毕后空间自动释放,所以需要将其存储起来。

(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;
}
这里的kmalloc函数的作用同上面的作用一样。

注:由于内核实在是太深了,这里只是表面上按流程大体了解了子同设备的注册和系统设备驱动的注册以及DMA设备的注册和初始化,函数中有很多细节有待进一步研究。

分享到:
评论

相关推荐

    详解ARM的AMBA设备中的DMA设备PL08X的Linux驱动 v1.1

    本文主要分析了Linux的DMA驱动中ARM的PL08X的实现细节。

    zynq-7000.rar_linux 中断 zynq_zynq DMA_zynq DMA Linux_zynq gpio_i2

    linux3.0 device tree,包括zynq-7000,arm9,coretx-a9芯片级的所有外设驱动树,i2c,spi,dma,中断,gpio

    Linux编程--Linux内核

    Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 什么是操作...

    Move to ARM linux 3

    3. 建议各SoC统一采用dmaengine架构实现DMA驱动,该架构提供了通用的DMA通道API如dmaengine_prep_slave_single()、dmaengine_submit()等,要求SoC实现dma_device的成员函数实现代码统一放入drivers/dma目录。...

    嵌入式ARM9-2440实战手册

    针对s3c2440a,通过实例精讲,介绍了arm9嵌入式常用模块的原理和驱动程序实现方法,广嵌教育出品 实验1 ARM 汇编指令编程实验1 实验2 C 和ARM 汇编混合编程实验8 实验3 C 语言实现LED 控制实验15 实验4 外部中断...

    Linux芯片级移植与底层驱动

    Linux芯片级移植与底层驱动,为了让 Linux 在一个全新的 ARM SoC 上运行,需要提供大量的底层支撑,如定时器节 拍、中断控制器、SMP 启动、CPU hotplug 以及底层的 GPIO、clock、pinctrl 和 DMA 硬件 的封装等。

    嵌入式ARM9-2440实战手册.pdf

    实验1 ARM 汇编指令...实验24 Linux 驱动程序开发实验270 实验25 QT/Embedded 实验280 实验26 WinCE50 开发实验294 附录一 S3C2440A 启动代码314 附录二 GEC2440 核心板电路图327 附录三 GEC2440 主板电路图335

    LPC1700系列ARM数据手册

    Host, Device, or OTG, 8 channel general purpose DMA controller, 4 UARTs, 2 CAN channels, 2 SSP controllers, SPI interface, 3 I 2C interfaces, 2-input plus 2-output I 2S interface, 8 channel 12-bit ...

    dma.zip_S3C6410 DMA_linux dma_s3c6410_site:www.pudn.com

    ARM S3C6410 的 DMA 驱动,直接编译运行就可以执行,可以用来播放音乐

    r58_evb_sc5806v4加载TP成功修改config 20160815 2026.7z

    /bin/sh: 1: arm-linux-gnueabi-arm-linux-gnueabi-gcc: not found make:进入目录'/home/rootroot/wyb/r58_evb_sc5806/lichee/linux-3.4/modules/eurasia_km/eurasiacon/build/linux2/sunxi_android' CC [M] /home/...

    ARM系统中DMA方式在数据采集中的应用

    讨论了ARM系统中DMA通道的工作原理,并利用DMA技术设计了基于S3C2410和FPGA的CCD相机采集系统,给出了数据采集接口设计方案,以及Linux操作系统下接口的设备驱动程序。测试结果表明系统工作稳定,数据采集速率可以...

    嵌入式\(高校应用案例)北航软件学院

    ZKQ090208 嵌入式Linux驱动开发 设备驱动及内核模块概述;构造和运行模块;编写字符设备驱动程序I;编写字符设备驱动程序II;高级字符驱动程序;与硬件通信;中断处理;Linux设备模型;内存映射操作;DMA技术及应用...

    ARM嵌入式系统设计基础教程课件

    第12章介绍了Bootloader的移植,嵌入式Linux内核和文件系统的移植,Linux下设备驱动程序的开发,应用程序开发。 第13章介绍了图形用户接口(GUI)的层次结构,桌面Linux系统中GUI,嵌入式Linux系统GUI ,MiniGUI 的...

    r40_tinav2.1_最终验证通过_使用CB-S来验证OV5640有横条纹fpscamera+SPI2.0成功_20171114_1443没有外层目录.7z

    W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\device\Makefile obj-m += ov5640.o #obj-m += ov2640.o #obj-m += ov7736.o #obj-m += s5k4ec.o #obj-m += s5k4ec_mipi.o #obj-m += gc...

    Linux2.6内核下同步串行通信驱动的开发与应用 (2010年)

    针对Linux2.6版本内核,详细介绍了基于AT91TM9200的SSC同步串行通信设备驱动的模块化、分层次的设计架构,并在驱动中使用DMA传输模式、分段缓存技术和修改PDA控制器优先级。在此基础上给出了 Arl91RM9200/DSP56309...

    mini2440 beer(pwm) 驱动

    Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 2164248 Bytes = 2.1 MiB Load Address: 30008000 Entry Point: 30008040 Verifying Checksum ... OK XIP Kernel Image ... OK OK ...

    Linux编程从入门到精通

    第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 ...

    Linux内核 内容很全

    处理器 115 10.1 X86 115 10.2 ARM 115 10.3 Alpha AXP处理器 115 第11章 Linux内核源代码 117 11.1 怎样得到Linux内核源码 117 11.2 内核源码的编排 117 11.3 从何处看起 118 第12章 Linux...

Global site tag (gtag.js) - Google Analytics