Linux NAND FLASH驱动程序分析(mini2440)
2011-06-18 10:34
423 查看
Linux NAND FLASH驱动程序分析(mini2440)
一、Linux-MTD Subsystem介绍
FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR
FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。
在引入MTD后Linux系统中FLASH设备驱动可分为四层,如图:
1. 硬件驱动层
FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。
特定硬件层负责完成特定硬件的读写,例如2440的/driver/mtd/nand/s3c2410.c
通用驱动为所有nand的通用部分,实现nand设备发现,通用读写等./driver/mtd/nand/nand_base.c
2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.c、mtdpart.c),另一部分是各个 特定的FLASH的数据,例如分区。
3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。
4. 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。
也可通过下图理解:
从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数会调用nand_chip(nand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以一个flash硬件驱动程序时的步骤大致如下:
1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。
mtd_partition
friendly_arm_default_nand_part[],arch/arm/plat-s3c24xx/common-friendly-arm.c
2. 在模块加载时为每一个chip(主分区)分配mtd_info和nand_chip的内存,根据目标板nand 控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()、dev_ready()、read_byte()、write_byte()等。填充mtd_info,并将其priv成员指向nand_chip。 driver/mtd/nand/s3c2410/c
3. 以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。 driver/mtd/nand/nand_base/c
nand_scan()分为两步,首先是nand_scan_ident(),主要完成flashID的读取等,然后是nand_scan_tail(),主要完成chip结构体中未初始化函数的默认赋值,以及坏块的扫描。
4. 如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区分配一个mtd_info结构体填充,并注册。
二、nand flash驱动程序实例分析
我们以2.6.29内核中mini2440的nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程:
1. 注册nand flash设备
nand flash分区:
nand控制器资源
2.注册nand flash driver
当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序
会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数
probe的具体调用流程见 Linux NAND FLASH驱动代码分析
一、Linux-MTD Subsystem介绍
FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR
FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。
在引入MTD后Linux系统中FLASH设备驱动可分为四层,如图:
1. 硬件驱动层
FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。
特定硬件层负责完成特定硬件的读写,例如2440的/driver/mtd/nand/s3c2410.c
通用驱动为所有nand的通用部分,实现nand设备发现,通用读写等./driver/mtd/nand/nand_base.c
2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.c、mtdpart.c),另一部分是各个 特定的FLASH的数据,例如分区。
3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。
4. 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。
也可通过下图理解:
从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数会调用nand_chip(nand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以一个flash硬件驱动程序时的步骤大致如下:
1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。
mtd_partition
friendly_arm_default_nand_part[],arch/arm/plat-s3c24xx/common-friendly-arm.c
2. 在模块加载时为每一个chip(主分区)分配mtd_info和nand_chip的内存,根据目标板nand 控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()、dev_ready()、read_byte()、write_byte()等。填充mtd_info,并将其priv成员指向nand_chip。 driver/mtd/nand/s3c2410/c
3. 以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。 driver/mtd/nand/nand_base/c
nand_scan()分为两步,首先是nand_scan_ident(),主要完成flashID的读取等,然后是nand_scan_tail(),主要完成chip结构体中未初始化函数的默认赋值,以及坏块的扫描。
4. 如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区分配一个mtd_info结构体填充,并注册。
二、nand flash驱动程序实例分析
我们以2.6.29内核中mini2440的nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程:
1. 注册nand flash设备
nand flash分区:
//arch/arm/plat-s3c24xx/common-friendly-arm.c static struct mtd_partition friendly_arm_default_nand_part[] = { [0] = { .name = "supervivi", .size = 0x00060000, .offset = 0, }, [1] = { .name = "Kernel", .offset = 0x00060000, .size = 0x00200000, }, [2] = { .name = "root", .offset = 0x00260000, .size = 1024 * 1024 * 1024, //64U * 1024 * 1024 - 0x00260000, }, [3] = { .name = "nand", .offset = 0x00000000, .size = 1024 * 1024 * 1024, //64U * 1024 * 1024 - 0x00260000, } }; static struct s3c2410_nand_set friendly_arm_nand_sets[] = { [0] = { .name = "NAND", .nr_chips = 1, //系统只有一个chip .nr_partitions = ARRAY_SIZE(friendly_arm_default_nand_part), // 4个分区 .partitions = friendly_arm_default_nand_part, }, }; static struct s3c2410_platform_nand friendly_arm_nand_info = { .tacls = 20,//2440 rNFCONF 需要设置的时钟信息 .twrph0 = 60, .twrph1 = 20, .nr_sets = ARRAY_SIZE(friendly_arm_nand_sets), .sets = friendly_arm_nand_sets, };//这里将许多数据作为platform_data传入包括chip数组
nand控制器资源
//arch/arm/plat-s3c24xx/devs.c static struct resource s3c_nand_resource[] = { [0] = { .start = S3C24XX_PA_NAND, .end = S3C24XX_PA_NAND + S3C24XX_SZ_NAND - 1, .flags = IORESOURCE_MEM, } }; struct platform_device s3c_device_nand = { .name = "s3c2410-nand", .id = -1, .num_resources = ARRAY_SIZE(s3c_nand_resource), .resource = s3c_nand_resource, }; //注册nand flash作为platform device: static struct platform_device __initdata *friendly_arm_devs[] = { &s3c_device_nand, &s3c_device_sdi, &s3c_device_usbgadget, }; void __init friendly_arm_machine_init(void) { /* Configure the LEDs (even if we have no LED support)*/ s3c_device_nand.dev.platform_data = &friendly_arm_nand_info; //在nand的驱动程序的probe中会利用这个负责,以及后面添加分区是获取分区信息 platform_add_devices(friendly_arm_devs, ARRAY_SIZE(friendly_arm_devs)); s3c2410_pm_init(); }
2.注册nand flash driver
//linux/drivers/mtd/nand/s3c2410.c static struct platform_driver s3c2440_nand_driver = { .probe = s3c2440_nand_probe, .remove = s3c2410_nand_remove, .suspend = s3c24xx_nand_suspend, .resume = s3c24xx_nand_resume, .driver = { .name = "s3c2440-nand", .owner = THIS_MODULE, }, }; static int __init s3c2410_nand_init(void) { printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics/n"); platform_driver_register(&s3c2412_nand_driver); platform_driver_register(&s3c2440_nand_driver); return platform_driver_register(&s3c2410_nand_driver); }
当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序
会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数
probe的具体调用流程见 Linux NAND FLASH驱动代码分析
static int s3c2440_nand_probe(struct platform_device *dev) { return s3c24xx_nand_probe(dev, TYPE_S3C2440); } /* s3c2410_nand_probe * * called by device layer when it finds a device matching * one our driver can handled. This code checks to see if * it can allocate all necessary resources then calls the * nand layer to look for devices */ static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type) { struct s3c2410_platform_nand *plat = to_nand_plat(pdev); struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; struct resource *res; int err = 0; int size; int nr_sets; int setno; pr_debug("s3c2410_nand_probe(%p)/n", pdev); info = kmalloc(sizeof(*info), GFP_KERNEL); //分配s3c2410_nand_info内存 if (info == NULL) { dev_err(&pdev->dev, "no memory for flash info/n"); err = -ENOMEM; goto exit_error; } memset(info, 0, sizeof(*info)); platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); /* get the clock source and enable it */ info->clk = clk_get(&pdev->dev, "nand"); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get clock/n"); err = -ENOENT; goto exit_error; } clk_enable(info->clk); /* allocate and map the resource */ /* currently we assume we have the one resource */ res = pdev->resource; size = res->end - res->start + 1; info->area = request_mem_region(res->start, size, pdev->name); if (info->area == NULL) { dev_err(&pdev->dev, "cannot reserve register region/n"); err = -ENOENT; goto exit_error; } info->device = &pdev->dev; info->platform = plat; info->regs = ioremap(res->start, size);//存储Nand控制器的虚拟地址 info->cpu_type = cpu_type; if (info->regs == NULL) { dev_err(&pdev->dev, "cannot reserve register region/n"); err = -EIO; goto exit_error; } dev_dbg(&pdev->dev, "mapped registers at %p/n", info->regs); /* initialise the hardware */ err = s3c2410_nand_inithw(info);//初始化硬件,设置TACLS,TWRPH0/1(2440-rNFCONF)并使能nand控制器 if (err != 0) goto exit_error; sets = (plat != NULL) ? plat->sets : NULL; nr_sets = (plat != NULL) ? plat->nr_sets : 1; info->mtd_count = nr_sets;//1 /* allocate our information */ size = nr_sets * sizeof(*info->mtds);//nr_sets = 1 info->mtds = kmalloc(size, GFP_KERNEL); if (info->mtds == NULL) { dev_err(&pdev->dev, "failed to allocate mtd storage/n"); err = -ENOMEM; goto exit_error; } memset(info->mtds, 0, size); /* initialise all possible chips */ nmtd = info->mtds; for (setno = 0; setno < nr_sets; setno++, nmtd++) {//nr_sets = 1 pr_debug("initialising set %d (%p, info %p)/n", setno, nmtd, info); //初始化nand_chip中的read_buf,write_buf等函数指针,ecc设置 s3c2410_nand_init_chip(info, nmtd, sets); //nand_scan()的第一步(nand_base.c),read ID… nmtd->scan_res = nand_scan_ident(&nmtd->mtd, (sets) ? sets->nr_chips : 1); if (nmtd->scan_res == 0) { //如果使用硬件ecc,根据id信息中的page大小更新ecc设置 s3c2410_nand_update_chip(info, nmtd); //nand_scan()的第二步(nand_base.c),给初始化的函数指针默认值,扫描坏块 nand_scan_tail(&nmtd->mtd); s3c2410_nand_add_partition(info, nmtd, sets);//添加分区,想mtd注册分区信息 } if (sets != NULL) sets++; } err = s3c2410_nand_cpufreq_register(info); if (err < 0) { dev_err(&pdev->dev, "failed to init cpufreq support/n"); goto exit_error; } if (allow_clk_stop(info)) { dev_info(&pdev->dev, "clock idle support enabled/n"); clk_disable(info->clk); } pr_debug("initialised ok/n"); return 0; exit_error: s3c2410_nand_remove(pdev); if (err == 0) err = -EINVAL; return err; }
相关文章推荐
- Linux NAND FLASH驱动程序分析(mini2440)
- linux-2.6.32在mini2440开发板上移植(10)之触摸屏工作原理以及驱动程序详细分析
- linux-2.6.32在mini2440开发板上移植(10)之触摸屏工作原理以及驱动程序详细分析
- Linux NAND FLASH驱动程序框架分析
- linux-2.6.32在mini2440开发板上移植---触摸屏工作原理以及驱动程序详细分析
- linux-2.6.32在mini2440开发板上移植(10)之触摸屏工作原理以及驱动程序详细分析
- s3c2410 linux LCD驱动程序分析
- linux驱动程序之电源管理之标准linux休眠与唤醒机制分析(一)
- linux-2.6.32在mini2440开发板上移植(9)之添加触摸屏驱动程序
- 嵌入式linux之Nand flash驱动程序框架
- Linux内核--网络栈实现分析(十一)--驱动程序层(下)
- linux驱动程序之电源管理之标准linux休眠和唤醒机制分析(二)
- Linux DM9000网卡驱动程序完全分析
- linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动
- Linux下TTY驱动程序分析
- 11-S3C2440驱动学习(七)嵌入式linux-字符设备的另一种写法及RTC驱动程序分析和字符设备驱动框架总结
- 嵌入式Linux系统之I.MX6触摸屏驱动程序TSC2007.C的分析、移植与校准
- linux设备驱动之 i2c设备驱动 at24c08驱动程序分析【全部地址的操作】
- <2012 11 6 > linux设备驱动程序开发初探(5) poll机制分析
- Linux NAND FLASH驱动代码分析