既然是命令那自然要看到U_BOOT_CMD宏,这个宏分析的很多就不分析了。在cmd_nand.c文件中。nand的命令执行函数是do_nand。当然我们没有定义CFG_NAND_LEGACY,要看这个分支。do_nand函数也没有什么好分析的,摘取几个命令的处理分析下。
1。nand bad命令
列出函数调用次序先,
do_nand
nand_block_isbad//include/nand.h
nand_block_isbad//在Nand_base.c中, info->block_isbad函数指针指向
nand_block_checkbad//在Nand_base.c
nand_block_bad() //在Nand_base.c中,nand_chip,this->block_bad函数指针指向,
nand_isbad_bbt
//在Nand_bbt.c中
如下。
- nand?=?&nand_info[nand_curr_device];
-
if?(strcmp(cmd,?"bad")?==?0{
- printf("nDevice %d bad blocks:n")for?(off?;?off?<?nand->size+=?nand>erasesize//按块循环
-
(nand_block_isbad(nand(1)
- printf" %08xn";
- return 0;
- }
这个函数的定义在include/nand.h中,它调用nand_info[]变量中的block_isbad函数指针指向的函数;这个指针在初始化时已经被分配,这里是Nand_base.c文件中的nand_block_isbad函数。这里有个小问题,那有两个都被编译的nand_block_isbad函数的定义,那到底调用的是哪个呢。答案是nand.h中的,因为Nand_base.c中的是被定义成static的函数,只能在本文件中使用。
- static?int?nand_block_isbad?(struct mtd_info?*mtd)
-
{
-
*?Check?for?invalid offset?*/
-
(ofs?>?mtd)
- return?-EINVAL;
- return nand_block_checkbad?(mtd?1}
这个函数又会调用nand_block_checkbad 函数
- static?int?nand_block_checkbad?int?getchipint?allowbbt{
- struct nand_chip?*this?=?mtd>priv;
-
(!this>bbt/如果nand_chip结构体变量中的bbt(坏块标记表)表指针是空的
- return this>block_bad*?Return info from the table?/
- return nand_isbad_bbt?}
block_bad函数指针被指向nand_block_bad,分析它,
此函数将从芯片读取坏块标记
- static?int?nand_block_bad{
-
int?page?res?;
- struct nand_chip?;
- u16 bad;
- page?int>>?this>page_shift&?this>pagemask;?)
-
(getchip{?/选中芯片
- chipnr?>chip_shift*?Grab the lock?and?see?if?the device?is?available?/
- nand_get_device?(this?FL_READING*?Select?the NAND device?/
- this>select_chip}
-
>options?&?NAND_BUSWIDTH_16{
- this>cmdfunc??this>badblockpos?&?0xFE;
- bad?=?cpu_to_le16>read_word&?0x1)
- bad?=?1(bad?&?0xFF!=?0xff)
- res?}?else?>badblockpos(2>read_byte(3*?Deselect?and?wake up anyone waiting?on?the device?/
- nand_release_device}
- return res}
(1)从偏移地址获取页号。page_shift是page页位数(就是一页的大小的数值用二进制表示最高位的序号)。将偏移地址右移页位数,则低位就是页的号码,有相当于除页大小。然后在与上pagemask,就是页大小(主要是将高位置0,其实这里与不与感觉都无所谓,高位本来就是0)
(2)主要就是这一句,cmdfunc()函数,发送读取oob区命令。this->badblockpos在nand_scan函数中设置了大页0,小页5。
(3)读出的位是否是0xff,如果不是就是坏块。
...................................
再看下如果有bbt表,nand_block_checkbad函数将调用nand_isbad_bbt。bbt表在初始化时scan_bbt函数已经建立。所以nand bad命令在这个uboot中都是通过查bbt表完成的。
-
int?nand_isbad_bbt?;
-
int?block;
- uint8_t resGet?block number?*?2?/
- block?(offs?>?>bbt_erase_shift?-?1[block?>?3]?(block?&?0x06&?0x03)
- DEBUG?(MTD_DEBUG_LEVEL2"nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02xn"(unsigned?)offs?block?>?1;
- switch?)rescase?0x00:?return 0case?0x01:?return 1case?0x02:?return allowbbt???0?:?1}
- return 1}
nand bad命令处理暂时分析到这里
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
分析nand read 命令:
nand read命令的调用顺序为:
do_nand //cmd_nand.c
nand_read_opts
///driver/mtd/nand/nand_util.c
nand_read //nand_base.c,meminfo->read指针指向
nand_read_ecc //nand_base.c
sep4020_nand_read_buf//cpu/sep4020/nand_flash.c
这里的代码大多照搬了内核的mtd层代码,而仅仅对于uboot不需要这么复杂,一些操作觉得不合理,有很多无用又费周折的操作。
do_nand函数中read相关部分:
-
(strncmp"read"=?0?||?strncmp"write"{
-
int?read;
-
(argc?<?4)
- goto usage;
- addr?(ulong)simple_strtoul(argv[2NULL;
- read?=?strncmp*?1?=?read=?write?/
- printf"nNAND %s: "??"read"?:?;
-
(arg_off_size-?3+?3&off&size)
- return 1;
- s?=?strchr'.(s?NULL?&&
-
!strcmp(s".jffs2"|?".e"".i"{
-
(read*?read?/
- nand_read_options_t opts;
- memset&opts(opts;
- opts.buffer?(u_char)?addr/addr是内存地址,nand读出来的数据最终将存入这里
- opts.length?=?size/读取的大小
- opts.offset?=?off/flash地址
- opts.quiet?=?quiet;
- ret?=?nand_read_opts/读数据操作,opts将保存必要的信息。
-
*?write?/
- nand_write_options_t opts*?opts.forcejffs2?/
- opts.pad?.blockalign?=?nand_write_opts}
-
&?".yaffs";?
- opts.forceyaffs?.noecc?.writeoob?.skipfirstblk?}
-
".oob"*?read out-of-band data?/
-
)
- ret?>read_oob
-
(u_char?else
- ret?>write_oob=?nand_read)addr=?nand_write}
- printf" %d bytes %s: %sn""written""ERROR"?"OK";
- return ret?;
- }
nand_read_opts在/driver/mtd/nand/nand_util.c,参照代码中原本的英文注释,代码量大也就不做详细分析了
-
*
-
*?nand_read_opts:?-?read image from NAND flash with support?for?various options
-
*?@param meminfo NAND device?to?erase
-
*?@param opts read options?@see struct nand_read_options)
-
*?@return 0?in?case?of success
-
/
-
int?nand_read_opts(nand_info_t?*meminfoconst?nand_read_options_t?*optsint?imglen?=?opts>length;
-
int?pagelenint?baderaseblockint?blockstart?-1int?percent_complete?;
- loff_t offs;
- size_t readlen;
- ulong mtdoffset?>offset;
- u_char?*buffer?>bufferint?result*?make sure device page sizes are valid?(meminfo>oobsize?=?16?&?meminfo>oobblock?=?512=?8?=?256=?64?=?2048{
- printf"Unknown flash (not normal NAND)n";
- return?}
- pagelen?=?meminfo>oobblock
-
+?>readoob???meminfo:?0*?checkif?length?is?not?larger than device?(imglen?/?pagelen*?meminfo>oobblock>size?-?opts"Image %d bytes,NAND page %d bytes,"
-
"OOB area %u bytes,device size %u bytesn"?pagelen;
- printf"Input block is larger than devicen"}
-
!opts>quiet)
- printf"n"get?data from input?and?write?to?the device?while?(mtdoffset?<?meminfo{
- WATCHDOG_RESET?*?new eraseblockfor?bad block.?Stay?in?the
-
loop?to?be sure?if?the offset changes because of
-
*?a bad blocknext?block that will be
-
*?written?is?also checked.?Thus avoiding errors?if
-
*?the block)?after the skipped blockis?also bad
-
(number of blocks depending?on?the blockalign
-
(blockstart?~meminfo+1{
- blockstart?=?mtdoffset?;
- offs?=?blockstart;
- baderaseblock?*?check all the blocks?in?an?erase?block?for
-
*?bad blocks?do?int?ret?>block_isbad(ret?<?0"Bad block check failedn"{
- baderaseblock?"rBad block at 0x%lx "
-
"in erase block from "
-
"0x%x will be skippedn"(long)?offs(baderaseblock{
- mtdoffset?=?blockstart
-
+?meminfo}
- offs?<?blockstart?*?read page data?to?memory buffer?/
- result?>read/读2048字节(不包含oob的一页),
- mtdoffset/nand flash地址
- meminfo/页大小(2048)&readlen(unsigned char?&data_buf(result?"reading NAND page at offset 0x%lx failedn"<?readlen{
- readlen?=?imglen}
- memcpy(buffer?readlen;
- buffer?=?readlen;
- imglen?/上面是读页有效数据(2048),这里读oob数据。
-
>readoob{?
- result?>oobsize&oob_buf"nMTD readoob failure: %dn"?oob_buf;
- buffer?{
- unsigned long long n?(unsigned long long-imglen*?100int?percent;
- do_div(n;
- percent?)n*?output progress message only at whole percent
-
*?steps?to?reduce the number of messages printed
-
on?(slow)?serial consoles
-
(percent?=?percent_complete"rReading data from 0x%x "
-
"-- %3d%% complete.";
- percent_complete?=?percent}
- mtdoffset?>?0"Could not read entire image due to bad blocksn"*?return happy?/
- return 0}
上面meminfo->read指向的函数是,nand_read在nand_base.c文件中。
- static?int?nand_read??size_t?len*?retlen*?buf{
- return nand_read_ecc??retlen}
nand_read_ecc函数在nand_base.c中,函数如下
this->cmdfunc (mtd,NAND_CMD_READ0,0x00,page)函数指针指向的函数为sep4020_nand_command函数
- static void sep4020_nand_command?(struct mtd_info?*mtd,?unsigned command,?int?column,?intpage_addr)
- {
- register struct nand_chip?*this?=?mtd->priv;
- if(command?==?NAND_CMD_READOOB)?//(1)
- {
- column?+=?mtd->oobblock;
- command?=?NAND_CMD_READ0;
- }
- //column是坏块在oob中的位置,加上oobblock(就是页大小pagesiz,不知道为什么起这个名字oobblock),这样就是
- //地址中的列地址。command命令赋值NAND_CMD_READ0(0),读命令。
- this->hwcontrol(mtd,?NAND_CTL_SETCLE);
- //命令引脚使能
- switch(command)
- {
- case?NAND_CMD_READ0:
- *(volatile unsigned long*)EMI_NAND_COM_RAW?=?0x40003000;
- //这个寄存器[7:0]命令的第一个字节00?[15:8]是命令的第二个字节30?.
- //最高位是使能位(暂不开启),30位是字节表示1字节还是2字节命令。4=0100
- break;
- case?NAND_CMD_SEQIN:
- *(volatile unsigned long*)EMI_NAND_COM_RAW?=?0x40001080;
- //?80,10 写flash
- break;
- default:
- this->write_byte(mtd,command);
- break;
- }
- this->hwcontrol(mtd,NAND_CTL_CLRCLE);
- //命令引脚无效
- if?(command?==?NAND_CMD_READID)
- {
- EMI_NAND_COM?|=?0x80000000;?//使能EMI_NAND_COM
- this->hwcontrol(mtd,?NAND_READ_ID);?
- return;
- }
- if?(command?==?NAND_CMD_STATUS)
- {
- EMI_NAND_COM?|=?0x80000000;?//使能EMI_NAND_COM
- this->hwcontrol(mtd,?NAND_READ_STATUS);
- }
- if?(command?==?NAND_CMD_RESET)
- {
- EMI_NAND_COM?|=?0x80000000;
- this->hwcontrol(mtd,?NAND_CTL_CLRALE);
- }
- /*?Set?ALE?and?clear CLE?to?start address cycle?*/
- if?(column?!=?-1?||?page_addr?!=?-1)?{
- this->hwcontrol(mtd,?NAND_CTL_SETALE);?//这里这个函数其实没什么用。?
- EMI_NAND_ADDR1?=?page_addr<<16;?//page_addr是页号。128M,2Kflash一共就64K页
- EMI_NAND_ADDR2?=?page_addr>>16;?//对于一共总数64K的页,这个值等于0
- this->hwcontrol(mtd,?NAND_CTL_CLRALE);
- }
- //
- }
分析sep4020_hwcontrol函数。此函数之所以存在,应该是为了和MCU通过引脚直接控制或其他MCU的nand flash的代码结构保持兼容,此处此函数的主要作用是将IO_ADDR_W替换成对应的寄存器地址
- static void sep4020_hwcontrol(struct mtd_info?*mtd,?int?cmd)
- {?
- struct nand_chip?*this?=?mtd->priv;
- switch?(cmd)?{
- case?NAND_CTL_SETNCE:?
- case?NAND_CTL_CLRNCE:
- break;
- //对于nCE位的操作都不予理睬
- case?NAND_CTL_SETCLE:
- this->IO_ADDR_W?=?(void __iomem?*)?EMI_NAND_COM_RAW;
- break;
- //IO_ADDR_W是nand flash的数据寄存器地址。是_iomem类型变量(这是个空的宏定义,
- //但这样可以让人很容易知道这是个寄存器变量。),这里的作用是将EMI_NAND_COM_RAW即nand flash
- //内存的地址赋值给IO_ADDR_W,这样后面的操作,在使用IO_ADDR_W时就是使用EMI_NAND_COM_RAW。
- case?NAND_CTL_SETALE:
- this->IO_ADDR_W?=?(void __iomem?*)?EMI_NAND_ADDR1_RAW;
- break;
- case?NAND_READ_ID:
- this->IO_ADDR_R?=?(void __iomem?*)?EMI_NAND_ID_RAW;
- break;
- case?NAND_READ_STATUS:
- this->IO_ADDR_R?=?(void __iomem?*)?EMI_NAND_STA_RAW;
- break;
- /*?NAND_CTL_CLRCLE:?*/
- /*?NAND_CTL_CLRALE:?*/
- default:
- this->IO_ADDR_W?=?(void __iomem?*)?EMI_NAND_DATA_RAW;
- this->IO_ADDR_R?=?(void __iomem?*)?EMI_NAND_DATA_RAW;
- //在一些命令使能和地址使能后,将IO_ADDR_W还原成EMI_NAND_DATA_RAW nand flash数据寄存器地址
- break;
- }
- }
this->read_buf(mtd,data_poi,end);read_buf指向的函数为sep4020_nand_read_buf,
- static void sep4020_nand_read_buf*bufint?/配置DMAC用于nand的传输
- DMAC_C0CONTROL?(2112>2<14<13<9<6<3;
- DMAC_C0SRCADDR?=?EMI_NAND_DATA_RAW?;
- DMAC_C0DESTADD?=?vaddr/vaddr在board_nand_init函数中,使用malloc分配的一块2112大的内存空间
- DMAC_C0CONFIGURATION?=?0x31d?;?
- EMI_NAND_COM?=?0xc0003000/nand命令控制器,00 30读命令,且最高位使能nand控制器,开始读数据。
-
while(EMI_NAND_IDLE?&?0x01if=?2048?|?=?2112/如果要读取的长度是1页或包含oob的1页。则从vaddr开始复制len长度的数据
-
{
- memcpy(buf;?
-
=?64/如果读取的长度是64,则是要只读取oob区域,则从vaddr+2048地址处开始复制。
-
{?
- memcpy+2048}
- }
这个函数使能了nand flash控制器,将nandflash中对于的一页数据读出,并将适当的数据复制给了参数传来的buf。
nand read命令大致就是这样一个流程。本来想只是写写uboot关于nand的处理,和这个sep4020 nand控制器的特点。没想到这个版本的uboot就是nand驱动和内核的差不多,代码量太多。可能也是自己不熟悉这块,陆陆续续写了几天,感觉写的效率很低,写的想吐。于是草草结尾。之后看看其他版本的uboot的nand相关,不知道还是不是这样了。