Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_MCU_K64F/drivers/fsl_sdhc.c
- Committer:
- <>
- Date:
- 2016-09-02
- Revision:
- 144:ef7eb2e8f9f7
- Child:
- 148:21d94c44109e
File content as of revision 144:ef7eb2e8f9f7:
/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification,
* are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice, this
* list
* of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* o Neither the name of Freescale Semiconductor, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fsl_sdhc.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*! @brief Clock setting */
/* Max SD clock divisor from base clock */
#define SDHC_MAX_DVS ((SDHC_SYSCTL_DVS_MASK >> SDHC_SYSCTL_DVS_SHIFT) + 1U)
#define SDHC_INITIAL_DVS (1U) /* Initial value of SD clock divisor */
#define SDHC_INITIAL_CLKFS (2U) /* Initial value of SD clock frequency selector */
#define SDHC_NEXT_DVS(x) ((x) += 1U)
#define SDHC_PREV_DVS(x) ((x) -= 1U)
#define SDHC_MAX_CLKFS ((SDHC_SYSCTL_SDCLKFS_MASK >> SDHC_SYSCTL_SDCLKFS_SHIFT) + 1U)
#define SDHC_NEXT_CLKFS(x) ((x) <<= 1U)
#define SDHC_PREV_CLKFS(x) ((x) >>= 1U)
/*! @brief ADMA table configuration */
typedef struct _sdhc_adma_table_config
{
uint32_t *admaTable; /*!< ADMA table address, can't be null if transfer way is ADMA1/ADMA2 */
uint32_t admaTableWords; /*!< ADMA table length united as words, can't be 0 if transfer way is ADMA1/ADMA2 */
} sdhc_adma_table_config_t;
/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief Get the instance.
*
* @param base SDHC peripheral base address.
* @return Instance number.
*/
static uint32_t SDHC_GetInstance(SDHC_Type *base);
/*!
* @brief Set transfer interrupt.
*
* @param base SDHC peripheral base address.
* @param usingInterruptSignal True to use IRQ signal.
*/
static void SDHC_SetTransferInterrupt(SDHC_Type *base, bool usingInterruptSignal);
/*!
* @brief Start transfer according to current transfer state
*
* @param base SDHC peripheral base address.
* @param command Command to be sent.
* @param data Data to be transferred.
*/
static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data);
/*!
* @brief Receive command response
*
* @param base SDHC peripheral base address.
* @param command Command to be sent.
*/
static void SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command);
/*!
* @brief Read DATAPORT when buffer enable bit is set.
*
* @param base SDHC peripheral base address.
* @param data Data to be read.
* @param transferredWords The number of data words have been transferred last time transaction.
* @return The number of total data words have been transferred after this time transaction.
*/
static uint32_t SDHC_ReadDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords);
/*!
* @brief Read data by using DATAPORT polling way.
*
* @param base SDHC peripheral base address.
* @param data Data to be read.
* @retval kStatus_Fail Read DATAPORT failed.
* @retval kStatus_Success Operate successfully.
*/
static status_t SDHC_ReadByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data);
/*!
* @brief Write DATAPORT when buffer enable bit is set.
*
* @param base SDHC peripheral base address.
* @param data Data to be read.
* @param transferredWords The number of data words have been transferred last time.
* @return The number of total data words have been transferred after this time transaction.
*/
static uint32_t SDHC_WriteDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords);
/*!
* @brief Write data by using DATAPORT polling way.
*
* @param base SDHC peripheral base address.
* @param data Data to be transferred.
* @retval kStatus_Fail Write DATAPORT failed.
* @retval kStatus_Success Operate successfully.
*/
static status_t SDHC_WriteByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data);
/*!
* @brief Send command by using polling way.
*
* @param base SDHC peripheral base address.
* @param command Command to be sent.
* @retval kStatus_Fail Send command failed.
* @retval kStatus_Success Operate successfully.
*/
static status_t SDHC_SendCommandBlocking(SDHC_Type *base, sdhc_command_t *command);
/*!
* @brief Transfer data by DATAPORT and polling way.
*
* @param base SDHC peripheral base address.
* @param data Data to be transferred.
* @retval kStatus_Fail Transfer data failed.
* @retval kStatus_Success Operate successfully.
*/
static status_t SDHC_TransferByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data);
/*!
* @brief Transfer data by ADMA2 and polling way.
*
* @param base SDHC peripheral base address.
* @param data Data to be transferred.
* @retval kStatus_Fail Transfer data failed.
* @retval kStatus_Success Operate successfully.
*/
static status_t SDHC_TransferByAdma2Blocking(SDHC_Type *base, sdhc_data_t *data);
/*!
* @brief Transfer data by polling way.
*
* @param dmaMode DMA mode.
* @param base SDHC peripheral base address.
* @param data Data to be transferred.
* @retval kStatus_Fail Transfer data failed.
* @retval kStatus_InvalidArgument Argument is invalid.
* @retval kStatus_Success Operate successfully.
*/
static status_t SDHC_TransferDataBlocking(sdhc_dma_mode_t dmaMode, SDHC_Type *base, sdhc_data_t *data);
/*!
* @brief Handle card detect interrupt.
*
* @param handle SDHC handle.
* @param interruptFlags Card detect related interrupt flags.
*/
static void SDHC_TransferHandleCardDetect(sdhc_handle_t *handle, uint32_t interruptFlags);
/*!
* @brief Handle command interrupt.
*
* @param base SDHC peripheral base address.
* @param handle SDHC handle.
* @param interruptFlags Command related interrupt flags.
*/
static void SDHC_TransferHandleCommand(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags);
/*!
* @brief Handle data interrupt.
*
* @param base SDHC peripheral base address.
* @param handle SDHC handle.
* @param interruptFlags Data related interrupt flags.
*/
static void SDHC_TransferHandleData(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags);
/*!
* @brief Handle SDIO card interrupt signal.
*
* @param handle SDHC handle.
*/
static void SDHC_TransferHandleSdioInterrupt(sdhc_handle_t *handle);
/*!
* @brief Handle SDIO block gap event.
*
* @param handle SDHC handle.
*/
static void SDHC_TransferHandleSdioBlockGap(sdhc_handle_t *handle);
/*******************************************************************************
* Variables
******************************************************************************/
/*! @brief SDHC internal handle pointer array */
static sdhc_handle_t *s_sdhcHandle[FSL_FEATURE_SOC_SDHC_COUNT];
/*! @brief SDHC base pointer array */
static SDHC_Type *const s_sdhcBase[] = SDHC_BASE_PTRS;
/*! @brief SDHC IRQ name array */
static const IRQn_Type s_sdhcIRQ[] = SDHC_IRQS;
/*! @brief SDHC clock array name */
static const clock_ip_name_t s_sdhcClock[] = SDHC_CLOCKS;
/*******************************************************************************
* Code
******************************************************************************/
static uint32_t SDHC_GetInstance(SDHC_Type *base)
{
uint8_t instance = 0;
while ((instance < FSL_FEATURE_SOC_SDHC_COUNT) && (s_sdhcBase[instance] != base))
{
instance++;
}
assert(instance < FSL_FEATURE_SOC_SDHC_COUNT);
return instance;
}
static void SDHC_SetTransferInterrupt(SDHC_Type *base, bool usingInterruptSignal)
{
uint32_t interruptEnabled; /* The Interrupt status flags to be enabled */
sdhc_dma_mode_t dmaMode = (sdhc_dma_mode_t)((base->PROCTL & SDHC_PROCTL_DMAS_MASK) >> SDHC_PROCTL_DMAS_SHIFT);
bool cardDetectDat3 = (bool)(base->PROCTL & SDHC_PROCTL_D3CD_MASK);
/* Disable all interrupts */
SDHC_DisableInterruptStatus(base, (uint32_t)kSDHC_AllInterruptFlags);
SDHC_DisableInterruptSignal(base, (uint32_t)kSDHC_AllInterruptFlags);
DisableIRQ(s_sdhcIRQ[SDHC_GetInstance(base)]);
interruptEnabled =
(kSDHC_CommandIndexErrorFlag | kSDHC_CommandCrcErrorFlag | kSDHC_CommandEndBitErrorFlag |
kSDHC_CommandTimeoutFlag | kSDHC_CommandCompleteFlag | kSDHC_DataTimeoutFlag | kSDHC_DataCrcErrorFlag |
kSDHC_DataEndBitErrorFlag | kSDHC_DataCompleteFlag | kSDHC_AutoCommand12ErrorFlag);
if (cardDetectDat3)
{
interruptEnabled |= (kSDHC_CardInsertionFlag | kSDHC_CardRemovalFlag);
}
switch (dmaMode)
{
case kSDHC_DmaModeAdma1:
case kSDHC_DmaModeAdma2:
interruptEnabled |= (kSDHC_DmaErrorFlag | kSDHC_DmaCompleteFlag);
break;
case kSDHC_DmaModeNo:
interruptEnabled |= (kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag);
break;
default:
break;
}
SDHC_EnableInterruptStatus(base, interruptEnabled);
if (usingInterruptSignal)
{
SDHC_EnableInterruptSignal(base, interruptEnabled);
}
}
static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data)
{
assert(command);
uint32_t flags = 0U;
sdhc_transfer_config_t sdhcTransferConfig;
sdhc_dma_mode_t dmaMode;
/* Define the flag corresponding to each response type. */
switch (command->responseType)
{
case kSDHC_ResponseTypeNone:
break;
case kSDHC_ResponseTypeR1: /* Response 1 */
flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag);
break;
case kSDHC_ResponseTypeR1b: /* Response 1 with busy */
flags |= (kSDHC_ResponseLength48BusyFlag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag);
break;
case kSDHC_ResponseTypeR2: /* Response 2 */
flags |= (kSDHC_ResponseLength136Flag | kSDHC_EnableCrcCheckFlag);
break;
case kSDHC_ResponseTypeR3: /* Response 3 */
flags |= (kSDHC_ResponseLength48Flag);
break;
case kSDHC_ResponseTypeR4: /* Response 4 */
flags |= (kSDHC_ResponseLength48Flag);
break;
case kSDHC_ResponseTypeR5: /* Response 5 */
flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag);
break;
case kSDHC_ResponseTypeR5b: /* Response 5 with busy */
flags |= (kSDHC_ResponseLength48BusyFlag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag);
break;
case kSDHC_ResponseTypeR6: /* Response 6 */
flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag);
break;
case kSDHC_ResponseTypeR7: /* Response 7 */
flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag);
break;
default:
break;
}
if (command->type == kSDHC_CommandTypeAbort)
{
flags |= kSDHC_CommandTypeAbortFlag;
}
if (data)
{
flags |= kSDHC_DataPresentFlag;
dmaMode = (sdhc_dma_mode_t)((base->PROCTL & SDHC_PROCTL_DMAS_MASK) >> SDHC_PROCTL_DMAS_SHIFT);
if (dmaMode != kSDHC_DmaModeNo)
{
flags |= kSDHC_EnableDmaFlag;
}
if (data->rxData)
{
flags |= kSDHC_DataReadFlag;
}
if (data->blockCount > 1U)
{
flags |= (kSDHC_MultipleBlockFlag | kSDHC_EnableBlockCountFlag);
if (data->enableAutoCommand12)
{
/* Enable Auto command 12. */
flags |= kSDHC_EnableAutoCommand12Flag;
}
}
if (data->blockCount > SDHC_MAX_BLOCK_COUNT)
{
sdhcTransferConfig.dataBlockSize = data->blockSize;
sdhcTransferConfig.dataBlockCount = SDHC_MAX_BLOCK_COUNT;
flags &= ~(uint32_t)kSDHC_EnableBlockCountFlag;
}
else
{
sdhcTransferConfig.dataBlockSize = data->blockSize;
sdhcTransferConfig.dataBlockCount = data->blockCount;
}
}
else
{
sdhcTransferConfig.dataBlockSize = 0U;
sdhcTransferConfig.dataBlockCount = 0U;
}
sdhcTransferConfig.commandArgument = command->argument;
sdhcTransferConfig.commandIndex = command->index;
sdhcTransferConfig.flags = flags;
SDHC_SetTransferConfig(base, &sdhcTransferConfig);
}
static void SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command)
{
assert(command);
uint32_t i;
if (command->responseType != kSDHC_ResponseTypeNone)
{
command->response[0U] = SDHC_GetCommandResponse(base, 0U);
if (command->responseType == kSDHC_ResponseTypeR2)
{
command->response[1U] = SDHC_GetCommandResponse(base, 1U);
command->response[2U] = SDHC_GetCommandResponse(base, 2U);
command->response[3U] = SDHC_GetCommandResponse(base, 3U);
i = 4U;
/* R3-R2-R1-R0(lowest 8 bit is invalid bit) has the same format as R2 format in SD specification document
after removed internal CRC7 and end bit. */
do
{
command->response[i - 1U] <<= 8U;
if (i > 1U)
{
command->response[i - 1U] |= ((command->response[i - 2U] & 0xFF000000U) >> 24U);
}
} while (i--);
}
}
}
static uint32_t SDHC_ReadDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords)
{
assert(data);
uint32_t i;
uint32_t totalWords;
uint32_t wordsCanBeRead; /* The words can be read at this time. */
uint32_t readWatermark = ((base->WML & SDHC_WML_RDWML_MASK) >> SDHC_WML_RDWML_SHIFT);
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++] = SDHC_ReadData(base);
i++;
}
return transferredWords;
}
static status_t SDHC_ReadByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data)
{
assert(data);
uint32_t totalWords;
uint32_t transferredWords = 0U;
status_t error = kStatus_Success;
totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
while ((error == kStatus_Success) && (transferredWords < totalWords))
{
while (!(SDHC_GetInterruptStatusFlags(base) & (kSDHC_BufferReadReadyFlag | kSDHC_DataErrorFlag)))
{
}
if (SDHC_GetInterruptStatusFlags(base) & kSDHC_DataErrorFlag)
{
if (!(data->enableIgnoreError))
{
error = kStatus_Fail;
}
}
if (error == kStatus_Success)
{
transferredWords = SDHC_ReadDataPort(base, data, transferredWords);
}
/* Clear buffer enable flag to trigger transfer. Clear data error flag when SDHC encounter error */
SDHC_ClearInterruptStatusFlags(base, (kSDHC_BufferReadReadyFlag | kSDHC_DataErrorFlag));
}
/* Clear data complete flag after the last read operation. */
SDHC_ClearInterruptStatusFlags(base, kSDHC_DataCompleteFlag);
return error;
}
static uint32_t SDHC_WriteDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords)
{
assert(data);
uint32_t i;
uint32_t totalWords;
uint32_t wordsCanBeWrote; /* Words can be wrote at this time. */
uint32_t writeWatermark = ((base->WML & SDHC_WML_WRWML_MASK) >> SDHC_WML_WRWML_SHIFT);
totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
/* If watermark level is equal or bigger than totalWords, transfers totalWords data.*/
if (writeWatermark >= totalWords)
{
wordsCanBeWrote = totalWords;
}
/* If watermark level is less than totalWords and left words to be sent is equal or bigger than watermark,
transfers watermark level words. */
else if ((writeWatermark < totalWords) && ((totalWords - transferredWords) >= writeWatermark))
{
wordsCanBeWrote = writeWatermark;
}
/* If watermark level is less than totalWords and left words to be sent is less than watermark, transfers left
words. */
else
{
wordsCanBeWrote = (totalWords - transferredWords);
}
i = 0U;
while (i < wordsCanBeWrote)
{
SDHC_WriteData(base, data->txData[transferredWords++]);
i++;
}
return transferredWords;
}
static status_t SDHC_WriteByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data)
{
assert(data);
uint32_t totalWords;
uint32_t transferredWords = 0U;
status_t error = kStatus_Success;
totalWords = (data->blockCount * data->blockSize) / sizeof(uint32_t);
while ((error == kStatus_Success) && (transferredWords < totalWords))
{
while (!(SDHC_GetInterruptStatusFlags(base) & (kSDHC_BufferWriteReadyFlag | kSDHC_DataErrorFlag)))
{
}
if (SDHC_GetInterruptStatusFlags(base) & kSDHC_DataErrorFlag)
{
if (!(data->enableIgnoreError))
{
error = kStatus_Fail;
}
}
if (error == kStatus_Success)
{
transferredWords = SDHC_WriteDataPort(base, data, transferredWords);
}
/* Clear buffer enable flag to trigger transfer. Clear error flag when SDHC encounter error. */
SDHC_ClearInterruptStatusFlags(base, (kSDHC_BufferWriteReadyFlag | kSDHC_DataErrorFlag));
}
/* Wait write data complete or data transfer error after the last writing operation. */
while (!(SDHC_GetInterruptStatusFlags(base) & (kSDHC_DataCompleteFlag | kSDHC_DataErrorFlag)))
{
}
if (SDHC_GetInterruptStatusFlags(base) & kSDHC_DataErrorFlag)
{
if (!(data->enableIgnoreError))
{
error = kStatus_Fail;
}
}
SDHC_ClearInterruptStatusFlags(base, (kSDHC_DataCompleteFlag | kSDHC_DataErrorFlag));
return error;
}
static status_t SDHC_SendCommandBlocking(SDHC_Type *base, sdhc_command_t *command)
{
assert(command);
status_t error = kStatus_Success;
/* Wait command complete or SDHC encounters error. */
while (!(SDHC_GetInterruptStatusFlags(base) & (kSDHC_CommandCompleteFlag | kSDHC_CommandErrorFlag)))
{
}
if (SDHC_GetInterruptStatusFlags(base) & kSDHC_CommandErrorFlag)
{
error = kStatus_Fail;
}
/* Receive response when command completes successfully. */
if (error == kStatus_Success)
{
SDHC_ReceiveCommandResponse(base, command);
}
SDHC_ClearInterruptStatusFlags(base, (kSDHC_CommandCompleteFlag | kSDHC_CommandErrorFlag));
return error;
}
static status_t SDHC_TransferByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data)
{
assert(data);
status_t error = kStatus_Success;
if (data->rxData)
{
error = SDHC_ReadByDataPortBlocking(base, data);
}
else
{
error = SDHC_WriteByDataPortBlocking(base, data);
}
return error;
}
static status_t SDHC_TransferByAdma2Blocking(SDHC_Type *base, sdhc_data_t *data)
{
status_t error = kStatus_Success;
/* Wait data complete or SDHC encounters error. */
while (!(SDHC_GetInterruptStatusFlags(base) & (kSDHC_DataCompleteFlag | kSDHC_DataErrorFlag | kSDHC_DmaErrorFlag)))
{
}
if (SDHC_GetInterruptStatusFlags(base) & (kSDHC_DataErrorFlag | kSDHC_DmaErrorFlag))
{
if (!(data->enableIgnoreError))
{
error = kStatus_Fail;
}
}
SDHC_ClearInterruptStatusFlags(
base, (kSDHC_DataCompleteFlag | kSDHC_DmaCompleteFlag | kSDHC_DataErrorFlag | kSDHC_DmaErrorFlag));
return error;
}
#if defined FSL_SDHC_ENABLE_ADMA1
#define SDHC_TransferByAdma1Blocking(base, data) SDHC_TransferByAdma2Blocking(base, data)
#endif /* FSL_SDHC_ENABLE_ADMA1 */
static status_t SDHC_TransferDataBlocking(sdhc_dma_mode_t dmaMode, SDHC_Type *base, sdhc_data_t *data)
{
status_t error = kStatus_Success;
switch (dmaMode)
{
case kSDHC_DmaModeNo:
error = SDHC_TransferByDataPortBlocking(base, data);
break;
#if defined FSL_SDHC_ENABLE_ADMA1
case kSDHC_DmaModeAdma1:
error = SDHC_TransferByAdma1Blocking(base, data);
break;
#endif /* FSL_SDHC_ENABLE_ADMA1 */
case kSDHC_DmaModeAdma2:
error = SDHC_TransferByAdma2Blocking(base, data);
break;
default:
error = kStatus_InvalidArgument;
break;
}
return error;
}
static void SDHC_TransferHandleCardDetect(sdhc_handle_t *handle, uint32_t interruptFlags)
{
assert(interruptFlags & kSDHC_CardDetectFlag);
if (interruptFlags & kSDHC_CardInsertionFlag)
{
if (handle->callback.CardInserted)
{
handle->callback.CardInserted();
}
}
else
{
if (handle->callback.CardRemoved)
{
handle->callback.CardRemoved();
}
}
}
static void SDHC_TransferHandleCommand(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags)
{
assert(interruptFlags & kSDHC_CommandFlag);
if ((interruptFlags & kSDHC_CommandErrorFlag) && (!(handle->data)) && (handle->callback.TransferComplete))
{
handle->callback.TransferComplete(base, handle, kStatus_SDHC_SendCommandFailed, handle->userData);
}
else
{
/* Receive response */
SDHC_ReceiveCommandResponse(base, handle->command);
if ((!(handle->data)) && (handle->callback.TransferComplete))
{
handle->callback.TransferComplete(base, handle, kStatus_Success, handle->userData);
}
}
}
static void SDHC_TransferHandleData(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags)
{
assert(handle->data);
assert(interruptFlags & kSDHC_DataFlag);
if ((!(handle->data->enableIgnoreError)) && (interruptFlags & (kSDHC_DataErrorFlag | kSDHC_DmaErrorFlag)) &&
(handle->callback.TransferComplete))
{
handle->callback.TransferComplete(base, handle, kStatus_SDHC_TransferDataFailed, handle->userData);
}
else
{
if (interruptFlags & kSDHC_BufferReadReadyFlag)
{
handle->transferredWords = SDHC_ReadDataPort(base, handle->data, handle->transferredWords);
}
else if (interruptFlags & kSDHC_BufferWriteReadyFlag)
{
handle->transferredWords = SDHC_WriteDataPort(base, handle->data, handle->transferredWords);
}
else if ((interruptFlags & kSDHC_DataCompleteFlag) && (handle->callback.TransferComplete))
{
handle->callback.TransferComplete(base, handle, kStatus_Success, handle->userData);
}
else
{
/* Do nothing when DMA complete flag is set. Wait until data complete flag is set. */
}
}
}
static void SDHC_TransferHandleSdioInterrupt(sdhc_handle_t *handle)
{
if (handle->callback.SdioInterrupt)
{
handle->callback.SdioInterrupt();
}
}
static void SDHC_TransferHandleSdioBlockGap(sdhc_handle_t *handle)
{
if (handle->callback.SdioBlockGap)
{
handle->callback.SdioBlockGap();
}
}
void SDHC_Init(SDHC_Type *base, const sdhc_config_t *config)
{
assert(config);
#if !defined FSL_SDHC_ENABLE_ADMA1
assert(config->dmaMode != kSDHC_DmaModeAdma1);
#endif /* FSL_SDHC_ENABLE_ADMA1 */
uint32_t proctl;
uint32_t wml;
/* Enable SDHC clock. */
CLOCK_EnableClock(s_sdhcClock[SDHC_GetInstance(base)]);
/* Reset SDHC. */
SDHC_Reset(base, kSDHC_ResetAll, 100);
proctl = base->PROCTL;
wml = base->WML;
proctl &= ~(SDHC_PROCTL_D3CD_MASK | SDHC_PROCTL_EMODE_MASK | SDHC_PROCTL_DMAS_MASK);
/* Set DAT3 as card detection pin */
if (config->cardDetectDat3)
{
proctl |= SDHC_PROCTL_D3CD_MASK;
}
/* Endian mode and DMA mode */
proctl |= (SDHC_PROCTL_EMODE(config->endianMode) | SDHC_PROCTL_DMAS(config->dmaMode));
/* Watermark level */
wml &= ~(SDHC_WML_RDWML_MASK | SDHC_WML_WRWML_MASK);
wml |= (SDHC_WML_RDWML(config->readWatermarkLevel) | SDHC_WML_WRWML(config->writeWatermarkLevel));
base->WML = wml;
base->PROCTL = proctl;
/* Disable all clock auto gated off feature because of DAT0 line logic(card buffer full status) can't be updated
correctly when clock auto gated off is enabled. */
base->SYSCTL |= (SDHC_SYSCTL_PEREN_MASK | SDHC_SYSCTL_HCKEN_MASK | SDHC_SYSCTL_IPGEN_MASK);
/* Enable interrupt status but doesn't enable interrupt signal. */
SDHC_SetTransferInterrupt(base, false);
}
void SDHC_Deinit(SDHC_Type *base)
{
/* Disable clock. */
CLOCK_DisableClock(s_sdhcClock[SDHC_GetInstance(base)]);
}
bool SDHC_Reset(SDHC_Type *base, uint32_t mask, uint32_t timeout)
{
base->SYSCTL |= (mask & (SDHC_SYSCTL_RSTA_MASK | SDHC_SYSCTL_RSTC_MASK | SDHC_SYSCTL_RSTD_MASK));
/* Delay some time to wait reset success. */
while ((base->SYSCTL & mask))
{
if (!timeout)
{
break;
}
timeout--;
}
return ((!timeout) ? false : true);
}
void SDHC_GetCapability(SDHC_Type *base, sdhc_capability_t *capability)
{
assert(capability);
uint32_t htCapability;
uint32_t hostVer;
uint32_t maxBlockLength;
hostVer = base->HOSTVER;
htCapability = base->HTCAPBLT;
/* Get the capability of SDHC. */
capability->specVersion = ((hostVer & SDHC_HOSTVER_SVN_MASK) >> SDHC_HOSTVER_SVN_SHIFT);
capability->vendorVersion = ((hostVer & SDHC_HOSTVER_VVN_MASK) >> SDHC_HOSTVER_VVN_SHIFT);
maxBlockLength = ((htCapability & SDHC_HTCAPBLT_MBL_MASK) >> SDHC_HTCAPBLT_MBL_SHIFT);
capability->maxBlockLength = (512U << maxBlockLength);
/* Other attributes not in HTCAPBLT register. */
capability->maxBlockCount = SDHC_MAX_BLOCK_COUNT;
capability->flags = (htCapability & (kSDHC_SupportAdmaFlag | kSDHC_SupportHighSpeedFlag | kSDHC_SupportDmaFlag |
kSDHC_SupportSuspendResumeFlag | kSDHC_SupportV330Flag));
#if defined FSL_FEATURE_SDHC_HAS_V300_SUPPORT && FSL_FEATURE_SDHC_HAS_V300_SUPPORT
capability->flags |= (htCapability & kSDHC_SupportV300Flag);
#endif
#if defined FSL_FEATURE_SDHC_HAS_V180_SUPPORT && FSL_FEATURE_SDHC_HAS_V180_SUPPORT
capability->flags |= (htCapability & kSDHC_SupportV180Flag);
#endif
/* eSDHC on all kinetis boards will support 4/8 bit data bus width. */
capability->flags |= (kSDHC_Support4BitFlag | kSDHC_Support8BitFlag);
}
uint32_t SDHC_SetSdClock(SDHC_Type *base, uint32_t srcClock_Hz, uint32_t busClock_Hz)
{
assert(busClock_Hz && (busClock_Hz < srcClock_Hz));
uint32_t divisor;
uint32_t prescaler;
uint32_t sysctl;
uint32_t nearestFrequency = 0;
divisor = SDHC_INITIAL_DVS;
prescaler = SDHC_INITIAL_CLKFS;
/* Disable SD clock. It should be disabled before changing the SD clock frequency.*/
base->SYSCTL &= ~SDHC_SYSCTL_SDCLKEN_MASK;
if (busClock_Hz > 0U)
{
while ((srcClock_Hz / prescaler / SDHC_MAX_DVS > busClock_Hz) && (prescaler < SDHC_MAX_CLKFS))
{
SDHC_NEXT_CLKFS(prescaler);
}
while ((srcClock_Hz / prescaler / divisor > busClock_Hz) && (divisor < SDHC_MAX_DVS))
{
SDHC_NEXT_DVS(divisor);
}
nearestFrequency = srcClock_Hz / prescaler / divisor;
SDHC_PREV_CLKFS(prescaler);
SDHC_PREV_DVS(divisor);
/* Set the SD clock frequency divisor, SD clock frequency select, data timeout counter value. */
sysctl = base->SYSCTL;
sysctl &= ~(SDHC_SYSCTL_DVS_MASK | SDHC_SYSCTL_SDCLKFS_MASK | SDHC_SYSCTL_DTOCV_MASK);
sysctl |= (SDHC_SYSCTL_DVS(divisor) | SDHC_SYSCTL_SDCLKFS(prescaler) | SDHC_SYSCTL_DTOCV(0xEU));
base->SYSCTL = sysctl;
/* Wait until the SD clock is stable. */
while (!(base->PRSSTAT & SDHC_PRSSTAT_SDSTB_MASK))
{
}
/* Enable the SD clock. */
base->SYSCTL |= SDHC_SYSCTL_SDCLKEN_MASK;
}
return nearestFrequency;
}
bool SDHC_SetCardActive(SDHC_Type *base, uint32_t timeout)
{
base->SYSCTL |= SDHC_SYSCTL_INITA_MASK;
/* Delay some time to wait card become active state. */
while (!(base->SYSCTL & SDHC_SYSCTL_INITA_MASK))
{
if (!timeout)
{
break;
}
timeout--;
}
return ((!timeout) ? false : true);
}
void SDHC_SetTransferConfig(SDHC_Type *base, const sdhc_transfer_config_t *config)
{
assert(config);
base->BLKATTR = ((base->BLKATTR & ~(SDHC_BLKATTR_BLKSIZE_MASK | SDHC_BLKATTR_BLKCNT_MASK)) |
(SDHC_BLKATTR_BLKSIZE(config->dataBlockSize) | SDHC_BLKATTR_BLKCNT(config->dataBlockCount)));
base->CMDARG = config->commandArgument;
base->XFERTYP = (((config->commandIndex << SDHC_XFERTYP_CMDINX_SHIFT) & SDHC_XFERTYP_CMDINX_MASK) |
(config->flags & (SDHC_XFERTYP_DMAEN_MASK | SDHC_XFERTYP_MSBSEL_MASK | SDHC_XFERTYP_DPSEL_MASK |
SDHC_XFERTYP_CMDTYP_MASK | SDHC_XFERTYP_BCEN_MASK | SDHC_XFERTYP_CICEN_MASK |
SDHC_XFERTYP_CCCEN_MASK | SDHC_XFERTYP_RSPTYP_MASK | SDHC_XFERTYP_DTDSEL_MASK |
SDHC_XFERTYP_AC12EN_MASK)));
}
void SDHC_EnableSdioControl(SDHC_Type *base, uint32_t mask, bool enable)
{
uint32_t proctl = base->PROCTL;
uint32_t vendor = base->VENDOR;
if (enable)
{
if (mask & kSDHC_StopAtBlockGapFlag)
{
proctl |= SDHC_PROCTL_SABGREQ_MASK;
}
if (mask & kSDHC_ReadWaitControlFlag)
{
proctl |= SDHC_PROCTL_RWCTL_MASK;
}
if (mask & kSDHC_InterruptAtBlockGapFlag)
{
proctl |= SDHC_PROCTL_IABG_MASK;
}
if (mask & kSDHC_ExactBlockNumberReadFlag)
{
vendor |= SDHC_VENDOR_EXBLKNU_MASK;
}
}
else
{
if (mask & kSDHC_StopAtBlockGapFlag)
{
proctl &= ~SDHC_PROCTL_SABGREQ_MASK;
}
if (mask & kSDHC_ReadWaitControlFlag)
{
proctl &= ~SDHC_PROCTL_RWCTL_MASK;
}
if (mask & kSDHC_InterruptAtBlockGapFlag)
{
proctl &= ~SDHC_PROCTL_IABG_MASK;
}
if (mask & kSDHC_ExactBlockNumberReadFlag)
{
vendor &= ~SDHC_VENDOR_EXBLKNU_MASK;
}
}
base->PROCTL = proctl;
base->VENDOR = vendor;
}
void SDHC_SetMmcBootConfig(SDHC_Type *base, const sdhc_boot_config_t *config)
{
assert(config);
uint32_t mmcboot;
mmcboot = base->MMCBOOT;
mmcboot |= (SDHC_MMCBOOT_DTOCVACK(config->ackTimeoutCount) | SDHC_MMCBOOT_BOOTMODE(config->bootMode) |
SDHC_MMCBOOT_BOOTBLKCNT(config->blockCount));
if (config->enableBootAck)
{
mmcboot |= SDHC_MMCBOOT_BOOTACK_MASK;
}
if (config->enableBoot)
{
mmcboot |= SDHC_MMCBOOT_BOOTEN_MASK;
}
if (config->enableAutoStopAtBlockGap)
{
mmcboot |= SDHC_MMCBOOT_AUTOSABGEN_MASK;
}
base->MMCBOOT = mmcboot;
}
status_t SDHC_SetAdmaTableConfig(SDHC_Type *base,
sdhc_dma_mode_t dmaMode,
uint32_t *table,
uint32_t tableWords,
const uint32_t *data,
uint32_t dataBytes)
{
status_t error = kStatus_Success;
const uint32_t *startAddress;
uint32_t entries;
uint32_t i;
#if defined FSL_SDHC_ENABLE_ADMA1
sdhc_adma1_descriptor_t *adma1EntryAddress;
#endif
sdhc_adma2_descriptor_t *adma2EntryAddress;
if ((((!table) || (!tableWords)) && ((dmaMode == kSDHC_DmaModeAdma1) || (dmaMode == kSDHC_DmaModeAdma2))) ||
(!data) || (!dataBytes)
#if !defined FSL_SDHC_ENABLE_ADMA1
|| (dmaMode == kSDHC_DmaModeAdma1)
#endif /* FSL_SDHC_ENABLE_ADMA1 */
)
{
error = kStatus_InvalidArgument;
}
else
{
switch (dmaMode)
{
case kSDHC_DmaModeNo:
break;
#if defined FSL_SDHC_ENABLE_ADMA1
case kSDHC_DmaModeAdma1:
startAddress = data;
/* Check if ADMA descriptor's number is enough. */
entries = ((dataBytes / SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U);
/* ADMA1 needs two descriptors to finish a transfer */
entries <<= 1U;
if (entries > ((tableWords * sizeof(uint32_t)) / sizeof(sdhc_adma1_descriptor_t)))
{
error = kStatus_OutOfRange;
}
else
{
adma1EntryAddress = (sdhc_adma1_descriptor_t *)(table);
for (i = 0U; i < entries; i += 2U)
{
/* Each descriptor for ADMA1 is 32-bit in length */
if ((dataBytes - sizeof(uint32_t) * (startAddress - data)) <=
SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)
{
/* The last piece of data, setting end flag in descriptor */
adma1EntryAddress[i] = ((uint32_t)(dataBytes - sizeof(uint32_t) * (startAddress - data))
<< SDHC_ADMA1_DESCRIPTOR_LENGTH_SHIFT);
adma1EntryAddress[i] |= kSDHC_Adma1DescriptorTypeSetLength;
adma1EntryAddress[i + 1U] =
((uint32_t)(startAddress) << SDHC_ADMA1_DESCRIPTOR_ADDRESS_SHIFT);
adma1EntryAddress[i + 1U] |=
(SDHC_ADMA1_DESCRIPTOR_TYPE_TRANSFER | SDHC_ADMA1_DESCRIPTOR_END_MASK);
}
else
{
adma1EntryAddress[i] = ((uint32_t)SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY
<< SDHC_ADMA1_DESCRIPTOR_LENGTH_SHIFT);
adma1EntryAddress[i] |= kSDHC_Adma1DescriptorTypeSetLength;
adma1EntryAddress[i + 1U] =
((uint32_t)(startAddress) << SDHC_ADMA1_DESCRIPTOR_ADDRESS_SHIFT);
adma1EntryAddress[i + 1U] |= kSDHC_Adma1DescriptorTypeTransfer;
startAddress += SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY / sizeof(uint32_t);
}
}
/* When use ADMA, disable simple DMA */
base->DSADDR = 0U;
base->ADSADDR = (uint32_t)table;
}
break;
#endif /* FSL_SDHC_ENABLE_ADMA1 */
case kSDHC_DmaModeAdma2:
startAddress = data;
/* Check if ADMA descriptor's number is enough. */
entries = ((dataBytes / SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U);
if (entries > ((tableWords * sizeof(uint32_t)) / sizeof(sdhc_adma2_descriptor_t)))
{
error = kStatus_OutOfRange;
}
else
{
adma2EntryAddress = (sdhc_adma2_descriptor_t *)(table);
for (i = 0U; i < entries; i++)
{
/* Each descriptor for ADMA2 is 64-bit in length */
if ((dataBytes - sizeof(uint32_t) * (startAddress - data)) <=
SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)
{
/* The last piece of data, setting end flag in descriptor */
adma2EntryAddress[i].address = startAddress;
adma2EntryAddress[i].attribute = ((dataBytes - sizeof(uint32_t) * (startAddress - data))
<< SDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT);
adma2EntryAddress[i].attribute |=
(kSDHC_Adma2DescriptorTypeTransfer | kSDHC_Adma2DescriptorEndFlag);
}
else
{
adma2EntryAddress[i].address = startAddress;
adma2EntryAddress[i].attribute =
(((SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY / sizeof(uint32_t)) * sizeof(uint32_t))
<< SDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT);
adma2EntryAddress[i].attribute |= kSDHC_Adma2DescriptorTypeTransfer;
startAddress += (SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY / sizeof(uint32_t));
}
}
/* When use ADMA, disable simple DMA */
base->DSADDR = 0U;
base->ADSADDR = (uint32_t)table;
}
break;
default:
break;
}
}
return error;
}
status_t SDHC_TransferBlocking(SDHC_Type *base, uint32_t *admaTable, uint32_t admaTableWords, sdhc_transfer_t *transfer)
{
assert(transfer);
assert(transfer->command); /* Command must not be NULL, data can be NULL. */
status_t error = kStatus_Success;
sdhc_dma_mode_t dmaMode = (sdhc_dma_mode_t)((base->PROCTL & SDHC_PROCTL_DMAS_MASK) >> SDHC_PROCTL_DMAS_SHIFT);
sdhc_command_t *command = transfer->command;
sdhc_data_t *data = transfer->data;
/* DATA-PORT is 32-bit align, ADMA2 4 bytes align, ADMA1 is 4096 bytes align */
if ((!command) || (data && (data->blockSize % 4U)))
{
error = kStatus_InvalidArgument;
}
else
{
/* Wait until command/data bus out of busy status. */
while (SDHC_GetPresentStatusFlags(base) & kSDHC_CommandInhibitFlag)
{
}
while (data && (SDHC_GetPresentStatusFlags(base) & kSDHC_DataInhibitFlag))
{
}
/* Update ADMA descriptor table if data isn't NULL. */
if (data && (kStatus_Success != SDHC_SetAdmaTableConfig(base, dmaMode, admaTable, admaTableWords,
(data->rxData ? data->rxData : data->txData),
(data->blockCount * data->blockSize))))
{
error = kStatus_SDHC_PrepareAdmaDescriptorFailed;
}
else
{
SDHC_StartTransfer(base, command, data);
/* Send command and receive data. */
if (kStatus_Success != SDHC_SendCommandBlocking(base, command))
{
error = kStatus_SDHC_SendCommandFailed;
}
else if (data && (kStatus_Success != SDHC_TransferDataBlocking(dmaMode, base, data)))
{
error = kStatus_SDHC_TransferDataFailed;
}
else
{
}
}
}
return error;
}
void SDHC_TransferCreateHandle(SDHC_Type *base,
sdhc_handle_t *handle,
const sdhc_transfer_callback_t *callback,
void *userData)
{
assert(handle);
assert(callback);
/* Zero the handle. */
memset(handle, 0, sizeof(*handle));
/* Set the callback. */
handle->callback.CardInserted = callback->CardInserted;
handle->callback.CardRemoved = callback->CardRemoved;
handle->callback.SdioInterrupt = callback->SdioInterrupt;
handle->callback.SdioBlockGap = callback->SdioBlockGap;
handle->callback.TransferComplete = callback->TransferComplete;
handle->userData = userData;
/* Save the handle in global variables to support the double weak mechanism. */
s_sdhcHandle[SDHC_GetInstance(base)] = handle;
/* Enable interrupt in NVIC. */
SDHC_SetTransferInterrupt(base, true);
EnableIRQ(s_sdhcIRQ[SDHC_GetInstance(base)]);
}
status_t SDHC_TransferNonBlocking(
SDHC_Type *base, sdhc_handle_t *handle, uint32_t *admaTable, uint32_t admaTableWords, sdhc_transfer_t *transfer)
{
assert(transfer);
sdhc_dma_mode_t dmaMode = (sdhc_dma_mode_t)((base->PROCTL & SDHC_PROCTL_DMAS_MASK) >> SDHC_PROCTL_DMAS_SHIFT);
status_t error = kStatus_Success;
sdhc_command_t *command = transfer->command;
sdhc_data_t *data = transfer->data;
/* DATA-PORT is 32-bit align, ADMA2 4 bytes align, ADMA1 is 4096 bytes align */
if ((!(transfer->command)) || ((transfer->data) && (transfer->data->blockSize % 4U)))
{
error = kStatus_InvalidArgument;
}
else
{
/* Wait until command/data bus out of busy status. */
if ((SDHC_GetPresentStatusFlags(base) & kSDHC_CommandInhibitFlag) ||
(data && (SDHC_GetPresentStatusFlags(base) & kSDHC_DataInhibitFlag)))
{
error = kStatus_SDHC_BusyTransferring;
}
else
{
/* Update ADMA descriptor table and reset transferred words if data isn't NULL. */
if (data && (kStatus_Success != SDHC_SetAdmaTableConfig(base, dmaMode, admaTable, admaTableWords,
(data->rxData ? data->rxData : data->txData),
(data->blockCount * data->blockSize))))
{
error = kStatus_SDHC_PrepareAdmaDescriptorFailed;
}
else
{
/* Save command and data into handle before transferring. */
handle->command = command;
handle->data = data;
handle->interruptFlags = 0U;
/* transferredWords will only be updated in ISR when transfer way is DATAPORT. */
handle->transferredWords = 0U;
SDHC_StartTransfer(base, command, data);
}
}
}
return error;
}
void SDHC_TransferHandleIRQ(SDHC_Type *base, sdhc_handle_t *handle)
{
assert(handle);
uint32_t interruptFlags;
interruptFlags = SDHC_GetInterruptStatusFlags(base);
handle->interruptFlags = interruptFlags;
if (interruptFlags & kSDHC_CardDetectFlag)
{
SDHC_TransferHandleCardDetect(handle, (interruptFlags & kSDHC_CardDetectFlag));
}
if (interruptFlags & kSDHC_CommandFlag)
{
SDHC_TransferHandleCommand(base, handle, (interruptFlags & kSDHC_CommandFlag));
}
if (interruptFlags & kSDHC_DataFlag)
{
SDHC_TransferHandleData(base, handle, (interruptFlags & kSDHC_DataFlag));
}
if (interruptFlags & kSDHC_CardInterruptFlag)
{
SDHC_TransferHandleSdioInterrupt(handle);
}
if (interruptFlags & kSDHC_BlockGapEventFlag)
{
SDHC_TransferHandleSdioBlockGap(handle);
}
SDHC_ClearInterruptStatusFlags(base, interruptFlags);
}
#if defined(SDHC)
void SDHC_DriverIRQHandler(void)
{
assert(s_sdhcHandle[0]);
SDHC_TransferHandleIRQ(SDHC, s_sdhcHandle[0]);
}
#endif
