您的位置:首页 > 其它

WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(18)-Nand Flash驱动(FMD)及其简析(1)

2010-04-27 11:52 615 查看
CE下的Nand Flash使用了很多,eboot存储及其配置信息到Nand,CE Image存储到Nand, Nand上实现BINFS和FAT分区.这些都离不开Nand驱动的支持.在SMDK2410的BSP提供了FMD,编译成库和dll供eboot和OS调用.在直接移植到GEC2410开发板时,并不能正常使用,特别是erase的时候.经检查后发现需要增加了一些Nand命令才能正常完成操作.也许原来针对的Nand Flash命令或运行模式有所区别.GEC2410上用的K9F1208(64MB)的Nand Flash.之前的文章一直没有提及,接下来就对FMD库,Nand操作,以及针对K9F1208的修改作下详细介绍.

该驱动位于/SRC/COMMON/SMARTMEDIA, 文件夹FMD里的是Nand驱动代码,编译为Lib;DLL目录里只有source文件和def文件,source文件使用前面的lib库编译成dll,def文件定义了导出函数.在eboot里面使用的就是库smflash_lib.lib,而在CE下使用的就是smflash.dll.

2410的NAND控制器有6个寄存器,分别是NFCONF(配置寄存器),NFCMD(命令寄存器),NFADDR(地址寄存器),NFDATA(数据寄存器),NFSTAT(状态寄存器),NFECC(错误校准寄存器),寄存器详细定义可参考2410的datasheet.
K9F1208的命令如下图:



在nand.h我们定义为:
#define CMD_READID              0x90        //  ReadID
#define CMD_READ                0x00        //  Read
#define CMD_READ2               0x50        //  Read2
#define CMD_RESET               0xff        //  Reset
#define CMD_ERASE               0x60        //  Erase phase 1
#define CMD_ERASE2              0xd0        //  Erase phase 2
#define CMD_WRITE               0x80        //  Write phase 1
#define CMD_WRITE2              0x10        //  Write phase 2
#define CMD_STATUS              0x70        //  CMD_STATUS add
#define VALIDADDR               0x05        //  VALIDADDR  add

其中CMD_STATUS和VALIDADDR原来的文件中没有定义,而实际上我们是需要用这两个命令的,后面可以看到.
以下是K9F1208的配置信息, 1个Block有32个Page,每个Page512字节.
#define NAND_BLOCK_CNT          (1024 * 4)      /* Each Plane has 1024 Blocks   */
#define NAND_PAGE_CNT           (32)            /* Each Block has 32 Pages      */
#define NAND_PAGE_SIZE          (512)           /* Each Page has 512 Bytes      */
#define NAND_BLOCK_SIZE         (NAND_PAGE_CNT * NAND_PAGE_SIZE)


我们的FMD的NAND驱动是个标准的流接口驱动,我们来看看具体实现:
1.寄存器操作函数
这里定义了如何读写NAND寄存器,如命令,地址,数据,状态,复位等.
#define NF_CMD(cmd)	            {s2410NAND->NFCMD   =  (cmd);}
#define NF_ADDR(addr)	        {s2410NAND->NFADDR  =  (unsigned char)(addr);}
#define NF_nFCE_L()	            {s2410NAND->NFCONF &= ~(1 << 11);}
#define NF_nFCE_H()	            {s2410NAND->NFCONF |=  (1 << 11);}
#define NF_RSTECC()	            {s2410NAND->NFCONF |=  (1 << 12);}
#define NF_RDDATA() 	        (s2410NAND->NFDATA)
#define NF_WRDATA(data)         {s2410NAND->NFDATA  =  (data);}
#define NF_WAITRB()             {while (!(s2410NAND->NFSTAT & (1 << 0)));}

2.读写数据
这几个函数是在nand.s中汇编实现,进行了读写Page数据和PageInfo的操作,之后在详细分析.
extern "C" void RdPage512(unsigned char *bufPt);
extern "C" void RdPage512Unalign(unsigned char *bufPt);
extern "C" void WrPage512(unsigned char *bufPt);
extern "C" void WrPage512Unalign(unsigned char *bufPt);
extern "C" void WrPageInfo(PBYTE pBuff);
extern "C" void RdPageInfo(PBYTE pBuff);

3.ReadFlashID
ReadFlashID在eboot里会被调用来检测Nand是否初始化成功.
具体时序如datasheet所述.



static DWORD ReadFlashID(void)
{
BYTE Mfg, Dev;

NF_CMD(CMD_READID);        // Send flash ID read command.
NF_ADDR(0);                //
NF_WAITRB();            // Wait for flash to complete command.
Mfg    = NF_RDDATA();    //
Dev    = NF_RDDATA();    //

//RETAILMSG(1,(TEXT("FMD: ReadID (Mfg=%x, Dev=%x)/r/n"), Mfg, Dev));

return ((DWORD)Mfg*0x100+Dev);
}

4.IsBlockBad
IsBlockBad用来判断指定的Block是否是坏块.坏块信息存储在每个Block第一第二个Sector(Page)的16个字节的保留区,其中坏块状态就在第6个字节,通过READ2命令读取,如果为FF则为坏块.该函数被FMD_GetBlockStatus调用.
判断过程流程图如下:



具体实现代码:
static BOOL IsBlockBad(BLOCK_ID blockID)
{
BYTE Data;
SECTOR_ADDR blockPage = (blockID * NAND_PAGE_CNT);

BOOL bLastMode = SetKMode(TRUE);

NF_nFCE_L();            // Select the flash chip.
NF_CMD(CMD_READ2);        // Send read confirm command.
NF_ADDR(VALIDADDR);                            // Column = 0.
NF_ADDR(blockPage         & 0xff);    /* The mark of bad block is in 0 page   */
NF_ADDR((blockPage >>  8) & 0xff);  /* For block number A[24:17]            */
NF_ADDR((blockPage >> 16) & 0xff);  /* For block number A[25]               */
NF_WAITRB();            // Wait for flash to complete command.

// TODO
Data = NF_RDDATA();        // Read command status.
Data = NF_RDDATA();        // Read command status.
Data = NF_RDDATA();        // Read command status.
Data = NF_RDDATA();        // Read command status.
Data = NF_RDDATA();        // Read command status.
Data = NF_RDDATA();        // Read command status.

if(0xff != Data)
{
SetKMode (bLastMode);
return(TRUE);
}

NF_nFCE_H();            // Deassert the flash chip.

SetKMode (bLastMode);

return(FALSE);
}

5.MarkBlockBad
MarkBlockBad用来标记坏块,在erase一个block失败后,用来标记该块为坏块,这样下次用IsBlockBad判断时就能指定该块是否是坏块了.就是通过标记保留区第6个字节为0,这样IsBlockBad就通过该字节判断块的好坏了.该函数被FMD_SetBlockStatus调用.
对应的写操作流程图,之后写数据的流程也是一样的:



MarkBlockBad:
static BOOL MarkBlockBad(BLOCK_ID blockID)
{
BYTE Status;
ULONG blockPage = (blockID * NAND_PAGE_CNT);    // Convert block address to page address.

BOOL bLastMode = SetKMode(TRUE);

NF_nFCE_L();            // Select the flash chip.
NF_CMD(CMD_RESET);        // Send reset command.
NF_WAITRB();            // Wait for flash to complete command.
NF_CMD(CMD_READ2);        // Send read confirm command.
NF_CMD(CMD_WRITE);        // Send write command.
NF_ADDR(0);                            // Column = 0.
NF_ADDR(blockPage         & 0xff);    /* The mark of bad block is in 0 page   */
NF_ADDR((blockPage >>  8) & 0xff);  /* For block number A[24:17]            */
NF_ADDR((blockPage >> 16) & 0xff);  /* For block number A[25]               */

// TODO
NF_WRDATA(0xFF);            // Write bad block marker.
NF_WRDATA(0xFF);            // Write bad block marker.
NF_WRDATA(0xFF);            // Write bad block marker.
NF_WRDATA(0xFF);            // Write bad block marker.
NF_WRDATA(0xFF);            // Write bad block marker.

NF_WRDATA(0);            // Write bad block marker.
NF_CMD(CMD_WRITE2);        // Send write confirm command.
NF_WAITRB();            // Wait for flash to complete command.

NF_CMD(CMD_STATUS);
Status = NF_RDDATA();    // Read command status.
NF_nFCE_H();            // Deassert the flash chip.

SetKMode (bLastMode);
return((Status & 1) ? FALSE : TRUE);
}

6.FMD_GetInfo
FMD_GetInfo用来获得该Nand的信息,如类型,Sector大小,Block大小等,直接赋值给PFlashInfo即可.
BOOL FMD_GetInfo(PFlashInfo pFlashInfo)
{

if (!pFlashInfo)
return(FALSE);

pFlashInfo->flashType            = NAND;
pFlashInfo->wDataBytesPerSector = NAND_PAGE_SIZE;
pFlashInfo->dwNumBlocks         = NAND_BLOCK_CNT;
pFlashInfo->wSectorsPerBlock    = NAND_PAGE_CNT;
pFlashInfo->dwBytesPerBlock        = (pFlashInfo->wSectorsPerBlock * pFlashInfo->wDataBytesPerSector);

return(TRUE);
}

7.FMD_GetBlockStatus
FMD_GetBlockStatus用来获取Block的状态,看是否是Bad Block,读取sectorInfo获得其他状态信息,如是否只读等代码,用到的FMD_ReadSector后面在介绍,代码如下:
DWORD FMD_GetBlockStatus(BLOCK_ID blockID)
{
SECTOR_ADDR Sector = (blockID * NAND_PAGE_CNT);
SectorInfo SI;
DWORD dwResult = 0;

if (IsBlockBad(blockID))
return BLOCK_STATUS_BAD;

if (!FMD_ReadSector(Sector, NULL, &SI, 1))
return BLOCK_STATUS_UNKNOWN;

if (!(SI.bOEMReserved & OEM_BLOCK_READONLY))
dwResult |= BLOCK_STATUS_READONLY;

if (!(SI.bOEMReserved & OEM_BLOCK_RESERVED))
dwResult |= BLOCK_STATUS_RESERVED;

return(dwResult);
}

8.FMD_SetBlockStatus
FMD_SetBlockStatus用来设置Block状态,如在Erase Block失败后调用,用来标记坏块.还可以设置sectorInfor信息,用到的FMD_WriteSector同样在后面介绍.代码如下:
BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
if (dwStatus & BLOCK_STATUS_BAD)
{
if (!MarkBlockBad(blockID))
return(FALSE);
}

if (dwStatus & (BLOCK_STATUS_READONLY | BLOCK_STATUS_RESERVED)) {

SECTOR_ADDR Sector = blockID * NAND_PAGE_CNT;
SectorInfo SI;

if (!FMD_ReadSector(Sector, NULL, &SI, 1)) {
return FALSE;
}

if (dwStatus & BLOCK_STATUS_READONLY) {
SI.bOEMReserved &= ~OEM_BLOCK_READONLY;
}

if (dwStatus & BLOCK_STATUS_RESERVED) {
SI.bOEMReserved &= ~OEM_BLOCK_RESERVED;
}

if (!FMD_WriteSector (Sector, NULL, &SI, 1)) {
return FALSE;
}

}

return(TRUE);
}

下一篇就来介绍dll的导出函数,也就是对应的流接口函数.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐