SylixOS下基於NUC970的NAND驅動

開發環境

  1. 開發環境

    宿主機: Windows7 64bits 系統算法

    開發板: 安米MDK972框架

    軟件環境: RealEvo-IDE3.0函數

    NAND Flash: S34ML02G100TF100佈局

  2. S34ML02G100TF100芯片參數

  • Density:2 Gbitui

  • Input / Output Bus Width: 8-bitsspa

  • Page Size:2112 (2048 + 64) bytes; 64 bytes is spare area指針

  • Block Size: 64 Pages;128k + 4k bytesrest

  • Plane Size: 1024 Blocks per Plane;128M + 4M bytescode

  • Device Size: 2 Planes per Device or 256 Mbyteip

NAND控制器結構

     NUC970的NAND控制器包含在FMI中。FMI分爲DMA單元和FMI單元。對於NAND,支持單一DMA通      道和硬件ECC,如圖 2-1所示。

圖 2-1   NUC970 NAND控制器

 

技術實現

  1. 驅動框架

    SylixOS中NAND Flsh的驅動框架如圖 3-1所示。NAND通用驅動主要在fs/mtd/nand/nand_base.c中,該文件包含了NAND的通用操做。驅動工程師須要在NAND通用驅動的基礎上實現與硬件相關的驅動層的結構體(nand_chip),該結構體包含了對具體硬件相關的控制和操做函數,以及相關硬件參數和配置信息。MTD層與文件系統,SylixOS已經徹底實現,不須要驅動工程師實現。

    圖 3-1   NAND驅動框架

  2. 框架實現

    NAND驅動須要完成NAND控制器、ECC的配置以及NAND的相關操做函數及文件系統掛載,若是使用硬件ECC通常本身定義OOB佈局。NUC970驅動實現的操做如程序清單 3-1所示。

    程序清單 3-1   NAND實現框架

    nandchipNand->cmd_ctrl        = hwControl;
        nandchipNand->cmdfunc         = nandCommand;
        nandchipNand->dev_ready       = devReady;
        nandchipNand->select_chip     = chipSelect;
        nandchipNand->read_byte       = nandReadByte;
        nandchipNand->write_buf       = nandWriteBuf;
        nandchipNand->read_buf        = nandReadBuf;
        nandchipNand->chip_delay      = 50;
        nandchipNand->ecc.mode        = NAND_ECC_HW_OOB_FIRST;
        nandchipNand->ecc.hwctl       = nandEnableHwEcc;
        nandchipNand->ecc.calculate   = nandCalculateEcc;
        nandchipNand->ecc.correct     = nandCorrectData;
        nandchipNand->ecc.write_page  = nandWritePageHwEcc;
        nandchipNand->ecc.read_page   = nandReadPageHwEccOobFirst;
        nandchipNand->ecc.read_oob    = nandReadoobHwEcc;
    nandchipNand->ecc.layout      = &__Gpnuc970nandoob;

     

  3. 控制器初始化

    控制器初始化主要實現了模塊時鐘使能、管腳複用、時序設置、片選、解除寫保護、頁大小、軟件復位等操做。

  4. ECC配置

    ECC配置主要設置冗餘區大小,保護前3字節,自動寫校驗值到NAND,設置算法等級,ECC使能等操做。

  5. 函數cmd_ctrl

    該函數主要實現對ALE/CLE/nCE的控制,同時用來寫命令和地址。

    程序清單 3-2  命令控制函數

     

    static VOID hwControl (struct mtd_info  *pMtd, INT  iCmd, UINT  uiCtrl)
    {
        struct nand_chip *pChip = pMtd->priv;
    
        if (uiCtrl & NAND_CTRL_CHANGE) {
            ULONG IO_ADDR_W = (ULONG)REG_NANDDATA;
    
            if ((uiCtrl & NAND_CLE)) {
                IO_ADDR_W = REG_NANDCMD;
            }
            if ((uiCtrl & NAND_ALE)) {
                IO_ADDR_W = REG_NANDADDR;
            }
    
            pChip->IO_ADDR_W = (VOID *)IO_ADDR_W;
        }
    
        if (iCmd != NAND_CMD_NONE) {
            writeb(iCmd, pChip->IO_ADDR_W);
        }
    }
  6. 函數cmdfunc

    該函數主要實現向芯片中寫命令的功能,在系統提供的默認函數中經過調用cmd_ctrl函數來實現具體寫操做。因爲NUC970的控制器需在最後一個地址週期手動設置EOA位,沒法使用默認函數,差別代碼如程序清單 3-3所示:

    程序清單 3-3   命令功能函數差別代碼

    writel((iColumn >> BUS_WIDTH) | NANDADDR_EOA, REG_NANDADDR);

     

  7. 函數dev_ready

    該函數主要用來得到設備ready/busy引腳狀態。若是該函數指針設置爲NULL沒法得到ready/busy引腳狀態,則ready/busy信息須要經過讀取NAND芯片的狀態寄存器。代碼實現如程序清單 3-4所示。

    程序清單 3-4  得到NAND狀態

    return ((readl(REG_NANDINTSTS) & NANDINTSTS_RB0_Status) ? 1 : 0);

     

  8. 函數read_byte

    該函數功能爲從NAND芯片讀取一個字節,代碼如程序清單 3-5所示。

    程序清單 3-5  從NAND讀一個字節

     

    return ((UCHAR)readl(REG_NANDDATA));

     

  9. 函數write_buf

    該函數功能爲從一個緩衝區寫數數據到NAND芯片。代碼實現如程序清單 3-6。

    程序清單 3-6  寫緩衝區數據到NAND

     

    for (i = 0; i < iLen; i++) {
            writel(pucbuf[i], REG_NANDDATA);
        }

     

  10. 函數read_buf

    該函數功能爲從NAND芯片讀數據到一個緩衝區。代碼實現如程序清單 3-7所示。

    程序清單 3-7  讀數據到緩衝區

     

    for (i = 0; i < iLen; i++) {
            writel(pucbuf[i], REG_NANDDATA);
        }

     

  11. 函數ecc.hwctl

    該函數用於控制硬件ECC發生器,只有在使用硬件ECC時實現。本例的硬件校驗在傳輸中實現,所以該函數爲空實現。

  12. 函數ecc.calculate

    該函數用於ECC計算,或從ECC硬件中讀回。本例的硬件校驗在傳輸中實現,所以該函數爲空實現。

  13. 函數ecc.correct

    該函數用於ECC校訂。本例的硬件校驗在傳輸中實現,所以該函數爲空實現。

  14. 函數ecc.write_page

    該函數主要實現帶ECC的寫一頁數據到NAND芯片。在傳輸的過程當中,ECC電路會自動計算ECC校驗值,並存儲到控制器分配的寄存器組中。完成傳輸後寄存器組中的OOB數據會根據設置自動寫進NAND芯片。實現流程如程序清單 3-8所示。

    程序清單 3-8  帶硬件ECC的寫頁

    static INT nandWritePageHwEcc (struct mtd_info   *pMtd,
                                   struct nand_chip  *pChip,
                                   const UCHAR       *pucBuf,
                                   INT                iOobRequired)
    {
        UCHAR          *pucEccCalc   = pChip->buffers->ecccalc;
        UINT             uiEccBytes  = pChip->ecc.layout->eccbytes;
        register CHAR     *pcPtr     = (CHAR *)REG_NANDRA0;
    
        memset((VOID *)pcPtr, 0xFF, pMtd->oobsize);
        memcpy((VOID *)pcPtr, (VOID *)pChip->oob_poi,  pMtd->oobsize - pChip->ecc.total);
    
        nandDmaTransfer(pMtd, pucBuf, pMtd->writesize , 0x1);
    
        /*
         *  Copy parity code in SMRA to calc
         */
        memcpy((VOID *)pucEccCalc,
               (VOID *)(REG_NANDRA0 + (pMtd->oobsize - pChip->ecc.total)),
               pChip->ecc.total);
    
        /*
         *  Copy parity code in calc to oob_poi
         */
        memcpy((VOID *)(pChip->oob_poi + uiEccBytes),
                (VOID *)pucEccCalc,
                pChip->ecc.total);
    
        return 0;
    }
     

     

  15. 函數ecc.read_page

    該函數主要實現帶ECC校驗的從NAND芯片讀出一頁數據。本例爲硬件ECC,須要先讀出OOB區數據到控制器分配的寄存器組中。在數據傳輸的過程當中,ECC電路會計算ECC校驗值,並與寄存器組中的值比較,檢查是否產生錯誤,以及定位和計算校錯值。若產生錯誤,程序須要根據錯誤位置和錯誤值進行校錯。具體流程如程序清單 3-9所示:

    程序清單 3-9  帶ECC的讀頁

     

    static INT nandReadPageHwEccOobFirst (struct mtd_info   *pMtd,
                                          struct nand_chip  *pChip,
                                          UCHAR             *ucBuf,
                                          INT                iOobRequired,
                                          INT                iPage)
    {
        INT      iEccSize = pChip->ecc.size;
        CHAR    *pcPtr    = (CHAR *)REG_NANDRA0;
    
        /*
         *  At first, read the OOB area
         */
        nandCommand(pMtd, NAND_CMD_READOOB, 0, iPage);
        nandReadBuf(pMtd, pChip->oob_poi, pMtd->oobsize);
    
        /*
         *  Second, copy OOB data to SMRA for page read
         */
        memcpy((VOID *)pcPtr, (VOID *)pChip->oob_poi, pMtd->oobsize);
    
        /*
         *  Third, read data from nand
         */
        nandCommand(pMtd, NAND_CMD_READ0, 0, iPage);
        nandDmaTransfer(pMtd, ucBuf, iEccSize, 0x0);
    
        /*
         *  Fouth, restore OOB data from SMRA
         */
        memcpy((VOID *)pChip->oob_poi, (VOID *)pcPtr, pMtd->oobsize);
    
        return 0;
    }

     

  16. 函數ecc.read_oob

    該函數主要實現從芯片中讀取OOB數據。實現流程如程序清單 3-10所示。

    程序清單 3-10  帶硬件ECC的讀OOB區數據

     

    static INT nandReadoobHwEcc(struct mtd_info  *pMtd, struct nand_chip  *pChip, INT  iPage)
    {
        CHAR *cPtr = (char *)REG_NANDRA0;
    
        /*
         *  At first, read the OOB area
         */
        nandCommand(pMtd, NAND_CMD_READOOB, 0, iPage);
        nandReadBuf(pMtd, pChip->oob_poi, pMtd->oobsize);
    
        /*
         *  Second, copy OOB data to SMRA for page read
         */
        memcpy ((VOID *)cPtr, (VOID *)pChip->oob_poi, pMtd->oobsize);
    
        return 0;
    }

     

  17. ecc.layout

    nand_ecc爲ECC佈局控制結構體。經過該結構體配置OOB區中ECC的位數和位置,可用位數和空閒位數。本例經過調用程序清單 3-11代碼實現OOB區的佈局控制。

    程序清單 3-11   OOB區佈局

     

    static VOID oobTableLayout ( struct nand_ecclayout  *pNandOOBTbl, INT  iOobSize , INT  iEccBytes)
    {
        pNandOOBTbl->eccbytes     = iEccBytes;
    
        pNandOOBTbl->oobavail     = iOobSize - DEF_RESERVER_OOB_SIZE_FOR_MARKER - iEccBytes ;
    
        pNandOOBTbl->oobfree[0].offset = DEF_RESERVER_OOB_SIZE_FOR_MARKER;     /*  Bad block marker size    */
    
        pNandOOBTbl->oobfree[0].length = iOobSize - iEccBytes - pNandOOBTbl->oobfree[0].offset ;
    }

     

  18. 文件系統掛載

    在SylixOS下NAND Flash一般掛載YAFFS文件系統,並分爲n0和n1分區,其中n0分區用做啓動分區,n1做爲應用分區。掛載流程如程序清單 3-12所示。

    程序清單 3-12  文件系統掛掛載

     

    yaffs_mtd_drv_install(&__GyaffsdevBootDev);
        yaffs_mtd_drv_install(&__GyaffsdevCommDev);
    
        yaffs_add_device(&__GyaffsdevBootDev);                              /* add to yaffs device table    */
        yaffs_add_device(&__GyaffsdevCommDev);                              /* add to yaffs device table    */
    
        yaffs_mount(cBootDevName);
        yaffs_mount(cCommDevName);

     

參考資料

    無。

相關文章
相關標籤/搜索