/*******************************************************************************
 * 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 2021-2022 Renesas Electronics Corporation All rights reserved.
 *******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : eMMC Mount
 ******************************************************************************/
/******************************************************************************
 * @file          emmc_mount.c
 * - Version      : 0.04
 * @brief         initialize of condition.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 22.02.2022 0.01     First Release
 *         : 22.10.2021 0.02     Remove unnecessary code
 *         : 22.12.2021 0.03     Support static analysis
 *         : 06.01.2022 0.04     Modify SDHI access from DMA to PIO.
 *****************************************************************************/

/* ************************ 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 "micro_wait.h"
#include "remap.h"
#include "log.h"

/* ***************** 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_high_speed(void);
static EMMC_ERROR_CODE emmc_bus_width(uint32_t width);
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(uint32_t* freq);

/* ********************************* CODE ********************************** */

/** eMMC mount operation.
 *
 * Sequence is the following.
 * 1) Bus initialization (emmc_card_init())
 * 2) Switching to high speed mode. (emmc_high_speed())
 * 3) Changing the data bus width. (emmc_bus_width())
 *
 * - 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;

	/* 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%08x\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;
	}

	/* Switching high speed mode */
	result = emmc_high_speed();
	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;
	}

	/* 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;
	uint32_t freq = MMC_400KHZ; /* 390KHz */
	EMMC_ERROR_CODE result = EMMC_ERR;
	uint32_t resultCalc = 0U;

	/* 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%08x\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(&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%08x\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%08x\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%08x\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(&freq); /* Card spec is calculated from TRAN_SPEED(CSD).  */
	if (resultCalc == 0U) {
		ERROR("%s:0x%08x\n",__func__,EMMC_ERR_ILLEGAL_CARD);
		return EMMC_ERR_ILLEGAL_CARD;
	}
	mmc_drv_obj.max_freq = freq; /* max frequency (card spec) */

	result = emmc_set_request_mmc_clock(&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(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 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.
 *
 * 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_high_speed(void)
{
	uint32_t freq; /**< High speed mode clock frequency */
	EMMC_ERROR_CODE result = EMMC_ERR;
	uint8_t cardType;

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

	/* max frequency */
	cardType = (uint8_t)mmc_drv_obj.ext_csd_data[EMMC_EXT_CSD_CARD_TYPE];
	if ((cardType & EMMC_EXT_CSD_CARD_TYPE_52MHZ) != 0U) {
		freq = MMC_52MHZ;
	} else if ((cardType & EMMC_EXT_CSD_CARD_TYPE_26MHZ) != 0U) {
		freq = MMC_26MHZ;
	} else {
		freq = MMC_20MHZ;
	}

	/* Hi-Speed-mode selction */
	if (( MMC_52MHZ == freq) || ( MMC_26MHZ == freq)) {
		/* 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;
		}

	}

	/* set mmc clock */
	mmc_drv_obj.max_freq = freq;
	result = emmc_set_request_mmc_clock(&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(freq);
	mem_write32(SD_OPTION, ((mem_read32(SD_OPTION) & ~(SD_OPTION_TIMEOUT_CNT_MASK)) | mmc_drv_obj.data_timeout));

	/* 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__);
		return result;
	}

	return EMMC_SUCCESS;
}

/** 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%08x\n",__func__,EMMC_ERR_PARAM);
		return EMMC_ERR_PARAM;
	}

	/* state check */
	if (mmc_drv_obj.selected != TRUE) {
		ERROR("%s:0x%08x\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;
	}

	/* 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) {
		goto EXIT;
	}

	/* 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%08x\n",__func__,result);
	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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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(uint32_t* freq)
{
	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) {
		*freq = MMC_52MHZ;
		result = 1U;
	} else if ( MMC_FREQ_26MHZ <= maxFreq) {
		*freq = MMC_26MHZ;
		result = 1U;
	} else if ( MMC_FREQ_20MHZ <= maxFreq) {
		*freq = MMC_20MHZ;
		result = 1U;
	} else {
		*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;
}
