diff options
Diffstat (limited to 'drivers/fsl_sdhc.c')
-rw-r--r-- | drivers/fsl_sdhc.c | 503 |
1 files changed, 311 insertions, 192 deletions
diff --git a/drivers/fsl_sdhc.c b/drivers/fsl_sdhc.c index 7697cdc..3151cd2 100644 --- a/drivers/fsl_sdhc.c +++ b/drivers/fsl_sdhc.c @@ -1,34 +1,28 @@ /* - * Copyright (c) 2015, Freescale Semiconductor, Inc. - * All rights reserved. + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP * - * Redistribution and use in source and binary forms, with or without - * modification, + * 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 + * 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 + * 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 + * o Neither the name of the copyright holder 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 + * 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 + * DISCLAIMED. IN NO EVENT SHALL 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 + * 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. @@ -42,14 +36,13 @@ /*! @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) +/* Typedef for interrupt handler. */ +typedef void (*sdhc_isr_t)(SDHC_Type *base, sdhc_handle_t *handle); + /*! @brief ADMA table configuration */ typedef struct _sdhc_adma_table_config { @@ -82,8 +75,9 @@ static void SDHC_SetTransferInterrupt(SDHC_Type *base, bool usingInterruptSignal * @param base SDHC peripheral base address. * @param command Command to be sent. * @param data Data to be transferred. + * @param DMA mode selection */ -static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data); +static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data, sdhc_dma_mode_t dmaMode); /*! * @brief Receive command response @@ -91,7 +85,7 @@ static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_da * @param base SDHC peripheral base address. * @param command Command to be sent. */ -static void SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command); +static status_t SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command); /*! * @brief Read DATAPORT when buffer enable bit is set. @@ -227,8 +221,13 @@ static SDHC_Type *const s_sdhcBase[] = SDHC_BASE_PTRS; /*! @brief SDHC IRQ name array */ static const IRQn_Type s_sdhcIRQ[] = SDHC_IRQS; +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /*! @brief SDHC clock array name */ static const clock_ip_name_t s_sdhcClock[] = SDHC_CLOCKS; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/* SDHC ISR for transactional APIs. */ +static sdhc_isr_t s_sdhcIsr; /******************************************************************************* * Code @@ -237,12 +236,12 @@ static uint32_t SDHC_GetInstance(SDHC_Type *base) { uint8_t instance = 0; - while ((instance < FSL_FEATURE_SOC_SDHC_COUNT) && (s_sdhcBase[instance] != base)) + while ((instance < ARRAY_SIZE(s_sdhcBase)) && (s_sdhcBase[instance] != base)) { instance++; } - assert(instance < FSL_FEATURE_SOC_SDHC_COUNT); + assert(instance < ARRAY_SIZE(s_sdhcBase)); return instance; } @@ -250,7 +249,6 @@ static uint32_t SDHC_GetInstance(SDHC_Type *base) 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 */ @@ -261,23 +259,12 @@ static void SDHC_SetTransferInterrupt(SDHC_Type *base, bool usingInterruptSignal interruptEnabled = (kSDHC_CommandIndexErrorFlag | kSDHC_CommandCrcErrorFlag | kSDHC_CommandEndBitErrorFlag | kSDHC_CommandTimeoutFlag | kSDHC_CommandCompleteFlag | kSDHC_DataTimeoutFlag | kSDHC_DataCrcErrorFlag | - kSDHC_DataEndBitErrorFlag | kSDHC_DataCompleteFlag | kSDHC_AutoCommand12ErrorFlag); + kSDHC_DataEndBitErrorFlag | kSDHC_DataCompleteFlag | kSDHC_AutoCommand12ErrorFlag | kSDHC_BufferReadReadyFlag | + kSDHC_BufferWriteReadyFlag | kSDHC_DmaErrorFlag | kSDHC_DmaCompleteFlag); 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) @@ -286,50 +273,47 @@ static void SDHC_SetTransferInterrupt(SDHC_Type *base, bool usingInterruptSignal } } -static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data) +static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data, sdhc_dma_mode_t dmaMode) { - assert(command); - uint32_t flags = 0U; - sdhc_transfer_config_t sdhcTransferConfig; - sdhc_dma_mode_t dmaMode; + sdhc_transfer_config_t sdhcTransferConfig = {0}; /* Define the flag corresponding to each response type. */ switch (command->responseType) { - case kSDHC_ResponseTypeNone: + case kCARD_ResponseTypeNone: break; - case kSDHC_ResponseTypeR1: /* Response 1 */ + case kCARD_ResponseTypeR1: /* Response 1 */ flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag); break; - case kSDHC_ResponseTypeR1b: /* Response 1 with busy */ + case kCARD_ResponseTypeR1b: /* Response 1 with busy */ flags |= (kSDHC_ResponseLength48BusyFlag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag); break; - case kSDHC_ResponseTypeR2: /* Response 2 */ + case kCARD_ResponseTypeR2: /* Response 2 */ flags |= (kSDHC_ResponseLength136Flag | kSDHC_EnableCrcCheckFlag); break; - case kSDHC_ResponseTypeR3: /* Response 3 */ + case kCARD_ResponseTypeR3: /* Response 3 */ flags |= (kSDHC_ResponseLength48Flag); break; - case kSDHC_ResponseTypeR4: /* Response 4 */ + case kCARD_ResponseTypeR4: /* Response 4 */ flags |= (kSDHC_ResponseLength48Flag); break; - case kSDHC_ResponseTypeR5: /* Response 5 */ - flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag); + case kCARD_ResponseTypeR5: /* Response 5 */ + flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag); break; - case kSDHC_ResponseTypeR5b: /* Response 5 with busy */ + case kCARD_ResponseTypeR5b: /* Response 5 with busy */ flags |= (kSDHC_ResponseLength48BusyFlag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag); break; - case kSDHC_ResponseTypeR6: /* Response 6 */ + case kCARD_ResponseTypeR6: /* Response 6 */ flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag); break; - case kSDHC_ResponseTypeR7: /* Response 7 */ + case kCARD_ResponseTypeR7: /* Response 7 */ flags |= (kSDHC_ResponseLength48Flag | kSDHC_EnableCrcCheckFlag | kSDHC_EnableIndexCheckFlag); break; default: break; } - if (command->type == kSDHC_CommandTypeAbort) + if (command->type == kCARD_CommandTypeAbort) { flags |= kSDHC_CommandTypeAbortFlag; } @@ -337,7 +321,7 @@ static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_da 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; @@ -355,18 +339,9 @@ static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_da 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; - } + sdhcTransferConfig.dataBlockSize = data->blockSize; + sdhcTransferConfig.dataBlockCount = data->blockCount; } else { @@ -380,16 +355,14 @@ static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_da SDHC_SetTransferConfig(base, &sdhcTransferConfig); } -static void SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command) +static status_t SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command) { - assert(command); - uint32_t i; - if (command->responseType != kSDHC_ResponseTypeNone) + if (command->responseType != kCARD_ResponseTypeNone) { command->response[0U] = SDHC_GetCommandResponse(base, 0U); - if (command->responseType == kSDHC_ResponseTypeR2) + if (command->responseType == kCARD_ResponseTypeR2) { command->response[1U] = SDHC_GetCommandResponse(base, 1U); command->response[2U] = SDHC_GetCommandResponse(base, 2U); @@ -408,17 +381,38 @@ static void SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command } while (i--); } } + /* check response error flag */ + if ((command->responseErrorFlags != 0U) && + ((command->responseType == kCARD_ResponseTypeR1) || (command->responseType == kCARD_ResponseTypeR1b) || + (command->responseType == kCARD_ResponseTypeR6) || (command->responseType == kCARD_ResponseTypeR5))) + { + if (((command->responseErrorFlags) & (command->response[0U])) != 0U) + { + return kStatus_SDHC_SendCommandFailed; + } + } + + return kStatus_Success; } 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); + /* + * Add non aligned access support ,user need make sure your buffer size is big + * enough to hold the data,in other words,user need make sure the buffer size + * is 4 byte aligned + */ + 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. */ @@ -451,12 +445,21 @@ static uint32_t SDHC_ReadDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t t 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; + /* + * Add non aligned access support ,user need make sure your buffer size is big + * enough to hold the data,in other words,user need make sure the buffer size + * is 4 byte aligned + */ + 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)); while ((error == kStatus_Success) && (transferredWords < totalWords)) @@ -476,26 +479,34 @@ static status_t SDHC_ReadByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data) { 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 buffer ready and error */ + SDHC_ClearInterruptStatusFlags(base, kSDHC_BufferReadReadyFlag | kSDHC_DataErrorFlag); } /* Clear data complete flag after the last read operation. */ - SDHC_ClearInterruptStatusFlags(base, kSDHC_DataCompleteFlag); + SDHC_ClearInterruptStatusFlags(base, kSDHC_DataCompleteFlag | kSDHC_DataErrorFlag); 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); + /* + * Add non aligned access support ,user need make sure your buffer size is big + * enough to hold the data,in other words,user need make sure the buffer size + * is 4 byte aligned + */ + 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.*/ @@ -528,12 +539,21 @@ static uint32_t SDHC_WriteDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t 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; + /* + * Add non aligned access support ,user need make sure your buffer size is big + * enough to hold the data,in other words,user need make sure the buffer size + * is 4 byte aligned + */ + 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); while ((error == kStatus_Success) && (transferredWords < totalWords)) @@ -569,6 +589,7 @@ static status_t SDHC_WriteByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data) error = kStatus_Fail; } } + SDHC_ClearInterruptStatusFlags(base, (kSDHC_DataCompleteFlag | kSDHC_DataErrorFlag)); return error; @@ -576,8 +597,6 @@ static status_t SDHC_WriteByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data) 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. */ @@ -592,7 +611,7 @@ static status_t SDHC_SendCommandBlocking(SDHC_Type *base, sdhc_command_t *comman /* Receive response when command completes successfully. */ if (error == kStatus_Success) { - SDHC_ReceiveCommandResponse(base, command); + error = SDHC_ReceiveCommandResponse(base, command); } SDHC_ClearInterruptStatusFlags(base, (kSDHC_CommandCompleteFlag | kSDHC_CommandErrorFlag)); @@ -602,8 +621,6 @@ static status_t SDHC_SendCommandBlocking(SDHC_Type *base, sdhc_command_t *comman static status_t SDHC_TransferByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data) { - assert(data); - status_t error = kStatus_Success; if (data->rxData) @@ -669,8 +686,6 @@ static status_t SDHC_TransferDataBlocking(sdhc_dma_mode_t dmaMode, SDHC_Type *ba static void SDHC_TransferHandleCardDetect(sdhc_handle_t *handle, uint32_t interruptFlags) { - assert(interruptFlags & kSDHC_CardDetectFlag); - if (interruptFlags & kSDHC_CardInsertionFlag) { if (handle->callback.CardInserted) @@ -689,7 +704,7 @@ static void SDHC_TransferHandleCardDetect(sdhc_handle_t *handle, uint32_t interr static void SDHC_TransferHandleCommand(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags) { - assert(interruptFlags & kSDHC_CommandFlag); + assert(handle->command); if ((interruptFlags & kSDHC_CommandErrorFlag) && (!(handle->data)) && (handle->callback.TransferComplete)) { @@ -709,7 +724,6 @@ static void SDHC_TransferHandleCommand(SDHC_Type *base, sdhc_handle_t *handle, u 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)) @@ -726,7 +740,11 @@ static void SDHC_TransferHandleData(SDHC_Type *base, sdhc_handle_t *handle, uint { handle->transferredWords = SDHC_WriteDataPort(base, handle->data, handle->transferredWords); } - else if ((interruptFlags & kSDHC_DataCompleteFlag) && (handle->callback.TransferComplete)) + else + { + } + + if ((interruptFlags & kSDHC_DataCompleteFlag) && (handle->callback.TransferComplete)) { handle->callback.TransferComplete(base, handle, kStatus_Success, handle->userData); } @@ -759,12 +777,16 @@ void SDHC_Init(SDHC_Type *base, const sdhc_config_t *config) #if !defined FSL_SDHC_ENABLE_ADMA1 assert(config->dmaMode != kSDHC_DmaModeAdma1); #endif /* FSL_SDHC_ENABLE_ADMA1 */ + assert((config->writeWatermarkLevel >= 1U) && (config->writeWatermarkLevel <= 128U)); + assert((config->readWatermarkLevel >= 1U) && (config->readWatermarkLevel <= 128U)); uint32_t proctl; uint32_t wml; +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Enable SDHC clock. */ CLOCK_EnableClock(s_sdhcClock[SDHC_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ /* Reset SDHC. */ SDHC_Reset(base, kSDHC_ResetAll, 100); @@ -798,8 +820,10 @@ void SDHC_Init(SDHC_Type *base, const sdhc_config_t *config) void SDHC_Deinit(SDHC_Type *base) { +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Disable clock. */ CLOCK_DisableClock(s_sdhcClock[SDHC_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } bool SDHC_Reset(SDHC_Type *base, uint32_t mask, uint32_t timeout) @@ -850,46 +874,86 @@ void SDHC_GetCapability(SDHC_Type *base, sdhc_capability_t *capability) uint32_t SDHC_SetSdClock(SDHC_Type *base, uint32_t srcClock_Hz, uint32_t busClock_Hz) { - assert(busClock_Hz && (busClock_Hz < srcClock_Hz)); + assert(srcClock_Hz != 0U); + assert((busClock_Hz != 0U) && (busClock_Hz <= srcClock_Hz)); - uint32_t divisor; - uint32_t prescaler; - uint32_t sysctl; - uint32_t nearestFrequency = 0; + uint32_t totalDiv = 0U; + uint32_t divisor = 0U; + uint32_t prescaler = 0U; + uint32_t sysctl = 0U; + uint32_t nearestFrequency = 0U; - divisor = SDHC_INITIAL_DVS; - prescaler = SDHC_INITIAL_CLKFS; + /* calucate total divisor first */ + totalDiv = srcClock_Hz / busClock_Hz; - /* Disable SD clock. It should be disabled before changing the SD clock frequency.*/ - base->SYSCTL &= ~SDHC_SYSCTL_SDCLKEN_MASK; - - if (busClock_Hz > 0U) + if (totalDiv != 0U) { - while ((srcClock_Hz / prescaler / SDHC_MAX_DVS > busClock_Hz) && (prescaler < SDHC_MAX_CLKFS)) + /* calucate the divisor (srcClock_Hz / divisor) <= busClock_Hz */ + if ((srcClock_Hz / totalDiv) > busClock_Hz) { - SDHC_NEXT_CLKFS(prescaler); + totalDiv++; } - while ((srcClock_Hz / prescaler / divisor > busClock_Hz) && (divisor < SDHC_MAX_DVS)) + + /* divide the total divisor to div and prescaler */ + if (totalDiv > SDHC_MAX_DVS) { - SDHC_NEXT_DVS(divisor); + prescaler = totalDiv / SDHC_MAX_DVS; + /* prescaler must be a value which equal 2^n and smaller than SDHC_MAX_CLKFS */ + while (((SDHC_MAX_CLKFS % prescaler) != 0U) || (prescaler == 1U)) + { + prescaler++; + } + /* calucate the divisor */ + divisor = totalDiv / prescaler; + /* fine tuning the divisor until divisor * prescaler >= totalDiv */ + while ((divisor * prescaler) < totalDiv) + { + divisor++; + } + nearestFrequency = srcClock_Hz / divisor / prescaler; } - nearestFrequency = srcClock_Hz / prescaler / divisor; - SDHC_PREV_CLKFS(prescaler); + else + { + divisor = totalDiv; + prescaler = 0U; + nearestFrequency = srcClock_Hz / divisor; + } + } + /* in this condition , srcClock_Hz = busClock_Hz, */ + else + { + /* total divider = 1U */ + divisor = 0U; + prescaler = 0U; + nearestFrequency = srcClock_Hz; + } + + /* calucate the value write to register */ + if (divisor != 0U) + { SDHC_PREV_DVS(divisor); + } + /* calucate the value write to register */ + if (prescaler != 0U) + { + SDHC_PREV_CLKFS(prescaler); + } - /* 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; + /* Disable SD clock. It should be disabled before changing the SD clock frequency.*/ + base->SYSCTL &= ~SDHC_SYSCTL_SDCLKEN_MASK; - /* Wait until the SD clock is stable. */ - while (!(base->PRSSTAT & SDHC_PRSSTAT_SDSTB_MASK)) - { - } - /* Enable the SD clock. */ - base->SYSCTL |= SDHC_SYSCTL_SDCLKEN_MASK; + /* 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; } @@ -898,7 +962,7 @@ 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)) + while (base->SYSCTL & SDHC_SYSCTL_INITA_MASK) { if (!timeout) { @@ -913,6 +977,8 @@ bool SDHC_SetCardActive(SDHC_Type *base, uint32_t timeout) void SDHC_SetTransferConfig(SDHC_Type *base, const sdhc_transfer_config_t *config) { assert(config); + assert(config->dataBlockSize <= (SDHC_BLKATTR_BLKSIZE_MASK >> SDHC_BLKATTR_BLKSIZE_SHIFT)); + assert(config->dataBlockCount <= (SDHC_BLKATTR_BLKCNT_MASK >> SDHC_BLKATTR_BLKCNT_SHIFT)); base->BLKATTR = ((base->BLKATTR & ~(SDHC_BLKATTR_BLKSIZE_MASK | SDHC_BLKATTR_BLKCNT_MASK)) | (SDHC_BLKATTR_BLKSIZE(config->dataBlockSize) | SDHC_BLKATTR_BLKCNT(config->dataBlockCount))); @@ -975,12 +1041,13 @@ void SDHC_EnableSdioControl(SDHC_Type *base, uint32_t mask, bool enable) void SDHC_SetMmcBootConfig(SDHC_Type *base, const sdhc_boot_config_t *config) { assert(config); + assert(config->ackTimeoutCount <= (SDHC_MMCBOOT_DTOCVACK_MASK >> SDHC_MMCBOOT_DTOCVACK_SHIFT)); + assert(config->blockCount <= (SDHC_MMCBOOT_BOOTBLKCNT_MASK >> SDHC_MMCBOOT_BOOTBLKCNT_SHIFT)); - uint32_t mmcboot; + uint32_t mmcboot = 0U; - mmcboot = base->MMCBOOT; - mmcboot |= (SDHC_MMCBOOT_DTOCVACK(config->ackTimeoutCount) | SDHC_MMCBOOT_BOOTMODE(config->bootMode) | - SDHC_MMCBOOT_BOOTBLKCNT(config->blockCount)); + mmcboot = (SDHC_MMCBOOT_DTOCVACK(config->ackTimeoutCount) | SDHC_MMCBOOT_BOOTMODE(config->bootMode) | + SDHC_MMCBOOT_BOOTBLKCNT(config->blockCount)); if (config->enableBootAck) { mmcboot |= SDHC_MMCBOOT_BOOTACK_MASK; @@ -1004,7 +1071,7 @@ status_t SDHC_SetAdmaTableConfig(SDHC_Type *base, uint32_t dataBytes) { status_t error = kStatus_Success; - const uint32_t *startAddress; + const uint32_t *startAddress = data; uint32_t entries; uint32_t i; #if defined FSL_SDHC_ENABLE_ADMA1 @@ -1016,14 +1083,19 @@ status_t SDHC_SetAdmaTableConfig(SDHC_Type *base, (!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 */ +#endif ) { error = kStatus_InvalidArgument; } + else if (((dmaMode == kSDHC_DmaModeAdma2) && (((uint32_t)startAddress % SDHC_ADMA2_LENGTH_ALIGN) != 0U)) +#if defined FSL_SDHC_ENABLE_ADMA1 + || ((dmaMode == kSDHC_DmaModeAdma1) && (((uint32_t)startAddress % SDHC_ADMA1_LENGTH_ALIGN) != 0U)) +#endif + ) + { + error = kStatus_SDHC_DMADataBufferAddrNotAlign; + } else { switch (dmaMode) @@ -1032,7 +1104,17 @@ status_t SDHC_SetAdmaTableConfig(SDHC_Type *base, break; #if defined FSL_SDHC_ENABLE_ADMA1 case kSDHC_DmaModeAdma1: - startAddress = data; + /* + * Add non aligned access support ,user need make sure your buffer size is big + * enough to hold the data,in other words,user need make sure the buffer size + * is 4 byte aligned + */ + if (dataBytes % sizeof(uint32_t) != 0U) + { + dataBytes += + sizeof(uint32_t) - (dataBytes % sizeof(uint32_t)); /* make the data length as word-aligned */ + } + /* 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 */ @@ -1074,11 +1156,24 @@ status_t SDHC_SetAdmaTableConfig(SDHC_Type *base, /* When use ADMA, disable simple DMA */ base->DSADDR = 0U; base->ADSADDR = (uint32_t)table; + /* disable the buffer ready flag in DMA mode */ + SDHC_DisableInterruptSignal(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); + SDHC_DisableInterruptStatus(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); } break; #endif /* FSL_SDHC_ENABLE_ADMA1 */ case kSDHC_DmaModeAdma2: - startAddress = data; + /* + * Add non aligned access support ,user need make sure your buffer size is big + * enough to hold the data,in other words,user need make sure the buffer size + * is 4 byte aligned + */ + if (dataBytes % sizeof(uint32_t) != 0U) + { + dataBytes += + sizeof(uint32_t) - (dataBytes % sizeof(uint32_t)); /* make the data length as word-aligned */ + } + /* 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))) @@ -1115,6 +1210,9 @@ status_t SDHC_SetAdmaTableConfig(SDHC_Type *base, /* When use ADMA, disable simple DMA */ base->DSADDR = 0U; base->ADSADDR = (uint32_t)table; + /* disable the buffer read flag in DMA mode */ + SDHC_DisableInterruptSignal(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); + SDHC_DisableInterruptStatus(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); } break; default: @@ -1128,55 +1226,62 @@ status_t SDHC_SetAdmaTableConfig(SDHC_Type *base, 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))) + /* make sure the cmd/block count is valid */ + if ((!command) || (data && (data->blockCount > SDHC_MAX_BLOCK_COUNT))) { - error = kStatus_InvalidArgument; + return kStatus_InvalidArgument; } - else + + /* Wait until command/data bus out of busy status. */ + while (SDHC_GetPresentStatusFlags(base) & kSDHC_CommandInhibitFlag) { - /* Wait until command/data bus out of busy status. */ - while (SDHC_GetPresentStatusFlags(base) & kSDHC_CommandInhibitFlag) - { - } - while (data && (SDHC_GetPresentStatusFlags(base) & kSDHC_DataInhibitFlag)) + } + while (data && (SDHC_GetPresentStatusFlags(base) & kSDHC_DataInhibitFlag)) + { + } + + /* Update ADMA descriptor table according to different DMA mode(no DMA, ADMA1, ADMA2).*/ + if (data && (NULL != admaTable)) + { + error = + SDHC_SetAdmaTableConfig(base, dmaMode, admaTable, admaTableWords, + (data->rxData ? data->rxData : data->txData), (data->blockCount * data->blockSize)); + /* in this situation , we disable the DMA instead of polling transfer mode */ + if (error == kStatus_SDHC_DMADataBufferAddrNotAlign) { + dmaMode = kSDHC_DmaModeNo; + SDHC_EnableInterruptStatus(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); } - - /* 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)))) + else if (error != kStatus_Success) { - error = kStatus_SDHC_PrepareAdmaDescriptorFailed; + return error; } 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; + /* Send command and receive data. */ + SDHC_StartTransfer(base, command, data, dmaMode); + if (kStatus_Success != SDHC_SendCommandBlocking(base, command)) + { + return kStatus_SDHC_SendCommandFailed; + } + else if (data && (kStatus_Success != SDHC_TransferDataBlocking(dmaMode, base, data))) + { + return kStatus_SDHC_TransferDataFailed; + } + else + { + } + + return kStatus_Success; } void SDHC_TransferCreateHandle(SDHC_Type *base, @@ -1203,6 +1308,10 @@ void SDHC_TransferCreateHandle(SDHC_Type *base, /* Enable interrupt in NVIC. */ SDHC_SetTransferInterrupt(base, true); + + /* save IRQ handler */ + s_sdhcIsr = SDHC_TransferHandleIRQ; + EnableIRQ(s_sdhcIRQ[SDHC_GetInstance(base)]); } @@ -1216,42 +1325,52 @@ status_t SDHC_TransferNonBlocking( 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))) + /* make sure cmd/block count is valid */ + if ((!command) || (data && (data->blockCount > SDHC_MAX_BLOCK_COUNT))) { - error = kStatus_InvalidArgument; + return kStatus_InvalidArgument; } - else + + /* Wait until command/data bus out of busy status. */ + if ((SDHC_GetPresentStatusFlags(base) & kSDHC_CommandInhibitFlag) || + (data && (SDHC_GetPresentStatusFlags(base) & kSDHC_DataInhibitFlag))) { - /* Wait until command/data bus out of busy status. */ - if ((SDHC_GetPresentStatusFlags(base) & kSDHC_CommandInhibitFlag) || - (data && (SDHC_GetPresentStatusFlags(base) & kSDHC_DataInhibitFlag))) + return kStatus_SDHC_BusyTransferring; + } + + /* Update ADMA descriptor table according to different DMA mode(no DMA, ADMA1, ADMA2).*/ + if (data && (NULL != admaTable)) + { + error = + SDHC_SetAdmaTableConfig(base, dmaMode, admaTable, admaTableWords, + (data->rxData ? data->rxData : data->txData), (data->blockCount * data->blockSize)); + /* in this situation , we disable the DMA instead of polling transfer mode */ + if (error == kStatus_SDHC_DMADataBufferAddrNotAlign) { - error = kStatus_SDHC_BusyTransferring; + /* change to polling mode */ + dmaMode = kSDHC_DmaModeNo; + SDHC_EnableInterruptSignal(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); + SDHC_EnableInterruptStatus(base, kSDHC_BufferReadReadyFlag | kSDHC_BufferWriteReadyFlag); + } + else if (error != kStatus_Success) + { + return error; } 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; + /* 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, dmaMode); + + return kStatus_Success; } void SDHC_TransferHandleIRQ(SDHC_Type *base, sdhc_handle_t *handle) @@ -1292,6 +1411,6 @@ void SDHC_DriverIRQHandler(void) { assert(s_sdhcHandle[0]); - SDHC_TransferHandleIRQ(SDHC, s_sdhcHandle[0]); + s_sdhcIsr(SDHC, s_sdhcHandle[0]); } #endif |