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

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调用.
判断过程流程图如下:
WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(18)-Nand Flash驱动(FMD)及其简析(1)_第1张图片
具体实现代码:
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调用.
对应的写操作流程图,之后写数据的流程也是一样的:
WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(18)-Nand Flash驱动(FMD)及其简析(1)_第2张图片
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的导出函数,也就是对应的流接口函数.

你可能感兴趣的:(cmd,command,Flash,dll,byte,WinCE)