2110 lines
65 KiB
C
2110 lines
65 KiB
C
/*
|
|
* Copyright (c) 2015, Freescale Semiconductor, Inc.
|
|
* Copyright 2016-2020 NXP
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "fsl_sdio.h"
|
|
|
|
/*******************************************************************************
|
|
* Definitions
|
|
******************************************************************************/
|
|
/*! @brief define the tuple number will be read during init */
|
|
#define SDIO_COMMON_CIS_TUPLE_NUM (3U)
|
|
/*! @brief SDIO retry times */
|
|
#define SDIO_RETRY_TIMES (1000U)
|
|
/*!@brief power reset delay */
|
|
/*!@brief power reset delay */
|
|
#define SDIO_POWER_ON_DELAY (400U)
|
|
#define SDIO_POWER_OFF_DELAY (100U)
|
|
/*******************************************************************************
|
|
* Prototypes
|
|
******************************************************************************/
|
|
/*!
|
|
* @brief probe bus voltage.
|
|
* @param card Card descriptor.
|
|
*/
|
|
static status_t SDIO_ProbeBusVoltage(sdio_card_t *card);
|
|
|
|
/*!
|
|
* @brief send card operation condition
|
|
* @param card Card descriptor.
|
|
* @param command argment
|
|
* @param accept1V8 flag indicate card acccpt 1v8 switch or not
|
|
* argument = 0U , means to get the operation condition
|
|
* argument !=0 , set the operation condition register
|
|
*/
|
|
static status_t SDIO_SendOperationCondition(sdio_card_t *card, uint32_t argument, uint32_t *accept1V8);
|
|
|
|
/*!
|
|
* @brief card Send relative address
|
|
* @param card Card descriptor.
|
|
*/
|
|
static status_t SDIO_SendRca(sdio_card_t *card);
|
|
|
|
/*!
|
|
* @brief card select card
|
|
* @param card Card descriptor.
|
|
* @param select/diselect flag
|
|
*/
|
|
static inline status_t SDIO_SelectCard(sdio_card_t *card, bool isSelected);
|
|
|
|
/*!
|
|
* @brief card go idle
|
|
* @param card Card descriptor.
|
|
*/
|
|
static inline status_t SDIO_GoIdle(sdio_card_t *card);
|
|
|
|
/*!
|
|
* @brief decode CIS
|
|
* @param card Card descriptor.
|
|
* @param func number
|
|
* @param data buffer pointer
|
|
* @param tuple code
|
|
* @param tuple link
|
|
*/
|
|
static status_t SDIO_DecodeCIS(
|
|
sdio_card_t *card, sdio_func_num_t func, uint8_t *dataBuffer, uint32_t tplCode, uint32_t tplLink);
|
|
|
|
/*!
|
|
* @brief switch to the maxium support bus width, depend on the host and card's capability.
|
|
* @param card Card descriptor.
|
|
*/
|
|
static status_t SDIO_SetMaxDataBusWidth(sdio_card_t *card);
|
|
|
|
/*!
|
|
* @brief sdio card excute tuning.
|
|
* @param card Card descriptor.
|
|
*/
|
|
static status_t SDIO_ExecuteTuning(sdio_card_t *card);
|
|
|
|
/*!
|
|
* @brief sdio io access direct
|
|
* @param card Card descriptor.
|
|
* @param direction access direction.
|
|
* @param func number
|
|
* @param regAddr register address.
|
|
* @param dataIn data to write
|
|
* @param dataOut data address for read
|
|
* @param rawFlag read after write flag, it is used for write access only.
|
|
*/
|
|
static status_t SDIO_IO_Access_Direct(sdio_card_t *card,
|
|
sdio_io_direction_t direction,
|
|
sdio_func_num_t func,
|
|
uint32_t regAddr,
|
|
uint8_t dataIn,
|
|
uint8_t *dataOut,
|
|
bool rawFlag);
|
|
/*******************************************************************************
|
|
* Variables
|
|
******************************************************************************/
|
|
/* define the tuple list */
|
|
static const uint32_t s_tupleList[SDIO_COMMON_CIS_TUPLE_NUM] = {
|
|
SDIO_TPL_CODE_MANIFID,
|
|
SDIO_TPL_CODE_FUNCID,
|
|
SDIO_TPL_CODE_FUNCE,
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
static inline status_t SDIO_SelectCard(sdio_card_t *card, bool isSelected)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_SelectCard(card->host, card->relativeAddress, isSelected);
|
|
}
|
|
|
|
static inline status_t SDIO_GoIdle(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_GoIdle(card->host);
|
|
}
|
|
|
|
static status_t SDIO_SwitchIOVoltage(sdio_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 SDIO_SwitchVoltage(sdio_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 (SDIO_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 */
|
|
SDIO_SetCardPower(card, false);
|
|
SDIO_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.");
|
|
}
|
|
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.");
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t SDIO_ExecuteTuning(sdio_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 SDIO_SendRca(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t i = FSL_SDMMC_MAX_CMD_RETRIES;
|
|
|
|
sdmmchost_transfer_t content = {0};
|
|
sdmmchost_cmd_t command = {0};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSDIO_SendRelativeAddress;
|
|
command.argument = 0U;
|
|
command.responseType = kCARD_ResponseTypeR6;
|
|
command.responseErrorFlags =
|
|
(uint32_t)kSDIO_StatusR6Error | (uint32_t)kSDIO_StatusIllegalCmd | (uint32_t)kSDIO_StatusCmdCRCError;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
|
|
while (--i != 0U)
|
|
{
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success == error)
|
|
{
|
|
/* check illegal state and cmd CRC error, may be the voltage or clock not stable, retry the cmd*/
|
|
if ((command.response[0U] & ((uint32_t)kSDIO_StatusIllegalCmd | (uint32_t)kSDIO_StatusCmdCRCError)) != 0U)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
card->relativeAddress = (command.response[0U] >> 16U);
|
|
|
|
return kStatus_Success;
|
|
}
|
|
}
|
|
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
status_t SDIO_CardInActive(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
return SDMMC_SetCardInactive(card->host);
|
|
}
|
|
|
|
static status_t SDIO_SendOperationCondition(sdio_card_t *card, uint32_t argument, uint32_t *accept1V8)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
sdmmchost_transfer_t content = {0U};
|
|
sdmmchost_cmd_t command = {0U};
|
|
uint32_t i = SDIO_RETRY_TIMES;
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSDIO_SendOperationCondition;
|
|
command.argument = argument;
|
|
command.responseType = kCARD_ResponseTypeR4;
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
|
|
while (--i != 0U)
|
|
{
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if ((kStatus_Success != error) || (command.response[0U] == 0U))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* if argument equal 0, then should check and save the info */
|
|
if ((argument != 0U) && ((command.response[0U] & SDMMC_MASK(kSDIO_OcrPowerUpBusyFlag)) == 0U))
|
|
{
|
|
continue;
|
|
}
|
|
else if (argument == 0U)
|
|
{
|
|
/* check the io number and ocr value */
|
|
if ((((command.response[0U] & SDIO_OCR_IO_NUM_MASK) >> kSDIO_OcrIONumber) == 0U) ||
|
|
((command.response[0U] & 0xFFFFFFU) == 0U))
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* check if memory present */
|
|
if ((command.response[0U] & SDMMC_MASK(kSDIO_OcrMemPresent)) == SDMMC_MASK(kSDIO_OcrMemPresent))
|
|
{
|
|
card->memPresentFlag = true;
|
|
}
|
|
/* save the io number */
|
|
card->ioTotalNumber = (uint8_t)((command.response[0U] & SDIO_OCR_IO_NUM_MASK) >> kSDIO_OcrIONumber);
|
|
/* save the operation condition */
|
|
card->ocr = command.response[0U] & 0xFFFFFFU;
|
|
if (accept1V8 != NULL)
|
|
{
|
|
*accept1V8 = command.response[0U] & 0x1000000U;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ((i != 0U) ? kStatus_Success : kStatus_Fail);
|
|
}
|
|
|
|
static status_t SDIO_IO_Access_Direct(sdio_card_t *card,
|
|
sdio_io_direction_t direction,
|
|
sdio_func_num_t func,
|
|
uint32_t regAddr,
|
|
uint8_t dataIn,
|
|
uint8_t *dataOut,
|
|
bool rawFlag)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
sdmmchost_transfer_t content = {0U};
|
|
sdmmchost_cmd_t command = {0U};
|
|
status_t error = kStatus_Success;
|
|
|
|
command.index = (uint32_t)kSDIO_RWIODirect;
|
|
command.argument = ((uint32_t)func << SDIO_CMD_ARGUMENT_FUNC_NUM_POS) |
|
|
((regAddr & SDIO_CMD_ARGUMENT_REG_ADDR_MASK) << SDIO_CMD_ARGUMENT_REG_ADDR_POS);
|
|
|
|
if ((dataOut != NULL) && (direction == kSDIO_IOWrite))
|
|
{
|
|
command.argument |= (1UL << SDIO_CMD_ARGUMENT_RW_POS) | ((uint32_t)rawFlag << SDIO_DIRECT_CMD_ARGUMENT_RAW_POS);
|
|
}
|
|
|
|
if (direction == kSDIO_IOWrite)
|
|
{
|
|
command.argument |= (uint32_t)dataIn & SDIO_DIRECT_CMD_DATA_MASK;
|
|
}
|
|
|
|
command.responseType = kCARD_ResponseTypeR5;
|
|
command.responseErrorFlags =
|
|
((uint32_t)kSDIO_StatusCmdCRCError | (uint32_t)kSDIO_StatusIllegalCmd | (uint32_t)kSDIO_StatusError |
|
|
(uint32_t)kSDIO_StatusFunctionNumError | (uint32_t)kSDIO_StatusOutofRange);
|
|
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
if ((error == kStatus_Success) && (dataOut != NULL))
|
|
{
|
|
/* read data from response */
|
|
*dataOut = (uint8_t)(command.response[0U] & SDIO_DIRECT_CMD_DATA_MASK);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_IO_Write_Direct(sdio_card_t *card, sdio_func_num_t func, uint32_t regAddr, uint8_t *data, bool raw)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, func, regAddr, *data, data, raw);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_IO_Read_Direct(sdio_card_t *card, sdio_func_num_t func, uint32_t regAddr, uint8_t *data)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, func, regAddr, 0, data, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_IO_RW_Direct(sdio_card_t *card,
|
|
sdio_io_direction_t direction,
|
|
sdio_func_num_t func,
|
|
uint32_t regAddr,
|
|
uint8_t dataIn,
|
|
uint8_t *dataOut)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
error = SDIO_IO_Access_Direct(card, direction, func, regAddr, dataIn, dataOut, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_IO_Write_Extended(
|
|
sdio_card_t *card, sdio_func_num_t func, uint32_t regAddr, uint8_t *buffer, uint32_t count, uint32_t flags)
|
|
{
|
|
assert(card != NULL);
|
|
assert(buffer != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
sdmmchost_transfer_t content = {0U};
|
|
sdmmchost_cmd_t command = {0U};
|
|
sdmmchost_data_t data = {0U};
|
|
bool blockMode = false;
|
|
bool opCode = false;
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
/* check if card support block mode */
|
|
if (((card->cccrflags & (uint32_t)kSDIO_CCCRSupportMultiBlock) != 0U) &&
|
|
((flags & SDIO_EXTEND_CMD_BLOCK_MODE_MASK) != 0U))
|
|
{
|
|
blockMode = true;
|
|
}
|
|
|
|
if ((flags & SDIO_EXTEND_CMD_OP_CODE_MASK) != 0U)
|
|
{
|
|
opCode = true;
|
|
}
|
|
|
|
/* check the byte size counter in non-block mode
|
|
* so you need read CIS for each function first,before you do read/write
|
|
*/
|
|
if (!blockMode)
|
|
{
|
|
if ((func == kSDIO_FunctionNum0) && (card->commonCIS.fn0MaxBlkSize != 0U) &&
|
|
(count > card->commonCIS.fn0MaxBlkSize))
|
|
{
|
|
error = kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
else if ((func != kSDIO_FunctionNum0) && (card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize != 0U) &&
|
|
(count > card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize))
|
|
{
|
|
error = kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
else
|
|
{
|
|
/* Intentional empty */
|
|
}
|
|
}
|
|
|
|
if (error == kStatus_Success)
|
|
{
|
|
command.index = (uint32_t)kSDIO_RWIOExtended;
|
|
command.argument = ((uint32_t)func << SDIO_CMD_ARGUMENT_FUNC_NUM_POS) |
|
|
((regAddr & SDIO_CMD_ARGUMENT_REG_ADDR_MASK) << SDIO_CMD_ARGUMENT_REG_ADDR_POS) |
|
|
(1UL << SDIO_CMD_ARGUMENT_RW_POS) | (count & SDIO_EXTEND_CMD_COUNT_MASK) |
|
|
((blockMode ? 1UL : 0UL) << SDIO_EXTEND_CMD_ARGUMENT_BLOCK_MODE_POS |
|
|
((opCode ? 1UL : 0UL) << SDIO_EXTEND_CMD_ARGUMENT_OP_CODE_POS));
|
|
command.responseType = kCARD_ResponseTypeR5;
|
|
command.responseErrorFlags =
|
|
((uint32_t)kSDIO_StatusCmdCRCError | (uint32_t)kSDIO_StatusIllegalCmd | (uint32_t)kSDIO_StatusError |
|
|
(uint32_t)kSDIO_StatusFunctionNumError | (uint32_t)kSDIO_StatusOutofRange);
|
|
|
|
if (blockMode)
|
|
{
|
|
if (func == kSDIO_FunctionNum0)
|
|
{
|
|
data.blockSize = card->io0blockSize;
|
|
}
|
|
else
|
|
{
|
|
data.blockSize = card->ioFBR[(uint32_t)func - 1U].ioBlockSize;
|
|
}
|
|
data.blockCount = count;
|
|
}
|
|
else
|
|
{
|
|
data.blockSize = count;
|
|
data.blockCount = 1U;
|
|
}
|
|
data.txData = (uint32_t *)(uint32_t)buffer;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_IO_Read_Extended(
|
|
sdio_card_t *card, sdio_func_num_t func, uint32_t regAddr, uint8_t *buffer, uint32_t count, uint32_t flags)
|
|
{
|
|
assert(card != NULL);
|
|
assert(buffer != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
sdmmchost_transfer_t content = {0U};
|
|
sdmmchost_cmd_t command = {0U};
|
|
sdmmchost_data_t data = {0U};
|
|
bool blockMode = false;
|
|
bool opCode = false;
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
/* check if card support block mode */
|
|
if (((card->cccrflags & (uint32_t)kSDIO_CCCRSupportMultiBlock) != 0U) &&
|
|
((flags & SDIO_EXTEND_CMD_BLOCK_MODE_MASK) != 0U))
|
|
{
|
|
blockMode = true;
|
|
}
|
|
|
|
/* op code =0 : read/write to fixed addr
|
|
* op code =1 :read/write addr incrementing
|
|
*/
|
|
if ((flags & SDIO_EXTEND_CMD_OP_CODE_MASK) != 0U)
|
|
{
|
|
opCode = true;
|
|
}
|
|
|
|
/* check the byte size counter in non-block mode
|
|
* so you need read CIS for each function first,before you do read/write
|
|
*/
|
|
if (!blockMode)
|
|
{
|
|
if ((func == kSDIO_FunctionNum0) && (card->commonCIS.fn0MaxBlkSize != 0U) &&
|
|
(count > card->commonCIS.fn0MaxBlkSize))
|
|
{
|
|
error = kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
else if ((func != kSDIO_FunctionNum0) && (card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize != 0U) &&
|
|
(count > card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize))
|
|
{
|
|
error = kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
else
|
|
{
|
|
/* Intentional empty */
|
|
}
|
|
}
|
|
|
|
if (error == kStatus_Success)
|
|
{
|
|
command.index = (uint32_t)kSDIO_RWIOExtended;
|
|
command.argument = ((uint32_t)func << SDIO_CMD_ARGUMENT_FUNC_NUM_POS) |
|
|
((regAddr & SDIO_CMD_ARGUMENT_REG_ADDR_MASK) << SDIO_CMD_ARGUMENT_REG_ADDR_POS) |
|
|
(count & SDIO_EXTEND_CMD_COUNT_MASK) |
|
|
((blockMode ? 1UL : 0UL) << SDIO_EXTEND_CMD_ARGUMENT_BLOCK_MODE_POS |
|
|
((opCode ? 1UL : 0UL) << SDIO_EXTEND_CMD_ARGUMENT_OP_CODE_POS));
|
|
command.responseType = kCARD_ResponseTypeR5;
|
|
command.responseErrorFlags =
|
|
((uint32_t)kSDIO_StatusCmdCRCError | (uint32_t)kSDIO_StatusIllegalCmd | (uint32_t)kSDIO_StatusError |
|
|
(uint32_t)kSDIO_StatusFunctionNumError | (uint32_t)kSDIO_StatusOutofRange);
|
|
|
|
if (blockMode)
|
|
{
|
|
if (func == kSDIO_FunctionNum0)
|
|
{
|
|
data.blockSize = card->io0blockSize;
|
|
}
|
|
else
|
|
{
|
|
data.blockSize = card->ioFBR[(uint32_t)func - 1U].ioBlockSize;
|
|
}
|
|
data.blockCount = count;
|
|
}
|
|
else
|
|
{
|
|
data.blockSize = count;
|
|
data.blockCount = 1U;
|
|
}
|
|
data.rxData = (uint32_t *)(uint32_t)buffer;
|
|
|
|
content.command = &command;
|
|
content.data = &data;
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success != error)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_IO_Transfer(sdio_card_t *card,
|
|
sdio_command_t cmd,
|
|
uint32_t argument,
|
|
uint32_t blockSize,
|
|
uint8_t *txData,
|
|
uint8_t *rxData,
|
|
uint16_t dataSize,
|
|
uint32_t *response)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t actualSize = dataSize;
|
|
sdmmchost_transfer_t content = {0U};
|
|
sdmmchost_cmd_t command = {0U};
|
|
sdmmchost_data_t data = {0U};
|
|
uint32_t i = SDIO_RETRY_TIMES;
|
|
uint32_t *dataAddr = (uint32_t *)(uint32_t)(txData == NULL ? rxData : txData);
|
|
uint8_t *alignBuffer = (uint8_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
status_t error = kStatus_Fail;
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
|
|
if ((dataSize != 0U) && (txData != NULL) && (rxData != NULL))
|
|
{
|
|
error = kStatus_InvalidArgument;
|
|
}
|
|
else
|
|
{
|
|
command.index = (uint32_t)cmd;
|
|
command.argument = argument;
|
|
command.responseType = kCARD_ResponseTypeR5;
|
|
command.responseErrorFlags =
|
|
((uint32_t)kSDIO_StatusCmdCRCError | (uint32_t)kSDIO_StatusIllegalCmd | (uint32_t)kSDIO_StatusError |
|
|
(uint32_t)kSDIO_StatusFunctionNumError | (uint32_t)kSDIO_StatusOutofRange);
|
|
content.command = &command;
|
|
content.data = NULL;
|
|
|
|
if (dataSize != 0U)
|
|
{
|
|
/* if block size bigger than 1, then use block mode */
|
|
if ((argument & SDIO_EXTEND_CMD_BLOCK_MODE_MASK) != 0U)
|
|
{
|
|
if (dataSize % blockSize != 0U)
|
|
{
|
|
actualSize = ((dataSize / blockSize) + 1U) * blockSize;
|
|
}
|
|
|
|
data.blockCount = actualSize / blockSize;
|
|
data.blockSize = blockSize;
|
|
}
|
|
else
|
|
{
|
|
data.blockCount = 1;
|
|
data.blockSize = dataSize;
|
|
}
|
|
/* if data buffer address can not meet host controller internal DMA requirement, sdio driver will try to use
|
|
* internal align buffer if data size is not bigger than internal buffer size,
|
|
* Align address transfer always can get a better performance, so if you want sdio driver make buffer
|
|
* address align, you should redefine the SDMMC_GLOBAL_BUFFER_SIZE macro to a value which is big enough for
|
|
* your application.
|
|
*/
|
|
if (
|
|
#if SDMMCHOST_DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE != 1U
|
|
(((uint32_t)dataAddr & (SDMMCHOST_DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE - 1U)) != 0U) &&
|
|
#endif
|
|
(actualSize <= FSL_SDMMC_DEFAULT_BLOCK_SIZE) && (!card->noInternalAlign))
|
|
{
|
|
dataAddr = (uint32_t *)(uint32_t)alignBuffer;
|
|
(void)memset(alignBuffer, 0, actualSize);
|
|
if (txData != NULL)
|
|
{
|
|
(void)memcpy(alignBuffer, txData, dataSize);
|
|
}
|
|
}
|
|
|
|
if (rxData != NULL)
|
|
{
|
|
data.rxData = dataAddr;
|
|
}
|
|
else
|
|
{
|
|
data.txData = dataAddr;
|
|
}
|
|
|
|
content.data = &data;
|
|
}
|
|
|
|
do
|
|
{
|
|
error = SDMMCHOST_TransferFunction(card->host, &content);
|
|
if (kStatus_Success == error)
|
|
{
|
|
if ((rxData != NULL) && (dataSize != 0U) &&
|
|
#if SDMMCHOST_DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE != 1U
|
|
(((uint32_t)rxData & (SDMMCHOST_DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE - 1U)) != 0U) &&
|
|
#endif
|
|
(actualSize <= FSL_SDMMC_DEFAULT_BLOCK_SIZE) && (!card->noInternalAlign))
|
|
{
|
|
(void)memcpy(rxData, alignBuffer, dataSize);
|
|
}
|
|
|
|
if (response != NULL)
|
|
{
|
|
*response = command.response[0];
|
|
}
|
|
|
|
error = kStatus_Success;
|
|
break;
|
|
}
|
|
|
|
i--;
|
|
} while (i != 0U);
|
|
}
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_GetCardCapability(sdio_card_t *card, sdio_func_num_t func)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
uint8_t *tempBuffer = (uint8_t *)FSL_SDMMC_CARD_INTERNAL_BUFFER_ALIGN_ADDR(card->internalBuffer);
|
|
uint32_t i = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
(void)memset(tempBuffer, 0, SDIO_CCCR_REG_NUMBER);
|
|
|
|
for (i = 0U; i <= SDIO_CCCR_REG_NUMBER; i++)
|
|
{
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, SDIO_FBR_BASE((uint32_t)func) + i, 0U,
|
|
&tempBuffer[i], false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
}
|
|
|
|
switch (func)
|
|
{
|
|
case kSDIO_FunctionNum0:
|
|
|
|
card->sdVersion = tempBuffer[kSDIO_RegSDVersion];
|
|
card->sdioVersion = tempBuffer[kSDIO_RegCCCRSdioVer] >> 4U;
|
|
card->cccrVersioin = tempBuffer[kSDIO_RegCCCRSdioVer] & 0xFU;
|
|
/* continuous SPI interrupt */
|
|
if ((tempBuffer[kSDIO_RegBusInterface] & 0x40U) != 0U)
|
|
{
|
|
card->cccrflags |= (uint32_t)kSDIO_CCCRSupportContinuousSPIInt;
|
|
}
|
|
/* 8bit data bus */
|
|
if ((tempBuffer[kSDIO_RegBusInterface] & 0x4U) != 0U)
|
|
{
|
|
card->cccrflags |= SDIO_CCCR_SUPPORT_8BIT_BUS;
|
|
}
|
|
|
|
/* card capability register */
|
|
card->cccrflags |= (tempBuffer[kSDIO_RegCardCapability] & 0xDFUL);
|
|
/* master power control */
|
|
if ((tempBuffer[kSDIO_RegPowerControl] & 0x01U) != 0U)
|
|
{
|
|
card->cccrflags |= (uint32_t)kSDIO_CCCRSupportMasterPowerControl;
|
|
}
|
|
/* high speed flag */
|
|
if ((tempBuffer[kSDIO_RegBusSpeed] & 0x01U) != 0U)
|
|
{
|
|
card->cccrflags |= SDIO_CCCR_SUPPORT_HIGHSPEED;
|
|
}
|
|
/* uhs mode flag */
|
|
card->cccrflags |= (tempBuffer[kSDIO_RegUHSITimingSupport] & 7UL) << 11U;
|
|
/* driver type flag */
|
|
card->cccrflags |= (tempBuffer[kSDIO_RegDriverStrength] & 7UL) << 14U;
|
|
/* low speed 4bit */
|
|
if ((tempBuffer[kSDIO_RegCardCapability] & 0x80U) != 0U)
|
|
{
|
|
card->cccrflags |= (uint32_t)kSDIO_CCCRSupportLowSpeed4Bit;
|
|
}
|
|
/* common CIS pointer */
|
|
card->commonCISPointer = tempBuffer[kSDIO_RegCommonCISPointer] |
|
|
((uint32_t)tempBuffer[(uint32_t)kSDIO_RegCommonCISPointer + 1U] << 8U) |
|
|
((uint32_t)tempBuffer[(uint32_t)kSDIO_RegCommonCISPointer + 2U] << 16U);
|
|
|
|
/* check card capability of support async interrupt */
|
|
if ((tempBuffer[kSDIO_RegInterruptExtension] & SDIO_CCCR_ASYNC_INT_MASK) == SDIO_CCCR_ASYNC_INT_MASK)
|
|
{
|
|
card->cccrflags |= SDIO_CCCR_SUPPORT_ASYNC_INT;
|
|
}
|
|
|
|
break;
|
|
|
|
case kSDIO_FunctionNum1:
|
|
case kSDIO_FunctionNum2:
|
|
case kSDIO_FunctionNum3:
|
|
case kSDIO_FunctionNum4:
|
|
case kSDIO_FunctionNum5:
|
|
case kSDIO_FunctionNum6:
|
|
case kSDIO_FunctionNum7:
|
|
card->ioFBR[(uint32_t)func - 1U].ioStdFunctionCode = tempBuffer[0U] & 0x0FU;
|
|
card->ioFBR[(uint32_t)func - 1U].ioExtFunctionCode = tempBuffer[1U];
|
|
card->ioFBR[(uint32_t)func - 1U].ioPointerToCIS =
|
|
tempBuffer[9U] | ((uint32_t)tempBuffer[10U] << 8U) | ((uint32_t)tempBuffer[11U] << 16U);
|
|
card->ioFBR[(uint32_t)func - 1U].ioPointerToCSA =
|
|
tempBuffer[12U] | ((uint32_t)tempBuffer[13U] << 8U) | ((uint32_t)tempBuffer[14U] << 16U);
|
|
if ((tempBuffer[2U] & 0x01U) != 0U)
|
|
{
|
|
card->ioFBR[(uint32_t)func - 1U].flags |= (uint8_t)kSDIO_FBRSupportPowerSelection;
|
|
}
|
|
if ((tempBuffer[0U] & 0x40U) != 0U)
|
|
{
|
|
card->ioFBR[(uint32_t)func - 1U].flags |= (uint8_t)kSDIO_FBRSupportCSA;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_SetBlockSize(sdio_card_t *card, sdio_func_num_t func, uint32_t blockSize)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
assert(blockSize <= SDIO_MAX_BLOCK_SIZE);
|
|
|
|
uint8_t temp = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* check the block size for block mode
|
|
* so you need read CIS for each function first,before you do read/write
|
|
*/
|
|
if ((func == kSDIO_FunctionNum0) && (card->commonCIS.fn0MaxBlkSize != 0U) &&
|
|
(blockSize > card->commonCIS.fn0MaxBlkSize))
|
|
{
|
|
return kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
else if ((func != kSDIO_FunctionNum0) && (card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize != 0U) &&
|
|
(blockSize > card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize))
|
|
{
|
|
return kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
else
|
|
{
|
|
/* Intentional empty */
|
|
}
|
|
|
|
temp = (uint8_t)(blockSize & 0xFFU);
|
|
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0,
|
|
SDIO_FBR_BASE((uint32_t)func) + (uint32_t)kSDIO_RegFN0BlockSizeLow, temp, &temp, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SetCardBlockSizeFailed;
|
|
}
|
|
|
|
temp = (uint8_t)((blockSize >> 8U) & 0xFFU);
|
|
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0,
|
|
SDIO_FBR_BASE((uint32_t)func) + (uint32_t)kSDIO_RegFN0BlockSizeHigh, temp, &temp, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_SetCardBlockSizeFailed;
|
|
}
|
|
|
|
/* record the current block size */
|
|
if (func == kSDIO_FunctionNum0)
|
|
{
|
|
card->io0blockSize = blockSize;
|
|
}
|
|
else
|
|
{
|
|
card->ioFBR[(uint32_t)func - 1U].ioBlockSize = (uint16_t)blockSize;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_CardReset(sdio_card_t *card)
|
|
{
|
|
status_t error = kStatus_Success;
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegIOAbort, 0x08U, NULL, false);
|
|
if (error != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_SetDataBusWidth(sdio_card_t *card, sdio_bus_width_t busWidth)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint8_t regBusInterface = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
if (((busWidth == kSDIO_DataBus4Bit) && ((card->cccrflags & (uint32_t)kSDIO_CCCRSupportHighSpeed) == 0U) &&
|
|
((card->cccrflags & (uint32_t)kSDIO_CCCRSupportLowSpeed4Bit) == 0U)))
|
|
{
|
|
return kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
|
|
if ((((card->cccrflags & SDIO_CCCR_SUPPORT_8BIT_BUS) == 0U) ||
|
|
((card->usrParam.capability & (uint32_t)kSDMMC_Support8BitWidth) == 0U) ||
|
|
((card->host->capability & (uint32_t)kSDMMCHOST_Support8BitDataWidth) == 0U)) &&
|
|
(busWidth == kSDIO_DataBus8Bit))
|
|
{
|
|
return kStatus_SDMMC_SDIO_InvalidArgument;
|
|
}
|
|
|
|
/* load bus interface register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegBusInterface, 0U, ®BusInterface,
|
|
false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* set bus width */
|
|
regBusInterface &= 0xFCU;
|
|
regBusInterface |= (uint8_t)busWidth;
|
|
|
|
/* write to register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegBusInterface, regBusInterface,
|
|
®BusInterface, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
#if SDMMCHOST_SUPPORT_8_BIT_WIDTH
|
|
if (busWidth == kSDIO_DataBus8Bit)
|
|
{
|
|
SDMMCHOST_SetCardBusWidth(card->host, kSDMMC_BusWdith8Bit);
|
|
}
|
|
else
|
|
#endif
|
|
if (busWidth == kSDIO_DataBus4Bit)
|
|
{
|
|
SDMMCHOST_SetCardBusWidth(card->host, kSDMMC_BusWdith4Bit);
|
|
}
|
|
else
|
|
{
|
|
SDMMCHOST_SetCardBusWidth(card->host, kSDMMC_BusWdith1Bit);
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SDIO_SetMaxDataBusWidth(sdio_card_t *card)
|
|
{
|
|
sdio_bus_width_t busWidth = kSDIO_DataBus1Bit;
|
|
|
|
if (((card->cccrflags & SDIO_CCCR_SUPPORT_8BIT_BUS) != 0U) &&
|
|
((card->usrParam.capability & (uint32_t)kSDMMC_Support8BitWidth) != 0U) &&
|
|
((card->host->capability & (uint32_t)kSDMMCHOST_Support8BitDataWidth) != 0U))
|
|
{
|
|
busWidth = kSDIO_DataBus8Bit;
|
|
}
|
|
/* switch data bus width */
|
|
else if ((((card->cccrflags & (uint32_t)kSDIO_CCCRSupportHighSpeed) != 0U) ||
|
|
((card->cccrflags & (uint32_t)kSDIO_CCCRSupportLowSpeed4Bit) != 0U)) &&
|
|
((card->host->capability & (uint32_t)kSDMMCHOST_Support4BitDataWidth) != 0U))
|
|
{
|
|
busWidth = kSDIO_DataBus4Bit;
|
|
}
|
|
else
|
|
{
|
|
busWidth = kSDIO_DataBus1Bit;
|
|
}
|
|
|
|
return SDIO_SetDataBusWidth(card, busWidth);
|
|
}
|
|
|
|
status_t SDIO_SwitchToHighSpeed(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint8_t temp = 0U;
|
|
uint32_t retryTimes = SDIO_RETRY_TIMES;
|
|
status_t status = kStatus_SDMMC_SDIO_SwitchHighSpeedFail;
|
|
status_t error = kStatus_Success;
|
|
|
|
if ((card->cccrflags & SDIO_CCCR_SUPPORT_HIGHSPEED) != 0U)
|
|
{
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegBusSpeed, 0U, &temp, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
do
|
|
{
|
|
temp &= (uint8_t)~SDIO_CCCR_BUS_SPEED_MASK;
|
|
temp |= SDIO_CCCR_ENABLE_HIGHSPEED_MODE;
|
|
|
|
retryTimes--;
|
|
/* enable high speed mode */
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegBusSpeed, temp, &temp, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
continue;
|
|
}
|
|
/* either EHS=0 and SHS=0 ,the card is still in default mode */
|
|
if ((temp & 0x03U) == 0x03U)
|
|
{
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(
|
|
card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_50MHZ));
|
|
status = kStatus_Success;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
} while (retryTimes != 0U);
|
|
}
|
|
else
|
|
{
|
|
/* default mode 25MHZ */
|
|
card->busClock_Hz =
|
|
SDMMCHOST_SetCardClock(card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_25MHZ));
|
|
status = kStatus_Success;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static status_t SDIO_SelectBusTiming(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t targetBusFreq = SD_CLOCK_25MHZ;
|
|
uint32_t targetTiming = 0U;
|
|
uint8_t temp = 0U;
|
|
uint32_t supportModeFlag = 0U;
|
|
uint32_t retryTimes = SDIO_RETRY_TIMES;
|
|
status_t error = kStatus_Success;
|
|
|
|
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) &&
|
|
((card->cccrflags & SDIO_CCCR_SUPPORT_SDR104) == SDIO_CCCR_SUPPORT_SDR104) &&
|
|
(card->operationVoltage == kSDMMC_OperationVoltage180V))
|
|
{
|
|
targetTiming = SDIO_CCCR_ENABLE_SDR104_MODE;
|
|
targetBusFreq = FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_208MHZ);
|
|
supportModeFlag = SDIO_CCCR_SUPPORT_SDR104;
|
|
}
|
|
else
|
|
{
|
|
card->currentTiming = kSD_TimingDDR50Mode;
|
|
}
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingDDR50Mode)
|
|
{
|
|
if (((card->host->capability & (uint32_t)kSDMMCHOST_SupportDDRMode) != 0U) &&
|
|
((card->cccrflags & SDIO_CCCR_SUPPORT_DDR50) == SDIO_CCCR_SUPPORT_DDR50) &&
|
|
(card->operationVoltage == kSDMMC_OperationVoltage180V))
|
|
{
|
|
targetTiming = SDIO_CCCR_ENABLE_DDR50_MODE;
|
|
targetBusFreq = FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_50MHZ);
|
|
supportModeFlag = SDIO_CCCR_SUPPORT_DDR50;
|
|
}
|
|
else
|
|
{
|
|
card->currentTiming = kSD_TimingSDR50Mode;
|
|
}
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingSDR50Mode)
|
|
{
|
|
if (((card->host->capability & (uint32_t)kSDMMCHOST_SupportSDR50) != 0U) &&
|
|
((card->cccrflags & SDIO_CCCR_SUPPORT_SDR50) == SDIO_CCCR_SUPPORT_SDR50) &&
|
|
(card->operationVoltage == kSDMMC_OperationVoltage180V))
|
|
{
|
|
targetTiming = SDIO_CCCR_ENABLE_SDR50_MODE;
|
|
targetBusFreq = FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_100MHZ);
|
|
supportModeFlag = SDIO_CCCR_SUPPORT_SDR50;
|
|
}
|
|
else
|
|
{
|
|
card->currentTiming = kSD_TimingSDR25HighSpeedMode;
|
|
}
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingSDR25HighSpeedMode)
|
|
{
|
|
if ((card->cccrflags & SDIO_CCCR_SUPPORT_HIGHSPEED) == SDIO_CCCR_SUPPORT_HIGHSPEED)
|
|
{
|
|
targetTiming = SDIO_CCCR_ENABLE_HIGHSPEED_MODE;
|
|
targetBusFreq = FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, SD_CLOCK_50MHZ);
|
|
supportModeFlag = SDIO_CCCR_SUPPORT_HIGHSPEED;
|
|
}
|
|
else
|
|
{
|
|
card->currentTiming = kSD_TimingSDR12DefaultMode;
|
|
}
|
|
}
|
|
|
|
if (card->currentTiming == kSD_TimingSDR12DefaultMode)
|
|
{
|
|
/* default timing mode */
|
|
targetBusFreq = SD_CLOCK_25MHZ;
|
|
}
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegBusSpeed, 0U, &temp, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
do
|
|
{
|
|
temp &= (uint8_t)~SDIO_CCCR_BUS_SPEED_MASK;
|
|
temp |= (uint8_t)targetTiming;
|
|
|
|
retryTimes--;
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegBusSpeed, temp, &temp, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ((temp & targetTiming) != targetTiming)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
|
|
} while (retryTimes != 0U);
|
|
|
|
if (retryTimes == 0U)
|
|
{
|
|
retryTimes = SDIO_RETRY_TIMES;
|
|
/* if cannot switch target timing, it will switch continuously until find a valid timing. */
|
|
card->cccrflags &= ~supportModeFlag;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
|
|
} while (true);
|
|
|
|
card->busClock_Hz =
|
|
SDMMCHOST_SetCardClock(card->host, FSL_SDMMC_CARD_MAX_BUS_FREQ(card->usrParam.maxFreq, targetBusFreq));
|
|
|
|
/* enable DDR mode if it is the target mode */
|
|
if (card->currentTiming == kSD_TimingDDR50Mode)
|
|
{
|
|
SDMMCHOST_EnableDDRMode(card->host, true, 0U);
|
|
}
|
|
|
|
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 (SDIO_ExecuteTuning(card) != kStatus_Success)
|
|
{
|
|
return kStatus_SDMMC_TuningFail;
|
|
}
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_SetDriverStrength(sdio_card_t *card, sd_driver_strength_t driverStrength)
|
|
{
|
|
uint8_t strength = 0U, temp = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
switch (driverStrength)
|
|
{
|
|
case kSD_DriverStrengthTypeA:
|
|
strength = SDIO_CCCR_ENABLE_DRIVER_TYPE_A;
|
|
break;
|
|
case kSD_DriverStrengthTypeC:
|
|
strength = SDIO_CCCR_ENABLE_DRIVER_TYPE_C;
|
|
break;
|
|
case kSD_DriverStrengthTypeD:
|
|
strength = SDIO_CCCR_ENABLE_DRIVER_TYPE_D;
|
|
break;
|
|
default:
|
|
strength = SDIO_CCCR_ENABLE_DRIVER_TYPE_B;
|
|
break;
|
|
}
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegDriverStrength, 0U, &temp, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
temp &= (uint8_t)~SDIO_CCCR_DRIVER_TYPE_MASK;
|
|
temp |= strength;
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegDriverStrength, temp, &temp, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
status_t SDIO_EnableAsyncInterrupt(sdio_card_t *card, bool enable)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint8_t eai = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
if ((card->cccrflags & SDIO_CCCR_SUPPORT_ASYNC_INT) == 0U)
|
|
{
|
|
return kStatus_SDMMC_NotSupportYet;
|
|
}
|
|
|
|
/* load interrupt enable register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegInterruptExtension, 0U, &eai, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* if already enable/disable , do not need enable/disable again */
|
|
if (((eai)&SDIO_CCCR_ENABLE_AYNC_INT) == (enable ? SDIO_CCCR_ENABLE_AYNC_INT : 0U))
|
|
{
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/* enable the eai */
|
|
if (enable)
|
|
{
|
|
eai |= SDIO_CCCR_ENABLE_AYNC_INT;
|
|
}
|
|
else
|
|
{
|
|
eai &= (uint8_t) ~(SDIO_CCCR_ENABLE_AYNC_INT);
|
|
}
|
|
|
|
/* write to register */
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegInterruptExtension, eai, &eai, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SDIO_DecodeCIS(
|
|
sdio_card_t *card, sdio_func_num_t func, uint8_t *dataBuffer, uint32_t tplCode, uint32_t tplLink)
|
|
{
|
|
assert(card != NULL);
|
|
assert(dataBuffer != NULL);
|
|
|
|
if (func == kSDIO_FunctionNum0)
|
|
{
|
|
/* only decode MANIFID,FUNCID,FUNCE here */
|
|
if (tplCode == SDIO_TPL_CODE_MANIFID)
|
|
{
|
|
card->commonCIS.mID = dataBuffer[0U] | ((uint16_t)dataBuffer[1U] << 8U);
|
|
card->commonCIS.mInfo = dataBuffer[2U] | ((uint16_t)dataBuffer[3U] << 8U);
|
|
}
|
|
else if (tplCode == SDIO_TPL_CODE_FUNCID)
|
|
{
|
|
card->commonCIS.funcID = dataBuffer[0U];
|
|
}
|
|
else if (tplCode == SDIO_TPL_CODE_FUNCE)
|
|
{
|
|
/* max transfer block size and data size */
|
|
card->commonCIS.fn0MaxBlkSize = dataBuffer[1U] | ((uint16_t)dataBuffer[2U] << 8U);
|
|
/* max transfer speed */
|
|
card->commonCIS.maxTransSpeed = dataBuffer[3U];
|
|
}
|
|
else
|
|
{
|
|
/* reserved here */
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* only decode FUNCID,FUNCE here */
|
|
if (tplCode == SDIO_TPL_CODE_FUNCID)
|
|
{
|
|
card->funcCIS[(uint32_t)func - 1U].funcID = dataBuffer[0U];
|
|
}
|
|
else if (tplCode == SDIO_TPL_CODE_FUNCE)
|
|
{
|
|
if (tplLink == 0x2AU)
|
|
{
|
|
card->funcCIS[(uint32_t)func - 1U].funcInfo = dataBuffer[1U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioVersion = dataBuffer[2U];
|
|
card->funcCIS[(uint32_t)func - 1U].cardPSN = dataBuffer[3U] | ((uint32_t)dataBuffer[4U] << 8U) |
|
|
((uint32_t)dataBuffer[5U] << 16U) |
|
|
((uint32_t)dataBuffer[6U] << 24U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioCSASize = dataBuffer[7U] | ((uint32_t)dataBuffer[8U] << 8U) |
|
|
((uint32_t)dataBuffer[9U] << 16U) |
|
|
((uint32_t)dataBuffer[10U] << 24U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioCSAProperty = dataBuffer[11U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioMaxBlockSize = dataBuffer[12U] | ((uint16_t)dataBuffer[13U] << 8U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioOCR = dataBuffer[14U] | ((uint32_t)dataBuffer[15U] << 8U) |
|
|
((uint32_t)dataBuffer[16U] << 16U) |
|
|
((uint32_t)dataBuffer[17U] << 24U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioOPMinPwr = dataBuffer[18U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioOPAvgPwr = dataBuffer[19U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioOPMaxPwr = dataBuffer[20U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioSBMinPwr = dataBuffer[21U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioSBAvgPwr = dataBuffer[22U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioSBMaxPwr = dataBuffer[23U];
|
|
card->funcCIS[(uint32_t)func - 1U].ioMinBandWidth = dataBuffer[24U] | ((uint16_t)dataBuffer[25U] << 8U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioOptimumBandWidth =
|
|
dataBuffer[26U] | ((uint16_t)dataBuffer[27U] << 8U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioReadyTimeout = dataBuffer[28U] | ((uint16_t)dataBuffer[29U] << 8U);
|
|
|
|
card->funcCIS[(uint32_t)func - 1U].ioHighCurrentAvgCurrent =
|
|
dataBuffer[34U] | ((uint16_t)dataBuffer[35U] << 8U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioHighCurrentMaxCurrent =
|
|
dataBuffer[36U] | ((uint16_t)dataBuffer[37U] << 8U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioLowCurrentAvgCurrent =
|
|
dataBuffer[38U] | ((uint16_t)dataBuffer[39U] << 8U);
|
|
card->funcCIS[(uint32_t)func - 1U].ioLowCurrentMaxCurrent =
|
|
dataBuffer[40U] | ((uint16_t)dataBuffer[41U] << 8U);
|
|
}
|
|
else
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_ReadCIS(sdio_card_t *card, sdio_func_num_t func, const uint32_t *tupleList, uint32_t tupleNum)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
assert(tupleList != NULL);
|
|
|
|
uint8_t tplCode = 0U;
|
|
uint8_t tplLink = 0U;
|
|
uint32_t cisPtr = 0U;
|
|
uint32_t i = 0U, num = 0U;
|
|
bool tupleMatch = false;
|
|
status_t error = kStatus_Success;
|
|
|
|
uint8_t dataBuffer[255U] = {0U};
|
|
|
|
/* get the CIS pointer for each function */
|
|
if (func == kSDIO_FunctionNum0)
|
|
{
|
|
cisPtr = card->commonCISPointer;
|
|
}
|
|
else
|
|
{
|
|
cisPtr = card->ioFBR[(uint32_t)func - 1U].ioPointerToCIS;
|
|
}
|
|
|
|
if (0U == cisPtr)
|
|
{
|
|
return kStatus_SDMMC_SDIO_ReadCISFail;
|
|
}
|
|
|
|
do
|
|
{
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, cisPtr++, 0U, &tplCode, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* end of chain tuple */
|
|
if (tplCode == 0xFFU)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (tplCode == 0U)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < tupleNum; i++)
|
|
{
|
|
if (tplCode == tupleList[i])
|
|
{
|
|
tupleMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, cisPtr++, 0U, &tplLink, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* end of chain tuple */
|
|
if (tplLink == 0xFFU)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (tupleMatch)
|
|
{
|
|
(void)memset(dataBuffer, 0, 255U);
|
|
for (i = 0; i < tplLink; i++)
|
|
{
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, cisPtr++, 0U, &dataBuffer[i], false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
}
|
|
tupleMatch = false;
|
|
/* pharse the data */
|
|
(void)SDIO_DecodeCIS(card, func, dataBuffer, tplCode, tplLink);
|
|
/* read finish then return */
|
|
if (++num == tupleNum)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* move pointer */
|
|
cisPtr += tplLink;
|
|
/* tuple code not match,continue read tuple code */
|
|
continue;
|
|
}
|
|
} while (true);
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static status_t SDIO_ProbeBusVoltage(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint32_t ocr = 0U, accept1V8 = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* application able to set the supported voltage window */
|
|
if ((card->ocr & SDIO_OCR_VOLTAGE_WINDOW_MASK) != 0U)
|
|
{
|
|
ocr = card->ocr & SDIO_OCR_VOLTAGE_WINDOW_MASK;
|
|
}
|
|
else
|
|
{
|
|
/* 3.3V voltage should be supported as default */
|
|
ocr |= SDMMC_MASK(kSD_OcrVdd29_30Flag) | SDMMC_MASK(kSD_OcrVdd32_33Flag) | SDMMC_MASK(kSD_OcrVdd33_34Flag);
|
|
}
|
|
|
|
if ((card->operationVoltage != kSDMMC_OperationVoltage180V) && (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 */
|
|
ocr |= SDMMC_MASK(kSD_OcrSwitch18RequestFlag);
|
|
|
|
/* reset to 3v3 signal voltage */
|
|
if (SDIO_SwitchIOVoltage(card, kSDMMC_OperationVoltage330V) == kStatus_Success)
|
|
{
|
|
/* Host changed the operation signal voltage successfully, then card need power reset */
|
|
SDIO_SetCardPower(card, false);
|
|
SDIO_SetCardPower(card, true);
|
|
}
|
|
}
|
|
|
|
/* send card active */
|
|
SDMMCHOST_SendCardActive(card->host);
|
|
|
|
do
|
|
{
|
|
/* card go idle */
|
|
if (kStatus_Success != SDIO_GoIdle(card))
|
|
{
|
|
return kStatus_SDMMC_GoIdleFailed;
|
|
}
|
|
|
|
/* Get IO OCR-CMD5 with arg0 ,set new voltage if needed*/
|
|
if (kStatus_Success != SDIO_SendOperationCondition(card, 0U, NULL))
|
|
{
|
|
return kStatus_SDMMC_HandShakeOperationConditionFailed;
|
|
}
|
|
|
|
if (kStatus_Success != SDIO_SendOperationCondition(card, ocr, &accept1V8))
|
|
{
|
|
return kStatus_SDMMC_InvalidVoltage;
|
|
}
|
|
|
|
/* check if card support 1.8V */
|
|
if ((accept1V8 & SDMMC_MASK(kSD_OcrSwitch18AcceptFlag)) != 0U)
|
|
{
|
|
if ((card->usrParam.ioVoltage != NULL) && (card->usrParam.ioVoltage->type == kSD_IOVoltageCtrlNotSupport))
|
|
{
|
|
break;
|
|
}
|
|
|
|
error = SDIO_SwitchVoltage(card, kSDMMC_OperationVoltage180V);
|
|
if (kStatus_SDMMC_SwitchVoltageFail == error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (error == kStatus_SDMMC_SwitchVoltage18VFail33VSuccess)
|
|
{
|
|
ocr &= ~SDMMC_MASK(kSD_OcrSwitch18RequestFlag);
|
|
error = kStatus_Success;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
card->operationVoltage = kSDMMC_OperationVoltage180V;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} while (true);
|
|
|
|
return error;
|
|
}
|
|
|
|
static status_t sdiocard_init(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
if (!card->isHostReady)
|
|
{
|
|
return kStatus_SDMMC_HostNotReady;
|
|
}
|
|
/* Identify mode ,set clock to 400KHZ. */
|
|
card->busClock_Hz = SDMMCHOST_SetCardClock(card->host, SDMMC_CLOCK_400KHZ);
|
|
SDMMCHOST_SetCardBusWidth(card->host, kSDMMC_BusWdith1Bit);
|
|
|
|
error = SDIO_ProbeBusVoltage(card);
|
|
if (error != kStatus_Success)
|
|
{
|
|
return kStatus_SDMMC_SwitchVoltageFail;
|
|
}
|
|
|
|
/* there is a memonly card */
|
|
if ((card->ioTotalNumber == 0U) && (card->memPresentFlag))
|
|
{
|
|
return kStatus_SDMMC_SDIO_InvalidCard;
|
|
}
|
|
|
|
/* send relative address ,cmd3*/
|
|
if (kStatus_Success != SDIO_SendRca(card))
|
|
{
|
|
return kStatus_SDMMC_SendRelativeAddressFailed;
|
|
}
|
|
/* select card cmd7 */
|
|
if (kStatus_Success != SDIO_SelectCard(card, true))
|
|
{
|
|
return kStatus_SDMMC_SelectCardFailed;
|
|
}
|
|
|
|
/* get card capability */
|
|
if (kStatus_Success != SDIO_GetCardCapability(card, kSDIO_FunctionNum0))
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* read common CIS here */
|
|
if (SDIO_ReadCIS(card, kSDIO_FunctionNum0, s_tupleList, SDIO_COMMON_CIS_TUPLE_NUM) != kStatus_Success)
|
|
{
|
|
return kStatus_SDMMC_SDIO_ReadCISFail;
|
|
}
|
|
|
|
/* switch data bus width */
|
|
if (kStatus_Success != SDIO_SetMaxDataBusWidth(card))
|
|
{
|
|
return kStatus_SDMMC_SetDataBusWidthFailed;
|
|
}
|
|
|
|
/* trying switch to card support timing mode. */
|
|
if (kStatus_Success != SDIO_SelectBusTiming(card))
|
|
{
|
|
return kStatus_SDMMC_SDIO_SwitchHighSpeedFail;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_CardInit(sdio_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);
|
|
|
|
SDIO_SetCardPower(card, true);
|
|
|
|
error = sdiocard_init(card);
|
|
|
|
(void)SDMMC_OSAMutexUnlock(&card->lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
void SDIO_CardDeinit(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
(void)SDMMC_OSAMutexLock(&card->lock, osaWaitForever_c);
|
|
(void)SDIO_CardReset(card);
|
|
(void)SDIO_SelectCard(card, false);
|
|
SDIO_SetCardPower(card, false);
|
|
(void)SDMMC_OSAMutexDestroy(&card->lock);
|
|
}
|
|
|
|
status_t SDIO_HostInit(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
if (!card->isHostReady)
|
|
{
|
|
if (SDMMCHOST_Init(card->host) != 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);
|
|
}
|
|
|
|
if (card->usrParam.sdioInt != NULL)
|
|
{
|
|
(void)SDMMCHOST_CardIntInit(card->host, card->usrParam.sdioInt);
|
|
}
|
|
|
|
/* set the host status flag, after the card re-plug in, don't need init host again */
|
|
card->isHostReady = true;
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
void SDIO_HostDeinit(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
SDMMCHOST_Deinit(card->host);
|
|
|
|
/* should re-init host */
|
|
card->isHostReady = false;
|
|
}
|
|
|
|
void SDIO_HostDoReset(sdio_card_t *card)
|
|
{
|
|
SDMMCHOST_Reset(card->host);
|
|
}
|
|
|
|
status_t SDIO_PollingCardInsert(sdio_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;
|
|
}
|
|
|
|
bool SDIO_IsCardPresent(sdio_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;
|
|
}
|
|
|
|
void SDIO_SetCardPower(sdio_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 ? SDIO_POWER_ON_DELAY : card->usrParam.powerOnDelayMS;
|
|
}
|
|
else
|
|
{
|
|
powerDelay = card->usrParam.powerOffDelayMS == 0U ? SDIO_POWER_OFF_DELAY : card->usrParam.powerOffDelayMS;
|
|
}
|
|
|
|
SDMMC_OSADelay(powerDelay);
|
|
}
|
|
|
|
status_t SDIO_Init(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
assert(card->host != NULL);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
if (!card->isHostReady)
|
|
{
|
|
if (SDIO_HostInit(card) != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_HostNotReady;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* reset the host */
|
|
SDIO_HostDoReset(card);
|
|
}
|
|
|
|
if (error == kStatus_Success)
|
|
{
|
|
/* card detect */
|
|
if (SDIO_PollingCardInsert(card, kSD_Inserted) != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_CardDetectFailed;
|
|
}
|
|
else
|
|
{
|
|
error = SDIO_CardInit(card);
|
|
if (error != kStatus_Success)
|
|
{
|
|
error = kStatus_SDMMC_CardInitFailed;
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void SDIO_Deinit(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
SDIO_CardDeinit(card);
|
|
SDIO_HostDeinit(card);
|
|
}
|
|
|
|
status_t SDIO_EnableIOInterrupt(sdio_card_t *card, sdio_func_num_t func, bool enable)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
uint8_t intEn = 0U;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* load io interrupt enable register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegIOIntEnable, 0U, &intEn, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
if (enable)
|
|
{
|
|
/* if already enable , do not need enable again */
|
|
if ((((intEn >> (uint32_t)func) & 0x01U) == 0x01U) && ((intEn & 0x01U) != 0U))
|
|
{
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/* enable the interrupt and interrupt master */
|
|
intEn |= (1U << (uint32_t)func) | 0x01U;
|
|
card->ioIntNums++;
|
|
}
|
|
else
|
|
{
|
|
/* if already disable , do not need enable again */
|
|
if (((intEn >> (uint32_t)func) & 0x01U) == 0x00U)
|
|
{
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/* disable the interrupt, don't disable the interrupt master here */
|
|
intEn &= ~(1U << (uint32_t)func);
|
|
if (card->ioIntNums != 0U)
|
|
{
|
|
card->ioIntNums--;
|
|
}
|
|
}
|
|
|
|
/* write to register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegIOIntEnable, intEn, &intEn, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_GetPendingInterrupt(sdio_card_t *card, uint8_t *pendingInt)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
status_t error = kStatus_Success;
|
|
|
|
/* load io interrupt enable register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegIOIntPending, 0U, pendingInt, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_EnableIO(sdio_card_t *card, sdio_func_num_t func, bool enable)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
assert(func != kSDIO_FunctionNum0);
|
|
|
|
uint8_t ioEn = 0U, ioReady = 0U;
|
|
volatile uint32_t i = SDIO_RETRY_TIMES;
|
|
uint32_t ioReadyTimeoutMS =
|
|
(uint32_t)card->funcCIS[(uint32_t)func - 1U].ioReadyTimeout * SDIO_IO_READY_TIMEOUT_UNIT;
|
|
status_t error = kStatus_Success;
|
|
|
|
if (ioReadyTimeoutMS != 0U)
|
|
{
|
|
/* do not poll the IO ready status, but use IO ready timeout */
|
|
i = 1U;
|
|
}
|
|
|
|
/* load io enable register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegIOEnable, 0U, &ioEn, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* if already enable/disable , do not need enable/disable again */
|
|
if (((ioEn >> (uint8_t)func) & 0x01U) == (enable ? 1U : 0U))
|
|
{
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/* enable the io */
|
|
if (enable)
|
|
{
|
|
ioEn |= (1U << (uint32_t)func);
|
|
}
|
|
else
|
|
{
|
|
ioEn &= ~(1U << (uint32_t)func);
|
|
}
|
|
|
|
/* write to register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegIOEnable, ioEn, &ioEn, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
/* if enable io, need check the IO ready status */
|
|
if (enable)
|
|
{
|
|
do
|
|
{
|
|
SDMMC_OSADelay(ioReadyTimeoutMS);
|
|
/* wait IO ready */
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IORead, kSDIO_FunctionNum0, kSDIO_RegIOReady, 0U, &ioReady, false);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
/* check if IO ready */
|
|
if ((ioReady & (1U << (uint32_t)func)) != 0U)
|
|
{
|
|
return kStatus_Success;
|
|
}
|
|
|
|
i--;
|
|
} while (i != 0U);
|
|
|
|
return kStatus_Fail;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_SelectIO(sdio_card_t *card, sdio_func_num_t func)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionMemory);
|
|
|
|
uint8_t ioSel = (uint8_t)func;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* write to register */
|
|
error =
|
|
SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegFunctionSelect, ioSel, &ioSel, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
status_t SDIO_AbortIO(sdio_card_t *card, sdio_func_num_t func)
|
|
{
|
|
assert(card != NULL);
|
|
assert(func <= kSDIO_FunctionNum7);
|
|
|
|
uint8_t ioAbort = (uint8_t)func;
|
|
status_t error = kStatus_Success;
|
|
|
|
/* write to register */
|
|
error = SDIO_IO_Access_Direct(card, kSDIO_IOWrite, kSDIO_FunctionNum0, kSDIO_RegIOAbort, ioAbort, &ioAbort, true);
|
|
if (kStatus_Success != error)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
void SDIO_SetIOIRQHandler(sdio_card_t *card, sdio_func_num_t func, sdio_io_irq_handler_t handler)
|
|
{
|
|
assert(card != NULL);
|
|
assert((func <= kSDIO_FunctionNum7) && (func != kSDIO_FunctionNum0));
|
|
|
|
card->ioIRQHandler[(uint32_t)func - 1U] = handler;
|
|
card->ioIntIndex = (uint8_t)func;
|
|
}
|
|
|
|
status_t SDIO_HandlePendingIOInterrupt(sdio_card_t *card)
|
|
{
|
|
assert(card != NULL);
|
|
|
|
uint8_t i = 0, pendingInt = 0;
|
|
|
|
/* call IRQ handler directly if one IRQ handler only */
|
|
if (card->ioIntNums == 1U)
|
|
{
|
|
if (card->ioIRQHandler[card->ioIntIndex - 1U] != NULL)
|
|
{
|
|
(card->ioIRQHandler[card->ioIntIndex - 1U])(card, card->ioIntIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* get pending int firstly */
|
|
if (SDIO_GetPendingInterrupt(card, &pendingInt) != kStatus_Success)
|
|
{
|
|
return kStatus_SDMMC_TransferFailed;
|
|
}
|
|
|
|
for (i = 1; i <= FSL_SDIO_MAX_IO_NUMS; i++)
|
|
{
|
|
if ((pendingInt & (1U << i)) != 0U)
|
|
{
|
|
if ((card->ioIRQHandler[i - 1U]) != NULL)
|
|
{
|
|
(card->ioIRQHandler[i - 1U])(card, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|