#include "gw3323_sdio.h"

/* SDIO RESP Registers Address */
#define SDIO_RESP_ADDR              ((uint32_t)(SD0_BASE + 0x10))

/**
  * @brief  Initializes the sdio peripheral according to the specified
  *         parameters in the spi_init_struct.
  * @param  sdio_init_struct: pointer to a sdio_init_typedef structure that
  *         contains the configuration information for the specified SDIO peripheral.
  * @retval None
  */
void sdio_init(sdio_init_typedef* sdio_init_struct)
{
    uint32_t temp_reg = SDIO->con;
    SDIO->baud = sdio_init_struct->baud;
    temp_reg &= ~(uint32_t)(SDIO_CON_DWTH | SDIO_CON_OUTEDGE);
    temp_reg |= sdio_init_struct->dwth;
    temp_reg |= sdio_init_struct->out_edge;
    SDIO->con   = temp_reg;
}

/**
  * @brief  Enable or disable the specified SDIO peripheral.
  * @param  state: the state of the SDIO peripheral.
  *         this parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void sdio_cmd(FUNCTIONAL_STATE state)
{
    if(state) {
        SDIO->con |= SDIO_CON_SDEN;
    } else {
        SDIO->con &= (uint32_t)~SDIO_CON_SDEN;
    }
}

/**
  * @brief  Set the SDIO baud register value.
  * @param  div: specifies the baud register new value.
  * @retval None
  */
void sdio_set_baud(uint8_t baud)
{
    SDIO->baud = baud;
}

/**
  * @brief  Enable or disable the SDIO clock always output.
  * @param  state: state of the SDIO clock always output.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void sdio_clk_keep_out_cmd(FUNCTIONAL_STATE state)
{
    if(state) {
        SDIO->con |= SDIO_CON_CKEN;
    } else {
        SDIO->con &= (uint32_t)~SDIO_CON_CKEN;
    }
}

/**
  * @brief  Enable or disable the SDIO command interrupt.
  * @param  state: state of the SDIO command interrupt.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void sdio_cmd_it_cmd(FUNCTIONAL_STATE state)
{
    if(state) {
        SDIO->con |= SDIO_CON_CMDIE;
    } else {
        SDIO->con &= (uint32_t)~SDIO_CON_CMDIE;
    }
}

/**
  * @brief  Enable or disable the SDIO data interrupt.
  * @param  state: state of the SDIO data interrupt.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void sdio_data_it_cmd(FUNCTIONAL_STATE state)
{
    if(state) {
        SDIO->con |= SDIO_CON_DATIE;
    } else {
        SDIO->con &= (uint32_t)~SDIO_CON_DATIE;
    }
}

/**
  * @brief  Select the SDIO clk idle state.
  * @param  idle_sta: state of the SDIO clk idle.
  * @retval None
  */
void sdio_clk_idle_sta_sel(SDIO_CLK_IDLE_STA_TYPEDEF idle_sta)
{
    uint32_t temp_reg = SDIO->con;
    temp_reg &= (uint32_t)~SDIO_CLK_IDLE_STA_HIGH;
    temp_reg |= (uint32_t)idle_sta;
    SDIO->con = temp_reg;
}

/**
  * @brief  Get the sdio pending.
  * @param  sdio_flag: specifies the flag to set.
  *         this parameter can be one of the following values:
  *             @arg SDIO_FLAG_CMD_FINISH: SDIO command finish flag.
  *             @arg SDIO_FLAG_DATA_FINISH: SDIO data finish flag.
  *             @arg SDIO_FLAG_CMD_CRC_ERROR: SDIO command crc error flag.
  *             @arg SDIO_FLAG_NO_RESPONSE: SDIO no response received flag.
  *             @arg SDIO_FLAG_DATA_CRC_ERROR: SDIO read data crc error flag.
  *             @arg SDIO_FLAG_BUSY: SDIO bus busy flag.
  * @retval The state of sdio_flag (SET or RESET).
  */
FLAG_STATE sdio_get_flag(uint32_t sdio_flag)
{
    if ((SDIO->con & sdio_flag) != RESET) {
        return SET;
    }
    return RESET;
}

/**
  * @brief  Clear the SDIO pending.
  * @param  sdio_flag: specifies the flag to clear.
  *         this parameter can be one of the following values:
  *             @arg SDIO_FLAG_CMD_FINISH: SDIO command finish flag.
  *             @arg SDIO_FLAG_DATA_FINISH: SDIO data finish flag.
  * @retval None
  */
void sdio_clear_flag(uint32_t sdio_flag)
{
    SDIO->cpnd |= (uint32_t)sdio_flag;
}

/**
  * @brief  Initializes the SDIO Command according to the specified
  *         parameters in the sdio_cmd_init_struct and send the command.
  * @param  sdio_cmd_init_struct: pointer to a sdio_cmd_init_typedef
  *         structure that contains the configuration information for the SDIO.
  * @retval None
  */
void sdio_send_command(sdio_cmd_init_typedef *sdio_cmd_init_struct)
{
    uint32_t temp_reg = 0;
    SDIO->arg3 = sdio_cmd_init_struct->argument;
    temp_reg |= sdio_cmd_init_struct->cmd_index;
    temp_reg |= (uint32_t)sdio_cmd_init_struct->rsp_type;
    if (sdio_cmd_init_struct->ccbusy_en) {
        temp_reg |= SDIO_CCBUSY_EN;
    }
    if (sdio_cmd_init_struct->ck8_en) {
        temp_reg |= SDIO_CK8_EN;
    }
//    printf("cmd:%04x\n", temp_reg);
    SDIO->cmd = temp_reg;
}

/**
  * @brief  Returns command index of last command for which response received.
  * @param  None
  * @retval Returns command index of last command for which response received.
  */
uint8_t sdio_get_cmd_response(void)
{
    return (uint8_t)(SDIO->cmd);
}


/**
  * @brief  Returns response received from the card for the last command.
  * @param  sdio_resp: Specifies the SDIO response register.
  *          This parameter can be one of the following values:
  *             @arg SDIO_RESP1:Response Register 1
  *             @arg SDIO_RESP2:Response Register 2
  *             @arg SDIO_RESP3:Response Register 3
  *             @arg SDIO_RESP4:Response Register 4
  * @retval The Corresponding response register value.
  */
uint32_t sdio_get_response(uint32_t sdio_resp)
{
    uint32_t temp = 0;
    temp = SDIO_RESP_ADDR + sdio_resp;
    return (*(uint32_t *) temp);
}

/**
  * @brief  Returns the SDIO crc error status for sent data.
  * @param  None
  * @retval Returns the SDIO crc error status for sent data.
  */
uint8_t sdio_get_send_data_crc_sta(void)
{
    return (uint8_t)((SDIO->con>>17)&0x00000007);
}

/**
  * @brief  Read data from SDIO DMA address.
  * @param  buf: SDIO DMA addr.
  * @param  len: The lenght of data.
  * @retval None
  */
void sdio_read_data(void *buf, uint16_t len)
{
    SDIO->dma_adr = (uint32_t)buf;
    SDIO->dma_cnt = len;
}

/**
  * @brief  Write data to SDIO DMA address.
  * @param  buf: SDIO DMA addr.
  * @param  len: The lenght of data.
  * @retval None
  */
void sdio_write_data(void *buf, uint16_t len)
{
    SDIO->dma_adr = (uint32_t)buf;
    SDIO->dma_cnt = SDIO_DMA_WRD | SDIO_DMA_CDBUSY_EN | SDIO_DMA_WRD | len;
}

/**
  * @brief  Set the SDIO DMA counter register value.
  * @param  cnt: specifies the SDIO DMA counter new value.
  * @retval None
  */
void sdio_set_dma_cnt( uint16_t cnt)
{
    uint32_t temp_reg;
    temp_reg = SDIO->dma_cnt;
    temp_reg &= ~0x000007FF;
    temp_reg |= cnt;
    SDIO->dma_cnt = temp_reg;
}

/**
  * @brief  Set the SDIO DMA address register value.
  * @param  addr: specifies the SDIO DMA address register new value.
  * @retval None
  */
void sdio_set_dma_addr(uint32_t *addr)
{
    SDIO->dma_adr = (uint32_t)addr;
}
