📄 fmd.c
字号:
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 2002 Microsoft Corporation
Module Name: FMD.CPP
Abstract: FLASH Media Driver Interface Samsung K9F2808UOB NAND Flash Chip
on AT-Rise development board.
Notes: Currently, *only* the CE 3.0 (and earlier) power management logic
is implemented (i.e. the PowerUp() and PowerDown() APIs).
Environment: As noted, this media driver works on behalf of the FAL to directly
access the underlying FLASH hardware. Consquently, this module
needs to be linked with FAL.LIB to produce the device driver
named FLASHDRV.DLL.
-----------------------------------------------------------------------------*/
#include "fmd.h"
#define BOOT_LOADER 1
#define EdbgOutputDebugString Uart_Printf
typedef BYTE /*near*/ *PBYTE;
// Use Macros here to avoid extra over head for c function calls
#define READ_REGISTER_BYTE(p) (*(volatile PBYTE) (p))
#define WRITE_REGISTER_BYTE(p, v) (*(volatile PBYTE)(p)) = (v)
#define READ_REGISTER_USHORT(p) (*(volatile PUSHORT) (p))
#define WRITE_REGISTER_USHORT(p, v) (*(volatile PUSHORT)(p)) = (v)
#define READ_REGISTER_ULONG(p) (*(volatile PULONG) (p))
#define WRITE_REGISTER_ULONG(p, v) (*(volatile PULONG)(p)) = (v)
// Registers
volatile PUSHORT pNFReg;
volatile PUSHORT pNFCONF;
volatile PUSHORT pNFCONT;
volatile PUSHORT pNFCMD;
volatile PUSHORT pNFADDR;
volatile PUSHORT pNFDATA;
volatile PUSHORT pNFSTAT;
volatile PULONG pNFMECC0;
volatile PULONG pNFSECC;
#define NFDATA 0x4E000010
// Status bit pattern
#define STATUS_READY 0x40
#define STATUS_ERROR 0x01
// HCLK=133Mhz
#define TACLS 0
#define TWRPH0 6
#define TWRPH1 2
#define CMD_READ 0x00 // Read
#define CMD_READ1 0x01 // Read1
#define CMD_READ2 0x30 // Read2
#define CMD_READID 0x90 // ReadID
#define CMD_WRITE 0x80 // Write phase 1
#define CMD_WRITE2 0x10 // Write phase 2
#define CMD_ERASE 0x60 // Erase phase 1
#define CMD_ERASE2 0xd0 // Erase phase 2
#define CMD_STATUS 0x70 // Status read
#define CMD_RESET 0xff // Reset
// MACROS
#define NF_CE_L() WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) & ~(1<<1)))
#define NF_CE_H() WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) | (1<<1)))
#define NF_CMD(cmd) WRITE_REGISTER_USHORT(pNFCMD, (USHORT) (cmd))
#define NF_ADDR(addr) WRITE_REGISTER_USHORT(pNFADDR, (USHORT) (addr))
#define NF_DATA_R() READ_REGISTER_BYTE(pNFDATA)
#define NF_DATA_W(val) WRITE_REGISTER_BYTE(pNFDATA, (BYTE) (val))
#define NF_DATA_R4() READ_REGISTER_ULONG(pNFDATA)
#define NF_DATA_W4(val) WRITE_REGISTER_ULONG(pNFDATA, (ULONG) (val))
#define NF_STAT() READ_REGISTER_USHORT(pNFSTAT)
#define NF_MECC_UnLock() WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) & ~(1<<5)))
#define NF_MECC_Lock() WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) | (1<<5)))
#define NF_RSTECC() WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) | (1<<4)))
#define NF_WAITRB() {while(!(NF_STAT() & (1<<1))) ;}
#define NF_CLEAR_RB() WRITE_REGISTER_USHORT(pNFSTAT, (USHORT) (READ_REGISTER_USHORT(pNFSTAT) | (1<<2)))
#define NF_DETECT_RB() {while(!(NF_STAT() & (1<<2)));}
#define NF_ECC() READ_REGISTER_ULONG(pNFMECC0)
// External function
extern BOOL ECC_CorrectData(LPBYTE pData, LPBYTE pExistingECC, LPBYTE pNewECC);
// Flags and pointers
#define BADBLOCKMARK 0x00
// VALIDADDR is 5 << 8
//
// Explain: 5 means the 6th byte in spare area (517 byte in the sector)
// Shift 8 bit to the left to form the correct address for 16bit port
//
#define VALIDADDR 0x05
#define OEMADDR 0x04 // 5th byte in spare area
// Reset the chip
//
void NF_Reset()
{
int i;
NF_CE_L();
NF_CLEAR_RB();
NF_CMD(CMD_RESET);
for(i=0;i<10;i++); //tWB = 100ns. //??????
NF_DETECT_RB();
NF_CE_H();
}
/*
Here is the statement from SamSung's K9F2808U0B datasheet:
All device locations are erased (FFh) except location where the invalid block(s)
information is written prior to shipping. The invalid block(s) status is defined
by the 6th byte in the spare area. Samsung makes sure that either the 1st or 2nd
page of every invalid block has non-FFh data at the column address of 517. Since
the invalid block information is also erasable in most cases, it is impossible to
recover the information once it has been erased.
Here is the logic we are taking:
If the block is invalid, non-FFh value in column address of 517, we don't use that
block anyway. Otherwise, the whole spare area is subject to use by our program. If
we found additional bad block later on, we don't necessary have to use column 517
to represent invalid block. We could use any of the 16 byte spare area.
But for simplicity, in our current implementation, we avoid to touch column 517. So
the we allocate SectorInfo as following:
- - - - - - - - - - - - - - - -
|R|R|R|R|O|V|R|R|E|E|E|E|E|E|E|E|
- - - - - - - - - - - - - - - -
Where Rs are reserved bytes used by the FAL
O is a byte for use by the OEM
V is a byte indicating if the block is valid (a.k.a. bad)
Es are bytes used for ECC
*/
/*
* NAND_ReadSectorInfo
*
* Read SectorInfo out of the spare area. The current implementation only handles
* one sector at a time.
*/
void NAND_ReadSectorInfo(SECTOR_ADDR sectorAddr, PSectorInfo pInfo)
{
// RETAILMSG(1, (TEXT("FlashDrv!FMD!NAND_ReadSectorInfo: \r\n")));
// Chip enable
NF_CE_L();
NF_CLEAR_RB();
// Write the command
NF_CMD(CMD_READ);
// Write the address
NF_ADDR(2048&0xFF);
NF_ADDR((2048>>8)&0xFF);
NF_ADDR(sectorAddr & 0xff);
NF_ADDR((sectorAddr >> 8) & 0xff);
NF_ADDR((sectorAddr >> 16) & 0xff);
NF_CMD(CMD_READ2);
// if (NEED_EXT_ADDR) {
// NF_ADDR((sectorAddr >> 16) & 0xff);
// }
// RETAILMSG(1, (TEXT("sectorAddr : %x \r\n"), sectorAddr));
// Wait for the Ready bit
NF_DETECT_RB(); // Wait tR(max 12us)
// Read the SectorInfo data (we only need to read first 8 bytes)
pInfo->dwReserved1 = NF_DATA_R4();
// RETAILMSG(1, (TEXT("pInfo->dwReserved1 = %x\r\n"), pInfo->dwReserved1));
// OEM byte
pInfo->bOEMReserved = (BYTE) NF_DATA_R();
// RETAILMSG(1, (TEXT("pInfo->bOEMReserved = %x\r\n"), pInfo->bOEMReserved));
// Read the bad block mark
pInfo->bBadBlock = (BYTE) NF_DATA_R();
// RETAILMSG(1, (TEXT("pInfo->bBadBlock = %x\r\n"), pInfo->bBadBlock));
// Second reserved field (WORD)
pInfo->wReserved2 = ((BYTE) NF_DATA_R() << 8);
pInfo->wReserved2 |= ((BYTE) NF_DATA_R());
// RETAILMSG(1, (TEXT("pInfo->wReserved2 = %x\r\n"), pInfo->wReserved2));
// RETAILMSG(1, (TEXT("\r\n")));
NF_CE_H();
}
/*
* NAND_WriteSectorInfo
*
* Write SectorInfo out to the spare area. The current implementation only handles
* one sector at a time.
*/
BOOL NAND_WriteSectorInfo(SECTOR_ADDR sectorAddr, PSectorInfo pInfo)
{
BOOL bRet = TRUE;
// RETAILMSG(1, (TEXT("FlashDrv!FMD!NAND_WriteSectorInfo: \r\n")));
// Chip enable
NF_CE_L();
NF_CLEAR_RB();
// Write the command
// First, let's point to the spare area
// NF_CMD(CMD_READ2);
NF_CMD(CMD_WRITE);
// Write the address
NF_ADDR(2048&0xFF);
NF_ADDR((2048>>8)&0xFF);
NF_ADDR(sectorAddr & 0xff);
NF_ADDR((sectorAddr >> 8) & 0xff);
NF_ADDR((sectorAddr>>16)&0xff);
// if (NEED_EXT_ADDR) {
// NF_ADDR((sectorAddr >> 16) & 0xff);
// }
// Now let's write the SectorInfo data
//
// Write the first reserved field (DWORD)
NF_DATA_W4( pInfo->dwReserved1 );
// Write OEM reserved flag
NF_DATA_W( (pInfo->bOEMReserved) );
// Write the bad block flag
NF_DATA_W( (pInfo->bBadBlock) );
// Write the second reserved field
NF_DATA_W( (pInfo->wReserved2 >> 8) & 0xff );
NF_DATA_W( (pInfo->wReserved2) );
// Issue the write complete command
NF_CMD(CMD_WRITE2);
// Check ready bit
NF_DETECT_RB(); // Wait tR(max 12us)
// Check the status of program
NF_CMD(CMD_STATUS);
if(NF_DATA_R() & STATUS_ERROR) {
Uart_Printf("NAND_WriteSectorInfo() ######## Error Programming page %d!\n", sectorAddr);
bRet = FALSE;
}
NF_CE_H();
return bRet;
}
/*-----------------------------------------------------------------------------
* FMD Interface functions
*
*----------------------------------------------------------------------------*/
// FMD_Init
//
// Initialize the flash chip
//
// Note: Presently, the Flash size characteristics are hardcoded in CFNAND.H
// and are NOT stored in the registry. Refer to the StratFlash FMD in
// %WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\BLOCK\... for an example of how
// to use the registry for storing this information.
//
PVOID FMD_Init()//LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
// 0. Create the Mutex for shared access between the kernel and MSFLASH
// RETAILMSG(1, (TEXT("FMD::FMD_Init:VirtualAlloc \r\n")));
pNFReg = (PUSHORT)0x4E000000;
pNFCONF = pNFReg;
pNFCONT = (PUSHORT) ((PBYTE) pNFReg + 0x04);
pNFCMD = (PUSHORT) ((PBYTE) pNFReg + 0x08);
pNFADDR = (PUSHORT) ((PBYTE) pNFReg + 0x0C);
pNFDATA = (PULONG) ((PBYTE) pNFReg + 0x10);
pNFSTAT = (PUSHORT) ((PBYTE) pNFReg + 0x20);
pNFMECC0 = (PULONG) ((PBYTE) pNFReg + 0x2C);
pNFSECC = (PULONG) ((PBYTE) pNFReg + 0x34);
// Now we need enable the NAND Flash controller
#if ID_NAND == ID_K9F1G08U0A
WRITE_REGISTER_USHORT(pNFCONF, (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(1<<3)|(1<<2) |(0<<0));
#elif ID_NAND == ID_K9K8G08U0A
WRITE_REGISTER_USHORT(pNFCONF, (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(1<<3)|(1<<2)|(1<<1)|(0<<0));
#elif ID_NAND == ID_K9S1208V0M
WRITE_REGISTER_USHORT(pNFCONF, (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4) | (0<<0));
#endif
WRITE_REGISTER_USHORT(pNFCONT, (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0));
WRITE_REGISTER_USHORT(pNFSTAT, 0);
return (PVOID)pNFCONF;
ErrExit:
return 0;
}
// FMD_GetInfo
//
// Return the Flash information
//
BOOL FMD_GetInfo(PFlashInfo pFlashInfo)
{
pFlashInfo->flashType = NAND;
// OK, instead of reading it from the chip, we use the hardcoded
// numbers here.
pFlashInfo->dwNumBlocks = NUM_BLOCKS;
pFlashInfo->wSectorsPerBlock = PAGES_PER_BLOCK;
pFlashInfo->wDataBytesPerSector = SECTOR_SIZE;
return TRUE;
}
// FMD_ReadSector
//
// Read the content of the sector.
//
// startSectorAddr: Starting page address
// pSectorBuff : Buffer for the data portion
// pSectorInfoBuff: Buffer for Sector Info structure
// dwNumSectors : Number of sectors
//
typedef union _ECCRegVal
{
DWORD dwECCVal;
BYTE bECCBuf[4];
} ECCRegVal;
BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff,
PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
DWORD i;
BYTE eccBuf[8];
ECCRegVal eccRegVal;
// RETAILMSG(1, (TEXT("FlashDrv!FMD!FMD_ReadSector: startSectorAddr = %x \r\n"), startSectorAddr));
// Sanity check
if (!pSectorBuff && !pSectorInfoBuff || dwNumSectors > 1) {
#ifdef BOOT_LOADER
EdbgOutputDebugString("Invalid parameters!\r\n");
#else
// RETAILMSG(1, (TEXT("Invalid parameters!\n")));
#endif
return FALSE;
}
NF_Reset();
if(!pSectorBuff) {
// We are reading spare only
NAND_ReadSectorInfo(startSectorAddr, pSectorInfoBuff);
// There is no ECC for the sector info, so the read always succeed.
return TRUE;
}
// RETAILMSG(1, (TEXT("FlashDrv!FMD!FMD_ReadSector: startSectorAddr = %x \r\n"), startSectorAddr));
// Initialize ECC register
NF_RSTECC();
NF_MECC_UnLock();
// Enable the chip
NF_CE_L();
NF_CLEAR_RB();
// Issue command
NF_CMD(CMD_READ);
// Set up address
NF_ADDR(0x00);
NF_ADDR(0x00);
NF_ADDR((startSectorAddr) & 0xff);
NF_ADDR((startSectorAddr >> 8) & 0xff);
NF_ADDR((startSectorAddr>>16)&0xff);
NF_CMD(CMD_READ2);
// if (NEED_EXT_ADDR) {
// NF_ADDR((startSectorAddr >> 16) & 0xff);
// }
NF_DETECT_RB(); // Wait tR(max 12us)
// BUGBUG, because Media Player for Pocket PC sometimes pass us un-aligned buffer
// we have to waste cycle here to work around this problem
if(1){// ((DWORD) pSectorBuff) & 0x3) {
for(i=0; i<SECTOR_SIZE; i++) {
pSectorBuff[i] = (BYTE) NF_DATA_R();
}
}
else {
// The right way.
// __RdPage2048(pSectorBuff);
}
/*
for ( i = 0 ; i < 512; i++ )
#ifdef BOOT_LOADER
EdbgOutputDebugString("%x ", *(pSectorBuff+i));
#else
RETAILMSG(1, (TEXT("%x "), *(pSectorBuff+i)));
#endif
*/
// Do the ECC thing here
// We read the ECC value from the ECC register pFNECC
NF_MECC_Lock();
eccRegVal.dwECCVal = NF_ECC();
// Read the SectorInfo data
if(pSectorInfoBuff) {
// Read the SectorInfo data (we only need to read first 8 bytes)
pSectorInfoBuff->dwReserved1 = NF_DATA_R4();
// OEM byte
pSectorInfoBuff->bOEMReserved = (BYTE) NF_DATA_R();
// Read the bad block mark
pSectorInfoBuff->bBadBlock = (BYTE) NF_DATA_R();
// Second reserved field (WORD)
pSectorInfoBuff->wReserved2 = ((BYTE) NF_DATA_R() << 8);
pSectorInfoBuff->wReserved2 |= ((BYTE) NF_DATA_R());
}
else {
// Advance the read pointer
for(i=0; i<sizeof(SectorInfo); i++) {
eccBuf[i] = (BYTE) NF_DATA_R();
}
}
// Verify the ECC values
//
// Read the ECC buffer
for(i=0; i<3; i++) {
eccBuf[i] = (BYTE) NF_DATA_R();
}
NF_CE_H();
// Copmare with the ECC generated from the HW
if(eccBuf[0] != eccRegVal.bECCBuf[0] ||
eccBuf[1] != eccRegVal.bECCBuf[1] ||
eccBuf[2] != eccRegVal.bECCBuf[2] ) {
#ifdef BOOT_LOADER
EdbgOutputDebugString("FMD: ECC ERROR - Page #: %d\r\n", startSectorAddr);
EdbgOutputDebugString("(%x:%x %x:%x %x:%x)\r\n", eccBuf[0], eccRegVal.bECCBuf[0], eccBuf[1], eccRegVal.bECCBuf[1], eccBuf[2], eccRegVal.bECCBuf[2]);
#else
Uart_Printf("FMD: ECC ERROR - Page #: %d\r\n", startSectorAddr);
#endif
// Now try to correct them
if(!ECC_CorrectData(pSectorBuff, eccBuf, eccRegVal.bECCBuf)) {
Uart_Printf("FMD: Unable to correct the ECC error - Page #: %d\r\n",
startSectorAddr);
return FALSE;
}
}
return TRUE;
}
//
// IsBlockBad
//
// Check to see if the given block is bad. A block is bad if the 517th byte on
// the first or second page is not 0xff.
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -