/*******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 */
/******************************************************************************/
/** \file sdmmc_cmd.c
 **
 ** A detailed description is available at
 ** @link SdiocGroup SDIOC description @endlink
 **
 **   - 2018-11-15  CDT First version for Device Driver Library of SDIOC.
 **
 ******************************************************************************/

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "sdmmc_cmd.h"

/**
 *******************************************************************************
 ** \addtogroup SdiocGroup
 ******************************************************************************/
//@{

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/*!< Parameter valid check for SDIOC Instances. */
#define IS_VALID_SDIOC(__SDIOCx__)                                             \
(   (M4_SDIOC1 == (__SDIOCx__))             ||                                 \
    (M4_SDIOC2 == (__SDIOCx__)))

/*!< Parameter valid check for SDIOC Instances. */
#define IS_VALID_SD_TYPE(x)                                                    \
(   (SdmmcHighCapacity     == (x))          ||                                 \
    (SdmmcStanderdCapacity == (x)))

/*!< CMDBSY bit loop count */
#define SDIOC_FLAG_CMDBUSY_LOOPS            (200000ul)

/*!< Command send and response timeout */
#define SDMMC_CMD_TIMEOUT                   (10000ul)

/*!< OCR register bit */
#define SDMMC_SD_OCR_HCS                    (0x40000000ul)   /* High Capacity Support */
#define SDMMC_SD_OCR_BUSY                   (0x80000000ul)   /* Card power up status bit (busy) */


#define SDMMC_BUS_WIDTH_1BIT                (0x00000000ul)
#define SDMMC_BUS_WIDTH_4BIT                (0x00000002ul)

#define SDMMC_CMD8_CHECK_PATTERN            (0x000000AAul)
#define SDMMC_CMD8_VOLTAGE_SUPPLIED         (0x00000100ul)   /* 2.7 - 3.6v*/
#define SDMMC_CMD8_ARGUEMENT                (SDMMC_CMD8_VOLTAGE_SUPPLIED | SDMMC_CMD8_CHECK_PATTERN)
#define SDMMC_ACMD41_VOLTAGE                (SDMMC_SD_VOLT_3_3)
#define SDMMC_ACMD41_HCS                    (1ul << 30u)

/*!< Masks for R1 Response (SD Card Status Register) */
#define SDMMC_R1_AKE_SEQ_ERROR              (0x1ul << 3u )
#define SDMMC_R1_ERASE_RESET                (0x1ul << 13u)
#define SDMMC_R1_CARD_ECC_DISABLED          (0x1ul << 14u)
#define SDMMC_R1_WP_ERASE_SKIP              (0x1ul << 15u)
#define SDMMC_R1_CID_CSD_OVERWRITE          (0x1ul << 16u)
#define SDMMC_R1_STREAM_WRITE_OVERRUN       (0x1ul << 17u)
#define SDMMC_R1_STREAM_READ_UNDERRUN       (0x1ul << 18u)
#define SDMMC_R1_GENERAL_UNKNOWN_ERROR      (0x1ul << 19u)
#define SDMMC_R1_CC_ERROR                   (0x1ul << 20u)
#define SDMMC_R1_CARD_ECC_FAILED            (0x1ul << 21u)
#define SDMMC_R1_ILLEGAL_COMMAND            (0x1ul << 22u)
#define SDMMC_R1_COM_CRC_ERROR              (0x1ul << 23u)
#define SDMMC_R1_LOCK_UNLOCK_FAILED         (0x1ul << 24u)
#define SDMMC_R1_WP_VIOLATION               (0x1ul << 26u)
#define SDMMC_R1_ERASE_PARAM_ERROR          (0x1ul << 27u)
#define SDMMC_R1_ERASE_SEQ_ERROR            (0x1ul << 28u)
#define SDMMC_R1_BLOCK_LEN_ERROR            (0x1ul << 29u)
#define SDMMC_R1_ADDRESS_ERROR              (0x1ul << 30u)
#define SDMMC_R1_OUT_OF_RANGE               (0x1ul << 31u)
#define SDMMC_R1_ERRORS                     (0xFDFFE008ul)

/*!< Masks for R6 Response. */
#define SDMMC_R6_GENERAL_UNKNOWN_ERROR      (0x1ul << 13u)
#define SDMMC_R6_ILLEGAL_CMD                (0x1ul << 14u)
#define SDMMC_R6_COM_CRC_ERROR              (0x1ul << 15u)

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp1(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32CmdResp1,
                                uint32_t u32Timeout);
static en_result_t SDMMC_GetCmdResp1b(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32CmdResp1,
                                uint32_t u32Timeout);
static en_result_t SDMMC_GetCmdResp2(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t u32CID_CSD[4]);
static en_result_t SDMMC_GetCmdResp3(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32OCR);
static en_result_t SDMMC_GetCmdResp6(M4_SDIOC_TypeDef *SDIOx,
                                uint32_t *pu32RCA);
static en_result_t SDMMC_GetCmdResp7(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32IfCond);
static en_result_t SDMMC_WaitBusIdle(M4_SDIOC_TypeDef *SDIOCx);
static en_result_t SDMMC_WaitResponse(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t u32Timeout);
static bool SDMMC_GetCardDetection(M4_SDIOC_TypeDef *SDIOCx);

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 *******************************************************************************
 ** \brief Send the GO_IDLE_STATE command which will reset all cards to idle state
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd0_GoIdleState(M4_SDIOC_TypeDef *SDIOCx)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            stcCmdCfg.u8CmdIndex = CMD0_GO_IDLE_STATE;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdNoRsp;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            /* Set command and argument. */
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_WaitResponse(SDIOCx, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_OP_COND command.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32Argument             Argument used for the command.
 ** \param [in] pu32OCR                 Pointer to buffer which will store OCR content.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd1_SendOpCond(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32Argument, uint32_t *pu32OCR)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            stcCmdCfg.u8CmdIndex = CMD1_SEND_OP_COND;
            stcCmdCfg.u32Argument = u32Argument;
            stcCmdCfg.enRspIndex = SdiocCmdRspR3;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;

            /* Set command and argument. */
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp3(SDIOCx, pu32OCR);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the ALL_SEND_CID command for asking any card to send the CID numbers
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32CID                  Pointer to buffer which will store CID content.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd2_AllSendCID(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32CID[4])
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            stcCmdCfg.u8CmdIndex = CMD2_ALL_SEND_CID;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdRspR2;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;

            /* Set command and argument. */
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp2(SDIOCx, u32CID);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_RELATIVE_ADDR command for asking the card to publish a
 **        new relative address(RCA)
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu32RCA                 Pointer to buffer which will store RCA.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd3_SendRelativeAddr(M4_SDIOC_TypeDef *SDIOCx, uint32_t *pu32RCA)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            stcCmdCfg.u8CmdIndex = CMD3_SEND_RELATIVE_ADDR;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdRspR6;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;

            /* Set command and argument. */
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp6(SDIOCx, pu32RCA);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SWITCH_FUNC command.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32Argument             Argument used for the command.
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd6_SwitchFunc(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32Argument, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            stcCmdCfg.u8CmdIndex = CMD6_SWITCH_FUNC;
            stcCmdCfg.u32Argument = u32Argument;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;

            /* Set command and argument. */
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SELECT/DESELECT_CARD command for toggling a card between the
 **        stand-by and transfer states or between the programming and disconnect states.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32RCA                  Relative Sdcard Address.
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd7_SelectDeselectCard(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32RCA, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD7_SEL_DESEL_CARD;
            stcCmdCfg.u32Argument = (u32RCA << 16u);
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_IF_COND command. Sends SD Memory Card interface
 **        condition, which includes host supply voltage information and asks
 **        the card whether card supports voltage
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu32IfCond              Pointer to buffers for storing interface condition.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd8_SendIfCond(M4_SDIOC_TypeDef *SDIOCx, uint32_t *pu32IfCond)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Send CMD8 to verify SD card interface operating condition */
            /* Argument: - [31:12]: Reserved (shall be set to '0')
            - [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
            - [7:0]: Check Pattern (recommended 0xAA) */
            /* CMD Response: R7 */
            stcCmdCfg.u8CmdIndex = CMD8_SEND_IF_COND;
            stcCmdCfg.u32Argument = SDMMC_CMD8_ARGUEMENT;
            stcCmdCfg.enRspIndex = SdiocCmdRspR7;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);  /* Set command and argument. */
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp7(SDIOCx, pu32IfCond);
                if (enRet == Ok)
                {
                    if((*pu32IfCond & 0xFFu) != SDMMC_CMD8_CHECK_PATTERN)
                    {
                        enRet = Error;
                    }
                }
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_CSD command to addressed card which will sends its
 **        card-specificdata (CSD).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32RCA                  Relative Card Address
 ** \param [in] u32CSD                  Pointer to buffers for storing CSD contents.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd9_SendCSD(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32RCA, uint32_t u32CSD[4])
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD9_SEND_CSD;
            stcCmdCfg.u32Argument = (u32RCA << 16u);
            stcCmdCfg.enRspIndex = SdiocCmdRspR2;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp2(SDIOCx, u32CSD);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_CID command to addressed card which will sends its card
 **        identification(CID).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32RCA                  Relative Card Address
 ** \param [in] u32CID                  Pointer to buffers for storing CID contents.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd10_SendCID(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32RCA, uint32_t u32CID[4])
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD10_SEND_CID;
            stcCmdCfg.u32Argument = (u32RCA << 16u);
            stcCmdCfg.enRspIndex = SdiocCmdRspR2;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp2(SDIOCx, u32CID);
            }
        }
    }

    return enRet;
}

/*******************************************************************************
** \brief Send the STOP_TRANSMISSION command to force the card to stop transmission.
**
** \param [in] SDIOCx                  Pointer to SDIOC instance register base
** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
**
** \retval Ok                          Send successfully.
** \retval Error                       Send unsuccessfully.
**
*******************************************************************************/
en_result_t SDMMC_Cmd12_StopTransmission(M4_SDIOC_TypeDef *SDIOCx, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD12_STOP_TRANSMISSION;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_STATUS to addressed card which will send its status
 **        register contents
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32RCA                  Relative Card Address
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd13_SendStatus(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32RCA, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD13_SEND_STATUS;
            stcCmdCfg.u32Argument = (u32RCA << 16u);
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SET_BLOCKLEN command and check the response
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32BlockLen             Block length
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd16_SetBlockLength(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32BlockLen, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD16_SET_BLOCKLEN;
            stcCmdCfg.u32Argument = u32BlockLen;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the READ_SINGLE_BLOCK command and check the response
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32ReadAddr             Data address
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd17_ReadSingleBlock(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32ReadAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD17_READ_SINGLE_BLOCK;
            stcCmdCfg.u32Argument = u32ReadAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Enable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the READ_MULTIPLE_BLOCK command and check the response
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32ReadAddr             Data address
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd18_ReadMultipleBlock(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32ReadAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD18_READ_MULTIPLE_BLOCK;
            stcCmdCfg.u32Argument = u32ReadAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Enable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the WRITE_BLOCK command and check the response
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32WriteAddr            Data address
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd24_WriteSingleBlock(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32WriteAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD24_WRITE_SINGLE_BLOCK;
            stcCmdCfg.u32Argument = u32WriteAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Enable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the WRITE_MULTIPLE_BLOCK command and check the response
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32WriteAddr            Data address
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd25_WriteMultipleBlock(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32WriteAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD25_WRITE_MULTIPLE_BLOCK;
            stcCmdCfg.u32Argument = u32WriteAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Enable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the ERASE_WR_BLK_START command for setting the address of the
 **        first write block to be erased.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32StartAddr            The start address will be erased
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd32_EraseWrBlkStart(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32StartAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD32_ERASE_WR_BLK_START;
            stcCmdCfg.u32Argument = u32StartAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the ERASE_WR_BLK_END command for setting the address of the
 **        last write block of the continuous range to be erased.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32EndAddr              The last address will be erased
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd33_EraseWrBlkEnd(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32EndAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD33_ERASE_WR_BLK_END;
            stcCmdCfg.u32Argument = u32EndAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the ERASE_GRP_START command and check the response.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32StartAddr            The start address will be erased
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd35_SetEraseStartAddr(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32StartAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD35_ERASE_GRP_START;
            stcCmdCfg.u32Argument = u32StartAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the ERASE_GRP_END command and check the response.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32EndAddr              The end address will be erased
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd36_SetEraseEndAddr(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32EndAddr, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD36_ERASE_GRP_END;
            stcCmdCfg.u32Argument = u32EndAddr;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the ERASE command for erasing all previously selected write blocks.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd38_Erase(M4_SDIOC_TypeDef *SDIOCx, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            SDIOC_SetDataTimeout(SDIOCx, SdiocDtoSdclk_2_27);

            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD38_ERASE;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1b;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1b(SDIOCx, pu32Resp1, 50u * SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the APP_CMD Command to interpret the following command as an
 **        application-specific command(ACMD).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32Argument             Command argument(RCA)
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Cmd55_AppCmd(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32Argument, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = CMD55_APP_CMD;
            stcCmdCfg.u32Argument = u32Argument;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SET_BUS_WIDTH(ACMD6) Command to set bus width.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32BusWidth             The data bus width, '00'= 1bit or '10' = 4bits bus
 ** \arg SDMMC_BUS_WIDTH_1BIT           1 bits bus
 ** \arg SDMMC_BUS_WIDTH_4BIT           4 bits bus
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Acmd6_SetBusWidth(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32BusWidth, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = ACMD6_APP_SD_SET_BUSWIDTH;
            stcCmdCfg.u32Argument = u32BusWidth;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SD_STATUS(ACMD13) Command to ask the Status Register.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Acmd13_SdStatus(M4_SDIOC_TypeDef *SDIOCx, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = ACMD13_SD_APP_STATUS;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SD_SEND_OP_COND(ACMD41) command to negotiate the operation
 **        voltage range and get OCR.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32SdType               Sdcard type
 ** \arg SdmmcHighCapacity              SD memory card: Standard capacity
 ** \arg SdmmcHighCapacity              SD memory card: High capacity
 ** \param [in] pu32OCR                 Pointer to the variable that will contain
 **                                     the OCR register contents.
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Acmd41_SdSendOpCond(M4_SDIOC_TypeDef *SDIOCx, uint32_t u32SdType, uint32_t *pu32OCR)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if ((IS_VALID_SDIOC(SDIOCx)) && (NULL != pu32OCR))
    {
        DDL_ASSERT(IS_VALID_SD_TYPE(u32SdType));

        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = ACMD41_SD_SEND_OP_COND;
            stcCmdCfg.u32Argument = SDMMC_ACMD41_VOLTAGE | u32SdType;
            stcCmdCfg.enRspIndex = SdiocCmdRspR3;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp3(SDIOCx, pu32OCR);
                if (enRet == Ok)
                {
                    if (!(*pu32OCR & SDMMC_SD_OCR_BUSY))
                    {
                        enRet = ErrorOperationInProgress;
                    }
                }
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Send the SEND_SCR(ACMD51) command to read the SD Configuration Register(SCR).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [out] pu32Resp1              Pointer to response data buffer
 **
 ** \retval Ok                          Send successfully.
 ** \retval Error                       Send unsuccessfully.
 **
 ******************************************************************************/
en_result_t SDMMC_Acmd51_SendSCR(M4_SDIOC_TypeDef *SDIOCx, uint32_t *pu32Resp1)
{
    en_result_t enRet = ErrorInvalidParameter;
    stc_sdioc_cmd_cfg_t stcCmdCfg = {0};

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitBusIdle(SDIOCx);
        if (enRet == Ok)
        {
            /* Set command and argument. */
            stcCmdCfg.u8CmdIndex = ACMD51_SD_APP_SEND_SCR;
            stcCmdCfg.u32Argument = 0u;
            stcCmdCfg.enRspIndex = SdiocCmdRspR1;
            stcCmdCfg.enDataPresentEnable = Disable;
            stcCmdCfg.enCmdType = SdiocCmdNormal;
            enRet = SDIOC_SendCommand(SDIOCx, &stcCmdCfg);
            if (enRet == Ok)
            {
                /* Check for error conditions */
                enRet = SDMMC_GetCmdResp1(SDIOCx, pu32Resp1, SDMMC_CMD_TIMEOUT);
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get Response R1(normal response command).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [out] pu32CmdResp1           received response value
 ** \param [in] u32Timeout              timeout for waiting SDIOC idle
 **
 ** \retval Ok                          Get successfully.
 ** \retval Error                       Get unsuccessfully.
 ** \retval ErrorInvalidParameter       pu32CmdResp1 == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp1(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32CmdResp1,
                                uint32_t u32Timeout)
{
    uint32_t u32R1 = 0u;
    en_result_t enRet = ErrorInvalidParameter;

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitResponse(SDIOCx, u32Timeout);
        if (enRet == Ok)
        {
            /* received response, retrieve it for analysis  */
            u32R1 = SDIOC_GetResponse(SDIOCx, SdiocRegResp01);
            if (u32R1 & SDMMC_R1_ERRORS)
            {
                enRet = Error;
            }

            if (pu32CmdResp1 != NULL)
            {
                *pu32CmdResp1 = u32R1;
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get Response R1(normal response command) with busy signal.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [out] pu32CmdResp1           received response value
 ** \param [in] u32Timeout              timeout for waiting SDIOC idle
 **
 ** \retval Ok                          Get successfully.
 ** \retval Error                       Get unsuccessfully.
 ** \retval ErrorInvalidParameter       pu32CmdResp1 == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp1b(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32CmdResp1,
                                uint32_t u32Timeout)
{
    uint32_t u32R1 = 0u;
    en_result_t enRet = ErrorInvalidParameter;
    __IO uint32_t u32TmpCnt = u32Timeout;

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = SDMMC_WaitResponse(SDIOCx, u32Timeout);
        if (enRet == Ok)
        {
            do
            {
                if (u32TmpCnt-- == 0u)
                {
                    enRet = ErrorTimeout;
                }
            } while (Reset == SDIOC_GetStatus(SDIOCx, SdiocData0PinLvl));

            if (enRet == Ok)
            {
                /* received response, retrieve it for analysis  */
                u32R1 = SDIOC_GetResponse(SDIOCx, SdiocRegResp01);
                if (u32R1 & SDMMC_R1_ERRORS)
                {
                    enRet = Error;
                }

                if (pu32CmdResp1 != NULL)
                {
                    *pu32CmdResp1 = u32R1;
                }
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get Response R2(CID, CSD register).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32CID_CSD              Pointer to the variable that will contain the
 **                                     CID or CSD register contents
 **
 ** \retval Ok                          Get successfully.
 ** \retval Error                       Get unsuccessfully.
 ** \retval ErrorInvalidParameter       u32CID_CSD == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp2(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t u32CID_CSD[4])
{
    en_result_t enRet = ErrorInvalidParameter;

    if ((IS_VALID_SDIOC(SDIOCx)) && (NULL != u32CID_CSD))
    {
        enRet = SDMMC_WaitResponse(SDIOCx, SDMMC_CMD_TIMEOUT);
        if (enRet == Ok)
        {
            /* received response */
            u32CID_CSD[0] = SDIOC_GetResponse(SDIOCx, SdiocRegResp01);
            u32CID_CSD[1] = SDIOC_GetResponse(SDIOCx, SdiocRegResp23);
            u32CID_CSD[2] = SDIOC_GetResponse(SDIOCx, SdiocRegResp45);
            u32CID_CSD[3] = SDIOC_GetResponse(SDIOCx, SdiocRegResp67);
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get Response R3(OCR register).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu32OCR                 Pointer to the variable that will contain
 **                                     the OCR register contents.
 **
 ** \retval Ok                          Get successfully.
 ** \retval Error                       Get unsuccessfully.
 ** \retval ErrorInvalidParameter       pu32OCR == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp3(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32OCR)
{
    en_result_t enRet = ErrorInvalidParameter;

    if((IS_VALID_SDIOC(SDIOCx)) && (NULL != pu32OCR))
    {
        enRet = SDMMC_WaitResponse(SDIOCx, SDMMC_CMD_TIMEOUT);
        if (enRet == Ok)
        {
            /* received response, retrieve it for analysis  */
            *pu32OCR = SDIOC_GetResponse(SDIOCx, SdiocRegResp01);
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get Response R6(Published RCA response).
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu32RCA                 Pointer to the variable that will contain
 **                                     published RCA number
 **
 ** \retval Ok                          Get successfully.
 ** \retval Error                       Get unsuccessfully.
 ** \retval ErrorInvalidParameter       pu32RCA == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp6(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32RCA)
{
    uint32_t u32R6 = 0ul;
    en_result_t enRet = ErrorInvalidParameter;

    if ((IS_VALID_SDIOC(SDIOCx)) && (NULL != pu32RCA))
    {
        enRet = SDMMC_WaitResponse(SDIOCx, SDMMC_CMD_TIMEOUT);
        if (enRet == Ok)
        {
            /* received response, retrieve it. */
            u32R6 = SDIOC_GetResponse(SDIOCx, SdiocRegResp01);
            if (!(u32R6 & (SDMMC_R6_GENERAL_UNKNOWN_ERROR | SDMMC_R6_ILLEGAL_CMD | SDMMC_R6_COM_CRC_ERROR)))
            {
                *pu32RCA =  (u32R6 >> 16);
            }
            else
            {
                enRet = Error;
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get Response R7.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu32IfCond              Pointer to the variable that will contain
 **                                     interface condition.
 **
 ** \retval Ok                          Get successfully.
 ** \retval Error                       Get unsuccessfully.
 ** \retval ErrorInvalidParameter       pu32IfCond == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_GetCmdResp7(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t *pu32IfCond)
{
    en_result_t enRet = ErrorInvalidParameter;

    if ((IS_VALID_SDIOC(SDIOCx)) && (NULL != pu32IfCond))
    {
        enRet = SDMMC_WaitResponse(SDIOCx, SDMMC_CMD_TIMEOUT);
        if (enRet == Ok)
        {
            *pu32IfCond = SDIOC_GetResponse(SDIOCx, SdiocRegResp01);
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Wait for bus line idle
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 **
 ** \retval Ok                          Command&Data line is free within a certain time
 ** \retval ErrorTimeout                Command&Data line is still busy after a certain time.
 ** \retval ErrorInvalidParameter       SDIOCx == NULL
 **
 ******************************************************************************/
static en_result_t SDMMC_WaitBusIdle(M4_SDIOC_TypeDef *SDIOCx)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint32_t u32Retry = SDIOC_FLAG_CMDBUSY_LOOPS;
    en_flag_status_t enInhibitCmdStatus = Set;
    en_flag_status_t enInhibitDataStatus = Set;

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = ErrorTimeout;
        while (u32Retry-- > 0ul)
        {
            enInhibitCmdStatus = SDIOC_GetStatus(SDIOCx, SdiocCommandInhibitCmd);
            enInhibitDataStatus = SDIOC_GetStatus(SDIOCx, SdiocCommandInhibitData);
            if ((Reset == enInhibitCmdStatus) || (Reset == enInhibitDataStatus))
            {
                enRet = Ok;
                break;
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Wait response
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] u32Timeout              timeout times
 **
 ** \retval Ok                          Respond normal
 ** \retval ErrorTimeout                Respond timeout.
 ** \retval ErrorInvalidParameter       SDIOCx == NULL
 ** \retval ErrorAccessRights           Card is removed.
 **
 ******************************************************************************/
static en_result_t SDMMC_WaitResponse(M4_SDIOC_TypeDef *SDIOCx,
                                uint32_t u32Timeout)
{
    /* 8 is the number of required instructions cycles for the below loop statement.
    The Timeout is expressed in ms */
    __IO uint32_t u32Count = u32Timeout * (SystemCoreClock / 8u /1000u);
    en_flag_status_t enErrorInt = Set;
    en_flag_status_t enCommandComplete = Set;
    en_result_t enRet = ErrorInvalidParameter;

    if (IS_VALID_SDIOC(SDIOCx))
    {
        enRet = Ok;
        do
        {
            if (u32Count-- == 0u)
            {
                enRet = ErrorTimeout;
                break;
            }
            enErrorInt = SDIOC_GetNormalIrqFlag(SDIOCx, SdiocErrorInt);
            enCommandComplete = SDIOC_GetNormalIrqFlag(SDIOCx, SdiocCommandComplete);
        } while ((Reset == enErrorInt) && (Reset == enCommandComplete));

        if (enRet == Ok)
        {
            if (Set == SDIOC_GetNormalIrqFlag(SDIOCx, SdiocErrorInt))
            {
                enRet = Error;
            }
            else
            {
                if (false == SDMMC_GetCardDetection(SDIOCx))
                {
                    enRet = ErrorAccessRights;
                }
                else
                {
                    if (Set == SDIOC_GetNormalIrqFlag(SDIOCx, SdiocCommandComplete))
                    {
                        SDIOC_ClearNormalIrqFlag(SDIOCx, SdiocCommandComplete);
                    }

                    if (Set == SDIOC_GetNormalIrqFlag(SDIOCx, SdiocTransferComplete))
                    {
                        SDIOC_ClearNormalIrqFlag(SDIOCx, SdiocTransferComplete);
                    }
                }
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get card insertion status
 **
 ** This function checks card is inserted or not.
 **
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 **
 ** \retval true                        Card is inserted
 ** \retval false                       Card is removed
 **
 ******************************************************************************/
static bool SDMMC_GetCardDetection(M4_SDIOC_TypeDef *SDIOCx)
{
    bool bRet = false;
    __IO uint32_t u32Count = 5000ul * (SystemCoreClock / 8ul /1000ul);

    /* Wait until card is stable */
    do
    {
        if (Set == SDIOC_GetStatus(SDIOCx, SdiocCardStateStable))
        {
            break;
        }
    } while (u32Count--);

    if (u32Count != 0ul)
    {
        bRet = true;//(Set == SDIOC_GetStatus(SDIOCx, SdiocCardInserted)) ? true : false;
    }

    return bRet;
}

//@} // SdiocGroup

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
