• «
  • 1
  • 2
  • 3
  • »
  • Pages: 1/3     Go
主题 : tiny210--U-Boot实现NAND的擦除(型号:K9GAG08U0F) 复制链接 | 浏览器收藏 | 打印
级别: 侠客
UID: 70046
精华: 1
发帖: 40
金钱: 250 两
威望: 50 点
贡献值: 1 点
综合积分: 100 分
注册时间: 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: 12573
精华: 27
发帖: 8881
金钱: 46490 两
威望: 9298 点
贡献值: 27 点
综合积分: 18302 分
注册时间: 2010-01-09
最后登录: 2019-07-16
1楼  发表于: 2013-03-12 08:26
支持,多谢分享
新手如何向我们反馈有效的信息,以便解决问题,见此贴:
http://www.arm9home.net/read.php?tid-14431.html

[注]: 此处签名链接仅为指引方向,而非解答问题本身.
级别: 侠客
UID: 54383
精华: 0
发帖: 63
金钱: 315 两
威望: 63 点
贡献值: 0 点
综合积分: 126 分
注册时间: 2011-08-29
最后登录: 2016-09-14
2楼  发表于: 2013-03-12 09:35
K9GAG08U0F 的oobsize不是512吗,怎么是215呢?
级别: 侠客
UID: 70046
精华: 1
发帖: 40
金钱: 250 两
威望: 50 点
贡献值: 1 点
综合积分: 100 分
注册时间: 2012-05-15
最后登录: 2013-10-04
3楼  发表于: 2013-03-12 10:33
是512b,写错了,不好意思
级别: 侠客
UID: 54383
精华: 0
发帖: 63
金钱: 315 两
威望: 63 点
贡献值: 0 点
综合积分: 126 分
注册时间: 2011-08-29
最后登录: 2016-09-14
4楼  发表于: 2013-03-15 10:38
楼主,请问你的uboot基于K9GAG08U0F支持写功能吗,uboot能从nandflash启动吗?
因为K9GAG08U0F oobsize是512,用的是16bit ECC校准,想做K9GAG08U0F 驱动,
现在关于卡在 8Kb page size with 16_bit ECC Nand flash oob definition 如何定义,类似
static struct nand_ecclayout s3c_nand_oob_16 = {
    .eccbytes = 4,
    .eccpos = {
        1, 2, 3, 4
    },
    .oobfree = {
        {
        .offset = 6,
        .length = 10
        }
    }
};

/* Nand flash oob definition for SLC 2k page size */
static struct nand_ecclayout s3c_nand_oob_64 = {
    .eccbytes = 16,
    .eccpos = {
        40, 41, 42, 43, 44, 45, 46, 47,
        48, 49, 50, 51, 52, 53, 54, 55
    },
    .oobfree = {
        {
        .offset = 2,
        .length = 38
        }
    }
};

/* Nand flash oob definition for MLC 2k page size */
static struct nand_ecclayout s3c_nand_oob_mlc_64 = {
    .eccbytes = 32,
    .eccpos = {
        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
    },
    .oobfree = {
        {
        .offset = 2,
        .length = 28
        }
    }
};

/* Nand flash oob definition for 4Kb page size with 8_bit ECC */
static struct nand_ecclayout s3c_nand_oob_128 = {
    .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
        }
    }
};

不知道大家有什么建议,谢谢
级别: 侠客
UID: 70046
精华: 1
发帖: 40
金钱: 250 两
威望: 50 点
贡献值: 1 点
综合积分: 100 分
注册时间: 2012-05-15
最后登录: 2013-10-04
5楼  发表于: 2013-03-18 23:52

 回 4楼(cc999123) 的帖子

相信这几天就能搞定了。
级别: 侠客
UID: 54383
精华: 0
发帖: 63
金钱: 315 两
威望: 63 点
贡献值: 0 点
综合积分: 126 分
注册时间: 2011-08-29
最后登录: 2016-09-14
6楼  发表于: 2013-03-19 09:48

 回 5楼(2012shiyi) 的帖子

是吧,到时分享一下你的移植经验,不胜感激!
级别: 侠客
UID: 16060
精华: 0
发帖: 87
金钱: 435 两
威望: 87 点
贡献值: 0 点
综合积分: 174 分
注册时间: 2010-03-13
最后登录: 2018-02-08
7楼  发表于: 2013-03-22 10:11
不知道楼主用的哪个版本的uboot!
级别: 侠客
UID: 87780
精华: 0
发帖: 94
金钱: 470 两
威望: 94 点
贡献值: 0 点
综合积分: 188 分
注册时间: 2013-02-26
最后登录: 2016-07-27
8楼  发表于: 2013-04-01 16:26
妹的,搞了半天,我把NAND flash的芯片型号给搞错了,我之前下的哪个资料太啃了,用的是它以前的版本!
级别: 侠客
UID: 87780
精华: 0
发帖: 94
金钱: 470 两
威望: 94 点
贡献值: 0 点
综合积分: 188 分
注册时间: 2013-02-26
最后登录: 2016-07-27
9楼  发表于: 2013-04-01 20:10
 mtd->oobsize =512;

uboot启动以后出现:no oob scheme defined for oobsize
  • «
  • 1
  • 2
  • 3
  • »
  • Pages: 1/3     Go