/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright 2018-2023 Renesas Electronics Corporation All rights reserved.
*******************************************************************************/



/*******************************************************************************
 * DESCRIPTION   : emmc mount
 ******************************************************************************/
/******************************************************************************
 * @file          emmc_mount.c
 * - Version      : 0.08
 * @brief         
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 02.02.2022 0.01     First Release
 *         : 17.02.2022 0.02     V4H software restrictions
 *         : 18.03.2022 0.03     Unrestricted V4H software
 *         : 16.06.2022 0.04     Change log output
 *         : 24.10.2022 0.05     Support HS200/400
 *         : 31.10.2022 0.06     License notation change.
 *         : 08.12.2022 0.07     Support build option of EMMC_TRANS_MODE
 *                               and add cmd21 function
 *         : 21.08.2023 0.08     Add support for V4M.
*****************************************************************************/

/* ************************ HEADER (INCLUDE) SECTION *********************** */
#include "emmc_config.h"
#include "emmc_hal.h"
#include "emmc_std.h"
#include "emmc_registers.h"
#include "emmc_def.h"
#include <stdint.h>
#include "timer.h"
#include "log.h"

#define BIT0                (0x0000000000000001U)
#define BIT1                (0x0000000000000002U)
#define BIT2                (0x0000000000000004U)
#define BIT3                (0x0000000000000008U)
#define BIT4                (0x0000000000000010U)
#define BIT5                (0x0000000000000020U)
#define BIT6                (0x0000000000000040U)
#define BIT8                (0x0000000000000100U)
#define BIT2_0              (BIT2|BIT1|BIT0)
#define BIT3_0              (BIT3|BIT2|BIT1|BIT0)
#define BIT6_0              (BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0)
#define BIT23_16            (0x0000000000FF0000U)

#define SMPCMP_SUCCESS      (0x0U)
#define FAIL                (0x0U)
#define SUCCESS             (0x1U)
#define FIRST               (0x1U)

#define MMC_CMD21           (0x00001C15U)

/* for S4 */
#define PFC_DRVCTRL1_GP1_DM0_MMC_MASK       (0x77770000U)
#define PFC_DRVCTRL1_GP1_DM0_MMC_VALUE      (0x66660000U)
#define PFC_DRVCTRL2_GP1_DM0_MMC_MASK       (0x07777777U)
#define PFC_DRVCTRL2_GP1_DM0_MMC_VALUE      (0x06666666U)

/* for V4H */
#define PFC_DRVCTRL0_GP3_DM0_MMC_MASK       (0x77777777U)
#define PFC_DRVCTRL0_GP3_DM0_MMC_VALUE      (0x66666666U)
#define PFC_DRVCTRL1_GP3_DM0_MMC_MASK       (0x00000777U)
#define PFC_DRVCTRL1_GP3_DM0_MMC_VALUE      (0x00000666U)

/* ***************** MACROS, CONSTANTS, COMPILATION FLAGS ****************** */

/* ********************** STRUCTURES, TYPE DEFINITIONS ********************* */

/* ********************** DECLARATION OF EXTERNAL DATA ********************* */

/* ************************** FUNCTION PROTOTYPES ************************** */
static EMMC_ERROR_CODE emmc_clock_ctrl(uint32_t mode);
static EMMC_ERROR_CODE emmc_card_init(void);
static EMMC_ERROR_CODE emmc_mode_select(uint32_t *speed_mode);
static EMMC_ERROR_CODE emmc_bus_width(uint32_t width);
static EMMC_ERROR_CODE emmc_power_select(void);
static uint32_t emmc_set_timeout_register_value(uint32_t freq);
static void set_sd_clk(uint32_t clkDiv);
static uint32_t emmc_calc_tran_speed(void);
static EMMC_ERROR_CODE emmc_scc_tuning(void);
static uint8_t emmc_scc_tuningmain(uint32_t tapset);
static EMMC_ERROR_CODE emmc_exec_cmd21(void);
static EMMC_ERROR_CODE emmc_tapset_position_find(uint32_t tapsetNum, const uint8_t result_tuning[], uint32_t *tapset);
static EMMC_ERROR_CODE emmc_data_read(void *buf);
static EMMC_ERROR_CODE emmc_wait_readbuf_enable(void);
static EMMC_ERROR_CODE emmc_wait_readbuf_end(void);
static void pfc_reg_modify_write(uintptr_t addr, uint32_t mask, uint32_t data);
/* ********************************* CODE ********************************** */

/** eMMC mount operation.
 *
 * Sequence is the following.
 * 1) Bus initialization (emmc_card_init())
 * 2) Changing the data bus width. (emmc_bus_width())
 * 3) Selecting high speed or HS200 or HS400 mode refering to CARD_TYPE[196]. (emmc_mode_select())
 * 4) Selecting power class. (emmc_power_select())
 * 5) If selected mode is HS200 or HS400, perform timing tuning. (emmc_scc_tuning())
 * 6) If selected mode is HS400, switching to HS400 mode.
 *
 * - Pre-conditions:<BR>
 * eMMC driver is initialized. The power supply of MMC IF must be turning on.
 * - Post-conditions:<BR>
 * MMC card state changes to transfer state.
 *
 * return eMMC error code.
 */
EMMC_ERROR_CODE emmc_mount(void)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t speed_mode;

    /* Frequency initialization setting */
    mmc_drv_obj.set_freq = MMC_400KHZ;
    
    /* state check */
    if ((mmc_drv_obj.initialize != TRUE) || (mmc_drv_obj.card_power_enable != TRUE)
            || ((mem_read32(SD_INFO2) & SD_INFO2_CBSY) != 0U))
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_STATE);
        return EMMC_ERR_STATE;
    }

    /* initialize card (IDLE state --> Transfer state) */
    result = emmc_card_init();
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        (void)emmc_clock_ctrl(FALSE);
        return result;
    }

    /* Changing the data bus width */
    result = emmc_bus_width(8U);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        (void)emmc_clock_ctrl(FALSE);
        return result;
    }

    /* Selecting Power class */
    result = emmc_power_select();
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        (void)emmc_clock_ctrl(FALSE);
        return result;
    }

    /* Switching HS400 or HS200 or High speed mode */
    result = emmc_mode_select(&speed_mode);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        (void)emmc_clock_ctrl(FALSE);
        return result;
    }

    switch (speed_mode)
    {
        case TIMING_HS400:
            NOTICE("eMMC Transfer mode: HS400\n");
            break;
        case TIMING_HS200:
            NOTICE("eMMC Transfer mode: HS200\n");
            break;
        case TIMING_HIGH_SPEED:
            NOTICE("eMMC Transfer mode: High Speed\n");
            break;
        default:
            NOTICE("eMMC Transfer mode: No High Speed\n");
            break;
    }

    /* Tuning for HS200 and HS400 */
    if ((speed_mode==TIMING_HS200) || (speed_mode==TIMING_HS400))
    {
        /* EnableSccHs200Mmc */
        mem_write32(SCC_DTCNTL, 0x00080001U); /* TAPNUM=8, TAPEN=1 */
        mem_write32(SCC_CKSEL, 0x00000001U);  /* DTSEL=1 */
        mem_write32(SCC_TMPPORT2, 0x00000000U);

        /* Timing Tuning for HS200/HS400 */
        result = emmc_scc_tuning();
        if(result != EMMC_SUCCESS)
        {
            ERROR("%s\n",__func__);
            return result;
        }
    } else {
        /* DisableSccMmc */
        mem_write32(SCC_DTCNTL, 0x00080000U); /* TAPNUM=8, TAPEN=0 */
        mem_write32(SCC_CKSEL, 0x00000000U);  /* DTSEL=0 */
        mem_write32(SCC_TMPPORT2, 0x00000000U);
    }

    /* Setting for HS400 */
    if (speed_mode==TIMING_HS400)
    {
        /*Set the SoC's driver strength for HS400 mode */
#if (RCAR_LSI == RCAR_S4)
        /* DRVCTRL1_1[30:28][26:24][22:20][18:16]/ = D2, D1, D0, CLK */
        pfc_reg_modify_write(PFC_DRVCTRL1_GP1_DM0, PFC_DRVCTRL1_GP1_DM0_MMC_MASK, PFC_DRVCTRL1_GP1_DM0_MMC_VALUE);
        /* DRVCTRL2_1[26:24][22:20][18:16][14:12][10:8][6:4][2:1]/ = CMD, D7, DS, D6, D4, D5, D3 */
        pfc_reg_modify_write(PFC_DRVCTRL2_GP1_DM0, PFC_DRVCTRL2_GP1_DM0_MMC_MASK, PFC_DRVCTRL2_GP1_DM0_MMC_VALUE);
#elif ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
        /* DRVCTRL0_3[30:28][26:24][22:20][18:16][14:12][10:8][6:4][2:1]/ = D4, D5, D3, DS, CLK, D2, D0, D1 */
        pfc_reg_modify_write(PFC_DRVCTRL0_GP3_DM0, PFC_DRVCTRL0_GP3_DM0_MMC_MASK, PFC_DRVCTRL0_GP3_DM0_MMC_VALUE);
        /* DRVCTRL1_3[10:8][6:4][2:0] = CMD, D6, D7 */
        pfc_reg_modify_write(PFC_DRVCTRL1_GP3_DM0, PFC_DRVCTRL1_GP3_DM0_MMC_MASK, PFC_DRVCTRL1_GP3_DM0_MMC_VALUE);
#endif

        /* EnableHs400Mmc*/
        mem_write32(SDIF_MODE, 0x00000001U);
    
        /* Set HS_TIMING to 0x01 (High Speed) */
        /* CMD6 */
        emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_HS_TIMING);
        result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
        if (result != EMMC_SUCCESS)
        {
            ERROR("%s\n",__func__);
            return result;
        }

        /* Host changes frequency to <= 52MHz */
        mmc_drv_obj.set_freq = MMC_52MHZ;
        result = emmc_set_request_mmc_clock(&mmc_drv_obj.set_freq);
        if (result != EMMC_SUCCESS)
        {
            ERROR("%s\n",__func__);
            return result;
        }

        /* Set the bus width to DDR 8bit (CMD6) */
        emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_BUS_WIDTH_8_DDR);
        result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
        if (result != EMMC_SUCCESS)
        {
            ERROR("%s\n",__func__);
            return result;
        }
        
        /* Set the bus width to 8bit for SD card access control option register */
        mem_write32(SD_OPTION, ((mem_read32(SD_OPTION) & ~(SD_OPTION_WIDTH|SD_OPTION_WIDTH8)) | SD_OPTION_WIDTH8 ));

        /* Set HS_TIMING to 0x03 (HS400) */
        emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_HS400);
        result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
        if (result != EMMC_SUCCESS)
        {
            ERROR("%s\n",__func__);
            return result;
        }

        /* CMD13 */
        emmc_make_nontrans_cmd(CMD13_SEND_STATUS, EMMC_RCA << 16U);
        result = emmc_exec_cmd(EMMC_R1_ERROR_MASK_WITHOUT_CRC, mmc_drv_obj.response);
        if (result != EMMC_SUCCESS)
        {
            ERROR("%s\n", __func__);
            panic;
        }

        /* InitSdCkcrForHS400 */
        mem_write32(CPG_CPGWPR, ~(0x00000001U));
        mem_write32(CPG_SD0CKCR, 0x00000001U); /* SD0H-Clock=800MHz(Supply), SD0=200MHz(Supply) */

        /* Host changes frequency to 200MHz */
        mmc_drv_obj.max_freq = HS400_200MHZ;
        mmc_drv_obj.set_freq = mmc_drv_obj.max_freq;
        result = emmc_set_request_mmc_clock(&mmc_drv_obj.set_freq);
        if (result != EMMC_SUCCESS)
        {
            ERROR("%s\n",__func__);
            return result;
        }

        /* EnableSccHs400Mmc */
        mem_write32(SCC_DTCNTL, 0x00040001U);   /* TAPNUM=4, TAPEN=1 */
        mem_write32(SCC_CKSEL, 0x00000001U);    /* DTSEL=1 */
        mem_write32(SCC_TMPPORT2, 0x80000010U); /* HS400EN=1, HS400OSEL=1 (use SDxH clock) */
    }

    /* mount complete */
    mmc_drv_obj.mount = TRUE;

    return EMMC_SUCCESS;
}

/** Bus initialization function
 *
 * - Pre-conditions:<BR>
 * eMMC driver is initialized. The power supply of MMC IF must be turning on.
 * - Post-conditions:<BR>
 * MMC card state changes to transfer state.
 *
 * retval EMMC_SUCCESS successful.
 * return eMMC error code.
 * attention upper layer must be check pre-conditions.
 */
static EMMC_ERROR_CODE emmc_card_init(void)
{
    int32_t retry;
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t resultCalc = 0U;
    mmc_drv_obj.set_freq = MMC_400KHZ; /* 390KHz */

    /* state check */
    if ((mmc_drv_obj.initialize != TRUE) || (mmc_drv_obj.card_power_enable != TRUE)
            || ((mem_read32(SD_INFO2) & SD_INFO2_CBSY) != 0U))
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_STATE);
        return EMMC_ERR_STATE;
    }

    /* clock on (force change) */
    mmc_drv_obj.current_freq = 0U;
    mmc_drv_obj.max_freq = MMC_20MHZ; /* MMC_20MHZ = MMC_12MHZ = 12.187MHz */
    result = emmc_set_request_mmc_clock(&mmc_drv_obj.set_freq);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return EMMC_ERR;
    }

    micro_wait(1000U); /* wait 1ms */

    /* CMD0, arg=0x00000000 */
    result = emmc_send_idle_cmd (0x00000000U);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return result;
    }

    micro_wait(200U); /* wait 74clock 390kHz(189.74us)*/

    /* CMD1 */
    emmc_make_nontrans_cmd(CMD1_SEND_OP_COND, EMMC_HOST_OCR_VALUE);
    for (retry = 300; retry > 0; retry--)
    {
        result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
        if (result != EMMC_SUCCESS)
    {
            ERROR("%s\n",__func__);
            return result;
        }

        if ((mmc_drv_obj.r3_ocr & EMMC_OCR_STATUS_BIT) != 0U)
        {
            break; /* card is ready. exit loop */
        }
        micro_wait(1000U); /* wait 1ms */
    }

    if (retry == 0)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_TIMEOUT);
        return EMMC_ERR_TIMEOUT;
    }

    if ((mmc_drv_obj.r3_ocr & EMMC_OCR_ACCESS_MODE_MASK) != EMMC_OCR_ACCESS_MODE_SECT)
    {
        /* unknown value */
        ERROR("%s:0x%x\n",__func__,EMMC_ERR);
        return EMMC_ERR;
    }

    /* CMD2 */
    emmc_make_nontrans_cmd(CMD2_ALL_SEND_CID_MMC, 0x00000000U);
    mmc_drv_obj.response = (uint32_t *)(&mmc_drv_obj.cid_data[0U]); /* use CID special buffer */
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return result;
    }

    /* CMD3 */
    emmc_make_nontrans_cmd(CMD3_SET_RELATIVE_ADDR, EMMC_RCA << 16U);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return result;
    }

    /* CMD9 : CSD */
    emmc_make_nontrans_cmd(CMD9_SEND_CSD, EMMC_RCA << 16U);
    mmc_drv_obj.response = (uint32_t *)(&mmc_drv_obj.csd_data[0U]); /* use CSD special buffer */
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return result;
    }

    /* card version check */
    if (EMMC_CSD_SPEC_VARS() < 4U)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_ILLEGAL_CARD);
        return EMMC_ERR_ILLEGAL_CARD;
    }

    /* CMD7 (select card) */
    emmc_make_nontrans_cmd(CMD7_SELECT_CARD, EMMC_RCA << 16U);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return result;
    }

    mmc_drv_obj.selected = TRUE;

    /* card speed check */
    resultCalc = emmc_calc_tran_speed(); /* Card spec is calculated from TRAN_SPEED(CSD).  */
    if (resultCalc == 0U)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_ILLEGAL_CARD);
        return EMMC_ERR_ILLEGAL_CARD;
    }
    mmc_drv_obj.max_freq = mmc_drv_obj.set_freq; /* max frequency (card spec) */

    result = emmc_set_request_mmc_clock(&mmc_drv_obj.set_freq);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return EMMC_ERR;
    }

    /* set read/write timeout */
    mmc_drv_obj.data_timeout = emmc_set_timeout_register_value(mmc_drv_obj.set_freq);
    mem_write32(SD_OPTION, ((mem_read32(SD_OPTION) & ~(SD_OPTION_TIMEOUT_CNT_MASK)) | mmc_drv_obj.data_timeout));

    /* SET_BLOCKLEN:512byte */
    /* CMD16 */
    emmc_make_nontrans_cmd(CMD16_SET_BLOCKLEN, EMMC_BLOCK_LENGTH);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        return result;
    }

    /* Transfer Data Length */
    mem_write32(SD_SIZE, EMMC_BLOCK_LENGTH);

    /* CMD8 : EXT_CSD */
    emmc_make_trans_cmd(CMD8_SEND_EXT_CSD, 0x00000000U, (uint32_t *)(&mmc_drv_obj.ext_csd_data[0U]),
            EMMC_MAX_EXT_CSD_LENGTH, HAL_MEMCARD_READ, HAL_MEMCARD_NOT_DMA);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        /* CMD12 is not send.
        * If BUS initialization is failed, user must be execute Bus initialization again.
        * Bus initialization is start CMD0(soft reset command).
        */
        ERROR("%s\n",__func__);
        return result;
    }

    return EMMC_SUCCESS;
}

/** Switching to high-speed or HS200/HS400 mode
 *
 * - Pre-conditions:<BR>
 * Executing Bus initializatin by emmc_card_init().
 * EXT_CSD data must be stored in mmc_drv_obj.ext_csd_data[].
 *
 * - Post-conditions:<BR>
 * Change the clock frequency to 26MHz or 52MHz or 200MHz.
 *
 * retval EMMC_SUCCESS successful or aleady switching.
 * retval EMMC_ERR_STATE state error.
 * retval EMMC_ERR unknown error.
 * return emmc error code.
 */
static EMMC_ERROR_CODE emmc_mode_select(uint32_t *speed_mode)
{
    EMMC_ERROR_CODE result = EMMC_ERR;

#if ((EMMC_TRANS_MODE == HS400) || (EMMC_TRANS_MODE == HS200) || (EMMC_TRANS_MODE == HIGH_SPEED))
        uint8_t cardType;
        cardType = (uint8_t)mmc_drv_obj.ext_csd_data[EMMC_EXT_CSD_CARD_TYPE];
#endif

    /* state check */
    if (mmc_drv_obj.selected != TRUE)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_STATE);
        panic;
    }

    #if (EMMC_TRANS_MODE == HS400)
        if ((cardType & EMMC_EXT_CSD_CARD_TYPE_400MHZ) != 0U)
        {
            /* CMD6 */
            emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_HS200);
            result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
            if (result != EMMC_SUCCESS)
            {
                ERROR("%s\n",__func__);
                panic;
            }

            *speed_mode = TIMING_HS400;
            mmc_drv_obj.max_freq = MMC_200MHZ;
        } else {
            ERROR("HS400 is not supported.");
            panic;
        }
    #elif (EMMC_TRANS_MODE == HS200)
        if ((cardType & EMMC_EXT_CSD_CARD_TYPE_200MHZ) != 0U)
        {
            /* CMD6 */
            emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_HS200);
            result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
            if (result != EMMC_SUCCESS)
            {
                ERROR("%s\n",__func__);
                panic;
            }

            *speed_mode = TIMING_HS200;
            mmc_drv_obj.max_freq = MMC_200MHZ;
        } else {
            ERROR("HS200 is not supported.");
            panic;
        }
    #elif (EMMC_TRANS_MODE == HIGH_SPEED)
        if ((cardType & EMMC_EXT_CSD_CARD_TYPE_52MHZ) != 0U)
        {
            /* CMD6 */
            emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_HS_TIMING);
            result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
            if (result != EMMC_SUCCESS)
            {
                ERROR("%s\n",__func__);
                panic;
            }

            *speed_mode = TIMING_HIGH_SPEED;
            mmc_drv_obj.max_freq = MMC_52MHZ;
        } else if ((cardType & EMMC_EXT_CSD_CARD_TYPE_26MHZ) != 0U) {
            /* CMD6 */
            emmc_make_nontrans_cmd(CMD6_SWITCH, EMMC_SWITCH_HS_TIMING);
            result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
            if (result != EMMC_SUCCESS)
            {
                ERROR("%s\n",__func__);
                panic;
            }

            *speed_mode = TIMING_HIGH_SPEED;
            mmc_drv_obj.max_freq = MMC_26MHZ;
        } else {
            ERROR("High Speed is not supported.");
            panic;
        }
    #elif (EMMC_TRANS_MODE == NO_HIGH_SPEED)
        *speed_mode = TIMING_HIGH_SPEED_OFF;
        mmc_drv_obj.max_freq = MMC_20MHZ;
    #endif /* EMMC_TRANS_MODE == HS400 */

#if ((EMMC_TRANS_MODE == HS400) || (EMMC_TRANS_MODE == HS200))
    /* CMD13 */
    emmc_make_nontrans_cmd(CMD13_SEND_STATUS, EMMC_RCA << 16U);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK_WITHOUT_CRC, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        panic;
    }
#endif /* ((EMMC_TRANS_MODE == HS400) || (EMMC_TRANS_MODE == HS200)) */

    /* set mmc clock */
    mmc_drv_obj.set_freq = mmc_drv_obj.max_freq;
    result = emmc_set_request_mmc_clock(&mmc_drv_obj.set_freq);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        panic;
    }

    /* set read/write timeout */
    mmc_drv_obj.data_timeout = emmc_set_timeout_register_value(mmc_drv_obj.set_freq);
    mem_write32(SD_OPTION, ((mem_read32(SD_OPTION) & ~(SD_OPTION_TIMEOUT_CNT_MASK)) | mmc_drv_obj.data_timeout));

    return result;
}

/** Changing the data bus width
 *
 * if chinging the data bus width failed, card is reset by CMD0.
 * Please do Bus initialization over again.
 *
 * - Pre-conditions:<BR>
 * Executing Bus initializatin by emmc_card_init().
 *
 * - Post-conditions:<BR>
 * Change the data bus width to 8bit or 4bit.
 * mmc_drv_obj.ext_csd_data is updated.
 *
 * param[in] width bus width (8 or 4)
 * retval EMMC_SUCCESS successful.
 * retval EMMC_ERR_PARAM parameter error
 * retval EMMC_ERR_STATE state error.
 *
 */
static EMMC_ERROR_CODE emmc_bus_width(uint32_t width)
{
    EMMC_ERROR_CODE result = EMMC_ERR;

    /* parameter check */
    if ((width != 8U) && (width != 4U) && (width != 1U))
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_PARAM);
        return EMMC_ERR_PARAM;
    }

    /* state check */
    if (mmc_drv_obj.selected != TRUE)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_STATE);
        return EMMC_ERR_STATE;
    }

    mmc_drv_obj.bus_width = (HAL_MEMCARD_DATA_WIDTH)(width >> 2U); /* 2 = 8bit, 1 = 4bit, 0 =1bit */

    /* CMD6 */
    emmc_make_nontrans_cmd(CMD6_SWITCH, ( EMMC_SWITCH_BUS_WIDTH_1 | ((uint32_t)(mmc_drv_obj.bus_width) << 8U)));
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        /* occurred error */
        mmc_drv_obj.bus_width = HAL_MEMCARD_DATA_WIDTH_1_BIT;
        goto EXIT;
    }

    switch (mmc_drv_obj.bus_width)
    {
        case HAL_MEMCARD_DATA_WIDTH_1_BIT:
            mem_write32(SD_OPTION, ((mem_read32(SD_OPTION) & ~(SD_OPTION_WIDTH|SD_OPTION_WIDTH8)) | SD_OPTION_WIDTH ));
            break;
        case HAL_MEMCARD_DATA_WIDTH_4_BIT:
            mem_write32(SD_OPTION, (mem_read32(SD_OPTION) & ~(SD_OPTION_WIDTH|SD_OPTION_WIDTH8)));
            break;
        case HAL_MEMCARD_DATA_WIDTH_8_BIT:
        default:
            mem_write32(SD_OPTION, ((mem_read32(SD_OPTION) & ~(SD_OPTION_WIDTH|SD_OPTION_WIDTH8)) | SD_OPTION_WIDTH8 ));
            break;
    }

    /* CMD8 : EXT_CSD */
    emmc_make_trans_cmd(CMD8_SEND_EXT_CSD, 0x00000000U, (uint32_t *)(&mmc_drv_obj.ext_csd_data[0U]),
        EMMC_MAX_EXT_CSD_LENGTH, HAL_MEMCARD_READ, HAL_MEMCARD_NOT_DMA);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        goto EXIT;
    }

    return EMMC_SUCCESS;

EXIT:
    ERROR("%s:0x%x\n",__func__,result);
    return result;
}

/** Switching to power class according to speed mode
 *
 * - Pre-conditions:<BR>
 * Executing Bus initializatin by emmc_card_init().
 * EXT_CSD data must be stored in mmc_drv_obj.ext_csd_data[].
 *
 * - Post-conditions:<BR>
 * Change the power class according to each speed mode.
 *
 * param[in] speed mode (TIMING_HIGH_SPEED_MODE(0x0) or TIMING_HIGH_SPEED(0x1) or TIMING_HS200(0x2) or TIMING_HS400(0x3))
 * retval EMMC_SUCCESS successful.
 * retval EMMC_ERR_PARAM parameter error
 * retval EMMC_ERR_STATE state error.
 *
 */
static EMMC_ERROR_CODE emmc_power_select(void)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t powerClass;

    powerClass = (uint8_t)mmc_drv_obj.ext_csd_data[EMMC_EXT_CSD_PWR_CL_DDR_200_360];    /* 200MHz, DDR, VCC=3.6V */

    /* read value [7:4]=8bit bus, [3:0]=4bit bus */
    powerClass = (powerClass & 0xF0U) >> 4U; /* 8bit */
    powerClass = (EXTCSD_ACCESS_BYTE|POW_CLASS_ADD|(powerClass << 8U));

    /* CMD6 */
    emmc_make_nontrans_cmd(CMD6_SWITCH, powerClass);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
    }

    return result;
}

/** select access partition
 *
 * This function write the EXT_CSD register(PARTITION_ACCESS: PARTITION_CONFIG[2:0]).
 *
 * - Pre-conditions:<BR>
 * MMC card is mounted.
 *
 * - Post-conditions:<BR>
 * selected partition can access.
 *
 * param[in] id user selects partitions to access.
 * retval EMMC_SUCCESS successful.
 * retval EMMC_ERR_STATE state error.
 * retval EMMC_ERR_PARAM parameter error.
 * return emmc error code.
 */
EMMC_ERROR_CODE emmc_select_partition(EMMC_PARTITION_ID id)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t partition;
    uint32_t partition_config;

    /* state check */
    if (mmc_drv_obj.mount != TRUE)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_STATE);
        return EMMC_ERR_STATE;
    }

    /* id has PARTITION_ACCESS(Bit[2:0]) */
    if ((uint32_t)((uint32_t)id & ~(uint32_t)PARTITION_ID_MASK) != 0U)
        {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_PARAM);
        return EMMC_ERR_PARAM;
    }

    /* EXT_CSD[179] value */
    partition_config = (uint32_t)mmc_drv_obj.ext_csd_data[EMMC_EXT_CSD_PARTITION_CONFIG];
    if ((partition_config & (uint32_t)PARTITION_ID_MASK) == (uint32_t)id)
    {
        result = EMMC_SUCCESS;
    } else {

        partition_config = ((partition_config & (~(uint32_t)PARTITION_ID_MASK)) | (uint32_t)id);
        partition = EMMC_SWITCH_PARTITION_CONFIG | (partition_config << 8U);

        result = emmc_set_ext_csd(partition);
    }

    return result;
}

/** set EXT CSD data
 *
 * - Pre-conditions:<BR>
 * MMC card is mounted.
 *
 * - Post-conditions:<BR>
 * mmc_drv_obj.ext_csd_data[] is updated.
 *
 * param[in] arg argument of CMD6
 * return emmc error code.
 */
EMMC_ERROR_CODE emmc_set_ext_csd(uint32_t arg)
{
    EMMC_ERROR_CODE result = EMMC_ERR;

    /* CMD6 */
    emmc_make_nontrans_cmd(CMD6_SWITCH, arg);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        return result;
    }

    /* CMD13 */
    emmc_make_nontrans_cmd(CMD13_SEND_STATUS, EMMC_RCA << 16U);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        return result;
    }

    /* CMD8 : EXT_CSD */
    emmc_make_trans_cmd(CMD8_SEND_EXT_CSD, 0x00000000, (uint32_t *)(&mmc_drv_obj.ext_csd_data[0U]),
            EMMC_MAX_EXT_CSD_LENGTH, HAL_MEMCARD_READ, HAL_MEMCARD_NOT_DMA);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        return result;
    }
    return EMMC_SUCCESS;
}

/** set request MMC clock frequency.
 *
 * Function returns EMMC_SUCCESS if clock is already running in the desired frequency.
 * EMMC_ERR is returned if the HW doesn't support requested clock frequency.
 * If matching frequence cannot be set the closest frequence below should be selected.
 * For example if 50MHz is requested, but HW supports only 48MHz then 48MHz should be returned in the freq parameter.
 *
 * - Pre-conditions:<BR>
 * initialized eMMC driver with emmc_init().
 * Memory card and MMCSDIO host controller needs to be powered up beforehand.
 *
 * - Post-conditions:<BR>
 * Desired clock frequency is set to memory card IF.
 *
 * param[in] freq frequency [Hz]
 * retval EMMC_SUCCESS successful.
 * retval EMMC_ERR_STATE state error.
 * retval EMMC_ERR busy
 */
EMMC_ERROR_CODE emmc_set_request_mmc_clock(const uint32_t *freq)
{
    /* parameter check */
    if (freq == NULL)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_PARAM);
        return EMMC_ERR_PARAM;
    }

    /* state check */
    if ((mmc_drv_obj.initialize != TRUE) || (mmc_drv_obj.card_power_enable != TRUE))
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_STATE);
        return EMMC_ERR_STATE;
    }

    /* clock is already running in the desired frequency. */
    if ((mmc_drv_obj.clock_enable == TRUE) && (mmc_drv_obj.current_freq == *freq))
    {
        return EMMC_SUCCESS;
    }

    /* busy check */
    if ((mem_read32(SD_INFO2) & SD_INFO2_CBSY) != 0U)
        {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_CARD_BUSY);
        return EMMC_ERR;
    }

    set_sd_clk(*freq);
    mmc_drv_obj.clock_enable = FALSE;

    return emmc_clock_ctrl(TRUE); /* clock on */
}

/** set sd clock.
 *
 * - Pre-conditions:<BR>
 * CSD data must be stored in mmc_drv_obj.csd_data[].
 *
 * - Post-conditions:<BR>
 * set mmc clock.
 *
 * param[in] clkDiv  request freq
 * return None.
 */
static void set_sd_clk(uint32_t clkDiv)
{
    uint32_t dataL;

    dataL = (mem_read32(SD_CLK_CTRL) & (~SD_CLK_CTRL_CLKDIV_MASK));

    switch (clkDiv)
    {
        case 1U: /* 1/1   */
            dataL |= 0x000000FFU;
            break;
        case 2U: /* 1/2   */
            dataL |= 0x00000000U;
            break;
        case 4U: /* 1/4   */
            dataL |= 0x00000001U;
            break;
        case 8U: /* 1/8   */
            dataL |= 0x00000002U;
            break;
        case 16U: /* 1/16  */
            dataL |= 0x00000004U;
            break;
        case 32U:/* 1/32  */
            dataL |= 0x00000008U;
            break;
        case 64U:/* 1/64  */
            dataL |= 0x00000010U;
            break;
        case 128U:/* 1/128 */
            dataL |= 0x00000020U;
            break;
        case 256U: /* 1/256 */
            dataL |= 0x00000040U;
            break;
        case 512U:/* 1/512 */
            dataL |= 0x00000080U;
            break;
        default:
            /* nop */
            break;
    }

    mem_write32(SD_CLK_CTRL, dataL);
    mmc_drv_obj.current_freq = (uint32_t)clkDiv;
}


/** Enable/Disable MMC clock
 *
 * - Pre-conditions:<BR>
 * Before enabling the clock for the first time the desired clock frequency must be set with
 * emmc_set_clock_freq().
 * Berore setting mmc_drv_obj.data_timeout with emmc_set_data_timeout().
 *
 * - Post-conditions:<BR>
 * After this function is called, clock to memory card IF is on/off.
 *
 * param[in] mode TRUE = clock on, FALSE = clock off
 * retval EMMC_SUCCESS succeeded
 * retval EMMC_ERR     Busy
 */
static EMMC_ERROR_CODE emmc_clock_ctrl(uint32_t mode)
{
    uint32_t value;

    /* busy check */
    if ((mem_read32(SD_INFO2) & SD_INFO2_CBSY) != 0U)
        {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_CARD_BUSY);
        return EMMC_ERR;
    }

    if (mode == TRUE)
    {
        /* clock ON */
        value = ((mem_read32(SD_CLK_CTRL) | MMC_SD_CLK_START) & SD_CLK_WRITE_MASK);
        mem_write32(SD_CLK_CTRL, value); /* on  */
        mmc_drv_obj.clock_enable = TRUE;
    } else {
        /* clock OFF */
        value = ((mem_read32(SD_CLK_CTRL) & MMC_SD_CLK_STOP) & SD_CLK_WRITE_MASK);
        mem_write32(SD_CLK_CTRL, value); /* off */
        mmc_drv_obj.clock_enable = FALSE;
    }

    return EMMC_SUCCESS;
}

/** Calculate Card support frequency.
 * TRAN_SPEED defines the clock frequency when not in high speed mode.
 *
 * - Pre-conditions:<BR>
 * CSD data must be stored in mmc_drv_obj.csd_data[].
 *
 * - Post-conditions:<BR>
 * None.
 * return Frquency[Hz]
 */
static uint32_t emmc_calc_tran_speed(void)
{
    const uint32_t unit[8U] = {10000U, 100000U, 1000000U, 10000000U, 0U, 0U, 0U, 0U}; /**< frequency unit (1/10) */
    const uint32_t mult[16U] = {0U, 10U, 12U, 13U, 15U, 20U, 26U, 30U, 35U, 40U, 45U, 52U, 55U, 60U, 70U, 80U}; /**< multiple factor (x10) */
    uint32_t maxFreq = 0U;
    uint32_t result = 0U;
    uint32_t tran_speed = EMMC_CSD_TRAN_SPEED();

    /* tran_speed = 0x32
    * unit[tran_speed&0x7] = uint[0x2] = 1000000
    * mult[(tran_speed&0x78)>>3] = mult[0x30>>3] = mult[6] = 26
    * 1000000 * 26 = 26000000 (26MHz)
    */

    maxFreq = unit[tran_speed & EMMC_TRANSPEED_FREQ_UNIT_MASK]
            * mult[(tran_speed & EMMC_TRANSPEED_MULT_MASK) >> EMMC_TRANSPEED_MULT_SHIFT];

    if (maxFreq == 0U)
    {
        result = 0U;
    } else if ( MMC_FREQ_52MHZ <= maxFreq) {
        mmc_drv_obj.set_freq = MMC_52MHZ;
        result = 1U;
    } else if ( MMC_FREQ_26MHZ <= maxFreq) {
        mmc_drv_obj.set_freq = MMC_26MHZ;
        result = 1U;
    } else if ( MMC_FREQ_20MHZ <= maxFreq) {
        mmc_drv_obj.set_freq = MMC_20MHZ;
        result = 1U;
    } else {
        mmc_drv_obj.set_freq = MMC_400KHZ;
        result = 1U;
    }

    return result;
}

/** Calculate read/write timeout.
 *
 * - Pre-conditions:<BR>
 * CSD data must be stored in mmc_drv_obj.csd_data[].
 *
 * - Post-conditions:<BR>
 * set mmc clock.
 *
 * param[in] freq  Base clock Div
 * return    SD_OPTION Timeout Counter
 */
static uint32_t emmc_set_timeout_register_value(uint32_t freq)
{
    uint32_t timeoutCnt = 0U; /* SD_OPTION   - Timeout Counter  */

    switch (freq)
    {
        case 1U:/* SDCLK * 2^27 */
            timeoutCnt = 0xE0U;
            break;
        case 2U:/* SDCLK * 2^27 */
            timeoutCnt = 0xE0U;
            break;
        case 4U:/* SDCLK * 2^26 */
            timeoutCnt = 0xD0U;
            break;
        case 8U:/* SDCLK * 2^25 */
            timeoutCnt = 0xC0U;
            break;
        case 16U:/* SDCLK * 2^24 */
            timeoutCnt = 0xB0U;
            break;
        case 32U:/* SDCLK * 2^23 */
            timeoutCnt = 0xA0U;
            break;
        case 64U:/* SDCLK * 2^22 */
            timeoutCnt = 0x90U;
            break;
        case 128U:/* SDCLK * 2^21 */
            timeoutCnt = 0x80U;
            break;
        case 256U:/* SDCLK * 2^20 */
            timeoutCnt = 0x70U;
            break;
        case 512U:/* SDCLK * 2^19 */
            timeoutCnt = 0x60U;
            break;
        default:
            /* nop */
            break;
    }

    return timeoutCnt;
}

/** Excecute tuning process and find optimal sampling clock position.
 *
 * Excecute tuning process in emmc_scc_tuningmain() and
 * find optimal sampling clock position in emmc_tapset_position_find.
 *
 * - Pre-conditions:<BR>
 * Executing Bus initializatin by emmc_card_init().
 * - Post-conditions:<BR>
 * None.
 *
 * return emmc error code.
 */
static EMMC_ERROR_CODE emmc_scc_tuning(void)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t i;
    uint8_t result_tuning[8];
    uint32_t smpcmp[8];
    uint32_t dataL;
    uint32_t tapset;
    uint32_t allPass;
    uint32_t tapsetNum;

    /* Hardware Adjustment Register */
    mem_write32(SCC_DT2FF, 0x00000300U);	/* fixed value */

    dataL = mem_read32(SCC_DTCNTL);
    tapsetNum = (dataL & BIT23_16) >> 16U;

    /* CRC16(for Data) Disable */
    mem_write32(SDIF_MODE, (mem_read32(SDIF_MODE) | BIT8));

    allPass = TRUE;	/* allPass flag */
    for (i = 0; i < tapsetNum; i++)
    {
        result_tuning[i] = emmc_scc_tuningmain(i);
        smpcmp[i] = mem_read32(SCC_SMPCMP);

        if( result_tuning[i] != TUNING_SUCCESS )
        {
            result_tuning[i] = FAIL;
            allPass = FALSE;		/* if fail : clear allPass flag */
        }
    }

    /* Detecting a change point of the input data
    SCC_SMPCMP[24:16] : CMPNGU = There is a change point of data just before TAP-CLK.
    SCC_SMPCMP[8:0]   : CMPNGD = There is a change point of data just after TAP-CLK. 
    If "result_tuning[i]" is all pass, confirm SCC_SMPCMP and avoid setting value close to change point */
    if (allPass == TRUE)
    {
        for (i = 0; i < tapsetNum; i++)
        {
            if(smpcmp[i] == SMPCMP_SUCCESS)
            {
                result_tuning[i] = SUCCESS;	/* result=Pass */
            } else {
                result_tuning[i] = FAIL;	/* result=Fail */
            }
        }
    }

    /* CRC16(for Data) Enable */
    mem_write32(SDIF_MODE, (mem_read32(SDIF_MODE) & ~BIT8));

    /* Int flag clear */
    mem_write32(SD_INFO1, 0x00000000U);
    mem_write32(SD_INFO2, 0x00000000U);

    result = emmc_tapset_position_find(tapsetNum, result_tuning, &tapset);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s:0x%x\n",__func__,RCAR_ERR);
        panic;
    }

    mem_write32(SCC_TAPSET, tapset);

    /* SET_BLOCKLEN */
    /* CMD16 */
    emmc_make_nontrans_cmd(CMD16_SET_BLOCKLEN, 512);
    result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s\n",__func__);
        panic;
    }

    /* Transfer Data Length */
    mem_write32(SD_SIZE, 512U);

    return result;
}

/** Excecute tuning process.
    *
    * Set sampling clock position 'tapset'(0-7) and
    * Excecute tuninng process by CMD21.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * None.
    *
    * param[in] tapset number 0-7
    * retval TUNING_SUCCESS successful
    * retval EMMC_TUNING_FAIL error from eMMC
    * retval RCAR_TUNING_FAIL error from R-car 
    * return result of tuning
    */
static uint8_t emmc_scc_tuningmain(uint32_t tapset)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint8_t result_tuning = TUNING_SUCCESS;
    uint64_t i;
    uint32_t dataL;
    uint32_t sd_info2;
    uint8_t tuningBuf[128] __attribute__ ((aligned (8)));   /* 1024bit (64bit alignment) */

    const uint32_t eMmcTuningBlockPattern[32]={
    0xFF00FFFFU, 0x0000FFFFU, 0xCCCCFFFFU, 0xCCCC33CCU,
    0xCC3333CCU, 0xFFFFCCCCU, 0xFFFFEEFFU, 0xFFEEEEFFU,
    0xFFDDFFFFU, 0xDDDDFFFFU, 0xBBFFFFFFU, 0xBBFFFFFFU,
    0xFFFFFFBBU, 0xFFFFFF77U, 0x77FF7777U, 0xFFEEDDBBU,
    0x00FFFFFFU, 0x00FFFFFFU, 0xCCFFFF00U, 0xCC33CCCCU,
    0x3333CCCCU, 0xFFCCCCCCU, 0xFFEEFFFFU, 0xEEEEFFFFU,
    0xDDFFFFFFU, 0xDDFFFFFFU, 0xFFFFFFDDU, 0xFFFFFFBBU,
    0xFFFFBBBBU, 0xFFFF77FFU, 0xFF7777FFU, 0xEEDDBB77U,
    };

    /* SCC_TAPSET : Sampling Clock Position */
    mem_write32(SCC_TAPSET, tapset);

    /* Clear flags */
    mem_write32(SD_INFO1, 0x00000000U);
    mem_write32(SD_INFO2, 0x00000000U);

    /* The data length of the tuning block is fixed. The BLOCKLEN command is unnecessary */

    /* Transfer Data Length */
    mem_write32(SD_SIZE, 128);

    /* Block Count Disable */
    mem_write32(SD_STOP, 0x00000000U);

    /* MMC_CMD21:SEND_TUNING_BLOCK */
    /* CMD21 */
    result = emmc_exec_cmd21();
    if (result != EMMC_SUCCESS)
    {
        result_tuning = EMMC_TUNING_FAIL;
        goto EXIT;
    }

    /* Check Error */
    /* BIT2: END error (Communications Errors) */
    /* BIT1: CRC error (Communications Errors) */
    /* BIT0: CMD error (Communications Errors) */
    sd_info2 = mem_read32(SD_INFO2);
    if ((BIT2_0 & sd_info2) != 0U)
    {
        result_tuning = RCAR_TUNING_FAIL;
        goto EXIT;
    }

    result = emmc_data_read((void*)tuningBuf);
    if (result != EMMC_SUCCESS)
    {
        result_tuning = RCAR_TUNING_FAIL;
        goto EXIT;
    }

    /* Check Error */
    /* BIT3: Data timeout(other than response timeout) (Timeouts) */
    /* BIT2: END error (Communications Errors) */
    /* BIT1: CRC error (Communications Errors) */
    /* BIT0: CMD error (Communications Errors) */
    sd_info2 = mem_read32(SD_INFO2);
    if ((BIT3_0 & sd_info2) != 0U)
    {
        result_tuning = RCAR_TUNING_FAIL;
        goto EXIT;
    } else {
        /* Tuning Data Verify */
        for (i = 0; i < (128U>>2U); i++)
        {
            dataL = mem_read32((uintptr_t)tuningBuf + (uintptr_t)(i<<2U));
            if(dataL != eMmcTuningBlockPattern[i])
            {
                result_tuning = RCAR_TUNING_FAIL;
                goto EXIT;
            }
        }
    }

    goto EXIT;

EXIT:
    return result_tuning;
}

/** Send CMD21(SEND_TUNING_BLOCK) to eMMC.
    *
    * Send CMD21 to eMMC and check error using SD_INFO1,2.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * None.
    *
    * return result(EMMC_SUCCESS or RCAR_ERR).
    */
static EMMC_ERROR_CODE emmc_exec_cmd21(void)
{
    EMMC_ERROR_CODE result = RCAR_ERR;
    uint32_t loop = 1U;
    uint32_t sd_info1;
    uint32_t sd_info2;

    mem_write32(SD_ARG, 0);
    mem_write32(SD_CMD, MMC_CMD21);

    while(loop == 1U)
    {
        sd_info1 = mem_read32(SD_INFO1);
        if(((sd_info1 & BIT0)) == 1U)           /* INFO0 = 1 (Response End) */
        {
            mem_write32(SD_INFO1, 0x0000FFFE);  /* clear BIT0 */
            result = EMMC_SUCCESS;
            loop = 0U;
        }

        sd_info2 = mem_read32(SD_INFO2);
        if ((BIT6_0 & sd_info2) != 0U)
        {
            result = RCAR_ERR;
            break;
        }
    }

    return result;
}

/** Find optimal sampling clock position.
    *
    * Based on the 'tapsetNum' number of result_tuning,
    * Choose optimal sampling clock position and store it in tapset.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * None.
    *
    * param[in] total number of tapset: 0-7
    *           result[] of tuning: 0 or 1
    *           pointer to tapset
    * retval EMMC_SUCCESS successful
    * retval RCAR_ERR the number of tuning failure is smaller than 3
    * return emmc error code.
    */
static EMMC_ERROR_CODE emmc_tapset_position_find(uint32_t tapsetNum, const uint8_t result_tuning[], uint32_t *tapset)
{
    EMMC_ERROR_CODE result = EMMC_SUCCESS;
    uint8_t tbl[8*2U] = {};
    uint32_t i; 
    uint32_t maxCount;
    uint32_t maxPassPos;
    uint32_t nowCount;
    uint32_t passPos;

    /* tbl <- result_tuning */
    if ((tapsetNum >= 1U) && (tapsetNum <= 8U))
    {
        for (i = 0; i < tapsetNum; i++)
        {
            tbl[i] = result_tuning[i];
            tbl[i+tapsetNum] = result_tuning[i];
        }
    } else {
        ERROR("invalid tapsetNum\n");
        panic;
    }

    /* Search */
    maxCount = 0;
    maxPassPos = 0;
    nowCount = 0;
    passPos = 0;
    for (i = 0; i < (tapsetNum*2U); i++)
    {
        if (tbl[i] == SUCCESS)
        {
            nowCount++;
            if (nowCount == FIRST)
            {
                passPos = i;
            }
            if (nowCount >= tapsetNum)
            {
                maxCount = tapsetNum;
                maxPassPos = 0;
                break;
            }
        } else {
            if (nowCount != 0U)
            {
                if (maxCount < nowCount)
                {
                    maxCount = nowCount;
                    maxPassPos = passPos;
                }
            }
            nowCount = 0;
            passPos = 0;
        }
    }

    /* Check 3 or more */
    if (maxCount < 3U)
    {
        *tapset = 0;
        result = RCAR_ERR;  /* Error End */
    }

    /* Set value select */
    if (maxCount == tapsetNum)
    {
        *tapset = 0;    /* All pass : Default value = 0 */
    } else {
        *tapset = maxPassPos+(maxCount>>1);
    }
    if (*tapset > 7U)
    {
        *tapset -= 8U;
    }

    return result;  /* Normal End */
}

/** Read emmc SD_BUF data.
    *
    * Read 128byte data and write the data to *buf.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * Completing writing emmc data to *buf.
    *
    * param[in] pointer to buffer in which read data is stored
    * retval EMMC_SUCCESS successful
    * retval RCAR_ERR SD_INFO2 error
    * return emmc error code.
    */
static EMMC_ERROR_CODE emmc_data_read(void *buf)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t i;
    uint64_t *bufPtrLL;

    bufPtrLL = (uint64_t*)buf;
    mem_write32(SD_INFO1, 0x0000FFFEU);  /* clear BIT0 */

    result = emmc_wait_readbuf_enable();
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s:0x%x\n",__func__,RCAR_ERR);
        panic;
    }

    for (i = 0; i < (128U>>3U); i++)    /* 64-bit access */
    {
        *bufPtrLL = mem_read64(SD_BUF0);
        bufPtrLL++;
    }

    result = emmc_wait_readbuf_end();
    if (result != EMMC_SUCCESS)
    {
        ERROR("%s:0x%x\n",__func__,RCAR_ERR);
    }

    return result;
}

/** Check if emmc read operation has enabled and error occured.
    *
    * Check BIT8 of SD_INFO2. If the bit is set, 
    * emmc read operation has enabled and EMMC_SUCCESS is returned.
    * Check BIT0-6 of SD_INFO2. If their bit is set, 
    * some errors has occured and RCAR_ERR is returned.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * None.
    *
    * retval EMMC_SUCCESS successful
    * retval RCAR_ERR SD_INFO2 error
    * return emmc error code.
    */
static EMMC_ERROR_CODE emmc_wait_readbuf_enable(void)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t dataL;

    while(true)
    {
        dataL = mem_read32(SD_INFO2);
        if((dataL & BIT8) != 0U)                    /* SD_BUF Read Enable = 1 */
        {
            mem_write32(SD_INFO2, 0x0000FEFFU);     /* clear BIT8 */
            result = EMMC_SUCCESS;
            break;
        }

        /* Check Error */
        /* BIT6: Response timeout (Timeouts) */
        /* BIT5: SD_BUF Illegal Read Access */
        /* BIT4: SD_BUF Illegal Write Access */
        /* BIT3: Data timeout(other than response timeout) (Timeouts) */
        /* BIT2: END error (Communications Errors) */
        /* BIT1: CRC error (Communications Errors) */
        /* BIT0: CMD error (Communications Errors) */
        if ((BIT6_0 & dataL) != 0U)
        {
            result = RCAR_ERR;
            break;
        }
    }

    return result;
}

/** Check if emmc read operation has completed and some error occured.
    *
    * Check BIT2 of SD_INFO1. If the bit is set, 
    * emmc read operation has completed and EMMC_SUCCESS is returned.
    * Check BIT0-6 of SD_INFO2. If their bit is set, 
    * some errors occured and RCAR_ERR is returned.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * None.
    *
    * retval EMMC_SUCCESS successful
    * retval RCAR_ERR SD_INFO2 error
    * return emmc error code.
    */
static EMMC_ERROR_CODE emmc_wait_readbuf_end(void)
{
    EMMC_ERROR_CODE result = EMMC_ERR;
    uint32_t dataL;

    while(true)
    {
        dataL = mem_read32(SD_INFO1);
        if((dataL & BIT2) != 0U)                    /* Access End = 1 */
        {
            mem_write32(SD_INFO1, 0x0000FFFBU);     /* clear BIT2 */
            result = EMMC_SUCCESS;
            break;
        }

        dataL = mem_read32(SD_INFO2);

        /* Check Error */
        /* BIT6: Response timeout (Timeouts) */
        /* BIT5: SD_BUF Illegal Read Access */
        /* BIT4: SD_BUF Illegal Write Access */
        /* BIT3: Data timeout(other than response timeout) (Timeouts) */
        /* BIT2: END error (Communications Errors) */
        /* BIT1: CRC error (Communications Errors) */
        /* BIT0: CMD error (Communications Errors) */
        if ((BIT6_0 & dataL) != 0U)
        {
            result = RCAR_ERR;
            break;
        }
    }

    return result;
}

/** PFC register write operation.
    *
    * Write data masked with mask to addr after writing PMMR register.
    *
    * - Pre-conditions:<BR>
    * Executing Bus initializatin by emmc_card_init().
    * - Post-conditions:<BR>
    * Update PFC register
    *
    * param[in] register address
    *           mask value
    *           data to write
    * return None.
    */
static void pfc_reg_modify_write(uintptr_t addr, uint32_t mask, uint32_t data)
{
    uint32_t localdata;

    localdata = mem_read32(addr);
    localdata &= ~(mask);
    localdata |= data;
    mem_write32(PFC_PMMR(addr), ~(localdata));
    mem_write32(addr, localdata);
}
