nandflash驱动分析 针对K9GAG08U0D uboot1.1.6(上)

来源:互联网 发布:极乐宿舍使用软件 编辑:程序博客网 时间:2024/06/05 01:15

先初步了解下这块nand芯片:

解释:这张是该芯片结构图,4096个块,1块128页,1页4KB+218B;

片内寻址-5个周期 分列地址和行地址,自己看图

这块和另一块芯片K9GAG08U0E差不多,驱动方面几近相同,只是后者每页是8KB+436B



这里先讲一点

.至于4KB+218B中哪些是数据区 哪些是额外的,这得看ecc模块的设计者,从flash角度来看,这4K+218字节是完全等同的。而ecc模块早就被生产厂商做死,无需自行设计。


现在开始分析了。

一.s3c6410支持从SD卡启动,从SD卡启动后就可以把Uboot.bin文件写入NandFlash了,可以认真的看一下Uboot 里面 的 nand write.uboot 命令是如何把数据写入NandFlash前4页 的。写入成功后就可以从NandFlash启动Uboot。

       nand write.uboot 的关键代码 位于uboot1.1.6/common/cmd_nand.c文件中:


else if (!read && s != NULL && (!strcmp(s, ".uboot")) && nand->writesize == 4096) {
            size=4096;
            nand_write(nand, off, &size, (u_char *)addr);
            off+=4096;
            addr+=2048;
            nand_write(nand, off, &size, (u_char *)addr);
            off+=4096;
            addr+=2048;
            nand_write(nand, off, &size, (u_char *)addr);
            off+=4096;
            addr+=2048;
            nand_write(nand, off, &size, (u_char *)addr);
            off+=4096;
            addr+=2048;

//写剩余的页面,这里的Uboot占用1M的NandFlash地址空间,在U0D里面占用256个页面(256Page=1M),上面已经写了四个页面了,这里写剩余的252个页,252个页面足够存放Uboot的有效数据了。                       

            size=1024*1024-4*4096;

             ret = nand_write(nand, off, &size, (u_char *)addr);



                }

//6410支持4K每页 只不过在nand启动时默认把前8K程序当作2K每页存取,故在nand上前16K(4个页)实际上是以每页2K存取8K的程序,其余都是4K每页存取的,上述代码就是这个意思。


二.uboot1.1.6/cpu/s3c64xx/nand_cp.c 文件



#include <common.h>

#ifdef CONFIG_S3C64XX
#include <asm/io.h>
#include <linux/mtd/nand.h>
#include <regs.h>

/*
 * address format地址格式
 *              17 16         9 8            0
 * --------------------------------------------
 * | block(12bit) | page(5bit) | offset(9bit) |
 * --------------------------------------------
 */

static int nandll_read_page (uchar *buf, ulong addr, int large_block)
{
        int i;
    int page_size = 512;

    if (large_block==1)
        page_size = 2048;
    if (large_block==2)
                page_size = 4096;//这个类型2
        if(large_block==3)
                page_size = 8192;

        NAND_ENABLE_CE();

        NFCMD_REG = NAND_CMD_READ0;

        /* Write Address */5 个寻址周期,参看第一张图
        NFADDR_REG = 0;

        if (large_block)
            NFADDR_REG = 0;

    NFADDR_REG = (addr) & 0xff;
    NFADDR_REG = (addr >> 8) & 0xff;
    NFADDR_REG = (addr >> 16) & 0xff;

    if (large_block)
        NFCMD_REG = NAND_CMD_READSTART;

        NF_TRANSRnB();

    /* for compatibility(2460). u32 cannot be used. by scsuh */
    for(i=0; i < page_size; i++) {
                *buf++ = NFDATA8_REG;
        }

        NAND_DISABLE_CE();
        return 0;
}

/*
 * Read data from NAND.读数据
 */
static int nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
{
        uchar *buf = (uchar *)dst_addr;
        int i;
    uint page_shift = 9;

    if (large_block==1)
        page_shift = 11;

    if(large_block==2)
        page_shift = 12;

/*

PAGE_SHIFT,页帧号

内核地址,无论是虚拟的还物理的,其都由两部分组成。往往是高N位是页号,低M位是页内的偏移量。

当我们将地址中的低M位偏移量抛弃不用,高N位的页号,移到右端,得到这个结果称页帧号。

宏PAGE_SHIFT, 告诉我们要右移多少位得到页帧号。至于为什么要右移12位,看第一张图。

*/       

       if(large_block==3)
                page_shift =13;

    if(large_block == 2)
    {
                /* Read pages */读前4页
        for (i = 0; i < 4; i++, buf+=(1<<(page_shift-1))) {//buf=0xffe
                        nandll_read_page(buf, i, large_block);
        }


        /* Read pages */读后56页
        for (i = 4; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift)) { //buf=0xfff,i<60
                nandll_read_page(buf, i, large_block);
        }

        }else if(large_block == 3)  //K9GAG08U0E
        {
            /* Read pages */
            for (i = 0; i < 4; i++, buf+=(1<<(page_shift-2))) {
                    nandll_read_page(buf, i, large_block);
            }


            /* Read pages */
            for (i = 4; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift)) {
                    nandll_read_page(buf, i, large_block);
            }
        }
    else
    {
        for (i = 0; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift)) {
                nandll_read_page(buf, i, large_block);
        }
    }
        return 0;
}

int copy_uboot_to_ram (void)//把uboot代码写到ram中
{


    int large_block = 0;
    int i;
    vu_char id;

        NAND_ENABLE_CE();
        NFCMD_REG=NAND_CMD_RESET;
        NF_TRANSRnB();


        NFCMD_REG = NAND_CMD_READID;
        NFADDR_REG =  0x00;

        NF_TRANSRnB();

    /* wait for a while */
        for (i=0; i<200; i++);

        int factory = NFDATA8_REG;
    id = NFDATA8_REG;

        int cellinfo=NFDATA8_REG;
        int tmp= NFDATA8_REG;

        int childType=tmp & 0x03; //Page size

    if (id > 0x80)
        {
            large_block = 1;
        }

        if(id == 0xd5 && childType==0x01 )
        {
            large_block = 2;


        }else if(id == 0xd5 && childType==0x02 )
        {
            large_block = 3;

        }

    /* read NAND Block.
     * 128KB ->240KB because of U-Boot size increase. by scsuh
     * So, read 0x3c000 bytes not 0x20000(128KB).
     */
    return nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000, large_block);
}


#if 1

#define REG_GPFCON       (0x7F0080A0)
#define REG_GPFDAT       (0x7F0080A4)

void beep(void)
{

   uint reg_f_cfg=readl(REG_GPFCON) & 0x3FFFFFFF | (1<<30);
   writel(reg_f_cfg,REG_GPFCON);

   uint reg_f_on=readl(REG_GPFDAT) & 0xFFFF7FFF | (1<<15);
   uint reg_f_off=readl(REG_GPFDAT) & 0xFFFF7FFF;

   writel(reg_f_on,REG_GPFDAT);
}
#else
void  beep(void)
{

}
#endif

#endif

三.现在来看nand驱动的流程 进入start.S看到这段


#ifdef CONFIG_BOOT_NAND//我们需要的
    mov    r0, #0x1000
    bl    copy_from_nand
#endif

关于copy_from_nand 具体如下:

    .globl copy_from_nand
copy_from_nand:
    mov    r10, lr        /* save return address */

    mov    r9, r0
    /* get ready to call C functions */
    ldr    sp, _TEXT_PHY_BASE    /* setup temp stack pointer */
    sub    sp, sp, #12
    mov    fp, #0            /* no previous frame, so fp=0 */
    mov    r9, #0x1000
    bl    copy_uboot_to_ram

3:    tst     r0, #0x0
    bne    copy_failed

    ldr    r0, =0x0c000000
    ldr    r1, _TEXT_PHY_BASE
1:    ldr    r3, [r0], #4
    ldr    r4, [r1], #4
    teq    r3, r4
    bne    compare_failed    /* not matched */
    subs    r9, r9, #4
    bne    1b

4:    mov    lr, r10        /* all is OK */
    mov    pc, lr

copy_failed:
    nop            /* copy from nand failed */
    b    copy_failed

compare_failed:
    nop            /* compare failed */
    b    compare_failed



其中copy_uboot_to_ram的实现在nand_cp.c中

int copy_uboot_to_ram (void)//把uboot代码写到ram中
{


    int large_block = 0;
    int i;
    vu_char id;

        NAND_ENABLE_CE();
        NFCMD_REG=NAND_CMD_RESET;
        NF_TRANSRnB();


        NFCMD_REG = NAND_CMD_READID;
        NFADDR_REG =  0x00;

        NF_TRANSRnB();

    /* wait for a while */
        for (i=0; i<200; i++);

        int factory = NFDATA8_REG;
    id = NFDATA8_REG;

        int cellinfo=NFDATA8_REG;
        int tmp= NFDATA8_REG;

        int childType=tmp & 0x03; //Page size

    if (id > 0x80)
        {
            large_block = 1;
        }

        if(id == 0xd5 && childType==0x01 )
        {
            large_block = 2;


        }else if(id == 0xd5 && childType==0x02 )
        {
            large_block = 3;

        }

    /* read NAND Block.
     * 128KB ->240KB because of U-Boot size increase. by scsuh
     * So, read 0x3c000 bytes not 0x20000(128KB).
     */
    return nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000, large_block);
}



进入board.c,arm_start中有nand_init具体实现在smdk6410.c中

void nand_init(void)
{
    nand_probe(CFG_NAND_BASE);//检测芯片型号
        if (nand_dev_desc[0].ChipID != NAND_ChipID_UNKNOWN)
        {
           print_size(nand_dev_desc[0].totlen, "\n");
        }
}
#endif


四.uboot加载运行后接下来分析nand命令,我感觉这才是重点

1.先认识下主要数据结构

struct nand_flash_dev 数据结构,在/include/linux/MTD/nand.h 中


struct nand_flash_dev {
    char *name;
    int id;
    unsigned long pagesize;
    unsigned long chipsize;
    unsigned long erasesize;
    unsigned long options;
};

struct nand_chip 数据结构
该数据结构在 include/linux/mtd/nand.h 中定义

struct platform_nand_chip {
    int            nr_chips;
    int            chip_offset;
    int            nr_partitions;
    struct mtd_partition    *partitions;
    struct nand_ecclayout    *ecclayout;
    int            chip_delay;
    unsigned int        options;
    void            *priv;
};

2.查看common/cmd_nand.c 咱们来看看都有哪些命令还有这些命令怎么执行的

erase擦除操作

if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) {
        nand_erase_options_t opts;
        /* "clean" at index 2 means request to write cleanmarker */
        int clean = argc > 2 && !strcmp("clean", argv[2]);
        int o = clean ? 3 : 2;
        int scrub = !strcmp(cmd, "scrub");

        printf("\nNAND %s: ", scrub ? "scrub" : "erase");
        /* skip first two or three arguments, look for offset and size */
        if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)
            return 1;

        memset(&opts, 0, sizeof(opts));
        opts.offset = off;
        opts.length = size;
        opts.jffs2  = clean;
        opts.quiet  = quiet;

        if (scrub) {
            puts("Warning: "
                 "scrub option will erase all factory set "
                 "bad blocks!\n"
                 "         "
                 "There is no reliable way to recover them.\n"
                 "         "
                 "Use this command only for testing purposes "
                 "if you\n"
                 "         "
                 "are sure of what you are doing!\n"
                 "\nReally scrub this NAND flash? <y/N>\n");

            if (getc() == 'y' && getc() == '\r') {
                opts.scrub = 1;
            } else {
                puts("scrub aborted\n");
                return -1;
            }
        }
        ret = nand_erase_opts(nand, &opts);
        printf("%s\n", ret ? "ERROR" : "OK");

        return ret == 0 ? 0 : 1;
    }

重点看对yaffs2文件系统的写支持

#ifdef CFG_NAND_YAFFS_WRITE
        } else if (!read && s != NULL && + (!strcmp(s, ".yaffs2") || !strcmp(s, ".yaffs1"))) {
            nand_write_options_t opts;
             memset(&opts, 0, sizeof(opts));
             opts.buffer = (u_char*) addr;
             opts.length = size;
             opts.offset = off;
             opts.pad = 0;
             opts.blockalign = 1;
             opts.quiet = quiet;
             opts.writeoob = 1;
             opts.autoplace = 1;

            /* jsgood */
             /* if (s[6] == '1')
                opts.forceyaffs = 1; */

             ret = nand_write_opts(nand, &opts);
#endif

        }

现在针对其中的调用处理进行详细跟踪

关于nand_write_options_t opts ;在nand.h和nand_util.c有定义

 


struct nand_write_options {
 u_char *buffer;  /* memory block containing image to write用于写镜像的内存块 */
 ulong length;  /* number of bytes to write描述多少字节 */
 ulong offset;  /* start address in NAND 烧写开始地址*/
 int quiet;  /* don't display progress messages 不显示烧写进度*/
 int autoplace;  /* if true use auto oob layout 采用自动(默认)obb布局*/
 int forcejffs2;  /* force jffs2 oob layout采用jffs2obb布局 */
 int forceyaffs;  /* force yaffs oob layout 采用yaffs布局*/
 int noecc;  /* write without ecc烧写时不写ecc */
 int writeoob;  /* image contains oob data烧写时包含obb区的数据 */
 int pad;  /* pad to page size 页大小*/
 int blockalign;  /* 1|2|4 set multiple of eraseblocks   设置擦写对齐方式
     * to align to */
};

typedef struct nand_write_options nand_write_options_t;

 

在nand_util.c里的具体实现,好长啊。。。。//我感觉英文注释已经很给力了 我就不注释了

 

 

/**
 * nand_write_opts: - write image to NAND flash with support for various options
 *
 * @param meminfo NAND device to erase
 * @param opts  write options (@see nand_write_options)
 * @return  0 in case of success
 *
 * This code is ported from nandwrite.c from Linux mtd utils by
 * Steven J. Hill and Thomas Gleixner.
 */
int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
{
 int imglen = 0;
 int pagelen;
 int baderaseblock;
 int blockstart = -1;
 loff_t offs;
 int readlen;
 int oobinfochanged = 0;
 int percent_complete = -1;

 /* org: struct nand_oobinfo old_oobinfo; */
 struct nand_ecclayout old_oobinfo;
 
 ulong mtdoffset = opts->offset;
 ulong erasesize_blockalign;
 u_char *buffer = opts->buffer;
 size_t written;
 int result;

 /* jsgood */
 struct mtd_oob_ops oob_ops;

 if (opts->pad && opts->writeoob) {
  printf("Can't pad when oob data is present.\n");
  return -1;
 }

 /* set erasesize to specified number of blocks - to match
  * jffs2 (virtual) block size */
 if (opts->blockalign == 0) {
  erasesize_blockalign = meminfo->erasesize;
 } else {
  erasesize_blockalign = meminfo->erasesize * opts->blockalign;
 }

 /* make sure device page sizes are valid */
 if (!(meminfo->oobsize == 16 && meminfo->writesize == 512)
     && !(meminfo->oobsize == 8 && meminfo->writesize == 256)
     && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)
            && !(meminfo->oobsize == 128 && meminfo->writesize == 4096)
            && !(meminfo->oobsize == 232 && meminfo->writesize == 8192))

        {
                printf("meminfo->oobsize == %d && meminfo->writesize == %d",meminfo->oobsize,meminfo->writesize);
  printf("Unknown flash (not normal NAND)\n");
  return -1;
 }

 /* read the current oob info */
 /* org: memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); */
 memcpy(&old_oobinfo, meminfo->ecclayout, sizeof(struct nand_ecclayout));

 /* write without ecc? */
 if (opts->noecc) {
  /* org: memcpy(&meminfo->oobinfo, &none_oobinfo,
         sizeof(meminfo->oobinfo)); */
  memcpy(meminfo->ecclayout, &none_oobinfo,
   sizeof(struct nand_ecclayout));
  oobinfochanged = 1;

                printf("opts->noecc***************************\n");
 }

 /* autoplace ECC? */
 if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {

  /* org: memcpy(&meminfo->oobinfo, &autoplace_oobinfo,
         sizeof(meminfo->oobinfo)); */
  memcpy(meminfo->ecclayout, &autoplace_oobinfo, sizeof(struct nand_ecclayout));
  oobinfochanged = 1;

               printf("opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)***************************\n");
 }

 /* force OOB layout for jffs2 or yaffs? */
 if (opts->forcejffs2 || opts->forceyaffs) {
  /* org: struct nand_oobinfo *oobsel =
   opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; */
  struct nand_ecclayout *oobsel =
   opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;

  if (meminfo->oobsize == 8) {
   if (opts->forceyaffs) {
    printf("YAFSS cannot operate on "
           "256 Byte page size\n");
    goto restoreoob;
   }
                        /* Adjust number of ecc bytes */
   jffs2_oobinfo.eccbytes = 3;
  }

  /* org: memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); */
  memcpy(meminfo->ecclayout, oobsel, sizeof(struct nand_ecclayout));

                printf("opts->forcejffs2 || opts->forceyaffs");
 }

 /* get image length */
 imglen = opts->length;
 pagelen = meminfo->writesize
  + ((opts->writeoob != 0) ? meminfo->oobsize : 0);

     //   printf("opts->writeoob=%d,meminfo->oobsize=%d.................. \n",opts->writeoob,meminfo->oobsize);

 /* check, if file is pagealigned */
 if ((!opts->pad) && ((imglen % pagelen) != 0)) {
  printf("Input block length is not page aligned\n");
  goto restoreoob;
 }

 /* check, if length fits into device */
 if (((imglen / pagelen) * meminfo->writesize)
      > (meminfo->size - opts->offset)) {
  printf("Image %d bytes, NAND page %d bytes, "
         "OOB area %u bytes, device size %u bytes\n",
         imglen, pagelen, meminfo->writesize, meminfo->size);
  printf("Input block does not fit into device\n");
  goto restoreoob;
 }

 if (!opts->quiet)
  printf("\n");

 /* get data from input and write to the device */
 while (imglen && (mtdoffset < meminfo->size)) {

  WATCHDOG_RESET ();

  /*
   * new eraseblock, check for bad block(s). Stay in the
   * loop to be sure if the offset changes because of
   * a bad block, that the next block that will be
   * written to is also checked. Thus avoiding errors if
   * the block(s) after the skipped block(s) is also bad
   * (number of blocks depending on the blockalign
   */
  while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
   blockstart = mtdoffset & (~erasesize_blockalign+1);
   offs = blockstart;
   baderaseblock = 0;

   /* check all the blocks in an erase block for
    * bad blocks */
   do {
    int ret = meminfo->block_isbad(meminfo, offs);

    if (ret < 0) {
     printf("Bad block check failed\n");
     goto restoreoob;
    }
    if (ret == 1) {
     baderaseblock = 1;
     if (!opts->quiet)
      printf("\rBad block at 0x%lx "
             "in erase block from "
             "0x%x will be skipped\n",
             (long) offs,
             blockstart);
    }

    if (baderaseblock) {
     mtdoffset = blockstart
      + erasesize_blockalign;
    }
    offs +=  erasesize_blockalign
     / opts->blockalign;
   } while (offs < blockstart + erasesize_blockalign);
  }

  readlen = meminfo->writesize;
  if (opts->pad && (imglen < readlen)) {
   readlen = imglen;
   memset(data_buf + readlen, 0xff,
          meminfo->writesize - readlen);
  }

  /* read page data from input memory buffer */
  memcpy(data_buf, buffer, readlen);
  buffer += readlen;

  /* This is yaffs2 writing if opts->writeoob == 1,
   * and the other case is jffs2 writing in S3C NAND by jsgood.
   */
  if (opts->writeoob) {
   /* read OOB data from input memory block, exit
    * on failure */
   memcpy(oob_buf, buffer, meminfo->oobsize);
   buffer += meminfo->oobsize;

                        ////debug
                        /*
                        int k=0;
                        for(k=0;k<meminfo->oobsize;k++)
                        {

                            if((k+1)%6==0)
                            {
                                printf("%x ",oob_buf[k]);
                                printf("\n");

                            }else
                            {
                                printf("%x ",oob_buf[k]);

                            }

 

                        }

                        printf("........................\n");

                        */


   /* write OOB data first, as ecc will be placed
    * in there*/
   /* org: result = meminfo->write_oob(meminfo,
          mtdoffset,
          meminfo->oobsize,
          &written,
          (unsigned char *)
          &oob_buf); */
   oob_ops.mode = MTD_OOB_AUTO;
   oob_ops.len = meminfo->writesize;
   oob_ops.ooboffs = 0;
   oob_ops.ooblen = meminfo->oobsize;   
   oob_ops.oobbuf = (unsigned char *)&oob_buf;
   oob_ops.datbuf = (unsigned char *)&data_buf;

   result = meminfo->write_oob(meminfo, mtdoffset, &oob_ops);

   if (result != 0) {
    printf("\nMTD writeoob failure: %d\n",
           result);
    goto restoreoob;
   }
   imglen -= meminfo->oobsize;
  } else {
   /* write out the page data */
   result = meminfo->write(meminfo,
      mtdoffset,
      meminfo->writesize,
      &written,
      (unsigned char *) &data_buf);

   if (result != 0) {
    printf("writing NAND page at offset 0x%lx failed\n",
           mtdoffset);
    goto restoreoob;
   }
  }

  imglen -= readlen;

  if (!opts->quiet) {
   unsigned long long n = (unsigned long long)
     (opts->length-imglen) * 100;
   int percent;

   do_div(n, opts->length);
   percent = (int)n;

   /* output progress message only at whole percent
    * steps to reduce the number of messages printed
    * on (slow) serial consoles
    */
   if (percent != percent_complete) {
    printf("\rWriting data at 0x%x "
           "-- %3d%% complete.",
           mtdoffset, percent);
    percent_complete = percent;
   }
  }

  mtdoffset += meminfo->writesize;
 }

 if (!opts->quiet)
  printf("\n");

restoreoob:
 if (oobinfochanged) {
  /* org: memcpy(&meminfo->oobinfo, &old_oobinfo,
         sizeof(meminfo->oobinfo)); */
  memcpy(meminfo->ecclayout, &old_oobinfo,
         sizeof(struct nand_ecclayout));        
 }

 if (imglen > 0) {
  printf("Data did not fit into device, due to bad blocks\n");
  return -1;
 }

 /* return happy */
 return 0;
}

 

 

关于menset即分配空间

下面那些参数已经在struct nand_write_options中说明了,详见上面。

五.现在开始分析nand驱动构架,所有驱动代码均位于nand中


 

1、  nand_base.c:
定义了NAND驱动中对NAND芯片最基本的操作函数和操作流程,如擦除、读写page、读写oob等。当然这些函数都只是进行一些default的操作,若你的系统在对NAND操作时有一些特殊的动作,则需要在你自己的驱动代码中进行定义,然后Replace这些default的函数。
 
2、  nand_bbt.c:
定义了NAND驱动中与坏块管理有关的函数和结构体。
 
3、  nand_ids.c:
定义了两个全局类型的结构体:struct nand_flash_dev nand_flash_ids[ ]和struct nand_manufacturers nand_manuf_ids[ ]。其中前者定义了一些NAND芯片的类型,后者定义了NAND芯片的几个厂商。NAND芯片的ID至少包含两项内容:厂商ID和厂商为自己的NAND芯片定义的芯片ID。当NAND驱动被加载的时候,它会去读取具体NAND芯片的ID,然后根据读取的内容到上述定义的nand_manuf_ids[ ]和nand_flash_ids[ ]两个结构体中去查找,以此判断该NAND芯片是那个厂商的产品,以及该NAND芯片的类型。若查找不到,则NAND驱动就会加载失败,因此在开发NAND驱动前必须事先将你的NAND芯片添加到这两个结构体中去(其实这两个结构体中已经定义了市场上绝大多数的NAND芯片,所以除非你的NAND芯片实在比较特殊,否则一般不需要额外添加)。值得一提的是,nand_flash_ids[ ]中有三项属性比较重要,即pagesize、chipsize和erasesize,驱动就是依据这三项属性来决定对NAND芯片进行擦除,读写等操作时的大小的。其中pagesize即NAND芯片的页大小,一般为256、512或2048;chipsize即NAND芯片的容量;erasesize即每次擦除操作的大小,通常就是NAND芯片的block大小。
 
4、  nand_ecc.c:
定义了NAND驱动中与softeware ECC有关的函数和结构体,若你的系统支持hardware ECC,且不需要software ECC,则该文件也不需理会。
 
5、  nand.c:
参考用的驱动例子 真正的nand.c在board/samsung/smdk6410中
 
6、  diskonchip.c:
定义了片上磁盘(DOC)相关的一些函数,开发普通NAND驱动时不用理会。
 
7.nand_util.c一些操作的定义和实现,蛮重要的
最后拿出nand.C瞧一瞧分析一下,我会就这一段代码进行详细解读

//如果是K9GAG08U0D就用下面的obb区结构
static struct nand_ecclayout s3c_nand_oob_mlc_128_8bit = {
        .useecc = MTD_NANDECC_AUTOPLACE,
    .eccbytes = 104,
    .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,
           64,65,66,67,68,69,70,71,72,73,
           74,75,76,77,78,79,80,81,82,83,
           84,85,86,87,88,89,90,91,92,93,
           94,95,96,97,98,99,100,101,102,103,
           104,105,106,107,108,109,110,111,112,113,
           114,115,116,117,118,119,120,121,122,123,
           124,125,126,127},
    .oobfree = {
        {.offset = 2,
                 .length = 22}}
};

//初始化代码
/*
 * Board-specific NAND initialization. The following members of the
 * argument are board-specific (per include/linux/mtd/nand.h):
 * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
 * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
 * - hwcontrol: hardwarespecific function for accesing control-lines
 * - dev_ready: hardwarespecific function for  accesing device ready/busy line
 * - enable_hwecc?: function to enable (reset)  hardware ecc generator. Must
 *   only be provided if a hardware ECC is available
 * - eccmode: mode of ecc, see defines
 * - chip_delay: chip dependent delay for transfering data from array to
 *   read regs (tR)
 * - options: various chip options. They can partly be set to inform
 *   nand_scan about special functionality. See the defines for further
 *   explanation
 * Members with a "?" were not set in the merged testing-NAND branch,
 * so they are not set here either.
 */
void board_nand_init(struct nand_chip *nand)
{
#if defined(CFG_NAND_HWECC)
    int i;
    u_char tmp;
    u_char dev_id;
    struct nand_flash_dev *type = NULL;
#endif

    if (NFCONF_REG & 0x80000000)
        boot_nand = 1;
    else
        boot_nand = 0;

    NFCONT_REG         &= ~NFCONT_WP;
    nand->IO_ADDR_R        = (void __iomem *)(NFDATA);
    nand->IO_ADDR_W        = (void __iomem *)(NFDATA);
    nand->cmd_ctrl        = s3c_nand_hwcontrol;
    nand->dev_ready        = s3c_nand_device_ready;
    nand->scan_bbt        = s3c_nand_scan_bbt;
    nand->options        = 0;

#if defined(CFG_NAND_FLASH_BBT)
        nand->options         |= NAND_USE_FLASH_BBT;
#else
        nand->options        |= NAND_SKIP_BBTSCAN;
#endif

#if defined(CFG_NAND_HWECC)
    nand->ecc.mode        = NAND_ECC_HW;
        nand->ecc.hwctl        = s3c_nand_enable_hwecc;
    nand->ecc.calculate    = s3c_nand_calculate_ecc;
    nand->ecc.correct    = s3c_nand_correct_data;


        // K9GAG08U0E must add below codes
        {
          s3c_nand_hwcontrol(0, NAND_CMD_RESET, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
          s3c_nand_device_ready(0);
        }

        s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
        s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
        s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
        s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

        s3c_nand_device_ready(0);

    tmp = readb(nand->IO_ADDR_R); /* Maf. ID */

      //  printf("Manufactor ID:%x\n",tmp);

    tmp = dev_id = readb(nand->IO_ADDR_R); /* Device ID */

        //printf("dev_id ID:%x \n",dev_id);

    for (i = 0; nand_flash_ids[i].name != NULL; i++) {
        if (tmp == nand_flash_ids[i].id) {
            type = &nand_flash_ids[i];
            break;
        }
    }
    
    nand->cellinfo = readb(nand->IO_ADDR_R);    /* 3rd byte */
    tmp = readb(nand->IO_ADDR_R);            /* 4th byte */

    //    printf("tmp = %x\n",tmp);

        if (!type->pagesize)
        {
                if (((nand->cellinfo >> 2) & 0x3) == 0)
                {

 #if 0
            nand_type = S3C_NAND_TYPE_MLC;
            nand->options |= NAND_NO_SUBPAGE_WRITE;    /* NOP = 1 if MLC */
            nand->ecc.read_page = s3c_nand_read_page_4bit;
            nand->ecc.write_page = s3c_nand_write_page_4bit;
            nand->ecc.size = 512;
            nand->ecc.bytes = 8;    /* really 7 bytes */
                        if ((1024 << (tmp & 0x3)) > 512)
                        {
                nand->ecc.layout = &s3c_nand_oob_mlc_64;
#ifdef FORLINX_DEBUG
                                printf("Nandflash:Type=MLC  ChipName:samsung-K9F2G08U0B or hynix-HY27UF082G2B \n");
#endif
                        }else
                        {
                                nand->ecc.layout = &s3c_nand_oob_16;
#ifdef FORLINX_DEBUG
                                printf("*************nand->ecc.layout = &s3c_nand_oob_mlc_16 \n");
#endif
            }

#endif
            nand_type = S3C_NAND_TYPE_SLC;
            nand->ecc.size = 512;
            nand->ecc.bytes    = 4;
            
                        if ((1024 << (tmp & 0x3)) > 512)
                        {
                nand->ecc.read_page = s3c_nand_read_page_1bit;
                nand->ecc.write_page = s3c_nand_write_page_1bit;
                nand->ecc.read_oob = s3c_nand_read_oob_1bit;
                nand->ecc.write_oob = s3c_nand_write_oob_1bit;
                nand->ecc.layout = &s3c_nand_oob_64;
#ifdef FORLINX_DEBUG
                                printk("Nandflash:Type=SLC  ChipName:samsung-K9F2G08U0B or hynix-HY27UF082G2B \n");
#endif

                        } else
                        {
                nand->ecc.layout = &s3c_nand_oob_16;
            }

                } else
                {
            nand_type = S3C_NAND_TYPE_MLC;
            nand->options |= NAND_NO_SUBPAGE_WRITE;    /* NOP = 1 if MLC */
            nand->ecc.read_page = s3c_nand_read_page_4bit;
            nand->ecc.write_page = s3c_nand_write_page_4bit;
            nand->ecc.size = 512;
            nand->ecc.bytes = 8;    /* really 7 bytes */
            nand->ecc.layout = &s3c_nand_oob_mlc_64;

                        printf("Nandflash:ChipType=MLC  ChipName=samsung-K9G8G08U0A \n");



        }
        } else
        {

        nand_type = S3C_NAND_TYPE_MLC;
        nand->options |= NAND_NO_SUBPAGE_WRITE;    /* NOP = 1 if MLC */
        nand->ecc.read_page = s3c_nand_read_page_4bit;
        nand->ecc.write_page = s3c_nand_write_page_4bit;
        nand->ecc.size = 512;
        nand->ecc.bytes = 8;    /* really 7 bytes */
        nand->ecc.layout = &s3c_nand_oob_mlc_64;

               int childType=tmp & 0x03; //Page size

              //  printf("dev_id=%x,childType=%x \n",dev_id,childType);

                if(dev_id == 0xd5 && childType==0x01)
        {
                        //K9GAG08U0D     size=2GB  type=MLC  Page=4K
#ifdef FORLINX_DEBUG
                        printf("Nandflash:ChipType= MLC  ChipName=samsung-K9GAG08U0D \n");

#endif         
                        // dg add #if 2011-11-09
        #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
            nand->ecc.read_page = s3c_nand_read_page_8bit;
            nand->ecc.write_page = s3c_nand_write_page_8bit;
            nand->ecc.size = 512;
                        nand->ecc.bytes = 13;
                        nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;//这个芯片就用这个,和内核中的分布一样  
      #endif
        }

                else if(dev_id == 0xd5 && childType==0x02)
                {
                    //K9GAG08U0E     size=2GB  type=MLC  Page=8K
#ifdef FORLINX_DEBUG
                    printf("Nandflash:ChipType= MLC  ChipName=samsung-K9GAG08U0E \n");

#endif
                    // dg add #if 2012-03-30
    #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
                    nand->ecc.read_page = s3c_nand_read_page_8bit;
                    nand->ecc.write_page = s3c_nand_write_page_8bit;
                    nand->ecc.size = 512;
                    nand->ecc.bytes = 13;
                    nand->ecc.layout = &s3c_nand_oob_mlc_232_8bit;
    #endif


                }
        else
        {
#ifdef FORLINX_DEBUG
            printf("select s3c_nand_oob_mlc_64\n");
#endif
        }
    }
#else
    nand->ecc.mode = NAND_ECC_SOFT;
#endif