/* * 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) #else /* Buffer address configured in ADMA1 descriptor must be 4KB aligned. */ || ((dmaMode == kSDHC_DmaModeAdma1) && (((uint32_t)data % SDHC_ADMA1_LENGTH_ALIGN) != 0U)) #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] |= (kSDHC_Adma1DescriptorTypeTransfer | kSDHC_Adma1DescriptorEndFlag); } 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