/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_sdif.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.sdif"
#endif

/* Typedef for interrupt handler. */
typedef void (*sdif_isr_t)(SDIF_Type *base, sdif_handle_t *handle);

/*! @brief convert the name here, due to RM use SDIO */
#define SDIF_DriverIRQHandler SDIO_DriverIRQHandler
/*! @brief define the controller support sd/sdio card version 2.0 */
#define SDIF_SUPPORT_SD_VERSION (0x20)
/*! @brief define the controller support mmc card version 4.4 */
#define SDIF_SUPPORT_MMC_VERSION (0x44)

#ifndef SDIF_TIMEOUT_VALUE
/*! @brief define the timeout counter, used to polling the start bit auto-cleared when sending clock sync command */
#define SDIF_TIMEOUT_VALUE (~0U)
#endif

#ifndef SDIF_RESET_TIMEOUT_VALUE
/*! @brief define the reset timeout counter, two AHB clock cycle, the reset should auto-cleared. */
#define SDIF_RESET_TIMEOUT_VALUE (100U)
#endif

/*! @brief this value can be any value */
#define SDIF_POLL_DEMAND_VALUE (0xFFU)
/*! @brief DMA descriptor buffer1 size */
#define SDIF_DMA_DESCRIPTOR_BUFFER1_SIZE(x) ((x)&0x1FFFU)
/*! @brief DMA descriptor buffer2 size */
#define SDIF_DMA_DESCRIPTOR_BUFFER2_SIZE(x) (((x)&0x1FFFU) << 13U)
/*! @brief RX water mark value */
#define SDIF_RX_WATERMARK (15U)
/*! @brief TX water mark value */
#define SDIF_TX_WATERMARK (16U)
/*! @brief check flag avalibility */
#define IS_SDIF_FLAG_SET(reg, flag) (((reg) & ((uint32_t)flag)) != 0UL)
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*!
 * @brief Get the instance.
 *
 * @param base SDIF peripheral base address.
 * @return Instance number.
 */
static uint32_t SDIF_GetInstance(SDIF_Type *base);

/*
 * @brief config the SDIF interface before transfer between the card and host
 * @param SDIF base address
 * @param transfer config structure
 * @param enDMA DMA enable flag
 */
static status_t SDIF_TransferConfig(SDIF_Type *base, sdif_transfer_t *transfer, bool enDMA);

/*
 * @brief wait the command done function and check error status
 * @param SDIF base address
 * @param command config structure
 */
static status_t SDIF_WaitCommandDone(SDIF_Type *base, sdif_command_t *command);

/*
 * @brief transfer data in a blocking way
 * @param SDIF base address
 * @param data config structure
 * @param indicate current transfer mode:DMA or polling
 */
static status_t SDIF_TransferDataBlocking(SDIF_Type *base, sdif_data_t *data, bool isDMA);

/*
 * @brief read the command response
 * @param SDIF base address
 * @param sdif command pointer
 */
static status_t SDIF_ReadCommandResponse(SDIF_Type *base, sdif_command_t *command);

/*
 * @brief handle transfer command interrupt
 * @param SDIF base address
 * @param sdif handle
 * @param interrupt mask flags
 */
static void SDIF_TransferHandleCommand(SDIF_Type *base, sdif_handle_t *handle, uint32_t interruptFlags);

/*
 * @brief handle transfer data interrupt
 * @param SDIF base address
 * @param sdif handle
 * @param interrupt mask flags
 */
static void SDIF_TransferHandleData(SDIF_Type *base, sdif_handle_t *handle, uint32_t interruptFlags);

/*
 * @brief handle DMA transfer
 * @param SDIF base address
 * @param sdif handle
 * @param interrupt mask flag
 */
static void SDIF_TransferHandleDMA(SDIF_Type *base, sdif_handle_t *handle, uint32_t interruptFlags);

/*
 * @brief driver IRQ handler
 * @param SDIF base address
 * @param sdif handle
 */
static void SDIF_TransferHandleIRQ(SDIF_Type *base, sdif_handle_t *handle);

/*
 * @brief read data port
 * @param SDIF base address
 * @param sdif data
 * @param the number of data been transferred
 */
static uint32_t SDIF_ReadDataPort(SDIF_Type *base, sdif_data_t *data, uint32_t transferredWords);

/*
 * @brief write data port
 * @param SDIF base address
 * @param sdif data
 * @param the number of data been transferred
 */
static uint32_t SDIF_WriteDataPort(SDIF_Type *base, sdif_data_t *data, uint32_t transferredWords);

/*
 * @brief read data by blocking way
 * @param SDIF base address
 * @param sdif data
 */
static status_t SDIF_ReadDataPortBlocking(SDIF_Type *base, sdif_data_t *data);

/*
 * @brief write data by blocking way
 * @param SDIF base address
 * @param sdif data
 */
static status_t SDIF_WriteDataPortBlocking(SDIF_Type *base, sdif_data_t *data);

/*
 * @brief handle sdio interrupt
 * This function will call the SDIO interrupt callback
 * @param SDIF base address
 * @param SDIF handle
 */
static void SDIF_TransferHandleSDIOInterrupt(SDIF_Type *base, sdif_handle_t *handle);

/*
 * @brief handle card detect
 * This function will call the cardInserted callback
 * @param SDIF base addres
 * @param SDIF handle
 */
static void SDIF_TransferHandleCardDetect(SDIF_Type *base, sdif_handle_t *handle);

/*
 * @brief set command register
 * This api include polling the status of the bit START_COMMAND, if 0 used as timeout value, then this function
 * will return directly without polling the START_CMD status.
 *
 * @param base SDIF base addres
 * @param cmdIndex command index
 * @param argument command argument
 * @param timeout timeout value
 *
 * @return kStatus_Success, kStatus_SDIF_SyncCmdTimeout
 */
static status_t SDIF_SetCommandRegister(SDIF_Type *base, uint32_t cmdIndex, uint32_t argument, uint32_t timeout);

/*
 * @brief SDIF sync clock command function.
 *
 * This function will try to recovery the host while sending clock command failed.
 *
 * @param base SDIF base addres
 *
 * @return kStatus_Success, kStatus_SDIF_SyncCmdTimeout
 */
static status_t SDIF_SyncClockCommand(SDIF_Type *base);

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*! @brief SDIF internal handle pointer array */
static sdif_handle_t *s_sdifHandle[FSL_FEATURE_SOC_SDIF_COUNT];

/*! @brief SDIF base pointer array */
static SDIF_Type *const s_sdifBase[] = SDIF_BASE_PTRS;

/*! @brief SDIF IRQ name array */
static const IRQn_Type s_sdifIRQ[] = SDIF_IRQS;

/* SDIF ISR for transactional APIs. */
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
static sdif_isr_t s_sdifIsr = (sdif_isr_t)DefaultISR;
#else
static sdif_isr_t s_sdifIsr;
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/
static uint32_t SDIF_GetInstance(SDIF_Type *base)
{
    uint8_t instance = 0U;

    while ((instance < ARRAY_SIZE(s_sdifBase)) && (s_sdifBase[instance] != base))
    {
        instance++;
    }

    assert(instance < ARRAY_SIZE(s_sdifBase));

    return instance;
}

static status_t SDIF_TransferConfig(SDIF_Type *base, sdif_transfer_t *transfer, bool enDMA)
{
    sdif_command_t *command = transfer->command;
    sdif_data_t *data       = transfer->data;

    if ((command == NULL) || ((data != NULL) && (data->blockSize > SDIF_BLKSIZ_BLOCK_SIZE_MASK)))
    {
        return kStatus_SDIF_InvalidArgument;
    }

    if (data != NULL)
    {
        /* config the block size register ,the block size maybe smaller than FIFO
         depth, will test on the board */
        base->BLKSIZ = SDIF_BLKSIZ_BLOCK_SIZE(data->blockSize);
        /* config the byte count register */
        base->BYTCNT = SDIF_BYTCNT_BYTE_COUNT(data->blockSize * data->blockCount);

        command->flags |= (uint32_t)kSDIF_DataExpect; /* need transfer data flag */

        if (data->txData != NULL)
        {
            command->flags |= (uint32_t)kSDIF_DataWriteToCard; /* data transfer direction */
        }
        else
        {
            /* config the card read threshold,enable the card read threshold */
            if (data->blockSize <= (SDIF_FIFO_COUNT * sizeof(uint32_t)))
            {
                base->CARDTHRCTL = SDIF_CARDTHRCTL_CARDRDTHREN_MASK | SDIF_CARDTHRCTL_CARDTHRESHOLD(data->blockSize);
            }
            else
            {
                base->CARDTHRCTL &= ~SDIF_CARDTHRCTL_CARDRDTHREN_MASK;
            }
        }

        if (data->streamTransfer)
        {
            command->flags |=
                (uint32_t)kSDIF_DataStreamTransfer; /* indicate if use stream transfer or block transfer  */
        }

        if ((data->enableAutoCommand12) &&
            (data->blockCount > 1UL)) /* indicate if auto stop will send after the data transfer done */
        {
            command->flags |= (uint32_t)kSDIF_DataTransferAutoStop;
        }
    }
    /* R2 response length long */
    if (command->responseType == (uint32_t)kCARD_ResponseTypeR2)
    {
        command->flags |= ((uint32_t)kSDIF_CmdCheckResponseCRC | (uint32_t)kSDIF_CmdResponseLengthLong |
                           (uint32_t)kSDIF_CmdResponseExpect);
    }
    else if ((command->responseType == (uint32_t)kCARD_ResponseTypeR3) ||
             (command->responseType == (uint32_t)kCARD_ResponseTypeR4))
    {
        command->flags |= (uint32_t)kSDIF_CmdResponseExpect; /* response R3 do not check Response CRC */
    }
    else
    {
        if (command->responseType != (uint32_t)kCARD_ResponseTypeNone)
        {
            command->flags |= ((uint32_t)kSDIF_CmdCheckResponseCRC | (uint32_t)kSDIF_CmdResponseExpect);
        }
    }

    if (command->type == (uint32_t)kCARD_CommandTypeAbort)
    {
        command->flags |= (uint32_t)kSDIF_TransferStopAbort;
    }

    /* wait pre-transfer complete */
    command->flags |= (uint32_t)kSDIF_WaitPreTransferComplete | (uint32_t)kSDIF_CmdDataUseHoldReg;

    /* handle interrupt and status mask */
    if (data != NULL)
    {
        SDIF_ClearInterruptStatus(base, (uint32_t)kSDIF_AllInterruptStatus);
        if (enDMA)
        {
            SDIF_ClearInternalDMAStatus(base, kSDIF_DMAAllStatus);
            SDIF_EnableDmaInterrupt(base, kSDIF_DMAAllStatus);
            SDIF_EnableInterrupt(base, (uint32_t)kSDIF_CommandTransferStatus);
        }
        else
        {
            SDIF_EnableInterrupt(base, (uint32_t)kSDIF_CommandTransferStatus | (uint32_t)kSDIF_DataTransferStatus);
        }
    }
    else
    {
        SDIF_ClearInterruptStatus(base, kSDIF_CommandTransferStatus);
        SDIF_EnableInterrupt(base, kSDIF_CommandTransferStatus);
    }

    return kStatus_Success;
}

static status_t SDIF_ReadCommandResponse(SDIF_Type *base, sdif_command_t *command)
{
    /* check if command exist,if not, do not read the response */
    if (NULL != command)
    {
        /* read response */
        command->response[0U] = base->RESP[0U];
        if (command->responseType == (uint32_t)kCARD_ResponseTypeR2)
        {
            command->response[1U] = base->RESP[1U];
            command->response[2U] = base->RESP[2U];
            command->response[3U] = base->RESP[3U];
        }

        if ((command->responseErrorFlags != 0U) && ((command->responseType == (uint32_t)kCARD_ResponseTypeR1) ||
                                                    (command->responseType == (uint32_t)kCARD_ResponseTypeR1b) ||
                                                    (command->responseType == (uint32_t)kCARD_ResponseTypeR6) ||
                                                    (command->responseType == (uint32_t)kCARD_ResponseTypeR5)))
        {
            if (((command->responseErrorFlags) & (command->response[0U])) != 0UL)
            {
                return kStatus_SDIF_ResponseError;
            }
        }
    }

    return kStatus_Success;
}

static status_t SDIF_WaitCommandDone(SDIF_Type *base, sdif_command_t *command)
{
    uint32_t status = 0U;

    do
    {
        status = SDIF_GetInterruptStatus(base);
    } while ((status & (uint32_t)kSDIF_CommandDone) != (uint32_t)kSDIF_CommandDone);
    /* clear interrupt status flag first */
    SDIF_ClearInterruptStatus(base, status & (uint32_t)kSDIF_CommandTransferStatus);
    if ((status & ((uint32_t)kSDIF_ResponseError | (uint32_t)kSDIF_ResponseCRCError | (uint32_t)kSDIF_ResponseTimeout |
                   (uint32_t)kSDIF_HardwareLockError)) != 0UL)
    {
        return kStatus_SDIF_SendCmdFail;
    }
    else
    {
        return SDIF_ReadCommandResponse(base, command);
    }
}

static status_t SDIF_SetCommandRegister(SDIF_Type *base, uint32_t cmdIndex, uint32_t argument, uint32_t timeout)
{
    uint32_t syncTimeout = timeout;

    base->CMDARG = argument;
    base->CMD    = cmdIndex | SDIF_CMD_START_CMD_MASK;

    while ((base->CMD & SDIF_CMD_START_CMD_MASK) == SDIF_CMD_START_CMD_MASK)
    {
        if (timeout == 0U)
        {
            break;
        }

        if (0UL == syncTimeout)
        {
            return kStatus_SDIF_SyncCmdTimeout;
        }

        --syncTimeout;
    }

    return kStatus_Success;
}

/*!
 * brief SDIF release the DMA descriptor to DMA engine
 * this function should be called when DMA descriptor unavailable status occurs
 * param base SDIF peripheral base address.
 * param sdif DMA config pointer
 */
status_t SDIF_ReleaseDMADescriptor(SDIF_Type *base, sdif_dma_config_t *dmaConfig)
{
    assert(NULL != dmaConfig);
    assert(NULL != dmaConfig->dmaDesBufferStartAddr);

    sdif_dma_descriptor_t *dmaDesAddr;
    uint32_t *tempDMADesBuffer = dmaConfig->dmaDesBufferStartAddr;
    uint32_t dmaDesBufferSize  = 0UL;

    dmaDesAddr = (sdif_dma_descriptor_t *)(uint32_t)tempDMADesBuffer;

    /* chain descriptor mode */
    if (dmaConfig->mode == kSDIF_ChainDMAMode)
    {
        while (((dmaDesAddr->dmaDesAttribute & SDIF_DMA_DESCRIPTOR_DATA_BUFFER_END_FLAG) !=
                SDIF_DMA_DESCRIPTOR_DATA_BUFFER_END_FLAG) &&
               (dmaDesBufferSize < dmaConfig->dmaDesBufferLen * sizeof(uint32_t)))
        {
            /* set the OWN bit */
            dmaDesAddr->dmaDesAttribute |= SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG;
            dmaDesAddr++;
            dmaDesBufferSize += sizeof(sdif_dma_descriptor_t);
        }
        /* if access dma des address overflow, return fail */
        if (dmaDesBufferSize > dmaConfig->dmaDesBufferLen * sizeof(uint32_t))
        {
            return kStatus_Fail;
        }
        dmaDesAddr->dmaDesAttribute |= SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG;
    }
    /* dual descriptor mode */
    else
    {
        while (((dmaDesAddr->dmaDesAttribute & SDIF_DMA_DESCRIPTOR_DESCRIPTOR_END_FLAG) !=
                SDIF_DMA_DESCRIPTOR_DESCRIPTOR_END_FLAG) &&
               (dmaDesBufferSize < dmaConfig->dmaDesBufferLen * sizeof(uint32_t)))
        {
            dmaDesAddr = (sdif_dma_descriptor_t *)(uint32_t)tempDMADesBuffer;
            dmaDesAddr->dmaDesAttribute |= SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG;
            tempDMADesBuffer += dmaConfig->dmaDesSkipLen;
        }
        /* if access dma des address overflow, return fail */
        if (dmaDesBufferSize > dmaConfig->dmaDesBufferLen * sizeof(uint32_t))
        {
            return kStatus_Fail;
        }
        dmaDesAddr->dmaDesAttribute |= SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG;
    }
    /* reload DMA descriptor */
    base->PLDMND = SDIF_POLL_DEMAND_VALUE;

    return kStatus_Success;
}

static uint32_t SDIF_ReadDataPort(SDIF_Type *base, sdif_data_t *data, uint32_t transferredWords)
{
    uint32_t i;
    uint32_t totalWords;
    uint32_t wordsCanBeRead; /* The words can be read at this time. */
    uint32_t readWatermark = ((base->FIFOTH & SDIF_FIFOTH_RX_WMARK_MASK) >> SDIF_FIFOTH_RX_WMARK_SHIFT);

    if ((base->CTRL & SDIF_CTRL_USE_INTERNAL_DMAC_MASK) == 0UL)
    {
        if (data->blockSize % sizeof(uint32_t) != 0U)
        {
            data->blockSize +=
                sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
        }

        totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));

        /* If watermark level is equal or bigger than totalWords, transfers totalWords data. */
        if (readWatermark >= totalWords)
        {
            wordsCanBeRead = totalWords;
        }
        /* If watermark level is less than totalWords and left words to be sent is equal or bigger than readWatermark,
        transfers watermark level words. */
        else if ((readWatermark < totalWords) && ((totalWords - transferredWords) >= readWatermark))
        {
            wordsCanBeRead = readWatermark;
        }
        /* If watermark level is less than totalWords and left words to be sent is less than readWatermark, transfers
        left
        words. */
        else
        {
            wordsCanBeRead = (totalWords - transferredWords);
        }

        i = 0U;
        while (i < wordsCanBeRead)
        {
            data->rxData[transferredWords++] = base->FIFO[i];
            i++;
        }
    }

    return transferredWords;
}

static uint32_t SDIF_WriteDataPort(SDIF_Type *base, sdif_data_t *data, uint32_t transferredWords)
{
    uint32_t i;
    uint32_t totalWords;
    uint32_t wordsCanBeWrite; /* The words can be read at this time. */
    uint32_t writeWatermark = ((base->FIFOTH & SDIF_FIFOTH_TX_WMARK_MASK) >> SDIF_FIFOTH_TX_WMARK_SHIFT);

    if ((base->CTRL & SDIF_CTRL_USE_INTERNAL_DMAC_MASK) == 0UL)
    {
        if (data->blockSize % sizeof(uint32_t) != 0U)
        {
            data->blockSize +=
                sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
        }

        totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));

        /* If watermark level is equal or bigger than totalWords, transfers totalWords data. */
        if (writeWatermark >= totalWords)
        {
            wordsCanBeWrite = totalWords;
        }
        /* If watermark level is less than totalWords and left words to be sent is equal or bigger than writeWatermark,
        transfers watermark level words. */
        else if ((writeWatermark < totalWords) && ((totalWords - transferredWords) >= writeWatermark))
        {
            wordsCanBeWrite = writeWatermark;
        }
        /* If watermark level is less than totalWords and left words to be sent is less than writeWatermark, transfers
        left
        words. */
        else
        {
            wordsCanBeWrite = (totalWords - transferredWords);
        }

        i = 0U;
        while (i < wordsCanBeWrite)
        {
            base->FIFO[i] = data->txData[transferredWords++];
            i++;
        }
    }

    return transferredWords;
}

static status_t SDIF_ReadDataPortBlocking(SDIF_Type *base, sdif_data_t *data)
{
    uint32_t totalWords;
    uint32_t transferredWords = 0U;
    status_t error            = kStatus_Success;
    uint32_t status;
    bool transferOver = false;

    if (data->blockSize % sizeof(uint32_t) != 0UL)
    {
        data->blockSize +=
            sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
    }

    totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));

    while ((transferredWords < totalWords) && (error == kStatus_Success))
    {
        /* wait data transfer complete or reach RX watermark */
        do
        {
            status = SDIF_GetInterruptStatus(base);
            if (IS_SDIF_FLAG_SET(status, kSDIF_DataTransferError))
            {
                if (!(data->enableIgnoreError))
                {
                    error = kStatus_Fail;
                    break;
                }
            }
        } while (!IS_SDIF_FLAG_SET(status, ((uint32_t)kSDIF_DataTransferOver | (uint32_t)kSDIF_ReadFIFORequest)) &&
                 (!transferOver));

        if (IS_SDIF_FLAG_SET(status, kSDIF_DataTransferOver))
        {
            transferOver = true;
        }

        if (error == kStatus_Success)
        {
            transferredWords = SDIF_ReadDataPort(base, data, transferredWords);
        }

        /* clear interrupt status */
        SDIF_ClearInterruptStatus(base, status);
    }

    return error;
}

static status_t SDIF_WriteDataPortBlocking(SDIF_Type *base, sdif_data_t *data)
{
    uint32_t totalWords;
    uint32_t transferredWords = 0U;
    status_t error            = kStatus_Success;
    uint32_t status;

    if (data->blockSize % sizeof(uint32_t) != 0UL)
    {
        data->blockSize +=
            sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
    }

    totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));

    while ((transferredWords < totalWords) && (error == kStatus_Success))
    {
        /* wait data transfer complete or reach RX watermark */
        do
        {
            status = SDIF_GetInterruptStatus(base);
            if (IS_SDIF_FLAG_SET(status, kSDIF_DataTransferError))
            {
                if (!(data->enableIgnoreError))
                {
                    error = kStatus_Fail;
                }
            }
        } while (!(IS_SDIF_FLAG_SET(status, kSDIF_WriteFIFORequest)));

        if (error == kStatus_Success)
        {
            transferredWords = SDIF_WriteDataPort(base, data, transferredWords);
        }

        /* clear interrupt status */
        SDIF_ClearInterruptStatus(base, status);
    }

    while ((SDIF_GetInterruptStatus(base) & (uint32_t)kSDIF_DataTransferOver) != (uint32_t)kSDIF_DataTransferOver)
    {
    }

    if (IS_SDIF_FLAG_SET(SDIF_GetInterruptStatus(base), kSDIF_DataTransferError))
    {
        if (!(data->enableIgnoreError))
        {
            error = kStatus_Fail;
        }
    }
    SDIF_ClearInterruptStatus(base, ((uint32_t)kSDIF_DataTransferOver | (uint32_t)kSDIF_DataTransferError));

    return error;
}

/*!
 * brief reset the different block of the interface.
 * param base SDIF peripheral base address.
 * param mask indicate which block to reset.
 * param timeout value,set to wait the bit self clear
 * return reset result.
 */
bool SDIF_Reset(SDIF_Type *base, uint32_t mask, uint32_t timeout)
{
    /* reset through CTRL */
    base->CTRL |= mask;
    /* DMA software reset */
    if (IS_SDIF_FLAG_SET(mask, kSDIF_ResetDMAInterface))
    {
        /* disable DMA first then do DMA software reset */
        base->BMOD = (base->BMOD & (~SDIF_BMOD_DE_MASK)) | SDIF_BMOD_SWR_MASK;
    }

    /* check software DMA reset here for DMA reset also need to check this bit */
    while ((base->CTRL & mask) != 0UL)
    {
        if (0UL == timeout)
        {
            break;
        }
        timeout--;
    }

    return timeout != 0UL ? true : false;
}

static status_t SDIF_TransferDataBlocking(SDIF_Type *base, sdif_data_t *data, bool isDMA)
{
    assert(NULL != data);

    uint32_t dmaStatus = 0UL;
    status_t error     = kStatus_Success;

    /* in DMA mode, only need to wait the complete flag and check error */
    if (isDMA)
    {
        do
        {
            dmaStatus = SDIF_GetInternalDMAStatus(base);
            if (IS_SDIF_FLAG_SET(dmaStatus, (uint32_t)kSDIF_DMAFatalBusError))
            {
                SDIF_ClearInternalDMAStatus(
                    base, (uint32_t)kSDIF_DMAFatalBusError | (uint32_t)kSDIF_AbnormalInterruptSummary);
                error = kStatus_SDIF_DMATransferFailWithFBE; /* in this condition,need reset */
            }
            /* Card error summary, include EBE,SBE,Data CRC,RTO,DRTO,Response error */
            if (IS_SDIF_FLAG_SET(dmaStatus, (uint32_t)kSDIF_DMACardErrorSummary))
            {
                SDIF_ClearInternalDMAStatus(
                    base, (uint32_t)kSDIF_DMACardErrorSummary | (uint32_t)kSDIF_AbnormalInterruptSummary);
                if (!(data->enableIgnoreError))
                {
                    error = kStatus_SDIF_DataTransferFail;
                }

                /* if error occur, then return */
                break;
            }
        } while (!(IS_SDIF_FLAG_SET(
            dmaStatus, ((uint32_t)kSDIF_DMATransFinishOneDescriptor | (uint32_t)kSDIF_DMARecvFinishOneDescriptor))));

        /* clear the corresponding status bit */
        SDIF_ClearInternalDMAStatus(
            base, ((uint32_t)kSDIF_DMATransFinishOneDescriptor | (uint32_t)kSDIF_DMARecvFinishOneDescriptor |
                   (uint32_t)kSDIF_NormalInterruptSummary));

        SDIF_ClearInterruptStatus(base, SDIF_GetInterruptStatus(base));

        if (error != kStatus_Success)
        {
            return kStatus_SDIF_DataTransferFail;
        }
    }
    else
    {
        if (data->rxData != NULL)
        {
            error = SDIF_ReadDataPortBlocking(base, data);
            if (error != kStatus_Success)
            {
                return kStatus_SDIF_DataTransferFail;
            }
        }
        else
        {
            error = SDIF_WriteDataPortBlocking(base, data);
            if (error != kStatus_Success)
            {
                return kStatus_SDIF_DataTransferFail;
            }
        }
    }

    return kStatus_Success;
}

/*!
 * brief send command to the card
 *
 * This api include polling the status of the bit START_COMMAND, if 0 used as timeout value, then this function
 * will return directly without polling the START_CMD status.
 * param base SDIF peripheral base address.
 * param command configuration collection.
 * param timeout the timeout value of polling START_CMD auto clear status.
 * return command excute status
 */
status_t SDIF_SendCommand(SDIF_Type *base, sdif_command_t *cmd, uint32_t timeout)
{
    assert(NULL != cmd);

    return SDIF_SetCommandRegister(base, SDIF_CMD_CMD_INDEX(cmd->index) | (cmd->flags & (~SDIF_CMD_CMD_INDEX_MASK)),
                                   cmd->argument, timeout);
}

/*!
 * brief SDIF send initialize 80 clocks for SD card after initial
 * param base SDIF peripheral base address.
 * param timeout value
 */
bool SDIF_SendCardActive(SDIF_Type *base, uint32_t timeout)
{
    bool enINT = false;

    /* add for conflict with interrupt mode,close the interrupt temporary */
    if ((base->CTRL & SDIF_CTRL_INT_ENABLE_MASK) == SDIF_CTRL_INT_ENABLE_MASK)
    {
        enINT = true;
        base->CTRL &= ~SDIF_CTRL_INT_ENABLE_MASK;
    }
    SDIF_ClearInterruptStatus(base, kSDIF_CommandDone);
    SDIF_EnableInterrupt(base, kSDIF_CommandDone);

    /* send initialization command */
    if (SDIF_SetCommandRegister(base, SDIF_CMD_SEND_INITIALIZATION_MASK, 0UL, timeout) != kStatus_Success)
    {
        return false;
    }

    /* wait command done */
    while ((SDIF_GetInterruptStatus(base) & (uint32_t)kSDIF_CommandDone) != (uint32_t)kSDIF_CommandDone)
    {
    }

    /* clear status */
    SDIF_ClearInterruptStatus(base, kSDIF_CommandDone);
    SDIF_DisableInterrupt(base, kSDIF_CommandDone);

    /* add for conflict with interrupt mode */
    if (enINT)
    {
        base->CTRL |= SDIF_CTRL_INT_ENABLE_MASK;
    }

    return true;
}

/*!
 * brief SDIF config the clock delay
 * This function is used to config the cclk_in delay to
 * sample and driver the data ,should meet the min setup
 * time and hold time, and user need to config this parameter
 * according to your board setting
 * param target freq work mode
 * param divider not used in this function anymore, use DELAY value instead of phase directly.
 */
void SDIF_ConfigClockDelay(uint32_t target_HZ, uint32_t divider)
{
    uint32_t sdioClkCtrl = SYSCON->SDIOCLKCTRL;

    sdioClkCtrl = SYSCON->SDIOCLKCTRL &
                  (~(SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY_ACTIVE_MASK | SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY_MASK |
                     SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY_ACTIVE_MASK | SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY_MASK));

    if (target_HZ >= SDIF_CLOCK_RANGE_NEED_DELAY)
    {
#ifdef SDIF_HIGHSPEED_SAMPLE_DELAY
        sdioClkCtrl |= SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY_ACTIVE_MASK |
                       SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY(SDIF_HIGHSPEED_SAMPLE_DELAY);
#endif
#ifdef SDIF_HIGHSPEED_DRV_DELAY
        sdioClkCtrl |=
            SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY_ACTIVE_MASK | SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY(SDIF_HIGHSPEED_DRV_DELAY);
#endif
    }
    else
    {
#if defined(SDIF_DEFAULT_MODE_SAMPLE_DELAY)
        sdioClkCtrl |= SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY_ACTIVE_MASK |
                       SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY(SDIF_DEFAULT_MODE_SAMPLE_DELAY);
#endif
#if defined(SDIF_DEFAULT_MODE_DRV_DELAY)
        sdioClkCtrl |= SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY_ACTIVE_MASK |
                       SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY(SDIF_DEFAULT_MODE_DRV_DELAY);
#endif
    }

    SYSCON->SDIOCLKCTRL = sdioClkCtrl;
}

static status_t SDIF_SyncClockCommand(SDIF_Type *base)
{
    uint32_t syncTimeout      = 10000U;
    uint32_t sendCommandRetry = 3U;

    do
    {
        /* update the clock register and wait the pre-transfer complete */
        if (SDIF_SetCommandRegister(
                base, (uint32_t)kSDIF_CmdUpdateClockRegisterOnly | (uint32_t)kSDIF_WaitPreTransferComplete, 0UL,
                syncTimeout) == kStatus_Success)
        {
            break;
        }
        /* if send clock command timeout, it means that polling START_CMD cleared failed, CIU cannot take command at
         * this comment, so reset the host controller to recover the CIU interface and state machine.
         */
        (void)SDIF_Reset(base, kSDIF_ResetController, syncTimeout);
        sendCommandRetry--;
    } while (sendCommandRetry != 0U);

    if (sendCommandRetry == 0U)
    {
        return kStatus_Fail;
    }

    return kStatus_Success;
}

/*!
 * brief Sets the card bus clock frequency.
 *
 * param base SDIF peripheral base address.
 * param srcClock_Hz SDIF source clock frequency united in Hz.
 * param target_HZ card bus clock frequency united in Hz.
 * return The nearest frequency of busClock_Hz configured to SD bus.
 */
uint32_t SDIF_SetCardClock(SDIF_Type *base, uint32_t srcClock_Hz, uint32_t target_HZ)
{
    uint32_t divider = 0UL, targetFreq = target_HZ;

    /* if target freq bigger than the source clk, set the target_HZ to
     src clk, this interface can run up to 52MHZ with card */
    if (srcClock_Hz < targetFreq)
    {
        targetFreq = srcClock_Hz;
    }

    /* disable the clock first,need sync to CIU*/
    SDIF_EnableCardClock(base, false);

    if (SDIF_SyncClockCommand(base) != kStatus_Success)
    {
        return 0U;
    }

    /*calculate the divider*/
    if (targetFreq != srcClock_Hz)
    {
        divider = srcClock_Hz / targetFreq;
        while (srcClock_Hz / divider > targetFreq)
        {
            divider++;
        }

        if (divider > (SDIF_CLKDIV_CLK_DIVIDER0_MASK * 2UL))
        {
            /* Note: if assert occur here, it means that the source clock frequency is too high, the suggestion is
             * reconfigure the SDIF divider in SYSCON to get a properly source clock */
            assert(false);
            divider = (SDIF_CLKDIV_CLK_DIVIDER0_MASK * 2UL);
        }

        divider = (divider + 1UL) / 2UL;
    }
    /* load the clock divider */
    base->CLKDIV = SDIF_CLKDIV_CLK_DIVIDER0(divider);
    /* update the divider to CIU */
    if (SDIF_SyncClockCommand(base) != kStatus_Success)
    {
        return 0U;
    }

    /* enable the card clock and sync to CIU */
    SDIF_EnableCardClock(base, true);
    (void)SDIF_SyncClockCommand(base);

    /* config the clock delay to meet the hold time and setup time */
    SDIF_ConfigClockDelay(target_HZ, divider);

    /* return the actual card clock freq */
    return (divider != 0UL) ? (srcClock_Hz / (divider * 2UL)) : srcClock_Hz;
}

/*!
 * brief SDIF abort the read data when SDIF card is in suspend state
 * Once assert this bit,data state machine will be reset which is waiting for the
 * next blocking data,used in SDIO card suspend sequence,should call after suspend
 * cmd send
 * param base SDIF peripheral base address.
 * param timeout value to wait this bit self clear which indicate the data machine
 * reset to idle
 */
bool SDIF_AbortReadData(SDIF_Type *base, uint32_t timeout)
{
    /* assert this bit to reset the data machine to abort the read data */
    base->CTRL |= SDIF_CTRL_ABORT_READ_DATA_MASK;
    /* polling the bit self clear */
    while ((base->CTRL & SDIF_CTRL_ABORT_READ_DATA_MASK) == SDIF_CTRL_ABORT_READ_DATA_MASK)
    {
        if (0UL == timeout)
        {
            break;
        }
        timeout--;
    }

    return IS_SDIF_FLAG_SET(base->CTRL, SDIF_CTRL_ABORT_READ_DATA_MASK) ? false : true;
}

/*!
 * brief SDIF internal DMA config function
 * param base SDIF peripheral base address.
 * param internal DMA configuration collection
 * param data buffer pointer
 * param data buffer size
 */
status_t SDIF_InternalDMAConfig(SDIF_Type *base, sdif_dma_config_t *config, const uint32_t *data, uint32_t dataSize)
{
    assert(NULL != config);
    assert(NULL != data);

    uint32_t dmaEntry = 0UL, i, dmaBufferSize = 0UL, dmaBuffer1Size = 0UL;
    uint32_t *tempDMADesBuffer               = config->dmaDesBufferStartAddr;
    const uint32_t *dataBuffer               = data;
    sdif_dma_descriptor_t *descriptorPoniter = NULL;
    uint32_t maxDMABuffer = (uint32_t)FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE * ((uint32_t)config->mode);

    if ((((uint32_t)data % SDIF_INTERNAL_DMA_ADDR_ALIGN) != 0UL) ||
        (((uint32_t)tempDMADesBuffer % SDIF_INTERNAL_DMA_ADDR_ALIGN) != 0UL))
    {
        return kStatus_SDIF_DMAAddrNotAlign;
    }

    /* check the read/write data size,must be a multiple of 4 */
    if (dataSize % sizeof(uint32_t) != 0UL)
    {
        dataSize += sizeof(uint32_t) - (dataSize % sizeof(uint32_t));
    }

    /*config the bus mode*/
    if (config->enableFixBurstLen)
    {
        base->BMOD |= SDIF_BMOD_FB_MASK;
    }

    /* calculate the dma descriptor entry due to DMA buffer size limit */
    /* if data size smaller than one descriptor buffer size */
    if (dataSize > maxDMABuffer)
    {
        dmaEntry = dataSize / maxDMABuffer + ((dataSize % maxDMABuffer) != 0UL ? 1UL : 0UL);
    }
    else /* need one dma descriptor */
    {
        dmaEntry = 1UL;
    }

    /* check the DMA descriptor buffer len one more time,it is user's responsibility to make sure the DMA descriptor
    table
    size is bigger enough to hold the transfer descriptor */
    if (config->dmaDesBufferLen * sizeof(uint32_t) < (dmaEntry * sizeof(sdif_dma_descriptor_t) + config->dmaDesSkipLen))
    {
        return kStatus_SDIF_DescriptorBufferLenError;
    }

    if (config->mode == kSDIF_DualDMAMode)
    {
        base->BMOD |= SDIF_BMOD_DSL(config->dmaDesSkipLen); /* config the distance between the DMA descriptor */
        for (i = 0UL; i < dmaEntry; i++)
        {
            if (dataSize > (uint32_t)FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE)
            {
                dmaBufferSize = FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE;
                dataSize -= dmaBufferSize;
                dmaBuffer1Size = dataSize > (uint32_t)FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE ?
                                     (uint32_t)FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE :
                                     dataSize;
                dataSize -= dmaBuffer1Size;
            }
            else
            {
                dmaBufferSize  = dataSize;
                dmaBuffer1Size = 0UL;
            }

            descriptorPoniter = (sdif_dma_descriptor_t *)(uint32_t)tempDMADesBuffer;
            if (i == 0UL)
            {
                descriptorPoniter->dmaDesAttribute = SDIF_DMA_DESCRIPTOR_DATA_BUFFER_START_FLAG |
                                                     SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG |
                                                     SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG;
            }
            else
            {
                descriptorPoniter->dmaDesAttribute =
                    SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG | SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG;
            }
            descriptorPoniter->dmaDataBufferSize =
                SDIF_DMA_DESCRIPTOR_BUFFER1_SIZE(dmaBufferSize) | SDIF_DMA_DESCRIPTOR_BUFFER2_SIZE(dmaBuffer1Size);

            descriptorPoniter->dmaDataBufferAddr0 = dataBuffer;
            descriptorPoniter->dmaDataBufferAddr1 = dataBuffer + dmaBufferSize / sizeof(uint32_t);
            dataBuffer += (dmaBufferSize + dmaBuffer1Size) / sizeof(uint32_t);

            /* descriptor skip length */
            tempDMADesBuffer += config->dmaDesSkipLen + sizeof(sdif_dma_descriptor_t) / sizeof(uint32_t);
        }
        /* enable the completion interrupt when reach the last descriptor */
        descriptorPoniter->dmaDesAttribute &= ~SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG;
        descriptorPoniter->dmaDesAttribute |=
            SDIF_DMA_DESCRIPTOR_DATA_BUFFER_END_FLAG | SDIF_DMA_DESCRIPTOR_DESCRIPTOR_END_FLAG;
    }
    else
    {
        for (i = 0UL; i < dmaEntry; i++)
        {
            if (dataSize > (uint32_t)FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE)
            {
                dmaBufferSize = FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE;
                dataSize -= (uint32_t)FSL_FEATURE_SDIF_INTERNAL_DMA_MAX_BUFFER_SIZE;
            }
            else
            {
                dmaBufferSize = dataSize;
            }

            descriptorPoniter = (sdif_dma_descriptor_t *)(uint32_t)tempDMADesBuffer;
            if (i == 0UL)
            {
                descriptorPoniter->dmaDesAttribute =
                    SDIF_DMA_DESCRIPTOR_DATA_BUFFER_START_FLAG | SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG |
                    SDIF_DMA_DESCRIPTOR_SECOND_ADDR_CHAIN_FLAG | SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG;
            }
            else
            {
                descriptorPoniter->dmaDesAttribute = SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG |
                                                     SDIF_DMA_DESCRIPTOR_SECOND_ADDR_CHAIN_FLAG |
                                                     SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG;
            }
            descriptorPoniter->dmaDataBufferSize =
                SDIF_DMA_DESCRIPTOR_BUFFER1_SIZE(dmaBufferSize); /* use only buffer 1 for data buffer*/
            descriptorPoniter->dmaDataBufferAddr0 = dataBuffer;
            dataBuffer += dmaBufferSize / sizeof(uint32_t);
            tempDMADesBuffer +=
                sizeof(sdif_dma_descriptor_t) / sizeof(uint32_t); /* calculate the next descriptor address */
            /* this descriptor buffer2 pointer to the next descriptor address */
            descriptorPoniter->dmaDataBufferAddr1 = tempDMADesBuffer;
        }
        /* enable the completion interrupt when reach the last descriptor */
        descriptorPoniter->dmaDesAttribute &= ~SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG;
        descriptorPoniter->dmaDesAttribute |= SDIF_DMA_DESCRIPTOR_DATA_BUFFER_END_FLAG;
    }

    /* use internal DMA interface */
    base->CTRL |= SDIF_CTRL_USE_INTERNAL_DMAC_MASK;
    /* enable the internal SD/MMC DMA */
    base->BMOD |= SDIF_BMOD_DE_MASK;
    /* load DMA descriptor buffer address */
    base->DBADDR = (uint32_t)config->dmaDesBufferStartAddr;

    return kStatus_Success;
}

#if defined(FSL_FEATURE_SDIF_ONE_INSTANCE_SUPPORT_TWO_CARD) && FSL_FEATURE_SDIF_ONE_INSTANCE_SUPPORT_TWO_CARD
/*!
 * brief set card data bus width
 * param base SDIF peripheral base address.
 * param data bus width type
 */
void SDIF_SetCardBusWidth(SDIF_Type *base, sdif_bus_width_t type)
{
    if (type == kSDIF_Bus1BitWidth)
    {
        base->CTYPE &= ~(SDIF_CTYPE_CARD0_WIDTH0_MASK | SDIF_CTYPE_CARD0_WIDTH1_MASK);
    }
    else if (type == kSDIF_Bus4BitWidth)
    {
        base->CTYPE = (base->CTYPE & (~SDIF_CTYPE_CARD0_WIDTH1_MASK)) | SDIF_CTYPE_CARD0_WIDTH0_MASK;
    }
    else
    {
        base->CTYPE |= SDIF_CTYPE_CARD0_WIDTH1_MASK;
    }
}

/*!
 * brief set card1 data bus width
 * param base SDIF peripheral base address.
 * param data bus width type
 */
void SDIF_SetCard1BusWidth(SDIF_Type *base, sdif_bus_width_t type)
{
    if (type == kSDIF_Bus1BitWidth)
    {
        base->CTYPE &= ~(SDIF_CTYPE_CARD1_WIDTH0_MASK | SDIF_CTYPE_CARD1_WIDTH1_MASK);
    }
    else if (type == kSDIF_Bus4BitWidth)
    {
        base->CTYPE = (base->CTYPE & (~SDIF_CTYPE_CARD1_WIDTH1_MASK)) | SDIF_CTYPE_CARD1_WIDTH0_MASK;
    }
    else
    {
        base->CTYPE |= SDIF_CTYPE_CARD1_WIDTH1_MASK;
    }
}
#else
/*!
 * brief set card data bus width
 * param base SDIF peripheral base address.
 * param data bus width type
 */
void SDIF_SetCardBusWidth(SDIF_Type *base, sdif_bus_width_t type)
{
    if (type == kSDIF_Bus1BitWidth)
    {
        base->CTYPE &= ~(SDIF_CTYPE_CARD_WIDTH0_MASK | SDIF_CTYPE_CARD_WIDTH1_MASK);
    }
    else if (type == kSDIF_Bus4BitWidth)
    {
        base->CTYPE = (base->CTYPE & (~SDIF_CTYPE_CARD_WIDTH1_MASK)) | SDIF_CTYPE_CARD_WIDTH0_MASK;
    }
    else
    {
        base->CTYPE |= SDIF_CTYPE_CARD_WIDTH1_MASK;
    }
}
#endif

/*!
 * brief SDIF module initialization function.
 *
 * Configures the SDIF according to the user configuration.
 * param base SDIF peripheral base address.
 * param config SDIF configuration information.
 */
void SDIF_Init(SDIF_Type *base, sdif_config_t *config)
{
    assert(NULL != config);

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Enable the clock. */
    CLOCK_EnableClock(kCLOCK_Sdio);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
    /* Reset the module. */
    RESET_PeripheralReset(kSDIO_RST_SHIFT_RSTn);
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */

    /*config timeout register */
    base->TMOUT = ((base->TMOUT) & ~(SDIF_TMOUT_RESPONSE_TIMEOUT_MASK | SDIF_TMOUT_DATA_TIMEOUT_MASK)) |
                  SDIF_TMOUT_RESPONSE_TIMEOUT(config->responseTimeout) | SDIF_TMOUT_DATA_TIMEOUT(config->dataTimeout);

    /* config the card detect debounce clock count */
    base->DEBNCE = SDIF_DEBNCE_DEBOUNCE_COUNT(config->cardDetDebounce_Clock);

    /*config the watermark/burst transfer value */
    base->FIFOTH =
        SDIF_FIFOTH_TX_WMARK(SDIF_TX_WATERMARK) | SDIF_FIFOTH_RX_WMARK(SDIF_RX_WATERMARK) | SDIF_FIFOTH_DMA_MTS(1U);

    /* disable all the interrupt */
    SDIF_DisableInterrupt(base, kSDIF_AllInterruptStatus);

    /* clear all interrupt/DMA status */
    SDIF_ClearInterruptStatus(base, kSDIF_AllInterruptStatus);
    SDIF_ClearInternalDMAStatus(base, kSDIF_DMAAllStatus);
}

/*!
 * brief SDIF transfer function data/cmd in a blocking way
 * param base SDIF peripheral base address.
 * param DMA config structure
 *       1. NULL
 *           In this condition, polling transfer mode is selected
 *       2. avaliable DMA config
 *           In this condition, DMA transfer mode is selected
 * param sdif transfer configuration collection
 */
status_t SDIF_TransferBlocking(SDIF_Type *base, sdif_dma_config_t *dmaConfig, sdif_transfer_t *transfer)
{
    assert(NULL != transfer);

    bool enDMA        = true;
    sdif_data_t *data = transfer->data;
    status_t error    = kStatus_Fail;

    /* if need transfer data in dma mode, config the DMA descriptor first */
    if ((data != NULL) && (dmaConfig != NULL))
    {
        if ((error = SDIF_InternalDMAConfig(base, dmaConfig, data->rxData != NULL ? data->rxData : data->txData,
                                            data->blockSize * data->blockCount)) ==
            kStatus_SDIF_DescriptorBufferLenError)
        {
            return kStatus_SDIF_DescriptorBufferLenError;
        }
        /* if DMA descriptor address or data buffer address not align with SDIF_INTERNAL_DMA_ADDR_ALIGN, switch to
        polling transfer mode, disable the internal DMA */
        if (error == kStatus_SDIF_DMAAddrNotAlign)
        {
            enDMA = false;
        }
    }
    else
    {
        enDMA = false;
    }

    if (!enDMA)
    {
        SDIF_EnableInternalDMA(base, false);
    }

    /* config the transfer parameter */
    if (SDIF_TransferConfig(base, transfer, enDMA) != kStatus_Success)
    {
        return kStatus_SDIF_InvalidArgument;
    }

    /* send command first, do not wait start bit auto cleared, command done bit should wait while sending normal command
     */
    if (SDIF_SendCommand(base, transfer->command, 0UL) != kStatus_Success)
    {
        return kStatus_SDIF_SendCmdFail;
    }

    if (SDIF_WaitCommandDone(base, transfer->command) != kStatus_Success)
    {
        return kStatus_SDIF_SendCmdFail;
    }
    /* if use DMA transfer mode ,check the corresponding status bit */
    if (data != NULL)
    {
        /* handle data transfer */
        error = SDIF_TransferDataBlocking(base, data, enDMA);
        if (error != kStatus_Success)
        {
            return kStatus_SDIF_DataTransferFail;
        }
    }

    return kStatus_Success;
}

/*!
 * brief SDIF transfer function data/cmd in a non-blocking way
 * this API should be use in interrupt mode, when use this API user
 * must call SDIF_TransferCreateHandle first, all status check through
 * interrupt
 * param base SDIF peripheral base address.
 * param sdif handle
 * param DMA config structure
 *  This parameter can be config as:
 *      1. NULL
            In this condition, polling transfer mode is selected
        2. avaliable DMA config
            In this condition, DMA transfer mode is selected
 * param sdif transfer configuration collection
 */
status_t SDIF_TransferNonBlocking(SDIF_Type *base,
                                  sdif_handle_t *handle,
                                  sdif_dma_config_t *dmaConfig,
                                  sdif_transfer_t *transfer)
{
    assert(NULL != transfer);

    sdif_data_t *data = transfer->data;
    status_t error    = kStatus_Fail;
    bool enDMA        = true;

    /* save the data and command before transfer */
    handle->data             = transfer->data;
    handle->command          = transfer->command;
    handle->transferredWords = 0U;

    if ((data != NULL) && (dmaConfig != NULL))
    {
        /* use internal DMA mode to transfer between the card and host*/
        if ((error = SDIF_InternalDMAConfig(base, dmaConfig, data->rxData != NULL ? data->rxData : data->txData,
                                            data->blockSize * data->blockCount)) ==
            kStatus_SDIF_DescriptorBufferLenError)
        {
            return kStatus_SDIF_DescriptorBufferLenError;
        }
        /* if DMA descriptor address or data buffer address not align with SDIF_INTERNAL_DMA_ADDR_ALIGN, switch to
        polling transfer mode, disable the internal DMA */
        if (error == kStatus_SDIF_DMAAddrNotAlign)
        {
            enDMA = false;
        }
    }
    else
    {
        enDMA = false;
    }

    if (!enDMA)
    {
        SDIF_EnableInternalDMA(base, false);
    }

    /* config the transfer parameter */
    if (SDIF_TransferConfig(base, transfer, enDMA) != kStatus_Success)
    {
        return kStatus_SDIF_InvalidArgument;
    }

    /* send command first, do not wait start bit auto cleared, command done bit should wait while sending normal command
     */
    return SDIF_SendCommand(base, transfer->command, 0UL);
}

/*!
 * brief Creates the SDIF handle.
 * register call back function for interrupt and enable the interrupt
 * param base SDIF peripheral base address.
 * param handle SDIF handle pointer.
 * param callback Structure pointer to contain all callback functions.
 * param userData Callback function parameter.
 */
void SDIF_TransferCreateHandle(SDIF_Type *base,
                               sdif_handle_t *handle,
                               sdif_transfer_callback_t *callback,
                               void *userData)
{
    assert(handle != NULL);
    assert(callback != NULL);

    /* reset the handle. */
    (void)memset(handle, 0, sizeof(*handle));

    /* Set the callback. */
    handle->callback.SDIOInterrupt     = callback->SDIOInterrupt;
    handle->callback.DMADesUnavailable = callback->DMADesUnavailable;
    handle->callback.CommandReload     = callback->CommandReload;
    handle->callback.TransferComplete  = callback->TransferComplete;
    handle->callback.cardInserted      = callback->cardInserted;
    handle->callback.cardRemoved       = callback->cardRemoved;
    handle->userData                   = userData;

    /* Save the handle in global variables to support the double weak mechanism. */
    s_sdifHandle[SDIF_GetInstance(base)] = handle;

    /* save IRQ handler */
    s_sdifIsr = SDIF_TransferHandleIRQ;

    /* enable the global interrupt */
    SDIF_EnableGlobalInterrupt(base, true);

    (void)EnableIRQ(s_sdifIRQ[SDIF_GetInstance(base)]);
}

/*!
 * brief SDIF return the controller capability
 * param base SDIF peripheral base address.
 * param sdif capability pointer
 */
void SDIF_GetCapability(SDIF_Type *base, sdif_capability_t *capability)
{
    assert(NULL != capability);

    /* Initializes the configure structure to zero. */
    (void)memset(capability, 0, sizeof(*capability));

    capability->sdVersion      = SDIF_SUPPORT_SD_VERSION;
    capability->mmcVersion     = SDIF_SUPPORT_MMC_VERSION;
    capability->maxBlockLength = SDIF_BLKSIZ_BLOCK_SIZE_MASK;
    /* set the max block count = max byte count / max block size */
    capability->maxBlockCount = SDIF_BYTCNT_BYTE_COUNT_MASK / SDIF_BLKSIZ_BLOCK_SIZE_MASK;
    capability->flags         = (uint32_t)kSDIF_SupportHighSpeedFlag | (uint32_t)kSDIF_SupportDmaFlag |
                        (uint32_t)kSDIF_SupportSuspendResumeFlag | (uint32_t)kSDIF_SupportV330Flag |
                        (uint32_t)kSDIF_Support4BitFlag | (uint32_t)kSDIF_Support8BitFlag;
}

static void SDIF_TransferHandleCommand(SDIF_Type *base, sdif_handle_t *handle, uint32_t interruptFlags)
{
    assert(handle->command != NULL);

    /* cmd buffer full, in this condition user need re-send the command */
    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_HardwareLockError))
    {
        if (handle->callback.CommandReload != NULL)
        {
            handle->callback.CommandReload(base, handle->userData);
        }
    }
    /* transfer command done */
    else
    {
        if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_CommandDone))
        {
            /* transfer error */
            if ((IS_SDIF_FLAG_SET(interruptFlags, ((uint32_t)kSDIF_ResponseError | (uint32_t)kSDIF_ResponseCRCError |
                                                   (uint32_t)kSDIF_ResponseTimeout))) ||
                (SDIF_ReadCommandResponse(base, handle->command) != kStatus_Success))
            {
                if (handle->callback.TransferComplete != NULL)
                {
                    handle->callback.TransferComplete(base, handle, kStatus_SDIF_SendCmdFail, handle->userData);
                }
            }
            else
            {
                if (handle->callback.TransferComplete != NULL)
                {
                    handle->callback.TransferComplete(base, handle, kStatus_SDIF_SendCmdSuccess, handle->userData);
                }
            }
        }
    }

    SDIF_DisableInterrupt(base, kSDIF_CommandTransferStatus);
    handle->command = NULL;
}

static void SDIF_TransferHandleData(SDIF_Type *base, sdif_handle_t *handle, uint32_t interruptFlags)
{
    assert(handle->data != NULL);

    status_t transferStatus   = kStatus_SDIF_BusyTransferring;
    uint32_t transferredWords = handle->transferredWords;

    /* data starvation by host time out, software should read/write FIFO*/
    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_DataStarvationByHostTimeout))
    {
        if (handle->data->rxData != NULL)
        {
            handle->transferredWords = SDIF_ReadDataPort(base, handle->data, transferredWords);
        }
        else if (handle->data->txData != NULL)
        {
            handle->transferredWords = SDIF_WriteDataPort(base, handle->data, transferredWords);
        }
        else
        {
            transferStatus = kStatus_SDIF_DataTransferFail;
        }
    }
    /* data transfer fail */
    else if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_DataTransferError))
    {
        if (!handle->data->enableIgnoreError)
        {
            transferStatus = kStatus_SDIF_DataTransferFail;
        }
        else
        {
            transferStatus = kStatus_SDIF_DataTransferSuccess;
        }
    }
    /* need fill data to FIFO */
    else if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_WriteFIFORequest))
    {
        handle->transferredWords = SDIF_WriteDataPort(base, handle->data, transferredWords);
    }
    /* need read data from FIFO */
    else if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_ReadFIFORequest))
    {
        handle->transferredWords = SDIF_ReadDataPort(base, handle->data, transferredWords);
    }
    else
    {
        /* Intentional empty */
    }

    /* data transfer over */
    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_DataTransferOver))
    {
        while ((handle->data->rxData != NULL) && ((base->STATUS & SDIF_STATUS_FIFO_COUNT_MASK) != 0UL))
        {
            handle->transferredWords = SDIF_ReadDataPort(base, handle->data, transferredWords);
        }
        transferStatus = kStatus_SDIF_DataTransferSuccess;
    }

    if ((handle->callback.TransferComplete != NULL) && (transferStatus != kStatus_SDIF_BusyTransferring))
    {
        handle->callback.TransferComplete(base, handle, transferStatus, handle->userData);
        handle->data = NULL;
    }
}

static void SDIF_TransferHandleDMA(SDIF_Type *base, sdif_handle_t *handle, uint32_t interruptFlags)
{
    status_t transferStatus = kStatus_SDIF_DataTransferFail;

    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_DMAFatalBusError))
    {
        transferStatus = kStatus_SDIF_DMATransferFailWithFBE;
    }
    else if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_DMADescriptorUnavailable))
    {
        if (handle->callback.DMADesUnavailable != NULL)
        {
            handle->callback.DMADesUnavailable(base, handle->userData);
        }
    }
    else if (IS_SDIF_FLAG_SET(interruptFlags,
                              ((uint32_t)kSDIF_AbnormalInterruptSummary | (uint32_t)kSDIF_DMACardErrorSummary)) &&
             (!handle->data->enableIgnoreError))
    {
        transferStatus = kStatus_SDIF_DataTransferFail;
    }
    /* card normal summary */
    else
    {
        transferStatus = kStatus_SDIF_DataTransferSuccess;
    }

    if (handle->callback.TransferComplete != NULL)
    {
        handle->callback.TransferComplete(base, handle, transferStatus, handle->userData);
        handle->data = NULL;
    }

    SDIF_DisableDmaInterrupt(base, kSDIF_DMAAllStatus);
}

static void SDIF_TransferHandleSDIOInterrupt(SDIF_Type *base, sdif_handle_t *handle)
{
    if (handle->callback.SDIOInterrupt != NULL)
    {
        handle->callback.SDIOInterrupt(base, handle->userData);
    }
}

static void SDIF_TransferHandleCardDetect(SDIF_Type *base, sdif_handle_t *handle)
{
    if (SDIF_DetectCardInsert(base, false) == 1UL)
    {
        if ((handle->callback.cardInserted) != NULL)
        {
            handle->callback.cardInserted(base, handle->userData);
        }
    }
    else
    {
        if ((handle->callback.cardRemoved) != NULL)
        {
            handle->callback.cardRemoved(base, handle->userData);
        }
    }
}

static void SDIF_TransferHandleIRQ(SDIF_Type *base, sdif_handle_t *handle)
{
    assert(handle != NULL);

    uint32_t interruptFlags, dmaInterruptFlags;

    interruptFlags    = SDIF_GetEnabledInterruptStatus(base);
    dmaInterruptFlags = SDIF_GetEnabledDMAInterruptStatus(base);

    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_CommandTransferStatus))
    {
        SDIF_TransferHandleCommand(base, handle, interruptFlags);
    }
    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_DataTransferStatus))
    {
        SDIF_TransferHandleData(base, handle, interruptFlags);
    }
    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_SDIOInterrupt))
    {
        SDIF_TransferHandleSDIOInterrupt(base, handle);
    }
    if (IS_SDIF_FLAG_SET(dmaInterruptFlags, kSDIF_DMAAllStatus))
    {
        SDIF_TransferHandleDMA(base, handle, dmaInterruptFlags);
    }
    if (IS_SDIF_FLAG_SET(interruptFlags, kSDIF_CardDetect))
    {
        SDIF_TransferHandleCardDetect(base, handle);
    }

    SDIF_ClearInterruptStatus(base, interruptFlags);
    SDIF_ClearInternalDMAStatus(base, dmaInterruptFlags);
}

/*!
 * brief SDIF module deinit function.
 * user should call this function follow with IP reset
 * param base SDIF peripheral base address.
 */
void SDIF_Deinit(SDIF_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Disable the clock. */
    CLOCK_DisableClock(kCLOCK_Sdio);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
    /* disable the SDIOCLKCTRL */
    SYSCON->SDIOCLKCTRL &= ~(SYSCON_SDIOCLKCTRL_CCLK_SAMPLE_DELAY_ACTIVE_MASK |
                             SYSCON_SDIOCLKCTRL_CCLK_DRV_DELAY_ACTIVE_MASK | SYSCON_SDIOCLKCTRL_PHASE_ACTIVE_MASK);

#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
    /* Reset the module. */
    RESET_PeripheralReset(kSDIO_RST_SHIFT_RSTn);
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
}

#if defined(SDIF)
void SDIF_DriverIRQHandler(void);
void SDIF_DriverIRQHandler(void)
{
    assert(s_sdifHandle[0] != NULL);

    s_sdifIsr(SDIF, s_sdifHandle[0]);
    SDK_ISR_EXIT_BARRIER;
}
#endif
