/* * The Clear BSD License * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted (subject to the limitations in the disclaimer below) 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 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. * * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. * 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. */ #include "fsl_sdmmc_common.h" /******************************************************************************* * Variables ******************************************************************************/ SDK_ALIGN(uint32_t g_sdmmc[SDK_SIZEALIGN(SDMMC_GLOBAL_BUFFER_SIZE, SDMMC_DATA_BUFFER_ALIGN_CACHE)], MAX(SDMMC_DATA_BUFFER_ALIGN_CACHE, SDMMCHOST_DMA_BUFFER_ADDR_ALIGN)); /******************************************************************************* * Code ******************************************************************************/ status_t SDMMC_SelectCard(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer, uint32_t relativeAddress, bool isSelected) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSDMMC_SelectCard; if (isSelected) { command.argument = relativeAddress << 16U; command.responseType = kCARD_ResponseTypeR1; } else { command.argument = 0U; command.responseType = kCARD_ResponseTypeNone; } content.command = &command; content.data = NULL; if ((kStatus_Success != transfer(base, &content)) || (command.response[0U] & kSDMMC_R1ErrorAllFlag)) { return kStatus_SDMMC_TransferFailed; } /* Wait until card to transfer state */ return kStatus_Success; } status_t SDMMC_SendApplicationCommand(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer, uint32_t relativeAddress) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSDMMC_ApplicationCommand; command.argument = (relativeAddress << 16U); command.responseType = kCARD_ResponseTypeR1; content.command = &command; content.data = 0U; if ((kStatus_Success != transfer(base, &content)) || (command.response[0U] & kSDMMC_R1ErrorAllFlag)) { return kStatus_SDMMC_TransferFailed; } if (!(command.response[0U] & kSDMMC_R1ApplicationCommandFlag)) { return kStatus_SDMMC_CardNotSupport; } return kStatus_Success; } status_t SDMMC_SetBlockCount(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer, uint32_t blockCount) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSDMMC_SetBlockCount; command.argument = blockCount; command.responseType = kCARD_ResponseTypeR1; content.command = &command; content.data = 0U; if ((kStatus_Success != transfer(base, &content)) || (command.response[0U] & kSDMMC_R1ErrorAllFlag)) { return kStatus_SDMMC_TransferFailed; } return kStatus_Success; } status_t SDMMC_GoIdle(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSDMMC_GoIdleState; content.command = &command; content.data = 0U; if (kStatus_Success != transfer(base, &content)) { return kStatus_SDMMC_TransferFailed; } return kStatus_Success; } status_t SDMMC_SetBlockSize(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer, uint32_t blockSize) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSDMMC_SetBlockLength; command.argument = blockSize; command.responseType = kCARD_ResponseTypeR1; content.command = &command; content.data = 0U; if ((kStatus_Success != transfer(base, &content)) || (command.response[0U] & kSDMMC_R1ErrorAllFlag)) { return kStatus_SDMMC_TransferFailed; } return kStatus_Success; } status_t SDMMC_SetCardInactive(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSDMMC_GoInactiveState; command.argument = 0U; command.responseType = kCARD_ResponseTypeNone; content.command = &command; content.data = 0U; if ((kStatus_Success != transfer(base, &content))) { return kStatus_SDMMC_TransferFailed; } return kStatus_Success; } status_t SDMMC_SwitchVoltage(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer) { assert(transfer); SDMMCHOST_TRANSFER content = {0}; SDMMCHOST_COMMAND command = {0}; command.index = kSD_VoltageSwitch; command.argument = 0U; command.responseType = kCARD_ResponseTypeR1; content.command = &command; content.data = NULL; if (kStatus_Success != transfer(base, &content)) { return kStatus_SDMMC_TransferFailed; } /* disable card clock */ SDMMCHOST_ENABLE_CARD_CLOCK(base, false); /* check data line and cmd line status */ if ((GET_SDMMCHOST_STATUS(base) & (CARD_DATA1_STATUS_MASK | CARD_DATA2_STATUS_MASK | CARD_DATA3_STATUS_MASK | CARD_DATA0_NOT_BUSY)) != 0U) { return kStatus_SDMMC_SwitchVoltageFail; } /* host switch to 1.8V */ SDMMCHOST_SWITCH_VOLTAGE180V(base, true); SDMMCHOST_Delay(100U); /*enable sd clock*/ SDMMCHOST_ENABLE_CARD_CLOCK(base, true); /*enable force clock on*/ SDMMCHOST_FORCE_SDCLOCK_ON(base, true); /* dealy 1ms,not exactly correct when use while */ SDMMCHOST_Delay(10U); /*disable force clock on*/ SDMMCHOST_FORCE_SDCLOCK_ON(base, false); /* check data line and cmd line status */ if ((GET_SDMMCHOST_STATUS(base) & (CARD_DATA1_STATUS_MASK | CARD_DATA2_STATUS_MASK | CARD_DATA3_STATUS_MASK | CARD_DATA0_NOT_BUSY)) == 0U) { return kStatus_SDMMC_SwitchVoltageFail; } return kStatus_Success; } status_t SDMMC_ExecuteTuning(SDMMCHOST_TYPE *base, SDMMCHOST_TRANSFER_FUNCTION transfer, uint32_t tuningCmd, uint32_t blockSize) { SDMMCHOST_TRANSFER content = {0U}; SDMMCHOST_COMMAND command = {0U}; SDMMCHOST_DATA data = {0U}; uint32_t buffer[32U] = {0U}; bool tuningError = true; command.index = tuningCmd; command.argument = 0U; command.responseType = kCARD_ResponseTypeR1; data.blockSize = blockSize; data.blockCount = 1U; data.rxData = buffer; /* add this macro for adpter to different driver */ SDMMCHOST_ENABLE_TUNING_FLAG(data); content.command = &command; content.data = &data; /* enable the standard tuning */ SDMMCHOST_EXECUTE_STANDARD_TUNING_ENABLE(base, true); while (true) { /* send tuning block */ if ((kStatus_Success != transfer(base, &content))) { return kStatus_SDMMC_TransferFailed; } SDMMCHOST_Delay(1U); /*wait excute tuning bit clear*/ if ((SDMMCHOST_EXECUTE_STANDARD_TUNING_STATUS(base) != 0U)) { continue; } /* if tuning error , re-tuning again */ if ((SDMMCHOST_CHECK_TUNING_ERROR(base) != 0U) && tuningError) { tuningError = false; /* enable the standard tuning */ SDMMCHOST_EXECUTE_STANDARD_TUNING_ENABLE(base, true); SDMMCHOST_ADJUST_TUNING_DELAY(base, SDMMCHOST_STANDARD_TUNING_START); } else { break; } } /* delay to wait the host controller stable */ SDMMCHOST_Delay(100U); /* check tuning result*/ if (SDMMCHOST_EXECUTE_STANDARD_TUNING_RESULT(base) == 0U) { return kStatus_SDMMC_TuningFail; } SDMMCHOST_AUTO_STANDARD_RETUNING_TIMER(base); return kStatus_Success; }