Linux NAND FLASH驱动程序分析(mini2440)

 

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

 

  1. //arch/arm/plat-s3c24xx/common-friendly-arm.c   
  2. static struct mtd_partition friendly_arm_default_nand_part[] = {  
  3.     [0] = {  
  4.         .name   = "supervivi",  
  5.         .size   = 0x00060000,  
  6.         .offset = 0,  
  7.     },  
  8.     [1] = {  
  9.         .name   = "Kernel",  
  10.         .offset = 0x00060000,  
  11.         .size   = 0x00200000,  
  12.     },  
  13.     [2] = {  
  14.         .name   = "root",  
  15.         .offset = 0x00260000,  
  16.         .size   = 1024 * 1024 * 1024, //64U * 1024 * 1024 - 0x00260000,   
  17.     },  
  18.     [3] = {  
  19.         .name   = "nand",  
  20.         .offset = 0x00000000,  
  21.         .size   = 1024 * 1024 * 1024, //64U * 1024 * 1024 - 0x00260000,   
  22.     }  
  23. };  
  24. static struct s3c2410_nand_set friendly_arm_nand_sets[] = {  
  25.     [0] = {  
  26.         .name       = "NAND",  
  27.         .nr_chips   = 1, //系统只有一个chip   
  28.         .nr_partitions  = ARRAY_SIZE(friendly_arm_default_nand_part), // 4个分区   
  29.         .partitions = friendly_arm_default_nand_part,  
  30.     },  
  31. };  
  32. static struct s3c2410_platform_nand friendly_arm_nand_info = {  
  33.     .tacls      = 20,//2440 rNFCONF 需要设置的时钟信息   
  34.     .twrph0     = 60,  
  35.     .twrph1     = 20,  
  36.     .nr_sets    = ARRAY_SIZE(friendly_arm_nand_sets),  
  37.     .sets       = friendly_arm_nand_sets,  
  38. };//这里将许多数据作为platform_data传入包括chip数组  
 

nand控制器资源

  1. //arch/arm/plat-s3c24xx/devs.c   
  2. static struct resource s3c_nand_resource[] = {  
  3.     [0] = {  
  4.         .start = S3C24XX_PA_NAND,  
  5.         .end   = S3C24XX_PA_NAND + S3C24XX_SZ_NAND - 1,  
  6.         .flags = IORESOURCE_MEM,  
  7.     }  
  8. };  
  9. struct platform_device s3c_device_nand = {  
  10.     .name         = "s3c2410-nand",  
  11.     .id       = -1,  
  12.     .num_resources    = ARRAY_SIZE(s3c_nand_resource),  
  13.     .resource     = s3c_nand_resource,  
  14. };  
  15. //注册nand flash作为platform device:   
  16. static struct platform_device __initdata *friendly_arm_devs[] = {  
  17.     &s3c_device_nand,  
  18.     &s3c_device_sdi,  
  19.     &s3c_device_usbgadget,  
  20. };  
  21. void __init friendly_arm_machine_init(void)  
  22. {  
  23.     /* Configure the LEDs (even if we have no LED support)*/  
  24.     s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;  
  25. //在nand的驱动程序的probe中会利用这个负责,以及后面添加分区是获取分区信息   
  26.     platform_add_devices(friendly_arm_devs, ARRAY_SIZE(friendly_arm_devs));  
  27.     s3c2410_pm_init();  
  28. }  
 

2.注册nand flash driver

  1. //linux/drivers/mtd/nand/s3c2410.c   
  2. static struct platform_driver s3c2440_nand_driver = {  
  3.     .probe      = s3c2440_nand_probe,  
  4.     .remove     = s3c2410_nand_remove,  
  5.     .suspend        = s3c24xx_nand_suspend,  
  6.     .resume     = s3c24xx_nand_resume,  
  7.     .driver     = {  
  8.         .name   = "s3c2440-nand",  
  9.         .owner  = THIS_MODULE,  
  10.     },  
  11. };  
  12. static int __init s3c2410_nand_init(void)  
  13. {  
  14.     printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics/n");  
  15.     platform_driver_register(&s3c2412_nand_driver);  
  16.     platform_driver_register(&s3c2440_nand_driver);  
  17.     return platform_driver_register(&s3c2410_nand_driver);  
  18. }  
 

 

        当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序

 

会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数

probe的具体调用流程见 Linux NAND FLASH驱动代码分析

  1. static int s3c2440_nand_probe(struct platform_device *dev)  
  2. {  
  3.     return s3c24xx_nand_probe(dev, TYPE_S3C2440);  
  4. }  
  5. /* s3c2410_nand_probe 
  6.  * 
  7.  * called by device layer when it finds a device matching 
  8.  * one our driver can handled. This code checks to see if 
  9.  * it can allocate all necessary resources then calls the 
  10.  * nand layer to look for devices 
  11. */  
  12. static int s3c24xx_nand_probe(struct platform_device *pdev,  
  13.                   enum s3c_cpu_type cpu_type)  
  14. {  
  15.     struct s3c2410_platform_nand *plat = to_nand_plat(pdev);  
  16.     struct s3c2410_nand_info *info;  
  17.     struct s3c2410_nand_mtd *nmtd;  
  18.     struct s3c2410_nand_set *sets;  
  19.     struct resource *res;  
  20.     int err = 0;  
  21.     int size;  
  22.     int nr_sets;  
  23.     int setno;  
  24.     pr_debug("s3c2410_nand_probe(%p)/n", pdev);  
  25.     info = kmalloc(sizeof(*info), GFP_KERNEL); //分配s3c2410_nand_info内存   
  26.     if (info == NULL) {  
  27.         dev_err(&pdev->dev, "no memory for flash info/n");  
  28.         err = -ENOMEM;  
  29.         goto exit_error;  
  30.     }  
  31.     memset(info, 0, sizeof(*info));  
  32.     platform_set_drvdata(pdev, info);  
  33.     spin_lock_init(&info->controller.lock);  
  34.     init_waitqueue_head(&info->controller.wq);  
  35.     /* get the clock source and enable it */  
  36.     info->clk = clk_get(&pdev->dev, "nand");  
  37.     if (IS_ERR(info->clk)) {  
  38.         dev_err(&pdev->dev, "failed to get clock/n");  
  39.         err = -ENOENT;  
  40.         goto exit_error;  
  41.     }  
  42.     clk_enable(info->clk);  
  43.     /* allocate and map the resource */  
  44.     /* currently we assume we have the one resource */  
  45.     res  = pdev->resource;  
  46.     size = res->end - res->start + 1;  
  47.     info->area = request_mem_region(res->start, size, pdev->name);  
  48.     if (info->area == NULL) {  
  49.         dev_err(&pdev->dev, "cannot reserve register region/n");  
  50.         err = -ENOENT;  
  51.         goto exit_error;  
  52.     }  
  53.     info->device     = &pdev->dev;  
  54.     info->platform   = plat;  
  55.     info->regs       = ioremap(res->start, size);//存储Nand控制器的虚拟地址   
  56.     info->cpu_type   = cpu_type;  
  57.     if (info->regs == NULL) {  
  58.         dev_err(&pdev->dev, "cannot reserve register region/n");  
  59.         err = -EIO;  
  60.         goto exit_error;  
  61.     }  
  62.     dev_dbg(&pdev->dev, "mapped registers at %p/n", info->regs);  
  63.     /* initialise the hardware */  
  64.     err = s3c2410_nand_inithw(info);//初始化硬件,设置TACLS,TWRPH0/1(2440-rNFCONF)并使能nand控制器   
  65.     if (err != 0)  
  66.         goto exit_error;  
  67.     sets = (plat != NULL) ? plat->sets : NULL;  
  68.     nr_sets = (plat != NULL) ? plat->nr_sets : 1;  
  69.     info->mtd_count = nr_sets;//1   
  70.     /* allocate our information */  
  71.     size = nr_sets * sizeof(*info->mtds);//nr_sets = 1   
  72.     info->mtds = kmalloc(size, GFP_KERNEL);  
  73.     if (info->mtds == NULL) {  
  74.         dev_err(&pdev->dev, "failed to allocate mtd storage/n");  
  75.         err = -ENOMEM;  
  76.         goto exit_error;  
  77.     }  
  78.     memset(info->mtds, 0, size);  
  79.     /* initialise all possible chips */  
  80.     nmtd = info->mtds;  
  81.     for (setno = 0; setno < nr_sets; setno++, nmtd++) {//nr_sets = 1   
  82.         pr_debug("initialising set %d (%p, info %p)/n", setno, nmtd, info);  
  83.         //初始化nand_chip中的read_buf,write_buf等函数指针,ecc设置   
  84. s3c2410_nand_init_chip(info, nmtd, sets);   
  85.           //nand_scan()的第一步(nand_base.c),read ID…   
  86.         nmtd->scan_res = nand_scan_ident(&nmtd->mtd,  
  87.                          (sets) ? sets->nr_chips : 1);  
  88.         if (nmtd->scan_res == 0) {  
  89.             //如果使用硬件ecc,根据id信息中的page大小更新ecc设置   
  90.             s3c2410_nand_update_chip(info, nmtd);  
  91.             //nand_scan()的第二步(nand_base.c),给初始化的函数指针默认值,扫描坏块   
  92. nand_scan_tail(&nmtd->mtd);  
  93.             s3c2410_nand_add_partition(info, nmtd, sets);//添加分区,想mtd注册分区信息   
  94.         }  
  95.         if (sets != NULL)  
  96.             sets++;  
  97.     }  
  98.     err = s3c2410_nand_cpufreq_register(info);  
  99.     if (err < 0) {  
  100.         dev_err(&pdev->dev, "failed to init cpufreq support/n");  
  101.         goto exit_error;  
  102.     }  
  103.     if (allow_clk_stop(info)) {  
  104.         dev_info(&pdev->dev, "clock idle support enabled/n");  
  105.         clk_disable(info->clk);  
  106.     }  
  107.     pr_debug("initialised ok/n");  
  108.     return 0;  
  109.  exit_error:  
  110.     s3c2410_nand_remove(pdev);  
  111.     if (err == 0)  
  112.         err = -EINVAL;  
  113.     return err;  
  114. }  

http://blog.csdn.net/tiany524/article/details/6553100

阅读更多
个人分类: mini2440驱动程序
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭