http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f8f2ebbf/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.h ---------------------------------------------------------------------- diff --git a/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.h b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.h new file mode 100644 index 0000000..44506fa --- /dev/null +++ b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.h @@ -0,0 +1,232 @@ +/* + * 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 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 + * 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. + */ +#ifndef _FSL_SAI_EDMA_H_ +#define _FSL_SAI_EDMA_H_ + +#include "fsl_sai.h" +#include "fsl_edma.h" + +/*! + * @addtogroup sai_edma + * @{ + */ + +/*! @file */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +typedef struct _sai_edma_handle sai_edma_handle_t; + +/*! @brief SAI eDMA transfer callback function for finish and error */ +typedef void (*sai_edma_callback_t)(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData); + +/*! @brief SAI DMA transfer handle, users should not touch the content of the handle.*/ +struct _sai_edma_handle +{ + edma_handle_t *dmaHandle; /*!< DMA handler for SAI send */ + uint8_t bytesPerFrame; /*!< Bytes in a frame */ + uint8_t channel; /*!< Which data channel */ + uint8_t count; /*!< The transfer data count in a DMA request */ + uint32_t state; /*!< Internal state for SAI eDMA transfer */ + sai_edma_callback_t callback; /*!< Callback for users while transfer finish or error occurs */ + void *userData; /*!< User callback parameter */ + edma_tcd_t tcd[SAI_XFER_QUEUE_SIZE + 1U]; /*!< TCD pool for eDMA transfer. */ + sai_transfer_t saiQueue[SAI_XFER_QUEUE_SIZE]; /*!< Transfer queue storing queued transfer. */ + size_t transferSize[SAI_XFER_QUEUE_SIZE]; /*!< Data bytes need to transfer */ + volatile uint8_t queueUser; /*!< Index for user to queue transfer. */ + volatile uint8_t queueDriver; /*!< Index for driver to get the transfer data and size */ +}; + +/******************************************************************************* + * APIs + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name eDMA Transactional + * @{ + */ + +/*! + * @brief Initializes the SAI eDMA handle. + * + * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param base SAI peripheral base address. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void SAI_TransferTxCreateHandleEDMA( + I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle); + +/*! + * @brief Initializes the SAI Rx eDMA handle. + * + * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param base SAI peripheral base address. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void SAI_TransferRxCreateHandleEDMA( + I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle); + +/*! + * @brief Configures the SAI Tx audio format. + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the eDMA parameter according to formatting requirements. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param format Pointer to SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master + * clock, this value should equals to masterClockHz in format. + * @retval kStatus_Success Audio format set successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. +*/ +void SAI_TransferTxSetFormatEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Configures the SAI Rx audio format. + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the eDMA parameter according to formatting requirements. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param format Pointer to SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master + * clock, this value should equal to masterClockHz in format. + * @retval kStatus_Success Audio format set successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. +*/ +void SAI_TransferRxSetFormatEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Performs a non-blocking SAI transfer using DMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param xfer Pointer to the DMA transfer structure. + * @retval kStatus_Success Start a SAI eDMA send successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_TxBusy SAI is busy sending data. + */ +status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Performs a non-blocking SAI receive using eDMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + * @param xfer Pointer to DMA transfer structure. + * @retval kStatus_Success Start a SAI eDMA receive successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_RxBusy SAI is busy receiving data. + */ +status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Aborts a SAI transfer using eDMA. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! + * @brief Aborts a SAI receive using eDMA. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! + * @brief Gets byte count sent by SAI. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param count Bytes count sent by SAI. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count); + +/*! + * @brief Gets byte count received by SAI. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + * @param count Bytes count received by SAI. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count); + +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ +#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f8f2ebbf/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sdhc.c ---------------------------------------------------------------------- diff --git a/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sdhc.c b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sdhc.c new file mode 100644 index 0000000..0c5dd2b --- /dev/null +++ b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sdhc.c @@ -0,0 +1,1294 @@ +/* + * 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