管理提醒: 本帖被 xoom 执行加亮操作(2013-03-12)
(我是菜鸟,贡献点微薄之力)
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重新编辑 ]