加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

nandflash驱动分析 针对K9GAG08U0D uboot1.1.6(上)

发布时间:2020-12-15 07:12:03 所属栏目:百科 来源:网络整理
导读:先初步了解下这块nand芯片: 解释:这张是该芯片结构图,4096个块,1块128页,1页4KB+218B; 片内寻址-5个周期 分列地址和行地址,自己看图 这块和另一块芯片K9GAG08U0E差不多,驱动方面几近相同,只是后者每页是8KB+436B 这里先讲一点 .至于4KB+218B中哪些

先初步了解下这块nand芯片:

解释:这张是该芯片结构图,4096个块,1块128页,1页4KB+218B;

片内寻址-5个周期 分列地址和行地址,自己看图

这块和另一块芯片K9GAG08U0E差不多,驱动方面几近相同,只是后者每页是8KB+436B



这里先讲一点

.至于4KB+218B中哪些是数据区 哪些是额外的,这得看ecc模块的设计者,从flash角度来看,这4K+218字节是完全等同的。而ecc模块早就被生产厂商做死,无需自行设计。


现在开始分析了。

一.s3c6410支持从SD卡启动,从SD卡启动后就可以把Uboot.bin文件写入NandFlash了,可以认真的看一下Uboot 里面 的 nand write.uboot 命令是如何把数据写入NandFlash前4页 的。写入成功后就可以从NandFlash启动Uboot。

? ? ? ?nand write.uboot 的关键代码 位于uboot1.1.6/common/cmd_nand.c文件中:


else if (!read && s != NULL && (!strcmp(s,".uboot")) && nand->writesize == 4096) {
?? ??? ??? ?size=4096;
?? ??? ??? ?nand_write(nand,off,&size,(u_char *)addr);
?? ??? ??? ?off+=4096;
?? ??? ??? ?addr+=2048;
?? ??? ??? ?nand_write(nand,(u_char *)addr);
?? ??? ??? ?off+=4096;
?? ??? ??? ?addr+=2048;

//写剩余的页面,这里的Uboot占用1M的NandFlash地址空间,在U0D里面占用256个页面(256Page=1M),上面已经写了四个页面了,这里写剩余的252个页,252个页面足够存放Uboot的有效数据了。???????????????????????

??????????? size=1024*1024-4*4096;

???????????? ret = nand_write(nand,(u_char *)addr);



??????????????? }

//6410支持4K每页 只不过在nand启动时默认把前8K程序当作2K每页存取,故在nand上前16K(4个页)实际上是以每页2K存取8K的程序,其余都是4K每页存取的,上述代码就是这个意思。


二.uboot1.1.6/cpu/s3c64xx/nand_cp.c 文件



#include <common.h>

#ifdef CONFIG_S3C64XX
#include <asm/io.h>
#include <linux/mtd/nand.h>
#include <regs.h>

/*
?* address format地址格式
?*????????????? 17 16???????? 9 8??????????? 0
?* --------------------------------------------
?* | block(12bit) | page(5bit) | offset(9bit) |
?* --------------------------------------------
?*/

static int nandll_read_page (uchar *buf,ulong addr,int large_block)
{
??????? int i;
?? ?int page_size = 512;

?? ?if (large_block==1)
?? ??? ?page_size = 2048;
?? ?if (large_block==2)
??????????????? page_size = 4096;//这个类型2
??????? if(large_block==3)
??????????????? page_size = 8192;

??????? NAND_ENABLE_CE();

??????? NFCMD_REG = NAND_CMD_READ0;

??????? /* Write Address */5 个寻址周期,参看第一张图
??????? NFADDR_REG = 0;

??????? if (large_block)
?? ???????? NFADDR_REG = 0;

?? ?NFADDR_REG = (addr) & 0xff;
?? ?NFADDR_REG = (addr >> 8) & 0xff;
?? ?NFADDR_REG = (addr >> 16) & 0xff;

?? ?if (large_block)
?? ??? ?NFCMD_REG = NAND_CMD_READSTART;

??????? NF_TRANSRnB();

?? ?/* for compatibility(2460). u32 cannot be used. by scsuh */
?? ?for(i=0; i < page_size; i++) {
??????????????? *buf++ = NFDATA8_REG;
??????? }

??????? NAND_DISABLE_CE();
??????? return 0;
}

/*
?* Read data from NAND.读数据
?*/
static int nandll_read_blocks (ulong dst_addr,ulong size,int large_block)
{
??????? uchar *buf = (uchar *)dst_addr;
??????? int i;
?? ?uint page_shift = 9;

?? ?if (large_block==1)
?? ??? ?page_shift = 11;

?? ?if(large_block==2)
?? ??? ?page_shift = 12;

/*

PAGE_SHIFT,页帧号

内核地址,无论是虚拟的还物理的,其都由两部分组成。往往是高N位是页号,低M位是页内的偏移量。

当我们将地址中的低M位偏移量抛弃不用,高N位的页号,移到右端,得到这个结果称页帧号。

宏PAGE_SHIFT, 告诉我们要右移多少位得到页帧号。至于为什么要右移12位,看第一张图。

*/ ??????

?????? if(large_block==3)
??????????????? page_shift =13;

?? ?if(large_block == 2)
?? ?{
??????????????? /* Read pages */读前4页
?? ??? ?for (i = 0; i < 4; i++,buf+=(1<<(page_shift-1))) {//buf=0xffe
??????????????????????? nandll_read_page(buf,i,large_block);
?? ??? ?}


?? ??? ?/* Read pages */读后56页
?? ??? ?for (i = 4; i < (0x3c000>>page_shift); i++,buf+=(1<<page_shift)) { //buf=0xfff,i<60
?? ??? ???????? nandll_read_page(buf,large_block);
?? ??? ?}

??????? }else if(large_block == 3)? //K9GAG08U0E
??????? {
??????????? /* Read pages */
??????????? for (i = 0; i < 4; i++,buf+=(1<<(page_shift-2))) {
??????????????????? nandll_read_page(buf,large_block);
??????????? }


??????????? /* Read pages */
??????????? for (i = 4; i < (0x3c000>>page_shift); i++,buf+=(1<<page_shift)) {
??????????????????? nandll_read_page(buf,large_block);
??????????? }
??????? }
?? ?else
?? ?{
?? ??? ?for (i = 0; i < (0x3c000>>page_shift); i++,buf+=(1<<page_shift)) {
?? ??? ???????? nandll_read_page(buf,large_block);
?? ??? ?}
?? ?}
??????? return 0;
}

int copy_uboot_to_ram (void)//把uboot代码写到ram中
{


?? ?int large_block = 0;
?? ?int i;
?? ?vu_char id;

??????? NAND_ENABLE_CE();
??????? NFCMD_REG=NAND_CMD_RESET;
??????? NF_TRANSRnB();


??????? NFCMD_REG = NAND_CMD_READID;
??????? NFADDR_REG =? 0x00;

??????? NF_TRANSRnB();

?? ?/* wait for a while */
??????? for (i=0; i<200; i++);

??????? int factory = NFDATA8_REG;
?? ?id = NFDATA8_REG;

??????? int cellinfo=NFDATA8_REG;
??????? int tmp= NFDATA8_REG;

??????? int childType=tmp & 0x03; //Page size

?? ?if (id > 0x80)
??????? {
??????????? large_block = 1;
??????? }

??????? if(id == 0xd5 && childType==0x01 )
??????? {
??????????? large_block = 2;


??????? }else if(id == 0xd5 && childType==0x02 )
??????? {
??????????? large_block = 3;

??????? }

?? ?/* read NAND Block.
?? ? * 128KB ->240KB because of U-Boot size increase. by scsuh
?? ? * So,read 0x3c000 bytes not 0x20000(128KB).
?? ? */
?? ?return nandll_read_blocks(CFG_PHY_UBOOT_BASE,0x3c000,large_block);
}


#if 1

#define REG_GPFCON?????? (0x7F0080A0)
#define REG_GPFDAT?????? (0x7F0080A4)

void beep(void)
{

?? uint reg_f_cfg=readl(REG_GPFCON) & 0x3FFFFFFF | (1<<30);
?? writel(reg_f_cfg,REG_GPFCON);

?? uint reg_f_on=readl(REG_GPFDAT) & 0xFFFF7FFF | (1<<15);
?? uint reg_f_off=readl(REG_GPFDAT) & 0xFFFF7FFF;

?? writel(reg_f_on,REG_GPFDAT);
}
#else
void? beep(void)
{

}
#endif

#endif

三.现在来看nand驱动的流程 进入start.S看到这段


#ifdef CONFIG_BOOT_NAND//我们需要的
?? ?mov?? ?r0,#0x1000
?? ?bl?? ?copy_from_nand
#endif

关于copy_from_nand 具体如下:

??? .globl copy_from_nand
copy_from_nand:
?? ?mov?? ?r10,lr?? ??? ?/* save return address */

?? ?mov?? ?r9,r0
?? ?/* get ready to call C functions */
?? ?ldr?? ?sp,_TEXT_PHY_BASE?? ?/* setup temp stack pointer */
?? ?sub?? ?sp,sp,#12
?? ?mov?? ?fp,#0?? ??? ??? ?/* no previous frame,so fp=0 */
?? ?mov?? ?r9,#0x1000
?? ?bl?? ?copy_uboot_to_ram

3:?? ?tst ?? ?r0,#0x0
?? ?bne?? ?copy_failed

?? ?ldr?? ?r0,=0x0c000000
?? ?ldr?? ?r1,_TEXT_PHY_BASE
1:?? ?ldr?? ?r3,[r0],#4
?? ?ldr?? ?r4,[r1],#4
?? ?teq?? ?r3,r4
?? ?bne?? ?compare_failed?? ?/* not matched */
?? ?subs?? ?r9,r9,#4
?? ?bne?? ?1b

4:?? ?mov?? ?lr,r10?? ??? ?/* all is OK */
?? ?mov?? ?pc,lr

copy_failed:
?? ?nop?? ??? ??? ?/* copy from nand failed */
?? ?b?? ?copy_failed

compare_failed:
?? ?nop?? ??? ??? ?/* compare failed */
?? ?b?? ?compare_failed



其中copy_uboot_to_ram的实现在nand_cp.c中

int copy_uboot_to_ram (void)//把uboot代码写到ram中
{


?? ?int large_block = 0;
?? ?int i;
?? ?vu_char id;

??????? NAND_ENABLE_CE();
??????? NFCMD_REG=NAND_CMD_RESET;
??????? NF_TRANSRnB();


??????? NFCMD_REG = NAND_CMD_READID;
??????? NFADDR_REG =? 0x00;

??????? NF_TRANSRnB();

?? ?/* wait for a while */
??????? for (i=0; i<200; i++);

??????? int factory = NFDATA8_REG;
?? ?id = NFDATA8_REG;

??????? int cellinfo=NFDATA8_REG;
??????? int tmp= NFDATA8_REG;

??????? int childType=tmp & 0x03; //Page size

?? ?if (id > 0x80)
??????? {
??????????? large_block = 1;
??????? }

??????? if(id == 0xd5 && childType==0x01 )
??????? {
??????????? large_block = 2;


??????? }else if(id == 0xd5 && childType==0x02 )
??????? {
??????????? large_block = 3;

??????? }

?? ?/* read NAND Block.
?? ? * 128KB ->240KB because of U-Boot size increase. by scsuh
?? ? * So,large_block);
}



进入board.c,arm_start中有nand_init具体实现在smdk6410.c中

void nand_init(void)
{
?? ?nand_probe(CFG_NAND_BASE);//检测芯片型号
??????? if (nand_dev_desc[0].ChipID != NAND_ChipID_UNKNOWN)
??????? {
?????????? print_size(nand_dev_desc[0].totlen,"n");
??????? }
}
#endif


四.uboot加载运行后接下来分析nand命令,我感觉这才是重点

1.先认识下主要数据结构

struct nand_flash_dev 数据结构,在/include/linux/MTD/nand.h 中


struct nand_flash_dev {
?? ?char *name;
?? ?int id;
?? ?unsigned long pagesize;
?? ?unsigned long chipsize;
?? ?unsigned long erasesize;
?? ?unsigned long options;
};

struct nand_chip 数据结构
该数据结构在 include/linux/mtd/nand.h 中定义

struct platform_nand_chip {
?? ?int?? ??? ??? ?nr_chips;
?? ?int?? ??? ??? ?chip_offset;
?? ?int?? ??? ??? ?nr_partitions;
?? ?struct mtd_partition?? ?*partitions;
?? ?struct nand_ecclayout?? ?*ecclayout;
?? ?int?? ??? ??? ?chip_delay;
?? ?unsigned int?? ??? ?options;
?? ?void?? ??? ??? ?*priv;
};

2.查看common/cmd_nand.c 咱们来看看都有哪些命令还有这些命令怎么执行的

erase擦除操作

if (strcmp(cmd,"erase") == 0 || strcmp(cmd,"scrub") == 0) {
?? ??? ?nand_erase_options_t opts;
?? ??? ?/* "clean" at index 2 means request to write cleanmarker */
?? ??? ?int clean = argc > 2 && !strcmp("clean",argv[2]);
?? ??? ?int o = clean ? 3 : 2;
?? ??? ?int scrub = !strcmp(cmd,"scrub");

?? ??? ?printf("nNAND %s: ",scrub ? "scrub" : "erase");
?? ??? ?/* skip first two or three arguments,look for offset and size */
?? ??? ?if (arg_off_size(argc - o,argv + o,nand,&off,&size) != 0)
?? ??? ??? ?return 1;

?? ??? ?memset(&opts,sizeof(opts));
?? ??? ?opts.offset = off;
?? ??? ?opts.length = size;
?? ??? ?opts.jffs2? = clean;
?? ??? ?opts.quiet? = quiet;

?? ??? ?if (scrub) {
?? ??? ??? ?puts("Warning: "
?? ??? ??? ????? "scrub option will erase all factory set "
?? ??? ??? ????? "bad blocks!n"
?? ??? ??? ????? "???????? "
?? ??? ??? ????? "There is no reliable way to recover them.n"
?? ??? ??? ????? "???????? "
?? ??? ??? ????? "Use this command only for testing purposes "
?? ??? ??? ????? "if youn"
?? ??? ??? ????? "???????? "
?? ??? ??? ????? "are sure of what you are doing!n"
?? ??? ??? ????? "nReally scrub this NAND flash? <y/N>n");

?? ??? ??? ?if (getc() == 'y' && getc() == 'r') {
?? ??? ??? ??? ?opts.scrub = 1;
?? ??? ??? ?} else {
?? ??? ??? ??? ?puts("scrub abortedn");
?? ??? ??? ??? ?return -1;
?? ??? ??? ?}
?? ??? ?}
?? ??? ?ret = nand_erase_opts(nand,&opts);
?? ??? ?printf("%sn",ret ? "ERROR" : "OK");

?? ??? ?return ret == 0 ? 0 : 1;
?? ?}

重点看对yaffs2文件系统的写支持

#ifdef CFG_NAND_YAFFS_WRITE
?? ??? ?} else if (!read && s != NULL && + (!strcmp(s,".yaffs2") || !strcmp(s,".yaffs1"))) {
?? ??? ??? ?nand_write_options_t opts;
??? ??? ??? ?memset(&opts,sizeof(opts));
??? ??? ??? ?opts.buffer = (u_char*) addr;
??? ??? ??? ?opts.length = size;
??? ??? ??? ?opts.offset = off;
??? ??? ??? ?opts.pad = 0;
??? ??? ??? ?opts.blockalign = 1;
??? ??? ??? ?opts.quiet = quiet;
??? ??? ??? ?opts.writeoob = 1;
??? ??? ??? ?opts.autoplace = 1;

?? ??? ??? ?/* jsgood */
??? ??? ??? ?/* if (s[6] == '1')
?? ??? ??? ??? ?opts.forceyaffs = 1; */

??? ??? ??? ?ret = nand_write_opts(nand,&opts);
#endif

?? ??? ?}

现在针对其中的调用处理进行详细跟踪

关于nand_write_options_t opts?;在nand.h和nand_util.c有定义

?


struct nand_write_options {
?u_char *buffer;??/* memory block containing image to write用于写镜像的内存块 */
?ulong length;??/* number of bytes to write描述多少字节 */
?ulong offset;??/* start address in NAND 烧写开始地址*/
?int quiet;??/* don't display progress messages 不显示烧写进度*/
?int autoplace;??/* if true use auto oob layout 采用自动(默认)obb布局*/
?int forcejffs2;??/* force jffs2 oob layout采用jffs2obb布局 */
?int forceyaffs;??/* force yaffs oob layout 采用yaffs布局*/
?int noecc;??/* write without ecc烧写时不写ecc */
?int writeoob;??/* image contains oob data烧写时包含obb区的数据 */
?int pad;??/* pad to page size 页大小*/
?int blockalign;??/* 1|2|4 set multiple of eraseblocks?? 设置擦写对齐方式
???? * to align to */
};

typedef struct nand_write_options nand_write_options_t;

?

在nand_util.c里的具体实现,好长啊。。。。//我感觉英文注释已经很给力了 我就不注释了

?

?

/**
?* nand_write_opts: - write image to NAND flash with support for various options
?*
?* @param meminfo?NAND device to erase
?* @param opts??write options (@see nand_write_options)
?* @return??0 in case of success
?*
?* This code is ported from nandwrite.c from Linux mtd utils by
?* Steven J. Hill and Thomas Gleixner.
?*/
int nand_write_opts(nand_info_t *meminfo,const nand_write_options_t *opts)
{
?int imglen = 0;
?int pagelen;
?int baderaseblock;
?int blockstart = -1;
?loff_t offs;
?int readlen;
?int oobinfochanged = 0;
?int percent_complete = -1;

?/* org: struct nand_oobinfo old_oobinfo; */
?struct nand_ecclayout old_oobinfo;
?
?ulong mtdoffset = opts->offset;
?ulong erasesize_blockalign;
?u_char *buffer = opts->buffer;
?size_t written;
?int result;

?/* jsgood */
?struct mtd_oob_ops oob_ops;

?if (opts->pad && opts->writeoob) {
??printf("Can't pad when oob data is present.n");
??return -1;
?}

?/* set erasesize to specified number of blocks - to match
? * jffs2 (virtual) block size */
?if (opts->blockalign == 0) {
??erasesize_blockalign = meminfo->erasesize;
?} else {
??erasesize_blockalign = meminfo->erasesize * opts->blockalign;
?}

?/* make sure device page sizes are valid */
?if (!(meminfo->oobsize == 16 && meminfo->writesize == 512)
???? && !(meminfo->oobsize == 8 && meminfo->writesize == 256)
???? && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)
??????????? && !(meminfo->oobsize == 128 && meminfo->writesize == 4096)
??????????? && !(meminfo->oobsize == 232 && meminfo->writesize == 8192))

??????? {
??????????????? printf("meminfo->oobsize == %d && meminfo->writesize == %d",meminfo->oobsize,meminfo->writesize);
??printf("Unknown flash (not normal NAND)n");
??return -1;
?}

?/* read the current oob info */
?/* org: memcpy(&old_oobinfo,&meminfo->oobinfo,sizeof(old_oobinfo)); */
?memcpy(&old_oobinfo,meminfo->ecclayout,sizeof(struct nand_ecclayout));

?/* write without ecc? */
?if (opts->noecc) {
??/* org: memcpy(&meminfo->oobinfo,&none_oobinfo,
???????? sizeof(meminfo->oobinfo)); */
??memcpy(meminfo->ecclayout,
???sizeof(struct nand_ecclayout));
??oobinfochanged = 1;

??????????????? printf("opts->noecc***************************n");
?}

?/* autoplace ECC? */
?if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {

??/* org: memcpy(&meminfo->oobinfo,&autoplace_oobinfo,sizeof(struct nand_ecclayout));
??oobinfochanged = 1;

?????????????? printf("opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)***************************n");
?}

?/* force OOB layout for jffs2 or yaffs? */
?if (opts->forcejffs2 || opts->forceyaffs) {
??/* org: struct nand_oobinfo *oobsel =
???opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; */
??struct nand_ecclayout *oobsel =
???opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;

??if (meminfo->oobsize == 8) {
???if (opts->forceyaffs) {
????printf("YAFSS cannot operate on "
?????????? "256 Byte page sizen");
????goto restoreoob;
???}
??????????????????????? /* Adjust number of ecc bytes */
???jffs2_oobinfo.eccbytes = 3;
??}

??/* org: memcpy(&meminfo->oobinfo,oobsel,sizeof(meminfo->oobinfo)); */
??memcpy(meminfo->ecclayout,sizeof(struct nand_ecclayout));

??????????????? printf("opts->forcejffs2 || opts->forceyaffs");
?}

?/* get image length */
?imglen = opts->length;
?pagelen = meminfo->writesize
??+ ((opts->writeoob != 0) ? meminfo->oobsize : 0);

???? //?? printf("opts->writeoob=%d,meminfo->oobsize=%d.................. n",opts->writeoob,meminfo->oobsize);

?/* check,if file is pagealigned */
?if ((!opts->pad) && ((imglen % pagelen) != 0)) {
??printf("Input block length is not page alignedn");
??goto restoreoob;
?}

?/* check,if length fits into device */
?if (((imglen / pagelen) * meminfo->writesize)
????? > (meminfo->size - opts->offset)) {
??printf("Image %d bytes,NAND page %d bytes,"
???????? "OOB area %u bytes,device size %u bytesn",
???????? imglen,pagelen,meminfo->writesize,meminfo->size);
??printf("Input block does not fit into devicen");
??goto restoreoob;
?}

?if (!opts->quiet)
??printf("n");

?/* get data from input and write to the device */
?while (imglen && (mtdoffset < meminfo->size)) {

??WATCHDOG_RESET ();

??/*
?? * new eraseblock,check for bad block(s). Stay in the
?? * loop to be sure if the offset changes because of
?? * a bad block,that the next block that will be
?? * written to is also checked. Thus avoiding errors if
?? * the block(s) after the skipped block(s) is also bad
?? * (number of blocks depending on the blockalign
?? */
??while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
???blockstart = mtdoffset & (~erasesize_blockalign+1);
???offs = blockstart;
???baderaseblock = 0;

???/* check all the blocks in an erase block for
??? * bad blocks */
???do {
????int ret = meminfo->block_isbad(meminfo,offs);

????if (ret < 0) {
?????printf("Bad block check failedn");
?????goto restoreoob;
????}
????if (ret == 1) {
?????baderaseblock = 1;
?????if (!opts->quiet)
??????printf("rBad block at 0x%lx "
???????????? "in erase block from "
???????????? "0x%x will be skippedn",
???????????? (long) offs,
???????????? blockstart);
????}

????if (baderaseblock) {
?????mtdoffset = blockstart
??????+ erasesize_blockalign;
????}
????offs +=? erasesize_blockalign
?????/ opts->blockalign;
???} while (offs < blockstart + erasesize_blockalign);
??}

??readlen = meminfo->writesize;
??if (opts->pad && (imglen < readlen)) {
???readlen = imglen;
???memset(data_buf + readlen,0xff,
????????? meminfo->writesize - readlen);
??}

??/* read page data from input memory buffer */
??memcpy(data_buf,buffer,readlen);
??buffer += readlen;

??/* This is yaffs2 writing if opts->writeoob == 1,
?? * and the other case is jffs2 writing in S3C NAND by jsgood.
?? */
??if (opts->writeoob) {
???/* read OOB data from input memory block,exit
??? * on failure */
???memcpy(oob_buf,meminfo->oobsize);
???buffer += meminfo->oobsize;

??????????????????????? ////debug
??????????????????????? /*
??????????????????????? int k=0;
??????????????????????? for(k=0;k<meminfo->oobsize;k++)
??????????????????????? {

??????????????????????????? if((k+1)%6==0)
??????????????????????????? {
??????????????????????????????? printf("%x ",oob_buf[k]);
??????????????????????????????? printf("n");

??????????????????????????? }else
??????????????????????????? {
??????????????????????????????? printf("%x ",oob_buf[k]);

??????????????????????????? }

?

??????????????????????? }

??????????????????????? printf("........................n");

??????????????????????? */


???/* write OOB data first,as ecc will be placed
??? * in there*/
???/* org: result = meminfo->write_oob(meminfo,
????????? mtdoffset,
????????? meminfo->oobsize,
????????? &written,
????????? (unsigned char *)
????????? &oob_buf); */
???oob_ops.mode = MTD_OOB_AUTO;
???oob_ops.len = meminfo->writesize;
???oob_ops.ooboffs = 0;
???oob_ops.ooblen = meminfo->oobsize;???
???oob_ops.oobbuf = (unsigned char *)&oob_buf;
???oob_ops.datbuf = (unsigned char *)&data_buf;

???result = meminfo->write_oob(meminfo,mtdoffset,&oob_ops);

???if (result != 0) {
????printf("nMTD writeoob failure: %dn",
?????????? result);
????goto restoreoob;
???}
???imglen -= meminfo->oobsize;
??} else {
???/* write out the page data */
???result = meminfo->write(meminfo,
??????mtdoffset,
??????meminfo->writesize,
??????&written,
??????(unsigned char *) &data_buf);

???if (result != 0) {
????printf("writing NAND page at offset 0x%lx failedn",
?????????? mtdoffset);
????goto restoreoob;
???}
??}

??imglen -= readlen;

??if (!opts->quiet) {
???unsigned long long n = (unsigned long long)
???? (opts->length-imglen) * 100;
???int percent;

???do_div(n,opts->length);
???percent = (int)n;

???/* output progress message only at whole percent
??? * steps to reduce the number of messages printed
??? * on (slow) serial consoles
??? */
???if (percent != percent_complete) {
????printf("rWriting data at 0x%x "
?????????? "-- %3d%% complete.",
?????????? mtdoffset,percent);
????percent_complete = percent;
???}
??}

??mtdoffset += meminfo->writesize;
?}

?if (!opts->quiet)
??printf("n");

restoreoob:
?if (oobinfochanged) {
??/* org: memcpy(&meminfo->oobinfo,&old_oobinfo,
???????? sizeof(struct nand_ecclayout));????????
?}

?if (imglen > 0) {
??printf("Data did not fit into device,due to bad blocksn");
??return -1;
?}

?/* return happy */
?return 0;
}

?

?

关于menset即分配空间

下面那些参数已经在struct nand_write_options中说明了,详见上面。

五.现在开始分析nand驱动构架,所有驱动代码均位于nand中


?

1、? nand_base.c:
定义了NAND驱动中对NAND芯片最基本的操作函数和操作流程,如擦除、读写page、读写oob等。当然这些函数都只是进行一些default的操作,若你的系统在对NAND操作时有一些特殊的动作,则需要在你自己的驱动代码中进行定义,然后Replace这些default的函数。
?
2、? nand_bbt.c:
定义了NAND驱动中与坏块管理有关的函数和结构体。
?
3、? nand_ids.c:
定义了两个全局类型的结构体:struct nand_flash_dev nand_flash_ids[ ]和struct nand_manufacturers nand_manuf_ids[ ]。其中前者定义了一些NAND芯片的类型,后者定义了NAND芯片的几个厂商。NAND芯片的ID至少包含两项内容:厂商ID和厂商为自己的NAND芯片定义的芯片ID。当NAND驱动被加载的时候,它会去读取具体NAND芯片的ID,然后根据读取的内容到上述定义的nand_manuf_ids[ ]和nand_flash_ids[ ]两个结构体中去查找,以此判断该NAND芯片是那个厂商的产品,以及该NAND芯片的类型。若查找不到,则NAND驱动就会加载失败,因此在开发NAND驱动前必须事先将你的NAND芯片添加到这两个结构体中去(其实这两个结构体中已经定义了市场上绝大多数的NAND芯片,所以除非你的NAND芯片实在比较特殊,否则一般不需要额外添加)。值得一提的是,nand_flash_ids[ ]中有三项属性比较重要,即pagesize、chipsize和erasesize,驱动就是依据这三项属性来决定对NAND芯片进行擦除,读写等操作时的大小的。其中pagesize即NAND芯片的页大小,一般为256、512或2048;chipsize即NAND芯片的容量;erasesize即每次擦除操作的大小,通常就是NAND芯片的block大小。
?
4、? nand_ecc.c:
定义了NAND驱动中与softeware ECC有关的函数和结构体,若你的系统支持hardware ECC,且不需要software ECC,则该文件也不需理会。
?
5、? nand.c:
参考用的驱动例子 真正的nand.c在board/samsung/smdk6410中
?
6、? diskonchip.c:
定义了片上磁盘(DOC)相关的一些函数,开发普通NAND驱动时不用理会。
?
7.nand_util.c一些操作的定义和实现,蛮重要的
最后拿出nand.C瞧一瞧分析一下,我会就这一段代码进行详细解读

//如果是K9GAG08U0D就用下面的obb区结构
static struct nand_ecclayout s3c_nand_oob_mlc_128_8bit = {
??????? .useecc = MTD_NANDECC_AUTOPLACE,
?? ?.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}}
};

//初始化代码
/*
?* Board-specific NAND initialization. The following members of the
?* argument are board-specific (per include/linux/mtd/nand.h):
?* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
?* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
?* - hwcontrol: hardwarespecific function for accesing control-lines
?* - dev_ready: hardwarespecific function for? accesing device ready/busy line
?* - enable_hwecc?: function to enable (reset)? hardware ecc generator. Must
?*?? only be provided if a hardware ECC is available
?* - eccmode: mode of ecc,see defines
?* - chip_delay: chip dependent delay for transfering data from array to
?*?? read regs (tR)
?* - options: various chip options. They can partly be set to inform
?*?? nand_scan about special functionality. See the defines for further
?*?? explanation
?* Members with a "?" were not set in the merged testing-NAND branch,
?* so they are not set here either.
?*/
void board_nand_init(struct nand_chip *nand)
{
#if defined(CFG_NAND_HWECC)
?? ?int i;
?? ?u_char tmp;
?? ?u_char dev_id;
?? ?struct nand_flash_dev *type = NULL;
#endif

?? ?if (NFCONF_REG & 0x80000000)
?? ??? ?boot_nand = 1;
?? ?else
?? ??? ?boot_nand = 0;

?? ?NFCONT_REG ?? ??? ?&= ~NFCONT_WP;
?? ?nand->IO_ADDR_R?? ??? ?= (void __iomem *)(NFDATA);
?? ?nand->IO_ADDR_W?? ??? ?= (void __iomem *)(NFDATA);
?? ?nand->cmd_ctrl?? ??? ?= s3c_nand_hwcontrol;
?? ?nand->dev_ready?? ??? ?= s3c_nand_device_ready;
?? ?nand->scan_bbt?? ??? ?= s3c_nand_scan_bbt;
?? ?nand->options?? ??? ?= 0;

#if defined(CFG_NAND_FLASH_BBT)
?? ??? ?nand->options ?? ??? ?|= NAND_USE_FLASH_BBT;
#else
?? ??? ?nand->options?? ??? ?|= NAND_SKIP_BBTSCAN;
#endif

#if defined(CFG_NAND_HWECC)
?? ?nand->ecc.mode?? ??? ?= NAND_ECC_HW;
??????? nand->ecc.hwctl?? ??? ?= s3c_nand_enable_hwecc;
?? ?nand->ecc.calculate?? ?= s3c_nand_calculate_ecc;
?? ?nand->ecc.correct?? ?= s3c_nand_correct_data;


??????? // K9GAG08U0E must add below codes
??????? {
????????? 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_READID,NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
??????? s3c_nand_hwcontrol(0,0x00,NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
??????? s3c_nand_hwcontrol(0,NAND_NCE | NAND_ALE);
??????? s3c_nand_hwcontrol(0,NAND_CMD_NONE,NAND_NCE | NAND_CTRL_CHANGE);

??????? s3c_nand_device_ready(0);

?? ?tmp = readb(nand->IO_ADDR_R); /* Maf. ID */

????? //? printf("Manufactor ID:%xn",tmp);

?? ?tmp = dev_id = readb(nand->IO_ADDR_R); /* Device ID */

??????? //printf("dev_id ID:%x n",dev_id);

?? ?for (i = 0; nand_flash_ids[i].name != NULL; i++) {
?? ??? ?if (tmp == nand_flash_ids[i].id) {
?? ??? ??? ?type = &nand_flash_ids[i];
?? ??? ??? ?break;
?? ??? ?}
?? ?}
?? ?
?? ?nand->cellinfo = readb(nand->IO_ADDR_R);?? ?/* 3rd byte */
?? ?tmp = readb(nand->IO_ADDR_R);?? ??? ??? ?/* 4th byte */

??? //??? printf("tmp = %xn",tmp);

??????? if (!type->pagesize)
??????? {
??????????????? if (((nand->cellinfo >> 2) & 0x3) == 0)
??????????????? {

?#if 0
?? ??? ??? ?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 */
??????????????????????? if ((1024 << (tmp & 0x3)) > 512)
??????????????????????? {
?? ??? ??? ??? ?nand->ecc.layout = &s3c_nand_oob_mlc_64;
#ifdef FORLINX_DEBUG
??????????????????????????????? printf("Nandflash:Type=MLC? ChipName:samsung-K9F2G08U0B or hynix-HY27UF082G2B n");
#endif
??????????????????????? }else
??????????????????????? {
??????????????????????????????? nand->ecc.layout = &s3c_nand_oob_16;
#ifdef FORLINX_DEBUG
??????????????????????????????? printf("*************nand->ecc.layout = &s3c_nand_oob_mlc_16 n");
#endif
?? ??? ??? ?}

#endif
?? ??? ??? ?nand_type = S3C_NAND_TYPE_SLC;
?? ??? ??? ?nand->ecc.size = 512;
?? ??? ??? ?nand->ecc.bytes?? ?= 4;
?? ??? ??? ?
??????????????????????? if ((1024 << (tmp & 0x3)) > 512)
??????????????????????? {
?? ??? ??? ??? ?nand->ecc.read_page = s3c_nand_read_page_1bit;
?? ??? ??? ??? ?nand->ecc.write_page = s3c_nand_write_page_1bit;
?? ??? ??? ??? ?nand->ecc.read_oob = s3c_nand_read_oob_1bit;
?? ??? ??? ??? ?nand->ecc.write_oob = s3c_nand_write_oob_1bit;
?? ??? ??? ??? ?nand->ecc.layout = &s3c_nand_oob_64;
#ifdef FORLINX_DEBUG
??????????????????????????????? printk("Nandflash:Type=SLC? ChipName:samsung-K9F2G08U0B or hynix-HY27UF082G2B n");
#endif

??????????????????????? } else
??????????????????????? {
?? ??? ??? ??? ?nand->ecc.layout = &s3c_nand_oob_16;
?? ??? ??? ?}

??????????????? } else
??????????????? {
?? ??? ??? ?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;

??????????????????????? printf("Nandflash:ChipType=MLC? ChipName=samsung-K9G8G08U0A n");



?? ??? ?}
??????? } else
??????? {

?? ??? ?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;

?????????????? int childType=tmp & 0x03; //Page size

????????????? //? printf("dev_id=%x,childType=%x n",dev_id,childType);

??????????????? if(dev_id == 0xd5 && childType==0x01)
?? ??? ?{
??????????????????????? //K9GAG08U0D???? size=2GB? type=MLC? Page=4K
#ifdef FORLINX_DEBUG
??????????????????????? printf("Nandflash:ChipType= MLC? ChipName=samsung-K9GAG08U0D n");

#endif??????? ?
??????????????????????? // dg add #if 2011-11-09
??????? #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
?? ??? ??? ?nand->ecc.read_page = s3c_nand_read_page_8bit;
?? ??? ??? ?nand->ecc.write_page = s3c_nand_write_page_8bit;
?? ??? ??? ?nand->ecc.size = 512;
??????????????????????? nand->ecc.bytes = 13;
??????????????????????? nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;//这个芯片就用这个,和内核中的分布一样??
????? #endif ?? ??? ?} ??????????????? else if(dev_id == 0xd5 && childType==0x02) ??????????????? { ??????????????????? //K9GAG08U0E???? size=2GB? type=MLC? Page=8K #ifdef FORLINX_DEBUG ??????????????????? printf("Nandflash:ChipType= MLC? ChipName=samsung-K9GAG08U0E n"); #endif ??????????????????? // dg add #if 2012-03-30 ??? #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)) ??????????????????? nand->ecc.read_page = s3c_nand_read_page_8bit; ??????????????????? nand->ecc.write_page = s3c_nand_write_page_8bit; ??????????????????? nand->ecc.size = 512; ??????????????????? nand->ecc.bytes = 13; ??????????????????? nand->ecc.layout = &s3c_nand_oob_mlc_232_8bit; ??? #endif ??????????????? } ?? ??? ?else ?? ??? ?{ #ifdef FORLINX_DEBUG ?? ??? ??? ?printf("select s3c_nand_oob_mlc_64n"); #endif ?? ??? ?} ?? ?} #else ?? ?nand->ecc.mode = NAND_ECC_SOFT; #endif

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读