uboot中nand flash代码分析(1)

本文主要分析uboot中nand flash初始化部分的代码,主要是driver/mtd/nand/nand.c中的nand_init()函数。nand flash bbt建立部分的代码后面再分析。

(一)nand flash 相关的参数

#define CONFIG_CMD_NAND 1
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0xB0000000
#define CONFIG_SYS_NAND_ALE_ADDR (1 << 10)
#define CONFIG_SYS_NAND_CLE_ADDR (1 << 12)
#define CONFIG_SYS_NO_FLASH 1
#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_ENV_OFFSET 0x40000
#define CONFIG_ENV_OFFSET_REDUND 0x60000
#define CONFIG_ENV_OVERWRITE 1
#define NAND_MAX_CHIPS 1
#define CONFIG_SYS_NAND_RB_BIT 18
#define CONFIG_SYS_NAND_RB_PORT 0xC2104200
(二)初始化
初始化流程如下:
start_armboot
    -->nand_init                driver/mtd/nand/nand.c
        -->nand_init_chip       driver/mtd/nand/nand.c
            -->board_nand_init  (和具体开发板有关,board/xxx/yyy/nand.c)

            -->nand_scan         driver/mtd/nand/nand_base.c

一、start_armboot()和nand_init()

void start_armboot (void)  
{  
    ...  
    #if defined(CONFIG_CMD_NAND)  
        puts ("NAND:  ")//注释1
        nand_init(); /* go init the NAND */  
    #endif  
   ...  
}  
void nand_init(void)  
{  
    int i;  
    unsigned int size = 0;  
    for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { //注释2
        nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);//注释 3 4 5  
        size += nand_info[i].size / 1024;  //注释6
        if (nand_curr_device == -1)  //注释7
            nand_curr_device = i;  
    }  
    printf("%u MiB\n", size / 1024);//注释1
} 

1.上述两行打印语句,会在串口上打印 NAND: XXX MiB 字样,如 NAND: 128 MiB

2. CONFIG_SYS_MAX_NAND_DEVICE表示系统有几块nand设备,一般只有1块。

    该宏定义会影响下面数组的大小:

    nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
    static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
    static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE];

3.有两个主要的数据结构struct mtd_info和 struct nand_chip。里面存放着nand flash

  的sizeerasesizewritesizeoobsizeecc布局等属性,和操作nand flash所需

  的readwriteerase等函数。nand_chip中变量的含义的详细注释在

    Include/linux/mtd/nand.h中。初始化的目的就是初始化这两个数据结构。

4. Nand_infomtd_info同义,typedef struct mtd_info nand_info_t;

5. base_addressnand flash的基地址,和硬件有关。可以通过定义

    CONFIG_SYS_NAND_BASE_LIST或者 CONFIG_SYS_NAND_BASE

  来设置 base_address的值。

6.nand_info.size是整个MTD的大小(Total size of the MTD),单位是Byte

  当系统只有一个nand flash作为静态存储器时,该变量等于nand flash的总大小。

7.nand_curr_device表示正在使用的nand flash在数组nand_info[]中的位置。

   用户想要显示当前nand flash的信息时,可以在uboot命令行输入

#nand device

    Uboot就会调用 nand_print_info(nand_curr_device); 函数来显示当前nand flash的信息。

二、nand_init_chip()

static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
   ulong base_addr)
{
	int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
	int __attribute__((unused)) i = 0;
 
	if (maxchips < 1)
		maxchips = 1;
	//建立两个重要数据结构之间的关系。mtd_info的priv指针指向nand_chip	
	mtd->priv = nand; 
	 
	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;//设置读写地址
	//初始化与硬件相关的函数,如片选,cmd_ctrl等函数
	if (board_nand_init(nand) == 0) {
		//利用片选,cmd_ctrl等函数,读取nand flash的信息,并存放在mtd_info和nand_chip数据结构中。
     if (nand_scan(mtd, maxchips) == 0) {
          if (!mtd->name) //nand_scan会设置该变量,若没设置,就用默认值
              mtd->name = (char *)default_nand_name;
#ifndef CONFIG_RELOC_FIXUP_WORKS
          else
              mtd->name += gd->reloc_off;
#endif
 
#ifdef CONFIG_MTD_DEVICE
	   /*
	   * Add MTD device so that we can reference it later
	   * via the mtdcore infrastructure (e.g. ubi).
	   */
			   
	  //若定义 CONFIG_MTD_DEVICE,则强制修改mtd->name为”nandx”
	  sprintf(dev_name[i], "nand%d", i);
	  mtd->name = dev_name[i++];
	  add_mtd_device(mtd);//在数组mtd_table中查找一个空闲位置,并占用之。
#endif
     } else
     mtd->name = NULL;
  } else {
    mtd->name = NULL;
    mtd->size = 0;
  }
}
三、board_nand_init()

    board_nand_init()函数主要初始化与硬件相关的函数,如片选,cmd_ctrl等函数。

    当我们移植uboot nand flash代码时,主要工作就是修改或者重写该函数。

   下面代码中的”yyy”,一般是指板子型号或者具体nand flash的型号。

int board_nand_init(struct nand_chip *nand)
{
    nand->cmd_ctrl = yyy_nand_hwcontrol;// 注释1
    nand->dev_ready = yyy_nand_readybusy;// 注释2
    nand->select_chip = yyy_nand_select_chip;
    nand->chip_delay = 100; // 注释3
    nand->ecc.mode = NAND_ECC_SOFT;  // 注释4
    nand->options = NAND_USE_FLASH_BBT | NAND_NO_READRDY;// 注释5
}

1.当向 nand flash 发送命令或数据时,需要拉高 CLE 或者 ALE ,该动作

  就是由nand->cmd_ctrl函数来实现。

    本例中,yyy_nand_hwcontrol()源码大致如下:

static void yyy_nand_hwcontrol(struct mtd_info *mtd, int dat,unsigned int ctrl)
{
    struct nand_chip *chip = mtd->priv;
    void *IO_ADDR_W = chip->IO_ADDR_W;
    if (ctrl & NAND_CLE) // flash的CLE引脚接在CPU的ADDR21引脚
        IO_ADDR_W += CONFIG_SYS_NAND_CLE_ADDR;
 
    if (ctrl & NAND_ALE) // flash的ALE引脚接在CPU的ADDR24引脚
        IO_ADDR_W += CONFIG_SYS_NAND_ALE_ADDR;
 
    if (dat != NAND_CMD_NONE) {
        if (chip->options & NAND_BUSWIDTH_16)//flash数据线宽度是8 还是16
            writew((unsigned short)dat, IO_ADDR_W);
        else
            writeb((unsigned char)dat, IO_ADDR_W);
    }
}

      如发送 reset 指令,需调用chip->cmd_ctrl(mtd,NAND_CMD_RESET, NAND_CLE);

      因为ctrl 等于NAND_CLE,所以NAND_CMD_RESET会发到

      (IO_ADDR_W + CONFIG_SYS_NAND_CLE_ADDR)地址上,即ADDR21会出现高电平,

   刚好满足发送命令时,CLE引脚是高电平的条件。

2.该函数可以为空,即不检测nand flashR/B引脚。而通过读取状态寄存器

  来判断flash是否空闲,或者不检测flash是否空闲,而仅仅等待一段时间。

3.等待时间,需要参考flash的数据手册,不能太小,否则会出错。

  有次设置太小,导致读取环境变量和内核时总是提示ecc错误。

4.计算ECC的方式,很重要

5.设置BBT存放方式,读操作时是否检测R/B状态等,很重要。

四、Nand_scan()

  Nand_scan()函数分为两部分。

int nand_scan(struct mtd_info *mtd, int maxchips)
{
    int ret;
    ret = nand_scan_ident(mtd, maxchips);
    if (!ret)
       ret = nand_scan_tail(mtd);
    return ret;
}


五、nand_scan_ident()

  首先将mtd_info和 nand_chip中还没初始化的函数,初始化为默认函数,

    然后读取flashID,获取flash的信息,并计算出flash的总容量,块大小等信息,

  并根据这些信息将mtd_info和 nand_chip设置为合适的值。

int nand_scan_ident(struct mtd_info *mtd, int maxchips)
{
    int i, busw, nand_maf_id;
    struct nand_chip *chip = mtd->priv;
    struct nand_flash_dev *type;
 
    /* Get buswidth to select the correct functions */
    busw = chip->options & NAND_BUSWIDTH_16;//判断flash数据总线的宽度
    /* Set the default functions */
    nand_set_defaults(chip, busw);见六
 
    /* Read the flash type */
    type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);见七
 
    if (IS_ERR(type)) {
        chip->select_chip(mtd, -1);
        return PTR_ERR(type);
    }
 
    /* Check for a chip array */
    for (i = 1; i < maxchips; i++) {
        chip->select_chip(mtd, i);
        /* See comment in nand_get_flash_type for reset */
        chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
        /* Send the command for reading device ID */
        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
        /* Read manufacturer and device IDs */
        if (nand_maf_id != chip->read_byte(mtd) ||
            type->id != chip->read_byte(mtd))
            break;
        }
    /* Store the number of chips and calc total size for mtd */
    chip->numchips = i; 
    mtd->size = i * chip->chipsize;
    //只有一块nand flash时,i等于1,mtd总大小就等于flash的总容量。
 
    return 0;
}

六、nand_set_defaults()

    将nand_chip中还没初始化的函数初始化为默认值。

static void nand_set_defaults(struct nand_chip *chip, int busw)
{
    /* check for proper chip_delay setup, set 20us if not */
    if (!chip->chip_delay)
        chip->chip_delay = 20;
 
    /* check, if a user supplied command function given */
    //仔细研究下nand_command,会有助于理解chip->chip_delay和chip->dev_ready函数的
    //含义,如果是大页flash,nand_get_flash_type()会将chip->cmdfunc 修改为 
    //nand_command_lp()
    if (chip->cmdfunc == NULL)
        chip->cmdfunc = nand_command;
 
    /* check, if a user supplied wait function given */
    if (chip->waitfunc == NULL)
        chip->waitfunc = nand_wait;
 
    if (!chip->select_chip)
        chip->select_chip = nand_select_chip;
    if (!chip->read_byte)
        chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
    if (!chip->read_word)
        chip->read_word = nand_read_word;
    if (!chip->block_bad)
        chip->block_bad = nand_block_bad;
    if (!chip->block_markbad)
        chip->block_markbad = nand_default_block_markbad;
    if (!chip->write_buf)
        chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
    if (!chip->read_buf)
        chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
    if (!chip->verify_buf)
        chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
    if (!chip->scan_bbt)
        chip->scan_bbt = nand_default_bbt;
 
    if (!chip->controller) {
        chip->controller = &chip->hwcontrol;
    }
}
 
七、nand_get_flash_type()

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  struct nand_chip *chip,
  int busw, int *maf_id)
{
	struct nand_flash_dev *type = NULL;
	int i, dev_id, maf_idx;
	int tmp_id, tmp_manf;
	
	/* Select the device */
	chip->select_chip(mtd, 0);
 
	/*
	* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
	* after power-up
	*/
	//调用nand_command(),reset flash。
	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
 
	/* Send the command for reading device ID */
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//读取 flash ID
	
	/* Read manufacturer and device IDs */
	*maf_id = chip->read_byte(mtd);
	dev_id = chip->read_byte(mtd);
	
	/* Try again to make sure, as some systems the bus-hold or other
	 * interface concerns can cause random data which looks like a
	 * possibly credible NAND flash to appear. If the two results do
	 * not match, ignore the device completely.
	 */
	//再次读取 flash ID
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 
	/* Read manufacturer and device IDs */
	 
	tmp_manf = chip->read_byte(mtd);
	tmp_id = chip->read_byte(mtd);
 
	//比较两次所读的 flash ID是否一致
	if (tmp_manf != *maf_id || tmp_id != dev_id) {
		printk(KERN_INFO "%s: second ID read did not match "
	       "%02x,%02x against %02x,%02x\n", __func__,
	       *maf_id, dev_id, tmp_manf, tmp_id);
		return ERR_PTR(-ENODEV);
	}
 
	/*nand_flash_ids[]保存着uboot所支持的nand flash的
	*name,id,pagesize,chipsize,erasesize,options 等信息。
	*下面的for循环,从该数组中查找和自身ID相等的元素
	*/
	/* Lookup the flash id */
	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
		if (dev_id == nand_flash_ids[i].id) {
			type =  &nand_flash_ids[i];
			break;
		}
	}
 
	if (!type) {
		/* supress warning if there is no nand */
		if (*maf_id != 0x00 && *maf_id != 0xff &&
		    dev_id  != 0x00 && dev_id  != 0xff)
			printk(KERN_INFO "%s: unknown NAND device: "
			"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
			__func__, *maf_id, dev_id);
			return ERR_PTR(-ENODEV);
	}
	//找到了,如“ {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},”

	//设置mtd->name,该变量有可能被强制修改为nandx
 
	if (!mtd->name)
		mtd->name = type->name;
	
	//根据数组中的参数,设置flash的总容量,128M
	chip->chipsize = (uint64_t)type->chipsize << 20;
	
	//若pagesize为0,表示需要继续读取ID,从ID中计算其他参数,而不是从 
	//nand_flash_ids[]中获取。
	/* Newer devices have all the information in additional id bytes */
	if (!type->pagesize) {
		int extid;
		/* The 3rd id byte holds MLC / multichip data */
		chip->cellinfo = chip->read_byte(mtd);
		/* The 4th id byte is the important one */
		
		// 4th id byte的含义见图一,本例用的nand flash是K9F1G08Q0A,读取到的是0x15,
		extid = chip->read_byte(mtd); 
		
		/* Calc pagesize */
		//bit0,bit1为0,pagesize就是1024<<1=1024;为1,pagesize就是1024<<1=2048
		//writesize等于pagesize,因为每次写都是以页为单位。2048
		mtd->writesize = 1024 << (extid & 0x3);
		extid >>= 2;
		
		/* Calc oobsize */
		//writesize>>9算出几个512bytes,每512bytes对应8个还是16个oob区是由
		//(8 << (extid & 0x01))算得,两者相乘就得oob的总大小,16*4=64
		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
		extid >>= 2;
		
		/* Calc blocksize. Blocksize is multiples of 64KiB */
		//块大小或者擦除大小 64*1024*2=128KB
		mtd->erasesize = (64 * 1024) << (extid & 0x03);
		extid >>= 2;
		
		/* Get buswidth information */
		//总线宽度为8
		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
	} else {
		/*
		 * Old devices have chip data hardcoded in the device id table
		 */
		mtd->erasesize = type->erasesize;
		mtd->writesize = type->pagesize;
		mtd->oobsize = mtd->writesize / 32;
		busw = type->options & NAND_BUSWIDTH_16;
	}
 
	/* Try to identify manufacturer */
	//根据制造商ID,获取制造商名称,0xec对应Samsung
	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
		if (nand_manuf_ids[maf_idx].id == *maf_id)
		break;
	}
 
	/*
	 * Check, if buswidth is correct. Hardware drivers should set
	 * chip correct !
	 */
	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
		printk(KERN_INFO "NAND device: Manufacturer ID:"
	       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
	       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
	       busw ? 16 : 8);
		return ERR_PTR(-EINVAL);
	}
 
	/* Calculate the address shift from the page size */
	//给出一个地址,利用该变量可以直接得出 该地址是第几页。
	//如 realpage = (int)(address>> chip->page_shift);
	chip->page_shift = ffs(mtd->writesize) - 1;

	/* Convert chipsize to number of pages per chip -1. */
	//chip->pagemask是总页数减1,用来防止要读写的页号不超过总页数
	// page = realpage & chip->pagemask;
	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
 
	//给出一个地址,利用该变量可以直接得出 该地址是第几块。
	chip->bbt_erase_shift = chip->phys_erase_shift =
	ffs(mtd->erasesize) - 1;
 
	//给出一个地址,利用该变量可以直接得出 该地址是第几个flash设备。
	if (chip->chipsize & 0xffffffff)
		chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
	else
		chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31;
 
	/* Set the bad block position */
	//坏块位置,后面讲到ECC时会用到
	chip->badblockpos = mtd->writesize > 512 ?
		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
	//设置chip->options
	/* Get chip options, preserve non chip based options */
	chip->options &= ~NAND_CHIPOPTIONS_MSK;
	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
 
	/*
	 * Set chip as a default. Board drivers can override it, if necessary
	 */
	chip->options |= NAND_NO_AUTOINCR;
 
	/* Check if chip is a not a samsung device. Do not clear the
	 * options for chips which are not having an extended id.
	 */
	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
 
	//修改默认的函数为合适的函数
	/* Check for AND chips with 4 page planes */
	if (chip->options & NAND_4PAGE_ARRAY)
		chip->erase_cmd = multi_erase_cmd;
	else
		chip->erase_cmd = single_erase_cmd;
	//如果是大页flash,则修改chip->cmdfunc 为 nand_command_lp()
	/* Do not replace user supplied command function ! */
	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
		chip->cmdfunc = nand_command_lp;
 
	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
	          nand_manuf_ids[maf_idx].name, type->name);
	 
	return type;
}

                                                                       uboot中nand flash代码分析(1)_第1张图片

                                                                                                                                     图一

 七、nand_scan_tail()

      nand_scan_tail()继续初始化mtd_infonand_info数据结构,主要初始化和ECC有关的变量和函数。

int nand_scan_tail(struct mtd_info *mtd)
{
	int i;
	struct nand_chip *chip = mtd->priv;
	 
	//分配buffers,用于临时存放从flash读取的数据,见nand_do_read_ops()函数
	if (!(chip->options & NAND_OWN_BUFFERS))
	chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
	if (!chip->buffers)
	return -ENOMEM;
	 
	//oob紧跟着数据存放
	/* Set the internal oob buffer location, just after the page data */
	chip->oob_poi = chip->buffers->databuf + mtd->writesize;
 
	/*
	 * If no default placement scheme is given, select an appropriate one
	 */
	if (!chip->ecc.layout) {
		switch (mtd->oobsize) {
			case 8:
				chip->ecc.layout = &nand_oob_8;
				break;
			case 16:
				chip->ecc.layout = &nand_oob_16;
				break;
			case 64:
				chip->ecc.layout = &nand_oob_64;//注释1
				break;
			case 128:
				chip->ecc.layout = &nand_oob_128;
				break;
			default:
				printk(KERN_WARNING "No oob scheme defined for "
				       "oobsize %d\n", mtd->oobsize);
		}
	}
	//初始化和ECC有关的函数
	if (!chip->write_page)
		chip->write_page = nand_write_page;
	 
	/*
	 * check ECC mode, default to software if 3byte/512byte hardware ECC is
	 * selected and we have 256 byte pagesize fallback to software ECC
	 */
	 
	switch (chip->ecc.mode) {
		case NAND_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 not possible\n");
				BUG();
			}
			if (!chip->ecc.read_page)
				chip->ecc.read_page = nand_read_page_hwecc_oob_first;
	 
		case NAND_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;
		 
		case NAND_ECC_HW_SYNDROME:
			if ((!chip->ecc.calculate || !chip->ecc.correct ||
			     !chip->ecc.hwctl) &&
			    (!chip->ecc.read_page ||
			     chip->ecc.read_page == nand_read_page_hwecc ||
			     !chip->ecc.write_page ||
			     chip->ecc.write_page == nand_write_page_hwecc)) {
				printk(KERN_WARNING "No ECC functions supplied, "
			       "Hardware ECC not possible\n");
				BUG();
			}
			/* Use standard syndrome read/write page function ? */
			if (!chip->ecc.read_page)
				chip->ecc.read_page = nand_read_page_syndrome;
			if (!chip->ecc.write_page)
				chip->ecc.write_page = nand_write_page_syndrome;
			if (!chip->ecc.read_page_raw)
				chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
			if (!chip->ecc.write_page_raw)
				chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
			if (!chip->ecc.read_oob)
				chip->ecc.read_oob = nand_read_oob_syndrome;
			if (!chip->ecc.write_oob)
				chip->ecc.write_oob = nand_write_oob_syndrome;
			 
			if (mtd->writesize >= chip->ecc.size)
				break;
			printk(KERN_WARNING "%d byte HW ECC not possible on "
		       "%d byte page size, fallback to SW ECC\n",
		       chip->ecc.size, mtd->writesize);
			chip->ecc.mode = NAND_ECC_SOFT;
	 
		case NAND_ECC_SOFT:
			chip->ecc.calculate = nand_calculate_ecc;
			chip->ecc.correct = nand_correct_data;
			chip->ecc.read_page = nand_read_page_swecc;
			chip->ecc.read_subpage = nand_read_subpage;
			chip->ecc.write_page = nand_write_page_swecc;
			chip->ecc.read_page_raw = nand_read_page_raw;
			chip->ecc.write_page_raw = nand_write_page_raw;
			chip->ecc.read_oob = nand_read_oob_std;
			chip->ecc.write_oob = nand_write_oob_std;
			chip->ecc.size = 256;//每256bit 产生3bit的ECC
			chip->ecc.bytes = 3;
			break;
	 
		case NAND_ECC_NONE:
			printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
			       "This is not recommended !!\n");
			chip->ecc.read_page = nand_read_page_raw;
			chip->ecc.write_page = nand_write_page_raw;
			chip->ecc.read_oob = nand_read_oob_std;
			chip->ecc.read_page_raw = nand_read_page_raw;
			chip->ecc.write_page_raw = nand_write_page_raw;
			chip->ecc.write_oob = nand_write_oob_std;
			chip->ecc.size = mtd->writesize;
			chip->ecc.bytes = 0;
			break;
	 
		default:
			printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
			       chip->ecc.mode);
			BUG();
	}
 
	/*
	 * The number of bytes available for a client to place data into
	 * the out of band area
	 */
	//可用的oob大小,即nand_oob_64中所设置的>oobfree的总和,本例是38bit
	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 one page 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;
 
	/*
	 * Allow subpage writes up to ecc.steps. Not possible for MLC
	 * FLASH.
	 */
	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
		switch(chip->ecc.steps) {
			case 2:
				mtd->subpage_sft = 1;
				break;
			case 4:
			case 8:
			case 16:
				mtd->subpage_sft = 2;
				break;
		}
	}
	//计算子页大小,即每次读flash时,最小需要读取的数据。为了检测数据是否有误,需
	//要至少读取一个单位的ECC对应的数据大小。
	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
	 
	/* Initialize state */
	chip->state = FL_READY;
	 
	/* De-select the device */
	chip->select_chip(mtd, -1);
	 
	//该变量应该是表示chip->buffers中的数据是第几页的数据。
	//读flash之前,需要先判断所读的页的数据是否已在chip->buffers中,是则,直接从  
	//buffers中读取
	/* Invalidate the pagebuffer reference */
	chip->pagebuf = -1;
	 
	//设置默认函数和变量
	/* 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;
	 
	//如果定义NAND_SKIP_BBTSCAN,则设置BBT“已扫描过”。以后会跳过BBT扫描,
	/* Check, if we should skip the bad block table scan */
	if (chip->options & NAND_SKIP_BBTSCAN)
		chip->options |= NAND_BBT_SCANNED;
	 
	return 0;
}
 


1.本例oobsize等于64,从下面结构可以看出oob区的结构

static struct nand_ecclayout nand_oob_64 = {
    .eccbytes = 24,
    .eccpos = {
       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 = 38}}
       };

   前两位有其他作用,从第2位开始,到第39位,一共38位是空闲的。从40位到63位共24位存放ECC数据,每256bytes对应3bytesECC,一页2048bytes,共24bytesECC

 




你可能感兴趣的:(uboot)