Nandflash 驅動移植 (五)

來源:互聯網
上載者:User

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矯正和讀取資料的部分給搞掂了。下一篇將介紹寫資料的部分

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.