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



/*******************************************************************************
 * DESCRIPTION   : emmc command
 ******************************************************************************/
/******************************************************************************
 * @file          emmc_cmd.c
 * - Version      : 0.04
 * @brief         
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 02.02.2022 0.01     First Release
 *         : 16.06.2022 0.02     Change log output
 *         : 31.10.2022 0.03     License notation change.
 *         : 07.06.2024 0.04     Modify the transfer end bit of DMAC channel.
 *****************************************************************************/

#include "emmc_config.h"
#include "emmc_hal.h"
#include "emmc_std.h"
#include "emmc_registers.h"
#include "emmc_def.h"
#include "timer.h"
#include "log.h"
/* ***************** MACROS, CONSTANTS, COMPILATION FLAGS ****************** */

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

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

/* ************************** FUNCTION PROTOTYPES ************************** */
static void emmc_read_response(uint32_t *response);
static void emmc_little_to_big(uint8_t *p, uint32_t value);
static void emmc_data_transfer_dma(void);
static EMMC_ERROR_CODE emmc_response_check(const uint32_t *response, uint32_t error_mask);
static void emmc_softreset(void);
static void emmc_WaitCmd2Cmd_8Cycle(void);

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

/* execute MMC command.
 *
 * - Pre-conditions:<BR>
 *  * Clock to memory card IF is enabled.
 * - Post-conditions:<BR>
 * Requested command is executed successfully
 *
 * param[in] error_mask Errors to be checked (error values; HAL_MEMCARD_ERRORS)
 * param[in,out] *response Response from the card  (virtual address)
 * return eMMC error code.
 */
EMMC_ERROR_CODE emmc_exec_cmd(uint32_t error_mask, uint32_t *response)
{
    EMMC_ERROR_CODE rtn_code = EMMC_ERR;
    HAL_MEMCARD_RESPONSE_TYPE response_type;
    HAL_MEMCARD_COMMAND_TYPE cmd_type;
    EMMC_INT_STATE state;

    /* parameter check */
    if (response == NULL)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR_PARAM);
        return EMMC_ERR_PARAM;
    }

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

    if (mmc_drv_obj.state_machine_blocking == TRUE)
    {
        ERROR("%s:0x%x\n",__func__,EMMC_ERR);
        return EMMC_ERR;
    }

    state = ESTATE_BEGIN;
    response_type = (HAL_MEMCARD_RESPONSE_TYPE)(uintptr_t)((uint32_t)(mmc_drv_obj.cmd_info.cmd) & (uint32_t)HAL_MEMCARD_RESPONSE_TYPE_MASK);
    cmd_type = (HAL_MEMCARD_COMMAND_TYPE)(uintptr_t)((uint32_t)(mmc_drv_obj.cmd_info.cmd) & (uint32_t)HAL_MEMCARD_COMMAND_TYPE_MASK);

    /* state machine */
    while ((mmc_drv_obj.force_terminate != TRUE) && (state != ESTATE_END))
    {
        /* The interrupt factor flag is observed. */
        (void)emmc_interrupt();

        /* wait interrupt */
        if (mmc_drv_obj.state_machine_blocking == TRUE)
        {
            continue;
        }

        switch (state)
        {
            case ESTATE_BEGIN:
                /* Busy check */
                if ((mmc_drv_obj.error_info.info2 & SD_INFO2_CBSY) != 0U)
                {
                    ERROR("%s:0x%x\n",__func__,EMMC_ERR_CARD_BUSY);
                    return EMMC_ERR_CARD_BUSY;
                }

                /* clear register */
                mem_write32(SD_INFO1, 0x00000000U);
                mem_write32(SD_INFO2, SD_INFO2_CLEAR);
                mem_write32(SD_INFO1_MASK, SD_INFO1_INFO0);
                mem_write32(SD_INFO2_MASK, ( SD_INFO2_ALL_ERR | SD_INFO2_CLEAR ));

                /* fallthrough */

            case ESTATE_ISSUE_CMD:
                /* ARG */
                mem_write32(SD_ARG, mmc_drv_obj.cmd_info.arg);
                /* issue cmd */
                mem_write32(SD_CMD, mmc_drv_obj.cmd_info.hw);
                /* Set driver flag */
                mmc_drv_obj.state_machine_blocking = TRUE;

                if (response_type == HAL_MEMCARD_RESPONSE_NONE)
                {
                    state = ESTATE_NON_RESP_CMD;
                } else {
                    state = ESTATE_RCV_RESP;
                }

                break;

            case ESTATE_NON_RESP_CMD:
                /* interrupt disable */
                mem_write32(SD_INFO1_MASK, 0x00000000U);
                mem_write32(SD_INFO2_MASK, SD_INFO2_CLEAR);

                /* check interrupt */
                if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0U)
                {
                    /* error interrupt */
                    rtn_code = EMMC_ERR_INFO2;
                    state = ESTATE_ERROR;
                } else if ((mmc_drv_obj.int_event1 & SD_INFO1_INFO0) == 0U) {
                    /* not receive expected interrupt */
                    rtn_code = EMMC_ERR_RESPONSE;
                    state = ESTATE_ERROR;
                } else {
                    emmc_WaitCmd2Cmd_8Cycle();
                    state = ESTATE_END;
                }
                break;

            case ESTATE_RCV_RESP:
                /* interrupt disable */
                mem_write32(SD_INFO1_MASK, 0x00000000U);
                mem_write32(SD_INFO2_MASK, SD_INFO2_CLEAR);

                /* check interrupt */
                if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0U)
                {
                    /* error interrupt */
                    rtn_code = EMMC_ERR_INFO2;
                    state = ESTATE_ERROR;
                    break;
                } else if ((mmc_drv_obj.int_event1 & SD_INFO1_INFO0) == 0U) {
                    /* not receive expected interrupt */
                    rtn_code = EMMC_ERR_RESPONSE;
                    state = ESTATE_ERROR;
                    break;
                } else {
                    /* nop */
                }

                /* read response */
                emmc_read_response(response);

                /* check response */
                rtn_code = emmc_response_check(response, error_mask);
                if (rtn_code != EMMC_SUCCESS)
                {
                    state = ESTATE_ERROR;
                    break;
                }

                if (response_type == HAL_MEMCARD_RESPONSE_R1b)
                {
                    /* R1b */
                    mem_write32(SD_INFO2_MASK, ( SD_INFO2_ALL_ERR | SD_INFO2_CLEAR ));
                    state = ESTATE_RCV_RESPONSE_BUSY;
                } else {
                    state = ESTATE_CHECK_RESPONSE_COMPLETE;
                }
                break;

            case ESTATE_RCV_RESPONSE_BUSY:
                /* check interrupt */
                if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0U)
                {
                    /* error interrupt */
                    rtn_code = EMMC_ERR_INFO2;
                    state = ESTATE_ERROR;
                    break;
                }
                /* DAT0 not Busy */
                if ((SD_INFO2_DAT0 & mmc_drv_obj.error_info.info2) != 0U)
                {
                    state = ESTATE_CHECK_RESPONSE_COMPLETE;
                    break;
                }
                break;

            case ESTATE_CHECK_RESPONSE_COMPLETE:
                if (cmd_type >= HAL_MEMCARD_COMMAND_TYPE_ADTC_WRITE)
                {
                    state = ESTATE_DATA_TRANSFER;
                } else {
                    emmc_WaitCmd2Cmd_8Cycle();
                    state = ESTATE_END;
                }
                break;

            case ESTATE_DATA_TRANSFER:
                /* ADTC command  */
                mmc_drv_obj.during_transfer = TRUE;
                mmc_drv_obj.state_machine_blocking = TRUE;

                if (mmc_drv_obj.transfer_mode == HAL_MEMCARD_DMA)
                {
                    /* DMA */
                    emmc_data_transfer_dma();
                } else {
                    /* PIO */
                    /* interrupt enable (FIFO read/write enable) */
                    if (mmc_drv_obj.cmd_info.dir == HAL_MEMCARD_WRITE)
                    {
                        mem_write32(SD_INFO2_MASK, ( SD_INFO2_BWE | SD_INFO2_ALL_ERR | SD_INFO2_CLEAR ));
                    } else {
                        mem_write32(SD_INFO2_MASK, ( SD_INFO2_BRE | SD_INFO2_ALL_ERR | SD_INFO2_CLEAR ));
                    }
                }
                state = ESTATE_DATA_TRANSFER_COMPLETE;
                break;

            case ESTATE_DATA_TRANSFER_COMPLETE:
                /* check interrupt */
                if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0U)
                {
                    /* error interrupt */
                    rtn_code = EMMC_ERR_INFO2;
                    state = ESTATE_TRANSFER_ERROR;
                    break;
                } else {
                    /* success. nothing to do. */
                }

                /* DMAC error ? */
                if (mmc_drv_obj.dma_error_flag == TRUE)
                {
                    /* Error occurred in DMAC driver. */
                    rtn_code = EMMC_ERR_FROM_DMAC_TRANSFER;
                    state = ESTATE_TRANSFER_ERROR;
                } else if (mmc_drv_obj.during_dma_transfer == TRUE) {
                    /* DMAC not finished. unknown error */
                    rtn_code = EMMC_ERR;
                    state = ESTATE_TRANSFER_ERROR;
                } else {
                    mem_write32(SD_INFO1_MASK, SD_INFO1_INFO2);
                    mem_write32(SD_INFO2_MASK, ( SD_INFO2_ALL_ERR | SD_INFO2_CLEAR ));

                    mmc_drv_obj.state_machine_blocking = TRUE;

                    state = ESTATE_ACCESS_END;
                }
                break;

            case ESTATE_ACCESS_END:

                /* clear flag */
                if (HAL_MEMCARD_DMA == mmc_drv_obj.transfer_mode)
                {
                    mem_write32(CC_EXT_MODE, CC_EXT_MODE_CLEAR); /* W (CC_EXT_MODE, H'0000_1010) SD_BUF DMA transfer disabled */
                    mem_write32(SD_STOP, 0x00000000U);
                    mmc_drv_obj.during_dma_transfer = FALSE;
                }

                mem_write32(SD_INFO1_MASK, 0x00000000U);
                mem_write32(SD_INFO2_MASK, SD_INFO2_CLEAR);
                mem_write32(SD_INFO1, 0x00000000U);
                mem_write32(SD_INFO2, SD_INFO2_CLEAR);

                if ((mmc_drv_obj.int_event1 & SD_INFO1_INFO2) != 0U)
                {
                    emmc_WaitCmd2Cmd_8Cycle();
                    state = ESTATE_END;
                } else {
                    state = ESTATE_ERROR;
                }
                break;

            case ESTATE_TRANSFER_ERROR:
                /* The error occurred in the Data transfer.  */
                if (HAL_MEMCARD_DMA == mmc_drv_obj.transfer_mode)
                {
                    mem_write32(CC_EXT_MODE, CC_EXT_MODE_CLEAR); /* W (CC_EXT_MODE, H'0000_1010) SD_BUF DMA transfer disabled */
                    mem_write32(SD_STOP, 0x00000000U);
                    mmc_drv_obj.during_dma_transfer = FALSE;
                }
                /* fallthrough */

            case ESTATE_ERROR:
                emmc_softreset();
                ERROR("%s:0x%x\n",__func__,rtn_code);
                return rtn_code;

            default:
                state = ESTATE_END;
                break;
        } /* switch (state) */
    } /*  while ( (mmc_drv_obj.force_terminate != TRUE) && (state != ESTATE_END) ) */

    /* force terminate */
    if (mmc_drv_obj.force_terminate == TRUE)
    {
        /* timeout timer is expired. Or, PIO data transfer error. */
        /* Timeout occurred in the DMA transfer. */
        if (mmc_drv_obj.during_dma_transfer == TRUE)
        {
            mmc_drv_obj.during_dma_transfer = FALSE;
        }
        emmc_softreset();

        return EMMC_ERR_FORCE_TERMINATE; /* error information has already been written. */
    }

    /* success */
    mmc_drv_obj.during_transfer = FALSE;

    return EMMC_SUCCESS;
}

/** host controller softrest.
 *
 * - Pre-conditions:<BR>
 * .
 * - Post-conditions:<BR>
 * .
 *
 * param None.
 * return None.
 */
static void emmc_softreset(void)
{
    int32_t loop = 10000;
    int32_t retry = 1000;

    /* flag clear */
    mmc_drv_obj.during_transfer = FALSE;
    mmc_drv_obj.during_dma_transfer = FALSE;
    mmc_drv_obj.state_machine_blocking = FALSE;
    mmc_drv_obj.force_terminate = FALSE;
    mmc_drv_obj.dma_error_flag = FALSE;

    /* during operation ? */
    if ((mem_read32(SD_INFO2) & SD_INFO2_CBSY) != 0U)
    {
        /* wait CMDSEQ = 0 */
        while (loop > 0)
        {
            if ((mem_read32(SD_INFO2) & SD_INFO2_CBSY) == 0U)
            {
                break; /* ready */
            }

            loop--;
            if ((loop == 0) && (retry > 0))
            {
                micro_wait(1000U); /* wait 1ms */
                loop = 10000;
                retry--;
            }
        }
    }

    /* reset */
    mem_write32(SOFT_RST, ( mem_read32(SOFT_RST) & (~SOFT_RST_SDRST) )); /* Soft reset */
    mem_write32(SOFT_RST, ( mem_read32(SOFT_RST) | SOFT_RST_SDRST )); /* Soft reset released */

    /* initialize */
    mem_write32(SD_INFO1, 0x00000000U);
    mem_write32(SD_INFO2, SD_INFO2_CLEAR);
    mem_write32(SD_INFO1_MASK, 0x00000000U); /* all interrupt disable */
    mem_write32(SD_INFO2_MASK, SD_INFO2_CLEAR); /* all interrupt disable */

}

/** read response
 *
 * - Pre-conditions:<BR>
 * Called from emmc_exec_cmd().
 * - Post-conditions:<BR>
 * .
 *
 * param[in,out] *response Response from the card
 * return None.
 */
static void emmc_read_response(uint32_t *response)
{

    uint8_t *p = NULL;

    if (response == NULL)
    {
        return;
    }

    /* read response */
    if (mmc_drv_obj.response_length == EMMC_MAX_RESPONSE_LENGTH)
    {
        /* CSD or CID */
        p = (uint8_t *)(response);
        emmc_little_to_big(p,       ((mem_read32(SD_RSP76) << 8U) | (mem_read32(SD_RSP54) >> 24U)));    /* [127:96] */
        emmc_little_to_big(p + 4U,  ((mem_read32(SD_RSP54) << 8U) | (mem_read32(SD_RSP32) >> 24U)));    /* [95:64]  */
        emmc_little_to_big(p + 8U,  ((mem_read32(SD_RSP32) << 8U) | (mem_read32(SD_RSP10) >> 24U)));    /* [63:32]  */
        emmc_little_to_big(p + 12U, (mem_read32(SD_RSP10) << 8U));                                      /* [31:0]   */
    } else {
        *response = mem_read32(SD_RSP10); /* [39:8] */
    }
}

/** response check
 *
 * - Pre-conditions:<BR>
 * Called from emmc_exec_cmd().
 * - Post-conditions:<BR>
 * .
 *
 * param[in] *response Response from the card
 * param[in] error_mask  Errors to be checked (for R1/R1b response)
 * return error code.
 */
static EMMC_ERROR_CODE emmc_response_check(const uint32_t *response, uint32_t error_mask)
{

    HAL_MEMCARD_RESPONSE_TYPE response_type = (HAL_MEMCARD_RESPONSE_TYPE)(uintptr_t)((uint32_t)(mmc_drv_obj.cmd_info.cmd)
            & (uint32_t)HAL_MEMCARD_RESPONSE_TYPE_MASK);

    if (response == NULL)
    {
        return EMMC_ERR_PARAM;
    }

    if (response_type == HAL_MEMCARD_RESPONSE_NONE)
    {
        return EMMC_SUCCESS;
    }

    /* response check */
    if (response_type <= HAL_MEMCARD_RESPONSE_R1b)
    {
        /* R1 or R1b */
        mmc_drv_obj.current_state = (EMMC_R1_STATE)((*response & EMMC_R1_STATE_MASK) >> EMMC_R1_STATE_SHIFT);
        if ((*response & error_mask) != 0U)
        {
            return EMMC_ERR_CARD_STATUS_BIT;
        }
    } else if (response_type == HAL_MEMCARD_RESPONSE_R4) {
        /* R4 */
        if ((*response & EMMC_R4_STATUS) != 0U)
        {
            return EMMC_ERR_CARD_STATUS_BIT;
        }
    } else {
        ; /* nothing to do. other type does not have status bit */
    }

    return EMMC_SUCCESS;
}

/** brief converts endian from little to big
 *
 * - Pre-conditions:<BR>
 * .
 * - Post-conditions:<BR>
 * .
 *
 * param[in,out] p destination buffer address.
 * param[in] value convert data.(little)
 * return None.
 */
static void emmc_little_to_big(uint8_t *p, uint32_t value)
{
    if (p == NULL)
    {
        return;
    }

    p[0] = (uint8_t)(value >> 24U);
    p[1] = (uint8_t)(value >> 16U);
    p[2] = (uint8_t)(value >> 8U);
    p[3] = (uint8_t)value;
}

/** data transfer with DMA.
 *
 * - Pre-conditions:<BR>
 * Called from emmc_exec_cmd().
 * - Post-conditions:<BR>
 * .
 *
 * return error code.
 */
static void emmc_data_transfer_dma(void)
{
    mmc_drv_obj.during_dma_transfer = TRUE;
    mmc_drv_obj.dma_error_flag = FALSE;

    mem_write32(SD_INFO1_MASK, 0x00000000U);
    mem_write32(SD_INFO2_MASK, ( SD_INFO2_ALL_ERR | SD_INFO2_CLEAR ));

    /* DMAC setting */
    if (mmc_drv_obj.cmd_info.dir == HAL_MEMCARD_WRITE)
    {
        /* transfer complete interrupt enable when write to eMMC */
        mem_write32(DM_CM_INFO1_MASK, ( DM_CM_INFO_MASK_CLEAR | DM_CM_INFO_CH0_ENABLE ));
        /* When writing to eMMC, set only bit 17 to "0". */
        mem_write32(DM_CM_INFO2_MASK, ( DM_CM_INFO2_MASK_CLEAR | DM_CM_INFO2_CH0_ENABLE ));
        /* BUFF --> FIFO */
        mem_write32(DM_CM_DTRAN_MODE, ( DM_CM_DTRAN_MODE_CH0 | DM_CM_DTRAN_MODE_BIT_WIDTH )); /* CH0(downstream), 64-bit width */
    } else {
        /* transfer complete interrupt enable when read from eMMC */
        mem_write32(DM_CM_INFO1_MASK, ( DM_CM_INFO_MASK_CLEAR | DM_CM_INFO_CH1_ENABLE ));
        /* When reading from eMMC, set only bit 16 to "0". */
        mem_write32(DM_CM_INFO2_MASK, ( DM_CM_INFO2_MASK_CLEAR | DM_CM_INFO2_CH1_ENABLE ));
        /* FIFO --> BUFF */
        mem_write32(DM_CM_DTRAN_MODE, ( DM_CM_DTRAN_MODE_CH1 | DM_CM_DTRAN_MODE_BIT_WIDTH )); /* CH1(upstream), 64-bit width */
    }
    mem_write32(DM_DTRAN_ADDR, ( ( (uintptr_t)mmc_drv_obj.buff_address_virtual & DM_DTRAN_ADDR_WRITE_MASK ) )); /* Set address */

    mem_write32(DM_CM_DTRAN_CTRL, DM_CM_DTRAN_CTRL_START); /* DMAC Start */
}

/** wait cmd-cmd 8cycle
 *
 * - Pre-conditions:<BR>
 *
 * - Post-conditions:<BR>
 * .
 *
 * return None.
 */
static void emmc_WaitCmd2Cmd_8Cycle(void)
{
    uint32_t dataL, wait = 0U;

    dataL = mem_read32(SD_CLK_CTRL);
    dataL &= 0x000000FFU;

    switch (dataL)
    {
        case 0xFFU: /* 1/1    10 us wait  (  1/200MHz)*8=  0.04 us(min) */
            wait = 10U;
            break;
        case 0x00U: /* 1/2    10 us wait  (  2/200MHz)*8=  0.08 us(min) */
            wait = 10U;
            break;
        case 0x01U: /* 1/4    10 us wait  (  4/200MHz)*8=  0.16 us(min) */
            wait = 10U;
            break;
        case 0x02U: /* 1/8    10 us wait  (  8/200MHz)*8=  0.32 us(min) */
            wait = 10U;
            break;
        case 0x04U: /* 1/16   10 us wait  ( 16/200MHz)*8=  0.64 us(min) */
            wait = 10U;
            break;
        case 0x08U: /* 1/32   10 us wait  ( 32/200MHz)*8=  1.28 us(min) */
            wait = 10U;
            break;
        case 0x10U: /* 1/64   10 us wait  ( 64/200MHz)*8=  2.56 us(min) */
            wait = 10U;
            break;
        case 0x20U: /* 1/128  10 us wait  (128/200MHz)*8=  5.12 us(min) */
            wait = 10U;
            break;
        case 0x40U: /* 1/256  20 us wait  (256/200MHz)*8= 10.24 us(min) */
            wait = 20U;
            break;
        case 0x80U: /* 1/512  30 us wait  (512/200MHz)*8= 20.48 us(min) */
            wait = 30U;
            break;
        default:
            /* nop */
            break;
    }
    micro_wait(wait);

}

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

