Nandflash驅動移植系列文章導航:
Nandflash 驅動移植 (一)
Nandflash 驅動移植 (二)
Nandflash 驅動移植 (三)
Nandflash 驅動移植 (四)
Nandflash 驅動移植 (五)
Nandflash 驅動移植 (六)
一共六篇
接著上一篇
1、ECC_CorrectData() 尋找ECC錯誤並矯正
BOOL ECC_CorrectData(SECTOR_ADDR sectoraddr, LPBYTE pData, UINT32 nRetEcc, ECC_CORRECT_TYPE nType){DWORD nErrDataNo;DWORD nErrBitNo;//BYTE Status;BYTE nErrDataNum;UINT8 nErrByteNum;UINT8 countdown = 155;BOOL bRet = TRUE;//RETAILMSG(1, (TEXT("#### FMD_DRIVER:::ECC_CorrectData %x, %x, %x\n"), sectoraddr, nRetEcc, nType));#if 0if( (nRetEcc & NF_ECC8ERR0_ECC_READY) )return TRUE;#endif// 8bit ECC error searching engine needs mini mum 372 cycles to find any errorcountdown = 372;while(countdown--);
// 等待ECC錯誤尋找完畢while(NF_ECC8_ERR0 & 0x80000000);
// 擷取8bit ECC解碼結果nErrDataNum = NF_ECC8BIT_NUM;// No error, if free page (all 0xff)if( (g_pNFConReg->NF8ECCERR0 >> 29) & 0x1 ){nErrDataNum = 0;}if (nErrDataNum == 0){bRet = TRUE;RETAILMSG(0,(TEXT("No Error\n")));goto finished;}else if (nErrDataNum == 9){bRet = FALSE;RETAILMSG(1,(TEXT("More than 8-bit error, uncorrectable\n")));goto finished;}else if (nErrDataNum > 9){bRet = FALSE;RETAILMSG(1,(TEXT("Reserved\n")));goto finished;}else{
// 擷取錯誤位對應的位置for (nErrByteNum = 1; nErrByteNum <= nErrDataNum; nErrByteNum++){switch(nErrByteNum){case 1:nErrDataNo = NF_ECC8LOCATION_BYTE1;break;case 2:nErrDataNo = NF_ECC8LOCATION_BYTE2;break;case 3:nErrDataNo = NF_ECC8LOCATION_BYTE3;break;case 4:nErrDataNo = NF_ECC8LOCATION_BYTE4;break;case 5:nErrDataNo = NF_ECC8LOCATION_BYTE5;break;case 6:nErrDataNo = NF_ECC8LOCATION_BYTE6;break;case 7:nErrDataNo = NF_ECC8LOCATION_BYTE7;break;case 8:nErrDataNo = NF_ECC8LOCATION_BYTE8;break;default:break;}
// 定位到具體錯誤位的位置nErrBitNo = NF_ECC8LOCATION_BIT(nErrByteNum);
// 矯正錯誤位(pData)[nErrDataNo] ^= (1<<nErrBitNo); RETAILMSG(1, (TEXT("8bit ECC_CorrectData %x, %x, %x, %x\n"), nErrDataNum, nErrByteNum, nErrDataNo, nErrBitNo));}}finished: return bRet;}
這裡是修改後的,支援8bit ECC校正。
注意到上面的
// No error, if free page (all 0xff)
if( (g_pNFConReg->NF8ECCERR0 >> 29) & 0x1 ){
nErrDataNum = 0;
}
代碼了嗎?讓我們對比手冊看看這個NF8ECCERR0[29]是何許人也
看到了吧,NF8ECCERR0[29]是保留位。然而,我參考6410的MLC的BSP源碼,發現裡面有用到這個位來判斷是否全為0xff。在飛淩最新發行的linux3.0的源碼中也查看到有用到這個保留位,而且還是針對8bit ECC來使用的,參考的兩個源碼都支援這兩個Nandflash。為什麼6410的晶片文檔上會寫成是保留位?是筆誤還是有所保留?為啥三星自己的MLC的BSP中也有使用?具體大家自己糾結去吧,反正上面這樣使用了也沒見著啥不良影響。
2、FMD_LB_ReadSector()
原來的代碼:
BOOL FMD_LB_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors){ ULONG SectorAddr = (ULONG)startSectorAddr; DWORD i, j; volatile DWORD rddata; UINT32 nRetEcc = 0; DWORD MECCBuf[16],tempMECCBuf[2]; // gjl 8 UINT16 nSectorLoop,nSectorLoop1; int NewSpareAddr = 4096; //gjl 2048 int NewDataAddr = 0; int NewSectorAddr = startSectorAddr; int SectorSpareAddr; UINT8 TempSectorInfo[40];BYTE *pSectorBuff1 = (BYTE *)pSectorBuff;UINT16 k=40;#if CHECK_SPAREECC DWORD SECCBuf[4]; // gjl 2#endif#if (NAND_DEBUG) RETAILMSG(1,(TEXT("#### FMD_DRIVER:::FMD_LB_READSECTOR %x %x\n"),startSectorAddr,NewDataAddr));#endif if (!pSectorBuff && !pSectorInfoBuff) { return(FALSE); } if ( dwNumSectors > 1 ) { RETAILMSG(1, (TEXT("######## FATAL ERROR => FMD::FMD_ReadSector->dwNumsectors is bigger than 1. \n"))); return FALSE; }if (!pSectorBuff) { if (!NAND_LB_ReadSectorInfo(startSectorAddr, pSectorInfoBuff)) {#if (NAND_DEBUG) RETAILMSG(1,(TEXT("#### FMD_DRIVER:::54321\n")));#endif return FALSE; }#if (NAND_DEBUG)RETAILMSG(1,(TEXT("#### FMD_DRIVER:::12345\n")));#endif return TRUE; }NF_nFCE_L(); NF_CLEAR_RB(); NF_CMD(CMD_READ); // Send read command. NF_ADDR((NewSpareAddr)&0xff); NF_ADDR((NewSpareAddr>>8)&0xff); NF_ADDR((NewSectorAddr) & 0xff); NF_ADDR((NewSectorAddr >> 8) & 0xff);#if LB_NEED_EXT_ADDR NF_ADDR((NewSectorAddr >> 16) & 0xff);#endif NF_CMD(CMD_READ3); // 2nd command NF_DETECT_RB(); // Wait for command to complete. NF_MSGLENGTH_512(); NF_ECCTYPE_4BIT(); if (pSectorInfoBuff) { pSectorInfoBuff->bBadBlock = NF_RDDATA_BYTE(); pSectorInfoBuff->dwReserved1 = NF_RDDATA_WORD(); pSectorInfoBuff->bOEMReserved = NF_RDDATA_BYTE(); pSectorInfoBuff->wReserved2 = NF_RDDATA_BYTE(); pSectorInfoBuff->wReserved2 |= (NF_RDDATA_BYTE()<<8); } else { for(i=0; i<sizeof(SectorInfo)/sizeof(DWORD); i++) { rddata = (DWORD) NF_RDDATA_WORD(); // read and trash the data } } for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE*2; nSectorLoop++) { MECCBuf[nSectorLoop] = NF_RDDATA_WORD(); }#if DEBUG_WRITE_READ_EQUAL for (nSectorLoop = 0; nSectorLoop < 8; nSectorLoop++) { g_MECCBuf_R[nSectorLoop] = MECCBuf[nSectorLoop]; }#endif for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE; nSectorLoop++) { NewDataAddr = nSectorLoop * SECTOR_SIZE; NF_CMD(CMD_RDO); // Send read command. NF_ADDR((NewDataAddr)&0xff); NF_ADDR((NewDataAddr>>8)&0xff); NF_CMD(CMD_RDO2); // 2nd commandNF_MSGLENGTH_512();NF_ECCTYPE_4BIT(); NF_RSTECC(); NF_MECC_UnLock(); if( ((DWORD) (pSectorBuff+nSectorLoop*SECTOR_SIZE)) & 0x3) { for(i=0; i<SECTOR_SIZE/sizeof(DWORD); i++) { rddata = (DWORD) NF_RDDATA_WORD(); (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+0] = (BYTE)(rddata & 0xff); (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+1] = (BYTE)(rddata>>8 & 0xff); (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+2] = (BYTE)(rddata>>16 & 0xff); (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+3] = (BYTE)(rddata>>24 & 0xff); } } else { RdPage512(pSectorBuff+nSectorLoop*SECTOR_SIZE); // Read page/sector data. } SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8;NF_WRDATA_WORD(MECCBuf[2*nSectorLoop]);SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8+4;NF_WRDATA_WORD(MECCBuf[2*nSectorLoop+1]); NF_MECC_Lock(); //decode done while (!(NF_RDSTAT & (1<<6))); tempMECCBuf[0]= NF_RDMECC0(); tempMECCBuf[1] = NF_RDMECC1();pSectorBuff1 = pSectorBuff+nSectorLoop*SECTOR_SIZE; if (!ECC_CorrectData(startSectorAddr, pSectorBuff1, nRetEcc, ECC_CORRECT_MAIN)) {RETAILMSG(1,(TEXT("ECC ERROR\n"))); return FALSE; } } NF_nFCE_H();return TRUE;}
這個是飛淩BSP中的源碼,裡面需要修改的地方還是挺多的。
首先,來看定義部分的
DWORD MECCBuf[16],tempMECCBuf[2]; // gjl 8
由於將要用的是8bit的ECC校正,這個ECC的buffer就應該是32,1page=8*512byte,每讀取512byte資料產生的ECC存放在4個32位的寄存器中,所以需要8*4個buffer:
DWORD MECCBuf[32];
至於tempMECCBuf,從上述代碼中就可以看出就一垃圾,根本沒用到,這裡就把它刪了。
定義完之後,我們需要使能一些相關的中斷(不這樣搞的話,發現無法正常校正ECC,具體原因請知道的朋友告知一聲)
在
if (!pSectorBuff && !pSectorInfoBuff)
{
return(FALSE);
}
的後面,我們添加以下代碼:
g_pNFConReg->NFCONT |= (1<<10);// Enable illegal access interrupt controlg_pNFConReg->NFCONT |= (1<<9);// Enable RnB interruptg_pNFConReg->NFCONT |= (1<<12);// Enable 4bit,8bit ECC decoding completion interrupt control
接下來看到代碼:(中間省略的那部分就不介紹了,大家有空可以參考一下LoongEmbedded的csdn blog)
NF_MSGLENGTH_512();
NF_ECCTYPE_4BIT();
我們使用8bit ECC,所以把 NF_ECCTYPE_4BIT(); 修改成
NF_ECCTYPE_8BIT();
之後就是讀取SectorInfo資料的操作,再過來就是讀取ECC資料的操作:
for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE*2; nSectorLoop++)
{
MECCBuf[nSectorLoop] = NF_RDDATA_WORD();
}
由於,我們使用的是8bit ECC,上面唯讀取了8*2 * 4位元組的ECC,而8bit的ECC需要8*4 *4位元組,所以修改成:
for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE*4; nSectorLoop++)// 8bit ECC,4096/page = 8*512, it has 8*4(register) ECC data { MECCBuf[nSectorLoop] = NF_RDDATA_WORD(); }
接下來直接看到迴圈讀取一頁資料的操作:
就是
for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE; nSectorLoop++)
{
NewDataAddr = nSectorLoop * SECTOR_SIZE;
NF_CMD(CMD_RDO); // Send read command.
NF_ADDR((NewDataAddr)&0xff);
NF_ADDR((NewDataAddr>>8)&0xff);
NF_CMD(CMD_RDO2); // 2nd command
NF_MSGLENGTH_512();
NF_ECCTYPE_4BIT();
這裡,把上面的 NF_ECCTYPE_4BIT(); 修改成:
NF_ECCTYPE_8BIT();
順便在上面這一句後面加上兩句:
NF_ECC_8BIT_STOP();// init 8bit ECC decodingNF_ECC_DIRECTION_IN();// 4/8BIT ECC Decoding, read page
接下來,原代碼是:
NF_RSTECC();
NF_MECC_UnLock();
我這裡把這兩個操作的順序換一下,變成:
NF_MECC_UnLock(); NF_RSTECC();
在NF_RSTECC()之前必須設定 NF_ECC_8BIT_STOP(); ,因為文檔中有說到: if you want to stop current work and start encoding/decoding for new data, you must set 8bitStop(NFCONT[11]) before set InitMECC(NFCONT[5]) bit.
接下來的代碼就是讀取512位元組資料的:
if( ((DWORD) (pSectorBuff+nSectorLoop*SECTOR_SIZE)) & 0x3)
{
for(i=0; i<SECTOR_SIZE/sizeof(DWORD); i++)
{
rddata = (DWORD) NF_RDDATA_WORD();
(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+0] = (BYTE)(rddata & 0xff);
(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+1] = (BYTE)(rddata>>8 & 0xff);
(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+2] = (BYTE)(rddata>>16 & 0xff);
(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+3] = (BYTE)(rddata>>24 & 0xff);
}
}
else
{
RdPage512(pSectorBuff+nSectorLoop*SECTOR_SIZE); // Read page/sector data.
}
然後,就看到代碼把前面讀取到的ECC接著寫進去了,這裡應該是寫進去的ECC與讀取產生的ECC在ECC模組中進行對比,用於尋找錯誤位
SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8;
NF_WRDATA_WORD(MECCBuf[2*nSectorLoop]);
SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8+4;
NF_WRDATA_WORD(MECCBuf[2*nSectorLoop+1]);
NF_MECC_Lock();
這裡,同樣需要修改成寫入8bit ECC的:
SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8;NF_WRDATA_WORD(MECCBuf[4*nSectorLoop]);SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8+4;NF_WRDATA_WORD(MECCBuf[4*nSectorLoop+1]);SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8+8;NF_WRDATA_WORD(MECCBuf[4*nSectorLoop+2]);SectorSpareAddr = NewSpareAddr+8+nSectorLoop*8+12;NF_WRDATA_WORD(MECCBuf[4*nSectorLoop+3]);NF_MECC_Lock();
之後,就是等待解碼操作的完成:
//decode done
while (!(NF_RDSTAT & (1<<6)));
tempMECCBuf[0]= NF_RDMECC0();
tempMECCBuf[1] = NF_RDMECC1();
後面這兩句含有tempMECCBuf的操作可以直接刪除了,沒用的。處理完這個之後,緊接著,就是尋找ECC錯誤並進行矯正了:
pSectorBuff1 = pSectorBuff+nSectorLoop*SECTOR_SIZE;
if (!ECC_CorrectData(startSectorAddr, pSectorBuff1, nRetEcc, ECC_CORRECT_MAIN))
{
RETAILMSG(1,(TEXT("ECC ERROR\n")));
return FALSE;
}
}
NF_nFCE_H();
在 NF_nFCE_H(); 這句之前,我們需要把使能的一些中斷關閉了:
g_pNFConReg->NFCONT &= ~(1<<10);// Disable illegal access interrupt controlg_pNFConReg->NFCONT &= ~(1<<9);// Disable RnB interrupt
3、NAND_LB_ReadSectorInfo()
原BSP代碼:
BOOL NAND_LB_ReadSectorInfo(SECTOR_ADDR sectorAddr, PSectorInfo pInfo){ BOOL bRet = TRUE; int NewSpareAddr = 4096; //gjl 2048 int NewSectorAddr = sectorAddr; DWORD MECCBuf[16]; // gjl 8 UINT16 nSectorLoop, i; UINT8 TempInfo[40];#if CHECK_SPAREECC DWORD SECCBuf[4]; //gjl 2 UINT32 nRetEcc = 0;#endif NF_nFCE_L(); NF_CLEAR_RB(); NF_CMD(CMD_READ); // Send read confirm command. NF_ADDR((NewSpareAddr)&0xff); NF_ADDR((NewSpareAddr>>8)&0xff); NF_ADDR((NewSectorAddr)&0xff); NF_ADDR((NewSectorAddr>>8) & 0xff);#if LB_NEED_EXT_ADDR NF_ADDR((NewSectorAddr >> 16) & 0xff);#endif NF_CMD(CMD_READ3); NF_DETECT_RB(); pInfo->bBadBlock = NF_RDDATA_BYTE(); pInfo->dwReserved1 = NF_RDDATA_WORD(); pInfo->bOEMReserved = NF_RDDATA_BYTE(); pInfo->wReserved2 = NF_RDDATA_BYTE(); pInfo->wReserved2 |= (NF_RDDATA_BYTE()<<8); for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE*2; nSectorLoop++) { MECCBuf[nSectorLoop] = NF_RDDATA_WORD(); } NF_nFCE_H(); #if (NAND_DEBUG) RETAILMSG(1,(TEXT("#### FMD_DRIVER:::56565656\n")));#endif return bRet;}
還是先看定義的 DWORD MECCBuf[16]; // gjl 8
這個我們要改成:
DWORD MECCBuf[32];
接著在 NF_nFCE_L(); 操作之前,添加:
NF_ECCTYPE_8BIT();// use 8bit ECC typeNF_ECC_8BIT_STOP();// init 8bit ECC decoding
然後,又看到讀取ECC的操作:
for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE*2; nSectorLoop++)
{
MECCBuf[nSectorLoop] = NF_RDDATA_WORD();
}
這個,我們需要改成:
for (nSectorLoop = 0; nSectorLoop < SECTORS_PER_PAGE*4; nSectorLoop++) { MECCBuf[nSectorLoop] = NF_RDDATA_WORD(); }
4、FMD_SB_ReadSector()
BOOL FMD_SB_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors){ ULONG SectorAddr = (ULONG)startSectorAddr; ULONG MECC; UINT32 nRet = TRUE; UINT32 nRetEcc = 0;#if (NAND_DEBUG) RETAILMSG(1,(TEXT("#### FMD_DRIVER:::FMD_sbreadT \n")));#endif if (!pSectorBuff && !pSectorInfoBuff) { RETAILMSG(1,(TEXT("[FMD:ERR] FMD_SB_ReadSector(0x%08x, 0x%08x) : Invalid Parameter\n"), pSectorBuff, pSectorInfoBuff)); return(FALSE); } while (dwNumSectors--) { NF_RSTECC(); NF_MECC_UnLock(); NF_nFCE_L(); if (!pSectorBuff) { NF_CLEAR_RB(); NF_CMD(CMD_READ2); // Send read confirm command. NF_ADDR(0); // Ignored. NF_ADDR(SectorAddr & 0xff); // Page address. NF_ADDR((SectorAddr >> 8) & 0xff);#if SB_NEED_EXT_ADDR NF_ADDR((SectorAddr >> 16) & 0xff);#endif NF_DETECT_RB(); RdPageInfo((PBYTE)pSectorInfoBuff); // Read page/sector information. pSectorInfoBuff++; } else { NF_CLEAR_RB(); NF_CMD(CMD_READ); // Send read command. NF_ADDR(0); // Column = 0. NF_ADDR(SectorAddr & 0xff); // Page address. NF_ADDR((SectorAddr >> 8) & 0xff);#if SB_NEED_EXT_ADDR NF_ADDR((SectorAddr >> 16) & 0xff);#endif NF_DETECT_RB(); // Wait for command to complete. if( ((DWORD) pSectorBuff) & 0x3) { RdPage512Unalign (pSectorBuff); } else { RdPage512(pSectorBuff); // Read page/sector data. } NF_MECC_Lock(); if (pSectorInfoBuff) { RdPageInfo((PBYTE)pSectorInfoBuff); // Read page/sector information. pSectorInfoBuff ++; } else { BYTE TempInfo[8]; RdPageInfo(TempInfo); // Read page/sector information. } MECC = NF_RDDATA_BYTE() << 0; MECC |= NF_RDDATA_BYTE() << 8; MECC |= NF_RDDATA_BYTE() << 16; MECC |= (NF_RDMECC0() &0xff000000); //MECC |= NF_RDDATA_BYTE() << 24; NF_WRMECCD0( ((MECC&0xff00)<<8)|(MECC&0xff) ); NF_WRMECCD1( ((MECC&0xff000000)>>8)|((MECC&0xff0000)>>16) ); nRetEcc = NF_ECC_ERR0; switch(nRetEcc & 0x3) { case 0: // No Error nRet = TRUE; break; case 1: // 1-bit Error(Correctable) RETAILMSG(1,(TEXT("ECC correctable error(0x%x)\n"), SectorAddr)); (pSectorBuff)[(nRetEcc>>7)&0x7ff] ^= (1<<((nRetEcc>>4)&0x7)); nRet = TRUE; break; case 2: // Multiple Error RETAILMSG(1,(TEXT("ECC Uncorrectable error(0x%x)\n"), SectorAddr)); nRet = FALSE; break; case 3: // ECC area Error RETAILMSG(1,(TEXT("ECC area error\n"))); default: nRet = FALSE; break; } pSectorBuff += NAND_SECTOR_SIZE; } NF_nFCE_H(); ++SectorAddr; } return(nRet);}
FMD_SB_ReadSector()是介紹SLC讀取操作的,這裡不用修改。
到此,這一篇就把ECC矯正和讀取資料的部分給搞掂了。下一篇將介紹寫資料的部分