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 0
if( (nRetEcc & NF_ECC8ERR0_ECC_READY) )
return TRUE;
#endif
// 8bit ECC error searching engine needs mini mum 372 cycles to find any error
countdown = 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 command
NF_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 control
g_pNFConReg->NFCONT |= (1<<9); // Enable RnB interrupt
g_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 decoding
NF_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 control
g_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 type
NF_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矫正和读取数据的部分给搞掂了。下一篇将介绍写数据的部分