MTD(2)---nand flash的底层驱动代码分析

http://blog.sina.com.cn/s/blog_87f8cc4e0101a449.html

介绍完了nand flash的基本知识,我们现在可以看kernel的代码了。

 

首先看下Board-dm365-evm.c(arch\arm\mach-davinci),这个文件定义了板子的一些外设信息,其中跟nandflash相关的摘录部分如下;

static struct davinci_nand_pdatadavinci_nand_data ={

      .mask_chipsel            = BIT(14),

      .parts                  = davinci_nand_partitions,

      .nr_parts       = ARRAY_SIZE(davinci_nand_partitions),

      .ecc_mode           = NAND_ECC_HW,

      .options        = NAND_USE_FLASH_BBT,

      .ecc_bits      = 4,

};

 

static struct platform_devicedavinci_nand_device ={

      .name                  = "davinci_nand",

      .id                = 0,

      .num_resources          = ARRAY_SIZE(davinci_nand_resources),

      .resource             = davinci_nand_resources,

      .dev              = {

             .platform_data      = &davinci_nand_data,

      },

};

 

static struct platform_device*dm365_evm_nand_devices[]__initdata = {

      &davinci_nand_device,

};

 

static void __initevm_init_cpld(void){

             platform_add_devices(dm365_evm_nand_devices,

                           ARRAY_SIZE(dm365_evm_nand_devices));

上面的代码,将nand设备信息添加到平台设备信息链表中,但还没有开始执行任何设备初始化的动作。

那上面这个函数是如何被调用到的呢?

static __init voiddm365_evm_init(void) àevm_init_cpld();

MACHINE_START(DAVINCI_DM365_EVM,"DaVinci DM36xEVM")

      .phys_io = IO_PHYS,

      .io_pg_offst   =(__IO_ADDRESS(IO_PHYS) >> 18) &0xfffc,

      .boot_params = (0x80000100),

      .map_io        = dm365_evm_map_io,

      .init_irq   =dm365_evm_irq_init,

      .timer           = &davinci_timer,

      .init_machine = dm365_evm_init,

MACHINE_END

上面定义的structmachine_desc,会在kernel启动的时候根据machine_arch_type匹配后,调用init_machine,这个过程就不展开细说了,可以参考网上的linux启动流程的资料,总之,kernel会调用到这个函数。

 

在这个文件 Davinci_nand.c(drivers\mtd\nand)里面才是初始化nand硬件,并注册mtd设备驱动的动作;

static struct platform_drivernand_davinci_driver ={

      .remove        = __exit_p(nand_davinci_remove),

      .driver          = {

             .name    = "davinci_nand",

      },

};

static int __initnand_davinci_init(void)

{

      returnplatform_driver_probe(&nand_davinci_driver,nand_davinci_probe);

}

 

下面重点分析nand_davinci_probe()的过程;

      structdavinci_nand_pdata   *pdata=pdev->dev.platform_data;

pdata就是Board-dm365-evm.c中定义的davinci_nand_data,定义了分区表和eccmode等信息;

 

接下来看一段代码;

      res1= platform_get_resource(pdev, IORESOURCE_MEM, 0);

      res2= platform_get_resource(pdev, IORESOURCE_MEM, 1);

      if(!res1 || !res2) {

             dev_err(&pdev->dev,"resource missing\n");

             ret= -EINVAL;

             gotoerr_nomem;

      }

 

      vaddr= ioremap(res1->start, res1->end - res1->start);

      base= ioremap(res2->start, res2->end - res2->start);

      if(!vaddr || !base) {

             dev_err(&pdev->dev,"ioremap failed\n");

             ret= -EINVAL;

             gotoerr_ioremap;

      }

 

      info->dev             = &pdev->dev;

      info->base           = base;

      info->vaddr          = vaddr;

 

      info->mtd.priv             = &info->chip;

      info->mtd.name           = dev_name(&pdev->dev);

      info->mtd.owner          = THIS_MODULE;

 

      info->mtd.dev.parent   = &pdev->dev;

 

      info->chip.IO_ADDR_R      = vaddr;

      info->chip.IO_ADDR_W     = vaddr;

上面这段代码主要的目的是得到了2个      IO地址空间:

void__iomem                    *vaddr;

void__iomem                    *base;

这2个地址实际上是跟Board-dm365-evm.c中的一个结构体相关的,

#defineDM365_ASYNC_EMIF_CONTROL_BASE      0x01d10000

#defineDM365_ASYNC_EMIF_DATA_CE0_BASE     0x02000000

#defineDM365_ASYNC_EMIF_DATA_CE1_BASE     0x04000000

static struct resourcedavinci_nand_resources[] = {

      {

             .start            = DM365_ASYNC_EMIF_DATA_CE0_BASE,

             .end       = DM365_ASYNC_EMIF_DATA_CE0_BASE +SZ_32M - 1,

             .flags            = IORESOURCE_MEM,

      },{

             .start            = DM365_ASYNC_EMIF_CONTROL_BASE,

             .end       = DM365_ASYNC_EMIF_CONTROL_BASE + SZ_4K- 1,

             .flags            = IORESOURCE_MEM,

      },

};

 

这部分必须要阅读DM368的硬件手册才能对应得上,sprufg5_TMS320DM36x DMSoC ARMSubsystem Reference Guide.pdf

4 Memory Mapping

4.1 Memory Map

0x0200 0000 0x09FF FFFF 128M ASYNCEMIF ASYNC EMIF

顺便再看一下DDR的映射,可以看到DM368最大支持256MB的DD2内存

0x8000 0000 0x8FFF FFFF 256M DDR2EMIF DDR2 EMIF

Table 7. ARMConfiguration Bus Access toPeripherals (continued)

ASYNC EMIF Control 0x01D1 0000 0x01D1 0FFF4K

ASYNC EMIF Data (CE0) 0x0200 0000 0x03FF FFFF32M

ASYNC EMIF Data (CE1) 0x0400 0000 0x05FF FFFF32M

The asynchronous external memoryinterface (AEMIF)provides an 8-bit or 16-bit data bus, an address bus width ofup to23 bits for 16-bit and 8-bit, and two dedicated chip selects, alongwithmemory control signals. The EMIF module supports:

· NAND flash memories

· OneNAND/NOR flash memories

从上面手册的说明,我们可以知道,DM368是能够支持2片nand flash的,但我们只用了一片。

这2个IO地址空间是干什么的,请参考sprufi1_TMS320DM36xDMSoCAsynchronous External Memory Interface User'sGuide.pdf,里面有详细说明。

下面列出EMIF Registers,让各位在阅读下面的代码时可以对照手册来理解。

Table 31.External Memory Interface (EMIF)Registers

OffsetAcronym Register DescriptionSection

04hAWCCR AsynchronousWait Cycle Configuration Register Section4.1

10h A1CRAsynchronous 1Configuration Register (CS2 space) Section4.2

14h A2CRAsynchronous 2Configuration Register (CS3 space) Section4.2

40h EIRREMIF InterruptRaw Register Section4.3

44h EIMREMIF InterruptMask Register Section4.4

48hEIMSR EMIF InterruptMask Set Register Section4.5

4ChEIMCR EMIF InterruptMask Clear Register Section4.6

5ChONENANDCTL OneNAND FlashControl Register Section4.7

60hNANDFCR NAND FlashControl Register Section4.8

64hNANDFSR NAND FlashStatus Register Section4.9

70hNANDF1ECC NAND Flash1-Bit ECC Register 1 (CS2 Space) Section4.10

74hNANDF2ECC NAND Flash1-Bit ECC Register 2 (CS3 Space) Section4.10

BChNAND4BITECCLOADNANDFlash 4-Bit ECC Load Register Section4.11

C0hNAND4BITECC1 NANDFlash 4-Bit ECC Register 1 Section4.12

C4hNAND4BITECC2 NANDFlash 4-Bit ECC Register 2 Section4.13

C8hNAND4BITECC3 NANDFlash 4-Bit ECC Register 3 Section4.14

CChNAND3BITECC4 NANDFlash 4-Bit ECC Register 4 Section4.15

D0hNANDERRADD1 NANDFlash 4-Bit ECC Error Address Register1 Section4.16

D4hNANDERRADD2 NANDFlash 4-Bit ECC Error Address Register2 Section4.17

D8hNANDERRVAL1 NANDFlash 4-Bit ECC Error Value Register 1 Section4.18

DChNANDERRVAL2 NANDFlash 4-Bit ECC Error Value Register 2 Section4.19

54 AsynchronousExternalMemory Interface (EMIF) SPRUFI1–March2009

SubmitDocumentation Feedback

 

现在继续看nand_davinci_probe的代码;

 

      

      info->mask_ale           = pdata->mask_ale ? : MASK_ALE;

      info->mask_cle           = pdata->mask_cle ? : MASK_CLE;

 

#define  MASK_ALE          0x08

#define  MASK_CLE         0x10

 

      

      info->chip.cmd_ctrl     = nand_davinci_hwcontrol;

      info->chip.dev_ready   = nand_davinci_dev_ready;

      

      info->chip.read_buf    = nand_davinci_read_buf;

      info->chip.write_buf   = nand_davinci_write_buf;

各位可以自行阅读这4个函数的代码,就会发现他们都用到了上面我们得到的2个IO地址空间。

 

代码讲到现在为止,我们还没有涉及到nandflash的具体硬件手册,但驱动程序不可能不跟硬件打交道,马上我们就看到跟nandflash相关的东西了。

      

      ecc_mode            = pdata->ecc_mode;

让我们再回头看下Board-dm365-evm.c中的pdata定义:

static struct davinci_nand_pdatadavinci_nand_data ={

      .mask_chipsel            = BIT(14),

      .parts                  = davinci_nand_partitions,

      .nr_parts       = ARRAY_SIZE(davinci_nand_partitions),

      .ecc_mode           = NAND_ECC_HW,

      .options        = NAND_USE_FLASH_BBT,

      .ecc_bits      = 4,

};

所以会走到下面的switch语句中;

      switch(ecc_mode) {

      caseNAND_ECC_HW:

             if(pdata->ecc_bits == 4) {

                    info->chip.ecc.calculate= nand_davinci_calculate_4bit;

                    info->chip.ecc.correct= nand_davinci_correct_4bit;

                    info->chip.ecc.hwctl= nand_davinci_hwctl_4bit;

                    info->chip.ecc.bytes= 10;

上面确定了芯片的ecc方案,继续看代码;

      info->clk= clk_get(&pdev->dev, "aemif");

      ret= clk_enable(info->clk);

clk定义在dm365.c中,此处不展开讲了,具体看硬件手册。

static struct clk aemif_clk = {

      .name           = "aemif",

      .parent          = &pll1_sysclk4,

      .lpsc             = DAVINCI_LPSC_AEMIF,

};

同样,后面关于时钟和时序相关的代码就略过不讲了,讲起来篇幅太大:)

 

      

      ret= nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2: 1);

我们跳转到nand_scan_ident里面来,这个函数在Nand_base.c(drivers\mtd\nand)中,这个文件里面就是nand flash驱动程序抽象层最底层的一些接口函数了;

      

      nand_set_defaults(chip,busw);

nand flash的操作指令是有行业标准的,所以在Nand_base.c里面定义的一些指令,所有的nandflash芯片都是要能兼容的,芯片自身特殊的操作指令,必须在这个函数之前定义自己的操作函数,就像我们在上面看到的4个函数,比如  info->chip.select_chip  =nand_davinci_select_chip;这个就是dm368自己的片选操作函数。

而大多数nand flash的接口函数都是通用的,例如:

      

      if(chip->cmdfunc == NULL)

             chip->cmdfunc= nand_command;

我们在nand_davinci_probe没有定义特别的cmdfunc,因此在这里就使用了通用的nand_command接口函数;

 

接下来是读取flash的硬件信息,主要是厂家ID和设备ID;

      

      type= nand_get_flash_type(mtd, chip, busw, &nand_maf_id);

所有系统支持的nand flash型号都在Nand_ids.c(drivers\mtd\nand)中定义,

由于版本问题,很可能你使用的flash型号在你的kernel中没有定义,那么你就需要自己添加你的flash型号到这个列表中了。

struct nand_flash_dev {

      char*name;

      intid;

      unsignedlong pagesize;

      unsignedlong chipsize;

      unsignedlong erasesize;

      unsignedlong options;

};

struct nand_flash_devnand_flash_ids[] = {

      {"NAND32MiB 1,8V 16-bit", 0x45, 512, 32,0x4000,NAND_BUSWIDTH_16},

      {"NAND128MiB 3,3V 8-bit", 0x79, 512, 128,0x4000, 0},

      {"NAND1GiB 3,3V8-bit",    0xD3,0, 1024,0, LP_OPTIONS},

      {"NAND2GiB 1,8V 16-bit",  0xB5, 0, 2048,0,LP_OPTIONS16},

摘录几个典型的nand芯片信息列表;

可见,nand驱动就是通过设备ID来判断芯片的规格参数的;

这个函数里面有段代码,需要注意,largepage的nandflash指令格式是不一样的,因此在这里有个判断,如果是largepage就使用nand_command_lp作为指令接口函数;

      

      if(mtd->writesize > 512 && chip->cmdfunc ==nand_command)

             chip->cmdfunc=  nand_command_lp;

剩下的代码,是检测2个CE上的flash是否是同一型号,如果型号不同,驱动是无法操作第二片flash的。

 

好了,从nand_scan_ident返回到nand_davinci_probe,继续看代码;

      if(pdata->ecc_bits == 4) {

             int   chunks =info->mtd.writesize / 512;

             if(chunks == 4) {

                    info->ecclayout= hwecc4_2048;

                    info->chip.ecc.mode= NAND_ECC_HW_OOB_FIRST;

                    gotosyndrome_done;

             }

syndrome_done:

             info->chip.ecc.layout= &info->ecclayout;

对应我们现在使用的8Gb的nand,page = 2KB,dm368使用的是4-bit ECC,512Bytes的数据产生10 Btyes的ECC校验码,可以校验4-bit;

 

static struct nand_ecclayouthwecc4_2048 __initconst ={

      .eccbytes= 40,

      .eccpos= {

             

            24, 25, 26, 27, 28, 29,  30, 31, 32, 33,

             34,35, 36, 37, 38, 39,  40, 41, 42, 43,

             44,45, 46, 47, 48, 49, 50, 51, 52, 53,

             54,55, 56, 57, 58, 59, 60, 61, 62, 63,

             },

      .oobfree= {

             

             {.offset= 2, .length = 22, },

             

             

      },

};

我想,nand flash厂家最初在设计OOB的时候,肯定没想到后面会有这么多人打它的主意:)

2KB的largepage对应的OOB是64Bytes,严格说来,只有坏块标记是厂家规定的,其他的字节都是可以自定义的,正因为如此,很多模块都利用了OOB来做自己的事情,比如ECC、BBT、JFFS2、YAFFS2;但是,用的人多了,还是要有规矩的,不然相互冲突,就很头疼了。

具体OOB的使用,我们会在后续相关部分再做说明,这里,我们先要知道ECC占用的区域。

 

继续看代码,

      ret= nand_scan_tail(&info->mtd);

其实,在这之前我们就已经看到mtd这个变量了,既然我们讲的是MTD子系统,那就不能再对mtd这个字眼视而不见了,必须要介绍下mtd了。

这里的info->mtd是一个 structmtd_info,它的定义很庞大,我们在之前也只见到了它的少部分成员,它的定义在Mtd.h(include\linux\mtd)里面。

简单来讲,struct mtd_info就是定义了一个flash设备的规格参数以及操作指令接口。

MTD(MemoryTechnologyDevice)就是一个专门管理flash设备的子系统,在历史上,它是独立于linuxkernel发布的,那时候,做linux系统移植的工程师需要自己添加合适的MTD版本到自己使用的kernel中来;现在省事了,MTD已经成了kernel的内置标准子系统了。

MTD的功能就是将物理的flash设备抽象成逻辑上的存储设备,提供给上层文件系统或应用程序使用,使得文件系统与具体的物理存储介质特性相分离,这在设计思想上是先进的。

 

nand_scan_tail(&info->mtd)这个函数的功能就是进一步补充完善info->mtd的成员变量,这里面主要对ECC设置做了一些处理,比如硬件ECC、软件ECC。

      structnand_chip *chip = mtd->priv;

在nand_davinci_probe里面有赋值,  info->mtd.priv            = &info->chip;

所以这里的chip就是info->chip

       switch(chip->ecc.mode){

       caseNAND_ECC_HW_OOB_FIRST:

这段代码,阅读的时候要注意下,case之后没有break,因此还要继续往下执行;

       switch(chip->ecc.mode){

       caseNAND_ECC_HW_OOB_FIRST:

              

              if(!chip->ecc.calculate|| !chip->ecc.correct ||

                  !chip->ecc.hwctl) {

                     printk(KERN_WARNING"NoECC functions supplied; "

                           "Hardware ECC notpossible\n");

                     BUG();

              }

              if(!chip->ecc.read_page)

                     chip->ecc.read_page=nand_read_page_hwecc_oob_first;

       caseNAND_ECC_HW:

              

              if(!chip->ecc.read_page)

                     chip->ecc.read_page=nand_read_page_hwecc;

              if(!chip->ecc.write_page)

                     chip->ecc.write_page=nand_write_page_hwecc;

              if(!chip->ecc.read_page_raw)

                     chip->ecc.read_page_raw=nand_read_page_raw;

              if(!chip->ecc.write_page_raw)

                     chip->ecc.write_page_raw=nand_write_page_raw;

              if(!chip->ecc.read_oob)

                     chip->ecc.read_oob=nand_read_oob_std;

              if(!chip->ecc.write_oob)

                     chip->ecc.write_oob=nand_write_oob_std;

      caseNAND_ECC_HW_SYNDROME:

             if(mtd->writesize >= chip->ecc.size)

                    break;

终于看到break了,那么这个if条件是否成立呢?

在nand_get_flash_type 中,mtd->writesize赋值为pagesize,对应我们的K9K8G08U0A就是2KB;

在nand_davinci_probe中,info->chip.ecc.size = 512;

因此,这个条件是成立的,可以break出去,不然就要使用软件ECC了。

下面的代码就很容易看懂了,计算freeoob的大小,和ecc steps;

因为ECC校验是以512 Bytes为单位的,所以large page需要做多次ECC校验。

      

      chip->ecc.layout->oobavail= 0;

      for(i = 0; chip->ecc.layout->oobfree[i].length

                    &&i < ARRAY_SIZE(chip->ecc.layout->oobfree);i++)

             chip->ecc.layout->oobavail+=

                    chip->ecc.layout->oobfree[i].length;

      mtd->oobavail= chip->ecc.layout->oobavail;

 

      

      chip->ecc.steps= mtd->writesize / chip->ecc.size;

      if(chip->ecc.steps* chip->ecc.size != mtd->writesize){

             printk(KERN_WARNING"Invalid ecc parameters\n");

             BUG();

      }

      chip->ecc.total= chip->ecc.steps * chip->ecc.bytes;

 

下面的代码也可以略过了,虽然理论上可以将512Bytes划分为subpage单独读写,但是这样做好处不大,反而要考虑兼容性。

      

 

      

      mtd->type= MTD_NANDFLASH;

      mtd->flags= MTD_CAP_NANDFLASH;

      mtd->erase= nand_erase;

      mtd->point= NULL;

      mtd->unpoint= NULL;

      mtd->read= nand_read;

      mtd->write= nand_write;

      mtd->read_oob= nand_read_oob;

      mtd->write_oob= nand_write_oob;

      mtd->sync= nand_sync;

      mtd->lock= NULL;

      mtd->unlock= NULL;

      mtd->suspend= nand_suspend;

      mtd->resume= nand_resume;

      mtd->block_isbad= nand_block_isbad;

      mtd->block_markbad= nand_block_markbad;

相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
本站公众号
   欢迎关注本站公众号,获取更多程序园信息
开发小院