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

#include "fsl_common.h"

/*!
 * @addtogroup sdif
 * @{
 */

/**********************************
 * Definitions.
 *****************************************************************************/

/*! @name Driver version */
/*! @{ */
/*! @brief Driver version 2.0.15. */
#define FSL_SDIF_DRIVER_VERSION (MAKE_VERSION(2U, 1U, 0U))
/*! @} */

/*! @brief  SDIOCLKCTRL setting
 * Below clock delay setting should depend on specific platform, so
 * it can be redefined when timing mismatch issue occur.
 * Such as: response error/CRC error and so on
 */
/*! @brief clock range value which need to add delay to avoid timing issue */
#ifndef SDIF_CLOCK_RANGE_NEED_DELAY
#define SDIF_CLOCK_RANGE_NEED_DELAY (50000000U)
#endif

/*
 * Fixed delay configuration
 * min hold time:2ns
 * min setup time: 6ns
 * delay = (x+1)*250ps
 */
/*! @brief High speed mode clk_sample fixed delay*/
#ifndef SDIF_HIGHSPEED_SAMPLE_DELAY
#define SDIF_HIGHSPEED_SAMPLE_DELAY (12U) /*!< 12 * 250ps = 3ns */
#endif
/*! @brief High speed mode clk_drv fixed delay */
#ifndef SDIF_HIGHSPEED_DRV_DELAY
#define SDIF_HIGHSPEED_DRV_DELAY (31U) /*!< 31 * 250ps = 7.75ns */
#endif

/*
 * Phase shift delay configuration
 * 0 degree: no delay
 * 90 degree: 0.25/source clk value
 * 180 degree: 0.50/source clk value
 * 270 degree: 0.75/source clk value
 */
/*! @brief High speed mode clk_sample phase shift */
#ifndef SDIF_HIGHSPEED_SAMPLE_PHASE_SHIFT
#define SDIF_HIGHSPEED_SAMPLE_PHASE_SHIFT (0U)
#endif
/*! @brief High speed mode clk_drv phase shift */
#ifndef SDIF_HIGHSPEED_DRV_PHASE_SHIFT
#define SDIF_HIGHSPEED_DRV_PHASE_SHIFT (1U) /* 90 degrees clk_drv phase delay */
#endif
/*! @brief default mode sample fixed delay */
#ifndef SDIF_DEFAULT_MODE_SAMPLE_DELAY
#define SDIF_DEFAULT_MODE_SAMPLE_DELAY (12U) /*!< 12 * 250ps = 3ns */
#endif

#ifndef SDIF_DEFAULT_MODE_DRV_DELAY
#define SDIF_DEFAULT_MODE_DRV_DELAY (31U) /*!< 31 * 250ps = 7.75ns */
#endif

/*! @brief SDIF internal DMA descriptor address and the data buffer address align */
#define SDIF_INTERNAL_DMA_ADDR_ALIGN (4U)
/*! @brief SDIF DMA descriptor flag */
#define SDIF_DMA_DESCRIPTOR_DISABLE_COMPLETE_INT_FLAG (1UL << 1U)
#define SDIF_DMA_DESCRIPTOR_DATA_BUFFER_END_FLAG      (1UL << 2U)
#define SDIF_DMA_DESCRIPTOR_DATA_BUFFER_START_FLAG    (1UL << 3U)
#define SDIF_DMA_DESCRIPTOR_SECOND_ADDR_CHAIN_FLAG    (1UL << 4U)
#define SDIF_DMA_DESCRIPTOR_DESCRIPTOR_END_FLAG       (1UL << 5U)
#define SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_FLAG           (1UL << 31U)

/*! @brief _sdif_status SDIF status */
enum
{
    kStatus_SDIF_DescriptorBufferLenError = MAKE_STATUS(kStatusGroup_SDIF, 0U), /*!< Set DMA descriptor failed */
    kStatus_SDIF_InvalidArgument          = MAKE_STATUS(kStatusGroup_SDIF, 1U), /*!< invalid argument status */
    kStatus_SDIF_SyncCmdTimeout = MAKE_STATUS(kStatusGroup_SDIF, 2U), /*!< sync command to CIU timeout status */
    kStatus_SDIF_SendCmdFail    = MAKE_STATUS(kStatusGroup_SDIF, 3U), /*!< send command to card fail */
    kStatus_SDIF_SendCmdErrorBufferFull =
        MAKE_STATUS(kStatusGroup_SDIF, 4U), /*!< send command to card fail, due to command buffer full
                                     user need to resend this command */
    kStatus_SDIF_DMATransferFailWithFBE =
        MAKE_STATUS(kStatusGroup_SDIF, 5U), /*!< DMA transfer data fail with fatal bus error ,
                                     to do with this error :issue a hard reset/controller reset*/
    kStatus_SDIF_DMATransferDescriptorUnavailable =
        MAKE_STATUS(kStatusGroup_SDIF, 6U),                                 /*!< DMA descriptor unavailable */
    kStatus_SDIF_DataTransferFail    = MAKE_STATUS(kStatusGroup_SDIF, 6U),  /*!< transfer data fail */
    kStatus_SDIF_ResponseError       = MAKE_STATUS(kStatusGroup_SDIF, 7U),  /*!< response error */
    kStatus_SDIF_DMAAddrNotAlign     = MAKE_STATUS(kStatusGroup_SDIF, 8U),  /*!< DMA address not align */
    kStatus_SDIF_BusyTransferring    = MAKE_STATUS(kStatusGroup_SDIF, 9U),  /*!< SDIF transfer busy status */
    kStatus_SDIF_DataTransferSuccess = MAKE_STATUS(kStatusGroup_SDIF, 10U), /*!< transfer data success */
    kStatus_SDIF_SendCmdSuccess      = MAKE_STATUS(kStatusGroup_SDIF, 11U), /*!< transfer command success */

};

/*! @brief _sdif_capability_flag Host controller capabilities flag mask */
enum
{
    kSDIF_SupportHighSpeedFlag     = 0x1U,  /*!< Support high-speed */
    kSDIF_SupportDmaFlag           = 0x2U,  /*!< Support DMA */
    kSDIF_SupportSuspendResumeFlag = 0x4U,  /*!< Support suspend/resume */
    kSDIF_SupportV330Flag          = 0x8U,  /*!< Support voltage 3.3V */
    kSDIF_Support4BitFlag          = 0x10U, /*!< Support 4 bit mode */
    kSDIF_Support8BitFlag          = 0x20U, /*!< Support 8 bit mode */
};

/*! @brief _sdif_reset_type define the reset type */
enum
{
    kSDIF_ResetController = SDIF_CTRL_CONTROLLER_RESET_MASK, /*!< reset controller,will reset: BIU/CIU interface
                                                               CIU and state machine,ABORT_READ_DATA,SEND_IRQ_RESPONSE
                                                               and READ_WAIT bits of control register,START_CMD bit of
                                                               the command register*/
    kSDIF_ResetFIFO         = SDIF_CTRL_FIFO_RESET_MASK,     /*!< reset data FIFO*/
    kSDIF_ResetDMAInterface = SDIF_CTRL_DMA_RESET_MASK,      /*!< reset DMA interface */

    kSDIF_ResetAll = kSDIF_ResetController | kSDIF_ResetFIFO | /*!< reset all*/
                     kSDIF_ResetDMAInterface,
};

/*! @brief define the card bus width type */
typedef enum _sdif_bus_width
{
    kSDIF_Bus1BitWidth = 0U, /*!< 1bit bus width, 1bit mode and 4bit mode
                                                   share one register bit */
    kSDIF_Bus4BitWidth = 1U, /*!< 4bit mode mask */
    kSDIF_Bus8BitWidth = 2U, /*!< support 8 bit mode */
} sdif_bus_width_t;

/*! @brief _sdif_command_flags define the command flags */
enum
{
    kSDIF_CmdResponseExpect     = SDIF_CMD_RESPONSE_EXPECT_MASK,    /*!< command request response*/
    kSDIF_CmdResponseLengthLong = SDIF_CMD_RESPONSE_LENGTH_MASK,    /*!< command response length long */
    kSDIF_CmdCheckResponseCRC   = SDIF_CMD_CHECK_RESPONSE_CRC_MASK, /*!< request check command response CRC*/
    kSDIF_DataExpect            = SDIF_CMD_DATA_EXPECTED_MASK,      /*!< request data transfer,either read/write*/
    kSDIF_DataWriteToCard       = SDIF_CMD_READ_WRITE_MASK,         /*!< data transfer direction */
    kSDIF_DataStreamTransfer    = SDIF_CMD_TRANSFER_MODE_MASK, /*!< data transfer mode :stream/block transfer command */
    kSDIF_DataTransferAutoStop  = SDIF_CMD_SEND_AUTO_STOP_MASK, /*!< data transfer with auto stop at the end of */
    kSDIF_WaitPreTransferComplete =
        SDIF_CMD_WAIT_PRVDATA_COMPLETE_MASK,                /*!< wait pre transfer complete before sending this cmd  */
    kSDIF_TransferStopAbort = SDIF_CMD_STOP_ABORT_CMD_MASK, /*!< when host issue stop or abort cmd to stop data transfer
                                                             ,this bit should set so that cmd/data state-machines of CIU
                                                             can return to idle correctly*/
    kSDIF_SendInitialization =
        SDIF_CMD_SEND_INITIALIZATION_MASK, /*!< send initialization  80 clocks for SD card after power on  */
    kSDIF_CmdUpdateClockRegisterOnly =
        SDIF_CMD_UPDATE_CLOCK_REGISTERS_ONLY_MASK,                /*!< send cmd update the CIU clock register only */
    kSDIF_CmdtoReadCEATADevice = SDIF_CMD_READ_CEATA_DEVICE_MASK, /*!< host is perform read access to CE-ATA device */
    kSDIF_CmdExpectCCS         = SDIF_CMD_CCS_EXPECTED_MASK, /*!< command expect command completion signal signal */
    kSDIF_BootModeEnable       = SDIF_CMD_ENABLE_BOOT_MASK,  /*!< this bit should only be set for mandatory boot mode */
    kSDIF_BootModeExpectAck    = SDIF_CMD_EXPECT_BOOT_ACK_MASK, /*!< boot mode expect ack */
    kSDIF_BootModeDisable      = SDIF_CMD_DISABLE_BOOT_MASK, /*!< when software set this bit along with START_CMD, CIU
                                                                terminates the boot operation*/
    kSDIF_BootModeAlternate = SDIF_CMD_BOOT_MODE_MASK,       /*!< select boot mode ,alternate or mandatory*/
    kSDIF_CmdVoltageSwitch  = SDIF_CMD_VOLT_SWITCH_MASK,     /*!< this bit set for CMD11 only */
    kSDIF_CmdDataUseHoldReg = SDIF_CMD_USE_HOLD_REG_MASK,    /*!< cmd and data send to card through the HOLD register*/
};

/*! @brief _sdif_command_type The command type */
enum
{
    kCARD_CommandTypeNormal  = 0U, /*!< Normal command */
    kCARD_CommandTypeSuspend = 1U, /*!< Suspend command */
    kCARD_CommandTypeResume  = 2U, /*!< Resume command */
    kCARD_CommandTypeAbort   = 3U, /*!< Abort command */
};

/*!
 * @brief _sdif_response_type The command response type.
 *
 * Define the command response type from card to host controller.
 */
enum
{
    kCARD_ResponseTypeNone = 0U, /*!< Response type: none */
    kCARD_ResponseTypeR1   = 1U, /*!< Response type: R1 */
    kCARD_ResponseTypeR1b  = 2U, /*!< Response type: R1b */
    kCARD_ResponseTypeR2   = 3U, /*!< Response type: R2 */
    kCARD_ResponseTypeR3   = 4U, /*!< Response type: R3 */
    kCARD_ResponseTypeR4   = 5U, /*!< Response type: R4 */
    kCARD_ResponseTypeR5   = 6U, /*!< Response type: R5 */
    kCARD_ResponseTypeR5b  = 7U, /*!< Response type: R5b */
    kCARD_ResponseTypeR6   = 8U, /*!< Response type: R6 */
    kCARD_ResponseTypeR7   = 9U, /*!< Response type: R7 */
};

/*! @brief _sdif_interrupt_mask define the interrupt mask flags */
enum
{
    kSDIF_CardDetect                  = SDIF_INTMASK_CDET_MASK,  /*!< mask for card detect */
    kSDIF_ResponseError               = SDIF_INTMASK_RE_MASK,    /*!< command response error */
    kSDIF_CommandDone                 = SDIF_INTMASK_CDONE_MASK, /*!< command transfer over*/
    kSDIF_DataTransferOver            = SDIF_INTMASK_DTO_MASK,   /*!< data transfer over flag*/
    kSDIF_WriteFIFORequest            = SDIF_INTMASK_TXDR_MASK,  /*!< write FIFO request */
    kSDIF_ReadFIFORequest             = SDIF_INTMASK_RXDR_MASK,  /*!< read FIFO request */
    kSDIF_ResponseCRCError            = SDIF_INTMASK_RCRC_MASK,  /*!< response CRC error */
    kSDIF_DataCRCError                = SDIF_INTMASK_DCRC_MASK,  /*!< data CRC error */
    kSDIF_ResponseTimeout             = SDIF_INTMASK_RTO_MASK,   /*!< response timeout */
    kSDIF_DataReadTimeout             = SDIF_INTMASK_DRTO_MASK,  /*!< read data timeout */
    kSDIF_DataStarvationByHostTimeout = SDIF_INTMASK_HTO_MASK,   /*!< data starvation by host time out */
    kSDIF_FIFOError                   = SDIF_INTMASK_FRUN_MASK,  /*!< indicate the FIFO under run or overrun error */
    kSDIF_HardwareLockError           = SDIF_INTMASK_HLE_MASK,   /*!< hardware lock write error */
    kSDIF_DataStartBitError           = SDIF_INTMASK_SBE_MASK,   /*!< start bit error */
    kSDIF_AutoCmdDone                 = SDIF_INTMASK_ACD_MASK,   /*!< indicate the auto command done */
    kSDIF_DataEndBitError             = SDIF_INTMASK_EBE_MASK,   /*!< end bit error */
    kSDIF_SDIOInterrupt               = SDIF_INTMASK_SDIO_INT_MASK_MASK, /*!< interrupt from the SDIO card */

    kSDIF_CommandTransferStatus = kSDIF_ResponseError | kSDIF_CommandDone | kSDIF_ResponseCRCError |
                                  kSDIF_ResponseTimeout | kSDIF_DataStartBitError |
                                  kSDIF_HardwareLockError, /*!< command transfer status collection*/
    kSDIF_DataTransferStatus = kSDIF_DataTransferOver | kSDIF_WriteFIFORequest | kSDIF_ReadFIFORequest |
                               kSDIF_DataCRCError | kSDIF_DataReadTimeout | kSDIF_DataStarvationByHostTimeout |
                               kSDIF_FIFOError | kSDIF_DataStartBitError |
                               kSDIF_DataEndBitError, /*!< data transfer status collection */
    kSDIF_DataTransferError =
        kSDIF_DataCRCError | kSDIF_FIFOError | kSDIF_DataStartBitError | kSDIF_DataEndBitError | kSDIF_DataReadTimeout,
    kSDIF_AllInterruptStatus = 0x1FFFFU, /*!< all interrupt mask */

};

/*! @brief _sdif_dma_status define the internal DMA status flags */
enum
{
    kSDIF_DMATransFinishOneDescriptor = SDIF_IDSTS_TI_MASK,  /*!< DMA transfer finished for one DMA descriptor */
    kSDIF_DMARecvFinishOneDescriptor  = SDIF_IDSTS_RI_MASK,  /*!< DMA receive finished for one DMA descriptor */
    kSDIF_DMAFatalBusError            = SDIF_IDSTS_FBE_MASK, /*!< DMA fatal bus error */
    kSDIF_DMADescriptorUnavailable    = SDIF_IDSTS_DU_MASK,  /*!< DMA descriptor unavailable */
    kSDIF_DMACardErrorSummary         = SDIF_IDSTS_CES_MASK, /*!< card error summary */
    kSDIF_NormalInterruptSummary      = SDIF_IDSTS_NIS_MASK, /*!< normal interrupt summary */
    kSDIF_AbnormalInterruptSummary    = SDIF_IDSTS_AIS_MASK, /*!< abnormal interrupt summary*/

    kSDIF_DMAAllStatus = kSDIF_DMATransFinishOneDescriptor | kSDIF_DMARecvFinishOneDescriptor | kSDIF_DMAFatalBusError |
                         kSDIF_DMADescriptorUnavailable | kSDIF_DMACardErrorSummary | kSDIF_NormalInterruptSummary |
                         kSDIF_AbnormalInterruptSummary,

};

/*! @brief _sdif_dma_descriptor_flag define the internal DMA descriptor flag
 *  @deprecated Do not use this enum anymore, please use SDIF_DMA_DESCRIPTOR_XXX_FLAG instead.
 */
enum
{
    kSDIF_DisableCompleteInterrupt = 0x2U,     /*!< disable the complete interrupt flag for the ends
                                           in the buffer pointed to by this descriptor*/
    kSDIF_DMADescriptorDataBufferEnd   = 0x4U, /*!< indicate this descriptor contain the last data buffer of data */
    kSDIF_DMADescriptorDataBufferStart = 0x8U, /*!< indicate this descriptor contain the first data buffer
                                            of data,if first buffer size is 0,next descriptor contain
                                            the begin of the data*/

    kSDIF_DMASecondAddrChained = 0x10U,            /*!< indicate that the second addr in the descriptor is the
                                               next descriptor addr not the data buffer */
    kSDIF_DMADescriptorEnd      = 0x20U,           /*!< indicate that the descriptor list reached its final descriptor*/
    kSDIF_DMADescriptorOwnByDMA = (int)0x80000000, /*!< indicate the descriptor is own by SD/MMC DMA */
};

/*! @brief define the internal DMA mode */
typedef enum _sdif_dma_mode
{
    kSDIF_ChainDMAMode = 0x01U, /* one descriptor with one buffer,but one descriptor point to another */
    kSDIF_DualDMAMode  = 0x02U, /* dual mode is one descriptor with two buffer */
} sdif_dma_mode_t;

/*! @brief define the internal DMA descriptor */
typedef struct _sdif_dma_descriptor
{
    uint32_t dmaDesAttribute;           /*!< internal DMA attribute control and status */
    uint32_t dmaDataBufferSize;         /*!< internal DMA transfer buffer size control */
    const uint32_t *dmaDataBufferAddr0; /*!< internal DMA buffer 0 addr ,the buffer size must be 32bit aligned */
    const uint32_t *dmaDataBufferAddr1; /*!< internal DMA buffer 1 addr ,the buffer size must be 32bit aligned */

} sdif_dma_descriptor_t;

/*! @brief Defines the internal DMA configure structure. */
typedef struct _sdif_dma_config
{
    bool enableFixBurstLen; /*!< fix burst len enable/disable flag,When set, the AHB will
                             use only SINGLE, INCR4, INCR8 or INCR16 during start of
                             normal burst transfers. When reset, the AHB will use SINGLE
                             and INCR burst transfer operations */

    sdif_dma_mode_t mode; /*!< define the DMA mode */

    uint8_t dmaDesSkipLen; /*!< define the descriptor skip length ,the length between two descriptor
                               this field is special for dual DMA mode */

    uint32_t *dmaDesBufferStartAddr; /*!< internal DMA descriptor start address*/
    uint32_t dmaDesBufferLen;        /*!< internal DMA buffer descriptor buffer len ,user need to pay attention to the
                                        dma descriptor buffer length if it is bigger enough for your transfer */

} sdif_dma_config_t;

/*!
 * @brief Card data descriptor
 */
typedef struct _sdif_data
{
    bool streamTransfer;      /*!< indicate this is a stream data transfer command */
    bool enableAutoCommand12; /*!< indicate if auto stop will send when data transfer over */
    bool enableIgnoreError;   /*!< indicate if enable ignore error when transfer data */

    size_t blockSize;       /*!< Block size, take care when configure this parameter */
    uint32_t blockCount;    /*!< Block count */
    uint32_t *rxData;       /*!< data buffer to receive */
    const uint32_t *txData; /*!< data buffer to transfer */
} sdif_data_t;

/*!
 * @brief Card command descriptor
 *
 * Define card command-related attribute.
 */
typedef struct _sdif_command
{
    uint32_t index;              /*!< Command index */
    uint32_t argument;           /*!< Command argument */
    uint32_t response[4U];       /*!< Response for this command */
    uint32_t type;               /*!< define the command type */
    uint32_t responseType;       /*!< Command response type */
    uint32_t flags;              /*!< Cmd flags */
    uint32_t responseErrorFlags; /*!< response error flags, need to check the flags when
                                    receive the cmd response */
} sdif_command_t;

/*! @brief Transfer state */
typedef struct _sdif_transfer
{
    sdif_data_t *data;       /*!< Data to transfer */
    sdif_command_t *command; /*!< Command to send */
} sdif_transfer_t;

/*! @brief Data structure to initialize the sdif */
typedef struct _sdif_config
{
    uint8_t responseTimeout;        /*!< command response timeout value */
    uint32_t cardDetDebounce_Clock; /*!< define the debounce clock count which will used in
                                        card detect logic,typical value is 5-25ms */
    uint32_t dataTimeout;           /*!< data timeout value  */
} sdif_config_t;

/*!
 * @brief SDIF capability information.
 * Defines a structure to get the capability information of SDIF.
 */
typedef struct _sdif_capability
{
    uint32_t sdVersion;      /*!< support SD card/sdio version */
    uint32_t mmcVersion;     /*!< support emmc card version */
    uint32_t maxBlockLength; /*!< Maximum block length united as byte */
    uint32_t maxBlockCount;  /*!< Maximum byte count can be transfered */
    uint32_t flags;          /*!< Capability flags to indicate the support information */
} sdif_capability_t;

/*! @brief sdif callback functions. */
typedef struct _sdif_transfer_callback
{
    void (*cardInserted)(SDIF_Type *base, void *userData);      /*!< card insert call back */
    void (*cardRemoved)(SDIF_Type *base, void *userData);       /*!< card remove call back */
    void (*SDIOInterrupt)(SDIF_Type *base, void *userData);     /*!< SDIO card interrupt occurs */
    void (*DMADesUnavailable)(SDIF_Type *base, void *userData); /*!< DMA descriptor unavailable */
    void (*CommandReload)(SDIF_Type *base, void *userData);     /*!< command buffer full,need re-load */
    void (*TransferComplete)(SDIF_Type *base,
                             void *handle,
                             status_t status,
                             void *userData); /*!< Transfer complete callback */
} sdif_transfer_callback_t;

/*!
 * @brief sdif handle
 *
 * Defines the structure to save the sdif state information and callback function. The detail interrupt status when
 * send command or transfer data can be obtained from interruptFlags field by using mask defined in
 * sdif_interrupt_flag_t;
 * @note All the fields except interruptFlags and transferredWords must be allocated by the user.
 */
typedef struct _sdif_handle
{
    /* Transfer parameter */
    sdif_data_t *volatile data;       /*!< Data to transfer */
    sdif_command_t *volatile command; /*!< Command to send */

    /* Transfer status */
    volatile uint32_t transferredWords; /*!< Words transferred by polling way */

    /* Callback functions */
    sdif_transfer_callback_t callback; /*!< Callback function */
    void *userData;                    /*!< Parameter for transfer complete callback */
} sdif_handle_t;

/*! @brief sdif transfer function. */
typedef status_t (*sdif_transfer_function_t)(SDIF_Type *base, sdif_transfer_t *content);

/*! @brief sdif host descriptor */
typedef struct _sdif_host
{
    SDIF_Type *base;                   /*!< sdif peripheral base address */
    uint32_t sourceClock_Hz;           /*!< sdif source clock frequency united in Hz */
    sdif_config_t config;              /*!< sdif configuration */
    sdif_transfer_function_t transfer; /*!< sdif transfer function */
    sdif_capability_t capability;      /*!< sdif capability information */
} sdif_host_t;

/*************************************************************************************************
 * API
 ************************************************************************************************/
#if defined(__cplusplus)
extern "C" {
#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);

/*!
 * @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);

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

#if defined(FSL_FEATURE_SDIF_ONE_INSTANCE_SUPPORT_TWO_CARD) && FSL_FEATURE_SDIF_ONE_INSTANCE_SUPPORT_TWO_CARD
/*!
 * @brief SDIF module enable/disable card0 clock.
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableCardClock(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CLKENA |= SDIF_CLKENA_CCLK0_ENABLE_MASK;
    }
    else
    {
        base->CLKENA &= SDIF_CLKENA_CCLK0_ENABLE_MASK;
    }
}

/*!
 * @brief SDIF module enable/disable card1 clock.
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableCard1Clock(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CLKENA |= SDIF_CLKENA_CCLK1_ENABLE_MASK;
    }
    else
    {
        base->CLKENA &= ~SDIF_CLKENA_CCLK1_ENABLE_MASK;
    }
}

/*!
 * @brief SDIF module enable/disable module disable the card clock
 * to enter low power mode when card is idle,for SDIF cards, if
 * interrupts must be detected, clock should not be stopped
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableLowPowerMode(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CLKENA |= SDIF_CLKENA_CCLK0_LOW_POWER_MASK;
    }
    else
    {
        base->CLKENA &= ~SDIF_CLKENA_CCLK0_LOW_POWER_MASK;
    }
}

/*!
 * @brief SDIF module enable/disable module disable the card clock
 * to enter low power mode when card is idle,for SDIF cards, if
 * interrupts must be detected, clock should not be stopped
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableCard1LowPowerMode(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CLKENA |= SDIF_CLKENA_CCLK1_LOW_POWER_MASK;
    }
    else
    {
        base->CLKENA &= ~SDIF_CLKENA_CCLK1_LOW_POWER_MASK;
    }
}

/*!
 * @brief enable/disable the card0 power.
 * once turn power on, software should wait for regulator/switch
 * ramp-up time before trying to initialize card.
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag.
 */
static inline void SDIF_EnableCardPower(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->PWREN |= SDIF_PWREN_POWER_ENABLE0_MASK;
    }
    else
    {
        base->PWREN &= ~SDIF_PWREN_POWER_ENABLE0_MASK;
    }
}

/*!
 * @brief enable/disable the card1 power.
 * once turn power on, software should wait for regulator/switch
 * ramp-up time before trying to initialize card.
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag.
 */
static inline void SDIF_EnableCard1Power(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->PWREN |= SDIF_PWREN_POWER_ENABLE1_MASK;
    }
    else
    {
        base->PWREN &= ~SDIF_PWREN_POWER_ENABLE1_MASK;
    }
}

/*!
 * @brief set card0 data bus width
 * @param base SDIF peripheral base address.
 * @param type data bus width type
 */
void SDIF_SetCardBusWidth(SDIF_Type *base, sdif_bus_width_t type);

/*!
 * @brief set card1 data bus width
 * @param base SDIF peripheral base address.
 * @param type data bus width type
 */
void SDIF_SetCard1BusWidth(SDIF_Type *base, sdif_bus_width_t type);

/*!
 * @brief SDIF module detect card0 insert status function.
 * @param base SDIF peripheral base address.
 * @param data3 indicate use data3 as card insert detect pin
 * @retval 1 card is inserted
 *         0 card is removed
 */
static inline uint32_t SDIF_DetectCardInsert(SDIF_Type *base, bool data3)
{
    if (data3)
    {
        return (base->STATUS & SDIF_STATUS_DATA_3_STATUS_MASK) == SDIF_STATUS_DATA_3_STATUS_MASK ? 1UL : 0UL;
    }
    else
    {
        return (base->CDETECT & SDIF_CDETECT_CARD0_DETECT_MASK) == 0UL ? 1UL : 0UL;
    }
}

/*!
 * @brief SDIF module detect card1 insert status function.
 * @param base SDIF peripheral base address.
 * @param data3 indicate use data3 as card insert detect pin
 * @retval 1 card is inserted
 *         0 card is removed
 */
static inline uint32_t SDIF_DetectCard1Insert(SDIF_Type *base, bool data3)
{
    if (data3)
    {
        return (base->STATUS & SDIF_STATUS_DATA_3_STATUS_MASK) == SDIF_STATUS_DATA_3_STATUS_MASK ? 1UL : 0UL;
    }
    else
    {
        return (base->CDETECT & SDIF_CDETECT_CARD1_DETECT_MASK) == 0UL ? 1UL : 0UL;
    }
}
#else
/*!
 * @brief SDIF module enable/disable card clock.
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableCardClock(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CLKENA |= SDIF_CLKENA_CCLK_ENABLE_MASK;
    }
    else
    {
        base->CLKENA &= ~SDIF_CLKENA_CCLK_ENABLE_MASK;
    }
}

/*!
 * @brief SDIF module enable/disable module disable the card clock
 * to enter low power mode when card is idle,for SDIF cards, if
 * interrupts must be detected, clock should not be stopped
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableLowPowerMode(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CLKENA |= SDIF_CLKENA_CCLK_LOW_POWER_MASK;
    }
    else
    {
        base->CLKENA &= ~SDIF_CLKENA_CCLK_LOW_POWER_MASK;
    }
}

/*!
 * @brief enable/disable the card power.
 * once turn power on, software should wait for regulator/switch
 * ramp-up time before trying to initialize card.
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag.
 */
static inline void SDIF_EnableCardPower(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->PWREN |= SDIF_PWREN_POWER_ENABLE_MASK;
    }
    else
    {
        base->PWREN &= ~SDIF_PWREN_POWER_ENABLE_MASK;
    }
}

/*!
 * @brief set card data bus width
 * @param base SDIF peripheral base address.
 * @param type bus width type
 */
void SDIF_SetCardBusWidth(SDIF_Type *base, sdif_bus_width_t type);

/*!
 * @brief SDIF module detect card insert status function.
 * @param base SDIF peripheral base address.
 * @param data3 indicate use data3 as card insert detect pin
 * @retval 1 card is inserted
 *         0 card is removed
 */
static inline uint32_t SDIF_DetectCardInsert(SDIF_Type *base, bool data3)
{
    if (data3)
    {
        return (base->STATUS & SDIF_STATUS_DATA_3_STATUS_MASK) == SDIF_STATUS_DATA_3_STATUS_MASK ? 1UL : 0UL;
    }
    else
    {
        return (base->CDETECT & SDIF_CDETECT_CARD_DETECT_MASK) == 0UL ? 1UL : 0UL;
    }
}
#endif

/*!
 * @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);

/*!
 * @brief reset the different block of the interface.
 * @param base SDIF peripheral base address.
 * @param mask indicate which block to reset.
 * @param timeout timeout value,set to wait the bit self clear
 * @return reset result.
 */
bool SDIF_Reset(SDIF_Type *base, uint32_t mask, uint32_t timeout);

/*!
 * @brief get the card write protect status
 * @param base SDIF peripheral base address.
 */
static inline uint32_t SDIF_GetCardWriteProtect(SDIF_Type *base)
{
    return base->WRTPRT & SDIF_WRTPRT_WRITE_PROTECT_MASK;
}

/*!
 * @brief toggle state on hardware reset PIN
 * This is used which card has a reset PIN typically.
 * @param base SDIF peripheral base address.
 */
static inline void SDIF_AssertHardwareReset(SDIF_Type *base)
{
    base->RST_N &= ~SDIF_RST_N_CARD_RESET_MASK;
}

/*!
 * @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 cmd 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);

/*!
 * @brief SDIF enable/disable global interrupt
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableGlobalInterrupt(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CTRL |= SDIF_CTRL_INT_ENABLE_MASK;
    }
    else
    {
        base->CTRL &= ~SDIF_CTRL_INT_ENABLE_MASK;
    }
}

/*!
 * @brief SDIF enable interrupt
 * @param base SDIF peripheral base address.
 * @param mask mask
 */
static inline void SDIF_EnableInterrupt(SDIF_Type *base, uint32_t mask)
{
    base->INTMASK |= mask;
}

/*!
 * @brief SDIF disable interrupt
 * @param base SDIF peripheral base address.
 * @param mask mask
 */
static inline void SDIF_DisableInterrupt(SDIF_Type *base, uint32_t mask)
{
    base->INTMASK &= ~mask;
}

/*!
 * @brief SDIF get interrupt status
 * @param base SDIF peripheral base address.
 */
static inline uint32_t SDIF_GetInterruptStatus(SDIF_Type *base)
{
    return base->MINTSTS;
}

/*!
 * @brief SDIF get enabled interrupt status
 * @param base SDIF peripheral base address.
 */
static inline uint32_t SDIF_GetEnabledInterruptStatus(SDIF_Type *base)
{
    uint32_t intStatus = base->MINTSTS;

    return intStatus & base->INTMASK;
}

/*!
 * @brief SDIF clear interrupt status
 * @param base SDIF peripheral base address.
 * @param mask mask to clear
 */
static inline void SDIF_ClearInterruptStatus(SDIF_Type *base, uint32_t mask)
{
    base->RINTSTS &= mask;
}

/*!
 * @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);

/*!
 * @brief SDIF enable DMA interrupt
 * @param base SDIF peripheral base address.
 * @param mask mask to set
 */
static inline void SDIF_EnableDmaInterrupt(SDIF_Type *base, uint32_t mask)
{
    base->IDINTEN |= mask;
}

/*!
 * @brief SDIF disable DMA interrupt
 * @param base SDIF peripheral base address.
 * @param mask mask to clear
 */
static inline void SDIF_DisableDmaInterrupt(SDIF_Type *base, uint32_t mask)
{
    base->IDINTEN &= ~mask;
}

/*!
 * @brief SDIF get internal DMA status
 * @param base SDIF peripheral base address.
 * @return the internal DMA status register
 */
static inline uint32_t SDIF_GetInternalDMAStatus(SDIF_Type *base)
{
    return base->IDSTS;
}

/*!
 * @brief SDIF get enabled internal DMA interrupt status
 * @param base SDIF peripheral base address.
 * @return the internal DMA status register
 */
static inline uint32_t SDIF_GetEnabledDMAInterruptStatus(SDIF_Type *base)
{
    uint32_t intStatus = base->IDSTS;

    return intStatus & base->IDINTEN;
}
/*!
 * @brief SDIF clear internal DMA status
 * @param base SDIF peripheral base address.
 * @param mask mask to clear
 */
static inline void SDIF_ClearInternalDMAStatus(SDIF_Type *base, uint32_t mask)
{
    base->IDSTS &= mask;
}

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

/*!
 * @brief SDIF internal DMA enable
 * @param base SDIF peripheral base address.
 * @param enable internal DMA enable or disable flag.
 */
static inline void SDIF_EnableInternalDMA(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        /* use internal DMA interface */
        base->CTRL |= SDIF_CTRL_USE_INTERNAL_DMAC_MASK;
        /* enable the internal SD/MMC DMA */
        base->BMOD |= SDIF_BMOD_DE_MASK;
    }
    else
    {
        /* use internal DMA interface */
        base->CTRL &= ~SDIF_CTRL_USE_INTERNAL_DMAC_MASK;
        /* enable the internal SD/MMC DMA */
        base->BMOD &= ~SDIF_BMOD_DE_MASK;
    }
}

/*!
 * @brief SDIF send read wait to SDIF card function
 * @param base SDIF peripheral base address.
 */
static inline void SDIF_SendReadWait(SDIF_Type *base)
{
    base->CTRL |= SDIF_CTRL_READ_WAIT_MASK;
}

/*!
 * @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 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);

/*!
 * @brief SDIF enable/disable CE-ATA card interrupt
 * this bit should set together with the card register
 * @param base SDIF peripheral base address.
 * @param enable enable/disable flag
 */
static inline void SDIF_EnableCEATAInterrupt(SDIF_Type *base, bool enable)
{
    if (enable)
    {
        base->CTRL |= SDIF_CTRL_CEATA_DEVICE_INTERRUPT_STATUS_MASK;
    }
    else
    {
        base->CTRL &= ~SDIF_CTRL_CEATA_DEVICE_INTERRUPT_STATUS_MASK;
    }
}

/*!
 * @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 handle handle
 * @param dmaConfig 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 transfer transfer configuration collection
 */
status_t SDIF_TransferNonBlocking(SDIF_Type *base,
                                  sdif_handle_t *handle,
                                  sdif_dma_config_t *dmaConfig,
                                  sdif_transfer_t *transfer);

/*!
 * @brief SDIF transfer function data/cmd in a blocking way
 * @param base SDIF peripheral base address.
 * @param dmaConfig 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 transfer transfer configuration collection
 */
status_t SDIF_TransferBlocking(SDIF_Type *base, sdif_dma_config_t *dmaConfig, sdif_transfer_t *transfer);

/*!
 * @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 dmaConfig DMA config pointer
 */
status_t SDIF_ReleaseDMADescriptor(SDIF_Type *base, sdif_dma_config_t *dmaConfig);

/*!
 * @brief SDIF return the controller capability
 * @param base SDIF peripheral base address.
 * @param capability capability pointer
 */
void SDIF_GetCapability(SDIF_Type *base, sdif_capability_t *capability);

/*!
 * @brief SDIF return the controller status
 * @param base SDIF peripheral base address.
 */
static inline uint32_t SDIF_GetControllerStatus(SDIF_Type *base)
{
    return base->STATUS;
}

/*!
 * @brief SDIF send command  complete signal disable to CE-ATA card
 * @param base SDIF peripheral base address.
 * @param withAutoStop auto stop flag
 */
static inline void SDIF_SendCCSD(SDIF_Type *base, bool withAutoStop)
{
    if (withAutoStop)
    {
        base->CTRL |= SDIF_CTRL_SEND_CCSD_MASK | SDIF_CTRL_SEND_AUTO_STOP_CCSD_MASK;
    }
    else
    {
        base->CTRL |= SDIF_CTRL_SEND_CCSD_MASK;
    }
}

/*!
 * @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_HZ 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);

/*! @} */

#if defined(__cplusplus)
}
#endif
/*! @} */

#endif /* FSL_SDIF_H_*/
