Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2872377
  • 博文数量: 674
  • 博客积分: 17881
  • 博客等级: 上将
  • 技术积分: 4849
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 10:15
文章分类

全部博文(674)

文章存档

2013年(34)

2012年(146)

2011年(197)

2010年(297)

分类: LINUX

2013-07-13 15:11:52

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

 

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

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 DM36x EVM")

       .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

上面定义的struct machine_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,定义了分区表和ecc mode等信息;

 

接下来看一段代码;

       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中的一个结构体相关的,

#define DM365_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 ARM Subsystem 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 to Peripherals (continued)

ASYNC EMIF Control 0x01D1 0000 0x01D1 0FFF 4K

ASYNC EMIF Data (CE0) 0x0200 0000 0x03FF FFFF 32M

ASYNC EMIF Data (CE1) 0x0400 0000 0x05FF FFFF 32M

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

· NAND flash memories

· OneNAND/NOR flash memories

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

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

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

Table 31.External Memory Interface (EMIF) Registers

OffsetAcronym Register Description Section

04h AWCCR AsynchronousWait Cycle Configuration Register Section4.1

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

14h A2CR Asynchronous 2Configuration Register (CS3 space) Section4.2

40h EIRR EMIF InterruptRaw Register Section 4.3

44h EIMR EMIF InterruptMask Register Section 4.4

48h EIMSR EMIF InterruptMask Set Register Section 4.5

4Ch EIMCR EMIF InterruptMask Clear Register Section 4.6

5Ch ONENANDCTL OneNAND FlashControl Register Section 4.7

60h NANDFCR NAND FlashControl Register Section 4.8

64h NANDFSR NAND FlashStatus Register Section 4.9

70h NANDF1ECC NAND Flash1-Bit ECC Register 1 (CS2 Space) Section 4.10

74h NANDF2ECC NAND Flash1-Bit ECC Register 2 (CS3 Space) Section 4.10

BCh NAND4BITECCLOADNANDFlash 4-Bit ECC Load Register Section4.11

C0h NAND4BITECC1 NANDFlash 4-Bit ECC Register 1 Section 4.12

C4h NAND4BITECC2 NANDFlash 4-Bit ECC Register 2 Section 4.13

C8h NAND4BITECC3 NANDFlash 4-Bit ECC Register 3 Section 4.14

CCh NAND3BITECC4 NANDFlash 4-Bit ECC Register 4 Section 4.15

D0h NANDERRADD1 NANDFlash 4-Bit ECC Error Address Register 1 Section 4.16

D4h NANDERRADD2 NANDFlash 4-Bit ECC Error Address Register 2 Section 4.17

D8h NANDERRVAL1 NANDFlash 4-Bit ECC Error Value Register 1 Section 4.18

DCh NANDERRVAL2 NANDFlash 4-Bit ECC Error Value Register 2 Section 4.19

54 AsynchronousExternal Memory Interface (EMIF) SPRUFI1–March 2009

SubmitDocumentation Feedback

 

现在继续看nand_davinci_probe的代码;

 

       /*use nandboot-capable ALE/CLE masks by default */

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

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

/* NOTE:  boards don't need to use these address bits

 * for ALE/CLE unless they support booting fromNAND.

 * They're used unless platform data overridesthem.

 */

#define   MASK_ALE           0x08

#define   MASK_CLE          0x10

 

       /*Set address of hardware control function */

       info->chip.cmd_ctrl      = nand_davinci_hwcontrol;

       info->chip.dev_ready    = nand_davinci_dev_ready;

       /*Speed up buffer I/O */

       info->chip.read_buf     = nand_davinci_read_buf;

       info->chip.write_buf    = nand_davinci_write_buf;

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

 

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

       /*Use board-specific ECC config */

       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,

};

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

 

       /*Scan to find existence of the device(s) */

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

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

       /*Set the default functions */

       nand_set_defaults(chip,busw);

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

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

       /*check, if a user supplied command function given */

       if(chip->cmdfunc == NULL)

              chip->cmdfunc= nand_command;

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

 

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

       /*Read the flash type */

       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,3V 8-bit",    0xD3, 0, 1024,0, LP_OPTIONS},

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

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

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

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

       /*Do not replace user supplied command function ! */

       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,512 Bytes的数据产生10 Btyes的ECC校验码,可以校验4-bit;

/* An ECC layout for using 4-bit ECCwith large-page (2048bytes) flash,

 * storing ten ECC bytes plus themanufacturer's bad block marker byte,

 * and not overlapping the default BBT markers.

 */

static struct nand_ecclayouthwecc4_2048 __initconst = {

       .eccbytes= 40,

       .eccpos= {

              /*at the end of spare sector */

              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= {

              /*2 bytes at offset 0 hold manufacturer badblock markers */

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

              /*5 bytes at offset 8 hold BBT markers */

              /*8 bytes at offset 16 hold JFFS2 clean markers */

       },

};

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

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

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

 

继续看代码,

       ret= nand_scan_tail(&info->mtd);

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

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

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

MTD(MemoryTechnology Device)就是一个专门管理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:

              /*Similar to NAND_ECC_HW, but a separate read_page handle */

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

                   !chip->ecc.hwctl) {

                     printk(KERN_WARNING"No ECC 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:

              /*Use standard hwecc read page function ? */

              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赋值为page size,对应我们的K9K8G08U0A就是2KB;

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

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

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

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

       /*

        * The number of bytes available for a clientto place data into

        * the out of band area

        */

       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;

 

       /*

        * Set the number of read / write steps for onepage depending on ECC

        * mode

        */

       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单独读写,但是这样做好处不大,反而要考虑兼容性。

       /*

        * Allow subpage writes up to ecc.steps. Notpossible for MLC

        * FLASH.

        */

 

       /*Fill in remaining MTD driver data */

       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;

 

       /*propagate ecc.layout to mtd_info */

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

 

       /*Build bad block table */

       returnchip->scan_bbt(mtd);

最后一步是扫描坏块,建立坏块表了。

       if(!chip->scan_bbt)

              chip->scan_bbt= nand_default_bbt;

坏块表是很重要的一部分,下次再讲吧。
http://blog.csdn.net/lidehua1975/article/details/7683311

阅读(3906) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~