/******************************************************************************* * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved. * * This software component is licensed by HDSC under BSD 3-Clause license * (the "License"); You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause */ /******************************************************************************/ /** \file sd_card.c ** ** A detailed description is available at ** @link SdiocGroup SDIOC description @endlink ** ** - 2018-11-15 CDT First version for Device Driver Library of SDIOC. ** ******************************************************************************/ /******************************************************************************* * Include files ******************************************************************************/ #include "sdmmc_cmd.h" #include "sd_card.h" /** ******************************************************************************* ** \addtogroup SdiocGroup ******************************************************************************/ //@{ /******************************************************************************* * Local type definitions ('typedef') ******************************************************************************/ typedef enum en_sd_card_type { SdCardSdsc = 0u, SdCardSdhcSdxc = 1u, SdCardSecured = 3u, } en_sd_card_type_t; /******************************************************************************* * Local pre-processor symbols/macros ('#define') ******************************************************************************/ /*!< Card Command Class supported. */ #define SDMMC_CARD_CCCC_ERASE (0x00000020u) /*!< Card locked */ #define SDMMC_CARD_LOCKED (0x02000000u) /*!< Voltage trial times */ #define SD_CARD_MAX_VOLT_TRIAL (0x0000FFFFu) /*!< High speed */ #define SD_CHECK_FUNCTION_HIGH_SPEED (0x00FFFF01u) #define SD_SET_FUNCTION_HIGH_SPEED (0x80FFFF01u) /*!< Block size is 512 bytes */ #define SD_CARD_BLOCK_SIZE (512u) /*!< SDIOC buffer align size */ #define SDIOC_BUF_ALIGN_SIZE (4ul) /*!< Log Block Number for 2G bytes Cards */ #define SD_CARD_CAPACITY (0x400000u) /*!< Card power up status bit (busy) */ #define SD_CARD_OCR_BUSY (0x80000000ul) #define SD_CARD_OP_NONE (0x00000000u) /*!< None */ #define SD_CARD_OP_READ_SINGLE_BLOCK (0x00000001u) /*!< Read single block operation */ #define SD_CARD_OP_READ_MULTIPLE_BLOCK (0x00000002u) /*!< Read multiple blocks operation */ #define SD_CARD_OP_WRITE_SINGLE_BLOCK (0x00000010u) /*!< Write single block operation */ #define SD_CARD_OP_WRITE_MULTIPLE_BLOCK (0x00000020u) /*!< Write multiple blocks operation */ #define SD_CARD_OP_IT (0x00000008u) /*!< Process in Interrupt mode */ #define SD_CARD_OP_DMA (0x00000080u) /*!< Process in DMA mode */ /*!< the SD card relative card address. */ #define RelCardAddress(handle) ((handle)->stcSdCardInfo.u32RelCardAddr) #define IsCardProgramming(handle) ((SdmmcCardStatePgm == (handle)->stcCardStatus.CURRENT_STATE) ? true : false) #define IsCardReadyForData(handle) ((1u == (handle)->stcCardStatus.READY_FOR_DATA) ? true : false) #define IS_DMA_CFG_VALID(handle) \ ( (NULL != (handle)->pstcDmaInitCfg) && \ (NULL != (handle)->pstcDmaInitCfg->DMAx)) /*!< the SD card use DMA unit && channel. */ #define DMA_Unit(handle) ((handle)->pstcDmaInitCfg->DMAx) #define DMA_CH(handle) ((handle)->pstcDmaInitCfg->enDmaCh) /*!< Parameter valid check for buffer address. */ #define IS_VALID_TRANSFER_BUF_ALIGN(x) (!((SDIOC_BUF_ALIGN_SIZE-1ul) & ((uint32_t)(x)))) /*!< Parameter valid check for SDIOC command value. */ #define IS_VALID_TRANSFER_BUF_LEN(x) (!((SD_CARD_BLOCK_SIZE - 1u) & (x))) /******************************************************************************* * Global variable definitions (declared in header file with 'extern') ******************************************************************************/ /******************************************************************************* * Local function prototypes ('static') ******************************************************************************/ static en_result_t SdCardInitSd(stc_sd_handle_t *handle); static en_result_t SdCardPowerON(stc_sd_handle_t *handle); static en_result_t SdCardInitHost(const stc_sd_handle_t *handle); static en_result_t SdCardSetSpeed(stc_sd_handle_t *handle); static en_result_t SdCardSetBusWidth(stc_sd_handle_t *handle); static en_result_t SdCardCheckReayForData(stc_sd_handle_t *handle, uint32_t u32Timeout); static en_result_t DmaSdiocTxConfig(M4_DMA_TypeDef* DMAx, en_dma_channel_t enCh, M4_SDIOC_TypeDef *SDIOCx, uint8_t *pu8TxBuf, uint16_t u16len); static en_result_t DmaSdiocRxConfig(M4_DMA_TypeDef* DMAx, en_dma_channel_t enCh, M4_SDIOC_TypeDef *SDIOCx, uint8_t *pu8RxBuf, uint16_t u16len); /******************************************************************************* * Local variable definitions ('static') ******************************************************************************/ /******************************************************************************* * Function implementation - global ('extern') and local ('static') ******************************************************************************/ /** ******************************************************************************* ** \brief Initialize SD. ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** \param [in] pstcInitCfg The pointer of SD configure structure ** \arg This parameter detail refer @ref stc_sdcard_init_t ** ** \retval Ok Initialize SD successfully. ** \retval Error Initialize SD unsuccessfully. ** \retval ErrorInvalidParameter If one of following cases matches: ** - handle == NULL ** - pstcInitCfg == NULL ** ******************************************************************************/ en_result_t SDCARD_Init(stc_sd_handle_t *handle, const stc_sdcard_init_t *pstcInitCfg) { en_result_t enRet = ErrorInvalidParameter; if ((NULL != handle) && (NULL != pstcInitCfg)) { handle->pstcCardInitCfg = pstcInitCfg; enRet = SdCardInitHost(handle); if (enRet != Ok) { return enRet; } enRet = SdCardPowerON(handle); if (enRet != Ok) { return enRet; } enRet = SdCardInitSd(handle); if (enRet != Ok) { return enRet; } enRet = SdCardSetBusWidth(handle); if (enRet != Ok) { return enRet; } enRet = SdCardSetSpeed(handle); } return enRet; } /** ******************************************************************************* ** \brief Set SD Card device Read/Write mode. ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** \param [in] enDevMode Read/Write mode ** \arg SdCardDmaMode DMA mode transfer ** \arg SdCardPollingMode Polling mode transfer ** ** \retval Ok Set successfully. ** \retval ErrorInvalidParameter handle pointer is NULL. ** ******************************************************************************/ en_result_t SDCARD_SetDeviceMode(stc_sd_handle_t *handle, en_sd_card_device_mode_t enDevMode) { en_result_t enRet = ErrorInvalidParameter; if (NULL != handle) { handle->enDevMode = enDevMode; enRet = Ok; } return enRet; } /** ******************************************************************************* ** \brief Get SD Card device Read/Write mode. ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval SdCardDmaMode DMA mode transfer ** \retval SdCardPollingMode Polling mode transfer ** ******************************************************************************/ en_sd_card_device_mode_t SDCARD_GetDeviceMode(const stc_sd_handle_t *handle) { DDL_ASSERT(NULL != handle); return handle->enDevMode; } /** ******************************************************************************* ** \brief Get Card specified Data information. ** ** \param [in] handle Pointer to SD Card handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval Ok Get successfully. ** \retval ErrorInvalidParameter If one of following cases matches: ** - handle == NULL ** ******************************************************************************/ en_result_t SDCARD_GetCardCSD(stc_sd_handle_t *handle) { uint32_t u32Csize = 0ul; uint32_t u32NumSector = 0ul; uint32_t u32CsizeMulti = 0ul; stc_sdcard_csd_v1_t *pstcCsdSd = NULL; stc_sdcard_csd_v2_t *pstcCsdSdHc = NULL; en_result_t enRet = ErrorInvalidParameter; if (NULL != handle) { /* High Capacity CSD Version 2.0*/ if ((handle->CSD[3] & 0x00FF0000ul) == 0x00400000ul) { pstcCsdSdHc = (stc_sdcard_csd_v2_t *)&handle->CSD[0]; u32Csize = ((unsigned int)pstcCsdSdHc->C_SIZE3 << 16ul) + ((unsigned int)pstcCsdSdHc->C_SIZE2 << 8ul) + pstcCsdSdHc->C_SIZE1; u32NumSector = (u32Csize + 1ul) << 10ul; handle->stcSdCardInfo.u32Class = (((uint32_t)pstcCsdSdHc->CCC2) << 4u) | ((uint32_t)pstcCsdSdHc->CCC1); handle->stcSdCardInfo.u32BlockSize = 1ul << (pstcCsdSdHc->READ_BL_LEN); } else /* Standard Capacity CSD Version 1.xx */ { pstcCsdSd = (stc_sdcard_csd_v1_t *)&handle->CSD[0]; u32Csize = ((unsigned int)pstcCsdSd->C_SIZE3 << 10) + ((unsigned int)pstcCsdSd->C_SIZE2 << 2) + pstcCsdSd->C_SIZE1; u32CsizeMulti = ((uint32_t)(pstcCsdSd->C_SIZE_MULTI2) << 1) + (uint32_t)pstcCsdSd->C_SIZE_MULTI1; u32NumSector = (u32Csize + 1ul) << (u32CsizeMulti + 2ul); if (pstcCsdSd->READ_BL_LEN == 0x0Au) { u32NumSector *= 2ul; } else if (pstcCsdSd->READ_BL_LEN == 0x0Bu) { u32NumSector *= 4ul; } else { /* Do nothing: only avoid MISRA warning */ } handle->stcSdCardInfo.u32Class = ((uint32_t)pstcCsdSd->CCC2 << 4) | (uint32_t)pstcCsdSd->CCC1; handle->stcSdCardInfo.u32BlockSize = 1ul << (pstcCsdSd->READ_BL_LEN); } handle->stcSdCardInfo.u32BlockNbr = u32NumSector; handle->stcSdCardInfo.u32LogBlockNbr = (handle->stcSdCardInfo.u32BlockNbr) * ((handle->stcSdCardInfo.u32BlockSize) / SD_CARD_BLOCK_SIZE); handle->stcSdCardInfo.u32LogBlockSize = SD_CARD_BLOCK_SIZE; enRet = Ok; } return enRet; } /** ******************************************************************************* ** \brief Erase SD card blocks. ** ** \param [in] handle Pointer to SD Card handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** \param [in] u32BlkStartAddr Block start address ** \param [in] u32BlkEndAddr Block end address ** \param [in] u32Timeout Erase timeout value ** ** \retval Ok Erase successfully. ** \retval Error Erase unsuccessfully. ** \retval ErrorInvalidParameter If one of following cases matches: ** - handle == NULL ** - u32BlkStartAddr out of range ** - u32BlkEndAddr out of range ** ******************************************************************************/ en_result_t SDCARD_Erase(stc_sd_handle_t *handle, uint32_t u32BlkStartAddr, uint32_t u32BlkEndAddr, uint32_t u32Timeout) { en_result_t enCmdRet = Error; __IO uint32_t u32TimeCount = u32Timeout * (SystemCoreClock / 8u / 1000u); if ((NULL == handle) || (u32BlkStartAddr > u32BlkEndAddr) || (u32BlkEndAddr > handle->stcSdCardInfo.u32BlockNbr)) { return ErrorInvalidParameter; } /* Check if the card command class supports erase command */ if (!(handle->stcSdCardInfo.u32Class & SDMMC_CARD_CCCC_ERASE)) { return ErrorAccessRights; } if ((SDIOC_GetResponse(handle->SDIOCx, SdiocRegResp01) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) { return ErrorAccessRights; } /* Check the Card capacity in term of Logical number of blocks */ if (handle->stcSdCardInfo.u32CardType != SdCardSdhcSdxc) { u32BlkStartAddr *= SD_CARD_BLOCK_SIZE; u32BlkEndAddr *= SD_CARD_BLOCK_SIZE; } /* Send CMD35 ERASE_GRP_START with argument as address */ enCmdRet = SDMMC_Cmd32_EraseWrBlkStart(handle->SDIOCx, u32BlkStartAddr, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } /* Send CMD36 ERASE_GRP_END with argument as address */ enCmdRet = SDMMC_Cmd33_EraseWrBlkEnd(handle->SDIOCx, u32BlkEndAddr, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } enCmdRet = SDMMC_Cmd38_Erase(handle->SDIOCx, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } enCmdRet = SdCardCheckReayForData(handle, u32TimeCount); return enCmdRet; } /** ******************************************************************************* ** \brief Read block data from SD card ** ** \param [in] handle Pointer to SD Card handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** \param [in] u32BlockAddr Block address ** \param [in] u16BlockCnt Block Count ** \param [in] pu8Data Pointer to buffer which will store SD Card data. ** \param [in] u32Timeout Transfer timeout ** ** \retval Ok Read successfully. ** \retval Error Read unsuccessfully. ** \retval ErrorInvalidParameter If one of following cases matches: ** - handle == NULL ** - pu8Data == NULL ** - u16BlockCnt == 0 ** - (u32BlockAddr + u16BlockCnt) out of range ** ******************************************************************************/ en_result_t SDCARD_ReadBlocks(stc_sd_handle_t *handle, uint32_t u32BlockAddr, uint16_t u16BlockCnt, uint8_t *pu8Data, uint32_t u32Timeout) { en_result_t enCmdRet; stc_sdioc_data_cfg_t stcDataCfg; uint8_t *pu8TempBuf = (uint8_t *)pu8Data; uint32_t u32TimeCount = u32Timeout * (SystemCoreClock / 8u / 1000u); __IO uint32_t u32Count = u32TimeCount; en_sd_card_device_mode_t enDeviceMode = SDCARD_GetDeviceMode(handle); en_flag_status_t enStatus = Reset; en_flag_status_t enIrqFlag = Reset; if (NULL == handle) { return ErrorInvalidParameter; } if ((NULL == pu8Data) || (0u == u16BlockCnt)) { handle->u32ErrorCode |= SD_CARD_ERROR_PARAM; return ErrorInvalidParameter; } handle->u32ErrorCode = SD_CARD_ERROR_NONE; if ((u32BlockAddr + u16BlockCnt) > (handle->stcSdCardInfo.u32LogBlockNbr)) { handle->u32ErrorCode |= SD_CARD_ERROR_ADDR_OUT_OF_RANGE; return ErrorInvalidParameter; } if (handle->stcSdCardInfo.u32CardType != SdCardSdhcSdxc) { u32BlockAddr *= SD_CARD_BLOCK_SIZE; } /* Set Block Size for Card */ enCmdRet = SDMMC_Cmd16_SetBlockLength(handle->SDIOCx, SD_CARD_BLOCK_SIZE, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } stcDataCfg.u16BlkCnt = u16BlockCnt; stcDataCfg.u16BlkSize = SD_CARD_BLOCK_SIZE; stcDataCfg.enDataTimeOut = SdiocDtoSdclk_2_27; stcDataCfg.enTransferDir = SdiocTransferToHost; stcDataCfg.enAutoCmd12Enable = (u16BlockCnt > 1u) ? Enable:Disable; stcDataCfg.enTransferMode = (u16BlockCnt > 1u) ? SdiocTransferMultiple:SdiocTransferSingle; enCmdRet = SDIOC_ConfigData(handle->SDIOCx, &stcDataCfg); if (enCmdRet != Ok) { return enCmdRet; } if (enDeviceMode == SdCardDmaMode) { if (IS_DMA_CFG_VALID(handle)) { DmaSdiocRxConfig(DMA_Unit(handle), DMA_CH(handle), handle->SDIOCx, pu8TempBuf, u16BlockCnt * SD_CARD_BLOCK_SIZE); } } /* Read block(s) in polling mode */ if (1u == u16BlockCnt) { handle->Context = SD_CARD_OP_READ_SINGLE_BLOCK; /* Read Single Block command */ enCmdRet = SDMMC_Cmd17_ReadSingleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus)); } else { handle->Context = SD_CARD_OP_READ_MULTIPLE_BLOCK; /* Read Multi Block command */ enCmdRet = SDMMC_Cmd18_ReadMultipleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus)); } if (enCmdRet != Ok) { return enCmdRet; } if (SdCardPollingMode == enDeviceMode) { /* Poll on SDIO flags */ while (u16BlockCnt) { enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocBufferReadEnble); enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocBufferReadReady); if ((Set == enStatus) && (Set == enIrqFlag)) { SDIOC_ReadBuffer(handle->SDIOCx, pu8TempBuf, SD_CARD_BLOCK_SIZE); u16BlockCnt--; u32Count = u32TimeCount; pu8TempBuf += SD_CARD_BLOCK_SIZE; } if (0ul == u32Count--) { return ErrorTimeout; } } } for (u32Count = u32TimeCount; u32Count > 0ul; u32Count--) { if (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete)) { break; } } if (0ul == u32Count) { return ErrorTimeout; } else { SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete); } /* check whether Data transfer stops */ if (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocErrorInt)) { enCmdRet = Error; } return enCmdRet; } /** ******************************************************************************* ** \brief Write block data to SD card ** ** \param [in] handle Pointer to SD Card handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** \param [in] u32BlockAddr Block address ** \param [in] u16BlockCnt Block Count ** \param [in] pu8Data Pointer to buffer which contains data to be send to SD Card. ** \param [in] u32Timeout Transfer timeout ** ** \retval Ok Write successfully. ** \retval Error Write unsuccessfully. ** \retval ErrorInvalidParameter If one of following cases matches: ** - handle == NULL ** - pu8Data == NULL ** - u16BlockCnt == 0 ** - (u32BlockAddr + u16BlockCnt) out of range ** ******************************************************************************/ en_result_t SDCARD_WriteBlocks(stc_sd_handle_t *handle, uint32_t u32BlockAddr, uint16_t u16BlockCnt, uint8_t *pu8Data, uint32_t u32Timeout) { en_result_t enCmdRet; stc_sdioc_data_cfg_t stcDataCfg; uint8_t *pu8TempBuf = (uint8_t *)pu8Data; uint32_t u32TimeCount = u32Timeout * (SystemCoreClock / 50ul / 1000ul); __IO uint32_t u32Count = u32TimeCount; en_sd_card_device_mode_t enDeviceMode = SDCARD_GetDeviceMode(handle); en_flag_status_t enStatus = Reset; en_flag_status_t enIrqFlag = Reset; if (NULL == handle) { return ErrorInvalidParameter; } if ((NULL == pu8Data) || (0u == u16BlockCnt)) { handle->u32ErrorCode |= SD_CARD_ERROR_PARAM; return ErrorInvalidParameter; } handle->u32ErrorCode = SD_CARD_ERROR_NONE; if ((u32BlockAddr + u16BlockCnt) > (handle->stcSdCardInfo.u32LogBlockNbr)) { handle->u32ErrorCode |= SD_CARD_ERROR_ADDR_OUT_OF_RANGE; return ErrorInvalidParameter; } /* Set Block Size for Card */ enCmdRet = SDMMC_Cmd16_SetBlockLength(handle->SDIOCx, SD_CARD_BLOCK_SIZE, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } stcDataCfg.u16BlkCnt = u16BlockCnt; stcDataCfg.u16BlkSize = SD_CARD_BLOCK_SIZE; stcDataCfg.enDataTimeOut = SdiocDtoSdclk_2_27; stcDataCfg.enTransferDir = SdiocTransferToCard; stcDataCfg.enAutoCmd12Enable = (u16BlockCnt > 1u) ? Enable:Disable; stcDataCfg.enTransferMode = (u16BlockCnt > 1u) ? SdiocTransferMultiple:SdiocTransferSingle; enCmdRet = SDIOC_ConfigData(handle->SDIOCx, &stcDataCfg); if (enCmdRet != Ok) { return enCmdRet; } if (handle->stcSdCardInfo.u32CardType != SdCardSdhcSdxc) { u32BlockAddr *= SD_CARD_BLOCK_SIZE; } if (enDeviceMode == SdCardDmaMode) { if (IS_DMA_CFG_VALID(handle)) { DmaSdiocTxConfig(DMA_Unit(handle), DMA_CH(handle), handle->SDIOCx, pu8TempBuf, u16BlockCnt * SD_CARD_BLOCK_SIZE); } } /* Write block(s) in polling mode */ if (1u == u16BlockCnt) { /* Read Single Block command */ enCmdRet = SDMMC_Cmd24_WriteSingleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus)); } else { /* Read Multi Block command */ enCmdRet = SDMMC_Cmd25_WriteMultipleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus)); } if (enCmdRet != Ok) { return enCmdRet; } if (SdCardPollingMode == enDeviceMode) { /* Poll on SDIO flags */ while (u16BlockCnt) { enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocBufferWriteEnble); enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocBufferWriteReady); if ((Set == enStatus) && (Set == enIrqFlag)) { /* Write data to SDIO Tx Buffer */ SDIOC_WriteBuffer(handle->SDIOCx, pu8TempBuf, SD_CARD_BLOCK_SIZE); u16BlockCnt--; u32Count = u32TimeCount; pu8TempBuf += SD_CARD_BLOCK_SIZE; } if (u32Count-- == 0ul) { return ErrorTimeout; } } } for (u32Count = u32TimeCount; u32Count > 0ul; u32Count--) { enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocData0PinLvl); enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete); if ((Set == enStatus) && (Set == enIrqFlag)) { break; } } if (0u == u32Count) { return ErrorTimeout; } else { SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete); } enCmdRet = SdCardCheckReayForData(handle, u32TimeCount); if (enCmdRet != Ok) { return enCmdRet; } /* check whether Data transfer stops */ if(Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocErrorInt)) { return Error; } SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocBufferWriteReady); return Ok; } /** ****************************************************************************** ** \brief Set SD bus width. ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval Ok Set SD bus width successfully. ** \retval Error Set SD bus width unsuccessfully. ** \retval ErrorInvalidParameter If one of following conditions matches: ** - NULL == pstcCfg ** - enBusWidth is invalid. ** - Other invalid configuration ** ******************************************************************************/ static en_result_t SdCardSetBusWidth(stc_sd_handle_t *handle) { uint32_t u32CmdArg = 0ul; en_result_t enCmdRet = Ok; if ((NULL == handle) || (NULL == handle->pstcCardInitCfg)) { return ErrorInvalidParameter; } switch (handle->pstcCardInitCfg->enBusWidth) { case SdiocBusWidth1Bit: u32CmdArg = 0u; break; case SdiocBusWidth4Bit: u32CmdArg = 2u; break; default: enCmdRet = ErrorInvalidParameter; break; } if (Ok != enCmdRet) { return enCmdRet; } enCmdRet = SDMMC_Cmd55_AppCmd(handle->SDIOCx, (RelCardAddress(handle) << 16u), (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } enCmdRet = SDMMC_Acmd6_SetBusWidth(handle->SDIOCx, u32CmdArg, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { return enCmdRet; } enCmdRet = SDIOC_SetBusWidth(handle->SDIOCx, handle->pstcCardInitCfg->enBusWidth); if (enCmdRet != Ok) { return enCmdRet; } return Ok; } /** ****************************************************************************** ** \brief Set SD high speed mode. ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval Ok Set SD bus width successfully. ** \retval Ok Set SD bus width unsuccessfully. ** \retval ErrorInvalidParameter If one of following conditions matches: ** - NULL == pstcCfg ** - u32Freq is invalid. ** ******************************************************************************/ static en_result_t SdCardSetSpeed(stc_sd_handle_t *handle) { uint8_t au8TempBuf[64] = {0}; uint32_t u32TimeCount = 2000 * (SystemCoreClock / 8u / 1000u); __IO uint32_t u32Count = u32TimeCount; en_flag_status_t enStatus = Reset; en_flag_status_t enIrqFlag = Reset; en_result_t enCmdRet = ErrorInvalidParameter; stc_sdioc_data_cfg_t stcDataCfg; uint32_t u32Arg = SD_SET_FUNCTION_HIGH_SPEED; if ((NULL != handle) && (NULL != handle->pstcCardInitCfg)) { enCmdRet = Ok; if (SdiocHighSpeedMode == handle->pstcCardInitCfg->enSpeedMode) { /* Set Block Size for Card */ enCmdRet = SDMMC_Cmd16_SetBlockLength(handle->SDIOCx, 64u, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet == Ok) { stcDataCfg.u16BlkCnt = 1u; stcDataCfg.u16BlkSize = 64u; stcDataCfg.enDataTimeOut = SdiocDtoSdclk_2_27; stcDataCfg.enTransferDir = SdiocTransferToHost; stcDataCfg.enAutoCmd12Enable = Disable; stcDataCfg.enTransferMode = SdiocTransferSingle; enCmdRet = SDIOC_ConfigData(handle->SDIOCx, &stcDataCfg); if (enCmdRet == Ok) { enCmdRet = SDMMC_Cmd6_SwitchFunc(handle->SDIOCx, u32Arg, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet == Ok) { while (u32Count--) { enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocBufferReadEnble); enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocBufferReadReady); if ((Set == enStatus) && (Set == enIrqFlag)) { SDIOC_ReadBuffer(handle->SDIOCx, au8TempBuf, sizeof(au8TempBuf)); break; } } if (0ul == u32Count) { return ErrorTimeout; } u32Count = u32TimeCount; while (u32Count--) { if (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete)) { SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete); break; } } if (0ul == u32Count) { return ErrorTimeout; } /* Check whether switch function with error or transfer error occur */ if ((!(au8TempBuf[16] & 0x01)) || (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocErrorInt))) { enCmdRet = Error; } } } } } if (enCmdRet == Ok) { SDIOC_SetSpeedMode(handle->SDIOCx, handle->pstcCardInitCfg->enSpeedMode); enCmdRet = SDIOC_SetClk(handle->SDIOCx, handle->pstcCardInitCfg->enClkFreq); } } return enCmdRet; } /** ******************************************************************************* ** \brief Initialize SDIO Host ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval Ok SDIO host initialized successfully ** \retval ErrorInvalidParameter If one of following conditions matches: ** - NULL == pstcInitCfg ** - Other invalid configuration ** ******************************************************************************/ static en_result_t SdCardInitHost(const stc_sd_handle_t *handle) { en_result_t enRet = Ok; if ((NULL == handle) || (NULL == handle->pstcCardInitCfg)) { return ErrorInvalidParameter; } /* Enable SD clock supply */ if (M4_SDIOC1 == handle->SDIOCx) { PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_SDIOC1, Enable); } else if (M4_SDIOC2 == handle->SDIOCx) { PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_SDIOC2, Enable); } else { return ErrorInvalidParameter; } /* Set mode: SD */ SDIOC_SetMode(handle->SDIOCx, SdiocModeSD); enRet = SDIOC_Init(handle->SDIOCx, handle->pstcCardInitCfg->pstcInitCfg); return enRet; } /** ******************************************************************************* ** \brief Initialize SD bus ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval Ok SDIO bus cmd operate successfully. ** \retval Error SDIO bus cmd operate unsuccessfully. ** \retval ErrorInvalidParameter If one of following conditions matches: ** - NULL == pstcCfg ** - Other invalid configuration ** ******************************************************************************/ static en_result_t SdCardInitSd(stc_sd_handle_t *handle) { en_result_t enCmdRet = Ok; if(NULL == handle) { return ErrorInvalidParameter; } if (handle->stcSdCardInfo.u32CardType != SdCardSecured) { /* Send CMD2 ALL_SEND_CID */ enCmdRet = SDMMC_Cmd2_AllSendCID(handle->SDIOCx, handle->CID); if (enCmdRet != Ok) { return enCmdRet; } /* Send CMD3 SET_REL_ADDR with argument 0 */ /* SD Card publishes its RCA. */ enCmdRet = SDMMC_Cmd3_SendRelativeAddr(handle->SDIOCx, &(handle->RCA)); if (enCmdRet != Ok) { return enCmdRet; } /* Get the SD card RCA */ RelCardAddress(handle) = handle->RCA; /* Send CMD9 SEND_CSD with argument as card's RCA */ enCmdRet = SDMMC_Cmd9_SendCSD(handle->SDIOCx, RelCardAddress(handle), handle->CSD); if (enCmdRet != Ok) { return enCmdRet; } } /* Get CSD parameters */ enCmdRet = SDCARD_GetCardCSD(handle); if (enCmdRet == Ok) { /* Select the Card */ enCmdRet = SDMMC_Cmd7_SelectDeselectCard(handle->SDIOCx, RelCardAddress(handle), (uint32_t *)(&handle->stcCardStatus)); } return enCmdRet; } /** ******************************************************************************* ** \brief Activate SD in power-on stage ** ** \param [in] handle Pointer to SD handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** ** \retval Ok Activate successfully. ** \retval Error Activate unsuccessfully. ** \retval ErrorInvalidParameter If one of following conditions matches: ** - NULL == handle ** - Other invalid configuration ** ******************************************************************************/ static en_result_t SdCardPowerON(stc_sd_handle_t *handle) { uint32_t u32IfCond = 0u; __IO uint32_t u32Count = 0ul; en_result_t enCmdRet = Error; if (NULL == handle) { return ErrorInvalidParameter; } handle->u32ErrorCode = SD_CARD_ERROR_NONE; /* CMD0: GO_IDLE_STATE */ enCmdRet = SDMMC_Cmd0_GoIdleState(handle->SDIOCx); if (enCmdRet != Ok) { handle->u32ErrorCode |= SD_CARD_ERROR_GENERAL_UNKNOWN_ERR; return enCmdRet; } /* Reset OCR valude */ handle->OCR = 0ul; /* CMD8: SEND_IF_COND: Command available only on V2.0 cards */ enCmdRet = SDMMC_Cmd8_SendIfCond(handle->SDIOCx, &u32IfCond); if (enCmdRet != Ok) { handle->stcSdCardInfo.u32CardVersion = SdCardVer1x; /* Send ACMD41 SD_APP_OP_COND with Argument 0x00100000 */ while (SD_CARD_OCR_BUSY != (handle->OCR & SD_CARD_OCR_BUSY)) { if (SD_CARD_MAX_VOLT_TRIAL == u32Count++) { handle->u32ErrorCode |= SD_CARD_ERROR_INVALID_VOLTRANGE; return enCmdRet; } /* SEND CMD55 APP_CMD with RCA as 0 */ enCmdRet = SDMMC_Cmd55_AppCmd(handle->SDIOCx, 0u, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { continue; } /* Send CMD41 */ enCmdRet = SDMMC_Acmd41_SdSendOpCond(handle->SDIOCx, SdmmcStanderdCapacity, &handle->OCR); if (enCmdRet == Error) { handle->u32ErrorCode |= SD_CARD_ERROR_UNSUPPORTED_FEATURE; return enCmdRet; } } /* Card type is SDSC */ handle->stcSdCardInfo.u32CardType = SdCardSdsc; } else { handle->stcSdCardInfo.u32CardVersion = SdCardVer2x; /* Send ACMD41 SD_APP_OP_COND with Argument 0x40100000 */ while (SD_CARD_OCR_BUSY != (handle->OCR & SD_CARD_OCR_BUSY)) { if (SD_CARD_MAX_VOLT_TRIAL == u32Count++) { handle->u32ErrorCode |= SD_CARD_ERROR_INVALID_VOLTRANGE; return Error; } /* SEND CMD55 APP_CMD with RCA as 0 */ enCmdRet = SDMMC_Cmd55_AppCmd(handle->SDIOCx, 0u, (uint32_t *)(&handle->stcCardStatus)); if (enCmdRet != Ok) { continue; } /* Send CMD41 */ enCmdRet = SDMMC_Acmd41_SdSendOpCond(handle->SDIOCx, SdmmcHighCapacity, &handle->OCR); if (enCmdRet == Error) { handle->u32ErrorCode |= SD_CARD_ERROR_UNSUPPORTED_FEATURE; return enCmdRet; } } if ((handle->OCR & SdmmcHighCapacity) == SdmmcHighCapacity) { handle->stcSdCardInfo.u32CardType = SdCardSdhcSdxc; } else { handle->stcSdCardInfo.u32CardType = SdCardSdsc; } } return Ok; } /** ******************************************************************************* ** \brief Checks if the SD card is ready for data. ** ** \param [in] handle Pointer to SD Card handle ** \arg This parameter detail refer @ref stc_sd_handle_t ** \param [in] u32Timeout Retry to get card status time ** ** \retval Ok Card is ready for data ** \retval Error Card is not ready for data ** \retval ErrorInvalidParameter NULL == handle ** ******************************************************************************/ static en_result_t SdCardCheckReayForData(stc_sd_handle_t *handle, uint32_t u32Timeout) { en_result_t enRet = ErrorInvalidParameter; if (NULL != handle) { do { enRet = SDMMC_Cmd13_SendStatus(handle->SDIOCx, RelCardAddress(handle), (uint32_t *)(&handle->stcCardStatus)); if (Ok != enRet) { break; } } while ((--u32Timeout) && (false == IsCardReadyForData(handle))); if (0u == u32Timeout) { enRet = ErrorTimeout; } } return enRet; } /** ******************************************************************************* ** \brief Configures the DMA Channel for SDIO Tx request. ** ** \param [in] DMAx The pointer to DMAC register base ** \arg M4_DMA1 DMAC unit 1 instance register base ** \arg M4_DMA2 DMAC unit 2 instance register base ** \param [in] enCh The specified DMAC channel. ** \arg DmaCh0 DMAC channel 0 ** \arg DmaCh1 DMAC channel 1 ** \arg DmaCh2 DMAC channel 2 ** \arg DmaCh3 DMAC channel 3 ** \param [in] SDIOCx Pointer to SDIOC instance register base ** \arg M4_SDIOC1 SDIOC unit 1 instance register base ** \arg M4_SDIOC2 SDIOC unit 2 instance register base ** \param [in] pu8TxBuf Pointer to the Tx buffer ** \param [in] u16len Tx buffer size ** ** \retval Ok Set successfully. ** \retval ErrorInvalidParameter Set unsuccessfully. ** ******************************************************************************/ static en_result_t DmaSdiocTxConfig(M4_DMA_TypeDef* DMAx, en_dma_channel_t enCh, M4_SDIOC_TypeDef *SDIOCx, uint8_t *pu8TxBuf, uint16_t u16len) { stc_dma_config_t stcDmaInit; uint32_t u32Fcg0Periph = (M4_DMA1 == DMAx) ? PWC_FCG0_PERIPH_DMA1 : PWC_FCG0_PERIPH_DMA2; en_event_src_t enEvtSrc = (M4_SDIOC1 == SDIOCx) ? EVT_SDIOC1_DMAW : EVT_SDIOC2_DMAW; en_result_t enRet = ErrorInvalidParameter; if ((NULL != pu8TxBuf) && \ (IS_VALID_TRANSFER_BUF_LEN(u16len)) && \ (IS_VALID_TRANSFER_BUF_ALIGN(pu8TxBuf))) { /* Enable peripheral clock */ PWC_Fcg0PeriphClockCmd(u32Fcg0Periph, Enable); /* Enable DMA. */ DMA_Cmd(DMAx, Enable); /* Initialize DMA. */ MEM_ZERO_STRUCT(stcDmaInit); stcDmaInit.u16BlockSize = SD_CARD_BLOCK_SIZE/4u; /* Set data block size. */ stcDmaInit.u16TransferCnt = u16len/SD_CARD_BLOCK_SIZE; /* Set transfer count. */ stcDmaInit.u32SrcAddr = (uint32_t)(&pu8TxBuf[0]); /* Set source address. */ stcDmaInit.u32DesAddr = (uint32_t)(&SDIOCx->BUF0); /* Set destination address. */ stcDmaInit.stcDmaChCfg.enSrcInc = AddressIncrease; /* Set source address mode. */ stcDmaInit.stcDmaChCfg.enDesInc = AddressFix; /* Set destination address mode. */ stcDmaInit.stcDmaChCfg.enTrnWidth = Dma32Bit; /* Set data width 8bit. */ DMA_InitChannel(DMAx, enCh, &stcDmaInit); /* Enable the specified DMA channel. */ DMA_ChannelCmd(DMAx, enCh, Enable); /* Clear DMA flag. */ DMA_ClearIrqFlag(DMAx, enCh, TrnCpltIrq); DMA_ClearIrqFlag(DMAx, enCh, BlkTrnCpltIrq); /* Enable peripheral circuit trigger function. */ PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable); /* Set DMA trigger source. */ DMA_SetTriggerSrc(DMAx, enCh, enEvtSrc); enRet = Ok; } return enRet; } /** ******************************************************************************* ** \brief Configures the DMA Channel for SDIO Rx request. ** ** \param [in] DMAx The pointer to DMAC register base ** \arg M4_DMA1 DMAC unit 1 instance register base ** \arg M4_DMA2 DMAC unit 2 instance register base ** \param [in] enCh The specified DMAC channel. ** \arg DmaCh0 DMAC channel 0 ** \arg DmaCh1 DMAC channel 1 ** \arg DmaCh2 DMAC channel 2 ** \arg DmaCh3 DMAC channel 3 ** \param [in] SDIOCx Pointer to SDIOC instance register base ** \arg M4_SDIOC1 SDIOC unit 1 instance register base ** \arg M4_SDIOC2 SDIOC unit 2 instance register base ** \param [in] pu8RxBuf Pointer to the Rx buffer ** \param [in] u16len Rx buffer size ** ** \retval Ok Set successfully. ** \retval ErrorInvalidParameter Set unsuccessfully. ** ******************************************************************************/ static en_result_t DmaSdiocRxConfig(M4_DMA_TypeDef* DMAx, en_dma_channel_t enCh, M4_SDIOC_TypeDef *SDIOCx, uint8_t *pu8RxBuf, uint16_t u16len) { stc_dma_config_t stcDmaInit; uint32_t u32Fcg0Periph = (M4_DMA1 == DMAx) ? PWC_FCG0_PERIPH_DMA1 : PWC_FCG0_PERIPH_DMA2; en_event_src_t enEvtSrc = (M4_SDIOC1 == SDIOCx) ? EVT_SDIOC1_DMAR : EVT_SDIOC2_DMAR; en_result_t enRet = ErrorInvalidParameter; if ((NULL != pu8RxBuf) && \ (IS_VALID_TRANSFER_BUF_LEN(u16len)) && \ (IS_VALID_TRANSFER_BUF_ALIGN(pu8RxBuf))) { /* Enable peripheral clock */ PWC_Fcg0PeriphClockCmd(u32Fcg0Periph,Enable); /* Enable DMA. */ DMA_Cmd(DMAx, Enable); /* Initialize DMA. */ MEM_ZERO_STRUCT(stcDmaInit); stcDmaInit.u16BlockSize = SD_CARD_BLOCK_SIZE/4u; /* Set data block size. */ stcDmaInit.u16TransferCnt = u16len/SD_CARD_BLOCK_SIZE; /* Set transfer count. */ stcDmaInit.u32SrcAddr = (uint32_t)(&SDIOCx->BUF0); /* Set source address. */ stcDmaInit.u32DesAddr = (uint32_t)(&pu8RxBuf[0]); /* Set destination address. */ stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix; /* Set source address mode. */ stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease; /* Set destination address mode. */ stcDmaInit.stcDmaChCfg.enTrnWidth = Dma32Bit; /* Set data width 8bit. */ DMA_InitChannel(DMAx, enCh, &stcDmaInit); /* Enable the specified DMA channel. */ DMA_ChannelCmd(DMAx, enCh, Enable); /* Clear DMA flag. */ DMA_ClearIrqFlag(DMAx, enCh, TrnCpltIrq); DMA_ClearIrqFlag(DMAx, enCh, BlkTrnCpltIrq); /* Enable peripheral circuit trigger function. */ PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable); /* Set DMA trigger source. */ DMA_SetTriggerSrc(DMAx, enCh, enEvtSrc); enRet = Ok; } return enRet; } //@} // SdiocGroup /******************************************************************************* * EOF (not truncated) ******************************************************************************/