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分区:

//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)相关推荐

  1. MTD 设备驱动 和 NAND Flash 驱动程序分析。

    硬件环境: 飞凌OK6410,256MB DDR,2GB NAND Flash.   NAND Flash 型号:K9G8G08U9A   .     分析源码:Linux 2.6.36.2 内核源码 ...

  2. linux nand 驱动,Linux NAND FLASH驱动分析(一)

    最近一直在忙着工作上的事情,好久都没有更新博客了,发现最近思想是比较混乱的.学任何东西都坚持不下去,既然选择驱动开发这条路就要坚持下去. 之前分析了Linux块设备驱动,是以内存块来模拟的虚拟块设备. ...

  3. Linux NAND Flash调试

    很久没接触过 nandflash 驱动,最近工作又摸了,于是顺便整理总结一下.nandflash 在我看来算是比较落后的存储设备,所以文章里没有太多细节的东西,更多的是一些开发思路和经验,希望能帮助到 ...

  4. NAND flash驱动程序(1)

    前言 前面我们已经写过一个块设备驱动程序,用内存来模拟块设备.假如我们想写一个NAND FLASH驱动程序,又应该怎么做呢?我们先看一下kernel代码中别人现成的驱动程序,分析一下流程框架,总结出一 ...

  5. Linux操作系统下 NAND FLASH驱动程序框架

    当我们需要在操作系统上读写普通文件的时候,总是需要一层层往下,最终到达硬件相关操作,当然底层设备大多数都是块设备 NAND FLASH就作为一个最底层的块设备. 而我们写驱动,就是要构建硬件与操作系统 ...

  6. 块设备驱动之NAND FLASH驱动程序

    转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/25240909 一.框架总结 二.硬件原理 相比于nor flash.我们能够清 ...

  7. Linux下触摸屏驱动程序分析

    [摘要: 本文以 linux 3.5--Exynos4412仄台,剖析 触摸屏 驱动焦点内容.Linux下触摸屏驱动(以ft5x06_ts为例)须要懂得以下学问: 1. I2C协定 2. Exynos ...

  8. linux nand flash模拟u盘,海思g_mass_storage.ko模拟U盘

    1. 基于平台:海思3556V100 2. 存储设备:nand flash 3. 驱动加载(参考<外围设备驱动 操作指南.pdf>)如下:insmod udc-hisi.ko insmod ...

  9. uboot下的nand flash驱动分析

    Nand flash芯片工作原理: ------------------------------------     Nand flash芯片型号为Samsung K9F2G08U0A,数据存储容量为 ...

最新文章

  1. Android 自定义View —— Canvas
  2. mybatis$和#的区别
  3. 【详解】()调试方法从线索(错误征兆)出发,通过分析这些线索之间的关系而找出故障,是从个别推断一般的方法。
  4. centos 6.5 yum mysql 5.6_centos 6.5 yum安装 mysql 5.6
  5. Treap树堆(bzoj 3224: Tyvj 1728 普通平衡树)
  6. 蓝牙学习笔记(五)——AC692x_BLE工具make_gatt_services
  7. 三菱PLC与上位机通讯协议讲解
  8. 基于android的个人收支财务管理,基于Android的个人财务管理系统的设计与实现.doc...
  9. 线性回归最小二乘法公式推导
  10. 物联网的媒介——java usb串口通信
  11. 弘辽科技:刷单越来越不行了吗?
  12. Selenium WebDriver使用IE浏览器
  13. python编程基础及应用(重庆大学):7-7 计算用户输入句子中的单词数量以及单词平均长度
  14. 关于坯布的面料克重问题
  15. MapGuide的历史
  16. 终极单词index 排序 C-D
  17. 家长会计算机教师致辞,初中生物教师家长会发言稿
  18. 艾美捷ProSci 激活素RIB / ACVR1B重组蛋白方案
  19. ds12c887程序C语言,单片机+TM1628+DS12C887时钟源程序
  20. Shell教程(四)--输出的格式化

热门文章

  1. 电商系统,商品属性表和功能设计,可用于各种实体的属性
  2. 如何把地址导航生成二维码?
  3. psn请验证您不是机器人_PS4无法登陆PSN的解决办法
  4. 用动画(animation)做弹力球
  5. 第4章 设计目标与原则
  6. nyoj 239 月老的难题 【二分匹配之匈牙利】
  7. 怎么将Excel多个工作表拆分成多个单独的Excel
  8. 虚拟场景+AR特效,世优科技助力京东手机华为新品发布会MR直播
  9. 收单-批量支付-批量支付文件规则设计
  10. 再谈 共轭方向法/Conjugate Direction Method In Optimization