/****************************************************************************** Copyright (C), 2001-2011, DCN Co., Ltd. ****************************************************************************** File Name : nand.c Version : Initial Draft Author : oucaijun Created : 2014/5/9 Last Modified : Description : K9F2G08 nandflash 底层读写、控制驱动程序 当前的程序可以对nand进行随机读写,随机读写的时候数据正确,但没有正确实现ecc算法。 当前程序可以实现对nand普通的页读写,并实现ecc算法。 因随机读写ecc不正确,因此不能和普通的带ecc的读写方式配套使用。 页内地址: 2048 = 2 ^ 11 , 使用A0-A11表示页内地址 见K9F2G08手册 Function List : CheckBadBlk CheckNandflash EraseBlock getBlockNum getPageNum InitNandCfg MarkBadBlk MY_ReadPage MY_WritePage nandNew NF_Reset RandomRead RandomWrite ReadBlock ReadChipId ReadPage ReadPageAll ReadStatus WaitNFBusy WriteBlock WritePage History : 1.Date : 2014/5/9 Author : ocj Modification: Created file ******************************************************************************/ /*----------------------------------------------* * routines' implementations * *----------------------------------------------*/ #include "Common.h" #include <string.h> #include "nand.h" #include "nfarea.h" /*----------------------------------------------* * macros * *----------------------------------------------*/ /*----------------------------------------------* * external routine prototypes * *----------------------------------------------*/ /*----------------------------------------------* * internal routine prototypes * *----------------------------------------------*/ /*----------------------------------------------* * project-wide global variables * *----------------------------------------------*/ /*----------------------------------------------* * module-wide global variables * *----------------------------------------------*/ /*----------------------------------------------* *external variables * *----------------------------------------------*/ /*----------------------------------------------* * constants * *----------------------------------------------*/ #define NF_WAIT_RB() {while(!(rNFSTAT &(1<<0)));} //this bit is ReadOnly #define NF_CLEAR_RB() {rNFSTAT |=(1<<2);} //清除RnB信号 //this bit is RW #define NF_DETECT_RB() {while(!(rNFSTAT &(1<<2))) ;} /* R/B: When low, it indicates that a program, erase or random read operation is in process and returns to high state upon completion. */ void NF_Reset(void) { NFChipEn(); NF_CLEAR_RB(); WrNFCmd(RESET_CMD); NF_DETECT_RB(); NFChipDs(); } /*------------------------------------------------------------/ 函数名称: InitNandCfg 功能描述: 配置flash 传 参: 无 返 回 值: 无 -------------------------------------------------------------*/ static void InitNandCfg(void) { rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);//初始化时序参数 rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0); //非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器 NF_Reset(); } /*------------------------------------------------------------/ 函数名称: WaitNFBusy 功能描述: Read Status Register , I/O = RdNFDat(); RdNFDat() until(I/O 6 = 1 or R/B = 1 ) ; if(I/O 0 = 0 ) program/erase/ success ; 传 参: 无 返 回 值: I/O bit0 , 表示了命令的执行状况。若为0,表示success -------------------------------------------------------------*/ static U32 WaitNFBusy(void) // { U8 stat; WrNFCmd(QUERYCMD); do { stat = RdNFDat(); } while (!(stat&0x40)); WrNFCmd(READCMD0); return stat&1; //注意0为操作成功 } /*------------------------------------------------------------/ 函数名称: ReadChipId 功能描述: 读flash ID 传 参: 无 返 回 值: static U32 id -------------------------------------------------------------*/ U32 ReadChipId(void) { U8 MID , PID , cyc3,cyc4,cyc5 ; NFChipEn(); WrNFCmd(RdIDCMD); WrNFAddr(0); while(NFIsBusy()); MID = RdNFDat(); PID = RdNFDat(); cyc3 = RdNFDat(); cyc4 = RdNFDat(); cyc5 = RdNFDat(); NFChipDs(); return (MID<<24)|(PID<<16)|(cyc3<<8)|(cyc4); } /*------------------------------------------------------------/ 函数名称: ReadStatus 功能描述: 读FLASH状态 传 参: 无 返 回 值: static U16 stat -------------------------------------------------------------*/ static U16 ReadStatus(void) { U16 stat; NFChipEn(); WrNFCmd(QUERYCMD); stat = RdNFDat(); NFChipDs(); return stat; } //CheckNandflash(int info) ret =0,没有flash或者不支持的flash。 int CheckNandflash(int info) { U32 i; int have_nandflash ; InitNandCfg(); i = ReadChipId(); if(info) debugk("NAND ID is 0x%04x \n", i); if((i==0xecda) || (i==0xadda)) { have_nandflash = 1; } else if(i==0xecf1) { have_nandflash = 1; } else { have_nandflash = 0; debugk("unsupported nandflash id \n"); } if(info) debugk("Nand flash status = %x\n", ReadStatus()); return have_nandflash ; } /*------------------------------------------------------------/ 函数名称: EraseBlock 功能描述: 擦除 FLASH 传 参: U32 addr 返 回 值: U32 ~stat -------------------------------------------------------------*/ U32 EraseBlock(U32 addr)// if( stat & 0x1 == 1),erase success { U8 stat; addr &= ~0x3f; // page_addr 的低六位清零 //addr(page_addr) : row_addr A12-A28 not need cloumn addr A0-A11 //64*2048(row_addr : one nand 64*2048 pages) * 2048(cloumn_addr : one page 2048 bytes) NFChipEn(); WrNFCmd(ERASECMD0); WrNFAddr(addr); //行地址A18~A19 WrNFAddr(addr>>8);//行地址A20~A27 WrNFAddr(addr>>16);//行地址A28 WrNFCmd(ERASECMD1); stat = WaitNFBusy(); NFChipDs(); return ~stat; //最后一位等于0,success } /*------------------------------------------------------------/ 函数名称: ReadPage 功能描述: 传 参: U32 addr, U8 *buf 返 回 值: 无 -------------------------------------------------------------*/ void ReadPage(U32 addr, U8 *buf) { U16 i; NFChipEn(); WrNFCmd(READCMD0); WrNFAddr(0); WrNFAddr(0); WrNFAddr(addr); WrNFAddr(addr>>8); WrNFAddr(addr>>16); WrNFCmd(READCMD1); InitEcc(); WaitNFBusy(); for(i=0; i<2048; i++) buf[i] = RdNFDat(); NFChipDs(); } void ReadPageAll(U32 addr, U8 *buf) { U16 i; NFChipEn(); WrNFCmd(READCMD0); WrNFAddr(0); WrNFAddr(0); WrNFAddr(addr); WrNFAddr(addr>>8); WrNFAddr(addr>>16); WrNFCmd(READCMD1); InitEcc(); WaitNFBusy(); for(i=0; i<2048+64; i++) buf[i] = RdNFDat(); NFChipDs(); } /*------------------------------------------------------------/ 函数名称: WritePage 功能描述: 传 参: U32 addr, U8 *buf 返 回 值: ret若为0xff,表示success -------------------------------------------------------------*/ U32 WritePage(U32 addr, U8 *buf) { U32 i, mecc; U8 stat, tmp[7]; NFChipEn(); WrNFCmd(PROGCMD0); WrNFAddr(0); WrNFAddr(0); WrNFAddr(addr); WrNFAddr(addr>>8); WrNFAddr(addr>>16); InitEcc(); //reset mecc and secc MEccUnlock(); for(i=0; i<2048; i++) WrNFDat(buf[i]); MEccLock(); //因为K9F2G08U0A是8位IO口,因此S3C2440共产生4个字节的main区ECC码和2个字节的spare区ECC码 //在这里我们规定,在每一页的spare区的第0个地址到第3个地址存储main区ECC,第4个地址和第5个地址存储spare区ECC。 //在下次读取这一页数据的时候,同样我们也计算ECC校验码,然后与spare区中的ECC校验码比较,如果一致则说明读取的数据正确,如果不一致则不正确 //读取rNFMECC0 -- Main的ECC校验码 //for 8 bit nand flash, only use NFMECC0 mecc = RdNFMEcc() ; tmp[0] = mecc&0xff; tmp[1] = (mecc>>8)&0xff; tmp[2] = (mecc>>16)&0xff; tmp[3] = (mecc>>24)&0xff; WrNFDat(0xff);//2048,坏块标志 SEccUnlock(); WrNFDat(tmp[0]);//ECC校验码 WrNFDat(tmp[1]); WrNFDat(tmp[2]); WrNFDat(tmp[3]); SEccLock(); WrNFCmd(PROGCMD1); stat = WaitNFBusy();//stat若为0,表示success NFChipDs(); return ~stat;////ret若为0xff,表示success } // RandomWrite 随机写 // 不直接改写spare区域 // addr_in_page 0-2047 U32 RandomWrite(int page_num, int addr_in_page ,U8 *buf,int len) { U8 stat ; U32 mecc , secc ; if( len+addr_in_page>2048 ){ debugs("RandomWrite :do not allow to write spare_section directly!\n"); } if(buf==NULL){ debugs("RandomWrite :wrong data pointer!\n"); } //cs NFChipEn(); WrNFCmd(PROGCMD0); WrNFAddr(0); WrNFAddr(0); WrNFAddr(page_num&0xff); WrNFAddr((page_num>>8) &0xff); WrNFAddr((page_num>>16) &0xff); WrNFCmd(RANDOM_PROGRAMCMD); //85h WrNFAddr( (addr_in_page&0xff) ); //页内A0~A7 WrNFAddr( ((addr_in_page>>8)&0x0f) ); //页内地址A8~A11 while(len--){ WrNFDat(*buf++); } WrNFCmd(PROGCMD1);//10 stat = WaitNFBusy(); //ncs NFChipDs(); return ~stat; } //MY_WritePage 写块 ,并将ecc写入spare区域 。 //完全同 WritePage U32 MY_WritePage(U32 addr, U8 *buf) { U32 mecc ,secc ; int i ,ret ; U8 stat; InitEcc(); //复位ECC MEccUnlock(); //解锁main区的ECC NFChipEn(); //打开nandflash片选 // NF_CLEAR_RB(); //清RnB信号 没有用这个判断busy而是直接读的query_nand ,没必要。 WrNFCmd(PROGCMD0);//页写命令周期1 //写入5个地址周期 WrNFAddr(0x00); //列地址A0~A7 WrNFAddr(0x00); //列地址A8~A11 列地址就是页内的地址,不能超过2048,要注意传入的值的有效性。 WrNFAddr((addr) & 0xff); //行地址A12~A19 WrNFAddr((addr >> 8) & 0xff); //行地址A20~A27 WrNFAddr((addr >> 16) & 0xff); //行地址A28 for (i = 0; i < 2048; i++)//写入一页数据 { WrNFDat( buf[i] ); } MEccLock(); //锁定main区的ECC值 //读取main区的ECC校验码 //for 8 bit nand flash, only use NFMECC0 mecc = rNFMECC0 ; SEccUnlock();//解锁spare区的ECC WrNFDat(0xff);//先写入0XFF非坏块标志到2048 WrNFDat32(mecc);//把main区的ECC值写入到spare区的前2-5字节地址内,即第2049~2052地址 SEccLock();//锁定spare区的ECC值 secc = rNFSECC ;//读取spare区的ECC校验码 WrNFDat8(secc & 0xff);//写入secc校验码到spare区第6-7个字节 WrNFDat8((secc>>8) & 0xff); WrNFCmd(PROGCMD1); WrNFCmd(QUERYCMD); do { stat = RdNFDat(); } while(!(stat & 0x40)); NFChipDs(); if (stat & 0x1) {//fail debugs("fail to write nand!\n"); } return ~(stat & 0x1); //if ok,ret = 0xff } /*------------------------------------------------------------/ 函数名称: ReadBlock 功能描述: 传 参: U32 addr, U8 *buf 返 回 值: 无 -------------------------------------------------------------*/ void ReadBlock(U32 addr, U8 buf[N_PAGES_NAND_BLOCK][N_BYTES_NAND_PAGE]) //addr :pages ,64的整数倍 { int i; int start ; start = addr ; for(i=0; i<64; i++) { ReadPage( start+i,buf[i] ); } } /*------------------------------------------------------------/ 函数名称: WriteBlock 功能描述: 传 参: U32 addr, U8 *buf 返 回 值: 无 -------------------------------------------------------------*/ void WriteBlock(U32 addr, U8 buf[64][2048]) { int i ; int start = addr ; for(i=0; i<64; i++) { WritePage( start+i ,buf[i] ); } } int getPageNum(u32 addr ,u32 PageNum[1]) { PageNum[0] = addr/2048 ; return PageNum[0] ; } int getBlockNum(u32 addr ,u32 BlockNum[1]) { BlockNum[0] = addr/2048/64 ; return BlockNum[0] ; } //// //// //CheckBadBlk //addr : pageaddr{A12--A28} //ret == 0 : badblk //在该块的第一页 第2048bytes查询是否为non-FFh,是则为badblk 。 int CheckBadBlk(U32 addr) { U8 dat; addr &= ~0x3f; /* Samsung makes sure that either the 1st or 2nd page of everyinitial invalid block has non-FFh data at the column address of 2048. */ NFChipEn(); WrNFCmd(READCMD0); WrNFAddr(0); // WrNFAddr(8); // A11=1 该地址值为2048 WrNFAddr(addr & 0xff); WrNFAddr((addr>>8) & 0xff); WrNFAddr((addr>>16) & 0xff);//in a page [offset == 2048] : read the 2048th bytes of page(addr) . WrNFCmd(READCMD1); WaitNFBusy(); dat = RdNFDat(); NFChipDs(); if(dat!=0xff) { debugs("Blk % 4d is NG! page_addr=%d\n" ,addr>>6 , addr ); } return (dat != 0xff); } //MarkBadBlk 标记坏块 //addr : pageaddr{A12--A28} //在该块的第一页 第2048bytes 写入non-FFh。 void MarkBadBlk(U32 addr) { addr &= ~0x3f; NFChipEn(); WrNFCmd(PROGCMD0); //mark offset 2048 WrNFAddr(0); // WrNFAddr(8); // A11=1 2048 WrNFAddr(addr & 0xff); WrNFAddr((addr>>8) & 0xff); WrNFAddr((addr>>16) & 0xff);//in a page [offset == 2048] : read the 2048th bytes of page(addr) . WrNFDat(0); //mark with 0 == “badblk” WrNFCmd(PROGCMD1); WaitNFBusy(); //needn't check return status NFChipDs(); } //MY_ReadPage //ret ==0 ok //ret ==1 ecc error int MY_ReadPage(U32 addr, U8 *buf) { int i ; U32 mecc ,secc ; char ch; InitEcc(); MEccUnlock(); NFChipEn(); NF_CLEAR_RB(); WrNFCmd(READCMD0); WrNFAddr(0); WrNFAddr(0); WrNFAddr(addr&0xff); WrNFAddr((addr>>8)&0xff); WrNFAddr((addr>>16)&0xff); WrNFCmd(READCMD1); NF_DETECT_RB(); for(i=0; i<2048; i++) { buf[i] = RdNFDat(); } MEccLock(); SEccUnlock(); ch = RdNFDat();//跳过0XFF坏块标志 2048 //PREFERENCE :DATASHEET--ECC MODULE FEATURES //读spare区的前4个地址内容,即第 2049 - 2052地址,这4个字节为main区的ECC //把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内 mecc = RdNFDat32(); rNFMECCD0 = ((mecc&0xff00)<<8) | (mecc&0xff) ; rNFMECCD1 = ((mecc&0xff000000)>>8) | ((mecc&0xff0000)>>16) ; SEccLock();//这之前计算的secc仅仅包括bit2048 2049 2050 2051 2052 的计算结果????? 应该是吧 secc = RdNFDat32(); //继续读spare区的4个地址内容,即第 2052--2055地址 //把读取到的spare区的ECC校验码放入NFSECCD的相应位置内 rNFSECCD = ((secc&0xff00)<<8) | (secc&0xff); NFChipDs(); if((rNFESTAT0&0xf)==0x0) { //查看ECC状态寄存器 return 0 ; } else { debugs("rNFESTAT0 = %0xh \n" ,rNFESTAT0 ); return 1 ; } } // int RandomRead(U32 page_number, U32 addr_in_page , U8 *bufr ,int len ) { if( len+addr_in_page>2048+64 ){ debugs("RandomRead error : read out of spare_section!\n"); return 1; } if(bufr==NULL){ debugs("RandomRead error : wrong data pointer!\n"); return 1 ; } NFChipEn(); //打开Nand Flash片选 NF_CLEAR_RB(); //清RnB信号 WrNFCmd(READCMD0); //页读命令周期1 //写入5个地址周期 WrNFAddr(0x00); //列地址A0~A7 WrNFAddr(0x00); //列地址A8~A11 WrNFAddr((page_number) & 0xff); //行地址A12~A19 WrNFAddr((page_number >> 8) & 0xff); //行地址A20~A27 WrNFAddr((page_number >> 16) & 0xff); //行地址A28 WrNFCmd(READCMD1); //页读命令周期2 NF_DETECT_RB(); //等待RnB信号变高,即不忙 WrNFCmd(RANDOM_READCMD0); //随意读命令周期1 //页内地址 WrNFAddr( addr_in_page&0xff ); //列地址A0~A7 WrNFAddr( (addr_in_page>>8)&0x0f ); //列地址A8~A11 WrNFCmd(RANDOM_READCMD1); //随意读命令周期2 while(len--){ *bufr++ = RdNFDat(); //读取数据 } NFChipDs(); return 0 ; } //http://www.cnblogs.com/idle_man/archive/2010/12/23/1915303.html #define getnum(i) {\ mygetstring(string) ;\ i = myatoi(string ) ;\ } void nandNew(void) { int ret , n ,i , j ,k; U32 nID; U8 MID , PID , cyc3,cyc4,cyc5 ; char dat ; char ch ; U8 uch; char string[1000] = {0}; char bufw[2048] = {1 , 1, 1}; char bufr[2048+64] = {0}; memset(bufw , 0xaa , 2048); while(1) { debugs("\n------------------------------------------------\n"); debugs("nand menu\n"); debugs("input num to select:\n"); debugs("now will operate block %d,its addr is %x\n",AppParaBpageS,AppParaBaseNF);//page num = 34816 debugs("0 sysHardwareReset\n"); debugs("1 nand EraseBlock\n"); debugs("2 nand MY_WritePage caculate ecc\n"); debugs("3 nand ReadPageAll\n"); debugs("4 nand my_cop\n"); debugs("5 nand CheckBadBlk\n"); debugs("6 nand MY_ReadPage check ecc\n"); debugs("------------------------------------------------\n"); ch = mygetc(); switch ( ch - '0' ) { case 0 : sysHardwareReset(); break; case 1 : ret = EraseBlock(AppParaBpageS ); if(ret&0x1 ==1 ) { debugs("EraseBlock ok!\n"); } else { debugs("EraseBlock fail!\n"); } break; case 2 : ret = MY_WritePage(AppParaBpageS, bufw); if(ret&0x1 ==1 ) { debugs("WritePage ok!\n"); } else { debugs("WritePage fail!\n"); } break; case 3: if(1){ int j ; int i = 34816 ; char buf[2047] = {4,5,6,7,8,9,10,11,12}; buf[2044] = 11; buf[2045] = 12; buf[2046] = 13; memset(bufr, 0 , sizeof(bufr)); ReadPageAll(i , bufr); ret = memcmp(buf , bufr+1 , 2047); debugs("ret of memcmp %x\n" , ret); arryprintfs("ReadPage+0" ,bufr ,2048 ); arryprintfs("ReadPage+2048" ,bufr+2048 ,64 ); } break; case 4: if(1){ int time = 0; int nPage = 34816 , nAddr = 1; char rbufw[10] = {1, 2, 3 ,4 ,5 ,6 ,7 ,8 ,9 ,10}; ret = RandomWrite(nPage , nAddr, rbufw , 10); if(ret&0x1 ==1 ) { debugs("WritePage ok!\n"); } else { debugs("WritePage fail!\n"); } for(time = 0 ; time<10 + 7; time++){ ret = 0x11; ret = RandomRead(nPage ,nAddr , bufr , 2048+63); debugs("RamdomRead(page %d, in_page_addr%d ) ,ret = 0x%x\n" ,nPage , nAddr, ret); arryprintfs("RamdomRead bufr" ,bufr ,2048+63 ) ; nAddr++; } } /* ----------------prints--------------------------- WritePage ok! RamdomRead(page 34816, in_page_addr1 ) ,uch = 0x1 RamdomRead(page 34816, in_page_addr2 ) ,uch = 0x2 RamdomRead(page 34816, in_page_addr3 ) ,uch = 0x3 RamdomRead(page 34816, in_page_addr4 ) ,uch = 0x4 RamdomRead(page 34816, in_page_addr5 ) ,uch = 0x5 RamdomRead(page 34816, in_page_addr6 ) ,uch = 0x6 RamdomRead(page 34816, in_page_addr7 ) ,uch = 0x7 RamdomRead(page 34816, in_page_addr8 ) ,uch = 0x8 RamdomRead(page 34816, in_page_addr9 ) ,uch = 0x9 RamdomRead(page 34816, in_page_addr10 ) ,uch = 0xa ----------------prints--------------------------- */ break; case 5: //CheckBadBlk from blk0~blk2047 for(ret=0 ; ret<2047; ret++) { CheckBadBlk(ret<<6); //ret<<6 : page_addr } break; case 6: // if(1){ memset(bufr, 0 , sizeof(bufr)); ret = MY_ReadPage(34816, bufr); debugs("MY_ReadPage ret==%d\n" , ret); arryprintfs("ReadPage+0" ,bufr ,2048 ); } break; case 7: // if(1){ T_ecc() ; } break; } } }
#ifndef _NAND_H_ #define _NAND_H_ #include "common.h" #define N_BYTES_NAND_PAGE 2048 #define N_PAGES_NAND_BLOCK 64 #define N_BYTES_NAND_BLOCK (N_PAGES_NAND_BLOCK*N_BYTES_NAND_PAGE) #define EnNandFlash() (rNFCONT |= 1) #define DsNandFlash() (rNFCONT &= ~1) #define NFChipEn() (rNFCONT &= ~(1<<1)) #define NFChipDs() (rNFCONT |= (1<<1)) #define InitEcc() (rNFCONT |= (1<<4)) #define MEccUnlock() (rNFCONT &= ~(1<<5)) #define MEccLock() (rNFCONT |= (1<<5)) #define SEccUnlock() (rNFCONT &= ~(1<<6)) #define SEccLock() (rNFCONT |= (1<<6)) #define WrNFDat8(dat) (rNFDATA8 = (dat)) #define WrNFDat32(dat) (rNFDATA = (dat)) #define RdNFDat8() (rNFDATA8) //byte access #define RdNFDat32() (rNFDATA) //word access #define WrNFCmd(cmd) (rNFCMD = (cmd)) #define WrNFAddr(addr) (rNFADDR = (addr)) #define WrNFDat(dat) WrNFDat8(dat) #define RdNFDat() RdNFDat8() //for 8 bit nand flash, use byte access #define RdNFMEcc() (rNFMECC0) //for 8 bit nand flash, only use NFMECC0 #define RdNFSEcc() (rNFSECC) //for 8 bit nand flash, only use low 16 bits #define RdNFStat() (rNFSTAT) #define NFIsBusy() (!(rNFSTAT&1)) #define NFIsReady() (rNFSTAT&1) #define READCMD0 0 #define READCMD1 0x30 #define ERASECMD0 0x60 #define ERASECMD1 0xd0 #define PROGCMD0 0x80 #define PROGCMD1 0x10 #define QUERYCMD 0x70 #define RdIDCMD 0x90 #define RESET_CMD 0xff #define RANDOM_READCMD0 0x05 #define RANDOM_READCMD1 0xe0 #define RANDOM_PROGRAMCMD 0x85 #define TACLS 1//7 // 1-clk(0ns) #define TWRPH0 4//7 // 3-clk(25ns) #define TWRPH1 1//7 // 1-clk(10ns) //TACLS+TWRPH0+TWRPH1>=50ns //func extern void ReadBlock(U32 addr, U8 buf[64][2048]) ; //pgaddr extern U32 EraseBlock(U32 addr) ; //pgaddr extern void WriteBlock(U32 addr, U8 buf[64][2048]); //pgaddr extern void ReadPage(U32 addr, U8 *buf); //pgaddr extern U32 WritePage(U32 addr, U8 *buf); //pgaddr extern int getBlockNum(u32 addr ,u32 BlockNum[1]); //0x_addr extern int getPageNum(u32 addr ,u32 PageNum[1]); //0x_addr extern U32 ReadChipId(void); extern int CheckNandflash(int info); #endif