MCUXpresso_LPC55S69/devices/LPC55S69/drivers/fsl_sdif.c

1689 lines
56 KiB
C

/*
* 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;
uint32_t errorStatus = (uint32_t)kSDIF_ResponseError | (uint32_t)kSDIF_ResponseTimeout |
(uint32_t)kSDIF_DataStartBitError | (uint32_t)kSDIF_HardwareLockError |
(uint32_t)kSDIF_ResponseCRCError;
do
{
status = SDIF_GetInterruptStatus(base);
} while ((status & (errorStatus | (uint32_t)kSDIF_CommandDone)) == 0UL);
/* clear interrupt status flag first */
SDIF_ClearInterruptStatus(base, status & (uint32_t)kSDIF_CommandTransferStatus);
if ((status & errorStatus) != 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_OWN_BY_DMA_FLAG) !=
SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_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_OWN_BY_DMA_FLAG) !=
SDIF_DMA_DESCRIPTOR_OWN_BY_DMA_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, handle->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