/* ----------------------------------------------------------------------------
 *         SAM Software Package License
 * ----------------------------------------------------------------------------
 * Copyright (c) 2013, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

/**
  *  \file
  *
  *  Implementation of NFC functions.
  */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"

static SmcStatus smcStatus;
/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Counts and return the number of bits set to '1' in the given hsiao code.
 * \param code  Hsizo code.
 */
static unsigned char CountBitsInByte(unsigned char byte)
{
    unsigned char count = 0;
    while (byte > 0) {

        if (byte & 1) {

            count++;
        }
        byte >>= 1;
    }

    return count;
}

/**
 * \brief Counts and return the number of bits set to '1' in the given hsiao code.
 * \param code  Hsizo code.
 */
static unsigned char CountBitsInCode(unsigned char *code)
{
    return CountBitsInByte(code[0])
           + CountBitsInByte(code[1])
           + CountBitsInByte(code[2]);
}

/**
  * \brief Clear the corresponding interrupt flag.
 */
static void SMC_Clear_Status (void)
{
    smcStatus.BStatus = 0;
}

/**
 * \brief Check the STATUS and set the corresponding interrupt flag.
 */
static void SMC_Handler(void)
{
    uint32_t status;
    status = HSMC->HSMC_SR;
#if 0
    if ((status & HSMC_SR_SMCSTS) == HSMC_SR_SMCSTS) 
    /* NAND Flash Controller is enabled */
        smcStatus.bStatus.smcSts = 1; 
#endif
    if ((status & HSMC_SR_XFRDONE) == HSMC_SR_XFRDONE)
    /* When set to one, this flag indicates that the NFC has terminated the Data Transfer. This flag is reset after a status read
       operation. */
        smcStatus.bStatus.xfrDone = 1; 
    if ((status & HSMC_SR_CMDDONE) == HSMC_SR_CMDDONE)
    /* When set to one, this flag indicates that the NFC has terminated the Command. This flag is reset after a status read
       operation.*/
        smcStatus.bStatus.cmdDone = 1; 
    if ((status & (1<<24)) == (1<<24)) 
    /* If set to one, this flag indicates that an edge has been detected on the Ready/Busy Line x. Depending on the EDGE CTRL
       field located in the SMC_CFG register, only rising or falling edge is detected. This flag is reset after a status read operation. */
        smcStatus.bStatus.rbEdge = 1; 
    if ((status & HSMC_SR_ECCRDY) == HSMC_SR_ECCRDY)
    /* When set to one, this flag indicates that the Hamming ECC computation is completed. This flag is reset after a status read
       operation.*/
        smcStatus.bStatus.hammingReady = 1; 
}

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Sets NFC configuration.
 * \param cfg  NFC configuration.
 */
void SMC_NFC_Configure(uint32_t cfg)
{
    HSMC->HSMC_CFG = cfg;
}

/**
 * \brief Reset NFC controller.
 */
void SMC_NFC_Reset(void)
{
    /* Disable all the SMC NFC interrupts */
    HSMC->HSMC_IDR = 0xFFFFFFFF;
    HSMC->HSMC_CTRL = 0;
}

/**
 * \brief Enable NFC controller.
 */
void SMC_NFC_EnableNfc(void)
{
    HSMC->HSMC_CTRL |= HSMC_CTRL_NFCEN;
}

/**
 * \brief Enable NFC controller reads both main and spare area in read mode.
 */
void SMC_NFC_EnableSpareRead(void)
{
    HSMC->HSMC_CFG |= HSMC_CFG_RSPARE;
}

/**
 * \brief The NFC controller skips spare area in read mode.
 */
void SMC_NFC_DisableSpareRead(void)
{
    HSMC->HSMC_CFG &= (~HSMC_CFG_RSPARE);
}

/**
 * \brief Enables the NFC controller writes both main and spare area in write
 */
void SMC_NFC_EnableSpareWrite(void)
{
    HSMC->HSMC_CFG |= HSMC_CFG_WSPARE;
}

/**
 * \brief The NFC controller skips spare area in write mode.
 */
void SMC_NFC_DisableSpareWrite(void)
{
    HSMC->HSMC_CFG &= (~HSMC_CFG_WSPARE);
}

/**
 * \brief Check if spare area be read in read mode.
 *
 * \return Returns 1 if NFC controller reads both main and spare area in
 *         read mode, otherwise returns 0.
 */
uint8_t SMC_NFC_isSpareRead(void)
{
    return (((HSMC->HSMC_CFG) >> 9) & 0x1);
}

/**
 * \brief Check if spare area be written in write mode.
 *
 * \return Returns 1 if NFC controller writes both main and spare area in
 *         write mode, otherwise returns 0.
 */
uint8_t SMC_NFC_isSpareWrite(void)
{
    return (((HSMC->HSMC_CFG) >> 8) & 0x1);
}

/**
 * \brief Check if NFC transfer complete.
 * \return Returns 1 if NFC controller has terminated the data transmission,
 *         otherwise returns 0.
 */
uint8_t SMC_NFC_isTransferComplete(void)
{
    return ((HSMC->HSMC_SR & HSMC_SR_XFRDONE) == HSMC_SR_XFRDONE);
}

/**
 * \brief Check Ready/Busy line.
 *
 * \return Returns 1 if  edge has been detected on the Ready/Busy line,
 *         otherwise returns 0.
 */
uint8_t SMC_NFC_isReadyBusy(void)
{
    return ((HSMC->HSMC_SR &  (1<<24)) ==  (1<<24));
}

/**
 * \brief Check if NFC Controller is busy.
 *
 * \return Returns 1 if NFC Controller is activated and accesses the memory device,
 *         otherwise returns 0.
 */
uint8_t SMC_NFC_isNfcBusy(void)
{
    return ((HSMC->HSMC_SR & HSMC_SR_NFCBUSY) == HSMC_SR_NFCBUSY);
}

/**
 * \brief Get NFC Status.
 *
 * \return Returns the current status register of SMC NFC Status Register.
 *         This resets the internal value of the status register, so further
 *         read may yield different values.
 */
uint32_t SMC_NFC_GetStatus(void)
{
    return HSMC->HSMC_SR;
}

/*
 * HOST command functions
 */

/**
 * \brief Check if the host controller is busy.
 * \return Returns 1 if the host controller is busy, otherwise returns 0.
 */
static uint8_t SMC_NFC_isHostBusy(void)
{
    return (((*((volatile uint32_t *) (NFC_CMD_BASE_ADDR + NFCADDR_CMD_NFCCMD))) & 0x8000000) == 0x8000000);
}

/**
 * \brief Wait for NFC command has done.
*/
void SMC_NFC_Wait_CommandDone(void)
{
    while (smcStatus.bStatus.cmdDone == 0)
    {
        SMC_Handler();
    }
}

/**
 * \brief Wait for NFC Data Transfer Terminated.
*/
void SMC_NFC_Wait_XfrDone(void)
{
    while (smcStatus.bStatus.xfrDone == 0)
    {
        SMC_Handler();
    }
}

/**
 * \brief Wait for NFC Data Transfer Terminated.
*/
void SMC_NFC_Wait_HammingReady(void)
{
    while (smcStatus.bStatus.hammingReady ==0)
    {
        SMC_Handler();
    }
}

/**
 * \brief Wait for NFC Ready/Busy Line 3 Edge Detected.
*/
void SMC_NFC_Wait_RBbusy(void)
{
    while (smcStatus.bStatus.rbEdge == 0)
    {
        SMC_Handler();
    }
}

/**
 * \brief Uses the HOST nandflash conntroller to send a command to the NFC.
 * \param cmd  command to send.
 * \param addressCycle  address cycle when command access id decoded.
 * \param cycle0  address at first cycle.
 */
void SMC_NFC_SendCommand(uint32_t cmd, uint32_t addressCycle, uint32_t cycle0)
{
    volatile uint32_t *pCommandAddress;
    SMC_Clear_Status();
    /* Wait until host controller is not busy. */
    while(SMC_NFC_isHostBusy());
    /* Send the command plus the ADDR_CYCLE */
    pCommandAddress = (volatile uint32_t *) (cmd + NFC_CMD_BASE_ADDR);
    HSMC->HSMC_ADDR = cycle0;
    *pCommandAddress = addressCycle;
    SMC_NFC_Wait_CommandDone();
}

/* ECC function */

/**
 * \brief Get 24-bit ECC code for 8-bit data path NAND flash.
 * 24-bit ECC is generated in order to perform one bit correction
 * for 512 byte in page 512/1024/2048/4096 for 8-bit words
 *
 * \param size  Data size in bytes.
 * \param code  Codes buffer.
 */
static void _smc_ecc_GetW9BitPer512Ecc(uint32_t pageDataSize, uint8_t *code)
{
    uint8_t i;
    uint8_t numEcc;
    uint32_t eccParity;
    uint32_t ecc[16];

    SMC_ECC_GetValue(ecc);
    numEcc = pageDataSize / 512;
    /*  P2048' P1024' P512' P256' P128'   P64'  P32' P16'  ---  3rd. Ecc Byte to store */
    /*  P8'    P4'    P2'   P1'   P2048   P1024 P512 P256  ---  2nd. Ecc Byte to store */
    /*  P128   P64    P32   P16   P8      P4    P2   P1    ---  1st. Ecc Byte to store */
    for (i = 0; i < numEcc; i++) {
        /* Get Parity and NParity value. */
        eccParity = ecc[i];
        eccParity = ~eccParity;
        code[i * 3] = eccParity & 0xff;
        code[i * 3 + 1] = (eccParity >> 8) & 0xff;
        code[i * 3 + 2] = (eccParity >> 16) & 0xff;
    }
}

/**
 * \brief Get 24-bit ECC code for 8-bit data path NAND flash.
 * 24-bit ECC is generated in order to perform one bit correction
 * for 256 byte in page 512/1024/2048/4096 for 8-bit words
 *
 * \param size  Data size in bytes.
 * \param code  Codes buffer.
 */
static void _smc_ecc_GetW8BitPer256Ecc(uint32_t pageDataSize, uint8_t *code)
{
    uint8_t i;
    uint8_t numEcc;
    uint32_t eccParity;
    uint32_t ecc[16];

    SMC_ECC_GetValue(ecc);
    numEcc = pageDataSize / 256;

    /*  P2048' P1024' P512' P256' P128'   P64'  P32' P16'  ---  3rd. Ecc Byte to store */
    /*  P8'    P4'    P2'   P1'   P2048   P1024 P512 P256  ---  2nd. Ecc Byte to store */
    /*  P128   P64    P32   P16   P8      P4    P2   P1    ---  1st. Ecc Byte to store */
    for (i = 0; i < numEcc; i++) {
        /* Get Parity and NParity value. */
        eccParity = ecc[i];
        eccParity = ~eccParity; 
        TRACE_DEBUG("ecc Parity%u is 0x%08u \n\r", (unsigned int)i, (unsigned int)eccParity);
        code[i * 3] = eccParity & 0xff;
        code[i * 3 + 1] = (eccParity >> 8) & 0xff;
        code[i * 3 + 2] = (eccParity >> 16) & 0xff;
    }
}

/**
 * \breif Get 32-bit ECC code for 16-bit data path NAND flash.
 * 32-bit ECC is generated in order to perform one bit correction
 * for a page in page 512/1024/2048/4096 for 16-bit words
 *
 * \param size  Data size in bytes.
 * \param code  Codes buffer.
 */
static void _smc_ecc_GetW12BitPerPageEcc(uint32_t pageDataSize, uint8_t *code)
{
    uint32_t eccParity;
    uint32_t eccNparity;
    uint32_t ecc[16];

    pageDataSize = pageDataSize; /* stop warning */
    /* Get Parity value. */
    SMC_ECC_GetValue(ecc);

    /*  ----   P16384'P8192'P4096'P2048'  P1024'P512'P256' ---  4th. Ecc Byte to store */
    /*  P128'  P64'   P32'  P16'  P8'     P4'   P2'  P1'   ---  3rd. Ecc Byte to store */
    /*  ----   P16384 P8192 P4096 P2048   P1024 P512 P256  ---  2nd. Ecc Byte to store */
    /*  P128   P64    P32   P16   P8      P4    P2   P1    ---  1st. Ecc Byte to store */

    /* Invert codes (linux compatibility) */
    eccParity = ecc[0];
    eccNparity = ecc[1];
    code[0] = eccParity & 0xff;
    code[1] = (eccParity >> 8 )& 0xff;
    code[2] = eccNparity & 0xff;
    code[3] = (eccNparity >> 8 )& 0xff;
}


/**
 * \brief Configures ECC mode.
 * \param type  Type of correction.
 * \param pageSize  Page size of NAND flash device.
 */
void SMC_ECC_Configure(uint32_t type, uint32_t pageSize)
{
    /* Software Reset ECC. */
    HSMC->HSMC_ECC_CTRL = (0x1 <<  1) ;
    HSMC->HSMC_ECC_MD = type | pageSize;
}

/**
 * \brief Get ECC correction type.
 *
 * \return Returns type of ECC correction setting.
 */
uint32_t SMC_ECC_GetCorrectoinType(void)
{
    return ((HSMC->HSMC_ECC_MD)& HSMC_ECC_MD_TYPCORREC_Msk);
}

/**
 * \brief Get ECC status.
 * \param eccNumber  ecc parity number from 0 to 15.
 *
 * \return Returns ECC status by giving ecc number.
 */
uint8_t SMC_ECC_GetStatus(uint8_t eccNumber)
{
    uint32_t status;

    if (eccNumber < 8){
        status = HSMC->HSMC_ECC_SR1;
    }
    else {
        status = HSMC->HSMC_ECC_SR2;
        eccNumber -=8;
    }
    return ((status >> (eccNumber * 4)) & 0x07);
}

/**
 * \brief Get all ECC parity and Nparity value.
 */
void SMC_ECC_GetValue(uint32_t *ecc)
{
   ecc[0] = HSMC->HSMC_ECC_PR0;
   ecc[1] = HSMC->HSMC_ECC_PR1;
   ecc[2] = HSMC->HSMC_ECC_PR2;
   ecc[3] = HSMC->HSMC_ECC_PR3;
   ecc[4] = HSMC->HSMC_ECC_PR4;
   ecc[5] = HSMC->HSMC_ECC_PR5;
   ecc[6] = HSMC->HSMC_ECC_PR6;
   ecc[7] = HSMC->HSMC_ECC_PR7;
   ecc[8] = HSMC->HSMC_ECC_PR8;
   ecc[9] = HSMC->HSMC_ECC_PR9;
   ecc[10] = HSMC->HSMC_ECC_PR10;
   ecc[11] = HSMC->HSMC_ECC_PR11;
   ecc[12] = HSMC->HSMC_ECC_PR12;
   ecc[13] = HSMC->HSMC_ECC_PR13;
   ecc[14] = HSMC->HSMC_ECC_PR14;
   ecc[15] = HSMC->HSMC_ECC_PR15;
}

/**
 * \brief verifies 4-bytes hsiao codes for a data block whose size is a page Size
 * word. Page words block is verified between the given HSIAO code 
 * generated by hardware and original HSIAO codes store has been previously stored.
 * Returns 0 if the data is correct, Hsiao_ERROR_SINGLEBIT if one or more
 * block(s) have had a single bit corrected, or either Hsiao_ERROR_ECC
 * or Hsiao_ERROR_MULTIPLEBITS.
 * \param data  Data buffer to verify.
 * \param originalCode  Original codes.
 * \param verifyCode  codes to be verified.
 */
static uint8_t _smc_ecc_VerifyW12BitPerPageEcc(
    uint8_t *data,
    const uint8_t *originalCode,
    const uint8_t *verifyCode)
{
    uint8_t correctionCode[4];
    uint8_t bitCount;
    // Xor both codes together
    correctionCode[0] = verifyCode[0] ^ originalCode[0];
    correctionCode[1] = verifyCode[1] ^ originalCode[1];
    correctionCode[2] = verifyCode[2] ^ originalCode[2];
    correctionCode[3] = verifyCode[3] ^ originalCode[3];
    TRACE_DEBUG("Correction code = %02X %02X %02X %02X\n\r",
                correctionCode[0], correctionCode[1], correctionCode[2], correctionCode[3]);
    /* If all bytes are 0, there is no error */
    if ((correctionCode[0] == 0)
        && (correctionCode[1] == 0)
        && (correctionCode[2] == 0)
        && (correctionCode[3] == 0)) {

        return 0;
    }
    /* If there is a single bit error, there are 15 bits set to 1 */
    bitCount = CountBitsInByte(correctionCode[0]) + 
               CountBitsInByte(correctionCode[1]) + 
               CountBitsInByte(correctionCode[2]) + 
               CountBitsInByte(correctionCode[3]);
    if (bitCount == 15) {
        /* Get byte and bit indexes */
        uint16_t byte = (correctionCode[0] & 0xf0) >> 4;
        byte |= (correctionCode[1] & 0xff) << 4;
        uint8_t bit = correctionCode[0] & 0x0f;
        /* Correct bit */
        printf("Correcting byte #%d at bit %d\n\r", (unsigned int)byte,(unsigned int) bit);
        data[byte] ^= (1 << bit);

        return Hsiao_ERROR_SINGLEBIT;
    }
   
    /* Check if ECC has been corrupted */
    if (bitCount == 1) {
        return Hsiao_ERROR_ECC;
    }
    /* Otherwise, this is a multi-bit error */
    else {
        return Hsiao_ERROR_MULTIPLEBITS;
    }
}

/**
 * \brief erifies 3-bytes hsiao codes for a data block whose size is a page Size
 * word. Page words block is verified between the given HSIAO code 
 * generated by hardware and original HSIAO codes store has been previously stored.
 * Returns 0 if the data is correct, Hsiao_ERROR_SINGLEBIT if one or more
 * block(s) have had a single bit corrected, or either Hsiao_ERROR_ECC
 * or Hsiao_ERROR_MULTIPLEBITS.
 * \param data  Data buffer to verify.
 * \param originalCode  Original codes.
 * \param verifyCode  codes to be verified.
 */
static uint8_t _smc_ecc_VerifyW8BitPer256Ecc(
    uint8_t *data,
    uint32_t size,
    const uint8_t *originalCode,
    const uint8_t *verifyCode)
{
    uint8_t correctionCode[3];
    uint32_t position = 0;
    uint8_t byte;
    uint8_t bit;
    uint8_t error = 0;
    
    TRACE_DEBUG("_smc_ecc_VerifyW8BitPer256Ecc()\n\r");
    while (position < size) {
        /* Xor both codes together */
        correctionCode[0] = verifyCode[0] ^ originalCode[0];
        correctionCode[1] = verifyCode[1] ^ originalCode[1];
        correctionCode[2] = verifyCode[2] ^ originalCode[2];
        TRACE_DEBUG("Correction code = %02X %02X %02X\n\r",
            correctionCode[0], correctionCode[1], correctionCode[2]);
            
        /* If all bytes are 0, there is no error */
        if ( correctionCode[0] || correctionCode[1] || correctionCode[2]) {
            /* If there is a single bit error, there are 11 bits set to 1 */
            if (CountBitsInCode(correctionCode) == 11) {
                /* Get byte and bit indexes */
                byte = (correctionCode[0] & 0xf8) >> 3;
                byte |= (correctionCode[1] & 0x07) << 5;
                bit = correctionCode[0] & 0x07;
                /* Correct bit */
                printf("Correcting byte #%u at bit %u\n\r", (unsigned int)(position + byte),  (unsigned int)bit);
                data[byte] ^= (1 << bit);
                error = Hsiao_ERROR_SINGLEBIT;
            }
            /* Check if ECC has been corrupted */
            else if (CountBitsInCode(correctionCode) == 1) {
                return Hsiao_ERROR_ECC;
            }
            else {
                /* Otherwise, this is a multi-bit error */
                return Hsiao_ERROR_MULTIPLEBITS;
            }
        }
        data += 256;
        originalCode += 3;
        verifyCode += 3;
        position += 256;
    }
    return error;
}

/**
 * \brief 3-bytes hsiao codes for a data block whose size is multiple of
 * 512 bytes. Each 512-bytes block is verified between the given HSIAO code 
 * generated by hardware and original HSIAO codes store has been previously stored.
 * Returns 0 if the data is correct, Hsiao_ERROR_SINGLEBIT if one or more
 * block(s) have had a single bit corrected, or either Hsiao_ERROR_ECC
 * or Hsiao_ERROR_MULTIPLEBITS.
 * \param data  Data buffer to verify.
 * \param originalCode  Original codes.
 * \param verifyCode  codes to be verified.
 */
static uint8_t _smc_ecc_VerifyW9BitPer512Ecc(
    uint8_t *data,
    uint32_t size,
    const uint8_t *originalCode,
    const uint8_t *verifyCode)
{
    uint8_t correctionCode[3];
    uint32_t position = 0;
    uint16_t byte;
    uint8_t bit;
    uint8_t error = 0;
    
    TRACE_DEBUG("_smc_ecc_VerifyW9BitPer512Ecc()\n\r");
    while (position < size) {
        /* Xor both codes together */
        correctionCode[0] = verifyCode[0] ^ originalCode[0];
        correctionCode[1] = verifyCode[1] ^ originalCode[1];
        correctionCode[2] = verifyCode[2] ^ originalCode[2];
        TRACE_DEBUG("Correction code = %02X %02X %02X\n\r",
            correctionCode[0], correctionCode[1], correctionCode[2]);
            
        /* If all bytes are 0, there is no error */
        if ( correctionCode[0] || correctionCode[1] || correctionCode[2]) {
            // If there is a single bit error, there are 11 bits set to 1
            if (CountBitsInCode(correctionCode) == 12) {
                /* Get byte and bit indexes */
                byte = (correctionCode[0] & 0xf8) >> 3;
                byte |= (correctionCode[1] & 0x0f) << 5;
                bit = correctionCode[0] & 0x07;
                /* Correct bit */
                printf("Correcting byte #%u at bit %u\n\r",  (unsigned int)(position + byte),  (unsigned int)bit);
                data[byte] ^= (1 << bit);
                error = Hsiao_ERROR_SINGLEBIT;
            }
            /* Check if ECC has been corrupted */
            else if (CountBitsInCode(correctionCode) == 1) {
                return Hsiao_ERROR_ECC;
            }
            else {
                /* Otherwise, this is a multi-bit error */
                return Hsiao_ERROR_MULTIPLEBITS;
            }
        }
        data += 512;
        originalCode += 3;
        verifyCode += 3;
        position += 512;
    }
    return error;
}

/**
 * Get ECC code for 8bit/16-bit data path NAND flash by giving data path.
 * 24-bit or 32-bit ECC is generated in order to perform one bit correction
 * for a page in page 512/1024/2048/4096.
 *
 * \param size  Data size in bytes.
 * \param code  Codes buffer.
 * \param busWidth 8bit/16bit data path.
 */
void SMC_ECC_GetEccParity(uint32_t pageDataSize, uint8_t *code, uint8_t busWidth)
{
    uint8_t correctionType;

    correctionType = SMC_ECC_GetCorrectoinType();
    /* For 16-bit data path */
    if (busWidth == 16 && correctionType == HSMC_ECC_MD_TYPCORREC_CPAGE )
        _smc_ecc_GetW12BitPerPageEcc(pageDataSize, code);
    /* For 8-bit data path */
    else {
        switch (correctionType){
            case HSMC_ECC_MD_TYPCORREC_CPAGE:
                _smc_ecc_GetW12BitPerPageEcc(pageDataSize, code);
                break;
            case HSMC_ECC_MD_TYPCORREC_C256B:
                 _smc_ecc_GetW8BitPer256Ecc(pageDataSize, code);
                 break;
            case HSMC_ECC_MD_TYPCORREC_C512B:
                _smc_ecc_GetW9BitPer512Ecc(pageDataSize, code);
                break;
        }
    }
}


/**
 *  Verifies hsiao codes for a data block. The block is verified between the given 
 *  HSIAO code generated by hardware and original HSIAO codes store has been 
 *  previously stored.
 *  Returns 0 if the data is correct, Hsiao_ERROR_SINGLEBIT if one or more
 *  block(s) have had a single bit corrected, or either Hsiao_ERROR_ECC
 *  or Hsiao_ERROR_MULTIPLEBITS.
 *  \param data  Data buffer to verify.
 *  \param size  Size of the data in words.
 *  \param originalCode  Original codes.
 *  \param verifyCode  codes to be verified.
 *  \param dataPath 8bit/16bit data path.
*/
uint8_t SMC_ECC_VerifyHsiao(
    uint8_t *data,
    uint32_t size,
    const uint8_t *originalCode,
    const uint8_t *verifyCode,
    uint8_t busWidth)
{
    uint8_t correctionType;
    uint8_t error = 0;
    correctionType = SMC_ECC_GetCorrectoinType();
    /* For 16-bit data path */
    if (busWidth == 16 && (correctionType == HSMC_ECC_MD_TYPCORREC_CPAGE) ) {
        error = _smc_ecc_VerifyW12BitPerPageEcc((uint8_t*)data, originalCode, verifyCode);
    }
    /* For 8-bit data path */
    else {
        switch (correctionType){
            case HSMC_ECC_MD_TYPCORREC_CPAGE:
                error = _smc_ecc_VerifyW12BitPerPageEcc(data, originalCode, verifyCode);

                break;
            case HSMC_ECC_MD_TYPCORREC_C256B:
                 error = _smc_ecc_VerifyW8BitPer256Ecc(data, size, originalCode, verifyCode);
                 break;
            case HSMC_ECC_MD_TYPCORREC_C512B:
                error = _smc_ecc_VerifyW9BitPer512Ecc(data, size, originalCode, verifyCode);
                break;
        }
    }
    return error;
}
