/****************************************************************************//**
 * @file     sdcard.c
 * @version  V1.00
 * @brief    NUC230_240 series SD Card source file
 *
 * @note
 * Copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include "SDCARD.h"
#include "NUC230_240.h"


/** @addtogroup NUC230_240_Library NUC230_240 Library
  @{
*/

/** @addtogroup NUC230_240_SDCARD SDCARD Library
  @{
*/



static SPI_T    *g_pSPI = SPI1;




/// @cond HIDDEN_SYMBOLS
int8_t Is_Initialized = 0, SDtype = 0;
uint32_t LogicSector = 0;

// Command table for MMC.  This table contains all commands available in SPI
// mode;  Format of command entries is described above in command structure
// definition;
COMMAND __I command_list[] =
{
    { 0, NO , 0x95, CMD, R1 , NO }, // CMD0;  GO_IDLE_STATE: reset card;
    { 1, NO , 0xFF, CMD, R1 , NO }, // CMD1;  SEND_OP_COND: initialize card;
    { 8, YES, 0xFF, CMD, R7 , NO }, // CMD8;  SEND_IF_COND
    { 9, NO , 0xFF, RD , R1 , NO }, // CMD9;  SEND_CSD: get card specific data;
    {10, NO , 0xFF, RD , R1 , NO }, // CMD10; SEND_CID: get card identifier;
    {12, NO , 0xFF, CMD, R1b, NO }, // CMD12; STOP_TRANSMISSION: end read;
    {13, NO , 0xFF, CMD, R2 , NO }, // CMD13; SEND_STATUS: read card status;
    {16, YES, 0xFF, CMD, R1 , NO }, // CMD16; SET_BLOCKLEN: set block size;
    {17, YES, 0xFF, RDB , R1 , NO }, // CMD17; READ_SINGLE_BLOCK: read 1 block;
    {18, YES, 0xFF, RD , R1 , YES}, // CMD18; READ_MULTIPLE_BLOCK: read > 1;
    {23, NO , 0xFF, CMD, R1 , NO }, // CMD23; SET_BLOCK_COUNT
    {24, YES, 0xFF, WR , R1 , NO }, // CMD24; WRITE_BLOCK: write 1 block;
    {25, YES, 0xFF, WR , R1 , YES}, // CMD25; WRITE_MULTIPLE_BLOCK: write > 1;
    {27, NO , 0xFF, CMD, R1 , NO }, // CMD27; PROGRAM_CSD: program CSD;
    {28, YES, 0xFF, CMD, R1b, NO }, // CMD28; SET_WRITE_PROT: set wp for group;
    {29, YES, 0xFF, CMD, R1b, NO }, // CMD29; CLR_WRITE_PROT: clear group wp;
    {30, YES, 0xFF, CMD, R1 , NO }, // CMD30; SEND_WRITE_PROT: check wp status;
    {32, YES, 0xFF, CMD, R1 , NO }, // CMD32; TAG_SECTOR_START: tag 1st erase;
    {33, YES, 0xFF, CMD, R1 , NO }, // CMD33; TAG_SECTOR_END: tag end(single);
    {34, YES, 0xFF, CMD, R1 , NO }, // CMD34; UNTAG_SECTOR: deselect for erase;
    {35, YES, 0xFF, CMD, R1 , NO }, // CMD35; TAG_ERASE_GROUP_START;
    {36, YES, 0xFF, CMD, R1 , NO }, // CMD36; TAG_ERASE_GROUP_END;
    {37, YES, 0xFF, CMD, R1 , NO }, // CMD37; UNTAG_ERASE_GROUP;
    {38, YES, 0xFF, CMD, R1b, NO }, // CMD38; ERASE: erase all tagged sectors;
    {42, YES, 0xFF, CMD, R1 , NO }, // CMD42; LOCK_UNLOCK;
    {55, NO , 0xFF, CMD, R1 , NO }, // CMD55; APP_CMD
    {58, NO , 0xFF, CMD, R3 , NO }, // CMD58; READ_OCR: read OCR register;
    {59, YES, 0xFF, CMD, R1 , NO }, // CMD59; CRC_ON_OFF: toggles CRC checking;
    {0x80 + 13, NO , 0xFF, CMD, R2 , NO }, // ACMD13; SD_SEND_STATUS: read card status;
    {0x80 + 23, YES, 0xFF, CMD, R1 , NO }, // ACMD23;SD_SET_WR_BLK_ERASE_COUNT
    {0x80 + 41, YES, 0xFF, CMD, R1 , NO } // ACMD41; SD_SEND_OP_COND: initialize card;
};
/// @endcond HIDDEN_SYMBOLS
/** @addtogroup NUC230_240_SDCARD_EXPORTED_FUNCTIONS SDCARD Library Exported Functions
  @{
*/

/**
  * @brief Delay function.
  * @param[in] count loop count.
  * @return none
  */
void SD_Delay(uint32_t count)
{
    uint32_t volatile loop;
    for(loop = 0; loop < count; loop++);
}
/*---------------------------------------------------------------------------------------------------------*/
/* SD CARD Protocol                                                                                        */
/*---------------------------------------------------------------------------------------------------------*/
/*
    Transfer Length:48bit
    BIT POSITION       WIDTH(BITS)          VAULE
            [47]                1               0:Start bit
            [46]                1               1:Transmit   0:Receive
         [45:40]                6               CMD8:001000
          [39:8]               32               Reserved
           [7:1]                7               Reserved
             [0]                1               1:End bit

*/

/**
  * @brief This function is used to generate CRC value
  * @param[in] u32Data Input Data
  * @param[in] u32GenPoly CRC7:0x1200, CRC16:0x1021
  * @param[in] u32Accum CRC value
  * @return CRC value
  */
static uint32_t GenerateCRC(uint32_t u32Data, uint32_t u32GenPoly, uint32_t u32Accum)
{
    volatile uint8_t i;

    u32Data <<= 8;
    for(i = 8; i > 0; i--)
    {
        if((u32Data ^ u32Accum) & 0x8000)
            u32Accum = (u32Accum << 1) ^ u32GenPoly;
        else
            u32Accum <<= 1;
        u32Data <<= 1;
    }
    return u32Accum;
}

/**
  * @brief This function is used to send data though SPI to general clock for SDCARD operation
  * @param[in] u32Data Data to send
  * @return none
  */
static uint32_t SingleWrite(uint32_t u32Data)
{
    while(SPI_IS_BUSY(g_pSPI));
    SPI_WRITE_TX0(g_pSPI, u32Data);
    SPI_TRIGGER(g_pSPI);
    while(SPI_IS_BUSY(g_pSPI));
    return SPI_READ_RX0(g_pSPI);
}

/**
  * @brief This function is used to Send SDCARD CMD and Receive Response
  * @param[in] nCmd Set command register
  * @param[in] nArg Set command argument
  * @param[out] *pchar Get register and data
  * @param[out] *response Get response
  * @retval TRUE get response
  * @retval FALSE 1.SD Card busy, 2.Card moved, 3.Timeout
  */
uint32_t MMC_Command_Exec(uint8_t nCmd, uint32_t nArg, uint8_t *pchar, uint32_t *response)
{
    uint8_t loopguard;
    COMMAND current_command;                // Local space for the command table
    UINT32  long_arg;                       // Local space for argument
    static uint32_t current_blklen = 512;
    uint32_t old_blklen = 512;
    int32_t counter = 0;                    // Byte counter for multi-byte fields;
    UINT16 card_response;                       // Variable for storing card response;
    uint8_t data_resp;                      // Variable for storing data response;
    UINT16 dummy_CRC;                       // Dummy variable for storing CRC field;

    card_response.i = 0;

    current_command = command_list[nCmd];// Retrieve desired command table entry
    // from code space;
    if(current_command.command_byte & 0x80)
    {
        // Detect ACMD
        if(MMC_Command_Exec(APP_CMD, EMPTY, EMPTY, response) == FALSE) //Send APP_CMD
            return FALSE;
    }

    PE7 = 0;//SPI_SET_SS_LOW(g_pSPI);// CS = 0

    SingleWrite(0xFF);
    SingleWrite((current_command.command_byte | 0x40) & 0x7f);
    DBG_PRINTF("CMD:%d,", current_command.command_byte & 0x7f);

    SD_Delay(200);

    long_arg.l = nArg;                  // Make argument byte addressable;
    // If current command changes block
    // length, update block length variable
    // to keep track;
    // Command byte = 16 means that a set
    // block length command is taking place
    // and block length variable must be
    // set;
    if(current_command.command_byte == 16)
    {
        current_blklen = nArg;
    }
    // Command byte = 9 or 10 means that a
    // 16-byte register value is being read
    // from the card, block length must be
    // set to 16 bytes, and restored at the
    // end of the transfer;
    if((current_command.command_byte == 9) || (current_command.command_byte == 10))
    {
        old_blklen = current_blklen;     // Command is a GET_CSD or GET_CID,
        current_blklen = 16;             // set block length to 16-bytes;
    }
    // If an argument is required, transmit
    // one, otherwise transmit 4 bytes of
    // 0x00;
    if(current_command.arg_required == YES)
    {
        dummy_CRC.i = GenerateCRC((current_command.command_byte | 0x40), 0x1200, 0);
        for(counter = 3; counter >= 0; counter--)
        {
            SingleWrite(long_arg.b[counter]);
            dummy_CRC.i = GenerateCRC(long_arg.b[counter], 0x1200, dummy_CRC.i);

        }
        dummy_CRC.i = (dummy_CRC.i >> 8) | 0x01;
        SingleWrite(dummy_CRC.b[0]);
    }
    else
    {
        counter = 0;
        while(counter <= 3)
        {
            SingleWrite(0x00);
            counter++;
        }
        SingleWrite(/*current_command.CRC*/current_command.CRC_SD);
    }

    // The command table entry will indicate
    // what type of response to expect for
    // a given command;  The following
    // conditional handles the MMC response;
    if(current_command.response == R1)
    {
        // Read the R1 response from the card;
        loopguard = 0;
        do
        {
            card_response.b[0] = SingleWrite(0xFF);
            if(!++loopguard) break;
        }
        while((card_response.b[0] & BUSY_BIT));
        DBG_PRINTF("R1:0x%x, counter:%d\n", card_response.b[0], loopguard);

        if(!loopguard)
        {
            BACK_FROM_ERROR;
        }

//      while((SingleWrite(0xFF)&0xFF) == 0x00);
        *response = card_response.b[0];
    }
    else if(current_command.response == R1b)   // Read the R1b response;
    {
        loopguard = 0;
        do
        {
            card_response.b[0] =  SingleWrite(0xFF);
            if(!++loopguard) break;
        }
        while((card_response.b[0] & BUSY_BIT));
        while((SingleWrite(0xFF) & 0xFF) == 0x00);
    }
    else if(current_command.response == R2)
    {
        loopguard = 0;
        do
        {
            card_response.b[0] = SingleWrite(0xFF);
            if(!++loopguard) break;
        }
        while((card_response.b[0] & BUSY_BIT));
        card_response.b[1] = SingleWrite(0xFF);
        DBG_PRINTF("R2:0x%x, counter:%d\n", card_response.i, loopguard);
        if(!loopguard)
        {
            BACK_FROM_ERROR;
        }
        *response = card_response.i;
    }
    else if(current_command.response == R3)
    {
        // Read R3 response;
        loopguard = 0;
        do
        {
            card_response.b[0] = SingleWrite(0xFF);
            if(!++loopguard) break;
        }
        while((card_response.b[0] & BUSY_BIT));
        DBG_PRINTF("R3:0x%x, counter:%d\n", card_response.b[0], loopguard);
        if(!loopguard)
        {
            BACK_FROM_ERROR;
        }
        counter = 0;
        while(counter <= 3)              // Read next three bytes and store them
        {
            // in local memory;  These bytes make up
            counter++;                    // the Operating Conditions Register
            *pchar++ = SingleWrite(0xFF);
        }
        *response = card_response.b[0];
    }
    else
    {
        // Read R7 response;
        loopguard = 0;
        do
        {
            card_response.b[0] = SingleWrite(0xFF);
            if(!++loopguard) break;
        }
        while((card_response.b[0] & BUSY_BIT));
        DBG_PRINTF("R7:0x%x, counter:%d\n", card_response.b[0], loopguard);
        if(!loopguard)
        {
            BACK_FROM_ERROR;
        }
        counter = 0;
        while(counter <= 3)              // Read next three bytes and store them
        {
            // in local memory;  These bytes make up
            counter++;                    // the Operating Conditions Register
            *pchar++ = SingleWrite(0xFF);
        }
        *response = card_response.b[0];
    }


    switch(current_command.trans_type)   // This conditional handles all data
    {
        // operations;  The command entry
        // determines what type, if any, data
        // operations need to occur;
        case RDB:                         // Read data from the MMC;
            loopguard = 0;

            while((SingleWrite(0xFF) & 0xFF) != START_SBR)
            {
                if(!++loopguard)
                {
                    BACK_FROM_ERROR;
                }
                SD_Delay(119);
            }
            counter = 0;                    // Reset byte counter;
            // Read <current_blklen> bytes;
            if(pchar)
            {
                for(counter = 0; counter < current_blklen; counter++)
                {
                    SPI_WRITE_TX0(g_pSPI, 0xFF);
                    SPI_TRIGGER(g_pSPI);
                    while(SPI_IS_BUSY(g_pSPI));
                    *(pchar + counter) = SPI_READ_RX0(g_pSPI);
                }
            }
            else
            {
                for(; counter < current_blklen; counter++)
                {
                    SPI_WRITE_TX0(g_pSPI, 0xFF);
                    SPI_TRIGGER(g_pSPI);
                    while(SPI_IS_BUSY(g_pSPI));
                }
            }
            dummy_CRC.b[1] = SingleWrite(0xFF); // After all data is read, read the two
            dummy_CRC.b[0] = SingleWrite(0xFF); // CRC bytes;  These bytes are not used
            // in this mode, but the place holders
            // must be read anyway;
            break;
        case RD:                         // Read data from the MMC;
            loopguard = 0;

            while((SingleWrite(0xFF) & 0xFF) != START_SBR)
            {
                if(!++loopguard)
                {
                    BACK_FROM_ERROR;
                }
            }
            counter = 0;                    // Reset byte counter;
            // Read <current_blklen> bytes;
            if(pchar)
            {
                for(counter = 0; counter < current_blklen; counter++)
                {
                    SPI_WRITE_TX0(g_pSPI, 0xFF);
                    SPI_TRIGGER(g_pSPI);
                    while(SPI_IS_BUSY(g_pSPI));
                    *(pchar + counter) = SPI_READ_RX0(g_pSPI);
                }
            }
            else
            {
                for(counter = 0; counter < current_blklen; counter++)
                {
                    SPI_WRITE_TX0(g_pSPI, 0xFF);
                    SPI_TRIGGER(g_pSPI);
                    while(SPI_IS_BUSY(g_pSPI));
                }
            }
            dummy_CRC.b[1] = SingleWrite(0xFF); // After all data is read, read the two
            dummy_CRC.b[0] = SingleWrite(0xFF); // CRC bytes;  These bytes are not used
            // in this mode, but the place holders
            // must be read anyway;
            break;

        case WR:
            SingleWrite(0xFF);
            SingleWrite(START_SBW);

            for(counter = 0; counter < current_blklen; counter++)
            {
                SPI_WRITE_TX0(g_pSPI, *(pchar + counter));
                SPI_TRIGGER(g_pSPI);
                dummy_CRC.i = GenerateCRC(*(pchar + counter), 0x1021, dummy_CRC.i);
                while(SPI_IS_BUSY(g_pSPI));
            }
            SingleWrite(dummy_CRC.b[1]);
            SingleWrite(dummy_CRC.b[0]);

            loopguard = 0;
            do                            // Read Data Response from card;
            {
                data_resp = SingleWrite(0xFF);
                if(!++loopguard) break;
            }
            while((data_resp & DATA_RESP_MASK) != 0x01);     // When bit 0 of the MMC response
            // is clear, a valid data response
            // has been received;

            if(!loopguard)
            {
                BACK_FROM_ERROR;
            }

            while((SingleWrite(0xFF) & 0xFF) != 0xFF); //Wait for Busy
            SingleWrite(0xFF);
            break;
        default:
            break;
    }

    PE7 = 1;//SPI_SET_SS_HIGH(g_pSPI);// CS = 1

    if((current_command.command_byte == 9) || (current_command.command_byte == 10))
    {
        current_blklen = old_blklen;
    }
    return TRUE;
}

/**
  * @brief This function is used to initialize the flash card
  * @return none
  */
void MMC_FLASH_Init(void)
{
    uint32_t response;
    uint16_t loopguard;
    uint32_t i;
    uint8_t counter = 0;
    uint8_t pchar[16];              // Data pointer for storing MMC
    uint32_t c_size, bl_len;
    uint8_t c_mult;


    Is_Initialized = 0;


    PE7 = 1;//SPI_SET_SS_HIGH(g_pSPI);// CS = 1

    SD_Delay(2000);
    //--------------------------------------------------------
    //  Send 74 SD clcok in SD mode for Toshiba SD Card
    //--------------------------------------------------------
    for(counter = 0; counter < 10; counter++)
    {
        SingleWrite(0xFF);
    }
    SD_Delay(2000);

    PE7 = 0;//SPI_SET_SS_LOW(g_pSPI);// CS = 0

    while(MMC_Command_Exec(GO_IDLE_STATE, EMPTY, EMPTY, &response) == FALSE)
        SD_Delay(1000);
    if(response != 0x01)
        return;

    if(MMC_Command_Exec(SEND_IF_COND, 0x15A, pchar, &response) && response == 1)
    {
        /* SDC ver 2.00 */
        if(pchar[2] == 0x01 && pchar[3] == 0x5A)
        {
            /* The card can work at VDD range of 2.7-3.6V */
            loopguard = 0;
            do
            {
                MMC_Command_Exec(SD_SEND_OP_COND, 0x40000000, EMPTY, &response); //Enable HCS(OCR[30])
                if(!++loopguard) break;
                SD_Delay(0x100);
            }
            while(response != 0);
            if(!loopguard)
                return;

            MMC_Command_Exec(READ_OCR, EMPTY, pchar, &response);
            SDtype = (pchar[0] & 0x40) ? SDv2 | SDBlock : SDv2;
        }
    }
    else
    {
        /* SDv1 or MMCv3 */
        MMC_Command_Exec(SD_SEND_OP_COND, 0x00, EMPTY, &response);
        if(response <= 1)
        {
            loopguard = 0;
            do
            {
                MMC_Command_Exec(SD_SEND_OP_COND, 0x00, EMPTY, &response);
                if(!++loopguard) break;
                SD_Delay(50);
            }
            while(response != 0);
            if(!loopguard)
                return;
            SDtype = SDv1;  /* SDv1 */
        }
        else
        {
            loopguard = 0;
            do
            {
                MMC_Command_Exec(SEND_OP_COND, 0x00, EMPTY, &response);
                if(!++loopguard) break;
                SD_Delay(50);
            }
            while(response != 0);
            if(!loopguard)
                return;
            SDtype = MMCv3; /* MMCv3 */
        }
        MMC_Command_Exec(SET_BLOCKLEN, (uint32_t)PHYSICAL_BLOCK_SIZE, EMPTY, &response);
    }
    if(MMC_Command_Exec(SEND_CSD, EMPTY, pchar, &response) == FALSE)
        return;

    if(response == 0)
    {
        DBG_PRINTF("Change speed:");
        for(i = 0; i < 16; i++)
        {
            DBG_PRINTF("0x%X ", pchar[i]);
        }

    }
    else
    {
        DBG_PRINTF("CARD STATUS 0x%X:\n", response);
        for(i = 0; i < 16; i++)
        {
            DBG_PRINTF("0x%X ", pchar[i]);
        }
        LogicSector = 0;
        return;
    }

    if(SDtype & SDBlock) // Determine the number of MMC sectors;
    {
        bl_len = 1 << (pchar[5] & 0x0f) ;
        c_size = ((pchar[7] & 0x3F) << 16) | (pchar[8] << 8) | (pchar[9]);
        LogicSector = c_size * ((512 * 1024) / bl_len);
    }
    else
    {
        bl_len = 1 << (pchar[5] & 0x0f) ;
        c_size = ((pchar[6] & 0x03) << 10) | (pchar[7] << 2) | ((pchar[8] & 0xc0) >> 6);
        c_mult = (((pchar[9] & 0x03) << 1) | ((pchar[10] & 0x80) >> 7));
        LogicSector = (c_size + 1) * (1 << (c_mult + 2)) * (bl_len / 512);
    }
    DBG_PRINTF("\nLogicSector:%d, PHYSICAL_SIZE:%dMB\n", LogicSector, (LogicSector / 2 / 1024));

    loopguard = 0;
    while((MMC_Command_Exec(READ_SINGLE_BLOCK, 0, 0, &response) == FALSE))
    {
        if(!++loopguard) break;
    }
    Is_Initialized = 1;
}

/**
  * @brief This function is used to Open GPIO function and initial SDCARD
  * @retval SD_FAIL Initial SDCARD Failed
  * @retval SD_SUCCESS Success
  */
volatile uint32_t  Gfreq;

uint32_t SDCARD_Open(void)
{
    PC15 = 0;//Enable SD Card power
    SD_Delay(100000);

    /* Configure g_pSPI as a master, 8-bit transaction*/
    SPI_Open(g_pSPI, SPI_MASTER, SPI_MODE_0, 8, 300000);//300Khz for SD initial flow
    DBG_PRINTF("SPI is running at %d Hz\n", SPI_GetBusClock(g_pSPI));
    SPI_DisableAutoSS(g_pSPI);
    PE7 = 1;//SPI_SET_SS_LOW(g_pSPI);
    SingleWrite(0xFFFFFFFF);

    MMC_FLASH_Init();
    SD_Delay(300000);
    if(Is_Initialized)
        DBG_PRINTF("SDCARD INIT OK\n\n");
    else
    {
        DBG_PRINTF("SDCARD INIT FAIL\n\n");
        return SD_FAIL;
    }

    Gfreq = SPI_SetBusClock(g_pSPI, 16000000);//16Mhz for SD operation speed
    DBG_PRINTF("Now, SPI is running at %d Hz\n", SPI_GetBusClock(g_pSPI));

    return SD_SUCCESS;
}

/**
  * @brief This function is used to close SDCARD
  * @return none
  */
void SDCARD_Close(void)
{
    SPI_Close(g_pSPI);
}

/**
  * @brief This function is used to get card total sector after SDCARD is opened
  * @param[out] *pu32TotSecCnt Get sector count
  * @retval TRUE The size is already saved
  * @retval FALSE The size is zero
  */
uint32_t SDCARD_GetCardSize(uint32_t *pu32TotSecCnt)
{
    if(LogicSector == 0)
        return FALSE;
    else
        *pu32TotSecCnt = LogicSector;

    return TRUE;
}

/**
  * @brief This function is used to get logic sector size
  * @return The Logic Sector size
  */
uint32_t GetLogicSector(void)
{
    return LogicSector;
}

/**
  * @brief This function is used to Get data from SD card
  * @param[in] addr Set start address for LBA
  * @param[in] size Set data size (byte)
  * @param[in] buffer Set buffer pointer
  * @return none
  */
void SpiRead(uint32_t addr, uint32_t size, uint8_t* buffer)
{
    /* This is low level read function of USB Mass Storage */
    uint32_t response;
    if(SDtype & SDBlock)
    {
        while(size >= PHYSICAL_BLOCK_SIZE)
        {
            MMC_Command_Exec(READ_SINGLE_BLOCK, addr, buffer, &response);
            addr   ++;
            buffer += PHYSICAL_BLOCK_SIZE;
            size  -= PHYSICAL_BLOCK_SIZE;
        }

    }
    else
    {
        addr *= PHYSICAL_BLOCK_SIZE;
        while(size >= PHYSICAL_BLOCK_SIZE)
        {
            MMC_Command_Exec(READ_SINGLE_BLOCK, addr, buffer, &response);
            addr   += PHYSICAL_BLOCK_SIZE;
            buffer += PHYSICAL_BLOCK_SIZE;
            size  -= PHYSICAL_BLOCK_SIZE;
        }
    }
}

/**
  * @brief This function is used to store data into SD card
  * @param[in] addr Set start address for LBA
  * @param[in] size Set data size (byte)
  * @param[in] buffer Set buffer pointer
  * @return none
  */
void SpiWrite(uint32_t addr, uint32_t size, uint8_t* buffer)
{
    uint32_t response;
    if(SDtype & SDBlock)
    {
        while(size >= PHYSICAL_BLOCK_SIZE)
        {
            MMC_Command_Exec(WRITE_BLOCK, addr, buffer, &response);
            addr   ++;
            buffer += PHYSICAL_BLOCK_SIZE;
            size  -= PHYSICAL_BLOCK_SIZE;
        }
    }
    else
    {
        addr *= PHYSICAL_BLOCK_SIZE;
        while(size >= PHYSICAL_BLOCK_SIZE)
        {
            MMC_Command_Exec(WRITE_BLOCK, addr, buffer, &response);
            addr   += (PHYSICAL_BLOCK_SIZE);
            buffer += PHYSICAL_BLOCK_SIZE;
            size  -= PHYSICAL_BLOCK_SIZE;
        }
    }
}
/*@}*/ /* end of group NUC230_240_SDCARD_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group NUC230_240_SDCARD_Driver */

/*@}*/ /* end of group NUC230_240_Library */

/*** (C) COPYRIGHT 2019 Nuvoton Technology Corp. ***/
