2291 lines
74 KiB
C
2291 lines
74 KiB
C
/*
|
|
* Copyright (c) 2015, Freescale Semiconductor, Inc.
|
|
* Copyright 2016-2019 NXP
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#include "fsl_sd.h"
|
|
|
|
/*******************************************************************************
|
|
* Definitions
|
|
******************************************************************************/
|
|
/*!@brief power reset delay */
|
|
#define SD_POWER_ON_DELAY (400U)
|
|
#define SD_POWER_OFF_DELAY (100U)
|
|
/*! @brief card access idle timeout value */
|
|
#ifndef SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT
|
|
#define SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT (600U) /* more then 500ms timeout value */
|
|
#endif
|
|
/*! @brief card cmd13 retry times */
|
|
#ifndef SD_CMD13_RETRY_TIMES
|
|
#define SD_CMD13_RETRY_TIMES (10)
|
|
#endif
|
|
/*******************************************************************************
|
|
* Prototypes
|
|
******************************************************************************/
|
|
/*!
|
|
* @brief Wait write process complete.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_Timeout Send command timeout.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SendCardStatus(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief send write success blocks.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param blocks blocks number wirte successed
|
|
* @retval kStatus_SDMMC_TransferFailed Send command failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SendWriteSuccessBlocks(sd_card_t *card, uint32_t *blocks);
|
|
|
|
/*!
|
|
* @brief Send SEND_APPLICATION_COMMAND command.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param relativeaddress
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_SDMMC_CardNotSupport Card doesn't support.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static inline status_t SD_SendApplicationCmd(sd_card_t *card, uint32_t relativeAddress);
|
|
|
|
/*!
|
|
* @brief Send GO_IDLE command to set the card to be idle state.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static inline status_t SD_GoIdle(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Send STOP_TRANSMISSION command after multiple blocks read/write.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_StopTransmission(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Send SET_BLOCK_SIZE command.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param blockSize Block size.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static inline status_t SD_SetBlockSize(sd_card_t *card, uint32_t blockSize);
|
|
|
|
/*!
|
|
* @brief Send GET_RCA command to get card relative address.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SendRca(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Send SWITCH_FUNCTION command to switch the card function group.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param mode 0 to check function group. 1 to switch function group
|
|
* @param group Function group
|
|
* @param number Function number in the function group.
|
|
* @param status Switch function status.
|
|
* @retval kStatus_SDMMC_SetCardBlockSizeFailed Set card block size failed.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SwitchFunction(sd_card_t *card, uint32_t mode, uint32_t group, uint32_t number, uint32_t *status);
|
|
|
|
/*!
|
|
* @brief Decode raw SCR register content in the data blocks.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param rawScr Raw SCR register content.
|
|
*/
|
|
static void SD_DecodeScr(sd_card_t *card, uint32_t *rawScr);
|
|
|
|
/*!
|
|
* @brief Send GET_SCR command.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_SendApplicationCommandFailed Send application command failed.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_SDMMC_NotSupportYet Not support yet.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SendScr(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Switch the card to be high speed mode.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param group Group number.
|
|
* @param functio Function number.
|
|
* @retval kStatus_SDMMC_CardNotSupport Card not support.
|
|
* @retval kStatus_SDMMC_SwitchFailed Switch failed.
|
|
* @retval kStatus_SDMMC_NotSupportYet Not support yet.
|
|
* @retval kStatus_Fail Switch failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SelectFunction(sd_card_t *card, uint32_t group, uint32_t function);
|
|
|
|
/*!
|
|
* @brief Send SET_DATA_WIDTH command to set SD bus width.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param width Data bus width.
|
|
* @retval kStatus_SDMMC_SendApplicationCommandFailed Send application command failed.
|
|
* @retval kStatus_InvalidArgument Invalid argument.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SetDataBusWidth(sd_card_t *card, uint32_t width);
|
|
|
|
/*!
|
|
* @brief Decode raw CSD register content in the data blocks.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param rawCsd Raw CSD register content.
|
|
*/
|
|
static void SD_DecodeCsd(sd_card_t *card, uint32_t *rawCsd);
|
|
|
|
/*!
|
|
* @brief Send SEND_CSD command to get CSD register content from Card.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SendCsd(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Decode raw CID register content in the data blocks.
|
|
*
|
|
* @param rawCid raw CID register content.
|
|
* @param card Card descriptor.
|
|
*/
|
|
static void SD_DecodeCid(sd_card_t *card, uint32_t *rawCid);
|
|
|
|
/*!
|
|
* @brief Send GET_CID command to get CID from card.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_AllSendCid(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Send SEND_OPERATION_CONDITION command.
|
|
*
|
|
* This function sends host capacity support information and asks the accessed card to send its operating condition
|
|
* register content.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param argument The argument of the send operation condition ncomamnd.
|
|
* @retval kStatus_SDMMC_SendApplicationCommandFailed Send application command failed.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Timeout Timeout.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_ApplicationSendOperationCondition(sd_card_t *card, uint32_t argument);
|
|
|
|
/*!
|
|
* @brief Send GET_INTERFACE_CONDITION command to get card interface condition.
|
|
*
|
|
* This function checks card interface condition, which includes host supply voltage information and asks the card
|
|
* whether card supports the specified host voltage.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_SDMMC_CardNotSupport Card doesn't support.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_SendInterfaceCondition(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Send switch voltage command
|
|
* switch card voltage to 1.8v
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param voltage target voltage
|
|
*/
|
|
static status_t SD_SwitchVoltage(sd_card_t *card, sdmmc_operation_voltage_t voltage);
|
|
|
|
/*!
|
|
* @brief select bus timing
|
|
* select card timing
|
|
* @param card Card descriptor.
|
|
*/
|
|
static status_t SD_SelectBusTiming(sd_card_t *card);
|
|
|
|
/*!
|
|
* @brief Decode sd 512 bit status
|
|
* @param card Card descriptor.
|
|
* @param 512 bits satus raw data.
|
|
*/
|
|
static void SD_DecodeStatus(sd_card_t *card, uint32_t *src);
|
|
|
|
/*!
|
|
* @brief Read data from specific SD card.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param buffer Buffer to save data blocks read.
|
|
* @param startBlock Card start block number to be read.
|
|
* @param blockSize Block size.
|
|
* @param blockCount Block count.
|
|
* @retval kStatus_SDMMC_CardNotSupport Card doesn't support.
|
|
* @retval kStatus_SDMMC_WaitWriteCompleteFailed Wait write complete failed.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_SDMMC_StopTransmissionFailed Stop transmission failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_Read(sd_card_t *card, uint8_t *buffer, uint32_t startBlock, uint32_t blockSize, uint32_t blockCount);
|
|
|
|
/*!
|
|
* @brief Write data to specific card
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param buffer Buffer to be sent.
|
|
* @param startBlock Card start block number to be written.
|
|
* @param blockSize Block size.
|
|
* @param blockCount Block count.
|
|
* @param writtenBlocks successfully write blocks
|
|
* @retval kStatus_SDMMC_CardNotSupport Card doesn't support.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_SDMMC_StopTransmissionFailed Stop transmission failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_Write(sd_card_t *card,
|
|
const uint8_t *buffer,
|
|
uint32_t startBlock,
|
|
uint32_t blockSize,
|
|
uint32_t blockCount,
|
|
uint32_t *writtenBlocks);
|
|
|
|
/*!
|
|
* @brief Erase data for the given block range.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param startBlock Card start block number to be erased.
|
|
* @param blockCount The block count to be erased.
|
|
* @param timeout timeout value in ms will be used to wait erase done.
|
|
*
|
|
* @retval kStatus_SDMMC_WaitWriteCompleteFailed wait erase timeout.
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
*/
|
|
static status_t SD_Erase(sd_card_t *card, uint32_t startBlock, uint32_t blockCount, uint32_t timeout);
|
|
|
|
/*!
|
|
* @brief card transfer function.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @param content Transfer content.
|
|
* @param retry Retry times
|
|
* @retval kStatus_SDMMC_TransferFailed Transfer failed.
|
|
* @retval kStatus_Success Operate successfully.
|
|
* @retval kStatus_SDMMC_TuningFail tuning fail
|
|
*/
|
|
static status_t SD_Transfer(sd_card_t *card, sdmmchost_transfer_t *content, uint32_t retry);
|
|
|
|
/*!
|
|
* @brief card execute tuning function.
|
|
*
|
|
* @param card Card descriptor.
|
|
* @retval kStatus_Success Operate successfully.
|
|
* @retval kStatus_SDMMC_TuningFail tuning fail.
|
|
* @retval kStatus_SDMMC_TransferFailed transfer fail
|
|
*/
|
|
static inline status_t SD_ExecuteTuning(sd_card_t *card);
|
|
|
|
/*******************************************************************************
|
|
* Variables
|
|
******************************************************************************/
|
|
/*!@brief sd size map */
|
|
static uint32_t s_sdAuSizeMap[] = {0,
|
|
16 * 1024,
|
|
32 * 1024,
|
|
64 * 1024,
|
|
128 * 1024,
|
|
256 * 1024,
|
|
512 * 1024,
|
|
1024 * 1024,
|
|
2 * 1024 * 1024,
|
|
4 * 1024 * 1024,
|
|
8 * 1024 * 1024,
|
|
12 * 1024 * 1024,
|
|
16 * 1024 * 1024,
|
|
24 * 1024 * 1024,
|
|
32 * 1024 * 1024,
|
|
64 * 1024 * 1024};
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
static status_t SD_SendApplicationCmd(sd_card_t *card, uint32_t relativeAddress)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_SendApplicationCommand(card->host, relativeAddress);
|
|
}
|
|
|
|
static status_t SD_GoIdle(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_GoIdle(card->host);
|
|
}
|
|
|
|
static status_t SD_SetBlockSize(sd_card_t *card, uint32_t blockSize)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_SetBlockSize(card->host, blockSize);
|
|
}
|
|
|
|
static status_t SD_ExecuteTuning(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMCHOST_ExecuteTuning(card->host, (uint32_t)kSD_SendTuningBlock,
|
|
(uint32_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer), 64U);
|
|
}
|
|
|
|
static status_t SD_SwitchIOVoltage(sd_card_t *card, sdmmc_operation_voltage_t voltage)
|
|
{
|
|
if ((card->usrParam.ioVoltage != NULL) && (card->usrParam.ioVoltage->type == kSD_IOVoltageCtrlByGpio))
|
|
{
|
|
/* make sure card signal line voltage is 3.3v before initalization */
|
|
if (card->usrParam.ioVoltage->func != NULL)
|
|
{
|
|
card->usrParam.ioVoltage->func(voltage);
|
|
}
|
|
}
|
|
else if ((card->usrParam.ioVoltage != NULL) && (card->usrParam.ioVoltage->type == kSD_IOVoltageCtrlByHost))
|
|
{
|
|
SDMMCHOST_SwitchToVoltage(card->host, (uint32_t)voltage);
|
|
}
|
|
else
|
|
{
|
|
return kStatus_SDMMC_NotSupportYet;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SD_SwitchVoltage(sd_card_t *card, sdmmc_operation_voltage_t voltage)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSD_VoltageSwitch;
|
|
command.argument = 0U;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* check data line and cmd line status */
|
|
if (SDMMCHOST_GetSignalLineStatus(card->host, (uint32_t)kSDMMC_SignalLineData0 | (uint32_t)kSDMMC_SignalLineData1 |
|
|
(uint32_t)kSDMMC_SignalLineData2 |
|
|
(uint32_t)kSDMMC_SignalLineData3) != 0U)
|
|
{
|
|
return kStatus_SDMMC_SwitchVoltageFail;
|
|
}
|
|
|
|
/* switch io voltage */
|
|
if (SD_SwitchIOVoltage(card, voltage) == kStatus_SDMMC_NotSupportYet)
|
|
{
|
|
return kStatus_SDMMC_SwitchVoltageFail;
|
|
}
|
|
|
|
SDMMC_OSADelay(100U);
|
|
|
|
/*enable force clock on*/
|
|
SDMMCHOST_ForceClockOn(card->host, true);
|
|
/* dealy 1ms,not exactly correct when use while */
|
|
SDMMC_OSADelay(10U);
|
|
/*disable force clock on*/
|
|
SDMMCHOST_ForceClockOn(card->host, false);
|
|
|
|
/* check data line and cmd line status */
|
|
if (SDMMCHOST_GetSignalLineStatus(card->host, (uint32_t)kSDMMC_SignalLineData0 | (uint32_t)kSDMMC_SignalLineData1 |
|
|
(uint32_t)kSDMMC_SignalLineData2 |
|
|
(uint32_t)kSDMMC_SignalLineData3) == 0U)
|
|
{
|
|
error = kStatus_SDMMC_SwitchVoltageFail;
|
|
/* power reset the card */
|
|
SD_SetCardPower(card, false);
|
|
SD_SetCardPower(card, true);
|
|
/* re-check the data line status */
|
|
if (SDMMCHOST_GetSignalLineStatus(
|
|
card->host, (uint32_t)kSDMMC_SignalLineData0 | (uint32_t)kSDMMC_SignalLineData1 |
|
|
(uint32_t)kSDMMC_SignalLineData2 | (uint32_t)kSDMMC_SignalLineData3) != 0U)
|
|
{
|
|
error = kStatus_SDMMC_SwitchVoltage18VFail33VSuccess;
|
|
SDMMC_LOG(
|
|
"\r\nNote: Current card support 1.8V, but board don't support, so sdmmc switch back to 3.3V.\r\n");
|
|
}
|
|
else
|
|
{
|
|
SDMMC_LOG(
|
|
"\r\nError: Current card support 1.8V, but board don't support, sdmmc tried to switch back\
|
|
to 3.3V, but failed, please check board setting.\r\n");
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_StopTransmission(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSDMMC_StopTransmission;
|
|
command.argument = 0U;
|
|
command.type = kCARD_CommandTypeAbort;
|
|
command.responseType = kCARD_ResponseTypeR1b;
|
|
command.responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD12 failed with host error %d, reponse %x\r\n", error, command.response[0U]);
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SD_Transfer(sd_card_t *card, sdmmchost_transfer_t *content, uint32_t retry)
|
|
{
|
|
assert(content != NULL);
|
|
status_t error;
|
|
uint32_t retuningCount = 3U;
|
|
|
|
do
|
|
{
|
|
error = SDMMCHOST_TransferFunction(card->host, content);
|
|
if (error == kStatus_Success)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* if transfer data failed, send cmd12 to abort current transfer */
|
|
if (content->data != NULL)
|
|
{
|
|
(void)SD_StopTransmission(card);
|
|
/* when transfer error occur, polling card status until it is ready for next data transfer, otherwise the
|
|
* retry transfer will fail again */
|
|
error = SD_PollingCardStatusBusy(card, SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT);
|
|
if (error != kStatus_SDMMC_CardStatusIdle)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
}
|
|
|
|
if ((retry == 0U) || (error == kStatus_SDMMC_ReTuningRequest))
|
|
{
|
|
if ((card->currentTiming == kSD_TimingSDR50Mode) || (card->currentTiming == kSD_TimingSDR104Mode))
|
|
{
|
|
if (--retuningCount == 0U)
|
|
{
|
|
break;
|
|
}
|
|
/* perform retuning */
|
|
if (SD_ExecuteTuning(card) != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_TuningFail;
|
|
SDMMC_LOG("\r\nError: retuning failed.\r\n");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
SDMMC_LOG("\r\nlog: retuning successfully.\r\n");
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (retry != 0U)
|
|
{
|
|
retry--;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
} while (true);
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SendCardStatus(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
uint32_t retry = SD_CMD13_RETRY_TIMES;
|
|
|
|
command.index = (uint32_t)kSDMMC_SendStatus;
|
|
command.argument = card->relativeAddress << 16U;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
while (retry != 0U)
|
|
{
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD13 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
retry--;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (((command.response[0U] & SDMMC_MASK(kSDMMC_R1ReadyForDataFlag)) != 0U) &&
|
|
(SDMMC_R1_CURRENT_STATE(command.response[0U]) != (uint32_t)kSDMMC_R1StateProgram))
|
|
{
|
|
error = kStatus_SDMMC_CardStatusIdle;
|
|
}
|
|
else
|
|
{
|
|
error = kStatus_SDMMC_CardStatusBusy;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SD_PollingCardStatusBusy(sd_card_t *card, uint32_t timeoutMs)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t statusTimeoutUs = timeoutMs * 1000U;
|
|
bool cardBusy = false;
|
|
status_t error = kStatus_SDMMC_CardStatusBusy;
|
|
|
|
do
|
|
{
|
|
cardBusy = SDMMCHOST_IsCardBusy(card->host);
|
|
|
|
if (cardBusy == false)
|
|
{
|
|
error = SD_SendCardStatus(card);
|
|
if (error == kStatus_SDMMC_CardStatusIdle)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Delay 125us to throttle the polling rate */
|
|
statusTimeoutUs -= SDMMC_OSADelayUs(125U);
|
|
}
|
|
|
|
} while (statusTimeoutUs != 0U);
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SendWriteSuccessBlocks(sd_card_t *card, uint32_t *blocks)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
sdmmchost_data_t data = {0};
|
|
status_t error = kStatus_Success;
|
|
uint32_t *rawBuffer = (uint32_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
|
|
(void)memset(rawBuffer, 0, 4U);
|
|
|
|
/* Wait for the card write process complete because of that card read process and write process use one buffer. */
|
|
error = SD_PollingCardStatusBusy(card, SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT);
|
|
if (kStatus_SDMMC_CardStatusIdle != error)
|
|
{
|
|
return kStatus_SDMMC_WaitWriteCompleteFailed;
|
|
}
|
|
|
|
if (kStatus_Success != SD_SendApplicationCmd(card, card->relativeAddress))
|
|
{
|
|
return kStatus_SDMMC_SendApplicationCommandFailed;
|
|
}
|
|
|
|
command.index = (uint32_t)kSD_ApplicationSendNumberWriteBlocks;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
|
|
data.blockSize = 4U;
|
|
data.blockCount = 1U;
|
|
data.rxData = rawBuffer;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if ((kStatus_Success != error) || (((command.response[0U]) & SDMMC_R1_ALL_ERROR_FLAG) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\nError: send ACMD22 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
}
|
|
else
|
|
{
|
|
*blocks = SWAP_WORD_BYTE_SEQUENCE(*rawBuffer);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SendRca(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSD_SendRelativeAddress;
|
|
command.argument = 0U;
|
|
command.responseType = kCARD_ResponseTypeR6;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success == error)
|
|
{
|
|
card->relativeAddress = (command.response[0U] >> 16U);
|
|
}
|
|
else
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD3 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SwitchFunction(sd_card_t *card, uint32_t mode, uint32_t group, uint32_t number, uint32_t *status)
|
|
{
|
|
assert(card != NULL);
|
|
assert(status != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
sdmmchost_data_t data = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSD_Switch;
|
|
command.argument = (mode << 31U | 0x00FFFFFFU);
|
|
command.argument &= ~((uint32_t)(0xFU) << (group * 4U));
|
|
command.argument |= (number << (group * 4U));
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
|
|
data.blockSize = 64U;
|
|
data.blockCount = 1U;
|
|
data.rxData = status;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if ((kStatus_Success != error) || (((command.response[0U]) & SDMMC_R1_ALL_ERROR_FLAG) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\n\r\nError: send CMD6 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void SD_DecodeScr(sd_card_t *card, uint32_t *rawScr)
|
|
{
|
|
assert(card != NULL);
|
|
assert(rawScr != NULL);
|
|
|
|
sd_scr_t *scr;
|
|
|
|
scr = &(card->scr);
|
|
scr->scrStructure = (uint8_t)((rawScr[0U] & 0xF0000000U) >> 28U);
|
|
scr->sdSpecification = (uint8_t)((rawScr[0U] & 0xF000000U) >> 24U);
|
|
if ((uint8_t)((rawScr[0U] & 0x800000U) >> 23U) != 0U)
|
|
{
|
|
scr->flags |= (uint16_t)kSD_ScrDataStatusAfterErase;
|
|
}
|
|
scr->sdSecurity = (uint8_t)((rawScr[0U] & 0x700000U) >> 20U);
|
|
scr->sdBusWidths = (uint8_t)((rawScr[0U] & 0xF0000U) >> 16U);
|
|
if ((uint8_t)((rawScr[0U] & 0x8000U) >> 15U) != 0U)
|
|
{
|
|
scr->flags |= (uint16_t)kSD_ScrSdSpecification3;
|
|
}
|
|
scr->extendedSecurity = (uint8_t)((rawScr[0U] & 0x7800U) >> 10U);
|
|
scr->commandSupport = (uint8_t)(rawScr[0U] & 0x3U);
|
|
scr->reservedForManufacturer = rawScr[1U];
|
|
/* Get specification version. */
|
|
if (scr->sdSpecification == 0U)
|
|
{
|
|
card->version = kSD_SpecificationVersion1_0;
|
|
}
|
|
else if (scr->sdSpecification == 1U)
|
|
{
|
|
card->version = kSD_SpecificationVersion1_1;
|
|
}
|
|
else if (scr->sdSpecification == 2U)
|
|
{
|
|
card->version = kSD_SpecificationVersion2_0;
|
|
if ((card->scr.flags & (uint32_t)kSD_ScrSdSpecification3) != 0U)
|
|
{
|
|
card->version = kSD_SpecificationVersion3_0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* reserved */
|
|
}
|
|
|
|
if ((card->scr.sdBusWidths & 0x4U) != 0U)
|
|
{
|
|
card->flags |= (uint32_t)kSD_Support4BitWidthFlag;
|
|
}
|
|
/* speed class control cmd */
|
|
if ((card->scr.commandSupport & 0x01U) != 0U)
|
|
{
|
|
card->flags |= (uint32_t)kSD_SupportSpeedClassControlCmd;
|
|
}
|
|
/* set block count cmd */
|
|
if ((card->scr.commandSupport & 0x02U) != 0U)
|
|
{
|
|
card->flags |= (uint32_t)kSD_SupportSetBlockCountCmd;
|
|
}
|
|
}
|
|
|
|
static status_t SD_SendScr(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
sdmmchost_data_t data = {0};
|
|
uint32_t *rawScr = (uint32_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
status_t error = kStatus_Success;
|
|
|
|
/* memset the global buffer */
|
|
(void)memset(rawScr, 0, 8U);
|
|
|
|
if (kStatus_Success != SD_SendApplicationCmd(card, card->relativeAddress))
|
|
{
|
|
return kStatus_SDMMC_SendApplicationCommandFailed;
|
|
}
|
|
|
|
command.index = (uint32_t)kSD_ApplicationSendScr;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
command.argument = 0U;
|
|
|
|
data.blockSize = 8U;
|
|
data.blockCount = 1U;
|
|
data.rxData = rawScr;
|
|
|
|
content.data = &data;
|
|
content.command = &command;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if ((kStatus_Success != error) || (((command.response[0U]) & SDMMC_R1_ALL_ERROR_FLAG) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\nError: send ACMD51 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
}
|
|
else
|
|
{
|
|
SDMMCHOST_ConvertDataToLittleEndian(card->host, rawScr, 2U, kSDMMC_DataPacketFormatMSBFirst);
|
|
/* decode scr */
|
|
SD_DecodeScr(card, rawScr);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SelectFunction(sd_card_t *card, uint32_t group, uint32_t function)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t *functionStatus = (uint32_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
uint16_t functionGroupInfo[6U] = {0};
|
|
uint32_t currentFunctionStatus = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* memset the global buffer */
|
|
(void)memset(functionStatus, 0, 64U);
|
|
|
|
/* check if card support CMD6 */
|
|
if ((card->version <= (uint32_t)kSD_SpecificationVersion1_0) ||
|
|
(0U == (card->csd.cardCommandClass & (uint32_t)kSDMMC_CommandClassSwitch)))
|
|
{
|
|
SDMMC_LOG("\r\nError: current card not support CMD6\r\n");
|
|
return kStatus_SDMMC_NotSupportYet;
|
|
}
|
|
|
|
error = SD_SwitchFunction(card, (uint32_t)kSD_SwitchCheck, group, function, functionStatus);
|
|
/* Check if card support high speed mode. */
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* convert to little endian sequence */
|
|
SDMMCHOST_ConvertDataToLittleEndian(card->host, functionStatus, 5U, kSDMMC_DataPacketFormatMSBFirst);
|
|
|
|
/* -functionStatus[0U]---bit511~bit480;
|
|
-functionStatus[1U]---bit479~bit448;
|
|
-functionStatus[2U]---bit447~bit416;
|
|
-functionStatus[3U]---bit415~bit384;
|
|
-functionStatus[4U]---bit383~bit352;
|
|
According to the "switch function status[bits 511~0]" return by switch command in mode "check function":
|
|
-Check if function 1(high speed) in function group 1 is supported by checking if bit 401 is set;
|
|
-check if function 1 is ready and can be switched by checking if bits 379~376 equal value 1;
|
|
*/
|
|
functionGroupInfo[5U] = (uint16_t)functionStatus[0U];
|
|
functionGroupInfo[4U] = (uint16_t)(functionStatus[1U] >> 16U);
|
|
functionGroupInfo[3U] = (uint16_t)(functionStatus[1U]);
|
|
functionGroupInfo[2U] = (uint16_t)(functionStatus[2U] >> 16U);
|
|
functionGroupInfo[1U] = (uint16_t)(functionStatus[2U]);
|
|
functionGroupInfo[0U] = (uint16_t)(functionStatus[3U] >> 16U);
|
|
currentFunctionStatus = ((functionStatus[3U] & 0xFFU) << 8U) | (functionStatus[4U] >> 24U);
|
|
|
|
/* check if function is support */
|
|
if (((functionGroupInfo[group] & (1UL << function)) == 0U) ||
|
|
((currentFunctionStatus >> (group * 4U)) & 0xFU) != function)
|
|
{
|
|
SDMMC_LOG("\r\nError: current card not support function %d\r\n", function);
|
|
return kStatus_SDMMC_NotSupportYet;
|
|
}
|
|
|
|
error = SD_SwitchFunction(card, (uint32_t)kSD_SwitchSet, group, function, functionStatus);
|
|
/* Switch to high speed mode. */
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* convert to little endian sequence */
|
|
SDMMCHOST_ConvertDataToLittleEndian(card->host, &functionStatus[3U], 2U, kSDMMC_DataPacketFormatMSBFirst);
|
|
|
|
/* According to the "switch function status[bits 511~0]" return by switch command in mode "set function":
|
|
-check if group 1 is successfully changed to function 1 by checking if bits 379~376 equal value 1;
|
|
*/
|
|
currentFunctionStatus = ((functionStatus[3U] & 0xFFU) << 8U) | (functionStatus[4U] >> 24U);
|
|
|
|
if (((currentFunctionStatus >> (group * 4U)) & 0xFU) != function)
|
|
{
|
|
SDMMC_LOG("\r\nError: switch to function %d failed\r\n", function);
|
|
return kStatus_SDMMC_SwitchFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SD_SetDataBusWidth(sd_card_t *card, uint32_t width)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
if (kStatus_Success != SD_SendApplicationCmd(card, card->relativeAddress))
|
|
{
|
|
return kStatus_SDMMC_SendApplicationCommandFailed;
|
|
}
|
|
|
|
command.index = (uint32_t)kSD_ApplicationSetBusWdith;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
|
|
if (width == (uint32_t)kSDMMC_BusWdith1Bit)
|
|
{
|
|
command.argument = 0U;
|
|
}
|
|
else if (width == (uint32_t)kSDMMC_BusWdith4Bit)
|
|
{
|
|
command.argument = 2U;
|
|
}
|
|
else
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if ((kStatus_Success != error) || (((command.response[0U]) & SDMMC_R1_ALL_ERROR_FLAG) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\nError: send ACMD6 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void SD_DecodeCsd(sd_card_t *card, uint32_t *rawCsd)
|
|
{
|
|
assert(card != NULL);
|
|
assert(rawCsd != NULL);
|
|
|
|
sd_csd_t *csd;
|
|
|
|
csd = &(card->csd);
|
|
csd->csdStructure = (uint8_t)((rawCsd[3U] & 0xC0000000U) >> 30U);
|
|
csd->dataReadAccessTime1 = (uint8_t)((rawCsd[3U] & 0xFF0000U) >> 16U);
|
|
csd->dataReadAccessTime2 = (uint8_t)((rawCsd[3U] & 0xFF00U) >> 8U);
|
|
csd->transferSpeed = (uint8_t)(rawCsd[3U] & 0xFFU);
|
|
csd->cardCommandClass = (uint16_t)((rawCsd[2U] & 0xFFF00000U) >> 20U);
|
|
csd->readBlockLength = (uint8_t)((rawCsd[2U] & 0xF0000U) >> 16U);
|
|
if ((rawCsd[2U] & 0x8000U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdReadBlockPartialFlag;
|
|
}
|
|
if ((rawCsd[2U] & 0x4000U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdReadBlockPartialFlag;
|
|
}
|
|
if ((rawCsd[2U] & 0x2000U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdReadBlockMisalignFlag;
|
|
}
|
|
if ((rawCsd[2U] & 0x1000U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdDsrImplementedFlag;
|
|
}
|
|
if (csd->csdStructure == 0U)
|
|
{
|
|
csd->deviceSize = (uint32_t)((rawCsd[2U] & 0x3FFU) << 2U);
|
|
csd->deviceSize |= (uint32_t)((rawCsd[1U] & 0xC0000000U) >> 30U);
|
|
csd->readCurrentVddMin = (uint8_t)((rawCsd[1U] & 0x38000000U) >> 27U);
|
|
csd->readCurrentVddMax = (uint8_t)((rawCsd[1U] & 0x7000000U) >> 24U);
|
|
csd->writeCurrentVddMin = (uint8_t)((rawCsd[1U] & 0xE00000U) >> 20U);
|
|
csd->writeCurrentVddMax = (uint8_t)((rawCsd[1U] & 0x1C0000U) >> 18U);
|
|
csd->deviceSizeMultiplier = (uint8_t)((rawCsd[1U] & 0x38000U) >> 15U);
|
|
|
|
/* Get card total block count and block size. */
|
|
card->blockCount = ((csd->deviceSize + 1U) << (csd->deviceSizeMultiplier + 2U));
|
|
card->blockSize = (1UL << (csd->readBlockLength));
|
|
if (card->blockSize != FSL_SDMMC_DEFAULT_BLOCK_SIZE)
|
|
{
|
|
card->blockCount = (card->blockCount * card->blockSize);
|
|
card->blockSize = FSL_SDMMC_DEFAULT_BLOCK_SIZE;
|
|
card->blockCount = (card->blockCount / card->blockSize);
|
|
}
|
|
}
|
|
else if (csd->csdStructure == 1U)
|
|
{
|
|
card->blockSize = FSL_SDMMC_DEFAULT_BLOCK_SIZE;
|
|
|
|
csd->deviceSize = (uint32_t)((rawCsd[2U] & 0x3FU) << 16U);
|
|
csd->deviceSize |= (uint32_t)((rawCsd[1U] & 0xFFFF0000U) >> 16U);
|
|
if (csd->deviceSize >= 0xFFFFU)
|
|
{
|
|
card->flags |= (uint32_t)kSD_SupportSdxcFlag;
|
|
}
|
|
|
|
card->blockCount = ((csd->deviceSize + 1U) * 1024U);
|
|
}
|
|
else
|
|
{
|
|
/* not support csd version */
|
|
}
|
|
|
|
if ((uint8_t)((rawCsd[1U] & 0x4000U) >> 14U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdEraseBlockEnabledFlag;
|
|
}
|
|
csd->eraseSectorSize = (uint8_t)((rawCsd[1U] & 0x3F80U) >> 7U);
|
|
csd->writeProtectGroupSize = (uint8_t)(rawCsd[1U] & 0x7FU);
|
|
if ((uint8_t)(rawCsd[0U] & 0x80000000U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdWriteProtectGroupEnabledFlag;
|
|
}
|
|
csd->writeSpeedFactor = (uint8_t)((rawCsd[0U] & 0x1C000000U) >> 26U);
|
|
csd->writeBlockLength = (uint8_t)((rawCsd[0U] & 0x3C00000U) >> 22U);
|
|
if ((uint8_t)((rawCsd[0U] & 0x200000U) >> 21U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdWriteBlockPartialFlag;
|
|
}
|
|
if ((uint8_t)((rawCsd[0U] & 0x8000U) >> 15U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdFileFormatGroupFlag;
|
|
}
|
|
if ((uint8_t)((rawCsd[0U] & 0x4000U) >> 14U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdCopyFlag;
|
|
}
|
|
if ((uint8_t)((rawCsd[0U] & 0x2000U) >> 13U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdPermanentWriteProtectFlag;
|
|
}
|
|
if ((uint8_t)((rawCsd[0U] & 0x1000U) >> 12U) != 0U)
|
|
{
|
|
csd->flags |= (uint16_t)kSD_CsdTemporaryWriteProtectFlag;
|
|
}
|
|
csd->fileFormat = (uint8_t)((rawCsd[0U] & 0xC00U) >> 10U);
|
|
}
|
|
|
|
static status_t SD_SendCsd(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSDMMC_SendCsd;
|
|
command.argument = (card->relativeAddress << 16U);
|
|
command.responseType = kCARD_ResponseTypeR2;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success == error)
|
|
{
|
|
(void)memcpy(card->internalBuffer, (uint8_t *)command.response, 16U);
|
|
/* The response is from bit 127:8 in R2, corrisponding to command.response[3U]:command.response[0U][31U:8]. */
|
|
SD_DecodeCsd(card, (uint32_t *)(uint32_t)card->internalBuffer);
|
|
}
|
|
else
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
SDMMC_LOG("\r\nError: send CMD9(get csd) failed with host error %d, response %x\r\n", error,
|
|
command.response[0U]);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void SD_DecodeCid(sd_card_t *card, uint32_t *rawCid)
|
|
{
|
|
assert(card != NULL);
|
|
assert(rawCid != NULL);
|
|
|
|
sd_cid_t *cid;
|
|
|
|
cid = &(card->cid);
|
|
cid->manufacturerID = (uint8_t)((rawCid[3U] & 0xFF000000U) >> 24U);
|
|
cid->applicationID = (uint16_t)((rawCid[3U] & 0xFFFF00U) >> 8U);
|
|
|
|
cid->productName[0U] = (uint8_t)((rawCid[3U] & 0xFFU));
|
|
cid->productName[1U] = (uint8_t)((rawCid[2U] & 0xFF000000U) >> 24U);
|
|
cid->productName[2U] = (uint8_t)((rawCid[2U] & 0xFF0000U) >> 16U);
|
|
cid->productName[3U] = (uint8_t)((rawCid[2U] & 0xFF00U) >> 8U);
|
|
cid->productName[4U] = (uint8_t)((rawCid[2U] & 0xFFU));
|
|
|
|
cid->productVersion = (uint8_t)((rawCid[1U] & 0xFF000000U) >> 24U);
|
|
|
|
cid->productSerialNumber = (uint32_t)((rawCid[1U] & 0xFFFFFFU) << 8U);
|
|
cid->productSerialNumber |= (uint32_t)((rawCid[0U] & 0xFF000000U) >> 24U);
|
|
|
|
cid->manufacturerData = (uint16_t)((rawCid[0U] & 0xFFF00U) >> 8U);
|
|
}
|
|
|
|
static status_t SD_AllSendCid(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_SDMMC_TransferFailed;
|
|
|
|
command.index = (uint32_t)kSDMMC_AllSendCid;
|
|
command.argument = 0U;
|
|
command.responseType = kCARD_ResponseTypeR2;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success == error)
|
|
{
|
|
(void)memcpy(card->internalBuffer, (uint8_t *)command.response, 16U);
|
|
SD_DecodeCid(card, (uint32_t *)(uint32_t)card->internalBuffer);
|
|
|
|
error = kStatus_Success;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_ApplicationSendOperationCondition(sd_card_t *card, uint32_t argument)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Fail;
|
|
uint32_t i = FSL_SDMMC_MAX_VOLTAGE_RETRIES;
|
|
|
|
command.index = (uint32_t)kSD_ApplicationSendOperationCondition;
|
|
command.argument = argument;
|
|
command.responseType = kCARD_ResponseTypeR3;
|
|
|
|
while (0U != i--)
|
|
{
|
|
if (kStatus_Success != SD_SendApplicationCmd(card, 0U))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send ACMD41 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* Wait until card exit busy state. */
|
|
if ((command.response[0U] & SDMMC_MASK(kSD_OcrPowerUpBusyFlag)) != 0U)
|
|
{
|
|
/* high capacity check */
|
|
if ((command.response[0U] & SDMMC_MASK(kSD_OcrCardCapacitySupportFlag)) != 0U)
|
|
{
|
|
card->flags |= (uint32_t)kSD_SupportHighCapacityFlag;
|
|
}
|
|
/* 1.8V support */
|
|
if ((command.response[0U] & SDMMC_MASK(kSD_OcrSwitch18AcceptFlag)) != 0U)
|
|
{
|
|
card->flags |= (uint32_t)kSD_SupportVoltage180v;
|
|
}
|
|
card->ocr = command.response[0U];
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
SDMMC_OSADelay(10U);
|
|
}
|
|
|
|
SDMMC_LOG("\r\nError: send ACMD41 timeout\r\n");
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SendInterfaceCondition(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
uint32_t i = FSL_SDMMC_MAX_CMD_RETRIES;
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSD_SendInterfaceCondition;
|
|
command.argument = 0x1AAU;
|
|
command.responseType = kCARD_ResponseTypeR7;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
do
|
|
{
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD8 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
}
|
|
else
|
|
{
|
|
if ((command.response[0U] & 0xFFU) != 0xAAU)
|
|
{
|
|
error = kStatus_SDMMC_CardNotSupport;
|
|
SDMMC_LOG("\r\nError: card not support CMD8\r\n");
|
|
}
|
|
else
|
|
{
|
|
error = kStatus_Success;
|
|
}
|
|
}
|
|
} while ((--i != 0U) && (error != kStatus_Success));
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_SelectBusTiming(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error = kStatus_SDMMC_SwitchBusTimingFailed;
|
|
|
|
if (card->operationVoltage != kSDMMC_OperationVoltage180V)
|
|
{
|
|
/* group 1, function 1 ->high speed mode*/
|
|
error = SD_SelectFunction(card, kSD_GroupTimingMode, kSD_FunctionSDR25HighSpeed);
|
|
/* If the result isn't "switching to high speed mode(50MHZ) successfully or card doesn't support high speed
|
|
* mode". Return failed status. */
|
|
if (error == kStatus_Success)
|
|
{
|
|
card->currentTiming = kSD_TimingSDR25HighSpeedMode;
|
|
card->busClock_Hz =
|
|
SDMMCHOST_SetCardClock(card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_50MHZ));
|
|
}
|
|
else
|
|
{
|
|
if (error == kStatus_SDMMC_NotSupportYet)
|
|
{
|
|
/* if not support high speed, keep the card work at default mode */
|
|
SDMMC_LOG("\r\nNote: High speed mode is not supported by card\r\n");
|
|
return kStatus_Success;
|
|
}
|
|
}
|
|
}
|
|
/* card is in UHS_I mode */
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (card->currentTiming == kSD_TimingSDR12DefaultMode)
|
|
{
|
|
/* if timing not specified, probe card capability from SDR104 mode */
|
|
card->currentTiming = kSD_TimingSDR104Mode;
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingSDR104Mode)
|
|
{
|
|
if ((card->host->capability & (uint32_t)kSDMMCHOST_SupportSDR104) != 0U)
|
|
{
|
|
error = SD_SelectFunction(card, kSD_GroupTimingMode, kSD_FunctionSDR104);
|
|
if (error == kStatus_Success)
|
|
{
|
|
card->currentTiming = kSD_TimingSDR104Mode;
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(
|
|
card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_208MHZ));
|
|
break;
|
|
}
|
|
}
|
|
SDMMC_LOG("\r\nNote: SDR104 mode is not supported\r\n");
|
|
card->currentTiming = kSD_TimingDDR50Mode;
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingDDR50Mode)
|
|
{
|
|
error = SD_SelectFunction(card, kSD_GroupTimingMode, kSD_FunctionDDR50);
|
|
if (error == kStatus_Success)
|
|
{
|
|
card->currentTiming = kSD_TimingDDR50Mode;
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(
|
|
card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_50MHZ));
|
|
SDMMCHOST_EnableDDRMode(card->host, true, 0U);
|
|
break;
|
|
}
|
|
SDMMC_LOG("\r\nNote: DDR50 mode is not supported\r\n");
|
|
card->currentTiming = kSD_TimingSDR50Mode;
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingSDR50Mode)
|
|
{
|
|
if ((card->host->capability & (uint32_t)kSDMMCHOST_SupportSDR50) != 0U)
|
|
{
|
|
error = SD_SelectFunction(card, kSD_GroupTimingMode, kSD_FunctionSDR50);
|
|
if (error == kStatus_Success)
|
|
{
|
|
card->currentTiming = kSD_TimingSDR50Mode;
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(
|
|
card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_100MHZ));
|
|
break;
|
|
}
|
|
}
|
|
SDMMC_LOG("\r\nNote: SDR50 mode is not supported\r\n");
|
|
card->currentTiming = kSD_TimingSDR25HighSpeedMode;
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingSDR25HighSpeedMode)
|
|
{
|
|
error = SD_SelectFunction(card, kSD_GroupTimingMode, kSD_FunctionSDR25HighSpeed);
|
|
if (error == kStatus_Success)
|
|
{
|
|
card->currentTiming = kSD_TimingSDR25HighSpeedMode;
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(
|
|
card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_50MHZ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDMMC_LOG("\r\nWarning: unknown timing mode\r\n");
|
|
} while (false);
|
|
}
|
|
|
|
if (error == kStatus_Success)
|
|
{
|
|
/* Update io strength according to different bus frequency */
|
|
if (card->usrParam.ioStrength != NULL)
|
|
{
|
|
card->usrParam.ioStrength(card->busClock_Hz);
|
|
}
|
|
|
|
/* SDR50 and SDR104 mode need tuning */
|
|
if ((card->currentTiming == kSD_TimingSDR50Mode) || (card->currentTiming == kSD_TimingSDR104Mode))
|
|
{
|
|
/* execute tuning */
|
|
if (SD_ExecuteTuning(card) != kStatus_Success)
|
|
{
|
|
SDMMC_LOG("\r\nError: tuning failed for mode %d\r\n", card->currentTiming);
|
|
return kStatus_SDMMC_TuningFail;
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void SD_DecodeStatus(sd_card_t *card, uint32_t *src)
|
|
{
|
|
assert(card != NULL);
|
|
assert(src != NULL);
|
|
|
|
card->stat.busWidth = (uint8_t)((src[0U] & 0xC0000000U) >> 30U); /* 511-510 */
|
|
card->stat.secureMode = (uint8_t)((src[0U] & 0x20000000U) >> 29U); /* 509 */
|
|
card->stat.cardType = (uint16_t)((src[0U] & 0x0000FFFFU)); /* 495-480 */
|
|
card->stat.protectedSize = src[1U]; /* 479-448 */
|
|
card->stat.speedClass = (uint8_t)((src[2U] & 0xFF000000U) >> 24U); /* 447-440 */
|
|
card->stat.performanceMove = (uint8_t)((src[2U] & 0x00FF0000U) >> 16U); /* 439-432 */
|
|
card->stat.auSize = (uint8_t)((src[2U] & 0x0000F000U) >> 12U); /* 431-428 */
|
|
card->stat.eraseSize = (uint16_t)(((src[2U] & 0x000000FFU) << 8U) | ((src[3U] & 0xFF000000U) >> 24U)); /* 423-408 */
|
|
card->stat.eraseTimeout = (((uint8_t)((src[3U] & 0x00FF0000U) >> 16U)) & 0xFCU) >> 2U; /* 407-402 */
|
|
card->stat.eraseOffset = ((uint8_t)((src[3U] & 0x00FF0000U) >> 16U)) & 0x3U; /* 401-400 */
|
|
card->stat.uhsSpeedGrade = (((uint8_t)((src[3U] & 0x0000FF00U) >> 8U)) & 0xF0U) >> 4U; /* 399-396 */
|
|
card->stat.uhsAuSize = ((uint8_t)((src[3U] & 0x0000FF00U) >> 8U)) & 0xFU; /* 395-392 */
|
|
}
|
|
|
|
status_t SD_ReadStatus(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
sdmmchost_data_t data = {0};
|
|
status_t error = kStatus_Success;
|
|
uint32_t *rawPointer = (uint32_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
|
|
(void)memset(rawPointer, 0, 64U);
|
|
|
|
/* wait card status ready. */
|
|
error = SD_PollingCardStatusBusy(card, SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT);
|
|
if (kStatus_SDMMC_CardStatusIdle != error)
|
|
{
|
|
return kStatus_SDMMC_WaitWriteCompleteFailed;
|
|
}
|
|
|
|
if (kStatus_Success != SD_SendApplicationCmd(card, card->relativeAddress))
|
|
{
|
|
return kStatus_SDMMC_SendApplicationCommandFailed;
|
|
}
|
|
|
|
command.index = (uint32_t)kSDMMC_SendStatus;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
|
|
data.blockSize = 64U;
|
|
data.blockCount = 1U;
|
|
data.rxData = rawPointer;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
error = SD_Transfer(card, &content, 3U);
|
|
if ((kStatus_Success != error) || (((command.response[0U]) & SDMMC_R1_ALL_ERROR_FLAG) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\nError: send ACMD13 failed with host error %d, response %x\r\n", error, command.response[0U]);
|
|
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* switch to little endian sequence */
|
|
SDMMCHOST_ConvertDataToLittleEndian(card->host, rawPointer, 16U, kSDMMC_DataPacketFormatMSBFirst);
|
|
SD_DecodeStatus(card, rawPointer);
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SD_SelectCard(sd_card_t *card, bool isSelected)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_SelectCard(card->host, card->relativeAddress, isSelected);
|
|
}
|
|
|
|
status_t SD_SetDriverStrength(sd_card_t *card, sd_driver_strength_t driverStrength)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error;
|
|
uint32_t strength = (uint32_t)driverStrength;
|
|
|
|
error = SD_SelectFunction(card, kSD_GroupDriverStrength, strength);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SD_SetMaxCurrent(sd_card_t *card, sd_max_current_t maxCurrent)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error;
|
|
uint32_t current = (uint32_t)maxCurrent;
|
|
|
|
error = SD_SelectFunction(card, kSD_GroupCurrentLimit, current);
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_Read(sd_card_t *card, uint8_t *buffer, uint32_t startBlock, uint32_t blockSize, uint32_t blockCount)
|
|
{
|
|
assert(card != NULL);
|
|
assert(buffer != NULL);
|
|
assert(blockCount != 0U);
|
|
assert(blockSize == FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
status_t error = kStatus_Success;
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
sdmmchost_data_t data = {0};
|
|
|
|
if ((((card->flags & (uint32_t)kSD_SupportHighCapacityFlag) != 0U) && (blockSize != 512U)) ||
|
|
(blockSize > card->blockSize) || (blockSize > card->host->maxBlockSize) || ((blockSize % 4U) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\nError: read with parameter, block size %d is not support\r\n", blockSize);
|
|
return kStatus_SDMMC_CardNotSupport;
|
|
}
|
|
|
|
/* read command are not allowed while card is programming */
|
|
error = SD_PollingCardStatusBusy(card, SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT);
|
|
if (kStatus_SDMMC_CardStatusIdle != error)
|
|
{
|
|
SDMMC_LOG("Error : read failed with wrong card busy\r\n");
|
|
return kStatus_SDMMC_PollingCardIdleFailed;
|
|
}
|
|
|
|
data.blockSize = blockSize;
|
|
data.blockCount = blockCount;
|
|
data.rxData = (uint32_t *)(uint32_t)buffer;
|
|
data.enableAutoCommand12 = true;
|
|
|
|
command.index = (blockCount == 1U) ? (uint32_t)kSDMMC_ReadSingleBlock : (uint32_t)kSDMMC_ReadMultipleBlock;
|
|
command.argument = startBlock;
|
|
if (0U == (card->flags & (uint32_t)kSD_SupportHighCapacityFlag))
|
|
{
|
|
command.argument *= data.blockSize;
|
|
}
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
command.responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
|
|
error = SD_Transfer(card, &content, 3U);
|
|
if (error != kStatus_Success)
|
|
{
|
|
return error;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SD_Write(sd_card_t *card,
|
|
const uint8_t *buffer,
|
|
uint32_t startBlock,
|
|
uint32_t blockSize,
|
|
uint32_t blockCount,
|
|
uint32_t *writtenBlocks)
|
|
{
|
|
assert(card != NULL);
|
|
assert(buffer != NULL);
|
|
assert(blockCount != 0U);
|
|
assert(blockSize == FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
sdmmchost_data_t data = {0};
|
|
status_t error;
|
|
|
|
if ((((card->flags & (uint32_t)kSD_SupportHighCapacityFlag) != 0U) && (blockSize != 512U)) ||
|
|
(blockSize > card->blockSize) || (blockSize > card->host->maxBlockSize) || ((blockSize % 4U) != 0U))
|
|
{
|
|
SDMMC_LOG("\r\nError: write with parameter, block size %d is not support\r\n", blockSize);
|
|
return kStatus_SDMMC_CardNotSupport;
|
|
}
|
|
|
|
/* polling card status idle */
|
|
error = SD_PollingCardStatusBusy(card, SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT);
|
|
if (kStatus_SDMMC_CardStatusIdle != error)
|
|
{
|
|
SDMMC_LOG("Error : write failed, card status busy\r\n");
|
|
return kStatus_SDMMC_PollingCardIdleFailed;
|
|
}
|
|
|
|
data.enableAutoCommand12 = true;
|
|
data.blockSize = blockSize;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
command.responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG;
|
|
|
|
command.index = (blockCount == 1U) ? (uint32_t)kSDMMC_WriteSingleBlock : (uint32_t)kSDMMC_WriteMultipleBlock;
|
|
command.argument = startBlock;
|
|
if (0U == (card->flags & (uint32_t)kSD_SupportHighCapacityFlag))
|
|
{
|
|
command.argument *= data.blockSize;
|
|
}
|
|
|
|
*writtenBlocks = blockCount;
|
|
data.blockCount = blockCount;
|
|
data.txData = (const uint32_t *)(uint32_t)buffer;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
|
|
error = SD_Transfer(card, &content, 3U);
|
|
if (error != kStatus_Success)
|
|
{
|
|
error = SD_SendWriteSuccessBlocks(card, writtenBlocks);
|
|
/* check the successfully written block */
|
|
if (error == kStatus_Success)
|
|
{
|
|
if (*writtenBlocks != 0U)
|
|
{
|
|
/* written success, but not all the blocks are written */
|
|
error = kStatus_Success;
|
|
}
|
|
}
|
|
SDMMC_LOG("\r\nWarning: write failed with block count %d, successed %d\r\n", blockCount, *writtenBlocks);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_Erase(sd_card_t *card, uint32_t startBlock, uint32_t blockCount, uint32_t timeout)
|
|
{
|
|
assert(card != NULL);
|
|
assert(blockCount != 0U);
|
|
assert(timeout != 0U);
|
|
|
|
uint32_t eraseBlockStart;
|
|
uint32_t eraseBlockEnd;
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
/* polling card status idle */
|
|
error = SD_PollingCardStatusBusy(card, SD_CARD_ACCESS_WAIT_IDLE_TIMEOUT);
|
|
if (kStatus_SDMMC_CardStatusIdle != error)
|
|
{
|
|
SDMMC_LOG("Error : write failed, card status busy\r\n");
|
|
return kStatus_SDMMC_PollingCardIdleFailed;
|
|
}
|
|
|
|
eraseBlockStart = startBlock;
|
|
eraseBlockEnd = eraseBlockStart + blockCount - 1U;
|
|
if (0U == (card->flags & (uint32_t)kSD_SupportHighCapacityFlag))
|
|
{
|
|
eraseBlockStart = eraseBlockStart * FSL_SDMMC_DEFAULT_BLOCK_SIZE;
|
|
eraseBlockEnd = eraseBlockEnd * FSL_SDMMC_DEFAULT_BLOCK_SIZE;
|
|
}
|
|
|
|
/* Send ERASE_WRITE_BLOCK_START command to set the start block number to erase. */
|
|
command.index = (uint32_t)kSD_EraseWriteBlockStart;
|
|
command.argument = eraseBlockStart;
|
|
command.responseType = kCARD_ResponseTypeR1;
|
|
command.responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SD_Transfer(card, &content, 1U);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD32(erase start) failed with host error %d, response %x\r\n", error,
|
|
command.response[0U]);
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* Send ERASE_WRITE_BLOCK_END command to set the end block number to erase. */
|
|
command.index = (uint32_t)kSD_EraseWriteBlockEnd;
|
|
command.argument = eraseBlockEnd;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SD_Transfer(card, &content, 0U);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD33(erase end) failed with host error %d, response %x\r\n", error,
|
|
command.response[0U]);
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* Send ERASE command to start erase process. */
|
|
command.index = (uint32_t)kSDMMC_Erase;
|
|
command.argument = 0U;
|
|
command.responseType = kCARD_ResponseTypeR1b;
|
|
command.responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SD_Transfer(card, &content, 0U);
|
|
if (kStatus_Success != error)
|
|
{
|
|
SDMMC_LOG("\r\nError: send CMD38(erase) failed with host error %d, response %x\r\n", error,
|
|
command.response[0U]);
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
bool SD_CheckReadOnly(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return (((card->csd.flags & (uint16_t)kSD_CsdPermanentWriteProtectFlag) != 0U) ||
|
|
((card->csd.flags & (uint16_t)kSD_CsdTemporaryWriteProtectFlag)) != 0U);
|
|
}
|
|
|
|
status_t SD_ReadBlocks(sd_card_t *card, uint8_t *buffer, uint32_t startBlock, uint32_t blockCount)
|
|
{
|
|
assert(card != NULL);
|
|
assert(buffer != NULL);
|
|
assert(blockCount != 0U);
|
|
assert((blockCount + startBlock) <= card->blockCount);
|
|
|
|
uint32_t blockCountOneTime;
|
|
uint32_t blockLeft;
|
|
uint32_t blockDone = 0U;
|
|
uint8_t *nextBuffer = buffer;
|
|
bool dataAddrAlign = true;
|
|
uint8_t *alignBuffer = (uint8_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
blockLeft = blockCount;
|
|
|
|
while (blockLeft != 0U)
|
|
{
|
|
nextBuffer = (uint8_t *)((uint32_t)buffer + blockDone * FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
if ((!card->noInteralAlign) && (!dataAddrAlign || ((((uint32_t)nextBuffer) & (sizeof(uint32_t) - 1U)) != 0U)))
|
|
{
|
|
blockCountOneTime = 1;
|
|
(void)memset(alignBuffer, 0, FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
dataAddrAlign = false;
|
|
blockLeft -= blockCountOneTime;
|
|
}
|
|
else
|
|
{
|
|
if (blockLeft > card->host->maxBlockCount)
|
|
{
|
|
blockLeft = (blockLeft - card->host->maxBlockCount);
|
|
blockCountOneTime = card->host->maxBlockCount;
|
|
}
|
|
else
|
|
{
|
|
blockCountOneTime = blockLeft;
|
|
blockLeft = 0U;
|
|
}
|
|
}
|
|
|
|
error = SD_Read(card, dataAddrAlign ? nextBuffer : alignBuffer, (startBlock + blockDone),
|
|
FSL_SDMMC_DEFAULT_BLOCK_SIZE, blockCountOneTime);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
break;
|
|
}
|
|
|
|
blockDone += blockCountOneTime;
|
|
|
|
if (!card->noInteralAlign && (!dataAddrAlign))
|
|
{
|
|
(void)memcpy(nextBuffer, alignBuffer, FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
}
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SD_WriteBlocks(sd_card_t *card, const uint8_t *buffer, uint32_t startBlock, uint32_t blockCount)
|
|
{
|
|
assert(card != NULL);
|
|
assert(buffer != NULL);
|
|
assert(blockCount != 0U);
|
|
assert((blockCount + startBlock) <= card->blockCount);
|
|
|
|
uint32_t blockCountOneTime = 0U; /* The block count can be wrote in one time sending WRITE_BLOCKS command. */
|
|
uint32_t blockWrittenOneTime = 0U;
|
|
uint32_t blockLeft = 0U; /* Left block count to be wrote. */
|
|
const uint8_t *nextBuffer;
|
|
bool dataAddrAlign = true;
|
|
uint8_t *alignBuffer = (uint8_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
blockLeft = blockCount;
|
|
while (blockLeft != 0U)
|
|
{
|
|
nextBuffer = (uint8_t *)((uint32_t)buffer + (blockCount - blockLeft) * FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
if (!card->noInteralAlign && (!dataAddrAlign || ((((uint32_t)nextBuffer) & (sizeof(uint32_t) - 1U)) != 0U)))
|
|
{
|
|
blockCountOneTime = 1;
|
|
(void)memcpy(alignBuffer, nextBuffer, FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
dataAddrAlign = false;
|
|
}
|
|
else
|
|
{
|
|
if (blockLeft > card->host->maxBlockCount)
|
|
{
|
|
blockCountOneTime = card->host->maxBlockCount;
|
|
}
|
|
else
|
|
{
|
|
blockCountOneTime = blockLeft;
|
|
}
|
|
}
|
|
|
|
error = SD_Write(card, dataAddrAlign ? nextBuffer : alignBuffer, (startBlock + blockCount - blockLeft),
|
|
FSL_SDMMC_DEFAULT_BLOCK_SIZE, blockCountOneTime, &blockWrittenOneTime);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
break;
|
|
}
|
|
|
|
blockLeft -= blockWrittenOneTime;
|
|
|
|
if ((!card->noInteralAlign) && !dataAddrAlign)
|
|
{
|
|
(void)memset(alignBuffer, 0, FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
}
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
static uint32_t SD_CalculateEraseTimeout(sd_card_t *card, uint32_t blockCount, uint32_t auSize)
|
|
{
|
|
uint32_t auCount = blockCount / (auSize / FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
uint32_t timeout_ms = 0U;
|
|
|
|
if (auCount == 0U)
|
|
{
|
|
auCount = 1U;
|
|
}
|
|
|
|
timeout_ms = auCount * 250U; /* 250 ms erase timeout per AU by default */
|
|
|
|
if ((card->stat.eraseTimeout != 0U) && (card->stat.eraseSize != 0U))
|
|
{
|
|
/* timeout determined by the block count to be erased and the au size */
|
|
timeout_ms = auCount * (((uint32_t)card->stat.eraseTimeout * 1000U) / (uint32_t)card->stat.eraseSize + 500U) +
|
|
card->stat.eraseOffset * 1000U;
|
|
}
|
|
|
|
/* convert to ms */
|
|
return timeout_ms < 1000U ? 1000U : timeout_ms;
|
|
}
|
|
|
|
status_t SD_EraseBlocks(sd_card_t *card, uint32_t startBlock, uint32_t blockCount)
|
|
{
|
|
assert(card != NULL);
|
|
assert(blockCount != 0U);
|
|
assert((blockCount + startBlock) <= card->blockCount);
|
|
|
|
uint32_t blockCountOneTime; /* The block count can be erased in one time sending ERASE_BLOCKS command. */
|
|
uint32_t blockDone = 0U; /* The block count has been erased. */
|
|
uint32_t blockLeft; /* Left block count to be erase. */
|
|
status_t error = kStatus_Success;
|
|
uint32_t onetimeMaxEraseBlocks = 0U;
|
|
uint32_t auSize = 0U;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
/* sdsc card erasable sector is determined by CSD register */
|
|
if (card->csd.csdStructure == 0U)
|
|
{
|
|
onetimeMaxEraseBlocks = card->csd.eraseSectorSize + 1UL;
|
|
}
|
|
else
|
|
{
|
|
/* limit one time maximum erase size to 1 AU */
|
|
if ((card->stat.auSize >= SD_AU_START_VALUE))
|
|
{
|
|
/* UHS card should use uhs au size field */
|
|
if (card->operationVoltage == kSDMMC_OperationVoltage180V)
|
|
{
|
|
auSize = s_sdAuSizeMap[card->stat.uhsAuSize == 0U ? card->stat.auSize : card->stat.uhsAuSize];
|
|
onetimeMaxEraseBlocks = auSize / FSL_SDMMC_DEFAULT_BLOCK_SIZE;
|
|
}
|
|
else
|
|
{
|
|
auSize = s_sdAuSizeMap[card->stat.auSize];
|
|
onetimeMaxEraseBlocks = auSize / FSL_SDMMC_DEFAULT_BLOCK_SIZE;
|
|
}
|
|
|
|
if (card->stat.eraseSize != 0U)
|
|
{
|
|
onetimeMaxEraseBlocks *= card->stat.eraseSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (onetimeMaxEraseBlocks == 0U)
|
|
{
|
|
SDMMC_LOG(
|
|
"Warning: AU size in sd descriptor is not set properly, please check if SD_ReadStatus is called before\
|
|
SD_EraseBlocks\r\n");
|
|
error = kStatus_SDMMC_AuSizeNotSetProperly;
|
|
}
|
|
else
|
|
{
|
|
blockLeft = blockCount;
|
|
while (blockLeft != 0U)
|
|
{
|
|
if (blockLeft > onetimeMaxEraseBlocks)
|
|
{
|
|
blockCountOneTime = onetimeMaxEraseBlocks;
|
|
blockLeft = blockLeft - blockCountOneTime;
|
|
}
|
|
else
|
|
{
|
|
blockCountOneTime = blockLeft;
|
|
blockLeft = 0U;
|
|
}
|
|
|
|
error = SD_Erase(card, (startBlock + blockDone), blockCountOneTime,
|
|
SD_CalculateEraseTimeout(card, blockCountOneTime, auSize));
|
|
if (error != kStatus_Success)
|
|
{
|
|
break;
|
|
}
|
|
|
|
blockDone += blockCountOneTime;
|
|
}
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SD_ProbeBusVoltage(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t applicationCommand41Argument = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* 3.3V voltage should be supported as default */
|
|
applicationCommand41Argument |=
|
|
SDMMC_MASK(kSD_OcrVdd29_30Flag) | SDMMC_MASK(kSD_OcrVdd32_33Flag) | SDMMC_MASK(kSD_OcrVdd33_34Flag);
|
|
|
|
if ((card->usrParam.ioVoltage != NULL) && (card->usrParam.ioVoltage->type != kSD_IOVoltageCtrlNotSupport) &&
|
|
((card->host->capability & (uint32_t)kSDMMCHOST_SupportVoltage1v8) != 0U) &&
|
|
((card->host->capability & ((uint32_t)kSDMMCHOST_SupportSDR104 | (uint32_t)kSDMMCHOST_SupportSDR50 |
|
|
(uint32_t)kSDMMCHOST_SupportDDRMode)) != 0U))
|
|
{
|
|
/* allow user select the work voltage, if not select, sdmmc will handle it automatically */
|
|
applicationCommand41Argument |= SDMMC_MASK(kSD_OcrSwitch18RequestFlag);
|
|
/* reset to 3v3 signal voltage */
|
|
if (SD_SwitchIOVoltage(card, kSDMMC_OperationVoltage330V) == kStatus_Success)
|
|
{
|
|
/* Host changed the operation signal voltage successfully, then card need power reset */
|
|
SD_SetCardPower(card, false);
|
|
SD_SetCardPower(card, true);
|
|
}
|
|
}
|
|
|
|
card->operationVoltage = kSDMMC_OperationVoltage330V;
|
|
|
|
/* send card active */
|
|
SDMMCHOST_SendCardActive(card->host);
|
|
|
|
do
|
|
{
|
|
/* card go idle */
|
|
if (kStatus_Success != SD_GoIdle(card))
|
|
{
|
|
error = kStatus_SDMMC_GoIdleFailed;
|
|
break;
|
|
}
|
|
|
|
error = SD_SendInterfaceCondition(card);
|
|
/* Check card's supported interface condition. */
|
|
if (kStatus_Success == error)
|
|
{
|
|
/* SDHC or SDXC card */
|
|
applicationCommand41Argument |= SDMMC_MASK(kSD_OcrHostCapacitySupportFlag);
|
|
card->flags |= (uint32_t)kSD_SupportSdhcFlag;
|
|
}
|
|
else
|
|
{
|
|
/* SDSC card */
|
|
if (kStatus_Success != SD_GoIdle(card))
|
|
{
|
|
error = kStatus_SDMMC_GoIdleFailed;
|
|
break;
|
|
}
|
|
}
|
|
error = SD_ApplicationSendOperationCondition(card, applicationCommand41Argument);
|
|
/* Set card interface condition according to SDHC capability and card's supported interface condition. */
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_HandShakeOperationConditionFailed;
|
|
break;
|
|
}
|
|
|
|
/* check if card support 1.8V */
|
|
if ((card->flags & (uint32_t)kSD_SupportVoltage180v) != 0U)
|
|
{
|
|
if ((card->usrParam.ioVoltage != NULL) && (card->usrParam.ioVoltage->type == kSD_IOVoltageCtrlNotSupport))
|
|
{
|
|
break;
|
|
}
|
|
|
|
error = SD_SwitchVoltage(card, kSDMMC_OperationVoltage180V);
|
|
if (kStatus_SDMMC_SwitchVoltageFail == error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (error == kStatus_SDMMC_SwitchVoltage18VFail33VSuccess)
|
|
{
|
|
applicationCommand41Argument &= ~SDMMC_MASK(kSD_OcrSwitch18RequestFlag);
|
|
card->flags &= ~(uint32_t)kSD_SupportVoltage180v;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
card->operationVoltage = kSDMMC_OperationVoltage180V;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} while (true);
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t sdcard_init(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
assert(card->isHostReady == true);
|
|
status_t error = kStatus_Success;
|
|
|
|
/* reset variables */
|
|
card->flags = 0U;
|
|
/* set DATA bus width */
|
|
SDMMCHOST_SetCardBusWidth(card->host, kSDMMC_BusWdith1Bit);
|
|
/*set card freq to 400KHZ*/
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(card->host, SDMMC_CLOCK_400KHZ);
|
|
|
|
error = SD_ProbeBusVoltage(card);
|
|
/* probe bus voltage*/
|
|
if (error != kStatus_Success)
|
|
{
|
|
return kStatus_SDMMC_SwitchVoltageFail;
|
|
}
|
|
|
|
error = SD_AllSendCid(card);
|
|
/* Initialize card if the card is SD card. */
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_AllSendCidFailed;
|
|
}
|
|
|
|
error = SD_SendRca(card);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SendRelativeAddressFailed;
|
|
}
|
|
|
|
error = SD_SendCsd(card);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SendCsdFailed;
|
|
}
|
|
|
|
error = SD_SelectCard(card, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SelectCardFailed;
|
|
}
|
|
|
|
/* Set to max frequency in non-high speed mode. */
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(card->host, SD_CLOCK_25MHZ);
|
|
|
|
error = SD_SendScr(card);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SendScrFailed;
|
|
}
|
|
|
|
/* Set to 4-bit data bus mode. */
|
|
if ((card->flags & (uint32_t)kSD_Support4BitWidthFlag) != 0U)
|
|
{
|
|
error = SD_SetDataBusWidth(card, kSDMMC_BusWdith4Bit);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SetDataBusWidthFailed;
|
|
}
|
|
SDMMCHOST_SetCardBusWidth(card->host, kSDMMC_BusWdith4Bit);
|
|
}
|
|
|
|
/* try to get card current status */
|
|
error = SD_ReadStatus(card);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SendScrFailed;
|
|
}
|
|
|
|
error = SD_SetBlockSize(card, FSL_SDMMC_DEFAULT_BLOCK_SIZE);
|
|
/* set block size */
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SetCardBlockSizeFailed;
|
|
}
|
|
|
|
error = SD_SelectBusTiming(card);
|
|
/* select bus timing */
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SwitchBusTimingFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SD_CardInit(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error = kStatus_Success;
|
|
/* create mutex lock */
|
|
(void)SDMMC_OSAMutexCreate(&card->lock);
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
SD_SetCardPower(card, true);
|
|
|
|
error = sdcard_init(card);
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
void SD_CardDeinit(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
(void)SD_SelectCard(card, false);
|
|
SD_HostDoReset(card);
|
|
SD_SetCardPower(card, false);
|
|
(void)SDMMC_OSAMutexDestroy(&card->lock);
|
|
}
|
|
|
|
status_t SD_HostInit(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
assert(card->usrParam.cd != NULL);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
if (!card->isHostReady)
|
|
{
|
|
error = SDMMCHOST_Init(card->host);
|
|
if (error != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
if ((card->usrParam.cd->type == kSD_DetectCardByHostCD) || (card->usrParam.cd->type == kSD_DetectCardByHostDATA3))
|
|
{
|
|
(void)SDMMCHOST_CardDetectInit(card->host, card->usrParam.cd);
|
|
}
|
|
|
|
/* set the host status flag, after the card re-plug in, don't need init host again */
|
|
card->isHostReady = true;
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
void SD_HostDeinit(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
SDMMCHOST_Deinit(card->host);
|
|
/* should re-init host */
|
|
card->isHostReady = false;
|
|
}
|
|
|
|
void SD_SetCardPower(sd_card_t *card, bool enable)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t powerDelay = 0U;
|
|
|
|
if (card->usrParam.pwr != NULL)
|
|
{
|
|
card->usrParam.pwr(enable);
|
|
}
|
|
else
|
|
{
|
|
SDMMCHOST_SetCardPower(card->host, enable);
|
|
}
|
|
|
|
if (enable)
|
|
{
|
|
powerDelay = card->usrParam.powerOnDelayMS == 0U ? SD_POWER_ON_DELAY : card->usrParam.powerOnDelayMS;
|
|
}
|
|
else
|
|
{
|
|
powerDelay = card->usrParam.powerOffDelayMS == 0U ? SD_POWER_OFF_DELAY : card->usrParam.powerOffDelayMS;
|
|
}
|
|
|
|
SDMMC_OSADelay(powerDelay);
|
|
}
|
|
|
|
bool SD_IsCardPresent(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
assert(card->usrParam.cd != NULL);
|
|
|
|
if (card->usrParam.cd->type == kSD_DetectCardByGpioCD)
|
|
{
|
|
if (card->usrParam.cd->cardDetected == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
return card->usrParam.cd->cardDetected();
|
|
}
|
|
else
|
|
{
|
|
if (card->isHostReady == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (SDMMCHOST_CardDetectStatus(card->host) == (uint32_t)kSD_Removed)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
status_t SD_PollingCardInsert(sd_card_t *card, uint32_t status)
|
|
{
|
|
assert(card != NULL);
|
|
assert(card->usrParam.cd != NULL);
|
|
|
|
if (card->usrParam.cd->type == kSD_DetectCardByGpioCD)
|
|
{
|
|
if (card->usrParam.cd->cardDetected == NULL)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
do
|
|
{
|
|
if ((card->usrParam.cd->cardDetected() == true) && (status == (uint32_t)kSD_Inserted))
|
|
{
|
|
SDMMC_OSADelay(card->usrParam.cd->cdDebounce_ms);
|
|
if (card->usrParam.cd->cardDetected() == true)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((card->usrParam.cd->cardDetected() == false) && (status == (uint32_t)kSD_Removed))
|
|
{
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
else
|
|
{
|
|
if (card->isHostReady == false)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
if (SDMMCHOST_PollingCardDetectStatus(card->host, status, ~0U) != kStatus_Success)
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SD_Init(sd_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
if (!card->isHostReady)
|
|
{
|
|
error = SD_HostInit(card);
|
|
if (error != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_HostNotReady;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SD_HostDoReset(card);
|
|
}
|
|
|
|
if (kStatus_Success == error)
|
|
{
|
|
if (SD_PollingCardInsert(card, kSD_Inserted) != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_CardDetectFailed;
|
|
}
|
|
else
|
|
{
|
|
error = SD_CardInit(card);
|
|
if (error != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_CardInitFailed;
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void SD_Deinit(sd_card_t *card)
|
|
{
|
|
/* card deinitialize */
|
|
SD_CardDeinit(card);
|
|
/* host deinitialize */
|
|
SD_HostDeinit(card);
|
|
}
|
|
|
|
void SD_HostDoReset(sd_card_t *card)
|
|
{
|
|
SDMMCHOST_Reset(card->host);
|
|
}
|