Nandflash 驱动移植 (五)
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,%xn"),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 Errorn"))); goto finished; } else if (nErrDataNum == 9) { bRet = FALSE; RETAILMSG(1,(TEXT("More than 8-bit error,uncorrectablen"))); goto finished; } else if (nErrDataNum > 9) { bRet = FALSE; RETAILMSG(1,(TEXT("Reservedn"))); 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,nErrDataNum,nErrByteNum,nErrDataNo,nErrBitNo)); } } finished: return bRet; } 这里是修改后的,支持8bit ECC校验。 注意到上面的 代码了吗?让我们对比手册看看这个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 %xn"),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:::54321n"))); #endif return FALSE; } #if (NAND_DEBUG) RETAILMSG(1,(TEXT("#### FMD_DRIVER:::12345n"))); #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,ECC_CORRECT_MAIN)) { RETAILMSG(1,(TEXT("ECC ERRORn"))); return FALSE; } } NF_nFCE_H(); return TRUE; } 这个是飞凌BSP中的源码,里面需要修改的地方还是挺多的。 首先,来看定义部分的 由于将要用的是8bit的ECC校验,这个ECC的buffer就应该是32,1page=8*512byte,每读取512byte数据产生的ECC存放在4个32位的寄存器中,所以需要8*4个buffer: DWORD MECCBuf[32]; 至于tempMECCBuf,从上述代码中就可以看出就一垃圾,根本没用到,这里就把它删了。 定义完之后,我们需要使能一些相关的中断(不这样搞的话,发现无法正常校验ECC,具体原因请知道的朋友告知一声) 在 if (!pSectorBuff && !pSectorInfoBuff) 的后面,我们添加以下代码: 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数据的操作: 由于,我们使用的是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(); }
就是 ??????? NF_CMD(CMD_RDO);??????????????????????????? // Send read command.
这里,把上面的 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_RSTECC();
接下来的代码就是读取512字节数据的: 然后,就看到代码把前面读取到的ECC接着写进去了,这里应该是写进去的ECC与读取产生的ECC在ECC模块中进行对比,用于查找错误位 这里,同样需要修改成写入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();
后面这两句含有tempMECCBuf的操作可以直接删除了,没用的。处理完这个之后,紧接着,就是查找ECC错误并进行矫正了:
??????? if (!ECC_CorrectData(startSectorAddr,ECC_CORRECT_MAIN)) ? ??? 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:::56565656n"))); #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*4; nSectorLoop++) { MECCBuf[nSectorLoop] = NF_RDDATA_WORD(); }
? 4、FMD_SB_ReadSector() BOOL FMD_SB_ReadSector(SECTOR_ADDR startSectorAddr,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 Parametern"),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 errorn"))); default: nRet = FALSE; break; } pSectorBuff += NAND_SECTOR_SIZE; } NF_nFCE_H(); ++SectorAddr; } return(nRet); } FMD_SB_ReadSector()是介绍SLC读取操作的,这里不用修改。 ? 到此,这一篇就把ECC矫正和读取数据的部分给搞掂了。下一篇将介绍写数据的部分 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |