主题 : tiny210--U-Boot实现NAND的擦除(型号:K9GAG08U0F) 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 70046
精华: 1
发帖: 39
金钱: 250 两
威望: 50 点
贡献值: 1 点
综合积分: 98 分
注册时间: 2012-05-15
最后登录: 2013-10-04
楼主  发表于: 2013-03-11 16:14

 tiny210--U-Boot实现NAND的擦除(型号:K9GAG08U0F)

管理提醒: 本帖被 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重新编辑 ]
级别: 新手上路
UID: 70046
精华: 1
发帖: 39
金钱: 250 两
威望: 50 点
贡献值: 1 点
综合积分: 98 分
注册时间: 2012-05-15
最后登录: 2013-10-04
1楼  发表于: 2013-03-12 10:33
是512b,写错了,不好意思
级别: 新手上路
UID: 70046
精华: 1
发帖: 39
金钱: 250 两
威望: 50 点
贡献值: 1 点
综合积分: 98 分
注册时间: 2012-05-15
最后登录: 2013-10-04
2楼  发表于: 2013-03-18 23:52

 回 4楼(cc999123) 的帖子

相信这几天就能搞定了。