T168_111\core\N32903文件:第15~19个文件

NandDrv.c   。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/*-----------------------------------------------------------------------------------*/
/* Nuvoton Technology Corporation confidential                                       */
/*                                                                                   */
/* Copyright (c) 2008 by Nuvoton Technology Corporation                              */
/* All rights reserved                                                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#include "wbio.h"

#include "w55fa93_sic.h"
#include "wblib.h"

//#include "fmi.h"
//#include "gnand_global.h"

//#include "w55fa93_gnand.h"
#include "w55fa93_reg.h"

// define DATE CODE and show it when running to make maintaining easy.
#define NAND_DATE_CODE  FMI_DATE_CODE

#define NAND_RESERVED_BLOCK     10
#define OPT_TWO_RB_PINS
#define OPT_FIRST_4BLOCKS_ECC4

#define OPT_SUPPORT_H27UAG8T2A

#ifdef OPT_SW_WP
#define SW_WP_DELAY_LOOP        3000
#endif


BOOL volatile _fmi_bIsNandFirstAccess = TRUE;
extern BOOL volatile _fmi_bIsSMDataReady;
INT fmiSMCheckBootHeader(INT chipSel, FMI_SM_INFO_T *pSM);
static int _nand_init0 = 0, _nand_init1 = 0;
__align(4096) UCHAR _fmi_ucSMBuffer[4096];

UINT8 *_fmi_pSMBuffer;

/* functions */
INT fmiSMCheckRB(FMI_SM_INFO_T *pSM)
{
#if 1    // no timer in it
    UINT32 ii;
    for (ii=0; ii<100; ii++);

    while(1)
    {
        if(pSM == pSM0)
        {
            if (inpw(REG_SMISR) & SMISR_RB0_IF)
            {
                while(! (inpw(REG_SMISR) & SMISR_RB0) );
                outpw(REG_SMISR, SMISR_RB0_IF);
                return 1;
            }
        }
        else
        {
            if (inpw(REG_SMISR) & SMISR_RB1_IF)
            {
                while(! (inpw(REG_SMISR) & SMISR_RB1) );
                outpw(REG_SMISR, SMISR_RB1_IF);
                return 1;
            }
        }
    }
    return 0;

#else
    unsigned int volatile tick;
    tick = sysGetTicks(TIMER0);

    while(1)
    {
        if(pSM == pSM0)
        {
            if (inpw(REG_SMISR) & SMISR_RB0_IF)
            {
                while(! (inpw(REG_SMISR) & SMISR_RB0) );
                outpw(REG_SMISR, SMISR_RB0_IF);
                return 1;
            }
        }
        else
        {
            if (inpw(REG_SMISR) & SMISR_RB1_IF)
            {
                while(! (inpw(REG_SMISR) & SMISR_RB1) );
                outpw(REG_SMISR, SMISR_RB1_IF);
                return 1;
            }
        }

        if ((sysGetTicks(TIMER0) - tick) > 1000)
            break;
    }
    return 0;
#endif
}


INT fmiSMCheckStatus(FMI_SM_INFO_T *pSM)
{
    UINT32 status, ret;
    
    ret = 0;
    outpw(REG_SMCMD, 0x70);     // Status Read command for NAND flash
    status = inpw(REG_SMDATA);

    if (status & BIT0)          // BIT0: Chip status: 1:fail; 0:pass
    {
#ifdef DEBUG
        printf("NAND device status: FAIL!!\n");
#endif
        sysprintf("ERROR: NAND device status: FAIL!!\n");
        ret = FMI_SM_STATUS_ERR;
    }

    if ((status & BIT7) == 0)   // BIT7: Write Protect: 1:unprotected; 0:protected
    {
#ifdef DEBUG
        printf("NAND device status: Write Protected!!\n");
#endif
        sysprintf("WARNING: NAND device status: Write Protected!!\n");
        ret = FMI_SM_STATUS_ERR;
    }
    
    return ret;
}


// SM functions
INT fmiSM_Reset(FMI_SM_INFO_T *pSM)
{
#ifdef OPT_TWO_RB_PINS
    UINT32 volatile i;

    if(pSM == pSM0)
        outpw(REG_SMISR, SMISR_RB0_IF);
    else
        outpw(REG_SMISR, SMISR_RB1_IF);

    outpw(REG_SMCMD, 0xff);
    for (i=100; i>0; i--);

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;
    return 0;

#else
    UINT32 volatile i;
    outpw(REG_SMISR, SMISR_RB0_IF);
    outpw(REG_SMCMD, 0xff);
    for (i=100; i>0; i--);

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;
    return 0;
#endif  // OPT_TWO_RB_PINS
}

VOID fmiSM_Initial(FMI_SM_INFO_T *pSM)
{
    if (pSM->nPageSize == NAND_PAGE_8KB)
    {
        outpw(REG_SMCSR, (inpw(REG_SMCSR)&(~SMCR_PSIZE))|PSIZE_8K);
    }
    else if (pSM->nPageSize == NAND_PAGE_4KB)
    {
        outpw(REG_SMCSR, (inpw(REG_SMCSR)&(~SMCR_PSIZE))|PSIZE_4K);
    }
    else if (pSM->nPageSize == NAND_PAGE_2KB)
    {
        outpw(REG_SMCSR, (inpw(REG_SMCSR)&(~SMCR_PSIZE))|PSIZE_2K);
    }
    else    // pSM->nPageSize = NAND_PAGE_512B
    {
        outpw(REG_SMCSR, (inpw(REG_SMCSR)&(~SMCR_PSIZE))|PSIZE_512);
    }

    outpw(REG_SMCSR,  inpw(REG_SMCSR) | SMCR_ECC_EN);   // enable ECC

    if (pSM->bIsCheckECC)
        outpw(REG_SMCSR,  inpw(REG_SMCSR) | SMCR_ECC_CHK);  // enable ECC check
    else
        outpw(REG_SMCSR, inpw(REG_SMCSR) & ~SMCR_ECC_CHK);  // disable ECC check

#ifdef _SIC_USE_INT_
    if ((_nand_init0 == 0) && (_nand_init1 == 0))
        outpw(REG_SMIER, SMIER_DMA_IE);
#endif  //_SIC_USE_INT_

    // set BCH_Tx and redundant area size
    outpw(REG_SMREAREA_CTL, inpw(REG_SMREAREA_CTL) & ~SMRE_MECC);       // Redundant area size
    if (pSM->nPageSize == NAND_PAGE_8KB)
    {
        outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
        outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T12);            // BCH_12 is selected
        outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 376);  // Redundant area size
    }
    else if (pSM->nPageSize == NAND_PAGE_4KB)
    {
        if (pSM->bIsNandECC12 == TRUE)
        {
            outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
            outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T12);            // BCH_12 is selected
    #ifdef OPT_SUPPORT_H27UAG8T2A
            outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 224);;  // Redundant area size
    #else
            outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 216);;  // Redundant area size
    #endif
        }
        else if (pSM->bIsNandECC8 == TRUE)
        {
            outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
            outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);             // BCH_8 is selected
            outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 128);  // Redundant area size
        }
        else
        {
            outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
            outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);             // BCH_4 is selected
            outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 128);  // Redundant area size
        }
    }
    else if (pSM->nPageSize == NAND_PAGE_2KB)
    {
        if (pSM->bIsNandECC8 == TRUE)
        {
            outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
            outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);             // BCH_8 is selected
            outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
        }
        else
        {
            outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
            outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);             // BCH_4 is selected
            outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
        }
    }
    else
    {
        outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
        outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);             // BCH_4 is selected
        outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 16);  // Redundant area size
    }
}


/*-----------------------------------------------------------------------------
 * Read NAND chip ID from chip and then set pSM and NDISK by chip ID.
 *---------------------------------------------------------------------------*/
INT fmiSM_ReadID(FMI_SM_INFO_T *pSM, NDISK_T *NDISK_info)
{
    UINT32 tempID[5];

    fmiSM_Reset(pSM);
    outpw(REG_SMCMD, 0x90);     // read ID command
    outpw(REG_SMADDR, EOA_SM);  // address 0x00

    tempID[0] = inpw(REG_SMDATA);
    tempID[1] = inpw(REG_SMDATA);
    tempID[2] = inpw(REG_SMDATA);
    tempID[3] = inpw(REG_SMDATA);
    tempID[4] = inpw(REG_SMDATA);

    NDISK_info->vendor_ID = tempID[0];
    NDISK_info->device_ID = tempID[1];

    if (tempID[0] == 0xC2)
        pSM->bIsCheckECC = FALSE;
    else
        pSM->bIsCheckECC = TRUE;

    pSM->bIsNandECC4 = FALSE;
    pSM->bIsNandECC8 = FALSE;
    pSM->bIsNandECC12 = FALSE;
    pSM->bIsNandECC15 = FALSE;

    switch (tempID[1])
    {
        /* page size 512B */
        case 0x79:  // 128M
            pSM->uSectorPerFlash = 255744;
            pSM->uBlockPerFlash = 8191;
            pSM->uPagePerBlock = 32;
            pSM->uSectorPerBlock = 32;
            pSM->bIsMulticycle = TRUE;
            pSM->nPageSize = NAND_PAGE_512B;
            pSM->bIsNandECC4 = TRUE;
            pSM->bIsMLCNand = FALSE;

            NDISK_info->NAND_type = NAND_TYPE_SLC;
            NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
            NDISK_info->nZone = 1;              /* number of zones */
            NDISK_info->nBlockPerZone = 8192;   /* blocks per zone */
            NDISK_info->nPagePerBlock = 32;     /* pages per block */
            NDISK_info->nLBPerZone = 8000;      /* logical blocks per zone */
            NDISK_info->nPageSize = 512;
            break;

        case 0x76:  // 64M
        case 0x5A:  // 64M XtraROM
            pSM->uSectorPerFlash = 127872;
            pSM->uBlockPerFlash = 4095;
            pSM->uPagePerBlock = 32;
            pSM->uSectorPerBlock = 32;
            pSM->bIsMulticycle = TRUE;
            pSM->nPageSize = NAND_PAGE_512B;
            pSM->bIsNandECC4 = TRUE;
            pSM->bIsMLCNand = FALSE;

            NDISK_info->NAND_type = NAND_TYPE_SLC;
            NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
            NDISK_info->nZone = 1;              /* number of zones */
            NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
            NDISK_info->nPagePerBlock = 32;     /* pages per block */
            NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */
            NDISK_info->nPageSize = 512;
            break;

        case 0x75:  // 32M
            pSM->uSectorPerFlash = 63936;
            pSM->uBlockPerFlash = 2047;
            pSM->uPagePerBlock = 32;
            pSM->uSectorPerBlock = 32;
            pSM->bIsMulticycle = FALSE;
            pSM->nPageSize = NAND_PAGE_512B;
            pSM->bIsNandECC4 = TRUE;
            pSM->bIsMLCNand = FALSE;

            NDISK_info->NAND_type = NAND_TYPE_SLC;
            NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
            NDISK_info->nZone = 1;              /* number of zones */
            NDISK_info->nBlockPerZone = 2048;   /* blocks per zone */
            NDISK_info->nPagePerBlock = 32;     /* pages per block */
            NDISK_info->nLBPerZone = 2000;      /* logical blocks per zone */
            NDISK_info->nPageSize = 512;
            break;

        case 0x73:  // 16M
            pSM->uSectorPerFlash = 31968;   // max. sector no. = 999 * 32
            pSM->uBlockPerFlash = 1023;
            pSM->uPagePerBlock = 32;
            pSM->uSectorPerBlock = 32;
            pSM->bIsMulticycle = FALSE;
            pSM->nPageSize = NAND_PAGE_512B;
            pSM->bIsNandECC4 = TRUE;
            pSM->bIsMLCNand = FALSE;

            NDISK_info->NAND_type = NAND_TYPE_SLC;
            NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
            NDISK_info->nZone = 1;              /* number of zones */
            NDISK_info->nBlockPerZone = 1024;   /* blocks per zone */
            NDISK_info->nPagePerBlock = 32;     /* pages per block */
            NDISK_info->nLBPerZone = 1000;      /* logical blocks per zone */
            NDISK_info->nPageSize = 512;
            break;

        /* page size 2KB */
        case 0xf1:  // 128M
        case 0xd1:
            pSM->uBlockPerFlash = 1023;
            pSM->uPagePerBlock = 64;
            pSM->uSectorPerBlock = 256;
            pSM->uSectorPerFlash = 255744;
            pSM->bIsMulticycle = FALSE;
            pSM->nPageSize = NAND_PAGE_2KB;
            pSM->bIsNandECC8 = TRUE;
            pSM->bIsMLCNand = FALSE;

            NDISK_info->NAND_type = NAND_TYPE_SLC;
            NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
            NDISK_info->nZone = 1;              /* number of zones */
            NDISK_info->nBlockPerZone = 1024;   /* blocks per zone */
            NDISK_info->nPagePerBlock = 64;     /* pages per block */
            NDISK_info->nLBPerZone = 1000;      /* logical blocks per zone */
            NDISK_info->nPageSize = 2048;

            // 2013/10/22, support MXIC MX30LF1G08AA NAND flash
            // 2015/06/22, support MXIC MX30LF1G18AC NAND flash
            if ( ((tempID[0]==0xC2)&&(tempID[1]==0xF1)&&(tempID[2]==0x80)&&(tempID[3]==0x1D)) ||
                 ((tempID[0]==0xC2)&&(tempID[1]==0xF1)&&(tempID[2]==0x80)&&(tempID[3]==0x95)&&(tempID[4]==0x02)) )
            {
                // The first ID of this NAND is 0xC2 BUT it is NOT NAND ROM (read only)
                // So, we MUST modify the configuration of it
                //      1. change pSM->bIsCheckECC to TRUE to enable ECC feature;
                //      2. assign a fake vendor_ID to make NVTFAT can write data to this NAND disk.
                //         (GNAND will check vendor_ID and set disk to DISK_TYPE_READ_ONLY if it is 0xC2)
                pSM->bIsCheckECC = TRUE;
                NDISK_info->vendor_ID = 0xFF;   // fake vendor_ID
            }

            // 2014/10/16, support Winbond W29N01GV NAND flash
            if ((tempID[0]==0xEF)&&(tempID[1]==0xF1)&&(tempID[2]==0x80)&&(tempID[3]==0x95))
            {
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
            }
            break;

        case 0xda:  // 256M
            if ((tempID[3] & 0x33) == 0x11)
            {
                pSM->uBlockPerFlash = 2047;
                pSM->uPagePerBlock = 64;
                pSM->uSectorPerBlock = 256;
                pSM->bIsMLCNand = FALSE;

                NDISK_info->NAND_type = NAND_TYPE_SLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 64;     /* pages per block */
                NDISK_info->nBlockPerZone = 2048;   /* blocks per zone */
                NDISK_info->nLBPerZone = 2000;      /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x21)
            {
                pSM->uBlockPerFlash = 1023;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 512;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nBlockPerZone = 1024;   /* blocks per zone */
                NDISK_info->nLBPerZone = 1000;      /* logical blocks per zone */
            }
            pSM->uSectorPerFlash = 511488;
            pSM->bIsMulticycle = TRUE;
            pSM->nPageSize = NAND_PAGE_2KB;
            pSM->bIsNandECC8 = TRUE;

            NDISK_info->nPageSize = 2048;
            break;

        case 0xdc:  // 512M
            if ((tempID[3] & 0x33) == 0x11)
            {
                pSM->uBlockPerFlash = 4095;
                pSM->uPagePerBlock = 64;
                pSM->uSectorPerBlock = 256;
                pSM->bIsMLCNand = FALSE;

                NDISK_info->NAND_type = NAND_TYPE_SLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 64;     /* pages per block */
                NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
                NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x21)
            {
                pSM->uBlockPerFlash = 2047;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 512;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nBlockPerZone = 2048;   /* blocks per zone */
                NDISK_info->nLBPerZone = 2000;      /* logical blocks per zone */
            }
            pSM->uSectorPerFlash = 1022976;
            pSM->bIsMulticycle = TRUE;
            pSM->nPageSize = NAND_PAGE_2KB;
            pSM->bIsNandECC8 = TRUE;

            NDISK_info->nPageSize = 2048;
            break;

        case 0xd3:
            // 2014/4/2, To support Samsung K9WAG08U1D 512MB NAND flash
            if ((tempID[0]==0xEC)&&(tempID[2]==0x51)&&(tempID[3]==0x95)&&(tempID[4]==0x58))
            {
                pSM->uBlockPerFlash  = 4095;        // block index with 0-base. = physical blocks - 1
                pSM->uPagePerBlock   = 64;
                pSM->nPageSize       = NAND_PAGE_2KB;
                pSM->uSectorPerBlock = pSM->nPageSize / 512 * pSM->uPagePerBlock;
                pSM->bIsMLCNand      = FALSE;
                pSM->bIsMulticycle   = TRUE;
                pSM->bIsNandECC8     = TRUE;
                pSM->uSectorPerFlash = 1022976;

                NDISK_info->NAND_type     = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone         = 1;      // number of zones
                NDISK_info->nBlockPerZone = pSM->uBlockPerFlash + 1;   // blocks per zone
                NDISK_info->nPagePerBlock = pSM->uPagePerBlock;
                NDISK_info->nPageSize     = pSM->nPageSize;
                NDISK_info->nLBPerZone    = 4000;   // logical blocks per zone
                break;
            }

            // 2016/9/29, support MXIC MX60LF8G18AC NAND flash
            if ((tempID[0]==0xC2)&&(tempID[1]==0xD3)&&(tempID[2]==0xD1)&&(tempID[3]==0x95)&&(tempID[4]==0x5A))
            {
                // The first ID of this NAND is 0xC2 BUT it is NOT NAND ROM (read only)
                // So, we MUST modify the configuration of it
                //      1. change pSM->bIsCheckECC to TRUE to enable ECC feature;
                //      2. assign a fake vendor_ID to make NVTFAT can write data to this NAND disk.
                //         (GNAND will check vendor_ID and set disk to DISK_TYPE_READ_ONLY if it is 0xC2)
                pSM->bIsCheckECC = TRUE;
                NDISK_info->vendor_ID = 0xFF;   // fake vendor_ID
            }

            if ((tempID[3] & 0x33) == 0x32)
            {
                pSM->uBlockPerFlash = 2047;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 1024;    /* 128x8 */
                pSM->nPageSize = NAND_PAGE_4KB;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nPageSize = 4096;
                NDISK_info->nBlockPerZone = 2048;   /* blocks per zone */
                NDISK_info->nLBPerZone = 2000;      /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x11)
            {
                pSM->uBlockPerFlash = 8191;
                pSM->uPagePerBlock = 64;
                pSM->uSectorPerBlock = 256;
                pSM->nPageSize = NAND_PAGE_2KB;
                pSM->bIsMLCNand = FALSE;

                NDISK_info->NAND_type = NAND_TYPE_SLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 64;     /* pages per block */
                NDISK_info->nPageSize = 2048;
                NDISK_info->nBlockPerZone = 8192;   /* blocks per zone */
                NDISK_info->nLBPerZone = 8000;      /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x21)
            {
                pSM->uBlockPerFlash = 4095;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 512;
                pSM->nPageSize = NAND_PAGE_2KB;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nPageSize = 2048;
                NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
                NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x22)
            {
                pSM->uBlockPerFlash = 4095;
                pSM->uPagePerBlock = 64;
                pSM->uSectorPerBlock = 512; /* 64x8 */
                pSM->nPageSize = NAND_PAGE_4KB;
                pSM->bIsMLCNand = FALSE;

                NDISK_info->NAND_type = NAND_TYPE_SLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 64;     /* pages per block */
                NDISK_info->nPageSize = 4096;
                NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
                NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */
            }

            pSM->uSectorPerFlash = 2045952;
            pSM->bIsMulticycle = TRUE;
            pSM->bIsNandECC8 = TRUE;
            break;

        case 0xd5:  // 2048M

    #ifdef OPT_SUPPORT_H27UAG8T2A
            if ((tempID[0]==0xAD)&&(tempID[2] == 0x94)&&(tempID[3] == 0x25))
            {
                pSM->uBlockPerFlash = 4095;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 1024;    /* 128x8 */
                pSM->nPageSize = NAND_PAGE_4KB;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nPageSize = 4096;
                NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
                NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */

                pSM->uSectorPerFlash = 4091904;
                pSM->bIsMulticycle = TRUE;
                pSM->bIsNandECC12 = TRUE;
                break;
            }
            else
            {
                if ((tempID[3] & 0x33) == 0x32)
                {
                    pSM->uBlockPerFlash = 4095;
                    pSM->uPagePerBlock = 128;
                    pSM->uSectorPerBlock = 1024;    /* 128x8 */
                    pSM->nPageSize = NAND_PAGE_4KB;
                    pSM->bIsMLCNand = TRUE;

                    NDISK_info->NAND_type = NAND_TYPE_MLC;
                    NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                    NDISK_info->nZone = 1;              /* number of zones */
                    NDISK_info->nPagePerBlock = 128;    /* pages per block */
                    NDISK_info->nPageSize = 4096;
                    NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
                    NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */
                }
                else if ((tempID[3] & 0x33) == 0x11)
                {
                    pSM->uBlockPerFlash = 16383;
                    pSM->uPagePerBlock = 64;
                    pSM->uSectorPerBlock = 256;
                    pSM->nPageSize = NAND_PAGE_2KB;
                    pSM->bIsMLCNand = FALSE;

                    NDISK_info->NAND_type = NAND_TYPE_SLC;
                    NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                    NDISK_info->nZone = 1;              /* number of zones */
                    NDISK_info->nPagePerBlock = 64;     /* pages per block */
                    NDISK_info->nPageSize = 2048;
                    NDISK_info->nBlockPerZone = 16384;  /* blocks per zone */
                    NDISK_info->nLBPerZone = 16000;     /* logical blocks per zone */
                }
                else if ((tempID[3] & 0x33) == 0x21)
                {
                    pSM->uBlockPerFlash = 8191;
                    pSM->uPagePerBlock = 128;
                    pSM->uSectorPerBlock = 512;
                    pSM->nPageSize = NAND_PAGE_2KB;
                    pSM->bIsMLCNand = TRUE;

                    NDISK_info->NAND_type = NAND_TYPE_MLC;
                    NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                    NDISK_info->nZone = 1;              /* number of zones */
                    NDISK_info->nPagePerBlock = 128;    /* pages per block */
                    NDISK_info->nPageSize = 2048;
                    NDISK_info->nBlockPerZone = 8192;   /* blocks per zone */
                    NDISK_info->nLBPerZone = 8000;      /* logical blocks per zone */
                }

                pSM->uSectorPerFlash = 4091904;
                pSM->bIsMulticycle = TRUE;
                pSM->bIsNandECC8 = TRUE;
                break;
            }
    #else
            if ((tempID[3] & 0x33) == 0x32)
            {
                pSM->uBlockPerFlash = 4095;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 1024;    /* 128x8 */
                pSM->nPageSize = NAND_PAGE_4KB;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nPageSize = 4096;
                NDISK_info->nBlockPerZone = 4096;   /* blocks per zone */
                NDISK_info->nLBPerZone = 4000;      /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x11)
            {
                pSM->uBlockPerFlash = 16383;
                pSM->uPagePerBlock = 64;
                pSM->uSectorPerBlock = 256;
                pSM->nPageSize = NAND_PAGE_2KB;
                pSM->bIsMLCNand = FALSE;

                NDISK_info->NAND_type = NAND_TYPE_SLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 64;     /* pages per block */
                NDISK_info->nPageSize = 2048;
                NDISK_info->nBlockPerZone = 16384;  /* blocks per zone */
                NDISK_info->nLBPerZone = 16000;     /* logical blocks per zone */
            }
            else if ((tempID[3] & 0x33) == 0x21)
            {
                pSM->uBlockPerFlash = 8191;
                pSM->uPagePerBlock = 128;
                pSM->uSectorPerBlock = 512;
                pSM->nPageSize = NAND_PAGE_2KB;
                pSM->bIsMLCNand = TRUE;

                NDISK_info->NAND_type = NAND_TYPE_MLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_IN_SEQ;
                NDISK_info->nZone = 1;              /* number of zones */
                NDISK_info->nPagePerBlock = 128;    /* pages per block */
                NDISK_info->nPageSize = 2048;
                NDISK_info->nBlockPerZone = 8192;   /* blocks per zone */
                NDISK_info->nLBPerZone = 8000;      /* logical blocks per zone */
            }

            pSM->uSectorPerFlash = 4091904;
            pSM->bIsMulticycle = TRUE;
            pSM->bIsNandECC8 = TRUE;
            break;
    #endif
        default:
            // 2013/9/25, support MXIC MX30LF1208AA NAND flash
            if ((tempID[0]==0xC2)&&(tempID[1]==0xF0)&&(tempID[2]==0x80)&&(tempID[3]==0x1D))
            {
                pSM->uBlockPerFlash  = 511;         // block index with 0-base. = physical blocks - 1
                pSM->uPagePerBlock   = 64;
                pSM->nPageSize       = NAND_PAGE_2KB;
                pSM->uSectorPerBlock = pSM->nPageSize / 512 * pSM->uPagePerBlock;
                pSM->bIsMLCNand      = FALSE;
                pSM->bIsMulticycle   = FALSE;
                pSM->bIsNandECC8     = TRUE;

                NDISK_info->NAND_type     = NAND_TYPE_SLC;
                NDISK_info->write_page_in_seq = NAND_TYPE_PAGE_OUT_SEQ;
                NDISK_info->nZone         = 1;      // number of zones
                NDISK_info->nBlockPerZone = pSM->uBlockPerFlash + 1;    // blocks per zone
                NDISK_info->nPagePerBlock = pSM->uPagePerBlock;
                NDISK_info->nPageSize     = pSM->nPageSize;
                // why nLBPerZone is not 512 ? sicSMInit() had reserved %2 blocks for bad block base on this value.
                NDISK_info->nLBPerZone    = 500;    // logical blocks per zone

                pSM->uSectorPerFlash = pSM->uSectorPerBlock * NDISK_info->nLBPerZone / 1000 * 999;

                // The first ID of this NAND is 0xC2 BUT it is NOT NAND ROM (read only)
                // So, we MUST modify the configuration of it
                //      1. change pSM->bIsCheckECC to TRUE to enable ECC feature;
                //      2. assign a fake vendor_ID to make NVTFAT can write data to this NAND disk.
                //         (GNAND will check vendor_ID and set disk to DISK_TYPE_READ_ONLY if it is 0xC2)
                pSM->bIsCheckECC = TRUE;
                NDISK_info->vendor_ID = 0xFF;   // fake vendor_ID

                break;
            }
            sysprintf("ERROR: SM ID not support!! [%02x][%02x][%02x][%02x][%02x]\n", tempID[0], tempID[1], tempID[2], tempID[3], tempID[4]);
            return FMI_SM_ID_ERR;
    }

    sysprintf("NAND: Found %s NAND, ID [%02x][%02x][%02x][%02x][%02x], page size %d, BCH T%d\n",
        pSM->bIsMLCNand ? "MLC" : "SLC",
        tempID[0], tempID[1], tempID[2], tempID[3], tempID[4],
        pSM->nPageSize,
        pSM->bIsNandECC4*4 + pSM->bIsNandECC8*8 + pSM->bIsNandECC12*12 + pSM->bIsNandECC15*15
        );
    return 0;
}


INT fmiSM2BufferM(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT8 ucColAddr)
{
    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    outpw(REG_SMCMD, 0x00);     // read command
    outpw(REG_SMADDR, ucColAddr);       // CA0 - CA7
    outpw(REG_SMADDR, uSector & 0xff);  // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    return 0;
}


INT fmiSM2BufferM_RA(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT8 ucColAddr)
{
    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    outpw(REG_SMCMD, 0x50);     // read command
    outpw(REG_SMADDR, ucColAddr);       // CA0 - CA7
    outpw(REG_SMADDR, uSector & 0xff);  // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    return 0;
}


INT fmiSMECCErrCount(UINT32 ErrSt, BOOL bIsECC8)
{
    unsigned int volatile errCount;

    if ((ErrSt & 0x02) || (ErrSt & 0x200) || (ErrSt & 0x20000) || (ErrSt & 0x2000000))
    {
#ifdef DEBUG
        printf("uncorrectable!![0x%x]\n", ErrSt);
#endif
        return FMI_SM_ECC_ERROR;
    }
    if (ErrSt & 0x01)
    {
        errCount = (ErrSt >> 2) & 0xf;
#ifdef DEBUG
        if (bIsECC8)
            printf("Field 5 have %d error!!\n", errCount);
        else
            printf("Field 1 have %d error!!\n", errCount);
#endif
    }
    if (ErrSt & 0x100)
    {
        errCount = (ErrSt >> 10) & 0xf;
#ifdef DEBUG
        if (bIsECC8)
            printf("Field 6 have %d error!!\n", errCount);
        else
            printf("Field 2 have %d error!!\n", errCount);
#endif
    }
    if (ErrSt & 0x10000)
    {
        errCount = (ErrSt >> 18) & 0xf;
#ifdef DEBUG
        if (bIsECC8)
            printf("Field 7 have %d error!!\n", errCount);
        else
            printf("Field 3 have %d error!!\n", errCount);
#endif
    }
    if (ErrSt & 0x1000000)
    {
        errCount = (ErrSt >> 26) & 0xf;
#ifdef DEBUG
        if (bIsECC8)
            printf("Field 8 have %d error!!\n", errCount);
        else
            printf("Field 4 have %d error!!\n", errCount);
#endif
    }
    return errCount;
}

static VOID fmiSM_CorrectData_BCH(UINT8 ucFieidIndex, UINT8 ucErrorCnt, UINT8* pDAddr)
{
    // ECC is based on 512+32, when ECC code error is encountered, we must coorect it by this base

    UINT32 uaData[16], uaAddr[16];
    UINT32 uaErrorData[4];
    UINT8   ii, jj;
    UINT32 uPageSize, spareSize;

    uPageSize = inpw(REG_SMCSR) & SMCR_PSIZE;

    jj = ucErrorCnt/4;
    jj ++;
    if (jj > 4) jj = 4;

    for(ii=0; ii<jj; ii++)
    {
        uaErrorData[ii] = inpw(REG_BCH_ECC_DATA0 + ii*4);
    }

    for(ii=0; ii<jj; ii++)
    {
        uaData[ii*4+0] = uaErrorData[ii] & 0xff;
        uaData[ii*4+1] = (uaErrorData[ii]>>8) & 0xff;
        uaData[ii*4+2] = (uaErrorData[ii]>>16) & 0xff;
        uaData[ii*4+3] = (uaErrorData[ii]>>24) & 0xff;
    }

    jj = ucErrorCnt/2;
    jj ++;
    if (jj > 8) jj = 8;

    for(ii=0; ii<jj; ii++)
    {
        uaAddr[ii*2+0] = inpw(REG_BCH_ECC_ADDR0 + ii*4) & 0x1fff;
        uaAddr[ii*2+1] = (inpw(REG_BCH_ECC_ADDR0 + ii*4)>>16) & 0x1fff;
    }

    pDAddr += (ucFieidIndex-1)*0x200;

    for(ii=0; ii<ucErrorCnt; ii++)
    {
        if (uPageSize == PSIZE_8K)
        {
            switch(inpw(REG_SMCSR) & SMCR_BCH_TSEL)
            {
                case BCH_T4:    // 8K+256
                default:
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 8*(ucFieidIndex-1);   // field offset, only Field-0
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 7 - uaAddr[ii];
                            uaAddr[ii] += 8*(ucFieidIndex-1);   // field offset
                            *((UINT8*)REG_SMRA_32+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;

                case BCH_T8:    // 8K+256
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 15*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 14 - uaAddr[ii];
                            uaAddr[ii] += 15*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_4+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;

                case BCH_T12:   // 8K+376
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 23*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 22 - uaAddr[ii];
                            uaAddr[ii] += 23*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_2+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;

                case BCH_T15:
                    break;
            }
        }
        else if (uPageSize == PSIZE_4K)
        {
            switch(inpw(REG_SMCSR) & SMCR_BCH_TSEL)
            {
                case BCH_T4:    // 4K+128
                default:
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 8*(ucFieidIndex-1);   // field offset, only Field-0
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 7 - uaAddr[ii];
                            uaAddr[ii] += 8*(ucFieidIndex-1);   // field offset
                            *((UINT8*)REG_SMRA_16+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;

                case BCH_T8:    // 4K+128
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 15*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 14 - uaAddr[ii];
                            uaAddr[ii] += 15*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_2+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;

                case BCH_T12:   // 4K+216
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 23*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 22 - uaAddr[ii];
                            uaAddr[ii] += 23*(ucFieidIndex-1);  // field offset

                    #ifdef OPT_SUPPORT_H27UAG8T2A
                            // 4K+224
                            spareSize = inpw(REG_SMREAREA_CTL) & SMRE_REA128_EXT;
                            // *((UINT8*)REG_SMRA_10+uaAddr[ii]) ^=  uaData[ii];
                             *((UINT8*)REG_SMRA_0+(spareSize-184)+uaAddr[ii]) ^=  uaData[ii];
                    #else
                            // 4K+216
                            *((UINT8*)REG_SMRA_8+uaAddr[ii]) ^=  uaData[ii];
                    #endif
                        }
                    }
                    break;
            }
        }
        else if (uPageSize == PSIZE_2K)
        {
            switch(inpw(REG_SMCSR) & SMCR_BCH_TSEL)
            {
                case BCH_T4:
                default:
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 8*(ucFieidIndex-1);   // field offset, only Field-0
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 7 - uaAddr[ii];
                            uaAddr[ii] += 8*(ucFieidIndex-1);   // field offset
                            *((UINT8*)REG_SMRA_8+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;

                case BCH_T8:
                    if (uaAddr[ii] < 512)
                        *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
                    else
                    {
                        if (uaAddr[ii] < 515)
                        {
                            uaAddr[ii] -= 512;
                            uaAddr[ii] += 15*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                        }
                        else
                        {
                            uaAddr[ii] = 543 - uaAddr[ii];
                            uaAddr[ii] = 14 - uaAddr[ii];
                            uaAddr[ii] += 15*(ucFieidIndex-1);  // field offset
                            *((UINT8*)REG_SMRA_1+uaAddr[ii]) ^=  uaData[ii];
                        }
                    }
                    break;
            }
        }
        else
        {
            if (uaAddr[ii] < 512)
                *(pDAddr+uaAddr[ii]) ^=  uaData[ii];
            else
            {
                if (uaAddr[ii] < 515)
                {
                    uaAddr[ii] -= 512;
                    *((UINT8*)REG_SMRA_0+uaAddr[ii]) ^=  uaData[ii];
                }
                else
                {
                    uaAddr[ii] = 543 - uaAddr[ii];
                    uaAddr[ii] = 7 - uaAddr[ii];
                    *((UINT8*)REG_SMRA_2+uaAddr[ii]) ^=  uaData[ii];
                }
            }
        }
    }
}

INT fmiSM_Read_512(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT32 uDAddr)
{
    int volatile ret=0;
    UINT32 uStatus;
    UINT32 uErrorCnt;
    volatile UINT32 uError = 0;

    outpw(REG_DMACSAR, uDAddr);
    ret = fmiSM2BufferM(pSM, uSector, 0);
    if (ret < 0)
        return ret;

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  //_SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DRD_EN);

#ifdef _SIC_USE_INT_
    while(!_fmi_bIsSMDataReady);
#else
    while(1)
    {
        if (!(inpw(REG_SMCSR) & SMCR_DRD_EN))
            break;
    }
#endif

    if (pSM->bIsCheckECC)
    {
        while(1)
        {
            if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)
            {
                if (inpw(REG_SMCSR) & BCH_T4)   // BCH_ECC selected
                {
                    uStatus = inpw(REG_SM_ECC_ST0);
                    uStatus &= 0x3f;

                    if ((uStatus & 0x03)==0x01)         // correctable error in 1st field
                    {
                        uErrorCnt = uStatus >> 2;
                        fmiSM_CorrectData_BCH(1, uErrorCnt, (UINT8*)uDAddr);

                #ifdef DEBUG
                        printf("Field 1 have %d error!!\n", uErrorCnt);
                #endif
                    }
                    else if (((uStatus & 0x03)==0x02)
                          ||((uStatus & 0x03)==0x03))   // uncorrectable error or ECC error
                    {
                #ifdef DEBUG
                        printf("SM uncorrectable error is encountered, %4x !!\n", uStatus);
                #endif
                        uError = 1;
                    }
                }
                else
                {
                #ifdef DEBUG
                    printf("Wrong BCH setting for page-512 NAND !!\n");
                #endif
                    uError = 2;
                }
                outpw(REG_SMISR, SMISR_ECC_FIELD_IF);       // clear ECC_FLD_Error
            }

            if (inpw(REG_SMISR) & SMISR_DMA_IF)      // wait to finish DMAC transfer.
            {
                if ( !(inpw(REG_SMISR) & SMISR_ECC_FIELD_IF) )
                    break;
            }
        }
    }
    else
        outpw(REG_SMISR, SMISR_ECC_FIELD_IF);

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    if (uError)
        return -1;

    return 0;
}

VOID fmiBuffer2SMM(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT8 ucColAddr)
{
    // set the spare area configuration
    /* write byte 514, 515 as used page */
    outpw(REG_SMRA_0, 0x0000FFFF);
    outpw(REG_SMRA_1, 0xFFFFFFFF);

    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, ucColAddr);       // CA0 - CA7
    outpw(REG_SMADDR, uSector & 0xff);  // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }
}

INT fmiSM_Write_512(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT32 uSAddr)
{
    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_REDUN_AUTO_WEN);

    outpw(REG_DMACSAR, uSAddr);
    fmiBuffer2SMM(pSM, uSector, 0);

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  // _SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DWR_EN);

    while(1)
    {
#ifdef _SIC_USE_INT_
        if (_fmi_bIsSMDataReady)
#else
        if (inpw(REG_SMISR) & SMISR_DMA_IF)         // wait to finish DMAC transfer.
#endif  //_SIC_USE_INT_
            break;
    }

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag

    outpw(REG_SMCMD, 0x10);     // auto program command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    if (fmiSMCheckStatus(pSM) != 0)
    {
#ifdef DEBUG
        printf("fmiSM_Write_512: data error!!\n");
#endif
        return FMI_SM_STATE_ERROR;
    }
    return 0;
}

INT fmiSM_Read_2K(FMI_SM_INFO_T *pSM, UINT32 uPage, UINT32 uDAddr)
{
    UINT32 uStatus;
    UINT32 uErrorCnt, ii;
    volatile UINT32 uError = 0;

//  fmiSM_Reset(pSM);

    while(inpw(REG_DMACCSR) & FMI_BUSY);    // wait DMAC FMI ready;
    outpw(REG_DMACCSR, inpw(REG_DMACCSR) | DMAC_EN);
    outpw(REG_DMACSAR, uDAddr); // set DMA transfer starting address

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)   /* ECC_FLD_IF */
    {
        //printf("read: ECC error!!\n");
        outpw(REG_SMISR, SMISR_ECC_FIELD_IF);
    }

    outpw(REG_SMCMD, 0x00);     // read command
    outpw(REG_SMADDR, 0);   // CA0 - CA7
    outpw(REG_SMADDR, 0);   // CA8 - CA11
    outpw(REG_SMADDR, uPage & 0xff);    // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uPage >> 8) & 0xff)|EOA_SM);        // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uPage >> 8) & 0xff);                 // PA8 - PA15
        outpw(REG_SMADDR, ((uPage >> 16) & 0xff)|EOA_SM);       // PA16 - PA17
    }
    outpw(REG_SMCMD, 0x30);     // read command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  //_SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DRD_EN);

//  if ((pSM->bIsCheckECC) || (inpw(REG_SMCSR)&SMCR_ECC_CHK) )
    if (pSM->bIsCheckECC)
    {
        while(1)
        {
            if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)
            {
                uStatus = inpw(REG_SM_ECC_ST0);
                for (ii=1; ii<5; ii++)
                {
                #if 0
                    if (!(uStatus & 0x03))
                    {
                        uStatus >>= 8;
                        continue;
                    }
                #endif

                    if ((uStatus & 0x03)==0x01)  // correctable error in 1st field
                    {
                        uErrorCnt = uStatus >> 2;
                        fmiSM_CorrectData_BCH(ii, uErrorCnt, (UINT8*)uDAddr);
                #ifdef DEBUG
                        printf("Field %d have %d error!!\n", ii, uErrorCnt);
                #endif
                        break;
                    }
                    else if (((uStatus & 0x03)==0x02)
                          ||((uStatus & 0x03)==0x03)) // uncorrectable error or ECC error in 1st field
                    {
                #ifdef DEBUG
                        printf("SM uncorrectable error is encountered, %4x !!\n", uStatus);
                #endif
                        uError = 1;
                        break;
                    }
                    uStatus >>= 8;
                }

                outpw(REG_SMISR, SMISR_ECC_FIELD_IF);       // clear ECC_FLD_Error
            }

    #ifdef _SIC_USE_INT_
            if (_fmi_bIsSMDataReady)
            {
                if ( !(inpw(REG_SMISR) & SMISR_ECC_FIELD_IF) )
                    break;
            }
    #else
            if (inpw(REG_SMISR) & SMISR_DMA_IF)      // wait to finish DMAC transfer.
            {
                if ( !(inpw(REG_SMISR) & SMISR_ECC_FIELD_IF) )
                    break;
            }
    #endif  //_SIC_USE_INT_
        }
    }
    else
    {
        while(1)
        {
            outpw(REG_SMISR, SMISR_ECC_FIELD_IF);
            if (inpw(REG_SMISR) & SMISR_DMA_IF)
            {
                outpw(REG_SMISR, SMISR_DMA_IF);     // clear DMA flag
                break;
            }
        }
    }

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    if (uError)
        return -1;

    return 0;
}


INT fmiSM_Read_RA(FMI_SM_INFO_T *pSM, UINT32 uPage, UINT32 ucColAddr)
{
    /* clear R/B flag */

//  fmiSM_Reset(pSM);

    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    outpw(REG_SMCMD, 0x00);     // read command
    outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
//  outpw(REG_SMADDR, (ucColAddr >> 8) & 0x1f); // CA8 - CA12
    outpw(REG_SMADDR, (ucColAddr >> 8) & 0xFF); // CA8 - CA12
    outpw(REG_SMADDR, uPage & 0xff);            // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uPage >> 8) & 0xff)|EOA_SM);        // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uPage >> 8) & 0xff);                 // PA8 - PA15
        outpw(REG_SMADDR, ((uPage >> 16) & 0xff)|EOA_SM);       // PA16 - PA17
    }
    outpw(REG_SMCMD, 0x30);     // read command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    return 0;
}

INT fmiSM_Read_RA_512(FMI_SM_INFO_T *pSM, UINT32 uPage, UINT32 uColumm)
{
    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    outpw(REG_SMCMD, 0x50);     // read command
    outpw(REG_SMADDR, uColumm);
    outpw(REG_SMADDR, uPage & 0xff);    // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uPage >> 8) & 0xff)|EOA_SM);        // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uPage >> 8) & 0xff);                 // PA8 - PA15
        outpw(REG_SMADDR, ((uPage >> 16) & 0xff)|EOA_SM);       // PA16 - PA17
    }

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    return 0;
}


INT fmiSM_Write_2K(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT32 ucColAddr, UINT32 uSAddr)
{

//  fmiSM_Reset(pSM);

    /* enable DMAC */
    while(inpw(REG_DMACCSR) & FMI_BUSY);    // wait DMAC FMI ready;
    outpw(REG_DMACCSR, inpw(REG_DMACCSR) | DMAC_EN);
    outpw(REG_DMACSAR, uSAddr); // set DMA transfer starting address

    // set the spare area configuration
    /* write byte 2050, 2051 as used page */
    outpw(REG_SMRA_0, 0x0000FFFF);
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_REDUN_AUTO_WEN);

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)   /* ECC_FLD_IF */
    {
        //printf("error sector !!\n");
        outpw(REG_SMISR, SMISR_ECC_FIELD_IF);
    }

    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
//  outpw(REG_SMADDR, (ucColAddr >> 8) & 0x0f); // CA8 - CA11
    outpw(REG_SMADDR, (ucColAddr >> 8) & 0xff); // CA8 - CA11
    outpw(REG_SMADDR, uSector & 0xff);          // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  //_SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DWR_EN);

    while(1)
    {
#ifdef _SIC_USE_INT_
        if (_fmi_bIsSMDataReady)
#else
        if (inpw(REG_SMISR) & SMISR_DMA_IF)             // wait to finish DMAC transfer.
#endif  //_SIC_USE_INT_
            break;
    }

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag

    outpw(REG_SMCMD, 0x10);     // auto program command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    if (fmiSMCheckStatus(pSM) != 0)
    {
#ifdef DEBUG
        printf("fmiSM_Write_2K: data error!!\n");
#endif
        sysprintf("ERROR: fmiSM_Write_2K() write data error on page %d since NAND status.\n", uSector);
        return FMI_SM_STATE_ERROR;
    }

#if 1
    if (inpw(REG_SMRA_0) != 0x0000FFFF)
        while(1);
#endif
    return 0;
}


INT fmiSM_Write_2K_ALC(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT32 ucColAddr, UINT32 uSAddr)
{
    outpw(REG_DMACSAR, uSAddr); // set DMA transfer starting address

    // set the spare area configuration
    /* write byte 2050, 2051 as used page */
    outpw(REG_SMRA_0, 0x0000FFFF);
    outpw(REG_SMCSR, inpw(REG_SMCSR) & ~SMCR_REDUN_AUTO_WEN);

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
    outpw(REG_SMADDR, (ucColAddr >> 8) & 0x0f); // CA8 - CA11
    outpw(REG_SMADDR, uSector & 0xff);          // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|0x80000000);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);                   // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|0x80000000);     // PA16 - PA17
    }

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  // _SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DWR_EN);

    while(1)
    {
#ifdef _SIC_USE_INT_
        if (_fmi_bIsSMDataReady)
#else
        if (inpw(REG_SMISR) & SMISR_DMA_IF)     // wait to finish DMAC transfer.
#endif  //_SIC_USE_INT_
            break;
    }

    outpw(REG_SMISR, SMISR_DMA_IF);     // clear DMA flag

    {
        int i;
        UINT8 *u8pData;

        u8pData = (UINT8 *)REG_SMRA_0;
        for (i=0; i<64; i++)
            outpw(REG_SMDATA, *u8pData++);
    }

    outpw(REG_SMCMD, 0x10);     // auto program command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    if (fmiSMCheckStatus(pSM) != 0)
    {
#ifdef DEBUG
        printf("fmiSM_Write_2K: data error!!\n");
#endif
        return FMI_SM_STATE_ERROR;
    }
    return 0;
}

INT fmiSM_Read_4K(FMI_SM_INFO_T *pSM, UINT32 uPage, UINT32 uDAddr)
{
    UINT32 uStatus;
    UINT32 uErrorCnt, ii, jj;
    volatile UINT32 uError = 0;

    outpw(REG_DMACSAR, uDAddr); // set DMA transfer starting address

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    outpw(REG_SMCMD, 0x00);     // read command
    outpw(REG_SMADDR, 0);               // CA0 - CA7
    outpw(REG_SMADDR, 0);               // CA8 - CA11
    outpw(REG_SMADDR, uPage & 0xff);    // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uPage >> 8) & 0xff)|EOA_SM);        // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uPage >> 8) & 0xff);                 // PA8 - PA15
        outpw(REG_SMADDR, ((uPage >> 16) & 0xff)|EOA_SM);       // PA16 - PA17
    }
    outpw(REG_SMCMD, 0x30);     // read command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  //_SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DRD_EN);

    if ((pSM->bIsCheckECC) || (inpw(REG_SMCSR)&SMCR_ECC_CHK) )
    {
        while(1)
        {
            if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)
            {
                for (jj=0; jj<2; jj++)
                {
                    uStatus = inpw(REG_SM_ECC_ST0+jj*4);
                    if (!uStatus)
                        continue;

                    for (ii=1; ii<5; ii++)
                    {
                        if (!(uStatus & 0x03))
                        {
                            uStatus >>= 8;
                            continue;
                        }

                        if ((uStatus & 0x03)==0x01)  // correctable error in 1st field
                        {
                            uErrorCnt = uStatus >> 2;
                            fmiSM_CorrectData_BCH(jj*4+ii, uErrorCnt, (UINT8*)uDAddr);
                    #ifdef DEBUG
                            printf("Field %d have %d error!!\n", jj*4+ii, uErrorCnt);
                    #endif
                            break;
                        }
                        else if (((uStatus & 0x03)==0x02)
                              ||((uStatus & 0x03)==0x03)) // uncorrectable error or ECC error in 1st field
                        {
                    #ifdef _DEBUG
                            printf("SM uncorrectable BCH error is encountered !!\n");
                    #endif
                            uError = 1;
                            break;
                        }
                        uStatus >>= 8;
                    }
                }
                outpw(REG_SMISR, SMISR_ECC_FIELD_IF);       // clear ECC_FLD_Error
            }

    #ifdef _SIC_USE_INT_
            if (_fmi_bIsSMDataReady)
            {
                if ( !(inpw(REG_SMISR) & SMISR_ECC_FIELD_IF) )
                    break;
            }
    #else
            if (inpw(REG_SMISR) & SMISR_DMA_IF)      // wait to finish DMAC transfer.
            {
                if ( !(inpw(REG_SMISR) & SMISR_ECC_FIELD_IF) )
                    break;
            }
    #endif  //_SIC_USE_INT_
        }
    }
    else
    {
        while(1)
        {
            outpw(REG_SMISR, SMISR_ECC_FIELD_IF);
            if (inpw(REG_SMISR) & SMISR_DMA_IF)
            {
                outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
                break;
            }
        }
    }

    if (uError)
        return -1;

    return 0;
}


INT fmiSM_Write_4K(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT32 ucColAddr, UINT32 uSAddr)
{
    outpw(REG_DMACSAR, uSAddr); // set DMA transfer starting address

    // set the spare area configuration
    /* write byte 2050, 2051 as used page */
    outpw(REG_SMRA_0, 0x0000FFFF);
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_REDUN_AUTO_WEN);

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
//  outpw(REG_SMADDR, (ucColAddr >> 8) & 0x1f); // CA8 - CA12
    outpw(REG_SMADDR, (ucColAddr >> 8) & 0xff); // CA8 - CA12
    outpw(REG_SMADDR, uSector & 0xff);          // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  //_SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DWR_EN);

    while(1)
    {
#ifdef _SIC_USE_INT_
        if (_fmi_bIsSMDataReady)
#else
        if (inpw(REG_SMISR) & SMISR_DMA_IF)     // wait to finish DMAC transfer.
#endif  //_SIC_USE_INT_
            break;
    }

    outpw(REG_SMISR, SMISR_DMA_IF);     // clear DMA flag

    outpw(REG_SMCMD, 0x10);     // auto program command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    if (fmiSMCheckStatus(pSM) != 0)
    {
#ifdef DEBUG
        printf("fmiSM_Write_4K: data error!!\n");
#endif
        return FMI_SM_STATE_ERROR;
    }
    return 0;
}


INT fmiSM_Write_4K_ALC(FMI_SM_INFO_T *pSM, UINT32 uSector, UINT32 ucColAddr, UINT32 uSAddr)
{
    outpw(REG_DMACSAR, uSAddr); // set DMA transfer starting address

    // set the spare area configuration
    /* write byte 2050, 2051 as used page */
    outpw(REG_SMRA_0, 0x0000FFFF);
    outpw(REG_SMCSR, inpw(REG_SMCSR) & ~SMCR_REDUN_AUTO_WEN);

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
    outpw(REG_SMADDR, (ucColAddr >> 8) & 0x1f); // CA8 - CA12
    outpw(REG_SMADDR, uSector & 0xff);          // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
        outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }

#ifdef _SIC_USE_INT_
    _fmi_bIsSMDataReady = FALSE;
#endif  // _SIC_USE_INT_

    outpw(REG_SMISR, SMISR_DMA_IF);                 // clear DMA flag
    outpw(REG_SMISR, SMISR_ECC_FIELD_IF);           // clear ECC_FIELD flag
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_DWR_EN);

    while(1)
    {
#ifdef _SIC_USE_INT_
        if (_fmi_bIsSMDataReady)
#else
        if (inpw(REG_SMISR) & SMISR_DMA_IF)     // wait to finish DMAC transfer.
#endif  //_SIC_USE_INT_
            break;
    }

    outpw(REG_SMISR, SMISR_DMA_IF);     // clear DMA flag

    {
        int i;
        UINT8 *u8pData;

        u8pData = (UINT8 *)REG_SMRA_0;
        for (i=0; i<218; i++)
            outpw(REG_SMDATA, *u8pData++);
    }

    outpw(REG_SMCMD, 0x10);     // auto program command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    if (fmiSMCheckStatus(pSM) != 0)
    {
#ifdef DEBUG
        printf("fmiSM_Write_2K: data error!!\n");
#endif
        return FMI_SM_STATE_ERROR;
    }
    return 0;
}

// mhkuo
INT fmiCheckInvalidBlock(FMI_SM_INFO_T *pSM, UINT32 BlockNo)
{
    int volatile status=0;
    unsigned int volatile sector;
    unsigned char volatile data512=0xff, data517=0xff, blockStatus=0xff;

    if (BlockNo == 0)
        return 0;

    sector = BlockNo * pSM->uPagePerBlock;

    if (pSM->nPageSize == NAND_PAGE_512B)
        status = fmiSM2BufferM_RA(pSM, sector, 0);
    else
        status = fmiSM_Read_RA(pSM, sector, pSM->nPageSize);

    if (status < 0)
    {
#ifdef DEBUG
        printf("fmiCheckInvalidBlock 0x%x\n", status);
#endif
        return 1;
    }

    if (pSM->nPageSize == NAND_PAGE_512B)
    {
        data512 = inpw(REG_SMDATA) & 0xff;
        data517 = inpw(REG_SMDATA);
        data517 = inpw(REG_SMDATA);
        data517 = inpw(REG_SMDATA);
        data517 = inpw(REG_SMDATA);
        data517 = inpw(REG_SMDATA) & 0xff;
//      if ((data512 != 0xFF) || (data517 != 0xFF))
        if ((data512 == 0xFF) && (data517 == 0xFF))
        {
            fmiSM_Reset(pSM);
            status = fmiSM2BufferM_RA(pSM, sector+1, 0);
            if (status < 0)
            {
    #ifdef DEBUG
                    printf("fmiCheckInvalidBlock 0x%x\n", status);
    #endif
                    return 1;
            }
            data512 = inpw(REG_SMDATA) & 0xff;
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA) & 0xff;
            if ((data512 != 0xFF) || (data517 != 0xFF))
            {
                fmiSM_Reset(pSM);
                return 1;   // invalid block
            }
        }
        else
        {
            fmiSM_Reset(pSM);
            return 1;   // invalid block
        }
    }
    else
    {
        blockStatus = inpw(REG_SMDATA) & 0xff;
        if (blockStatus == 0xFF)
        {
            fmiSM_Reset(pSM);

            if (pSM->bIsMLCNand == TRUE)
                sector = (BlockNo+1) * pSM->uPagePerBlock - 1;
            else
                sector++;

            status = fmiSM_Read_RA(pSM, sector, pSM->nPageSize);

            if (status < 0)
            {
    #ifdef DEBUG
                    printf("fmiCheckInvalidBlock 0x%x\n", status);
    #endif
                    return 1;
            }
            blockStatus = inpw(REG_SMDATA) & 0xff;
            if (blockStatus != 0xFF)
            {
                fmiSM_Reset(pSM);
                return 1;   // invalid block
            }
        }
        else
        {
            fmiSM_Reset(pSM);
            return 1;   // invalid block
        }
    }

    fmiSM_Reset(pSM);
    return 0;
}

static void sicSMselect(INT chipSel)
{
    if (chipSel == 0)
    {
        outpw(REG_GPDFUN, inpw(REG_GPDFUN) | 0x0003CC00);       // enable NAND NWR/NRD/RB0 pins
        outpw(REG_GPEFUN, inpw(REG_GPEFUN) | 0x00F30000);       // enable NAND ALE/CLE/CS0 pins
        outpw(REG_SMCSR, inpw(REG_SMCSR) & ~SMCR_CS0);
        outpw(REG_SMCSR, inpw(REG_SMCSR) |  SMCR_CS1);
    }
    else
    {
        outpw(REG_GPDFUN, inpw(REG_GPDFUN) | 0x0003F000);       // enable NAND NWR/NRD/RB1 pins
        outpw(REG_GPEFUN, inpw(REG_GPEFUN) | 0x00FC0000);       // enable NAND ALE/CLE/CS1 pins
        outpw(REG_SMCSR, inpw(REG_SMCSR) & ~SMCR_CS1);
        outpw(REG_SMCSR, inpw(REG_SMCSR) |  SMCR_CS0);
    }

    //--- 2014/2/26, Reset SD controller and DMAC to keep clean status for next access.
    // Reset DMAC engine and interrupt satus
    outpw(REG_DMACCSR, inpw(REG_DMACCSR) | DMAC_SWRST | DMAC_EN);
    while(inpw(REG_DMACCSR) & DMAC_SWRST);
    outpw(REG_DMACCSR, inpw(REG_DMACCSR) | DMAC_EN);
    outpw(REG_DMACISR, WEOT_IF | TABORT_IF);    // clear all interrupt flag

    // Reset FMI engine and interrupt status
    outpw(REG_FMICR, FMI_SWRST);
    while(inpw(REG_FMICR) & FMI_SWRST);
    outpw(REG_FMIISR, FMI_DAT_IF);              // clear all interrupt flag

    // Reset NAND engine and interrupt status
    outpw(REG_FMICR, FMI_SM_EN);
    outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_SM_SWRST);
    while(inpw(REG_SMCSR) & SDCR_SWRST);
    outpw(REG_SMISR, 0xFFFFFFFF);               // clear all interrupt flag
}

static INT fmiNormalCheckBlock(FMI_SM_INFO_T *pSM, UINT32 BlockNo)
{
    int volatile status=0;
    unsigned int volatile sector;
    unsigned char data, data517;

    _fmi_pSMBuffer = (UINT8 *)((UINT32)_fmi_ucSMBuffer | 0x80000000);

    /* MLC check the 2048 byte of last page per block */
    if (pSM->bIsMLCNand == TRUE)
    {
        if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            sector = (BlockNo+1) * pSM->uPagePerBlock - 1;
            /* Read 2048 byte */

            status = fmiSM_Read_RA(pSM, sector, 2048);
            if (status < 0)
            {
#ifdef DEBUG
                printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                return 1;
            }
            data = inpw(REG_SMDATA) & 0xff;
            if (data != 0xFF)
                return 1;   // invalid block
        }
        else if (pSM->nPageSize == NAND_PAGE_4KB)
        {
            sector = (BlockNo+1) * pSM->uPagePerBlock - 1;

            /* Read 4096 byte */
            status = fmiSM_Read_RA(pSM, sector, 4096);
            if (status < 0)
            {
#ifdef DEBUG
                printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                return 1;
            }
            data = inpw(REG_SMDATA) & 0xff;
            if (data != 0xFF)
                return 1;   // invalid block
        }
    }
    /* SLC check the 2048 byte of 1st or 2nd page per block */
    else    // SLC
    {
        sector = BlockNo * pSM->uPagePerBlock;
        if (pSM->nPageSize == NAND_PAGE_4KB)
        {
            status = fmiSM_Read_RA(pSM, sector, 4096);
            if (status < 0)
            {
#ifdef DEBUG
                printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                return 1;
            }
            data = inpw(REG_SMDATA) & 0xff;
            if (data == 0xFF)
            {
#ifdef DEBUG
//              printf("find bad block, check next page to confirm it.\n");
#endif
                status = fmiSM_Read_RA(pSM, sector+1, 4096);
                if (status < 0)
                {
#ifdef DEBUG
                    printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                    return 1;
                }
                data = inpw(REG_SMDATA) & 0xff;
                if (data != 0xFF)
                {
#ifdef DEBUG
                    printf("find bad block is conformed.\n");
#endif
                    return 1;   // invalid block
                }
            }
            else
            {
#ifdef DEBUG
                printf("find bad block is conformed.\n");
#endif
                return 1;   // invalid block
            }
        }
        else if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            status = fmiSM_Read_RA(pSM, sector, 2048);
            if (status < 0)
            {
#ifdef DEBUG
                printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                return 1;
            }
            data = inpw(REG_SMDATA) & 0xff;
            if (data == 0xFF)
            {
#ifdef DEBUG
//              printf("find bad block, check next page to confirm it.\n");
#endif
                status = fmiSM_Read_RA(pSM, sector+1, 2048);
                if (status < 0)
                {
#ifdef DEBUG
                    printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                    return 1;
                }
                data = inpw(REG_SMDATA) & 0xff;
                if (data != 0xFF)
                {
#ifdef DEBUG
                    printf("find bad block is conformed.\n");
#endif
                    return 1;   // invalid block
                }
            }
            else
            {
#ifdef DEBUG
                    printf("find bad block is conformed.\n");
#endif
                    return 1;   // invalid block
            }
        }
        else    /* page size 512B */
        {
            status = fmiSM2BufferM_RA(pSM, sector, 0);
            if (status < 0)
            {
#ifdef DEBUG
                printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                return 1;
            }
            data = inpw(REG_SMDATA) & 0xff;
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA);
            data517 = inpw(REG_SMDATA) & 0xff;
    //      if ((data != 0xFF) || (data517 != 0xFF))
            if ((data == 0xFF) && (data517 == 0xFF))
            {
                fmiSM_Reset(pSM);
                status = fmiSM2BufferM_RA(pSM, sector+1, 0);
                if (status < 0)
                {
#ifdef DEBUG
                    printf("fmiNormalCheckBlock 0x%x\n", status);
#endif
                    return 1;
                }
                data = inpw(REG_SMDATA) & 0xff;
                data517 = inpw(REG_SMDATA);
                data517 = inpw(REG_SMDATA);
                data517 = inpw(REG_SMDATA);
                data517 = inpw(REG_SMDATA);
                data517 = inpw(REG_SMDATA) & 0xff;
                if ((data != 0xFF) || (data517 != 0xFF))
                {
                    fmiSM_Reset(pSM);
                    return 1;   // invalid block
                }
            }
            else
            {
#ifdef DEBUG
                    printf("find bad block is conformed.\n");
#endif
                    fmiSM_Reset(pSM);
                    return 1;   // invalid block
            }
            fmiSM_Reset(pSM);
        }
    }
    return 0;
}


/* function pointer */
FMI_SM_INFO_T *pSM0=0, *pSM1=0;
static INT sicSMInit(INT chipSel, NDISK_T *NDISK_info)
{
    int status=0, count;

        outpw(REG_AHBCLK, inp32(REG_AHBCLK) | SIC_CKE | NAND_CKE);

    outpw(REG_DMACCSR, inpw(REG_DMACCSR) | DMAC_EN);
    outpw(REG_DMACCSR, inpw(REG_DMACCSR) | DMAC_SWRST);
    while(inpw(REG_DMACCSR) & DMAC_SWRST);
    
    outpw(REG_FMICR, FMI_SM_EN);


    if ((_nand_init0 == 0) && (_nand_init1 == 0))
    {
        // enable SM
        /* select NAND control pin used */
//      outpw(REG_SMTCR, 0x20304);
//      outpw(REG_SMTCR, 0x10205);
        outpw(REG_SMTCR, 0x20305);
        outpw(REG_SMCSR, (inpw(REG_SMCSR) & ~SMCR_PSIZE) | PSIZE_512);
        outpw(REG_SMCSR, inpw(REG_SMCSR) |  SMCR_ECC_3B_PROTECT);
        outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_ECC_CHK);

        /* init SM interface */
#ifdef _NAND_PAR_ALC_
        outpw(REG_SMCSR, inpw(REG_SMCSR) & ~SMCR_REDUN_AUTO_WEN);
#ifdef DEBUG
        printf("Parity only written to SM registers but not NAND !!\n");
#endif
#else
        outpw(REG_SMCSR, inpw(REG_SMCSR) | SMCR_REDUN_AUTO_WEN);
#ifdef DEBUG
        printf("Parity written to SM registers and NAND !!\n");
#endif
#endif  // _NAND_PAR_ALC_
    }

    sicSMselect(chipSel);

    if (chipSel == 0)
    {
        if (_nand_init0)
            return 0;

        pSM0 = malloc(sizeof(FMI_SM_INFO_T));
        if (pSM0 == NULL)
            return FMI_NO_MEMORY;
        memset((char *)pSM0, 0, sizeof(FMI_SM_INFO_T));

        if ((status = fmiSM_ReadID(pSM0, NDISK_info)) < 0)
        {
            if (pSM0 != NULL)
            {
                free(pSM0);
                pSM0 = 0;
            }
            return status;
        }
        fmiSM_Initial(pSM0);

#ifdef OPT_SW_WP
        outpw(REG_GPAFUN, inpw(REG_GPAFUN) & ~MF_GPA7);         // port A7 low (WP)
        outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~0x0080);  // port A7 low (WP)
        outpw(REG_GPIOA_OMD, inpw(REG_GPIOA_OMD) | 0x0080);     // port A7 output
#endif

        // check NAND boot header
        fmiSMCheckBootHeader(0, pSM0);
        while(1)
        {
            if (!fmiNormalCheckBlock(pSM0, pSM0->uLibStartBlock))
                break;
            else
            {
#ifdef DEBUG
                printf("invalid start block %d\n", pSM0->uLibStartBlock);
#endif
                pSM0->uLibStartBlock++;
            }
        }
        if (pSM0->bIsCheckECC)
            if (pSM0->uLibStartBlock == 0)
                pSM0->uLibStartBlock++;
        NDISK_info->nStartBlock = pSM0->uLibStartBlock;     /* available start block */
        pSM0->uBlockPerFlash -= pSM0->uLibStartBlock;
        count = NDISK_info->nBlockPerZone * 2 / 100 + NAND_RESERVED_BLOCK;

        NDISK_info->nBlockPerZone = (NDISK_info->nBlockPerZone * NDISK_info->nZone - NDISK_info->nStartBlock) / NDISK_info->nZone;
        NDISK_info->nLBPerZone = NDISK_info->nBlockPerZone - count;
        NDISK_info->nNandNo = chipSel;
        _nand_init0 = 1;
    }
    else if (chipSel == 1)
    {
        if (_nand_init1)
            return 0;

        pSM1 = malloc(sizeof(FMI_SM_INFO_T));
        if (pSM1 == NULL)
            return FMI_NO_MEMORY;
        memset((char *)pSM1, 0, sizeof(FMI_SM_INFO_T));

        if ((status = fmiSM_ReadID(pSM1, NDISK_info)) < 0)
        {
            if (pSM1 != NULL)
            {
                free(pSM1);
                pSM1 = 0;
            }
            return status;
        }
        fmiSM_Initial(pSM1);
#ifdef OPT_SW_WP
        outpw(REG_GPAFUN, inpw(REG_GPAFUN) & ~MF_GPA7);         // port A7 low (WP)
        outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~0x0080);  // port A7 low (WP)
        outpw(REG_GPIOA_OMD, inpw(REG_GPIOA_OMD) | 0x0080);     // port A7 output
#endif

        // check NAND boot header
        fmiSMCheckBootHeader(1, pSM1);
        while(1)
        {
            if (!fmiNormalCheckBlock(pSM1, pSM1->uLibStartBlock))
                break;
            else
            {
#ifdef DEBUG
                printf("invalid start block %d\n", pSM1->uLibStartBlock);
#endif
                pSM1->uLibStartBlock++;
            }
        }
        if (pSM1->bIsCheckECC)
            if (pSM1->uLibStartBlock == 0)
                pSM1->uLibStartBlock++;
        NDISK_info->nStartBlock = pSM1->uLibStartBlock;     /* available start block */
        pSM1->uBlockPerFlash -= pSM1->uLibStartBlock;
        count = NDISK_info->nBlockPerZone * 2 / 100 + NAND_RESERVED_BLOCK;

        NDISK_info->nBlockPerZone = (NDISK_info->nBlockPerZone * NDISK_info->nZone - NDISK_info->nStartBlock) / NDISK_info->nZone;
        NDISK_info->nLBPerZone = NDISK_info->nBlockPerZone - count;
        NDISK_info->nNandNo = chipSel;
        _nand_init1 = 1;
    }
    else
        return FMI_SM_INIT_ERROR;

    return 0;
}

//static INT sicSMpread(INT chipSel, INT PBA, INT page, UINT8 *buff)
INT sicSMpread(INT chipSel, INT PBA, INT page, UINT8 *buff)
{
    FMI_SM_INFO_T *pSM;
    int pageNo;
    int status=0;
    int i;
    char *ptr;

#ifdef OPT_SUPPORT_H27UAG8T2A
    int spareSize;
#endif

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    // enable SM
    outpw(REG_FMICR, FMI_SM_EN);
    fmiSM_Initial(pSM);     //removed by mhuko

    PBA += pSM->uLibStartBlock;
    pageNo = PBA * pSM->uPagePerBlock + page;

#ifdef OPT_FIRST_4BLOCKS_ECC4
    if (PBA <= 3)
    {
    #ifdef OPT_SUPPORT_H27UAG8T2A
        // set to ECC8 for Block 0-3
        if (pSM->nPageSize == NAND_PAGE_4KB)    /* 4KB */
        {
            if (pSM->bIsNandECC12 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);             // BCH_8 is selected
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 128);  // Redundant area size
            }
        }
        // set to ECC4 for Block 0-3
        else if (pSM->nPageSize == NAND_PAGE_2KB)   /* 2KB */
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);             // BCH_4 is selected
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #else
        // set to ECC4 for Block 0-3
        if (pSM->nPageSize == NAND_PAGE_2KB)    /* 2KB */
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);             // BCH_4 is selected
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #endif
    }
#endif

    if (pSM->nPageSize == NAND_PAGE_2KB)    /* 2KB */
    {
        ptr = (char *)REG_SMRA_0;
        fmiSM_Read_RA(pSM, pageNo, 2048);
        for (i=0; i<64; i++)
            *ptr++ = inpw(REG_SMDATA) & 0xff;

        status = fmiSM_Read_2K(pSM, pageNo, (UINT32)buff);
    }
    else if (pSM->nPageSize == NAND_PAGE_4KB)   /* 4KB */
    {
#ifdef OPT_SUPPORT_H27UAG8T2A
        spareSize = inpw(REG_SMREAREA_CTL) & SMRE_REA128_EXT;
        ptr = (char *)REG_SMRA_0;
        fmiSM_Read_RA(pSM, pageNo, 4096);

        for (i=0; i<spareSize; i++)
            *ptr++ = inpw(REG_SMDATA) & 0xff;
#else
        ptr = (char *)REG_SMRA_0;
        fmiSM_Read_RA(pSM, pageNo, 4096);
//      for (i=0; i<216; i++)
        for (i=0; i<128; i++)
            *ptr++ = inpw(REG_SMDATA) & 0xff;
#endif

        status = fmiSM_Read_4K(pSM, pageNo, (UINT32)buff);
    }
    else    /* 512B */
    {
        ptr = (char *)REG_SMRA_0;
        fmiSM_Read_RA_512(pSM, pageNo, 0);
        for (i=0; i<16; i++)
            *ptr++ = inpw(REG_SMDATA) & 0xff;

        status = fmiSM_Read_512(pSM, pageNo, (UINT32)buff);
    }

#ifdef DEBUG
    if (status)
        printf("read NAND page fail !!!\n");
#endif

#ifdef OPT_FIRST_4BLOCKS_ECC4

    if (PBA <= 3)
    {
    #ifdef OPT_SUPPORT_H27UAG8T2A
        // restore to ECC12
        if (pSM->nPageSize == NAND_PAGE_4KB)    /* 4KB */
        {
            if (pSM->bIsNandECC12 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T12);            // BCH_8 is selected
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 224);  // Redundant area size
            }
        }
        // restore to ECC8
        else if (pSM->nPageSize == NAND_PAGE_2KB)   /* 2KB */
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);             // BCH_8 is selected
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #else
        if (pSM->nPageSize == NAND_PAGE_2KB)    /* 2KB */
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);             // BCH_8 is selected
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #endif
    }
#endif

//  sysFlushCache(I_D_CACHE);
//  sysInvalidCache();

    return status;
}

//static INT sicSMpwrite(INT chipSel, INT PBA, INT page, UINT8 *buff)
INT sicSMpwrite(INT chipSel, INT PBA, INT page, UINT8 *buff)
{
    FMI_SM_INFO_T *pSM;
    int pageNo;
    int status=0;

//  sysFlushCache(D_CACHE);
    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    // enable SM
    outpw(REG_FMICR, FMI_SM_EN);
    fmiSM_Initial(pSM);         //removed by mhuko

    PBA += pSM->uLibStartBlock;
    pageNo = PBA * pSM->uPagePerBlock + page;

#ifdef OPT_FIRST_4BLOCKS_ECC4
    if (PBA <= 3)
    {
    #ifdef OPT_SUPPORT_H27UAG8T2A
        // set to ECC8 for Block 0-3
        if (pSM->nPageSize == NAND_PAGE_4KB)
        {
            if (pSM->bIsNandECC12 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 128);  // Redundant area size
            }
        }
        // set to ECC4 for Block 0-3
        else if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #else
        // set to ECC4 for Block 0-3
        if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T4);
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #endif
    }
#endif

    if (pSM->nPageSize == NAND_PAGE_2KB)
#ifdef _NAND_PAR_ALC_
        status = fmiSM_Write_2K_ALC(pSM, pageNo, 0, (UINT32)buff);
#else
        status = fmiSM_Write_2K(pSM, pageNo, 0, (UINT32)buff);
#endif
    else if (pSM->nPageSize == NAND_PAGE_4KB)
    {
#ifdef _NAND_PAR_ALC_
        status = fmiSM_Write_4K_ALC(pSM, pageNo, 0, (UINT32)buff);
#else
        status = fmiSM_Write_4K(pSM, pageNo, 0, (UINT32)buff);
#endif
    }
    else    /* 512B */
        status = fmiSM_Write_512(pSM, pageNo, (UINT32)buff);

#ifdef DEBUG
    if (status)
        printf("write NAND page fail !!!\n");
#endif

#ifdef OPT_FIRST_4BLOCKS_ECC4
    if (PBA <= 3)
    {
    #ifdef OPT_SUPPORT_H27UAG8T2A
        // restore to ECC12
        if (pSM->nPageSize == NAND_PAGE_4KB)
        {
            if (pSM->bIsNandECC12 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T12);
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 224);  // Redundant area size
            }
        }
        // restore to ECC8
        else if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #else
        // restore to ECC8
        if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            if (pSM->bIsNandECC8 == TRUE)
            {
                outpw(REG_SMCSR, inpw(REG_SMCSR) &  ~SMCR_BCH_TSEL);
                outpw(REG_SMCSR, inpw(REG_SMCSR) | BCH_T8);
                outpw(REG_SMREAREA_CTL, (inpw(REG_SMREAREA_CTL) & ~SMRE_REA128_EXT) | 64);  // Redundant area size
            }
        }
    #endif
    }
#endif

    return status;
}

static INT sicSM_is_page_dirty(INT chipSel, INT PBA, INT page)
{
    FMI_SM_INFO_T *pSM;
    int pageNo;
    UINT8 data0, data1;

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    // enable SM
    outpw(REG_FMICR, FMI_SM_EN);
    fmiSM_Initial(pSM);     //removed by mhuko

    PBA += pSM->uLibStartBlock;
    pageNo = PBA * pSM->uPagePerBlock + page;

    if (pSM->nPageSize == NAND_PAGE_2KB)
        fmiSM_Read_RA(pSM, pageNo, 2050);
    else if (pSM->nPageSize == NAND_PAGE_4KB)
        fmiSM_Read_RA(pSM, pageNo, 4098);
    else if (pSM->nPageSize == NAND_PAGE_8KB)
        fmiSM_Read_RA(pSM, pageNo, 8194);
    else    /* 512B */
        fmiSM_Read_RA_512(pSM, pageNo, 2);

    data0 = inpw(REG_SMDATA);
    data1 = inpw(REG_SMDATA);
    data1 = data1;      // avoid compile warning message

    if (pSM->nPageSize == NAND_PAGE_512B)
        fmiSM_Reset(pSM);

    if (data0 == 0x00)
        return 1;   // used page
    else if (data0 != 0xff)
        return 1;   // used page

    return 0;   // un-used page
}


static INT sicSM_is_valid_block(INT chipSel, INT PBA)
{
    FMI_SM_INFO_T *pSM;

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    PBA += pSM->uLibStartBlock;

    // enable SM
    outpw(REG_FMICR, FMI_SM_EN);
    fmiSM_Initial(pSM);

    if (fmiCheckInvalidBlock(pSM, PBA) == 1)    // invalid block
    {
#ifdef DEBUG
        printf("invalid block %d\n", PBA);
#endif
        return 0;
    }
    else
        return 1;   // valid block
}

#ifdef OPT_MARK_BAD_BLOCK_WHILE_ERASE_FAIL

static INT sicSMMarkBadBlock_WhileEraseFail(FMI_SM_INFO_T *pSM, UINT32 BlockNo)
{
    UINT32 uSector, ucColAddr;

    /* check if MLC NAND */
    if (pSM->bIsMLCNand == TRUE)
    {
        uSector = (BlockNo+1) * pSM->uPagePerBlock - 1; // write last page
        if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            ucColAddr = 2048;       // write 2048th byte
        }
        else if (pSM->nPageSize == NAND_PAGE_4KB)
        {
            ucColAddr = 4096;       // write 4096th byte
        }

        // send command
        outpw(REG_SMCMD, 0x80);     // serial data input command
        outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
        outpw(REG_SMADDR, (ucColAddr >> 8) & 0xff); // CA8 - CA11
        outpw(REG_SMADDR, uSector & 0xff);          // PA0 - PA7
        if (!pSM->bIsMulticycle)
            outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
        else
        {
            outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
            outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
        }
        outpw(REG_SMDATA, 0xf0);    // mark bad block (use 0xf0 instead of 0x00 to differ from Old (Factory) Bad Blcok Mark)
        outpw(REG_SMCMD, 0x10);

        if (! fmiSMCheckRB(pSM))
            return FMI_SM_RB_ERR;

        fmiSM_Reset(pSM);
        return 0;
    }
    /* SLC check the 2048 byte of 1st or 2nd page per block */
    else    // SLC
    {
        uSector = BlockNo * pSM->uPagePerBlock;     // write lst page
        if (pSM->nPageSize == NAND_PAGE_2KB)
        {
            ucColAddr = 2048;       // write 2048th byte
        }
        else if (pSM->nPageSize == NAND_PAGE_4KB)
        {
            ucColAddr = 4096;       // write 4096th byte
        }
        else if (pSM->nPageSize == NAND_PAGE_512B)
        {
            ucColAddr = 0;
            goto _mark_512;
        }

        // send command
        outpw(REG_SMCMD, 0x80);     // serial data input command
        outpw(REG_SMADDR, ucColAddr);               // CA0 - CA7
        outpw(REG_SMADDR, (ucColAddr >> 8) & 0xff); // CA8 - CA11
        outpw(REG_SMADDR, uSector & 0xff);          // PA0 - PA7
        if (!pSM->bIsMulticycle)
            outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
        else
        {
            outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
            outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
        }
        outpw(REG_SMDATA, 0xf0);    // mark bad block (use 0xf0 instead of 0x00 to differ from Old (Factory) Bad Blcok Mark)
        outpw(REG_SMCMD, 0x10);

        if (! fmiSMCheckRB(pSM))
            return FMI_SM_RB_ERR;

        fmiSM_Reset(pSM);
        return 0;

_mark_512:

        outpw(REG_SMCMD, 0x50);     // point to redundant area
        outpw(REG_SMCMD, 0x80);     // serial data input command
        outpw(REG_SMADDR, ucColAddr);       // CA0 - CA7
        outpw(REG_SMADDR, uSector & 0xff);  // PA0 - PA7
        if (!pSM->bIsMulticycle)
            outpw(REG_SMADDR, ((uSector >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
        else
        {
            outpw(REG_SMADDR, (uSector >> 8) & 0xff);               // PA8 - PA15
            outpw(REG_SMADDR, ((uSector >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
        }

        outpw(REG_SMDATA, 0xf0);    // 512
        outpw(REG_SMDATA, 0xff);
        outpw(REG_SMDATA, 0xff);
        outpw(REG_SMDATA, 0xff);
        outpw(REG_SMDATA, 0xf0);    // 516
        outpw(REG_SMDATA, 0xf0);    // 517
        outpw(REG_SMCMD, 0x10);
        if (! fmiSMCheckRB(pSM))
            return FMI_SM_RB_ERR;

        fmiSM_Reset(pSM);
        return 0;
    }
}

#endif      // OPT_MARK_BAD_BLOCK_WHILE_ERASE_FAIL

INT sicSMMarkBadBlock(FMI_SM_INFO_T *pSM, UINT32 BlockNo)
{
    UINT32 sector, column;

    /* page 0 */
    sector = BlockNo * pSM->uPagePerBlock;
    column = pSM->nPageSize;    // set address to begin of spare area

    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, column);                  // CA0 - CA7
    outpw(REG_SMADDR, (column >> 8) & 0x3f);    // CA8 - CA12
    outpw(REG_SMADDR, sector & 0xff);           // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((sector >> 8) & 0xff)|EOA_SM);   // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (sector >> 8) & 0xff);            // PA8 - PA15
        outpw(REG_SMADDR, ((sector >> 16) & 0xff)|EOA_SM);  // PA16 - PA17
    }

    outpw(REG_SMDATA, 0xf0);    // 1st byte of spare area
    outpw(REG_SMDATA, 0xff);
    outpw(REG_SMDATA, 0xff);
    outpw(REG_SMDATA, 0xff);
    outpw(REG_SMDATA, 0xf0);
    outpw(REG_SMDATA, 0xf0);    // 6th byte of spare area
    outpw(REG_SMCMD, 0x10);
    if (! fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;
   fmiSM_Reset(pSM);

    /* page 1 */
    sector++;
    // send command
    outpw(REG_SMCMD, 0x80);     // serial data input command
    outpw(REG_SMADDR, column);                  // CA0 - CA7
    outpw(REG_SMADDR, (column >> 8) & 0x3f);    // CA8 - CA12
    outpw(REG_SMADDR, sector & 0xff);           // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((sector >> 8) & 0xff)|EOA_SM);       // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, (sector >> 8) & 0xff);                // PA8 - PA15
        outpw(REG_SMADDR, ((sector >> 16) & 0xff)|EOA_SM);      // PA16 - PA17
    }

    outpw(REG_SMDATA, 0xf0);    // 512
    outpw(REG_SMDATA, 0xff);
    outpw(REG_SMDATA, 0xff);
    outpw(REG_SMDATA, 0xff);
    outpw(REG_SMDATA, 0xf0);    // 516
    outpw(REG_SMDATA, 0xf0);    // 517
    outpw(REG_SMCMD, 0x10);
    if (! fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;
   fmiSM_Reset(pSM);

    return 0;
}

static INT sicSMChangeBadBlockMark(INT chipSel)
{
    int status=0;
    FMI_SM_INFO_T *pSM;

    _fmi_pSMBuffer = (UINT8 *)((UINT32)_fmi_ucSMBuffer | 0x80000000);

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    // enable SM
    outpw(REG_FMICR, 0x08);
    fmiSM_Initial(pSM);

#ifndef OPT_FA93
    // scan all nand chip
    for (i=1; i<=pSM->uBlockPerFlash; i++)
    {
        if (fmiNormalCheckBlock(pSM, i))    // bad block
        {
            if (sicSMMarkBadBlock(pSM, i) < 0)
                return FMI_SM_MARK_BAD_BLOCK_ERR;
        }
    }
#endif

    /* read physical block 0 - image information */
    status = sicSMpread(chipSel, 0, pSM->uPagePerBlock-1, _fmi_pSMBuffer);
    if (status < 0)
        return status;

    /* write specific mark */
    _fmi_ucSMBuffer[pSM->nPageSize-6] = '5';
    _fmi_ucSMBuffer[pSM->nPageSize-5] = '5';
    _fmi_ucSMBuffer[pSM->nPageSize-4] = '0';
    _fmi_ucSMBuffer[pSM->nPageSize-3] = '0';
    _fmi_ucSMBuffer[pSM->nPageSize-2] = '9';
    _fmi_ucSMBuffer[pSM->nPageSize-1] = '1';

    // remove write "550091" in FA93
//  sicSMpwrite(chipSel, 0, pSM->uPagePerBlock-1, _fmi_ucSMBuffer);
//  status = sicSMpwrite(chipSel, 0, pSM->uPagePerBlock-1, _fmi_ucSMBuffer);    //mhkuo@20100730
//  status = sicSMpwrite(chipSel, 0, pSM->uPagePerBlock-1, _fmi_pSMBuffer); //mhkuo@20100730

    return status;
}


//static INT sicSMblock_erase(INT chipSel, INT PBA)
INT sicSMblock_erase(INT chipSel, INT PBA)
{
    FMI_SM_INFO_T *pSM;
    UINT32 page_no;

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    PBA += pSM->uLibStartBlock;

    // enable SM
    outpw(REG_FMICR, FMI_SM_EN);
    fmiSM_Initial(pSM);

    if (fmiCheckInvalidBlock(pSM, PBA) != 1)
    {
        page_no = PBA * pSM->uPagePerBlock;     // get page address

        /* clear R/B flag */
        if (pSM == pSM0)
        {
            while(!(inpw(REG_SMISR) & SMISR_RB0));
            outpw(REG_SMISR, SMISR_RB0_IF);
        }
        else
        {
            while(!(inpw(REG_SMISR) & SMISR_RB1));
            outpw(REG_SMISR, SMISR_RB1_IF);
        }

        if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)   /* ECC_FLD_IF */
        {
#ifdef DEBUG
            printf("erase: error sector !!\n");
#endif
            outpw(REG_SMISR, SMISR_ECC_FIELD_IF);
        }

        outpw(REG_SMCMD, 0x60);     // erase setup command

        outpw(REG_SMADDR, (page_no & 0xff));        // PA0 - PA7
        if (!pSM->bIsMulticycle)
            outpw(REG_SMADDR, ((page_no >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
        else
        {
            outpw(REG_SMADDR, ((page_no >> 8) & 0xff));             // PA8 - PA15
            outpw(REG_SMADDR, ((page_no >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
        }

        outpw(REG_SMCMD, 0xd0);     // erase command

        if (!fmiSMCheckRB(pSM))
            return FMI_SM_RB_ERR;

        if (fmiSMCheckStatus(pSM) != 0)
        {
#ifdef DEBUG
            printf("sicSMblock_erase error!!\n");
#endif

#ifdef OPT_MARK_BAD_BLOCK_WHILE_ERASE_FAIL
            sicSMMarkBadBlock_WhileEraseFail(pSM,PBA);
#endif
            return FMI_SM_STATUS_ERR;
        }
    }
    else
    {
        sysprintf("Don't erase block PBA %d since it is bad block.\n", PBA);
        return FMI_SM_INVALID_BLOCK;
    }

    return 0;
}


/*-----------------------------------------------------------------------------
 * Force to erase a block even if it is a bad block.
 * INPUT:
 *      chipSel: 0 for NAND0 port; 1 for NAND1 port.
 *      PBA: physical block address include reserve area.
 * OUTPUT:
 *      None.
 * RETURN:
 *      0 : erase successfully
 *      -1: erase fail and return error code
 *---------------------------------------------------------------------------*/
INT sicSMblock_erase_test(INT chipSel, INT PBA)
{
    FMI_SM_INFO_T *pSM;
    UINT32 page_no;

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    // enable SM
    outpw(REG_FMICR, 0x08);
    fmiSM_Initial(pSM);

    page_no = PBA * pSM->uPagePerBlock;     // get page address

    /* clear R/B flag */
    if (pSM == pSM0)
    {
        while(!(inpw(REG_SMISR) & SMISR_RB0));
        outpw(REG_SMISR, SMISR_RB0_IF);
    }
    else
    {
        while(!(inpw(REG_SMISR) & SMISR_RB1));
        outpw(REG_SMISR, SMISR_RB1_IF);
    }

    if (inpw(REG_SMISR) & SMISR_ECC_FIELD_IF)   /* ECC_FLD_IF */
    {
#ifdef DEBUG
        printf("erase: error sector !!\n");
#endif
        outpw(REG_SMISR, SMISR_ECC_FIELD_IF);
    }

    outpw(REG_SMCMD, 0x60);     // erase setup command

    outpw(REG_SMADDR, (page_no & 0xff));        // PA0 - PA7
    if (!pSM->bIsMulticycle)
        outpw(REG_SMADDR, ((page_no >> 8) & 0xff)|EOA_SM);      // PA8 - PA15
    else
    {
        outpw(REG_SMADDR, ((page_no >> 8) & 0xff));             // PA8 - PA15
        outpw(REG_SMADDR, ((page_no >> 16) & 0xff)|EOA_SM);     // PA16 - PA17
    }

    outpw(REG_SMCMD, 0xd0);     // erase command

    if (!fmiSMCheckRB(pSM))
        return FMI_SM_RB_ERR;

    if (fmiSMCheckStatus(pSM) != 0)
    {
#ifdef DEBUG
        printf("sicSMblock_erase error!!\n");
#endif
        return FMI_SM_STATUS_ERR;
    }

    return 0;
}


static INT sicSMchip_erase(INT chipSel)
{
    int i, status=0;
    FMI_SM_INFO_T *pSM;

    sicSMselect(chipSel);
    if (chipSel == 0)
        pSM = pSM0;
    else
        pSM = pSM1;

    // enable SM
    outpw(REG_FMICR, FMI_SM_EN);
    fmiSM_Initial(pSM);

    // erase all chip
    for (i=0; i<=pSM->uBlockPerFlash; i++)
    {
        status = sicSMblock_erase(chipSel, i);
        if (status < 0)
            sysprintf("SM block erase fail <%d>!!\n", i);
    }

    return 0;
}

/* driver function */
INT nandInit0(NDISK_T *NDISK_info)
{
    return (sicSMInit(0, NDISK_info));
}

INT nandpread0(INT PBA, INT page, UINT8 *buff)
{
    return (sicSMpread(0, PBA, page, buff));
}

INT nandpwrite0(INT PBA, INT page, UINT8 *buff)
{
#ifdef OPT_SW_WP
    int status;
    UINT32 ii;

    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) | 0x0080);   // port A7 high (WP)
    for (ii=0; ii<SW_WP_DELAY_LOOP; ii++);
    status = sicSMpwrite(0, PBA, page, buff);
    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~ 0x0080);     // port A7 low (WP)
    return status;
#else
    return (sicSMpwrite(0, PBA, page, buff));
#endif
}

INT nand_is_page_dirty0(INT PBA, INT page)
{
    return (sicSM_is_page_dirty(0, PBA, page));
}

INT nand_is_valid_block0(INT PBA)
{
    return (sicSM_is_valid_block(0, PBA));
}

INT nand_block_erase0(INT PBA)
{
#ifdef OPT_SW_WP
    int status;
    UINT32 ii;

    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) | 0x0080);   // port A7 high (WP)
#if 0
    sicSMselect(0);

    outpw(REG_SMCMD, 0x70);     // status read command
    while(!(inpw(REG_SMDATA) & 0x80))   // 0: protected, 1: un-potected
    {
        outpw(REG_SMCMD, 0x70);     // status read command
    }
#endif

    for (ii=0; ii<SW_WP_DELAY_LOOP; ii++);
    status = sicSMblock_erase(0, PBA);
    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~ 0x0080);     // port A7 low (WP)
    return status;
#else
    return (sicSMblock_erase(0, PBA));
#endif
}

INT nand_chip_erase0(void)
{
#ifdef OPT_SW_WP
    int status;
    UINT32 ii;

    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) | 0x0080);   // port A7 high (WP)
    for (ii=0; ii<SW_WP_DELAY_LOOP; ii++);
    status = sicSMchip_erase(0);
    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~ 0x0080);     // port A7 low (WP)
    return status;
#else
    return (sicSMchip_erase(0));
#endif
}

INT nand_ioctl(INT param1, INT param2, INT param3, INT param4)
{
    return 0;
}


INT fmiSMCheckBootHeader(INT chipSel, FMI_SM_INFO_T *pSM)
{
    int fmiNandSysArea=0;
    int volatile status, imageCount, i, block;
    unsigned int *pImageList;
    volatile int ii;

#define OPT_FOUR_BOOT_IMAGE

    _fmi_pSMBuffer = (UINT8 *)((UINT32)_fmi_ucSMBuffer | 0x80000000);
    pImageList = (UINT32 *)((UINT32)_fmi_ucSMBuffer | 0x80000000);

    /* read physical block 0 - image information */
#ifdef OPT_FOUR_BOOT_IMAGE
    for (ii=0; ii<4; ii++)
    {
        status = sicSMpread(chipSel, ii, pSM->uPagePerBlock-1, _fmi_pSMBuffer);
        if (!status)
        {
            if (((*(pImageList+0)) == 0x574255aa) && ((*(pImageList+3)) == 0x57425963))
                break;
        }
    }
#else
    status = sicSMpread(chipSel, 0, pSM->uPagePerBlock-1, _fmi_pSMBuffer);
#endif

    if (status < 0)
        return status;

    /* check specific mark */
    if (pSM->nPageSize != NAND_PAGE_512B)
    {
        if (((_fmi_ucSMBuffer[pSM->nPageSize-6]) == '5') && ((_fmi_ucSMBuffer[pSM->nPageSize-5]) == '5') &&
            ((_fmi_ucSMBuffer[pSM->nPageSize-4]) == '0') && ((_fmi_ucSMBuffer[pSM->nPageSize-3]) == '0') &&
            ((_fmi_ucSMBuffer[pSM->nPageSize-2]) == '9') && ((_fmi_ucSMBuffer[pSM->nPageSize-1]) == '1'))
        {
            _fmi_bIsNandFirstAccess = FALSE;
        }
        else
        {
            sicSMChangeBadBlockMark(chipSel);
        }
    }

    if (((*(pImageList+0)) == 0x574255aa) && ((*(pImageList+3)) == 0x57425963))
    {
        fmiNandSysArea = *(pImageList+1);
    }

    if ((fmiNandSysArea != 0xFFFFFFFF) && (fmiNandSysArea != 0))
    {
        pSM->uLibStartBlock = (fmiNandSysArea / pSM->uSectorPerBlock) + 1;
    }
    else
    {
        /* read physical block 0 - image information */

#ifdef OPT_FOUR_BOOT_IMAGE
        for (ii=0; ii<4; ii++)
        {
            status = sicSMpread(chipSel, ii, pSM->uPagePerBlock-2, _fmi_pSMBuffer);
            if (!status)
            {
                if (((*(pImageList+0)) == 0x574255aa) && ((*(pImageList+3)) == 0x57425963))
                    break;
            }
        }

#else
        status = sicSMpread(chipSel, 0, pSM->uPagePerBlock-2, _fmi_pSMBuffer);
#endif
        if (status < 0)
            return status;

        if (((*(pImageList+0)) == 0x574255aa) && ((*(pImageList+3)) == 0x57425963))
        {
            imageCount = *(pImageList+1);

            /* pointer to image information */
            pImageList = pImageList+4;
            for (i=0; i<imageCount; i++)
            {
                block = (*(pImageList + 1) & 0xFFFF0000) >> 16;
                if (block > pSM->uLibStartBlock)
                    pSM->uLibStartBlock = block;

                /* pointer to next image */
                pImageList = pImageList+12;
            }
            pSM->uLibStartBlock++;
        }
    }

    return 0;
}

VOID fmiSMClose(INT chipSel)
{
    if (chipSel == 0)
    {
        _nand_init0 = 0;
        if (pSM0 != 0)
        {
            free(pSM0);
            pSM0 = 0;
        }
    }
    else
    {
        _nand_init1 = 0;
        if (pSM1 != 0)
        {
            free(pSM1);
            pSM1 = 0;
        }
    }
    if ((_nand_init0 == 0) && (_nand_init1 == 0))
    {
        outpw(REG_SMCSR, inpw(REG_SMCSR)|0x06000000);       // CS-0/CS-1 -> HIGH
        outpw(REG_SMISR, 0xfff);
        outpw(REG_FMICR, 0x00);

        outpw(REG_GPDFUN, inpw(REG_GPDFUN) & ~0x0003FC00);      // enable NAND NWR/NRD/RB0/RB1 pins
        outpw(REG_GPEFUN, inpw(REG_GPEFUN) & ~0x00FF0000);      // enable NAND ALE/CLE/CS0/CS1 pins
    }
}


INT nandInit1(NDISK_T *NDISK_info)
{
    return (sicSMInit(1, NDISK_info));
}

INT nandpread1(INT PBA, INT page, UINT8 *buff)
{
    return (sicSMpread(1, PBA, page, buff));
}

INT nandpwrite1(INT PBA, INT page, UINT8 *buff)
{
#ifdef OPT_SW_WP
    int status;
    UINT32 ii;

    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) | 0x0080);   // port A7 high (WP)
    for (ii=0; ii<SW_WP_DELAY_LOOP; ii++);
#if 0
    sicSMselect(1);

    outpw(REG_SMCMD, 0x70);     // status read command
    while(!(inpw(REG_SMDATA) & 0x80))   // 0: protected, 1: un-potected
    {
        outpw(REG_SMCMD, 0x70);     // status read command
    }
#endif

    status = sicSMpwrite(1, PBA, page, buff);
    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~ 0x0080);     // port A7 low (WP)
    return status;
#else
    return (sicSMpwrite(1, PBA, page, buff));
#endif
}

INT nand_is_page_dirty1(INT PBA, INT page)
{
    return (sicSM_is_page_dirty(1, PBA, page));
}

INT nand_is_valid_block1(INT PBA)
{
    return (sicSM_is_valid_block(1, PBA));
}

INT nand_block_erase1(INT PBA)
{
#ifdef OPT_SW_WP
    int status;
    UINT32 ii;

    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) | 0x0080);   // port A7 high (WP)
    for (ii=0; ii<SW_WP_DELAY_LOOP; ii++);
#if 0
    sicSMselect(1);

    outpw(REG_SMCMD, 0x70);     // status read command
    while(!(inpw(REG_SMDATA) & 0x80))   // 0: protected, 1: un-potected
    {
        outpw(REG_SMCMD, 0x70);     // status read command
    }
#endif

    status = sicSMblock_erase(1, PBA);
    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~ 0x0080);     // port A7 low (WP)
    return status;
#else
    return (sicSMblock_erase(1, PBA));
#endif
}

INT nand_chip_erase1(void)
{
#ifdef OPT_SW_WP
    int status;
    UINT32 ii;

    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) | 0x0080);   // port A7 high (WP)
    for (ii=0; ii<SW_WP_DELAY_LOOP; ii++);
    status = sicSMchip_erase(1);
    outpw(REG_GPIOA_DOUT, inpw(REG_GPIOA_DOUT) & ~ 0x0080);     // port A7 low (WP)
    return status;
#else
    return (sicSMchip_erase(1));
#endif
}



nvtfat.h  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/*************************************************************************
 * Nuvoton Electronics Corporation confidential
 *
 * Copyright (c) 2008 by Nuvoton Electronics Corporation
 * All rights reserved
 * 
 * FILENAME
 *     nvtfat.h
 *
 * REMARK
 *     None
 **************************************************************************/

#ifndef _NVTFAT_H_
#define _NVTFAT_H_

#define MAX_PATH_LEN                520        /* maximum length of full path, counted by 
                                                  character, note that one character may be 
                                               compsoed of two bytes */
#define MAX_FILE_NAME_LEN           514


/*
 * Declared for pre-referenced data strucutres
 */
struct storage_driver_S;
struct physical_disk_S;
struct partition_S;
struct logical_disk_S;
struct fs_op_S;
struct file_op_S;
struct file_S;    
struct file_find_S;    
struct file_stat_S;

#define STORAGE_DRIVER_T        struct storage_driver_S
#define PDISK_T                    struct physical_disk_S
#define PARTITION_T                struct partition_S
#define LDISK_T                 struct logical_disk_S
#define DISK_OP_T                struct disk_op_S
#define FILE_OP_T                struct file_op_S
#define FILE_T                    struct file_S
#define FILE_FIND_T                struct file_find_S    
#define FILE_STAT_T                struct file_stat_S

/*
 *  Include other header files
 */
#include "nvtfat_fat.h"


/*================================================= Error Code Definitions ==*/

/*--- ERROR CODEs ---*/
#define FS_OK                          0

/* GENERAL ERRORs */                                               
#define ERR_FILE_EOF                0xFFFF8200    /* end of file */
#define ERR_GENERAL_FILE_ERROR      0xFFFF8202     /* general file error */
#define ERR_NO_FREE_MEMORY          0xFFFF8204    /* no free memory */
#define ERR_NO_FREE_BUFFER          0xFFFF8206    /* no available sector buffers */
#define ERR_NOT_SUPPORTED            0xFFFF8208    /* feature or function was not supported */
#define ERR_UNKNOWN_OP_CODE           0xFFFF820A    /* unrecognized operation code */
#define ERR_INTERNAL_ERROR            0xFFFF820C    /* file system internal error */
#define ERR_SYSTEM_LOCK                0xFFFF820E    /* file system locked by ScanDisk or Defragment */

/* FILE ERRORs */                                               
#define ERR_FILE_NOT_FOUND          0xFFFF8220    /* file not found */
#define ERR_FILE_INVALID_NAME       0xFFFF8222    /* invalid file name */
#define ERR_FILE_INVLAID_HANDLE     0xFFFF8224    /* invalid file hFile */
#define ERR_FILE_IS_DIRECTORY       0xFFFF8226  /* try to open a directory file */
#define ERR_FILE_IS_NOT_DIRECTORY   0xFFFF8228    /* is not a directory file */
#define ERR_FILE_CREATE_NEW         0xFFFF822A  /* can not create new entry    */
#define ERR_FILE_OPEN_MAX_LIMIT        0xFFFF822C    /* has opened too many files */
#define ERR_FILE_EXIST                0xFFFF822E    /* file already exist */
#define ERR_FILE_INVALID_OP            0xFFFF8230    /* invalid file operation */
#define ERR_FILE_INVALID_ATTR        0xFFFF8232    /* invalid file attribute */
#define ERR_FILE_INVALID_TIME        0xFFFF8234    /* invalid time specified */
#define ERR_FILE_TRUNC_UNDER        0xFFFF8236    /* truncate file underflow, size < pos */  
#define ERR_FILE_NO_MORE            0xFFFF8238    /* No really an error, used to identify end of file enumeration of a directory */
#define ERR_FILE_IS_CORRUPT            0xFFFF823A    /* file is corrupt */

/* PATH ERRORs */                                            
#define ERR_PATH_INVALID            0xFFFF8260    /* path name was invalid */
#define ERR_PATH_TOO_LONG           0xFFFF8262    /* path too long */
#define ERR_PATH_NOT_FOUND          0xFFFF8264    /* path not found */

/* DRIVE ERRORs */                                            
#define ERR_DRIVE_NOT_FOUND         0xFFFF8270    /* drive not found, the disk may have been unmounted */
#define ERR_DRIVE_INVALID_NUMBER    0xFFFF8272    /* invalid drive number */
#define ERR_DRIVE_NO_FREE_SLOT      0xFFFF8274    /* can not mount more drive */

/* DIRECTORY ERRORS */                                            
#define ERR_DIR_BUILD_EXIST         0xFFFF8290  /* try to build an already exist directory */
#define ERR_DIR_REMOVE_MISS         0xFFFF8292  /* try to remove a nonexistent directory */
#define ERR_DIR_REMOVE_ROOT         0xFFFF8294  /* try to remoe root directory */
#define ERR_DIR_REMOVE_NOT_EMPTY    0xFFFF8296  /* try to remove a non-empty directory */
#define ERR_DIR_DIFFERENT_DRIVE     0xFFFF8298  /* specified files on different drive */
#define ERR_DIR_ROOT_FULL           0xFFFF829A  /* root directory full */
#define ERR_DIR_SET_SIZE            0xFFFF829C    /* try to set file size of a directory */
#define ERR_DIR_MOVE_DISK            0xFFFF829E    /* cannot move the whole directory from disk to another disk */

/* ACCESS ERRORs */                                        
#define ERR_READ_VIOLATE            0xFFFF82C0  /* user has no read privilege */
#define ERR_WRITE_VIOLATE            0xFFFF82C2    /* user has no write privilege */
#define ERR_ACCESS_VIOLATE            0xFFFF82C4    /* can not access */
#define ERR_READ_ONLY                0xFFFF82C6  /* try to write a read-only file */
#define ERR_WRITE_CAP                0xFFFF82C8    /* try to write file which was opened with read-only */
#define ERR_OPEN_WRITE_OPENED        0xFFFF82CA    /* try to open-write a file, which has been opened */
#define ERR_DEL_OPENED                0xFFFF82CC    /* try to delete a file, which has been opened */

/* DISK ERRORs */                                        
#define ERR_NO_DISK_MOUNT            0xFFFF8300    /* there's no any disk mounted */
#define ERR_DISK_CHANGE_DIRTY       0xFFFF8302     /* disk change, buffer is dirty */
#define ERR_DISK_REMOVED            0xFFFF8304  /* portable disk has been removed */
#define ERR_DISK_WRITE_PROTECT      0xFFFF8306    /* disk is write-protected */
#define ERR_DISK_FULL               0xFFFF8308    /* disk is full */
#define ERR_DISK_BAD_PARTITION      0xFFFF830A    /* bad partition */
#define ERR_DISK_UNKNOWN_PARTITION    0xFFFF830C    /* unknow or not supported partition type */
#define ERR_DISK_UNFORMAT            0xFFFF830E    /* partition was not formatted */
#define ERR_DISK_UNKNOWN_FORMAT     0xFFFF8310    /* unknown disk format */
#define ERR_DISK_SECTOR_SIZE        0xFFFF8311    /* secotr size too large, not supported */
#define ERR_DISK_BAD_BPB            0xFFFF8312    /* bad BPB, disk may not be formatted */
#define ERR_DISK_IO                    0xFFFF8314    /* disk I/O failure */
#define ERR_DISK_IO_TIMEOUT            0xFFFF8316    /* disk I/O time-out */
#define ERR_DISK_FAT_BAD_CLUS        0xFFFF8318    /* bad cluster number in FAT table */
#define ERR_DISK_IO_BUSY            0xFFFF831A    /* I/O device is busy writing, must retry. direct-write mode only */
#define ERR_DISK_INVALID_PARM        0xFFFF831C    /* invalid parameter */
#define ERR_DISK_CANNOT_LOCK        0xFFFF831E    /* cannot lock disk, the disk was in-use or locked by other one */
#define ERR_DISK_PDISK_REMOVE        0xFFFF8320    /* physical disk disconnect error */
#define ERR_DISK_LDISK_REMOVE        0xFFFF8322    /* logical disk disconnect error */

/* FILE SEEK ERRORs */                                            
#define ERR_SEEK_SET_EXCEED         0xFFFF8350  /* file seek set exceed end-of-file */
#define ERR_ACCESS_SEEK_WRITE       0xFFFF8352  /* try to seek a file which was opened for written */

/* OTHER ERRORs */                                        
#define ERR_FILE_SYSTEM_NOT_INIT    0xFFFF83A0    /* file system was not initialized */
#define ERR_ILLEGAL_ATTR_CHANGE     0xFFFF83A2  /* illegal file attribute change */
#define ERR_CHECKDISK_FILE_OPENED    0xFFFF83A4    /* there's file opened, cannot do scandisk operation */ 
#define ERR_CHECKDISK_LOCK            0xFFFF83A6    /* service locked by check disk operation */

/*============================================= disk and file system types ==*/
#define FILE_SYSTEM_FAT12               12
#define FILE_SYSTEM_FAT16               16
#define FILE_SYSTEM_FAT32               32
#define FILE_SYSTEM_NTFS                64
#define FILE_SYSTEM_ISO9660             99

#define DISK_TYPE_MASK                    0x00FFFFFF
#define DISK_TYPE_HARD_DISK             0x00000001
#define DISK_TYPE_RAM_DISK              0x00000002
#define DISK_TYPE_CDR                   0x00000004
#define DISK_TYPE_SMART_MEDIA           0x00000008
#define DISK_TYPE_CF                       0x00000010
#define DISK_TYPE_SD_MMC                   0x00000020
#define DISK_TYPE_FM_CARD                0x00002000    /* A bit OR */
#define DISK_TYPE_USB_DEVICE            0x00004000    /* A bit OR */
#define DISK_TYPE_DMA_MODE              0x00008000    /* A bit OR */
#define DISK_TYPE_READ_ONLY             0x00010000
#define DISK_TYPE_SET_INSTANCE(n)        (n << 24)
#define DISK_INSTANCE_IS(n)                ((n >> 24) & 0xff)


#define PARTITION_TYPE_UNKNOWN            0x00
#define PARTITION_TYPE_FAT12            0x01
#define PARTITION_TYPE_FAT16_OLD        0x04
#define PARTITION_TYPE_EXTENDED_DOS        0x05
#define PARTITION_TYPE_FAT16            0x06
#define PARTITION_TYPE_NTFS                0x07
#define PARTITION_TYPE_FAT32            0x0B
#define PARTITION_TYPE_FAT32_LBA        0x0C
#define PARTITION_TYPE_FAT16_LBA        0x0E
#define PARTITION_TYPE_DOS_LBA            0x0F
#define PARTITION_TYPE_LINUX_NATIVE        0x83

/*
 *  Storage driver structure - Each type of media should support its own storage
 *  driver. The storage driver control the physical I/O access to a storage device.
 */

#undef STORAGE_DRIVER_T
    STORAGE_DRIVER_T;

#define BLOCKING_WRITE        0
#define NON_BLOCKING_WRITE    1

/*
 *  Storage I/O control
 */
#define STORAGE_IOCTL_EJECT_DOOR       1    /* eject door */
#define STORAGE_IOCTL_CLOSE_DOOR       2   /* close door */
#define STORAGE_IOCTL_LOCK_DOOR       3   /* lock door */
#define STORAGE_IOCTL_FORMAT           11  /* format disk */
#define STORAGE_IOCTL_SPEED           21  /* speed control */
#define STORAGE_IOCTL_POWER_MODE    31  /* power saving control */


typedef struct pt_rec        /* partition record */
{
    UINT8       ucState;            /* Current state of partition */
    UINT8       uStartHead;            /* Beginning of partition head */
    UINT8       ucStartSector;        /* Beginning of partition sector */
    UINT8       ucStartCylinder;    /* Beginning of partition cylinder */
    UINT8       ucPartitionType;    /* Partition type, refer to the subsequent definitions */
    UINT8       ucEndHead;            /* End of partition - head */
    UINT8       ucEndSector;        /* End of partition - sector */
    UINT8       ucEndCylinder;        /* End of partition - cylinder */
    UINT32      uFirstSec;            /* Number of Sectors Between the MBR and the First Sector in the Partition */
    UINT32        uNumOfSecs;            /* Number of Sectors in the Partition */
}    PT_REC_T;

    
#undef PDISK_T
    PDISK_T;


typedef struct partition_S
{
    /* The following 16 bytes are a direct mapping of paration record */
    UINT8       ucState;            /* Current state of partition */
    UINT8       ucStartHead;        /* Beginning of partition head */
    UINT8       ucStartSector;        /* Beginning of partition sector */
    UINT8       ucStartCylinder;    /* Beginning of partition cylinder */
    UINT8       ucPartitionType;    /* Partition type, refer to the subsequent definitions */
    UINT8       ucEndHead;            /* End of partition - head */
    UINT8       ucEndSector;        /* End of partition - sector */
    UINT8       ucEndCylinder;        /* End of partition - cylinder */
    UINT32      uFirstSec;            /* Number of Sectors Between the MBR and the First Sector in the Partition */
    UINT32        uNumOfSecs;            /* Number of Sectors in the Partition */

    /* Please use the followings */
    INT            nSectorN;            /* sectors per track, -1 means unknown */
    INT            nHeadN;                /* number of heads, -1 means unknown */
    INT            nCylinderN;            /* number of cylinders, -1 means unknown */
    UINT32        uPartRecSecN;        /* logical sector number of the sector where partition record resides */
    UINT32        uStartSecN;            /* Beginning logical sector number of this partition */
    UINT32        uTotalSecN;            /* Total number of sectors in this partition */
    INT            nErrorCode;            /* error on this partition */
    LDISK_T        *ptLDisk;            /* the only on logical disk on this partition, if exist */
    PARTITION_T    *ptNextPart;        /* link to the next partition */
}    
#undef PARTITION_T
    PARTITION_T;

/* Here the disk drive is a logical disk drive */
typedef struct logical_disk_S
{
    PDISK_T        *ptPDisk;            /* the physical disk it was located */
    INT            nDriveNo;            /* 'A', 'B', ... 'Z' */
    UINT8       ucFileSysType;
    BOOL           bIsDiskRemoved;        /* set is this disk has been removed but not unmounted */
    CHAR        szVolLabel[16];
    UINT32         uDiskSize;            /* disk size counted by sectors */
    UINT32        uFreeSpace;            /* free space counted by sectors */
    FAT_INFO_T  tFatInfo;        
    DISK_OP_T      *ptDiskOP;            /* disk operations */
    FILE_OP_T   *ptFileOP;            /* file operations */
    FILE_T       *ptFileList;        /* files opened on this disk */
    LDISK_T      *ptLinkForAllLDisk;    /* link used for all logical disk chain */

    VOID         *os_mutex;            /* for OS                                             */
    VOID         *os_priv;            /* for OS                                             */
}    
#undef LDISK_T
     LDISK_T;

typedef struct fdrv_t
{
    INT        nDiskType;
    INT        nDiskInstance;
    INT        nDriveNo;
    INT        nPartitionNo;
    PDISK_T    *ptPDisk;
}      FDRV_T;


/*
 *  File system operqations
 *  These service routines are provided by a specific file system. They are used
 *  to fulfill operations on a specific file system. They are system level 
 *  operations, instead of file level operations.
 */
typedef struct disk_op_S
{
    INT (*delfile)(LDISK_T *, CHAR *, CHAR *);    /* delete a file */
    INT (*mkdir)(LDISK_T *, CHAR *, CHAR *);    /* create a new directory */
    INT (*rmdir)(LDISK_T *, CHAR *, CHAR *);    /* remove a existent directory */
    INT (*rename)(LDISK_T *, CHAR *, CHAR *, CHAR *, CHAR *, BOOL);    /* rename file/directory */
    INT (*move)(LDISK_T *, CHAR *, CHAR *, CHAR *, CHAR *, BOOL);    /* move file/directory */
    INT (*volume_label)(LDISK_T *, CHAR *, CHAR *);    /* get/change disk volume label */
    //INT (*format)(LDISK_T *);                /* format this disk */
    //INT (*scan_disk)(LDISK_T *);            /* scan this disk */
    //INT (*defragment)(LDISK_T *);            /* defragment this disk */
}
#undef DISK_OP_T
    DISK_OP_T;


/*
 *  File operations
 *  These service routines are provided by a specific file system. They are used
 *  to fulfill operations on a specific file opened on a file system. They are
 *  file level operations, instead of system level operations.
 */
typedef struct file_op_S
{
    INT (*fopen)(FILE_T *, CHAR *, CHAR *);        /* open a file */
    INT (*fread)(FILE_T *, UINT8 *, INT, INT *);/* read data from file */
    INT (*fwrite)(FILE_T *, UINT8 *, INT, INT*);/* write data to file */
    INT (*fclose)(FILE_T *);                      /* close file */
    INT (*fsizing)(FILE_T *, INT64);            /* change file size */
    INT64 (*fseek)(FILE_T *, INT64);            /* setting the file position pointer */
    INT (*fget_stat)(FILE_T *, FILE_STAT_T *);     /* get file status */
    INT (*fset_stat)(FILE_T *, FILE_STAT_T *);     /* set file status */
    INT (*find_first)(FILE_T *, FILE_FIND_T *);
    INT (*find_next)(FILE_T *, FILE_FIND_T *);
    INT (*find_close)(FILE_T *, FILE_FIND_T *);
    INT (*fioctl)(FILE_T *, INT, VOID *);         /* file system specific control */
} 
#undef FILE_OP_T
    FILE_OP_T;

typedef struct file_S 
{
    //CHAR        suFileName[MAX_PATH_LEN+2];    /* full path file name */
    CHAR        szFnameAscii[MAX_FILE_NAME_LEN/2];
    UINT32       uFlag;                   
    LDISK_T      *ptLDisk;
    FAT_FCB_T      tFatFcb;                  /* File Control Block used by FAT file system */
    FILE_OP_T   *ptFileOP;                /* file operations */
    FILE_T      *ptFileAllLink;               /* used for global file link */
    FILE_T      *ptFileDiskLink;          /* used for file link on disk */
} 
#undef FILE_T
    FILE_T;                    


typedef enum file_sorting_t
{
    NO_SORTING = 0,                        /* without any file sorting */
    NAME_INCREASING,                    /* sorting by file name in increasing order */
    NAME_DECREASING,                    /* sorting by file name in decreasing order */ 
    CDATE_INCREASING,                   /* sorting by file creation date in increasing order */
    CDATE_DECREASING,                   /* sorting by file creation date in decreasing order */
    MDATE_INCREASING,                   /* sorting by file modification date in increasing order */
    MDATE_DECREASING,                   /* sorting by file modification date in decreasing order */
    SIZE_INCREASING,                    /* sorting by file size in increasing order */
    SIZE_DECREASING                     /* sorting by file size in decreasing order */
}   FILE_SORTING_T;


typedef struct file_find_S 
{
    INT            hFile;
    CHAR        suLongName[MAX_FILE_NAME_LEN+2];
    CHAR        szShortName[14];
    UINT8        ucAttrib;
    UINT8        ucCTimeMS;            /* create time minisecond */
    UINT8        ucCTimeSec;         /* create time */
    UINT8        ucCTimeMin;
    UINT8        ucCTimeHour;
    UINT8        ucCDateDay;         /* create date */
    UINT8        ucCDateMonth;
    UINT8        ucCDateYear;
    UINT8        ucLDateDay;         /* last access date */
    UINT8        ucLDateMonth;
    UINT8        ucLDateYear;
    UINT8        ucWTimeSec;            /* write time */
    UINT8        ucWTimeMin;
    UINT8        ucWTimeHour;
    UINT8        ucWDateDay;            /* write date */
    UINT8        ucWDateMonth;
    UINT8        ucWDateYear;
    INT64        n64FileSize;
    
    CHAR        *suPattern[8];
    BOOL        bIsPatternExcludeDir;  /* pattern rule did not apply to directory */
    FILE_SORTING_T  eFileSorting;
    BOOL        bIsDirFirst;        /* only valid if eFileSorting != NO_SORTING */
                                    /* TRUE: direcotry will be searched first */

    /* The following members are library internal used, user MUST NOT modified them! */
    BOOL        bIsAllDirSearched;
    BOOL        bHasSearchPattern;
    INT            nCandidatePos;
    INT            nLastChoicePos;
    struct file_find_S *ptCandidate;
    struct file_find_S *ptLastChoice;
} 
#undef FILE_FIND_T
    FILE_FIND_T;


typedef struct file_stat_S
{
    UINT32       uFlag;              /* forwarded from FAL */
    UINT8        ucAttrib;            /* file attribute */
    UINT8       ucDirNTRes;
    UINT8        ucCTimeMs;            /* create time minisecond */
    UINT8        ucCTimeSec;         /* create time */
    UINT8        ucCTimeMin;
    UINT8        ucCTimeHour;
    UINT8        ucCDateDay;         /* create date */
    UINT8        ucCDateMonth;
    UINT8        ucCDateYear;
    UINT8        ucLDateDay;          /* last access date */
    UINT8        ucLDateMonth;
    UINT8        ucLDateYear;
    UINT8        ucWTimeSec;            /* write time */
    UINT8        ucWTimeMin;
    UINT8        ucWTimeHour;
    UINT8        ucWDateDay;            /* write date */
    UINT8        ucWDateMonth;
    UINT8        ucWDateYear;
    UINT32        uDev;                /* ptLDisk value to fake Linux dev */
    UINT32        uUnique;                /* a unique value used to fake Linux inode number */
    INT64        n64FilePos;
    INT64       n64FileSize;
}
#undef FILE_STAT_T
    FILE_STAT_T;


/*===================================================== file mode and flag ==*/
/* File open mode */
#define O_RDONLY         0x0001        /* Open for read only*/
#define O_WRONLY         0x0002      /* Open for write only*/
#define O_RDWR           0x0003      /* Read/write access allowed.*/
#define O_APPEND         0x0004      /* Seek to eof on each write*/
#define O_CREATE         0x0008      /* Create the file if it does not exist.*/
#define O_TRUNC          0x0010      /* Truncate the file if it already exists*/
#define O_EXCL           0x0020      /* Fail if creating and already exists */
#define O_DIR              0x0080      /* Open a directory file */
#define O_EXCLUSIVE        0x0200        /* Open a file with exclusive lock */
#define O_NODIRCHK        0x0400        /* no dir/file check */
#define O_FSEEK            0x1000        /* Open with cluster chain cache to enhance file seeking performance */
#define O_IOC_VER2        0x2000        
#define O_INTERNAL        0x8000        

/* operation */
#define OP_READ            0x0010000   /* can read */
#define OP_WRITE           0x0020000   /* can write */
#define OP_RDWR            0x0030000   /* can read/write */
#define OP_DELETE         0x0100000   /* file deletion */
#define OP_SETTIME        0x0200000    /* set file time used */
#define OP_DIRTY        0x0400000   /* file has ever been changed */
#define OP_ROOT            0x0800000    /* is root directory */
#define OP_NULL_RDONLY  0x1000000
#define OP_WAS_DEL        0x2000000    /* file has been deleted by other process */
#define OP_ERROR        0x8000000    /* file has been deleted by other process */

/* file attributes */
#define FA_RDONLY       0x01        /* Read only attribute */
#define FA_HIDDEN       0x02        /* Hidden file */
#define FA_SYSTEM       0x04        /* System file */
#define FA_LABEL        0x08        /* Volume label */
#define FA_DIR          0x10        /* Directory    */
#define FA_ARCHIVE      0x20        /* Archive */

#define SEEK_SET        0
#define SEEK_CUR        1
#define SEEK_END        2


/* scan disk stage */
#define SDS_SCAN_FAT        1
#define SDS_BACKUP_FAT        2
#define SDS_SCAN_DIR        3
#define SDS_RECLAIM_CLUS    4
#define SDS_DUP_FAT            5

typedef INT (FS_DW_CB)(UINT8 *);
typedef INT (FS_SD_CB)(INT);
typedef VOID (FS_DEL_CB_T)(PDISK_T *, UINT32, INT);
typedef VOID (FS_MOUNT_CB_T)(LDISK_T *);
typedef VOID (FS_UNMOUNT_CB_T)(LDISK_T *);


/*===================================================== Exported Functions ==*/
/* NVTFAT File System APIs */
extern INT  fsInitFileSystem(VOID);
extern INT  fsAssignDriveNumber(INT nDriveNo, INT disk_type, INT instance, INT partition);
extern VOID fsInstallIoWriteCallBack(FS_DW_CB *cb_func);
extern VOID fsInstallFileDelCallBack(FS_DEL_CB_T *cb_func);

/* Disk operations */
extern INT  fsUnmountPhysicalDisk(PDISK_T *ptPDisk);
extern INT  fsMountLogicalDisk(LDISK_T *ptLDisk);
extern INT  fsUnmountLogicalDisk(LDISK_T *ptLDisk);
extern INT  fsDiskFreeSpace(INT nDriveNo, UINT32 *puBlockSize, UINT32 *puFreeSize, UINT32 *puDiskSize);
extern PDISK_T  *fsGetFullDiskInfomation(VOID);
extern VOID fsReleaseDiskInformation(PDISK_T *ptPDiskList);
extern INT  fsScanDisk(INT nDriveNo);
extern INT  fsFormatFlashMemoryCard(PDISK_T *ptPDisk);
extern INT  fsFormatFixedDrive(INT nDriveNo);
extern INT  fsFormatDisk(INT disk_type, INT nand_part);
extern INT  fsTwoPartAndFormatAll(PDISK_T *ptPDisk, INT firstPartSize, INT secondPartSize);
extern INT  fsCreateDiskPartition(PDISK_T *ptPDisk, UINT32 *puSizeList, INT nCount);            /* 2011-0621 add for multiple partition */ 
extern INT  fsSetReservedArea(UINT32 uStartSecter);                            /* 2011-0624 add for reserved area */                

extern VOID fsDiskWriteComplete(UINT8 *pucBuff);
extern INT  fsGetLDiskVolID(INT nDriveNo, UINT32 *uVolID);
extern INT  fsSetVolumeLabel(INT nDriveNo, CHAR *szLabel, INT nLen);
extern INT  fsGetVolumeLabel(INT nDriveNo, CHAR *szLabel, INT nLen);
extern INT  get_vdisk(INT nDriveNo, LDISK_T **plDisk);

/* File operations */
extern INT  fsOpenFile(CHAR *suFileName, CHAR *szAsciiName, UINT32 uFlag);
extern INT  fsCloseFile(INT hFile);
extern INT  fsReadFile(INT hFile, UINT8 *pucPtr, INT nBytes, INT *pnReadCnt);
extern INT  fsWriteFile(INT hFile, UINT8 *pucBuff, INT nBytes, INT *pnWriteCnt);
extern INT64  fsFileSeek(INT hFile, INT64 n64Offset, INT16 usWhence);
extern BOOL    fsIsEOF(INT hFile);
extern INT  fsSetFileSize(INT hFile, CHAR *suFileName, CHAR *szAsciiName, INT64 n64NewSize);
extern INT  fsGetFilePosition(INT hFile, UINT32 *puPos);
extern INT64  fsGetFileSize(INT hFile);
extern INT  fsGetFileStatus(INT hFile, CHAR *suFileName, CHAR *szAsciiName, FILE_STAT_T *ptFileStat);
extern INT  fsSetFileStatus(INT hFile, CHAR *suFileName, CHAR *szAsciiName, FILE_STAT_T *ptFileStat);
extern INT  fsSetFileAttribute(INT hFile, CHAR *suFileName, CHAR *szAsciiName, UINT8 ucAttrib, FILE_STAT_T *ptFileStat);
extern INT  fsSetFileTime(INT hFile, CHAR *suFileName, CHAR *szAsciiName, UINT8 ucYear, UINT8 ucMonth, UINT8 ucDay, UINT8 ucHour, UINT8 unMin, UINT8 ucSec);
extern INT  fsMergeFile(CHAR *suFileNameA, CHAR *szAsciiNameA, CHAR *suFileNameB, CHAR *szAsciiNameB);
extern INT  fsDeleteFile(CHAR *suFileName, CHAR *szAsciiName);
extern INT  fsRenameFile(CHAR *suOldName, CHAR *szOldAsciiName, CHAR *suNewName, CHAR *szNewAsciiName, BOOL bIsDirectory);
extern INT  fsMoveFile(CHAR *suOldName, CHAR *szOldAsciiName, CHAR *suNewName, CHAR *szNewAsciiName, INT bIsDirectory);
extern INT  fsCopyFile(CHAR *suSrcName, CHAR *szSrcAsciiName, CHAR *suDstName, CHAR *szDstAsciiName);
extern INT    fsFindFirst(CHAR *suDirName, CHAR *szAsciiName, FILE_FIND_T *ptFindObj);
extern INT  fsFindNext(FILE_FIND_T *ptFindObj);
extern INT    fsFindClose(FILE_FIND_T *ptFindObj);
extern BOOL fsIsFileOpened(CHAR *suFileName);

/* Directory operations */
extern INT  fsMakeDirectory(CHAR *suDirName, CHAR *szAsciiName);
extern INT  fsRemoveDirectory(CHAR *suDirName, CHAR *szAsciiName);
extern INT  fsDeleteDirTree(CHAR *suDirName, CHAR *szAsciiName);
extern INT  fsGetDirectoryInfo(CHAR *suDirName, CHAR *szAsciiName,
                        INT *nFileCnt, INT *nDirCnt, UINT64 *u64TotalSize, BOOL bSearchSubDir);
extern INT  fsGetDirectoryInfo2(CHAR *suDirName, CHAR *szAsciiName, CHAR **suPattern,
                        INT *pnFileCnt, INT *pnDirCnt, UINT64 *u64TotalSize, BOOL bSearchSubDir);

/* language support */
extern CHAR fsToUpperCase(CHAR chr);
extern INT  fsUnicodeToAscii(VOID *pvUniStr, VOID *pvASCII, BOOL bIsNullTerm);
extern INT  fsAsciiToUnicode(VOID *pvASCII, VOID *pvUniStr, BOOL bIsNullTerm);
extern VOID fsAsciiToUpperCase(VOID *pvASCII);
extern INT  fsAsciiNonCaseCompare(VOID *pvASCII1, VOID *pvASCII2);
extern VOID fsUnicodeToUpperCase(VOID *pvUnicode);
extern VOID fsUnicodeToLowerCase(VOID *pvUnicode);
extern INT  fsUnicodeStrLen(VOID *pvUnicode);
extern INT  fsUnicodeNonCaseCompare(VOID *pvUnicode1, VOID *pvUnicode2);
extern INT  fsUnicodeCopyStr(VOID *pvStrDst, VOID *pvStrSrc);
extern INT  fsUnicodeStrCat(VOID *pvUniStrHead, VOID *pvUniStrTail);
extern VOID fsGetAsciiFileName(VOID *pvUniStr, VOID *pvAscii);
extern INT  fsUnicodeWildCardCompare(CHAR *suStr1, CHAR *suStr2);

/* Driver supporting routines */
extern INT  fsPhysicalDiskConnected(PDISK_T *ptPDisk);
extern INT  fsPhysicalDiskDisconnected(PDISK_T *ptPDisk);


/* For debug and internal use, not exported funcions */
//extern CHAR *fsFindFirstSlash(CHAR *szFullName);
extern CHAR *fsFindLastSlash(CHAR *suFullName);
extern INT  fsTruncatePath(CHAR *szPath, CHAR *szToken);
extern VOID  fsTrimEndingSpace(CHAR *szStr);
extern FILE_T *fsHandleToDescriptor(INT hFile);
extern INT  fsDescriptorToHandle(FILE_T *ptFile);

extern VOID fsDumpBufferHex(UINT8 *pucBuff, INT nSize);
extern VOID fsDumpSectorHex(INT uSectorNo, UINT8 *pucBuff, INT nSize);
extern INT  fsDumpDiskSector(UINT32 uSectorNo, INT nSecNum);

extern LDISK_T *fsAllocateDisk(PDISK_T *ptPDisk, PARTITION_T *ptPartition);
extern UINT8 *fsAllocateSector(VOID);
extern INT  fsFreeSector(UINT8 *pucSecAddr);

extern CHAR  *fsDebugUniStr(CHAR *suStr);
//extern BOOL fsIsValidLongEntName(CHAR *szDirEntName);
//extern BOOL fsIsValidShortNameChar(CHAR cChar);

extern VOID  lname_to_sname(CHAR *szAsciiName, INT nTildeNum, CHAR *szShortName);

extern INT  fsFlushIOCache(VOID);
extern INT  fsIOWrite(PDISK_T *ptPDisk, UINT32 uStartSecNo, INT nCount, UINT8 *pucOutBuff);
extern INT  fsIORead(PDISK_T *ptPDisk, UINT32 uStartSecNo, INT nCount, UINT8 *in_buf);
extern VOID fs_enable_iow_cache(VOID);
extern INT  fs_flush_iow_cache(VOID);
extern VOID *fsCheckDriverPointer(VOID *pvDrive);

#endif  /* _NVTFAT_H_ */


nvtfat_fat.h    。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/*************************************************************************
 * Nuvoton Electronics Corporation confidential
 *
 * Copyright (c) 2008 by Nuvoton Electronics Corporation
 * All rights reserved
 * 
 * FILENAME
 *     nvtfat_fat.h
 *
 * REMARK
 *     None
 **************************************************************************/

#ifndef    _NVTFAT_FAT_H_
#define    _NVTFAT_FAT_H_


/* Defined for long filenames */
#define    LFN_MAX_FILENAME            514        /* maximum length of LFN in bytes */

/* Macro to    convert    Cluster    number to logical sector number    */
#define    ClustNo2SecNo(n) ((UINT32)(n - 2) * ptLDisk->tFatInfo.uBpbSecPerClus + ptLDisk->tFatInfo.uDataSecStart)

#define SECTOR_TYPE_FILE            0
#define SECTOR_TYPE_DIR                1
#define SECTOR_TYPE_FAT                2
#define SECTOR_TYPE_PART            3

typedef    struct sector_buf_S
{
    UINT8        *pucData;            /* cached sector contents                             */
    LDISK_T     *ptLDisk;             /* LDISK_T * associated    with this sector              */
    UINT32        uSectorNo;            /* logical sector number                              */
    BOOL         bIsDirty;            /* dirty or not                                       */
    INT          nReaderCnt;            /* how many threads are reading it                    */
    UINT32        jiffy;                /* access time stamp                                  */
    VOID         *os_owner;            /* for OS                                             */
    VOID         *os_priv;            /* for OS                                             */
}     SEC_BUFF_T;

/* For FAT and DATA sector check out */
#define READ_ACCESS                0
#define WRITE_ACCESS            1


/*======================================================== FAT information ==*/
typedef struct fat_info_S
{
    /*- The followings are BPB fields commonly used by FAT16/FAT32 -*/
    
    UINT32      uBpbBytesPerSec;    /* BPB[11], size:2 */
    UINT32      uBpbSecPerClus;     /* BPB[13], size:1 */
    UINT16      usBpbRsvdSecCnt;    /* BPB[14], size:2 */
    UINT8       ucBpbNumFATs;       /* BPB[16], size:1 */
    UINT16      usBpbRootEntCnt;    /* BPB[17], size:2 */
    UINT32      uBpbTotalSectors;   /* FAT12/FAT16 - BPB[19], size:2 */
                                    /* FAT32 - BPB[32], size: 4 */
    UINT8       ucBpbMedia;         /* BPB[21], size:1 */
    UINT32      uBpbFatSize;        /* FAT12/FAT16 - BPB[22], size:2 */
                                    /* FAT32 - BPB[36], size:4 */
    UINT16      usBpbSecPerTrk;     /* BPB[24], size:2 */
    UINT16      usBpbNumHeads;      /* BPB[26], size:2 */
    UINT32      uBpbHiddSec;        /* BPB[28], size:4 */
    
    UINT32      uBsVolID;           /* FAT12/FAT16 - BPB[39], size:4 */
                                    /* FAT32 - BPB[67], size:4 */
    CHAR        sBsVolLab[12];      /* FAT12/FAT16 - BPB[43], size:11 */
                                    /* FAT32 - BPB[71], size:4 */

    UINT32       uBpbRootClus;       /* BPB[44], size:4 */
    UINT16       usBpbFsInfo;        /* BPB[48], size:2 */
    
    /*- The following is FAT32 structure start from offset 36 -*/
    
    /* UINT32   uBpbBytesPerSec; */ /* BPB[36], size:4 */
    /* UINT16   usBsDrvNum;  */     /* BPB[40], size:2 */
    /* UINT16   usBpbFsVer; */      /* BPB[42], size:2 */
    /* UINT16   usBpbBkBootSec; */  /* BPB[50], size:2 */
    /* bpb_Reserved */              /* BPB[52], size:12 */
    /* usBsDrvNum */                /* BPB[64], size:1 */
    /* bs_Reserved1 */              /* BPB[65], size:1 */
    /* bs_BootSig */                /* BPB[66], size:1 */

    /*- The followings are proprietary used by our driver -*/
    UINT32      uEndClusMark;        /* FAT12 = 0xFF7, FAT16 = 0xFFF7, FAT32 = 0x0FFFFFF7 */
    UINT8        ucFatType;            /* TYPE_FAT12/TYPE_FAT16/TYPE_FAT32 */
    UINT32      uSecSzMask;          /* uBpbBytesPerSec - 1 */
    UINT32      uBytesPerClus;       /* uBpbBytesPerSec * uBpbSecPerClus */
    UINT32        uLastSecNo;            /* the last physical sector number on this partition */
    UINT32      uLastClusNo;        /* last cluster number on this partition */
    UINT32      uFatSecStart;        /* sector number of the first FAT sector */
    UINT32      uDataSecStart;       /* sector number of first data sector */
    UINT32      uRootDirStartSec;    /* FAT12/16 first sector number of the root directory */
    UINT32        uFsInfoSecNo;        /* FAT32 - the sector number of pucFSInfo sector */
    UINT32        uFreeClusSearchIdx;    /* the next start point to search a free cluster */
    UINT32        uTotalFreeClus;        /* total number of free clusters */
    UINT8        pucFSInfo[512];        /* pucFSInfo sector */
}      FAT_INFO_T;


#define FAT12_END_CLUS_MARK            0xFF8            
#define FAT16_END_CLUS_MARK            0xFFF8
#define FAT32_END_CLUS_MARK            0x0FFFFFF8

#define FAT12_BAD_CLUS_MARK            0xFF7
#define FAT16_BAD_CLUS_MARK            0xFFF7
#define FAT32_BAD_CLUS_MARK            0x0FFFFFF7


#define TYPE_FAT12                    12
#define TYPE_FAT16                    16
#define TYPE_FAT32                    32


/*================================================ FAT directory structure ==*/
/* 
 * A FAT directory entry size is 32 bytes and sector size is 512 bytes.
 * So, it must be word-aligned.
 */
/* physical file name entry */
typedef struct dir_ent_S
{
    UINT8       pucDirName[8];        /* Offset: 0,  Size: 11 */
    UINT8        pucDirExtName[3];    
    UINT8       ucDirAttrib;        /* Offset: 11, Size: 1  */
    UINT8       ucDirNTRes;            /* Offset: 12, Size: 1  */
    UINT8       ucDirCTimeTenth;    /* Offset: 13, Size: 1  */
    UINT8       pucDirCTime[2];        /* Offset: 14, Size: 2  */
    UINT8       pucDirCDate[2];     /* Offset: 16, Size: 2  */
    UINT8       pucDirLADate[2];      /* Offset: 18, Size: 2  */
    UINT8       pucDirFirstClusHi[2];/* Offset: 20, Size: 2  */
    UINT8       pucDirWTime[2];     /* Offset: 22, Size: 2  */
    UINT8       pucDirWDate[2];        /* Offset: 24, Size: 2  */
    UINT8       pucDirFirstClusLo[2];/* Offset: 26, Size: 2  */
    UINT8       pucDirFileSize[4];    /* Offset: 28, Size: 4  */
}     DIR_ENT_T;


/* physical long file name entry */
typedef struct lfn_ent_S
{
    UINT8       ucLDirOrd;            /* Offset: 0,  Size: 1 */
    UINT8       pucLDirName1[10];   /* Offset: 1,  Size: 10, Unicode Chars 1~5 */
    UINT8       ucLDirAttrib;       /* Offset: 11, Size: 1, always 0x0F */
    UINT8       ucLDirType;         /* Offset: 12, Size: 1 */
    UINT8       ucLDirChksum;       /* Offset: 13, Size: 1, Checksum of 8.3 name */
    UINT8       pucLDirName2[12];   /* Offset: 14, Size: 12, Unicode Chars 6-11 */
    UINT8       pucLDirFirstClusLo[2];     /* Offset: 26, Size: 2, always 0  */
    UINT8       pucLDirName3[4];    /* Offset: 28, Size: 4, Unicode Chars 12-13 */
}     LFN_ENT_T;

/* FAT file attributes */
#define A_NORMAL           0x00        /* Normal file, no attributes */
#define A_RDONLY           0x01        /* Read only attribute */
#define A_HIDDEN           0x02        /* Hidden file */
#define A_SYSTEM           0x04        /* System file */
#define A_LABEL            0x08        /* Volume label */
#define A_DIR              0x10        /* Directory    */
#define A_ARCHIVE       0x20        /* Archive */
#define A_LFN           0x0F        /* (A_RDONLY | A_HIDDEN | A_SYSTEM | A_LABEL) */
#define A_LFN_MASK      0x3F        /* (A_LFN | A_DIR | A_ARCHIVE) */


/*================================================ Run-time data structure ==*/
/*
 *  Each FILE_T has its own FAT configuration on a FAT file system. Refer to the
 *  FILE_T defined in nvtfat.h.
 */
typedef struct file_control_block_S
{
    /* For directory entry */    
    //CHAR        suLongName[LFN_MAX_FILENAME];
    CHAR        szShortName[14];
    UINT8       ucDirAttrib;        /* file attribute */
    UINT8       ucDirNTRes;
    UINT8        ucDirCTimeTenth;    /* Millisecond stamp at file creation time */    UINT16      dirCtime;           /* Time file was created */
    UINT16      usDirCDate;         /* Date file was created */
    UINT16      usDirLDate;         /* Last access date */
    UINT16      usDirWTime;            /* Time of last write. Note that file creation is considered a write. */
    UINT16      usDirWDate;            /* Date of last write. Note that file creation is considered a write. */
    
    UINT32        uDirEntSecNo;        /* Number of the sector where this directory entry is located. */
    INT            nDirEntInSecOffset;    /* Directory entry offset of this file in <uDirEntSecNo> sector. */

    /* for run-time file operation */
    BOOL        bIsFat16Root;        /* is FAT12/FAT16 root directory */

    BOOL        bIsRWBuffDirty;        /* read/write buffer is dirty or not */
    UINT8        *pucRWBuff;            /* file r/w buffer */
    INT64        n64RWBuffMapPos;    /* the file position of the r/w buffer mapped to */
    UINT32        uRWBuffSecNo;        /* the start sector number of r/w buffer */
    INT         nRWBuffSecCnt;        /* the start sector number of r/w buffer */
    INT            nClusCnt;            /* number of clusters cached in r/w buffer */
    UINT32        uRWBuffSize;        /* the size of read/write buffer */

    UINT32        *pucCCBuff;            /* cluster chain buffer */
    UINT32        uCCBuffSize;        /* cluster chain buffer size */
    UINT32        uCCEntCnt;            /* cluster chain entry count */
    UINT32        uCCDoneFPos;        /* cluster chain done file position */

    UINT32      uFirstClusNo;       /* first cluster number of this file */
    UINT32        uLastClusNo;
    UINT32      uCurClusNo;         /* the number of the cluster currently used */
    INT64        n64FileSize;        /* The total length of this file */
    INT64          n64FilePos;         /* current position in file */
}      FAT_FCB_T;


#ifdef ECOS
extern cyg_mutex_t  _fat_mutex;
#define FAT_MUTEX_INIT()        cyg_mutex_init(&_fat_mutex)   
#define FAT_MUTEX_LOCK()        cyg_mutex_lock(&_fat_mutex)
#define FAT_MUTEX_UNLOCK()        cyg_mutex_unlock(&_fat_mutex)
#else  /* Non-OS */
#define FAT_MUTEX_INIT()        
#define FAT_MUTEX_LOCK()        
#define FAT_MUTEX_UNLOCK()
#endif

/*====================================== External reference in library only ==*/
extern DISK_OP_T _fs_tFatDiskOperation;
extern FILE_OP_T _fs_tFatFileOperation;
extern BOOL         _fs_bLockFileSystem;

extern INT  fs_fat_parse_partition(PDISK_T *ptPDisk, PARTITION_T *ptPartition, UINT32 uBpbSecNo);
extern INT  fs_fat_format_partition(PDISK_T *ptPDisk, PARTITION_T *ptPartition, INT nType);

extern INT   fs_fat32_scan_disk(LDISK_T *ptLDisk);
extern INT   fs_fat32_defragment_disk(LDISK_T *ptLDisk);

/* Cache */
extern VOID fs_fat_init_sector_cache(VOID);
extern VOID fs_fat_debug_sector_cache(VOID);

extern INT  fs_fat_check_out_sec(LDISK_T *ptLDisk, UINT32 uSectorNo, INT nAccess, SEC_BUFF_T **ptSecBuff);
extern VOID  fs_fat_check_in_sec(SEC_BUFF_T *ptSecBuff, INT nAccess, BOOL bIsDirty);
extern INT  fs_fat_flush_sector_cache(LDISK_T *ptLDisk);
extern VOID fs_fat_clear_sector_cache(LDISK_T *ptLDisk);

/* FAT32 pucFSInfo sector */
//extern INT  fs_fat_update_fs_info(LDISK_T *ptLDisk);

extern INT  fsScanDisk(INT nDriveNo);

/* FAT table */
extern INT  read_fs_info(LDISK_T *ptLDisk);
extern INT  fs_read_fat_table(LDISK_T *ptLDisk, UINT32 uClusNo, UINT32 *puEntryValue);
extern INT  fs_write_fat_table(LDISK_T *ptLDisk, UINT32 uClusNo, UINT32 uEntryValue);
extern INT  fs_fat_reclaim_clusters(LDISK_T *ptLDisk, UINT32 uStartClusNo);
extern INT  fs_fat_allocate_cluster(FILE_T *ptFile);

/* directory entry */
extern INT  fs_fat_delete_file(LDISK_T *ptLDisk, CHAR *suFullName, CHAR *szAsciiName, BOOL bIsDirectory);
extern INT  fs_fat_create_file(FILE_T *ptFile, CHAR *suFullName, CHAR *szAsciiName, UINT32 *uClusNoOfParent);
extern INT  fs_fat_merge_file(LDISK_T *ptLDisk, CHAR *suFileNameA, CHAR *szAsciiNameA, CHAR *suFileNameB, CHAR *szAsciiNameB);
extern INT  fs_fat_get_next_dir_entry(FILE_T *ptFile, UINT8 *pucBuff, CHAR **szLongName, DIR_ENT_T *ptDirEnt, UINT32 *uStartPos);
extern VOID fs_fat_set_dire_mtime(FAT_FCB_T *ptFcb, DIR_ENT_T *ptDire, INT bType);
extern VOID fs_fat_set_sdir_name(CHAR *szShortName, DIR_ENT_T *ptDire);
extern VOID fs_fat_get_sdir_name(CHAR *szShortName, DIR_ENT_T *ptDire);
extern UINT8  *fs_fat_get_ldir_name(UINT8 *pucNameBuff, LFN_ENT_T *ptLDire);
extern VOID fs_fat_set_dire_info(FAT_FCB_T *ptFcb, DIR_ENT_T *ptDire);
extern VOID fs_fat_get_dire_info(FAT_FCB_T *ptFcb, DIR_ENT_T *ptDire);
extern INT  fs_fat_search_file(FILE_T *ptFile, CHAR *szFileName);
extern INT  fs_fat_delete_tree(LDISK_T *ptLDisk, CHAR *suFullName, CHAR *szAsciiName);
extern INT  fs_update_first_cluster(FILE_T *ptFile, UINT32 uClusNo);

extern VOID fs_fat_dump_fcb(FAT_FCB_T *ptFcb);
extern VOID fsDumpUniStr(UINT8 *suStr);

#endif    /* _NVTFAT_FAT_H_ */




PWM.c  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。



/*---------------------------------------------------------------------------------------------------------*/
/*                                                                                                         */
/* Copyright (c) Nuvoton Technology Corp. All rights reserved.                                             */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------------------------------------*/
/* Includes of system headers                                                                              */
/*---------------------------------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wbio.h"
#include "wblib.h"
#include "Common.h"
#include "PWM.h"


/*---------------------------------------------------------------------------------------------------------*/
/* Macro, type and constant definitions                                                                    */
/*---------------------------------------------------------------------------------------------------------*/
#define PWM_GLOBALS


/*---------------------------------------------------------------------------------------------------------*/
/* Global file scope (static) variables                                                                    */
/*---------------------------------------------------------------------------------------------------------*/
static PWM_CALLBACK_T pfnPWMHandler = {FALSE};

/*---------------------------------------------------------------------------------------------------------*/
/* Function:     <PWM0_ISR>                                                                                */
/*                                                                                                         */
/* Parameter:                                                                                              */
/*               VOID                                                                                      */
/* Returns:                                                                                                */
/*               None                                                                                      */
/* Side effects:                                                                                           */
/*                                                                                                         */
/* Description:                                                                                            */
/*               ISR to handle PWM0 interrupt event                                                          */
/*---------------------------------------------------------------------------------------------------------*/
static VOID PWM_ISR (VOID)
{ 
    UINT32 volatile u32RegPIIR,u32RegCCR0,u32RegCCR1;

    u32RegPIIR = inp32(PIIR);
    u32RegCCR0 = inp32(CCR0);
    u32RegCCR1 = inp32(CCR1);    
    
    if (u32RegPIIR & 0xF )    
    {
        if(u32RegPIIR & PIIR0);
        {
            outp32(PIIR, PIIR0);    
            if (pfnPWMHandler.pfnPWM0CallBack != NULL)                           
                pfnPWMHandler.pfnPWM0CallBack();    
        }                     
        if(u32RegPIIR & PIIR1);
        {
            outp32(PIIR, PIIR1);    
            if (pfnPWMHandler.pfnPWM1CallBack != NULL)                           
                pfnPWMHandler.pfnPWM1CallBack();    
        }   
        if(u32RegPIIR & PIIR2);
        {
            outp32(PIIR, PIIR2);    
            if (pfnPWMHandler.pfnPWM2CallBack != NULL)                           
                pfnPWMHandler.pfnPWM2CallBack();    
        } 
        if(u32RegPIIR & PIIR3);
        {
            outp32(PIIR, PIIR3);    
            if (pfnPWMHandler.pfnPWM3CallBack != NULL)                           
                pfnPWMHandler.pfnPWM3CallBack();    
        }    
    }

    if (u32RegCCR0 & CIIR0) 
    {
        outp32(CCR0, inp32(CCR0) & ~CIIR0);
        if (pfnPWMHandler.pfnCAP0CallBack != NULL)
        {
            pfnPWMHandler.pfnCAP0CallBack();
        }
    }

    if (u32RegCCR0 & CIIR1) 
    {
        outp32(CCR0, inp32(CCR0) & ~CIIR1);
        if (pfnPWMHandler.pfnCAP1CallBack != NULL)
        {
            pfnPWMHandler.pfnCAP1CallBack();
        }
    }        

    if (u32RegCCR1 & CIIR2) 
    {
        outp32(CCR1, inp32(CCR1) & ~CIIR2);
        if (pfnPWMHandler.pfnCAP2CallBack != NULL)
        {
            pfnPWMHandler.pfnCAP2CallBack();
        }
    }         
    if (u32RegCCR1 & CIIR3) 
    {
        outp32(CCR1, inp32(CCR1) & ~CIIR3);
        if (pfnPWMHandler.pfnCAP3CallBack != NULL)
        {
            pfnPWMHandler.pfnCAP3CallBack();
        }
    }            
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_IsTimerEnabled                                                                            */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                            */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               0        disable                                                                            */
/*               1        enable                                                                               */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to get PWM specified timer enable/disable state                     */
/*---------------------------------------------------------------------------------------------------------*/
BOOL PWM_IsTimerEnabled(UINT8 u8Timer)
{
    return ((inp32(PCR) & (1 << ((u8Timer & 0x7) << 3))) ? TRUE : FALSE);
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_SetTimerCounter                                                                           */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                      */
/*               u16Counter     - [in]      Timer counter : 0~65535                                        */
/* Returns:                                                                                                */
/*               0        disable                                                                            */
/*               1        enable                                                                               */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to set the PWM specified timer counter                                   */
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_SetTimerCounter(UINT8 u8Timer, UINT16 u16Counter)
{
    outp32(CNR0 + (u8Timer & 0x07) * 12, u16Counter);        
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_GetTimerCounter                                                                           */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                     */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               The specified timer counter value                                                         */
/*                                                                                                         */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to get the PWM specified timer counter value                        */
/*---------------------------------------------------------------------------------------------------------*/
UINT32 PWM_GetTimerCounter(UINT8 u8Timer)
{
    return (inp32(PDR0 + 0x0C * (u8Timer & 0x07)) & 0xFFFF);
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvPWM_InstallCallBack                                                                           */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        DRVPWM_TIMER0/DRVPWM_TIMER1/DRVPWM_TIMER2/DRVPWM_TIMER3        */
/*                                             DRVPWM_CAP0/DRVPWM_CAP1/DRVPWM_CAP2/DRVPWM_CAP3                   */
/*               pfncallback    - [in]        The call back function for specified timer / capture           */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                            */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to iNnstall the PWM timer/capture interrupt call back function      */
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_InstallCallBack(
    UINT8 u8Timer,
    PFN_PWM_CALLBACK pfncallback,
    PFN_PWM_CALLBACK *pfnOldcallback
)
{
    switch(u8Timer)
    {    
        case PWM_TIMER0:
        case PWM_CAP0:            
            if(pfncallback != NULL)
            {
                if(u8Timer & 0x10)
                {
                    *pfnOldcallback = pfnPWMHandler.pfnCAP0CallBack;
                       pfnPWMHandler.pfnCAP0CallBack = pfncallback;
                   }
                   else
                   {
                       *pfnOldcallback = pfnPWMHandler.pfnPWM0CallBack;
                       pfnPWMHandler.pfnPWM0CallBack = pfncallback;
                   }
            }
            break;
        case PWM_TIMER1:
        case PWM_CAP1:        
            if(pfncallback != NULL)
            {
                if(u8Timer & 0x10)
                {
                    *pfnOldcallback = pfnPWMHandler.pfnCAP1CallBack; 
                       pfnPWMHandler.pfnCAP1CallBack = pfncallback;
                   }
                   else
                   {
                       *pfnOldcallback = pfnPWMHandler.pfnPWM1CallBack;
                       pfnPWMHandler.pfnPWM1CallBack = pfncallback;
                   }
            }        
            break;
        case PWM_TIMER2:
        case PWM_CAP2:
            if(pfncallback != NULL)
            {
                if(u8Timer & 0x10)
                {
                    *pfnOldcallback = pfnPWMHandler.pfnCAP2CallBack;
                       pfnPWMHandler.pfnCAP2CallBack = pfncallback;
                   }
                   else
                   {
                       *pfnOldcallback = pfnPWMHandler.pfnPWM2CallBack;
                       pfnPWMHandler.pfnPWM2CallBack = pfncallback;
                   }
            }                
            break;
        case PWM_TIMER3:
        case PWM_CAP3:
            if(pfncallback != NULL)
            {
                if(u8Timer & 0x10)
                {
                    *pfnOldcallback = pfnPWMHandler.pfnCAP3CallBack;
                       pfnPWMHandler.pfnCAP3CallBack = pfncallback;
                   }
                   else
                   {
                       *pfnOldcallback = pfnPWMHandler.pfnPWM3CallBack; 
                       pfnPWMHandler.pfnPWM3CallBack = pfncallback;
                   }
            }                

            
            break;                                    
    }
    
    sysInstallISR(IRQ_LEVEL_7, IRQ_PWM, (PVOID)PWM_ISR);    
    sysSetLocalInterrupt(ENABLE_IRQ);
    sysEnableInterrupt(IRQ_PWM);       
}
/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_EnableInt                                                                                    */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                       */
/*                                             PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                               */
/*               u8Int             - [in]        PWM_CAP_RISING_INT/PWM_CAP_FALLING_INT/PWM_CAP_ALL_INT           */
/*                                          (The parameter is valid only when capture function)                  */
/*               pfncallback    - [in]        The call back function for specified timer / capture           */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                            */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to Enable the PWM timer/capture interrupt                            */
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_EnableInt(UINT8 u8Timer, UINT8 u8Int)
{
    if(u8Timer & 0x10)
    {        
        if(u8Timer & 0x02)        
            outp32(CCR1, (inp32(CCR1) & ~((0x06 << ((u8Timer & 0x01) << 4 )) | (CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2))) |    ((u8Int & 0x03) << (((u8Timer & 0x01) <<4) + 1)));                    
        else
            outp32(CCR0, (inp32(CCR0) & ~((0x06 << ((u8Timer & 0x01) << 4 )) | (CFLRD1 | CRLRD1 | CIIR1 | CFLRD0 | CRLRD0 |CIIR0))) |    ((u8Int & 0x03) << (((u8Timer & 0x01) <<4) + 1)));                    
    }
    else
        outp32(PIER,  inp32(PIER) | (1<<u8Timer));
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_DisableInt                                                                                     */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                     */
/*                                             PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                               */
/*               u8Int             - [in]        PWM_CAP_RISING_INT/PWM_CAP_FALLING_INT/PWM_CAP_ALL_INT            */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                            */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to clear the PWM timer/capture interrupt                            */
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_DisableInt(UINT8 u8Timer, UINT8 u8Int)
{   
    if(u8Timer & 0x10)
    {
        if(u8Timer & 0x02)        
            outp32(CCR1, inp32(CCR1) & ~(((u8Int & 0x03) << (((u8Timer & 0x01) <<4) + 1)) | (CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2)));                    
        else
            outp32(CCR0, inp32(CCR0) & ~(((u8Int & 0x03) << (((u8Timer & 0x01) <<4) + 1)) | (CFLRD1 | CRLRD1 | CIIR1 | CFLRD0 | CRLRD0 |CIIR0)));                    
        
    }
    else
    {            
          outp32(PIER,  inp32(PIER) & ~(1 << (u8Timer & 0x03)));
           outp32(PIIR,  inp32(PIIR) & ~(1 << (u8Timer & 0x03)));
       } 
    switch(u8Timer)
    {    
        case PWM_TIMER0:
        case PWM_CAP0:            
               pfnPWMHandler.pfnCAP0CallBack = NULL;
               pfnPWMHandler.pfnPWM0CallBack = NULL;    
            break;
        case PWM_TIMER1:
        case PWM_CAP1:        
               pfnPWMHandler.pfnCAP1CallBack = NULL;
               pfnPWMHandler.pfnPWM1CallBack = NULL;    
            break;
        case PWM_TIMER2:
        case PWM_CAP2:
               pfnPWMHandler.pfnCAP2CallBack = NULL;
               pfnPWMHandler.pfnPWM2CallBack = NULL;    
            break;
        case PWM_TIMER3:
        case PWM_CAP3:
               pfnPWMHandler.pfnCAP3CallBack = NULL;
               pfnPWMHandler.pfnPWM3CallBack = NULL;    
            break;                                    
    }
    
    
    if(((inp32(PIER) & 0xF) == 0) && ((inp32(CCR0) & 0x60006) == 0) && ((inp32(CCR1) & 0x60006) == 0))
        sysDisableInterrupt(IRQ_PWM);            
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_ClearInt                                                                                     */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                      */
/*                                             PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                                */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                            */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to clear the PWM timer/capture interrupt                            */
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_ClearInt(UINT8 u8Timer)
{
    if(u8Timer & 0x10)
    {
        if(u8Timer & 0x02)
            outp32(CCR1, inp32(CCR1) & ~((1 << (((u8Timer & 0x01) << 4) + 4)) | (CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2)));
        else
            outp32(CCR0, inp32(CCR0) & ~((1 << (((u8Timer & 0x01) << 4) + 4)) | (CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2)));            
    }
    else
        outp32(PIIR,  inp32(PIIR) & ~(1<<(u8Timer & 0x03)));
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_GetIntFlag                                                                                    */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                     */
/*                                             PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                               */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               0        FALSE                                                                              */
/*               1        TRUE                                                                               */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to get the PWM timer/capture interrupt flag                            */
/*---------------------------------------------------------------------------------------------------------*/
BOOL PWM_GetIntFlag(UINT8 u8Timer)
{
    if(u8Timer & 0x10)
    {
        if(u8Timer & 0x02)
        {    
            if(inp32(CCR1) & (1 << (((u8Timer & 0x01) << 4) + 4)))
                return TRUE;
            else
                return FALSE;
        }
        else
        {      
             if(inp32(CCR0) & (1 << (((u8Timer & 0x01) << 4) + 4)))
                return TRUE;
            else
                return FALSE;
        }        
    }
    else
    {
        if(inp32(PIIR) & (1<<(u8Timer & 0x03)))
            return TRUE;
        else
            return FALSE;
    }        
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_GetRisingCounter                                                                           */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Capture         - [in]        PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                                  */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               The value which latches the counter when thereˇs a rising transition                      */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to get value which latches the counter when thereˇs a rising        */        
/*                 transition                                                                                      */
/*---------------------------------------------------------------------------------------------------------*/
UINT16 PWM_GetRisingCounter(UINT8 u8Capture)
{
    return inp32(CRLR0 + ((u8Capture & 0x7) << 3));
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_GetFallingCounter                                                                            */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Capture         - [in]        PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                                  */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               The value which latches the counter when thereˇs a falling transition                     */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to get value which latches the counter when thereˇs a falling        */        
/*                 transition                                                                                      */
/*---------------------------------------------------------------------------------------------------------*/
UINT16 PWM_GetFallingCounter(UINT8 u8Capture)
{
    return inp32(CFLR0 + ((u8Capture & 0x7)<< 3));
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_GetCaptureIntStatus                                                                       */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Capture         - [in]        PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                                  */
/*               u8IntType         - [in]        PWM_CAP_RISING_FLAG/PWM_CAP_FALLING_FLAG                          */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               Check if thereˇs a rising / falling transition                                                */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to get the rising / falling transition interrupt flag               */        
/*---------------------------------------------------------------------------------------------------------*/

BOOL PWM_GetCaptureIntStatus(UINT8 u8Capture, UINT8 u8IntType)
{    
    if(u8Capture & 0x02)
    {
        if(inp32(CCR1) &  (1 << (((u8Capture & 0x01) << 4) + u8IntType)))
            return TRUE;
        else 
            return FALSE;
    }
    else
    {
        if(inp32(CCR0) &  (1 << (((u8Capture & 0x01) << 4) + u8IntType)))
            return TRUE;
        else 
            return FALSE;
    }
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_ClearCaptureIntStatus                                                                        */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Capture         - [in]        PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                                   */
/*               u8IntType         - [in]        PWM_CAP_RISING_FLAG/PWM_CAP_FALLING_FLAG                          */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               Clear the rising / falling transition interrupt flag                                       */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to clear the rising / falling transition interrupt flag                */        
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_ClearCaptureIntStatus(UINT8 u8Capture, UINT8 u8IntType)
{
    if(u8Capture & 0x02)
        outp32(CCR1, (inp32(CCR1) & ~(CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2)) | (1 << (((u8Capture & 0x01) << 4) + u8IntType)));
    else
        outp32(CCR0, (inp32(CCR0) & ~(CFLRD1 | CRLRD1 | CIIR1 | CFLRD0 | CRLRD0 |CIIR0)) | (1 << (((u8Capture & 0x01) << 4) + u8IntType)));
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_Open                                                                                          */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               None                                                                                        */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                           */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               Enable PWM engine clock and reset PWM                                                           */        
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_Open(VOID)
{ 
    outp32(REG_APBCLK, inp32(REG_APBCLK)|PWM_CKE);
    outp32(REG_APBIPRST, PWMRST);
    outp32(REG_APBIPRST, inp32(REG_APBIPRST) & ~PWMRST);
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_Close                                                                                          */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               None                                                                                        */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                           */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               Disable PWM engine clock and the I/O enable                                               */        
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_Close(VOID)
{
    outp32(POE, 0);
    outp32(CAPENR,0x0);
       sysDisableInterrupt(IRQ_PWM);
    outp32(REG_APBCLK, inp32(REG_APBCLK) & ~PWM_CKE);
    
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_EnableDeadZone                                                                                    */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                    */
/*               u8Length         - [in]        Dead Zone Length : 0~255                                             */
/*               bEnableDeadZone- [in]        Enable DeadZone (TRUE) / Diasble DeadZone (FALSE)                */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to set the dead zone length and enable/disable Dead Zone function   */        
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_EnableDeadZone(UINT8 u8Timer, UINT8 u8Length,BOOL bEnableDeadZone)
{
    if(u8Timer & 0x02)
    {
        outp32(PPR, (inp32(PPR) & ~DZI1) | ((u8Length & 0xFF) << 24));
        outp32(PCR, (inp32(PCR) & ~DZEN1) | (bEnableDeadZone?DZEN1:0));            
    }
    else
    {
        outp32(PPR, (inp32(PPR) & ~DZI0) | ((u8Length & 0xFF) << 16));                
        outp32(PCR, (inp32(PCR) & ~DZEN0) | (bEnableDeadZone?DZEN0:0));        
    }    
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_Enable                                                                                        */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                    */
/*                                             PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                               */
/*               bEnable        - [in]        Enable  (TRUE) / Disable  (FALSE)                                */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to enable PWM timer / capture function                                */        
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_Enable(UINT8 u8Timer,BOOL bEnable)
{
    outp32(PCR, (inp32(PCR)& ~(1 << ((u8Timer & 0x07) << 3) )) | (bEnable?(1 << ((u8Timer & 0x07) << 3) ):0) );
    
    if(u8Timer & 0x10)
    {
        if(u8Timer & 0x02)
            outp32(CCR1, (inp32(CCR1) & ~((1 << (((u8Timer & 0x01) << 4) + 3))) | (CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2)) | (bEnable?(1 << (((u8Timer & 0x01) << 4) + 3)):0));    
        else
            outp32(CCR0, (inp32(CCR0) & ~((1 << (((u8Timer & 0x01) << 4) + 3))) | (CFLRD1 | CRLRD1 | CIIR1 | CFLRD0 | CRLRD0 |CIIR0)) | (bEnable?(1 << (((u8Timer & 0x01) << 4) + 3)):0));
    }
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_SetTimerClk                                                                                */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer                         PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                    */
/*                                                 PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                           */
/*                 PWM_TIME_DATA_T *sPt                                                                       */
/*                   u8Frequency                    Frequency                                                     */
/*                   u8HighPulseRatio            High Pulse Ratio                                              */
/*                   u8Mode                        PWM_ONE_SHOT_MODE / PWM_TOGGLE_MODE                        */
/*                   bInverter                    Inverter Enable  (TRUE) / Inverter Disable  (FALSE)        */
/*                   u8ClockSelector                Clock Selector                                               */
/*                                              PWM_CLOCK_DIV_1/PWM_CLOCK_DIV_2/PWM_CLOCK_DIV_4              */
/*                                              PWM_CLOCK_DIV_8/PWM_CLOCK_DIV_16                           */
/*                                                (The parameter takes effect when u8Frequency = 0)           */
/*                   u8PreScale                    Prescale (2 ~ 256)                                           */ 
/*                                                (The parameter takes effect when u8Frequency = 0)           */
/*                   u32Duty                        Pulse duty                                                   */                             
/*                                                (The parameter takes effect when u8Frequency = 0 or           */
/*                                                u8Timer = PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3)               */    
/*                                                                                                         */
/* Returns:                                                                                                */
/*               The actual frequency                                                                       */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to configure the frequency/pulse/mode/inverter function               */        
/*---------------------------------------------------------------------------------------------------------*/
FLOAT PWM_SetTimerClk(UINT8 u8Timer, PWM_TIME_DATA_T *sPt)
{
    FLOAT     fFrequency;
    UINT16    u16Duty;
    UINT32 u32PllKHz, u32SysKHz, u32CpuKHz, u32HclkKHz, u32ApbKHz;
    E_SYS_SRC_CLK eSrcClk;
    
    sysGetSystemClock(&eSrcClk,
                     &u32PllKHz,    
                    &u32SysKHz,
                    &u32CpuKHz,
                    &u32HclkKHz,
                    &u32ApbKHz);    
    
    u32ApbKHz = u32ApbKHz* 1000;
    
                        
    if(u8Timer & 0x10)    
    {
        if(u8Timer & 0x02)
            outp32(CCR1, (inp32(CCR1) & ~((1 << (((u8Timer & 0x01) << 4)))) | (CFLRD3 | CRLRD3 | CIIR3 | CFLRD2 | CRLRD2 |CIIR2)) | (sPt->bInverter?(1 << ((u8Timer & 0x01) << 4)):0));
        else
            outp32(CCR0, (inp32(CCR0) & ~((1 << (((u8Timer & 0x01) << 4)))) | (CFLRD1 | CRLRD1 | CIIR1 | CFLRD0 | CRLRD0 |CIIR0)) | (sPt->bInverter?(1 << ((u8Timer & 0x01) << 4)):0));
    }
    else
        outp32(PCR, (inp32(PCR) & ~(1 << ((((u8Timer & 0x07)) << 3) + 2) )) |(sPt->bInverter?(1 << (((u8Timer & 0x07) << 3) + 2)):0));    
    
    outp32(PCR, (inp32(PCR) & ~(1 <<(((u8Timer & 0x07) << 3) + 3))) | ( sPt->u8Mode? (1 <<(((u8Timer & 0x07) << 3) + 3)):0));
    
    if(sPt->fFrequency == 0)
    {    
        UINT8    u8Divider;
        
        if(u8Timer & 0x02)    
            outp32(PPR, (inp32(PPR) & ~CP1) | (((sPt->u8PreScale - 1) & 0xFF) << 8));
        else    
            outp32(PPR, (inp32(PPR) & ~CP0) | ((sPt->u8PreScale - 1) & 0xFF));
            
        u8Divider = 1;    
        switch(sPt->u8ClockSelector)
        {
            case PWM_CLOCK_DIV_1:
                u8Divider = 4;
                break;
            case PWM_CLOCK_DIV_2:
                u8Divider = 0;            
                break;            
            case PWM_CLOCK_DIV_4:
                u8Divider = 1;            
                break;            
            case PWM_CLOCK_DIV_8:
                u8Divider = 2;            
                break;            
            case PWM_CLOCK_DIV_16: 
                u8Divider = 3;            
                break;        
        }                
            
        outp32(PWM_CSR, (inp32(PWM_CSR) & ~(0x7 << ((u8Timer & 0x07)<<2) )) | ((u8Divider & 0x7) << ((u8Timer & 0x07)<<2)));    
        outp32(CNR0 + (u8Timer & 0x07) * 12, (sPt->u32Duty - 1));
        outp32(CMR0 + (u8Timer & 0x07) * 12, (sPt->u32Duty * sPt->u8HighPulseRatio / 100 - 1));            
                
        fFrequency = (FLOAT)u32ApbKHz / sPt->u8PreScale / sPt->u8ClockSelector / sPt->u32Duty;
    }
    else
    {
        UINT8    u8Divider;
        UINT16    u16PreScale;
        
        fFrequency = (FLOAT)  u32ApbKHz / sPt->fFrequency;        
        
        if(fFrequency > 0x10000000)
            return 0;
            
        u8Divider = 1;            
            
        if(fFrequency < 0x20000)
            u16PreScale = 2;    
        else
        {
            u16PreScale = fFrequency / 65536;    
                            
            if(fFrequency / u16PreScale > 65536)
                u16PreScale++;
            
            if(u16PreScale > 256)
            {
                UINT8 u8i;
                u16PreScale = 256;
                fFrequency = fFrequency / u16PreScale;
                
                u8Divider = fFrequency / 65536;                
                
                if(fFrequency / u8Divider > 65536)
                    u8Divider++;                
                
                u8i = 0;
                while(1)    
                {
                    if((1 << u8i++) > u8Divider)
                        break;
                }
                
                u8Divider = 1 << (u8i - 1);
                
                if(u8Divider > 16)
                    return 0;    
                    
                fFrequency = fFrequency * u16PreScale;                        
            }        
                    
        }
        u16Duty = (UINT16 )(fFrequency/u16PreScale/u8Divider);
        
        fFrequency = ((FLOAT) u32ApbKHz / u16PreScale / u8Divider) / u16Duty;    
                
        if(u8Timer & 0x02)    
            outp32(PPR, (inp32(PPR) & ~CP1) | (((u16PreScale - 1) & 0xFF) << 8));
        else    
            outp32(PPR, (inp32(PPR) & ~CP0) | ((u16PreScale - 1) & 0xFF));    
                
        sPt->u8ClockSelector = u8Divider;    
                
        switch(u8Divider)
        {
            case 1:
                u8Divider = 4;
                break;
            case 2:
                u8Divider = 0;            
                break;            
            case 4:
                u8Divider = 1;            
                break;            
            case 8:
                u8Divider = 2;            
                break;            
            case 16:
                u8Divider = 3;            
                break;        
        }        
        
        sPt->u8PreScale = u16PreScale;        
                            
        outp32(PWM_CSR, (inp32(PWM_CSR) & ~(0x7 << ((u8Timer & 0x07)<<2) )) | ((u8Divider & 0x7) << ((u8Timer & 0x07)<<2)));    
        
        if(u8Timer & 0x10)
        {            
            if(sPt->u32Duty != 0)
            {
                outp32(CNR0 + (u8Timer & 0x07) * 12, (sPt->u32Duty - 1));
                outp32(CMR0 + (u8Timer & 0x07) * 12, (sPt->u32Duty * sPt->u8HighPulseRatio / 100 - 1));            
            }
            else
            {
                outp32(CNR0 + (u8Timer & 0x07) * 12, (u16Duty - 1));
                outp32(CMR0 + (u8Timer & 0x07) * 12, (u16Duty * sPt->u8HighPulseRatio / 100 - 1));                
            }
        }
        else
        {    
            outp32(CNR0 + (u8Timer & 0x07) * 12, (u16Duty - 1));            
            outp32(CMR0 + (u8Timer & 0x07) * 12, (u16Duty * sPt->u8HighPulseRatio / 100 - 1));        

            
        }

        
    }
    
    return fFrequency;

}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: PWM_SetTimerIO                                                                                    */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               u8Timer         - [in]        PWM_TIMER0/PWM_TIMER1/PWM_TIMER2/PWM_TIMER3                      */
/*                                             PWM_CAP0/PWM_CAP1/PWM_CAP2/PWM_CAP3                                */
/*               bEnable        - [in]        Enable  (TRUE) / Diasble  (FALSE)                                */
/*                                                                                                         */
/* Returns:                                                                                                */
/*               None                                                                                       */
/*                                                                                                          */
/* Side effects:                                                                                           */
/* Description:                                                                                            */
/*               This function is used to enable/disable PWM timer / capture I/O function                   */        
/*---------------------------------------------------------------------------------------------------------*/
VOID PWM_SetTimerIO(UINT8 u8Timer, BOOL bEnable)
{
    
    if(bEnable)
    {
        if(u8Timer & 0x10)
            outp32(CAPENR, inp32(CAPENR)  | (1 << (u8Timer & 0x07)));            
        else
            outp32(POE, inp32(POE)  | (1 << (u8Timer & 0x07)));    
            
        outp32(REG_GPDFUN, (inp32(REG_GPDFUN) & ~(0x3 << ((u8Timer & 0x07) * 2))) | (0x2 << ((u8Timer & 0x07) * 2)));
            
    }
    else
    {
        if(u8Timer & 0x10)            
            outp32(CAPENR, inp32(CAPENR) & ~(1 << (u8Timer & 0x07)));            
        else
            outp32(POE, inp32(POE) & ~(1 << (u8Timer & 0x07)));
        outp32(REG_GPDFUN, (inp32(REG_GPDFUN) & ~(0x3 << ((u8Timer & 0x07) * 2))));    
    }
}



PWM.h  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/*---------------------------------------------------------------------------------------------------------*/
/*                                                                                                         */
/* Copyright (c) Nuvoton Technology Corp. All rights reserved.                                             */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/

#ifndef __DRVPWM_H__
#define __DRVPWM_H__


/*---------------------------------------------------------------------------------------------------------*/
/* Includes of system headers                                                                              */
/*---------------------------------------------------------------------------------------------------------*/
#include "wbio.h"
#include "w55fa93_reg.h"


#ifdef  __cplusplus
extern "C"
{
#endif

/*---------------------------------------------------------------------------------------------------------*/
/*  Define Version number                                                                                   */
/*---------------------------------------------------------------------------------------------------------*/
#define    PWM_MAJOR_NUM 1
#define    PWM_MINOR_NUM 00
#define    PWM_BUILD_NUM 1

/*---------------------------------------------------------------------------------------------------------*/
/*  PWM Timer                                                                                                */
/*---------------------------------------------------------------------------------------------------------*/
#define    PWM_TIMER0     0x0
#define    PWM_TIMER1     0x1
#define    PWM_TIMER2     0x2
#define    PWM_TIMER3     0x3

/*---------------------------------------------------------------------------------------------------------*/
/*  PWM Capture                                                                                               */
/*---------------------------------------------------------------------------------------------------------*/
#define    PWM_CAP0     0x10
#define    PWM_CAP1     0x11
#define    PWM_CAP2     0x12
#define    PWM_CAP3     0x13

/*---------------------------------------------------------------------------------------------------------*/
/*  PWM Capture Interrupt Enable/Disable Flag                                                              */
/*---------------------------------------------------------------------------------------------------------*/
#define PWM_CAP_RISING_INT    1
#define PWM_CAP_FALLING_INT    2
#define PWM_CAP_ALL_INT        0

/*---------------------------------------------------------------------------------------------------------*/
/*  PWM Capture Interrupt Flag                                                                               */
/*---------------------------------------------------------------------------------------------------------*/
#define PWM_CAP_RISING_FLAG    6
#define PWM_CAP_FALLING_FLAG    7

/*---------------------------------------------------------------------------------------------------------*/
/*  PWM Clcok Selector                                                                                       */
/*---------------------------------------------------------------------------------------------------------*/
#define PWM_CLOCK_DIV_1    1
#define PWM_CLOCK_DIV_2    2
#define PWM_CLOCK_DIV_4    4
#define PWM_CLOCK_DIV_8    8
#define PWM_CLOCK_DIV_16   16

/*---------------------------------------------------------------------------------------------------------*/
/*  PWM Mode Selector                                                                                       */
/*---------------------------------------------------------------------------------------------------------*/
#define PWM_TOGGLE_MODE        TRUE
#define PWM_ONE_SHOT_MODE    FALSE

/*---------------------------------------------------------------------------------------------------------*/
/* Define PWM Time Data Struct                                                                             */
/*---------------------------------------------------------------------------------------------------------*/
typedef struct
{
    UINT8    u8Mode;
    FLOAT    fFrequency;
    UINT8    u8HighPulseRatio;
    BOOL    bInverter;
    UINT8    u8ClockSelector;
    UINT8    u8PreScale;
    UINT32    u32Duty;
 
}PWM_TIME_DATA_T;

/*---------------------------------------------------------------------------------------------------------*/
/* Define PWM Call back funvtion Data Struct                                                               */
/*---------------------------------------------------------------------------------------------------------*/
typedef void (*PFN_PWM_CALLBACK)(void);

/*---------------------------------------------------------------------------------------------------------*/
/* Define PWM Call back function Data Struct                                                               */
/*---------------------------------------------------------------------------------------------------------*/
typedef struct
{
    PFN_PWM_CALLBACK pfnPWM0CallBack;    
    PFN_PWM_CALLBACK pfnCAP0CallBack;
   
    PFN_PWM_CALLBACK pfnPWM1CallBack;    
    PFN_PWM_CALLBACK pfnCAP1CallBack;
    
    PFN_PWM_CALLBACK pfnPWM2CallBack;    
    PFN_PWM_CALLBACK pfnCAP2CallBack;
    
    PFN_PWM_CALLBACK pfnPWM3CallBack;    
    PFN_PWM_CALLBACK pfnCAP3CallBack;        
   
}PWM_CALLBACK_T;


/*---------------------------------------------------------------------------------------------------------*/
/* Define PWM functions prototype                                                                          */
/*---------------------------------------------------------------------------------------------------------*/
VOID    PWM_Open(VOID);
VOID    PWM_Close(VOID);
VOID    PWM_SetTimerCounter(UINT8 u8Timer, UINT16 u16Counter);

VOID    PWM_EnableInt(UINT8    u8Timer, UINT8 u8Int);
VOID    PWM_ClearInt(UINT8 u8Timer);
VOID    PWM_DisableInt(UINT8 u8Timer, UINT8 u8Int);
VOID    PWM_ClearCaptureIntStatus(UINT8    u8Capture, UINT8 u8IntType);
VOID    PWM_EnableDeadZone(UINT8 u8Timer, UINT8 u8Length, BOOL bEnableDeadZone);
VOID    PWM_SetTimerIO(UINT8 u8Timer, BOOL bEnable);
VOID    PWM_Enable(UINT8 u8Timer, BOOL bEnable);
BOOL    PWM_IsTimerEnabled(UINT8 u8Timer);
BOOL    PWM_GetCaptureIntStatus(UINT8 u8Capture, UINT8 u8IntType);
BOOL    PWM_GetIntFlag(UINT8 u8Timer);
UINT16    PWM_GetRisingCounter(UINT8 u8Capture);
UINT16    PWM_GetFallingCounter(UINT8 u8Capture);
UINT32    PWM_GetTimerCounter(UINT8 u8Timer);

FLOAT    PWM_SetTimerClk(UINT8 u8Timer, PWM_TIME_DATA_T *sPt);
VOID PWM_InstallCallBack(UINT8 u8Timer,    PFN_PWM_CALLBACK pfncallback,    PFN_PWM_CALLBACK *pfnOldcallback);
#ifdef  __cplusplus
}
#endif

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值