6c5f9ffb0a
update NXP SDK to 2_12_0
1689 lines
56 KiB
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
|