/*******************************************************************************
 * 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 Utility
 ******************************************************************************/
/******************************************************************************
 * @file          emmc_utility.c
 * - Version      : 0.04
 * @brief         Analysis of SDHI data.
 * .
 *****************************************************************************/
/******************************************************************************
 * 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"
/* ***************** MACROS, CONSTANTS, COMPILATION FLAGS ****************** */

#define BUSY_SIGNAL (1U << 10U)

static const uint32_t cmd_reg_hw[EMMC_CMD_MAX + 1U] = {
		0x00000000U, /* CMD0 */
		0x00000701U, /* CMD1 */
		0x00000002U, /* CMD2 */
		0x00000003U, /* CMD3 */
		0x00000004U, /* CMD4 */
		0x00000505U, /* CMD5 */
		0x00000406U, /* CMD6 */
		0x00000007U, /* CMD7 */
		0x00001C08U, /* CMD8 */
		0x00000009U, /* CMD9 */
		0x0000000AU, /* CMD10 */
		0x00000000U, /* reserved */
		0x0000000CU, /* CMD12 */
		0x0000000DU, /* CMD13 */
		0x00001C0EU, /* CMD14 */
		0x0000000FU, /* CMD15 */
		0x00000010U, /* CMD16 */
		0x00000011U, /* CMD17 */
		0x00007C12U, /* CMD18 */
		0x00000C13U, /* CMD19 */
		0x00000000U,
		0x00001C15U, /* CMD21 */
		0x00000000U,
		0x00000017U, /* CMD23 */
		0x00000018U, /* CMD24 */
		0x00006C19U, /* CMD25 */
		0x00000C1AU, /* CMD26 */
		0x0000001BU, /* CMD27 */
		0x0000001CU, /* CMD28 */
		0x0000001DU, /* CMD29 */
		0x0000001EU, /* CMD30 */
		0x00001C1FU, /* CMD31 */
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00000423U, /* CMD35 */
		0x00000424U, /* CMD36 */
		0x00000000U,
		0x00000026U, /* CMD38 */
		0x00000427U, /* CMD39 */
		0x00000428U, /* CMD40 : send cmd */
		0x00000000U,
		0x0000002AU, /* CMD42 */
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00000C31U,
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00007C35U,
		0x00006C36U,
		0x00000037U, /* CMD55 */
		0x00000038U, /* CMD56 : Read */
		0x00000000U,
		0x00000000U,
		0x00000000U,
		0x00000000U
};
/* ********************** STRUCTURES, TYPE DEFINITIONS ********************* */

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

/* ************************** FUNCTION PROTOTYPES ************************** */

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

/** make non-transfer command data
 *
 * Response data buffer is automatically selected.
 *
 * - Pre-conditions:<BR>
 * Clock to memory card IF is enabled.
 *
 * - Post-conditions:<BR>
 * After this function is called, command can be executed.
 *
 * param[in] cmd command information.
 * param[in] arg command argument
 * return None.
 */
void emmc_make_nontrans_cmd(HAL_MEMCARD_COMMAND cmd, uint32_t arg)
{
	/* command information */
	mmc_drv_obj.cmd_info.cmd = cmd;
	mmc_drv_obj.cmd_info.arg = arg;
	mmc_drv_obj.cmd_info.dir = HAL_MEMCARD_READ;
	mmc_drv_obj.cmd_info.hw = cmd_reg_hw[(uint32_t)cmd & HAL_MEMCARD_COMMAND_INDEX_MASK];

	/* clear data transfer information */
	mmc_drv_obj.trans_size = 0U;
	mmc_drv_obj.remain_size = 0U;
	mmc_drv_obj.buff_address_virtual = NULL;
	mmc_drv_obj.buff_address_physical = NULL;

	/* response information */
	mmc_drv_obj.response_length = 6U;

	switch ((HAL_MEMCARD_RESPONSE_TYPE)((uint32_t)(mmc_drv_obj.cmd_info.cmd) & (uint32_t)HAL_MEMCARD_RESPONSE_TYPE_MASK)) {
	case HAL_MEMCARD_RESPONSE_NONE:
		mmc_drv_obj.response = (uint32_t *)mmc_drv_obj.response_data;
		mmc_drv_obj.response_length = 0U;
		break;
	case HAL_MEMCARD_RESPONSE_R1:
		mmc_drv_obj.response = &mmc_drv_obj.r1_card_status;
		break;
	case HAL_MEMCARD_RESPONSE_R1b:
		mmc_drv_obj.cmd_info.hw |= BUSY_SIGNAL; /* bit10 = R1 busy bit */
		mmc_drv_obj.response = &mmc_drv_obj.r1_card_status;
		break;
	case HAL_MEMCARD_RESPONSE_R2:
		mmc_drv_obj.response = (uint32_t *)mmc_drv_obj.response_data;
		mmc_drv_obj.response_length = 17U;
		break;
	case HAL_MEMCARD_RESPONSE_R3:
		mmc_drv_obj.response = &mmc_drv_obj.r3_ocr;
		break;
	case HAL_MEMCARD_RESPONSE_R4:
		mmc_drv_obj.response = &mmc_drv_obj.r4_resp;
		break;
	case HAL_MEMCARD_RESPONSE_R5:
		mmc_drv_obj.response = &mmc_drv_obj.r5_resp;
		break;
	default:
		mmc_drv_obj.response = (uint32_t *)mmc_drv_obj.response_data;
		break;
	}
}

/** Making command information for data transfer command.
 *
 * - Pre-conditions:<BR>
 * None.
 *
 * - Post-conditions:<BR>
 * After this function is called, command can be executed.
 *
 * param[in] cmd command
 * param[in] arg command argument
 * param[in] buff_address_virtual Pointer to buffer where data is/will be stored. (virtual address)
 *            Client is responsible of allocation and deallocation of the buffer.
 * param[in] len transfer length in bytes
 * param[in] dir direction
 * param[in] transfer_mode Mode of data transfer, DMA or not DMA.
 * return None.
 */
void emmc_make_trans_cmd(HAL_MEMCARD_COMMAND cmd, uint32_t arg, uint32_t *buff_address_virtual, /* virtual address */
uint32_t len, HAL_MEMCARD_OPERATION dir, HAL_MEMCARD_DATA_TRANSFER_MODE transfer_mode)
{
	emmc_make_nontrans_cmd(cmd, arg); /* update common information */

	/* for data transfer command */
	mmc_drv_obj.cmd_info.dir = dir;
	mmc_drv_obj.buff_address_virtual = buff_address_virtual;
	mmc_drv_obj.buff_address_physical = buff_address_virtual;
	mmc_drv_obj.trans_size = len;
	mmc_drv_obj.remain_size = len;
	mmc_drv_obj.transfer_mode = transfer_mode;
}

/** Send idle command.
 * Function execute CMD0.
 *
 * - Pre-conditions:<BR>
 * Clock to MMC I/F enabled.
 *
 * - Post-conditions:<BR>
 * Card reset to idle or pre-idle state.
 *
 * param[in] arg CMD0 argument.
 * return error code
 */
EMMC_ERROR_CODE emmc_send_idle_cmd(uint32_t arg)
{
	EMMC_ERROR_CODE result = EMMC_ERR;
	uint32_t freq;

	/* initialize state */
	mmc_drv_obj.mount = FALSE;
	mmc_drv_obj.selected = FALSE;
	mmc_drv_obj.during_transfer = FALSE;
	mmc_drv_obj.during_dma_transfer = FALSE;
	mmc_drv_obj.dma_error_flag = FALSE;
	mmc_drv_obj.force_terminate = FALSE;
	mmc_drv_obj.state_machine_blocking = FALSE;

	mmc_drv_obj.bus_width = HAL_MEMCARD_DATA_WIDTH_1_BIT;
	mmc_drv_obj.max_freq = MMC_20MHZ; /* 20MHz */
	mmc_drv_obj.current_state = EMMC_R1_STATE_IDLE;

	/* CMD0 (MMC clock is current frequency. if Data transfer mode, 20MHz or higher.) */
	emmc_make_nontrans_cmd(CMD0_GO_IDLE_STATE, arg); /* CMD0 */
	result = emmc_exec_cmd(EMMC_R1_ERROR_MASK, mmc_drv_obj.response);
	if (result != EMMC_SUCCESS) {
		return result;
	}

	/* change MMC clock(400KHz) */
	freq = MMC_400KHZ;
	result = emmc_set_request_mmc_clock(&freq);
	if (result != EMMC_SUCCESS) {
		return result;
	}

	return EMMC_SUCCESS;
}

/** get bit field data for 16bytes data(CSD register).
 *
 * - Pre-conditions:<BR>
 * .
 * - Post-conditions:<BR>
 * .
 *
 * param[in] data  16bytes data.
 * param[in] top bit number(top). 128>top
 * param[in] bottom bit number(bottom). (0<=bottom<=top)
 * return bit field.
 */
uint32_t emmc_bit_field(const uint8_t *data, uint32_t top, uint32_t bottom)
{
	uint32_t value;

	uint32_t index_top = (uint32_t)(15U - (top >> 3U));
	uint32_t index_bottom = (uint32_t)(15U - (bottom >> 3U));

	if (index_top == index_bottom) {
		value = data[index_top];
	} else if ((index_top + 1U) == index_bottom) {
		value = (uint32_t)(((uint32_t)data[index_top] << 8U) | data[index_bottom]);
	} else if ((index_top + 2U) == index_bottom) {
		value = (uint32_t)(
				((uint32_t)data[index_top] << 16U) | ((uint32_t)data[index_top + 1U] << 8U) | data[index_top + 2U]);
	} else {
		value = (uint32_t)(
				((uint32_t)data[index_top] << 24U) | ((uint32_t)data[index_top + 1U] << 16U)
						| ((uint32_t)data[index_top + 2U] << 8U) | data[index_top + 3U]);
	}

	value = ((value >> (bottom & 0x07U)) & ((1U << ((top - bottom) + 1U)) - 1U));

	return value;
}

/* ******************************** END ************************************ */

