tiny210--U-Boot实现NAND的擦除(型号:K9GAG08U0F)
(我是菜鸟,贡献点微薄之力) 1、NAND的初始化: #if defined(CONFIG_CMD_NAND) puts("NAND: "); nand_init(); /* go init the NAND */ #endif 调用函数: nand_init_chip(&nand_info, &nand_chip, base_address); 参数base_address=0xB0E000000。 函数功能: 设置nand_chip结构体: nand->IO_ADDR_R = 0xB0E000000; 还有调用board_nand_init函数,这个函数和芯片操作息息相关,也是设置nand_chip结构体,设置初始化好了NAND。 现在看看核心代码: 先获取NAND的ID号的1st Cycle 和2nd Cycle, 然后通过nand_flash_ids[]数组来匹配,找出我们芯片对应的成员: for (i = 0; nand_flash_ids.name != NULL; i++) { if (tmp == nand_flash_ids.id) { type = &nand_flash_ids; break; } } 找出我们芯片对应的成员: {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, nand_flash_ids[]结构体原型: struct nand_flash_dev { char *name; int id; unsigned long pagesize; unsigned long chipsize; unsigned long erasesize; unsigned long options; }; 再获取NAND的ID号的3st Cycle 和4nd Cycle,这次的数据主要是体现该芯片的内部信息,如页大小,有多少块等,然后用这些值来设置nand->ecc结构体: 这里说明一下芯片内部一个信息: Cell Type:SLC / MLC bit2&bit3表示的是芯片的类型,是SLC还是某种MLC: Bit2,bit3=0x00 : SLC,简单说就是内部单个存储单元,存储一位的数据,所能表示的数值只有0,1,也就需要两种不同的电压来表示,所以叫做2 Level的Cell。 Bit2,bit3=0x01/0x10/0x11 : 4 /8/16 Level Cell,都叫做MLC,其含义是内部单个存储单元设计成可以表示多个,即4/8/16个不同的电压,对应地,可以表示2,3,4位的数据。 这类的MLC的nand flash,由于单个存储单元,要存储更多的数据,所以内部结构更复杂,读取和写入数据的逻辑更复杂,相对数据出错的几率也比SLC要大。 所以,一般MLC的使用,都需要检错和纠错能力更强的硬件或软件算法,以保证数据的正确性。 软件实现此类的多位数据的检错和纠错的效率相对较低,一般是硬件本身就已经提供此功能。 对应的其为硬件ECC,也就是Linux内核MTD中的HW_ECC。 遇到的问题:读取设备ID为0,说明我们的NNAD读操作有误,经过检测得到解决办法: 在读操作之前没有先复位NAND,导致操作NAND出错,在NAND操作前添加代码: /*Reset*/ 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_NONE, NAND_NCE | NAND_CTRL_CHANGE); /*******/ (后来发现,这部分可以不加,因为后面代码还会在读一次,那次就有复位) 这样打印的设备ID就是NAND: ID=d5 由于六次ID数据包含了该芯片的很多信息,所以打印出来: 1st Cycle(Maker Code)=ecH, 2st Cycle(Device Code)=d5H, 3st Cycle(cellinfo)=94H, 4st Cycle(Page Size, Block Size,Redundant Area Size)=76H, 5st Cycle(Plane Number, ECC Level, Organization.)=54H, 6st Cycle(Device Technology, EDO, Interface.)=43H, 我们的芯片是MLC会调用下面代码: 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; 到了函数nand_scan(struct mtd_info *mtd, int maxchips) Mtd:nand_info; Maxchips:1 函数核心代码: nand_set_defaults(chip, busw); //设置NAND的操作函数集 type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table); // 读取NAND的型号,并且最终根据{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},来设置nand_chip结构体。 Uboot已经定义好了NAND命令: U_BOOT_CMD( nand, CONFIG_SYS_MAXARGS, 1, do_nand, "NAND sub-system", "info - show available NAND devices\n" "nand device [dev] - show or set current device\n" "nand read - addr off|partition size\n" "nand write - addr off|partition size\n" " read/write 'size' bytes starting at offset 'off'\n" " to/from memory address 'addr', skipping bad blocks.\n" ...... ) 所以nand的操作都是从do_nand函数开始的: 启动结果: NAND: NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit) NAND bus width 8 instead 16 bit 随便测试一下: 210 # nand write.e no devices available 解决: 屏蔽nand_base函数里: #if 0 if (IS_ERR(type)) { #ifndef CONFIG_SYS_NAND_QUIET_TEST printk(KERN_WARNING "No NAND device found!!!\n"); #endif chip->select_chip(mtd, -1); return PTR_ERR(type); } #endif 到此NAND的初始化就结束了。现在不如NAND的三大重头戏: 1、nand的擦除函数的实现: 核心代码: if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { nand_erase_options_t opts;//核心结构体 struct nand_chip *chip = meminfo->priv; //取出初始化设置好的nand_chip; int ret = meminfo->block_isbad(meminfo, erase.addr); //检测坏块 result = meminfo->erase(meminfo, &erase); //擦除函数 现在看看 meminfo->erase(meminfo, &erase),就是函数nand_erase(struct mtd_info *mtd, struct erase_info *instr)。 参数:mtd结构体,这个负责调用nand_chip的擦除函数,擦除大小信息放在instr结构体。 根据下面的问题: 问题: 210 # nand erase 0 100000 nand_curr_device=0 NAND erase: device 0 offset 0x0, size 0x100000 Skipping bad block at 0x00000000 BUG: failure at nand_base.c:187/nand_select_chip()! 经过打印知道问题所在: 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); } 由 于我们执行这个函数,进入if语句了,这样就会返回,导致后面的chip->page_shift = ffs(mtd->writesize) - 1;没有执行,这样chip->page_shift = 0,这问题就大了,在检测坏块的时候nand_block_bad函数: chipnr = (int)(ofs >> chip->chip_shift); chip->select_chip(mtd, chipnr); 而select_chip: static void nand_select_chip(struct mtd_info *mtd, int chipnr) { struct nand_chip *chip = mtd->priv; switch (chipnr) { case -1: chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); break; case 0: break; default: BUG(); } } 这明显就出错。 在if (busw != (chip->options & NAND_BUSWIDTH_16)) 就返回了,导致后面的chip->page_shift没有操作,结果为0。这样, chipnr = (int)(ofs >> chip->chip_shift)得到的chipnr就有问题,nand_select_chip函数的switch函数就会跑到default:BUG(); 所以屏蔽这部分: #if 0 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); } #endif 函数nand_flash_detect_non_onfi是个很重要的函数,我们的芯片重要信息设置(修改的重点): if (!type->pagesize) { int extid; chip->cellinfo = chip->read_byte(mtd); extid = chip->read_byte(mtd); //mtd->writesize = 1024 << (extid & 0x3); 我们的NAND一页是8k,所以修改: mtd->writesize = 2048<< (extid & 0x3); extid >>= 2; //mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); 我们的NAND每一页obb为512B,改为: mtd->oobsize =512; extid >>= 2; //mtd->erasesize = (64 * 1024) << (extid & 0x03); 问题: 我们的NAND是以128K为一倍的,所以: mtd->erasesize = (128 * 1024) << (extid & 0x03); extid >>= 2; *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; } 启动结果(少了一句): NAND: NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit) 操作结果: #nand erase 0 10000000 NAND erase: device 0 offset 0x0, size 0x10000000 Skipping bad block at 0x00800000 Skipping bad block at 0x00880000 Erasing at 0xff80000 -- 100% complete. OK 擦除函数到这里就解决。 [ 此帖被2012shiyi在2013-03-12 10:41重新编辑 ] |
|