blob: 1e86ea46ee3e8f4476e2d28f09a978d979031051 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/** \addtogroup sdmmc_api
* @{
*/
/**
* \file
*
* Implementation of SD low level drivers.
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include "memories.h"
/*----------------------------------------------------------------------------
* Global variables
*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
* Local constants
*----------------------------------------------------------------------------*/
/** \addtogroup sdmmc_status_bm SD/MMC Status register constants
* @{*/
#define STATUS_APP_CMD (1UL << 5)
#define STATUS_SWITCH_ERROR (1UL << 7)
#define STATUS_READY_FOR_DATA (1UL << 8)
#define STATUS_IDLE (0UL << 9)
#define STATUS_READY (1UL << 9)
#define STATUS_IDENT (2UL << 9)
#define STATUS_STBY (3UL << 9)
#define STATUS_TRAN (4UL << 9)
#define STATUS_DATA (5UL << 9)
#define STATUS_RCV (6UL << 9)
#define STATUS_PRG (7UL << 9)
#define STATUS_DIS (8UL << 9)
#define STATUS_STATE (0xFUL << 9)
#define STATUS_ERASE_RESET (1UL << 13)
#define STATUS_WP_ERASE_SKIP (1UL << 15)
#define STATUS_CIDCSD_OVERWRITE (1UL << 16)
#define STATUS_OVERRUN (1UL << 17)
#define STATUS_UNERRUN (1UL << 18)
#define STATUS_ERROR (1UL << 19)
#define STATUS_CC_ERROR (1UL << 20)
#define STATUS_CARD_ECC_FAILED (1UL << 21)
#define STATUS_ILLEGAL_COMMAND (1UL << 22)
#define STATUS_COM_CRC_ERROR (1UL << 23)
#define STATUS_UN_LOCK_FAILED (1UL << 24)
#define STATUS_CARD_IS_LOCKED (1UL << 25)
#define STATUS_WP_VIOLATION (1UL << 26)
#define STATUS_ERASE_PARAM (1UL << 27)
#define STATUS_ERASE_SEQ_ERROR (1UL << 28)
#define STATUS_BLOCK_LEN_ERROR (1UL << 29)
#define STATUS_ADDRESS_MISALIGN (1UL << 30)
#define STATUS_ADDR_OUT_OR_RANGE (1UL << 31)
#define STATUS_STOP ((uint32_t)( STATUS_CARD_IS_LOCKED \
| STATUS_COM_CRC_ERROR \
| STATUS_ILLEGAL_COMMAND \
| STATUS_CC_ERROR \
| STATUS_ERROR \
| STATUS_STATE \
| STATUS_READY_FOR_DATA ))
#define STATUS_WRITE ((uint32_t)( STATUS_ADDR_OUT_OR_RANGE \
| STATUS_ADDRESS_MISALIGN \
| STATUS_BLOCK_LEN_ERROR \
| STATUS_WP_VIOLATION \
| STATUS_CARD_IS_LOCKED \
| STATUS_COM_CRC_ERROR \
| STATUS_ILLEGAL_COMMAND \
| STATUS_CC_ERROR \
| STATUS_ERROR \
| STATUS_ERASE_RESET \
| STATUS_STATE \
| STATUS_READY_FOR_DATA ))
#define STATUS_READ ((uint32_t)( STATUS_ADDR_OUT_OR_RANGE \
| STATUS_ADDRESS_MISALIGN \
| STATUS_BLOCK_LEN_ERROR \
| STATUS_CARD_IS_LOCKED \
| STATUS_COM_CRC_ERROR \
| STATUS_ILLEGAL_COMMAND \
| STATUS_CARD_ECC_FAILED \
| STATUS_CC_ERROR \
| STATUS_ERROR \
| STATUS_ERASE_RESET \
| STATUS_STATE \
| STATUS_READY_FOR_DATA ))
#define STATUS_SD_SWITCH ((uint32_t)( STATUS_ADDR_OUT_OR_RANGE \
| STATUS_CARD_IS_LOCKED \
| STATUS_COM_CRC_ERROR \
| STATUS_ILLEGAL_COMMAND \
| STATUS_CARD_ECC_FAILED \
| STATUS_CC_ERROR \
| STATUS_ERROR \
| STATUS_UNERRUN \
| STATUS_OVERRUN \
/*| STATUS_STATE*/))
#define STATUS_MMC_SWITCH ((uint32_t)( STATUS_CARD_IS_LOCKED \
| STATUS_COM_CRC_ERROR \
| STATUS_ILLEGAL_COMMAND \
| STATUS_CC_ERROR \
| STATUS_ERROR \
| STATUS_ERASE_RESET \
/*| STATUS_STATE*/ \
/*| STATUS_READY_FOR_DATA*/ \
| STATUS_SWITCH_ERROR ))
/** @}*/
/** \addtogroup sdio_status_bm SDIO Status definitions
* @{*/
/** The CRC check of the previous command failed. */
#define SDIO_COM_CRC_ERROR (1UL << 15)
/** Command not legal for the card state. */
#define SDIO_ILLEGAL_COMMAND (1UL << 14)
/** Status bits mask for SDIO R6 */
#define STATUS_SDIO_R6 (SDIO_COM_CRC_ERROR \
| SDIO_ILLEGAL_COMMAND \
| SDIO_R6_ERROR)
/** Status bits mask for SDIO R5 */
#define STATUS_SDIO_R5 (0/*SDIO_R5_STATE*/ \
| SDIO_R5_ERROR \
| SDIO_R5_FUNCN_ERROR \
| SDIO_R5_OUT_OF_RANGE)
/** @}*/
/*----------------------------------------------------------------------------
* Macros
*----------------------------------------------------------------------------*/
/** Return SD/MMC card address */
#define CARD_ADDR(pSd) (pSd->wAddress)
/** Return SD/MMC card block size (Default size now, 512B) */
#define BLOCK_SIZE(pSd) (pSd->wCurrBlockLen)
/** Convert block address to SD/MMC command parameter */
#define SD_ADDRESS(pSd, address) \
( ((pSd)->dwTotalSize == 0xFFFFFFFF) ? \
(address):((address) << 9) )
/** Check if SD Spec version 1.10 or later */
#define SD_IsVer1_10(pSd) \
( SD_SCR_SD_SPEC(pSd->SCR) >= SD_SCR_SD_SPEC_1_10 )
/** Check if SD card support HS mode (1.10 or later) */
#define SD_IsHsModeSupported(pSd) \
( /*SD_IsVer1_10(pSd)||*/(SD_CSD_STRUCTURE(pSd->CSD)>=1) )
/** Check if SD card support 4-bit mode (All SD card) */
#define SD_IsBusModeSupported(pSd) (1)
/** Check if MMC Spec version 4 */
#define MMC_IsVer4(pSd) ( MMC_CSD_SPEC_VERS(pSd->CSD) >= 4 )
/** Check if MMC CSD structure is 1.2 (3.1 or later) */
#define MMC_IsCSDVer1_2(pSd) \
( (SD_CSD_STRUCTURE(pSd->CSD)==2) \
||(SD_CSD_STRUCTURE(pSd->CSD)>2&&MMC_EXT_CSD_STRUCTURE(pSd->EXT)>=2) )
/** Check if MMC card support boot mode (4.3 or later) */
#define MMC_IsBootModeSupported(pSd) \
( (MMC_IsVer4(pSd)&&(eMMC_CID_CBX(pSd->CID)==0x01) )
/** Check if MMC card support 8-bit mode (4.0 or later) */
#define MMC_IsBusModeSupported(pSd) (MMC_IsVer4(pSd))
/** Check if MMC card support HS mode (4.0 or later) */
#define MMC_IsHsModeSupported(pSd) \
(MMC_IsCSDVer1_2(pSd)&&(MMC_EXT_CARD_TYPE(pSd->EXT)&0x2))
/*----------------------------------------------------------------------------
* Local variables
*----------------------------------------------------------------------------*/
/** SD/MMC transfer rate unit codes (10K) list */
static const uint32_t sdmmcTransUnits[7] = {
10, 100, 1000, 10000,
0, 0, 0
};
/** SD transfer multiplier factor codes (1/10) list */
static const uint32_t sdTransMultipliers[16] = {
0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
};
/** MMC transfer multiplier factor codes (1/10) list */
static const uint32_t mmcTransMultipliers[16] = {
0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
};
/*----------------------------------------------------------------------------
* Local functions
*----------------------------------------------------------------------------*/
/**
* Delay some loop
*/
static void Delay(volatile unsigned int loop)
{
for(;loop > 0; loop --);
}
/**
*/
static void _ResetCmd(sSdmmcCommand *pCmd)
{
memset(pCmd, 0, sizeof(sSdmmcCommand));
}
/**
*/
static uint8_t _SendCmd(sSdCard *pSd, fSdmmcCallback fCallback, void* pCbArg)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint8_t bRc;
pCmd->fCallback = fCallback;
pCmd->pArg = pCbArg;
bRc = pHal->fCommand(pSd->pDrv, pCmd);
if (fCallback == NULL)
{
uint32_t busy = 1;
for (; busy;)
{
pHal->fIOCtrl(pDrv, SDMMC_IOCTL_BUSY_CHECK, (uint32_t)&busy);
}
return pCmd->bStatus;
}
return bRc;
}
/**
*/
static uint8_t _HwGetBusWidth(sSdCard *pSd)
{
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint32_t busWidth = 1;
pHal->fIOCtrl(pDrv, SDMMC_IOCTL_GET_BUSMODE, (uint32_t)&busWidth);
return busWidth;
}
/**
*/
static uint8_t _HwSetBusWidth(sSdCard *pSd, uint8_t newWidth)
{
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint32_t busWidth = newWidth;
uint32_t rc;
rc = pHal->fIOCtrl(pDrv, SDMMC_IOCTL_SET_BUSMODE, (uint32_t)&busWidth);
return rc;
}
/**
*/
static uint8_t _HwGetHsMode(sSdCard *pSd)
{
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint32_t hsMode = 0;
pHal->fIOCtrl(pDrv, SDMMC_IOCTL_GET_HSMODE, (uint32_t)&hsMode);
return hsMode;
}
/**
*/
static uint8_t _HwSetHsMode(sSdCard *pSd, uint8_t newHsMode)
{
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint32_t hsMode = newHsMode;
uint32_t rc;
rc = pHal->fIOCtrl(pDrv, SDMMC_IOCTL_SET_HSMODE, (uint32_t)&hsMode);
return rc;
}
/**
*/
static uint8_t _HwSetClock(sSdCard *pSd, uint32_t *pIoValClk)
{
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint32_t dwClock = *pIoValClk;
uint32_t rc;
rc = pHal->fIOCtrl(pDrv, SDMMC_IOCTL_SET_CLOCK, (uint32_t)&dwClock);
if (rc == SDMMC_SUCCESS || rc == SDMMC_CHANGED)
{
*pIoValClk = dwClock;
}
return rc;
}
/**
*/
static uint8_t _HwReset(sSdCard *pSd)
{
sSdHalFunctions *pHal = pSd->pHalf;
void *pDrv = pSd->pDrv;
uint32_t rc;
rc = pHal->fIOCtrl(pDrv, SDMMC_IOCTL_RESET, 0);
return rc;
}
/**
* Find SDIO ManfID, Fun0 tuple.
* \param pSd Pointer to \ref sSdCard instance.
* \param address Search area start address.
* \param size Search area size.
* \param pAddrManfID Pointer to ManfID address value buffer.
* \param pAddrFunc0 Pointer to Func0 address value buffer.
*/
static uint8_t SdioFindTuples(sSdCard *pSd,
uint32_t address, uint32_t size,
uint32_t *pAddrManfID,
uint32_t *pAddrFunc0)
{
uint8_t error, tmp[3];
uint32_t addr = address;
uint8_t flagFound = 0; /* 1:Manf, 2:Func0 */
uint32_t addManfID = 0, addFunc0 = 0;
for(;flagFound != 3;) {
error = SDIO_ReadDirect(pSd, SDIO_CIA, addr, tmp, 3);
if (error) {
TRACE_ERROR("SdioFindTuples.RdDirect: %d\n\r", error);
return error;
}
/* End */
if (tmp[0] == CISTPL_END) break;
/* ManfID */
else if (tmp[0] == CISTPL_MANFID) {
flagFound |= 1; addManfID = addr;
}
/* Func0 */
else if (tmp[0] == CISTPL_FUNCE && tmp[2] == 0x00) {
flagFound |= 2; addFunc0 = addr;
}
/* Tuple error ? */
if (tmp[1] == 0) break;
/* Next address */
addr += (tmp[1] + 2);
if (addr > (address + size)) break;
}
if (pAddrManfID) *pAddrManfID = addManfID;
if (pAddrFunc0) *pAddrFunc0 = addFunc0;
return 0;
}
/**
* \brief Decode Trans Speed Value
* \param code The trans speed code value.
* \param unitCodes Unit list in 10K, 0 as unused value.
* \param multiCodes Multiplier list in 1/10, index 1 ~ 15 is valid.
*/
static uint32_t SdmmcDecodeTransSpeed(uint32_t code,
const uint32_t *unitCodes,
const uint32_t *multiCodes)
{
uint32_t speed;
uint8_t unitI, mulI;
/* Unit code is valid ? */
unitI = (code & 0x7);
if (unitCodes[unitI] == 0) return 0;
/* Multi code is valid ? */
mulI = (code >> 3) & 0xF;
if (multiCodes[mulI] == 0) return 0;
speed = unitCodes[unitI] * multiCodes[mulI];
return speed;
}
/**
* Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp
* up time.
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to \ref sSdCard instance.
*/
static inline uint8_t Pon(sSdCard *pSd)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("PwrOn()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_POWERONINIT;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Resets all cards to idle state
* \param pSd Pointer to \ref sSdCard instance.
* \param arg Argument used.
* \return the command transfer result (see SendMciCommand).
*/
static inline uint8_t Cmd0(sSdCard *pSd, uint8_t arg)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd0()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(0);
pCmd->dwArg = arg;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* MMC send operation condition command.
* Sends host capacity support information and activates the card's
* initialization process.
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to \ref sSdCard instance.
* \param pOCR Pointer to fill OCR value to send and get.
*/
static inline uint8_t Cmd1(sSdCard *pSd, uint8_t* pHd)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
uint32_t dwArg;
TRACE_DEBUG("Cmd1()\n\r");
_ResetCmd(pCmd);
dwArg = SD_HOST_VOLTAGE_RANGE | MMC_OCR_ACCESS_SECTOR;
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(3)
| SDMMC_CMD_bmOD;
pCmd->bCmd = 1;
pCmd->dwArg = dwArg;
pCmd->pResp = &dwArg;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
/* Check result */
if (bRc) return bRc;
if (dwArg & SD_OCR_BUSY)
{
*pHd = 0;
if ((dwArg & MMC_OCR_ACCESS_MODE) == MMC_OCR_ACCESS_SECTOR)
{
*pHd = 1;
}
return 0;
}
return SDMMC_ERROR_NOT_INITIALIZED;
}
/**
* Asks any card to send the CID numbers
* on the CMD line (any card that is
* connected to the host will respond)
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param pCID Pointer to buffer for storing the CID numbers.
*/
static inline uint8_t Cmd2(sSdCard *pSd)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd2()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(2)
| SDMMC_CMD_bmOD;
pCmd->bCmd = 2;
pCmd->pResp = pSd->CID;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Ask the SD card to publish a new relative address (RCA)
* or
* Assign relative address to the MMC card
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param pRsp Pointer to buffer to fill response (address on 31:16).
*/
static uint8_t Cmd3(sSdCard *pSd)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint32_t dwResp;
uint8_t bRc;
TRACE_DEBUG("Cmd3()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(6)
| SDMMC_CMD_bmOD;
pCmd->bCmd = 3;
pCmd->pResp = &dwResp;
if (pSd->bCardType == CARD_MMC || pSd->bCardType == CARD_MMCHD)
{
uint16_t wNewAddr = CARD_ADDR(pSd) + 1;
if (wNewAddr == 0) wNewAddr ++;
pCmd->dwArg = wNewAddr << 16;
bRc = _SendCmd(pSd, NULL, NULL);
if (bRc == SDMMC_OK)
{
CARD_ADDR(pSd) = wNewAddr;
}
}
else
{
bRc = _SendCmd(pSd, NULL, NULL);
if (bRc == SDMMC_OK)
{
CARD_ADDR(pSd) = dwResp >> 16;
}
}
return bRc;
}
/**
* SDIO SEND OPERATION CONDITION (OCR) command.
* Sends host capacity support information and acrivates the card's
* initialization process.
* \return The command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD/MMC card driver instance.
* \param pIo Pointer to data sent as well as response buffer (32bit).
*/
static uint8_t Cmd5(sSdCard *pSd, uint32_t *pIo)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd5()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(4)
| SDMMC_CMD_bmOD;
pCmd->bCmd = 5;
pCmd->dwArg = *pIo;
pCmd->pResp = pIo;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Switches the mode of operation of the selected card.
* CMD6 is valid under the "trans" state.
* \return The command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD/MMC card driver instance.
* \param pSwitchArg Pointer to a MmcCmd6Arg instance.
* \param pStatus Pointer to where the 512bit status is returned.
* \param pResp Pointer to where the response is returned.
*/
static uint8_t SdCmd6(sSdCard *pSd,
const void * pSwitchArg,
uint32_t * pStatus,
uint32_t * pResp)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
SdCmd6Arg * pSdSwitch;
TRACE_DEBUG("Cmd6()\n\r");
_ResetCmd(pCmd);
pSdSwitch = (SdCmd6Arg*)pSwitchArg;
pCmd->bCmd = 6;
pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
pCmd->dwArg = (pSdSwitch->mode << 31)
| (pSdSwitch->reserved << 30)
| (pSdSwitch->reserveFG6 << 20)
| (pSdSwitch->reserveFG5 << 16)
| (pSdSwitch->reserveFG4 << 12)
| (pSdSwitch->reserveFG3 << 8)
| (pSdSwitch->command << 4)
| (pSdSwitch->accessMode << 0);
if (pStatus) {
pCmd->wBlockSize = 512/8;
pCmd->wNbBlocks = 1;
pCmd->pData = (uint8_t*)pStatus;
}
pCmd->pResp = pResp;
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Switches the mode of operation of the selected card or
* Modifies the EXT_CSD registers.
* CMD6 is valid under the "trans" state.
* \return The command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD/MMC card driver instance.
* \param pSwitchArg Pointer to a MmcCmd6Arg instance.
* \param pResp Pointer to where the response is returned.
*/
static uint8_t MmcCmd6(sSdCard *pSd,
const void * pSwitchArg,
uint32_t * pResp)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
MmcCmd6Arg * pMmcSwitch;
TRACE_DEBUG("Cmd6()\n\r");
_ResetCmd(pCmd);
pMmcSwitch = (MmcCmd6Arg*)pSwitchArg;
pCmd->bCmd = 6;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1);
pCmd->dwArg = (pMmcSwitch->access << 24)
| (pMmcSwitch->index << 16)
| (pMmcSwitch->value << 8)
| (pMmcSwitch->cmdSet << 0);
pCmd->pResp = pResp;
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Command toggles a card between the
* stand-by and transfer states or between
* the programming and disconnect states.
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param cardAddr Relative Card Address (0 deselects all).
*/
static uint8_t Cmd7(sSdCard *pSd, uint16_t address)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd7()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(address ? 1 : 0);
pCmd->bCmd = 7;
pCmd->dwArg = address << 16;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Sends SD Memory Card interface condition, which includes host supply
* voltage information and asks the card whether card supports voltage.
* Should be performed at initialization time to detect the card type.
* \param pSd Pointer to a SD card driver instance.
* \param supplyVoltage Expected supply voltage(SD).
* \return 0 if successful;
* otherwise returns SD_ERROR_NORESPONSE if the card did not answer
* the command, or SDMMC_ERROR.
*/
static uint8_t SdCmd8(sSdCard *pSd,
uint8_t supplyVoltage)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint32_t dwResp;
uint8_t bRc;
TRACE_DEBUG("Cmd8()\n\r");
_ResetCmd(pCmd);
/* Fill command information */
pCmd->bCmd = 8;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(7)
| SDMMC_CMD_bmOD;
pCmd->dwArg = (supplyVoltage << 8) | (0xAA);
pCmd->pResp = &dwResp;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
/* Check result */
if (bRc == SDMMC_ERROR_NORESPONSE) {
return SDMMC_ERROR_NORESPONSE;
}
/* SD_R7
* Bit 0 - 7: check pattern (echo-back)
* Bit 8 -11: voltage accepted
*/
else if (!bRc &&
((dwResp & 0x00000FFF) == ((uint32_t)(supplyVoltage << 8) | 0xAA))) {
return 0;
}
else {
return SDMMC_ERROR;
}
}
/**
* SEND_EXT_CSD, to get EXT_CSD register as a block of data.
* Valid under "trans" state.
* \param pSd Pointer to a SD card driver instance.
* \param pEXT 512 byte buffer pointer for EXT_CSD data.
* \return 0 if successful;
* otherwise returns SD_ERROR_NORESPONSE if the card did not answer
* the command, or SDMMC_ERROR.
*/
static uint8_t MmcCmd8(sSdCard *pSd,
uint8_t* pEXT)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd8()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->bCmd = 8;
pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
pCmd->wBlockSize = 512;
pCmd->wNbBlocks = 1;
pCmd->pData = pEXT;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Addressed card sends its card-specific
* data (CSD) on the CMD line.
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param cardAddr Card Relative Address.
* \param pCSD Pointer to buffer for CSD data.
*/
static uint8_t Cmd9(sSdCard *pSd)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd9()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(2);
pCmd->bCmd = 9;
pCmd->dwArg = CARD_ADDR(pSd) << 16;
pCmd->pResp = pSd->CSD;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Forces the card to stop transmission
* \param pSd Pointer to a SD card driver instance.
* \param pStatus Pointer to a status variable.
*/
static uint8_t Cmd12(sSdCard *pSd, uint32_t *pStatus)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd12()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->bCmd = 12;
pCmd->cmdOp.wVal = SDMMC_CMD_CSTOP
| SDMMC_CMD_bmBUSY;
pCmd->pResp = pStatus;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Addressed card sends its status register.
* Returns the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param pStatus Pointer to a status variable.
*/
static uint8_t Cmd13(sSdCard *pSd, uint32_t *pStatus)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd13()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->bCmd = 13;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1);
pCmd->dwArg = CARD_ADDR(pSd) << 16;
pCmd->pResp = pStatus;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* In the case of a Standard Capacity SD Memory Card, this command sets the
* block length (in bytes) for all following block commands
* (read, write, lock).
* Default block length is fixed to 512 Bytes.
* Set length is valid for memory access commands only if partial block read
* operation are allowed in CSD.
* In the case of a High Capacity SD Memory Card, block length set by CMD16
* command does not affect the memory read and write commands. Always 512
* Bytes fixed block length is used. This command is effective for LOCK_UNLOCK
* command. In both cases, if block length is set larger than 512Bytes, the
* card sets the BLOCK_LEN_ERROR bit.
* \param pSd Pointer to a SD card driver instance.
* \param blockLength Block length in bytes.
*/
static uint8_t Cmd16(sSdCard *pSd, uint16_t blkLen)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd16()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1);
pCmd->bCmd = 16;
pCmd->dwArg = blkLen;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Read single block command
* \param pSd Pointer to a SD card driver instance.
* \param pData Pointer to the DW aligned buffer to be filled.
* \param address Data Address on SD/MMC card.
* \param pStatus Pointer response buffer as status return.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* Callback argument is fixed to a pointer to sSdCard instance.
*/
static uint8_t Cmd17(sSdCard *pSd,
uint8_t *pData,
uint32_t address,
uint32_t *pStatus,
fSdmmcCallback callback)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd17()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
pCmd->bCmd = 17;
pCmd->dwArg = address;
pCmd->pResp = pStatus;
pCmd->wBlockSize = BLOCK_SIZE(pSd);
pCmd->wNbBlocks = 1;
pCmd->pData = pData;
pCmd->fCallback = callback;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Continously transfers datablocks from card to host until interrupted by a
* STOP_TRANSMISSION command.
* \param pSd Pointer to a SD card driver instance.
* \param nbBlocks Number of blocks to send.
* \param pData Pointer to the DW aligned buffer to be filled.
* \param address Data Address on SD/MMC card.
* \param pStatus Pointer to the response status.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* Callback argument is fixed to a pointer to sSdCard instance.
*/
static uint8_t Cmd18(sSdCard *pSd,
uint16_t nbBlock,
uint8_t *pData,
uint32_t address,
uint32_t *pStatus,
fSdmmcCallback callback)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd18()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
pCmd->bCmd = 18;
pCmd->dwArg = address;
pCmd->pResp = pStatus;
pCmd->wBlockSize = BLOCK_SIZE(pSd);
pCmd->wNbBlocks = nbBlock;
pCmd->pData = pData;
pCmd->fCallback = callback;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Write single block command
* \param pSd Pointer to a SD card driver instance.
* \param blockSize Block size (shall be set to 512 in case of high capacity).
* \param pData Pointer to the DW aligned buffer to be filled.
* \param address Data Address on SD/MMC card.
* \param pStatus Pointer to response buffer as status.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* Callback argument is fixed to a pointer to sSdCard instance.
*/
static inline uint8_t Cmd24(sSdCard *pSd,
uint8_t *pData,
uint32_t address,
uint32_t *pStatus,
fSdmmcCallback callback)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd24()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CDATATX(1);
pCmd->bCmd = 24;
pCmd->dwArg = address;
pCmd->pResp = pStatus;
pCmd->wBlockSize = BLOCK_SIZE(pSd);
pCmd->wNbBlocks = 1;
pCmd->pData = pData;
pCmd->fCallback = callback;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Write multiple block command
* \param pSd Pointer to a SD card driver instance.
* \param blockSize Block size (shall be set to 512 in case of high capacity).
* \param nbBlock Number of blocks to send.
* \param pData Pointer to the DW aligned buffer to be filled.
* \param address Data Address on SD/MMC card.
* \param pStatus Pointer to the response buffer as status.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* Callback argument is fixed to a pointer to sSdCard instance.
*/
static uint8_t Cmd25(sSdCard *pSd,
uint16_t nbBlock,
uint8_t *pData,
uint32_t address,
uint32_t *pStatus,
fSdmmcCallback callback)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG("Cmd25()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CDATATX(1);
pCmd->bCmd = 25;
pCmd->dwArg = address;
pCmd->pResp = pStatus;
pCmd->wBlockSize = BLOCK_SIZE(pSd);
pCmd->wNbBlocks = nbBlock;
pCmd->pData = pData;
pCmd->fCallback = callback;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* SDIO IO_RW_DIRECT command, response R5.
* \return the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param pIoData Pointer to input argument (\ref SdioRwDirectArg) and
* response (\ref SdmmcR5) buffer.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* Callback argument is fixed to a pointer to sSdCard instance.
*/
static uint8_t Cmd52(sSdCard *pSd,
uint8_t wrFlag,
uint8_t funcNb,
uint8_t rdAfterWr,
uint32_t addr,
uint32_t *pIoData)
{
SdioCmd52Arg *pArg52 = (SdioCmd52Arg*)pIoData;
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
pArg52->rwFlag = wrFlag;
pArg52->functionNum = funcNb;
pArg52->rawFlag = rdAfterWr;
pArg52->regAddress = addr;
TRACE_DEBUG("Cmd52()\n\r");
_ResetCmd(pCmd);
/* Fill command */
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(5);
pCmd->bCmd = 52;
pCmd->dwArg = *pIoData;
pCmd->pResp = pIoData;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* SDIO IO_RW_EXTENDED command, response R5.
* \param pSd Pointer to a SD card driver instance.
* \param pArgResp Pointer to input argument (\ref SdioRwExtArg)
* and response (\ref SdmmcR5) buffer.
* \param pData Pointer to data buffer to transfer.
* \param size Transfer data size.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* Callback argument is fixed to a pointer to sSdCard instance.
* \param pArg Callback argument.
*/
static uint8_t Cmd53(sSdCard *pSd,
uint8_t wrFlag,
uint8_t funcNb,
uint8_t blockMode,
uint8_t incAddr,
uint32_t addr,
uint8_t *pIoData,
uint16_t len,
uint32_t *pArgResp,
fSdmmcCallback fCallback,
void* pCbArg)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
SdioCmd53Arg* pArg53;
pArg53 = (SdioCmd53Arg*)pArgResp;
pArg53->rwFlag = wrFlag;
pArg53->functionNum = funcNb;
pArg53->blockMode = blockMode;
pArg53->opCode = incAddr;
pArg53->regAddress = addr;
pArg53->count = len;
/* Fill command */
pCmd->bCmd = 53;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(5)
| SDMMC_CMD_bmIO;
if (wrFlag)
{
pCmd->cmdOp.wVal |= SDMMC_CMD_bmDATATX;
}
else
{
pCmd->cmdOp.wVal |= SDMMC_CMD_bmDATARX;
}
if (blockMode)
{
pCmd->wBlockSize = pSd->wCurrBlockLen;
}
else
{
pCmd->wBlockSize = 1;
}
pCmd->dwArg = *pArgResp;
pCmd->pResp = pArgResp;
pCmd->pData = pIoData;
pCmd->wNbBlocks = len;
pCmd->fCallback = fCallback;
pCmd->pArg = pCbArg;
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Indicates to the card that the next command is an application specific
* command rather than a standard command.
* \return the command transfer result (see SendMciCommand).
* \param pSd Pointer to a SD card driver instance.
* \param cardAddr Card Relative Address.
*/
static uint8_t Cmd55(sSdCard *pSd, uint16_t cardAddr)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint32_t dwResp;
uint8_t bRc;
TRACE_DEBUG( "Cmd55()\n\r" ) ;
_ResetCmd(pCmd);
/* Fill command information */
pCmd->bCmd = 55;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1)
| (cardAddr ? 0 : SDMMC_CMD_bmOD);
pCmd->dwArg = cardAddr << 16;
pCmd->pResp = &dwResp;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Defines the data bus width (00=1bit or 10=4 bits bus) to be used for data
* transfer.
* The allowed data bus widths are given in SCR register.
* Should be invoked after SdmmcCmd55().
* \param pSd Pointer to a SD card driver instance.
* \param arg Argument for this command.
* \param pStatus Pointer to buffer for command response as status.
* \return the command transfer result (see SendMciCommand).
*/
static uint8_t SdAcmd6(sSdCard *pSd,
uint32_t arg,
uint32_t *pStatus)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG( "Acmd6()\n\r" ) ;
_ResetCmd(pCmd);
/* Fill command information */
pCmd->bCmd = 6;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1);
pCmd->pResp = pStatus;
pCmd->dwArg = arg;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Defines the data bus width (00=1bit or 10=4 bits bus) to be used for data
* transfer.
* The allowed data bus widths are given in SCR register.
* \param pSd Pointer to a SD card driver instance.
* \param busWidth Bus width in bits (4 or 1).
* \return the command transfer result (see SendCommand).
*/
static uint8_t Acmd6(sSdCard *pSd, uint8_t busWidth)
{
uint8_t error;
uint32_t arg;
error = Cmd55(pSd, CARD_ADDR(pSd));
if (error)
{
TRACE_ERROR("Acmd6.cmd55:%d\n\r", error);
return error;
}
arg = (busWidth == 4)
? SD_ST_DATA_BUS_WIDTH_4BIT : SD_ST_DATA_BUS_WIDTH_1BIT;
return SdAcmd6(pSd, arg, NULL);
}
#ifdef SDCMD13
/**
* The SD Status contains status bits that are related to the SD memory Card
* proprietary features and may be used for future application-specific usage.
* Can be sent to a card only in 'tran_state'.
* Should be invoked after SdmmcCmd55().
* \param pSd Pointer to a SD card driver instance.
* \param pSdSTAT Pointer to buffer for SD STATUS data.
* \return the command transfer result (see SendMciCommand).
*/
static uint8_t SdAcmd13(sSdCard *pSd,
uint32_t * pSdSTAT)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint32_t dwResp;
uint8_t bRc;
TRACE_DEBUG( "Acmd13()\n\r" ) ;
_ResetCmd(pCmd);
/* Fill command information */
pCmd->bCmd = 13;
pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
pCmd->pResp = &dwResp;
pCmd->pData = (uint8_t*)pSdSTAT;
pCmd->wBlockSize = 512/8;
pCmd->wNbBlocks = 1;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* The SD Status contains status bits that are related to the SD memory Card
* proprietary features and may be used for future application-specific usage.
* Can be sent to a card only in 'tran_state'.
*/
static uint8_t Acmd13(sSdCard *pSd, uint32_t *pSdSTAT)
{
uint8_t error;
error = Cmd55(pSd, CARD_ADDR(pSd));
if (error) {
TRACE_ERROR("Acmd13.cmd55:%d\n\r", error);
return error;
}
return SdAcmd13(pSd, pSdSTAT);
}
#endif
/**
* Asks to all cards to send their operations conditions.
* Returns the command transfer result (see SendMciCommand).
* Should be invoked after SdmmcCmd55().
* \param pSd Pointer to a SD card driver instance.
* \param pOpIo Pointer to argument that should be sent and OCR content.
*/
static uint8_t SdAcmd41(sSdCard *pSd, uint32_t* pOpIo)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG( "Acmd41()\n\r" ) ;
_ResetCmd(pCmd);
/* Fill command information */
pCmd->bCmd = 41;
pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(3);
pCmd->dwArg = *pOpIo;
pCmd->pResp = pOpIo;
/* Send command */
bRc = _SendCmd(pSd, NULL, NULL);
return bRc;
}
/**
* Asks to all cards to send their operations conditions.
* Returns the command transfer result (see SendCommand).
* \param pSd Pointer to a SD card driver instance.
* \param hcs Shall be true if Host support High capacity.
* \param pCCS Set the pointed flag to 1 if hcs != 0 and SD OCR CCS flag is set.
*/
static uint8_t Acmd41(sSdCard *pSd, uint8_t hcs, uint8_t *pCCS)
{
uint8_t error;
uint32_t arg;
do {
error = Cmd55(pSd, 0);
if (error) {
TRACE_ERROR("Acmd41.cmd55:%d\n\r", error);
return error;
}
arg = SD_HOST_VOLTAGE_RANGE;
if (hcs) arg |= SD_OCR_CCS;
error = SdAcmd41(pSd, &arg);
if (error) {
TRACE_ERROR("Acmd41.cmd41:%d\n\r", error);
return error;
}
*pCCS = ((arg & SD_OCR_CCS)!=0);
} while ((arg & SD_OCR_BUSY) != SD_OCR_BUSY);
return 0;
}
/**
* Continue to transfer datablocks from card to host until interrupted by a
* STOP_TRANSMISSION command.
* \param pSd Pointer to a SD card driver instance.
* \param blockSize Block size (shall be set to 512 in case of high capacity).
* \param nbBlock Number of blocks to send.
* \param pData Pointer to the application buffer to be filled.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* \param pArg Callback argument.
*/
static uint8_t SdmmcRead(sSdCard *pSd,
uint16_t blockSize,
uint16_t nbBlock,
uint8_t *pData,
fSdmmcCallback fCallback,
void* pArg)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG( "Read()\n\r" ) ;
_ResetCmd(pCmd);
/* Fill command information */
pCmd->cmdOp.wVal = SDMMC_CMD_DATARX;
pCmd->wBlockSize = blockSize;
pCmd->wNbBlocks = nbBlock;
pCmd->pData = pData;
/* Send command */
bRc = _SendCmd(pSd, fCallback, pArg);
return bRc;
}
/**
* Continue to transfer datablocks from host to card until interrupted by a
* STOP_TRANSMISSION command.
* \param pSd Pointer to a SD card driver instance.
* \param blockSize Block size (shall be set to 512 in case of high capacity).
* \param nbBlock Number of blocks to send.
* \param pData Pointer to the application buffer to be filled.
* \param fCallback Pointer to optional callback invoked on command end.
* NULL: Function return until command finished.
* Pointer: Return immediately and invoke callback at end.
* \param pArg Callback argument.
*/
static uint8_t SdmmcWrite(sSdCard *pSd,
uint16_t blockSize,
uint16_t nbBlock,
const uint8_t *pData,
fSdmmcCallback fCallback,
void* pArg)
{
sSdmmcCommand *pCmd = &pSd->sdCmd;
uint8_t bRc;
TRACE_DEBUG( "Write()\n\r" ) ;
_ResetCmd(pCmd);
/* Fill command information */
pCmd->cmdOp.wVal = SDMMC_CMD_DATATX;
pCmd->wBlockSize = blockSize;
pCmd->wNbBlocks = nbBlock;
pCmd->pData = (uint8_t*)pData;
/* Send command */
bRc = _SendCmd(pSd, fCallback, pArg);
return bRc;
}
/**
* Try SW Reset several times (CMD0 with ARG 0)
* \param pSd Pointer to a SD card driver instance.
* \param retry Retry times.
* \return 0 or MCI error code.
*/
static uint8_t SwReset(sSdCard *pSd, uint32_t retry)
{
uint32_t i;
uint8_t error = 0;
for (i = 0; i < retry; i ++) {
error = Cmd0(pSd, 0);
if (error != SDMMC_ERROR_NORESPONSE)
break;
}
return error;
}
#if 1
#if 1
/**
* Stop TX/RX
*/
static uint8_t _StopCmd(sSdCard *pSd)
{
uint32_t status, state;
uint32_t i, j;
uint8_t error = 0;
/* Retry stop for 9 times */
for (i = 0; i < 9; i ++)
{
error = Cmd12(pSd, &status);
if (error)
{
TRACE_ERROR("_StopC.Cmd12: st%x, er%d\n\r", pSd->bState, error);
return error;
}
/* Check status for 299 times */
for (j = 0; j < 299; j ++)
{
error = Cmd13(pSd, &status);
if (error)
{
TRACE_ERROR("_StopC.Cmd13: st%x, er%d\n\r", pSd->bState, error);
return error;
}
state = status & STATUS_STATE;
/* Invalid state */
if( state == STATUS_IDLE
|| state == STATUS_READY
|| state == STATUS_IDENT
|| state == STATUS_STBY
|| state == STATUS_DIS )
{
TRACE_ERROR("_StopC.state\n\r");
return SDMMC_ERROR_NOT_INITIALIZED;
}
/* Ready? */
if ( (status & STATUS_READY_FOR_DATA) == STATUS_READY_FOR_DATA
&& state == STATUS_TRAN )
return SDMMC_SUCCESS;
}
/* For write, try stop command again */
if (state != STATUS_RCV)
break;
}
return SDMMC_ERROR_STATE;
}
#else
/**
* Wait for TX ready
*/
static uint8_t _WaitTxReady(sSdCard *pSd)
{
uint8_t error;
uint32_t status;
do
{
error = Cmd13(pSd, &status);
if (error)
{
TRACE_ERROR("SingleTx.WR.Cmd13: %d\n\r", error);
return error;
}
}
while ((status & STATUS_READY_FOR_DATA) == 0);
return SDMMC_SUCCESS;
}
/**
* Wait for RX ready
*/
static uint8_t _WaitRxReady(sSdCard *pSd)
{
uint32_t status;
uint8_t error;
/* Wait for card to be ready for data transfers */
do
{
error = Cmd13(pSd, &status);
if (error)
{
TRACE_ERROR("SingleTx.RD.Cmd13: %d\n\r", error);
return error;
}
if( ((status & STATUS_STATE) == STATUS_IDLE)
||((status & STATUS_STATE) == STATUS_READY)
||((status & STATUS_STATE) == STATUS_IDENT))
{
TRACE_ERROR("SingleTx.mode\n\r");
return SDMMC_ERROR_NOT_INITIALIZED;
}
/* If the card is in sending data state or
in receivce data state */
if ( ((status & STATUS_STATE) == STATUS_RCV) ||((status & STATUS_STATE) == STATUS_DATA) )
{
TRACE_DEBUG("SingleTx.state = 0x%X\n\r",
(status & STATUS_STATE) >> 9);
}
}
while ( ((status & STATUS_READY_FOR_DATA) == 0) || ((status & STATUS_STATE) != STATUS_TRAN) ) ;
return SDMMC_SUCCESS;
}
#endif
#endif
/**
* Perform sligle block transfer
*/
static uint8_t PerformSingleTransfer(sSdCard *pSd,
uint32_t address,
uint8_t *pData,
uint8_t isRead)
{
uint32_t status;
uint8_t error = 0;
/* Reset transfer state if previous in multi- mode */
if( (pSd->bState == SDMMC_STATE_DATA_RD)
|| (pSd->bState == SDMMC_STATE_DATA_WR)) {
/* Stop transfer */
#if 1
error = _StopCmd(pSd);
if (error) return error;
#else
error = Cmd12(pSd, &status);
if (error)
{
TRACE_ERROR("SingleTx.Cmd12: st%x, er%d\n\r", pSd->bState, error);
}
#if 0
else if (pSd->bState == SDMMC_STATE_DATA_WR)
{
if (isRead) _WaitRxReady(pSd);
else _WaitTxReady(pSd);
}
#endif
#endif
pSd->bState = SDMMC_STATE_TRAN;
pSd->dwPrevBlk = 0xFFFFFFFF;
}
if ( isRead )
{
/* Read single block */
error = Cmd17( pSd, pData, SD_ADDRESS(pSd,address), &status, NULL ) ;
if ( error )
{
TRACE_ERROR("SingleTx.Cmd17: %d\n\r", error);
return error;
}
if (status & ~(STATUS_READY_FOR_DATA | STATUS_STATE))
{
TRACE_ERROR("CMD17.stat: %lx\n\r",
status & ~(STATUS_READY_FOR_DATA | STATUS_STATE));
return SDMMC_ERROR;
}
return error;
}
/* Write */
{
/* Move to Sending data state */
error = Cmd24(pSd,
pData, SD_ADDRESS(pSd,address),
&status,
NULL);
if (error)
{
TRACE_DEBUG("SingleTx.Cmd24: %d\n\r", error);
return error;
}
if (status & (STATUS_WRITE & ~(STATUS_READY_FOR_DATA | STATUS_STATE)))
{
TRACE_ERROR("CMD24(0x%x).stat: %lx\n\r",
SD_ADDRESS(pSd,address),
status & (STATUS_WRITE
& ~(STATUS_READY_FOR_DATA | STATUS_STATE)));
return SDMMC_ERROR;
}
}
return error;
}
/**
* Move SD card to transfer state. The buffer size must be at
* least 512 byte long. This function checks the SD card status register and
* address the card if required before sending the transfer command.
* Returns 0 if successful; otherwise returns an code describing the error.
* \param pSd Pointer to a SD card driver instance.
* \param address Address of the block to transfer.
* \param nbBlocks Number of blocks to be transfer, 0 for infinite transfer.
* \param pData Data buffer whose size is at least the block size.
* \param isRead 1 for read data and 0 for write data.
*/
static uint8_t MoveToTransferState(sSdCard *pSd,
uint32_t address,
uint16_t nbBlocks,
uint8_t *pData,
uint8_t isRead)
{
uint32_t status;
uint8_t error;
if ( (pSd->bState == SDMMC_STATE_DATA_RD) || (pSd->bState == SDMMC_STATE_DATA_WR) )
{
#if 1
error = _StopCmd(pSd);
if (error) return error;
#else
/* Stop transfer */
error = Cmd12(pSd, &status);
if (error)
{
TRACE_ERROR("MTTranState.Cmd12: st%x, er%d\n\r", pSd->bState, error);
return error;
}
#if 1
else if (pSd->bState == SDMMC_STATE_DATA_WR)
{
if (isRead) _WaitRxReady(pSd);
else _WaitTxReady(pSd);
}
#endif
#endif
}
if ( isRead )
{
//_WaitRxReady(pSd);
/* Move to Receiving data state */
error = Cmd18(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address), &status, NULL);
//Cmd13(pSd, &status); //status -> 0xB00
if (error)
{
TRACE_ERROR("MTTranState.Cmd18: %d\n\r", error);
return error;
}
if (status & ~(STATUS_READY_FOR_DATA | STATUS_STATE)) {
TRACE_ERROR("CMD18.stat: %lx\n\r",
status & ~(STATUS_READY_FOR_DATA | STATUS_STATE));
return SDMMC_ERROR;
}
}
else
{
//_WaitTxReady(pSd);
/* Move to Sending data state */
error = Cmd25(pSd,
nbBlocks,
pData, SD_ADDRESS(pSd,address),
&status,
NULL);
if (error) {
TRACE_DEBUG("MoveToTransferState.Cmd25: %d\n\r", error);
return error;
}
if (status & (STATUS_WRITE & ~(STATUS_READY_FOR_DATA | STATUS_STATE))) {
TRACE_ERROR("CMD25(0x%x, %d).stat: %lx\n\r",
SD_ADDRESS(pSd,address), nbBlocks,
status & (STATUS_WRITE
& ~(STATUS_READY_FOR_DATA | STATUS_STATE)));
return SDMMC_ERROR;
}
}
if (!error) pSd->dwPrevBlk = address + (nbBlocks-1);
return error;
}
/**
* Switch card state between STBY and TRAN (or CMD and TRAN)
* \param pSd Pointer to a SD card driver instance.
* \param address Card address to TRAN, 0 to STBY
* \param statCheck Whether to check the status before CMD7.
*/
static uint8_t MmcSelectCard(sSdCard *pSd, uint16_t address, uint8_t statCheck)
{
uint8_t error;
uint32_t status;
uint32_t targetState = address ? STATUS_TRAN : STATUS_STBY;
uint32_t srcState = address ? STATUS_STBY : STATUS_TRAN;
/* At this stage the Initialization and identification process is achieved
* The SD card is supposed to be in Stand-by State */
while(statCheck) {
error = Cmd13(pSd, &status);
if (error) {
TRACE_ERROR("MmcSelectCard.Cmd13 (%d)\n\r", error);
return error;
}
if ((status & STATUS_READY_FOR_DATA)) {
uint32_t currState = status & STATUS_STATE;
if (currState == targetState) return 0;
if (currState != srcState) {
TRACE_ERROR("MmcSelectCard, wrong state %x\n\r", currState);
return SDMMC_ERROR;
}
break;
}
}
/* witch to TRAN mode to Select the current SD/MMC
* so that SD ACMD6 can process or EXT_CSD can read. */
error = Cmd7(pSd, address);
if (error == SDMMC_ERROR_NOT_INITIALIZED && address == 0) {}
else if (error) {
TRACE_ERROR("MmcSelectCard.Cmd7 (%d)\n\r", error);
}
return error;
}
/**
* Get MMC EXT_CSD information
* \param pSd Pointer to a SD card driver instance.
*/
static void MmcGetExtInformation(sSdCard *pSd)
{
uint8_t error;
/* MMC 4.0 Higher version */
if(SD_CSD_STRUCTURE(pSd->CSD) >= 2 && MMC_IsVer4(pSd)) {
error = MmcCmd8(pSd, (uint8_t*)pSd->EXT);
if (error) {
TRACE_ERROR("MmcGetExt.Cmd8: %d\n\r", error);
}
}
}
/**
* Get SD/MMC memory max transfer speed in K.
* \param pSd Pointer to a SD card driver instance.
*/
static uint32_t SdmmcGetMaxSpeed(sSdCard *pSd)
{
uint32_t speed = 0;
if (pSd->bCardType & CARD_TYPE_bmSDMMC) {
speed = SdmmcDecodeTransSpeed(SD_CSD_TRAN_SPEED(pSd->CSD),
sdmmcTransUnits,
((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD) ?
sdTransMultipliers : mmcTransMultipliers);
}
return speed;
}
/**
* Get SDIO max transfer speed, in K.
* \param pSd Pointer to a SD card driver instance.
*/
static uint32_t SdioGetMaxSpeed(sSdCard *pSd)
{
uint8_t error;
uint32_t speed = 0;
uint32_t addr = 0;
uint8_t buf[6];
if (pSd->bCardType & CARD_TYPE_bmSDIO) {
/* Check Func0 tuple in CIS area */
error = SDIO_ReadDirect(pSd,
SDIO_CIA, SDIO_CIS_PTR_REG,
(uint8_t*)&addr, 3);
if (error) {
TRACE_ERROR("SdioUpdateCardInformation.RdDirect: %d\n\r", error);
return error;
}
error = SdioFindTuples(pSd, addr, 256, NULL, &addr);
if (error) {
TRACE_ERROR("SdioUpdateCardInformation.FindTuple: %d\n\r", error);
return error;
}
if (addr) {
/* Fun0 tuple: fn0_blk_siz & max_tran_speed */
SDIO_ReadDirect(pSd, SDIO_CIA, addr, buf, 6);
speed = SdmmcDecodeTransSpeed(buf[5],
sdmmcTransUnits,
sdTransMultipliers);
}
}
return speed;
}
/**
* Get SCR and SD Status information
* \param pSd Pointer to a SD card driver instance.
*/
static void SdGetExtInformation(sSdCard *pSd)
{
pSd = pSd;
}
/**
* Update SD/MMC information.
* Update CSD for card speed switch.
* Update ExtDATA for any card function switch.
* \param pSd Pointer to a SD card driver instance.
* \return error code when update CSD error.
*/
static void SdMmcUpdateInformation(sSdCard *pSd,
uint8_t csd,
uint8_t extData)
{
uint8_t error;
/* Update CSD for new TRAN_SPEED value */
if (csd) {
MmcSelectCard(pSd, 0, 1);
Delay(800);
error = Cmd9(pSd);
if (error ) {
TRACE_ERROR("SdMmcUpdateInfo.Cmd9 (%d)\n\r", error);
return;
}
error = MmcSelectCard(pSd, pSd->wAddress, 1);
}
if (extData) {
switch(pSd->bCardType & CARD_TYPE_bmSDMMC) {
case CARD_TYPE_bmSD: SdGetExtInformation(pSd); break;
case CARD_TYPE_bmMMC: MmcGetExtInformation(pSd); break;
default: break;
}
}
}
/**
* \brief Check HS capability and enable it
* \param pSd Pointer to sSdCard instance.
*/
static uint8_t SdMmcEnableHighSpeed(sSdCard *pSd)
{
uint8_t error;
uint32_t status;
uint8_t io, sd, mmc;
io = ((pSd->bCardType & CARD_TYPE_bmSDIO) > 0);
sd = ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD);
mmc = ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmMMC);
/* Check host driver capability */
if (_HwGetHsMode(pSd) == 0) {
TRACE_INFO("HS Mode not supported by Host\n\r");
return SDMMC_ERROR_NOT_SUPPORT;
}
/* Check MMC */
if (mmc) {
/* MMC card type 3 (HS) */
//if (SD_CSD_STRUCTURE(pSd) >= 2 && (MMC_EXT_CARD_TYPE(pSd) & 0x2)) {
if (MMC_IsHsModeSupported(pSd)) {
/* Try switch to HS mode */
MmcCmd6Arg cmd6Arg = {
0x3,
MMC_EXT_HS_TIMING_I,
MMC_EXT_HS_TIMING_EN,
0};
error = MmcCmd6(pSd, &cmd6Arg, &status);
if (error) {
TRACE_ERROR("SdMmcEnableHS.Cmd6: %d\n\r", error);
return SDMMC_ERROR;
}
else if (status & STATUS_MMC_SWITCH) {
TRACE_ERROR("Mmc Switch HS: %x\n\r", status);
return SDMMC_ERROR_NOT_SUPPORT;
}
}
else {
TRACE_INFO("MMC without HS support\n\r");
return SDMMC_ERROR_NOT_SUPPORT;
}
TRACE_WARNING("MMC HS Enabled\n\r");
}
/* SD (+IO) */
else {
if (io) {
/* Check CIA.HS */
status = 0;
error = Cmd52(pSd, 0, SDIO_CIA, 0, SDIO_HS_REG, &status);
if (error) {
TRACE_ERROR("SdMmcEnableHS.Cmd52: %d\n\r", error);
return SDMMC_ERROR;
}
if ((status & SDIO_SHS) == 0) {
TRACE_INFO("HS Mode not supported by SDIO\n\r");
return SDMMC_ERROR_NOT_SUPPORT;
}
/* Enable HS mode */
else {
status = SDIO_EHS;
error = Cmd52(pSd, 1, SDIO_CIA, 1, SDIO_HS_REG, &status);
if (error) {
TRACE_ERROR("SdMmcEnableHS.Cmd52 H: %d\n\r", error);
return SDMMC_ERROR;
}
if (status & SDIO_EHS) {
TRACE_WARNING("SDIO HS Enabled\n\r");
}
}
}
if (sd) {
/* Check version */
if (SD_IsHsModeSupported(pSd)) {
/* Try enable HS mode */
SdCmd6Arg cmd6Arg = {
1, 0, 0xF, 0xF, 0xF, 0xF, 0, 1
};
uint32_t switchStatus[512/32];
error = SdCmd6(pSd, &cmd6Arg, switchStatus, &status);
if (error || (status & STATUS_SWITCH_ERROR)) {
TRACE_INFO("SD HS Fail\n\r");
return SDMMC_ERROR;
}
else if (SD_SWITCH_ST_FUN_GRP1_RC(switchStatus)
== SD_SWITCH_ST_FUN_GRP_RC_ERROR) {
TRACE_INFO("SD HS Not Supported\n\r");
return SDMMC_ERROR_NOT_SUPPORT;
}
else if (SD_SWITCH_ST_FUN_GRP1_BUSY(switchStatus)) {
TRACE_INFO("SD HS Locked\n\r");
return SDMMC_ERROR_BUSY;
}
else {
TRACE_WARNING("SD HS Enabled\n\r");
}
}
else {
TRACE_INFO("HS Not Supported in SD Rev 0x%x\n\r",
SD_SCR_SD_SPEC(pSd));
return SDMMC_ERROR_NOT_SUPPORT;
}
}
}
/* Enable Host HS mode */
_HwSetHsMode(pSd, 1);
return 0;
}
/**
* \brief Check bus width capability and enable it
*/
static uint8_t SdMmcDesideBuswidth(sSdCard *pSd)
{
uint8_t error, busWidth;
uint32_t status;
uint8_t mmc;
/* Best width that the card support */
busWidth = _HwGetBusWidth(pSd);
mmc = ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmMMC);
if (mmc) {
/* Check MMC revision 4 or later (1/4/8 bit mode) */
if (MMC_CSD_SPEC_VERS(pSd->CSD) >= 4) {
/* Select what's HC supported */
MmcCmd6Arg cmd6Arg = {
0x1, MMC_EXT_BUS_WIDTH_I, MMC_EXT_BUS_WIDTH_1BIT, 0
};
switch(busWidth) {
case 4:
cmd6Arg.value = MMC_EXT_BUS_WIDTH_4BITS;
break;
case 8:
cmd6Arg.value = MMC_EXT_BUS_WIDTH_8BUTS;
break;
case 1:
break;
}
error = MmcCmd6(pSd, &cmd6Arg, &status);
if (error) {
TRACE_ERROR("SdMmcSetBuswidth.Cmd6: %d\n\r", error);
return SDMMC_ERROR;
}
else {
if (status & STATUS_MMC_SWITCH) {
TRACE_ERROR("MMC Bus Switch error %x\n\r", status);
return SDMMC_ERROR_NOT_SUPPORT;
}
TRACE_WARNING("MMC Bus mode %x\n\r", busWidth);
}
}
else {
TRACE_WARNING("MMC 1-bit only\n\r");
return SDMMC_ERROR_NOT_SUPPORT;
}
}
else {
/* SD(IO): switch to 4-bit mode ? */
uint8_t io = ((pSd->bCardType & CARD_TYPE_bmSDIO) > 0);
uint8_t sd = ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD);
if (busWidth == 1)
return SDMMC_ERROR_NOT_SUPPORT;
/* No 8-bit mode, default to 4-bit mode */
busWidth = 4;
/* SDIO */
if (io) {
/* SDIO 1 bit only */
busWidth = 1;
}
/* SD */
if (sd) {
error = Acmd6(pSd, busWidth);
if (error) {
TRACE_ERROR("SdMmcSetBusWidth.Acmd6: %d\n\r", error);
return SDMMC_ERROR;
}
TRACE_WARNING("SD 4-bit mode\n\r");
}
}
/* Switch to selected bus mode */
pSd->bBusMode = busWidth;
_HwSetBusWidth(pSd, busWidth);
return 0;
}
/**
* \brief Run the SD/MMC/SDIO Mode initialization sequence.
* This function runs the initialization procedure and the identification
* process. Then it leaves the card in ready state. The following procedure must
* check the card type and continue to put the card into tran(for memory card)
* or cmd(for io card) state for data exchange.
* \param pSd Pointer to a SD card driver instance.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "SD_ERROR code".
*/
static uint8_t SdMmcIdentify(sSdCard *pSd)
{
uint8_t mem = 0, io = 0, f8 = 0, mp = 1, ccs = 0;
uint32_t status;
uint8_t error;
/* Reset HC to default HS and BusMode */
_HwSetHsMode(pSd, 0);
_HwSetBusWidth(pSd, 1);
/* Reset SDIO: CMD52, write 1 to RES bit in CCCR (bit 3 of register 6) */
status = SDIO_RES;
error = Cmd52(pSd, 1, SDIO_CIA, 0, SDIO_IOA_REG, &status);
if (!error && ((status & STATUS_SDIO_R5)==0)){}
else if (error == SDMMC_ERROR_NORESPONSE){}
else {
TRACE_DEBUG("SdMmcIdentify.Cmd52 fail: %u, %x\n\r", error, status);
}
/* Reset MEM: CMD0 */
error = SwReset(pSd, 1);
if (error) {
TRACE_DEBUG("SdMmcIdentify.SwReset fail: %u\n\r", error);
}
/* CMD8 is newly added in the Physical Layer Specification Version 2.00 to
* support multiple voltage ranges and used to check whether the card
* supports supplied voltage. The version 2.00 host shall issue CMD8 and
* verify voltage before card initialization.
* The host that does not support CMD8 shall supply high voltage range... */
error = SdCmd8(pSd, 1);
if (error == 0) f8 = 1;
else if (error != SDMMC_ERROR_NORESPONSE) {
TRACE_ERROR("SdMmcIdentify.Cmd8: %d\n\r", error);
return SDMMC_ERROR;
}
/* Delay after "no response" */
else Delay(8000);
/* CMD5 is newly added for SDIO initialize & power on */
status = 0;
error = Cmd5(pSd, &status);
if (error) {
TRACE_INFO("SdMmcIdentify.Cmd5: %d\n\r", error)
}
/* Card has SDIO function */
else if ((status & SDIO_OCR_NF) > 0) {
unsigned int cmd5Retries = 10000;
do {
status &= SD_HOST_VOLTAGE_RANGE;
error = Cmd5(pSd, &status);
if (status & SD_OCR_BUSY) break;
} while(!error && cmd5Retries --);
if (error) {
TRACE_ERROR("SdMmcIdentify.Cmd5 V: %d\n\r", error);
return SDMMC_ERROR;
}
io = 1;
TRACE_INFO("SDIO\n\r");
/* IO only ?*/
mp = ((status & SDIO_OCR_MP) > 0);
}
/* Has memory: SD/MMC/COMBO */
if (mp) {
/* Try SD memory initialize */
error = Acmd41(pSd, f8, &ccs);
if (error) {
unsigned int cmd1Retries = 10000;
TRACE_DEBUG("SdMmcIdentify.Acmd41: %u, try MMC\n\r", error);
/* Try MMC initialize */
error = SwReset(pSd, 10);
if (error) {
TRACE_ERROR("SdMmcIdentify.Mmc.SwReset: %d\n\r", error);
return SDMMC_ERROR;
}
ccs = 1;
do { error = Cmd1(pSd, &ccs); }while(error && cmd1Retries -- > 0);
if (error) {
TRACE_ERROR("SdMmcIdentify.Cmd1: %d\n\r", error);
return SDMMC_ERROR;
}
else if (ccs) pSd->bCardType = CARD_MMCHD;
else pSd->bCardType = CARD_MMC;
/* MMC card identification OK */
TRACE_INFO("MMC Card\n\r");
return 0;
}
else if (ccs) { TRACE_INFO("SDHC MEM\n\r");}
else { TRACE_INFO("SD MEM\n\r");}
mem = 1;
}
/* SD(IO) + MEM ? */
if (!mem) {
if (io) pSd->bCardType = CARD_SDIO;
else {
TRACE_ERROR("Unknown card\n\r");
return SDMMC_ERROR;
}
}
/* SD(HC) combo */
else if (io)
pSd->bCardType = ccs ? CARD_SDHCCOMBO : CARD_SDCOMBO;
/* SD(HC) */
else
pSd->bCardType = ccs ? CARD_SDHC : CARD_SD;
return 0;
}
/**
* \brief Run the SD/MMC/SDIO enumeration sequence.
* This function runs after the initialization and identification procedure. It
* gets all necessary information from the card and deside transfer block size,
* clock speed and bus width. It sets the SD/MMC/SDIO card in transfer
* (or command) state.
* \param pSd Pointer to a SD card driver instance.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "SD_ERROR code".
*/
static uint8_t SdMmcEnum(sSdCard *pSd)
{
uint8_t mem , io;
uint8_t error;
uint32_t ioSpeed = 0, memSpeed = 0;
uint8_t hsExec = 0, bwExec = 0;
/* - has Memory/IO/High-Capacigy - */
mem = ((pSd->bCardType & CARD_TYPE_bmSDMMC) > 0);
io = ((pSd->bCardType & CARD_TYPE_bmSDIO) > 0);
/* For MEMORY cards:
* The host then issues the command ALL_SEND_CID (CMD2) to the card to get
* its unique card identification (CID) number.
* Card that is unidentified (i.e. which is in Ready State) sends its CID
* number as the response (on the CMD line). */
if (mem) {
error = Cmd2(pSd);
if (error) {
TRACE_ERROR("SdMmcInit.cmd2(%d)\n\r", error);
return error;
}
}
/* For MEMORY and SDIO cards:
* Thereafter, the host issues CMD3 (SEND_RELATIVE_ADDR) asks the
* card to publish a new relative card address (RCA), which is shorter than
* CID and which is used to address the card in the future data transfer
* mode. Once the RCA is received the card state changes to the Stand-by
* State. At this point, if the host wants to assign another RCA number, it
* can ask the card to publish a new number by sending another CMD3 command
* to the card. The last published RCA is the actual RCA number of the
* card. */
error = Cmd3(pSd);
if (error) {
TRACE_ERROR("SdMmcInit.cmd3(%d)\n\r", error);
return error;
}
/* For MEMORY cards:
* SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register),
* e.g. block length, card storage capacity, etc... */
if (mem) {
error = Cmd9(pSd);
if (error) {
TRACE_ERROR("SdMmcInit.cmd9(%d)\n\r", error);
return error;
}
}
/* Now select the card, to TRAN state */
error = MmcSelectCard(pSd, CARD_ADDR(pSd), 0);
if (error) {
TRACE_ERROR("SdMmcInit.SelCard(%d)\n\r", error);
return error;
}
/* - Now in TRAN, obtain extended setup information - */
/* If the card support EXT_CSD, read it! */
TRACE_INFO("Card Type %d, CSD_STRUCTURE %u\n\r",
pSd->bCardType, SD_CSD_STRUCTURE(pSd));
/* Get extended information of the card */
SdMmcUpdateInformation(pSd, 0, 1);
/* Calculate transfer speed */
if (io) ioSpeed = SdioGetMaxSpeed(pSd);
if (mem) memSpeed = SdmmcGetMaxSpeed(pSd);
/* Combo, min speed */
if (io && mem) {
pSd->dwTranSpeed = (ioSpeed > memSpeed) ? memSpeed : ioSpeed;
}
/* SDIO only */
else if (io) {
pSd->dwTranSpeed = ioSpeed;
}
/* Memory card only */
else if (mem) {
pSd->dwTranSpeed = memSpeed;
}
pSd->dwTranSpeed *= 1000;
/* Enable more bus width Mode */
error = SdMmcDesideBuswidth(pSd);
if (!error) bwExec = 1;
else if (error != SDMMC_ERROR_NOT_SUPPORT) {
TRACE_ERROR("SdmmcEnum.DesideBusWidth: %d\n\r", error);
return SDMMC_ERROR;
}
/* Enable High-Speed Mode */
error = SdMmcEnableHighSpeed(pSd);
if (!error) hsExec = 1;
else if (error != SDMMC_ERROR_NOT_SUPPORT) {
TRACE_ERROR("SdmmcEnum.EnableHS: %d\n\r", error);
return SDMMC_ERROR;
}
/* In HS mode transfer speed *2 */
if (hsExec) pSd->dwTranSpeed *= 2;
/* Update card information since status changed */
if (bwExec || hsExec) SdMmcUpdateInformation(pSd, hsExec, 1);
return 0;
}
/*----------------------------------------------------------------------------
* Global functions
*----------------------------------------------------------------------------*/
/**
*/
/**
* Read Blocks of data in a buffer pointed by pData. The buffer size must be at
* least 512 byte long. This function checks the SD card status register and
* address the card if required before sending the read command.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
* \param pSd Pointer to a SD card driver instance.
* \param address Address of the block to read.
* \param pData Data buffer whose size is at least the block size, it can
* be 1,2 or 4-bytes aligned when used with DMA.
* \param length Number of blocks to be read.
* \param pCallback Pointer to callback function that invoked when read done.
* 0 to start a blocked read.
* \param pArgs Pointer to callback function arguments.
*/
uint8_t SD_Read(sSdCard *pSd,
uint32_t address,
void *pData,
uint32_t length,
fSdmmcCallback pCallback,
void *pArgs)
{
uint8_t error;
assert( pSd != NULL ) ;
assert( pData != NULL ) ;
if ( pSd->bState != SDMMC_STATE_DATA_RD
|| pSd->dwPrevBlk + 1 != address ) {
/* Start infinite block reading */
error = MoveToTransferState(pSd, address, 0, 0, 1);
}
else error = 0;
if (!error) {
pSd->bState = SDMMC_STATE_DATA_RD;
pSd->dwPrevBlk = address + (length - 1);
error = SdmmcRead(pSd, BLOCK_SIZE(pSd), length, pData,
pCallback, pArgs);
}
TRACE_DEBUG("SDrd(%u,%u):%u\n\r", address, length, error);
return 0;
}
/**
* Write Blocks of data in a buffer pointed by pData. The buffer size must be at
* least 512 byte long. This function checks the SD card status register and
* address the card if required before sending the read command.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
* \param pSd Pointer to a SD card driver instance.
* \param address Address of the block to read.
* \param pData Data buffer whose size is at least the block size, it can
* be 1,2 or 4-bytes aligned when used with DMA.
* \param length Number of blocks to be read.
* \param pCallback Pointer to callback function that invoked when read done.
* 0 to start a blocked read.
* \param pArgs Pointer to callback function arguments.
*/
uint8_t SD_Write(sSdCard *pSd,
uint32_t address,
const void *pData,
uint32_t length,
fSdmmcCallback pCallback,
void *pArgs)
{
uint8_t error = 0;
assert( pSd != NULL ) ;
#if 0
if ( pSd->bState != SDMMC_STATE_DATA_WR
|| pSd->dwPrevBlk + 1 != address ) {
/* Start block writing */
error = MoveToTransferState(pSd, address, length, (uint8_t*)pData, 0);
}
else {
error = SdmmcWrite(pSd, BLOCK_SIZE(pSd), length, pData,
pCallback, pArgs);
}
if (!error) {
pSd->bState = SDMMC_STATE_DATA_WR;
pSd->dwPrevBlk = address + (length - 1);
}
#else
if ( pSd->bState != SDMMC_STATE_DATA_WR
|| pSd->dwPrevBlk + 1 != address ) {
/* Start infinite block writing */
error = MoveToTransferState(pSd, address, 0, 0, 0);
}
if (!error) {
pSd->bState = SDMMC_STATE_DATA_WR;
error = SdmmcWrite(pSd, BLOCK_SIZE(pSd), length, pData,
pCallback, pArgs);
pSd->dwPrevBlk = address + (length - 1);
}
#endif
TRACE_DEBUG("SDwr(%u,%u):%u\n\r", address, length, error);
return 0;
}
/**
* Read Blocks of data in a buffer pointed by pData. The buffer size must be at
* least 512 byte long. This function checks the SD card status register and
* address the card if required before sending the read command.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
* \param pSd Pointer to a SD card driver instance.
* \param address Address of the block to read.
* \param nbBlocks Number of blocks to be read.
* \param pData Data buffer whose size is at least the block size, it can
* be 1,2 or 4-bytes aligned when used with DMA.
*/
uint8_t SD_ReadBlocks(sSdCard *pSd,
uint32_t address,
void *pData,
uint32_t nbBlocks)
{
uint8_t error = 0;
uint8_t *pBytes = (uint8_t*)pData;
assert( pSd != NULL ) ;
assert( pData != NULL ) ;
assert( nbBlocks != 0 ) ;
TRACE_DEBUG("RdBlks(%d,%d)\n\r", address, nbBlocks);
while(nbBlocks --) {
error = PerformSingleTransfer(pSd, address, pBytes, 1);
if (error)
break;
address += 1;
pBytes = &pBytes[512];
}
return error;
}
/**
* Write Block of data pointed by pData. The buffer size must be at
* least 512 byte long. This function checks the SD card status register and
* address the card if required before sending the read command.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
* \param pSd Pointer to a SD card driver instance.
* \param address Address of block to write.
* \param nbBlocks Number of blocks to be read
* \param pData Data buffer whose size is at least the block size, it can
* be 1,2 or 4-bytes aligned when used with DMA.
*/
uint8_t SD_WriteBlocks(sSdCard *pSd,
uint32_t address,
const void *pData,
uint32_t nbBlocks)
{
uint8_t error = 0;
uint8_t *pB = (uint8_t*)pData;
assert( pSd != NULL ) ;
assert( pData != NULL ) ;
assert( nbBlocks != 0 ) ;
TRACE_DEBUG("WrBlks(%d,%d)\n\r", address, nbBlocks);
while(nbBlocks --) {
error = PerformSingleTransfer(pSd, address, pB, 0);
if (error)
break;
address += 1;
pB = &pB[512];
}
return error;
}
/**
* Reset SD/MMC driver runtime parameters.
*/
static void _SdParamReset(sSdCard *pSd)
{
uint32_t i;
pSd->dwTranSpeed = 0;
pSd->dwTotalSize = 0;
pSd->dwNbBlocks = 0;
pSd->wBlockSize = 0;
pSd->wCurrBlockLen = 0;
pSd->dwCurrSpeed = 0;
pSd->dwPrevBlk = 0xFFFFFFFF;
pSd->wAddress = 0;
pSd->bCardType = 0;
pSd->bStatus = 0;
pSd->bState = SDMMC_STATE_IDENT;
/* Clear CID, CSD, EXT_CSD data */
for (i = 0; i < 128/8/4; i ++) pSd->CID[i] = 0;
for (i = 0; i < 128/8/4; i ++) pSd->CSD[i] = 0;
for (i = 0; i < 512/4; i ++) pSd->EXT[i] = 0;
}
/**
* Initialize SD/MMC driver struct.
* \param pSd Pointer to a SD card driver instance.
* \param pDrv Pointer to low level driver instance.
* \param bSlot Slot number.
* \param pHalf Pointer to hardware access functions.
*/
void SDD_Initialize(sSdCard * pSd,
const void * pDrv, uint8_t bSlot,
const sSdHalFunctions * pHalf)
{
pSd->pDrv = (void *)pDrv;
pSd->pHalf = (sSdHalFunctions *)pHalf;
pSd->pExt = NULL;
pSd->bSlot = bSlot;
_SdParamReset(pSd);
}
/**
* Run the SDcard initialization sequence. This function runs the
* initialisation procedure and the identification process, then it sets the
* SD card in transfer state to set the block length and the bus width.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
* \param pSd Pointer to a SD card driver instance.
*/
uint8_t SD_Init(sSdCard *pSd)
{
uint8_t error;
uint32_t dwClock;
_SdParamReset(pSd);
/* Set low speed for device identification (LS device max speed) */
dwClock = 400000;
_HwSetClock(pSd, &dwClock);
/* Initialization delay: The maximum of 1 msec, 74 clock cycles and supply
* ramp up time. Supply ramp up time provides the time that the power is
* built up to the operating level (the bus master supply voltage) and the
* time to wait until the SD card can accept the first command. */
/* Power On Init Special Command */
error = Pon(pSd);
if (error) {
TRACE_ERROR("SD_Init.PowON:%d\n\r", error);
return error;
}
/* After power-on or CMD0, all cards?
* CMD lines are in input mode, waiting for start bit of the next command.
* The cards are initialized with a default relative card address
* (RCA=0x0000) and with a default driver stage register setting
* (lowest speed, highest driving current capability). */
error = SdMmcIdentify(pSd);
if (error) {
TRACE_ERROR("SD_Init.Identify: %d\n\r", error);
return error;
}
error = SdMmcEnum(pSd);
if (error) {
TRACE_ERROR("SD_Init.Enum: %d\n\r", error);
return error;
}
/* In the case of a Standard Capacity SD Memory Card, this command sets the
* block length (in bytes) for all following block commands
* (read, write, lock).
* Default block length is fixed to 512 Bytes.
* Set length is valid for memory access commands only if partial block read
* operation are allowed in CSD.
* In the case of a High Capacity SD Memory Card, block length set by CMD16
* command does not affect the memory read and write commands. Always 512
* Bytes fixed block length is used. This command is effective for
* LOCK_UNLOCK command.
* In both cases, if block length is set larger than 512Bytes, the card sets
* the BLOCK_LEN_ERROR bit. */
if (pSd->bCardType == CARD_SD) {
error = Cmd16(pSd, SDMMC_BLOCK_SIZE);
}
pSd->wCurrBlockLen = SDMMC_BLOCK_SIZE;
/* Reset status for R/W */
pSd->bState = SDMMC_STATE_TRAN;
/* If MMC Card & get size from EXT_CSD */
if ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmMMC
&& SD_CSD_C_SIZE(pSd->CSD) == 0xFFF) {
pSd->dwNbBlocks = MMC_EXT_SEC_COUNT(pSd->EXT);
/* Block number less than 0x100000000/512 */
if (pSd->dwNbBlocks > 0x800000)
pSd->dwTotalSize = 0xFFFFFFFF;
else
pSd->dwTotalSize = MMC_EXT_SEC_COUNT(pSd->EXT)*512;
}
/* If SD CSD v2.0 */
else if((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD
&& SD_CSD_STRUCTURE(pSd->CSD) >= 1) {
pSd->wBlockSize = 512;
pSd->dwNbBlocks = SD_CSD_BLOCKNR_HC(pSd->CSD);
pSd->dwTotalSize = 0xFFFFFFFF;
}
/* Normal SD/MMC card */
else if (pSd->bCardType & CARD_TYPE_bmSDMMC) {
pSd->wBlockSize = SD_CSD_BLOCK_LEN(pSd->CSD);
pSd->dwTotalSize = SD_CSD_TOTAL_SIZE(pSd->CSD);
pSd->dwNbBlocks = SD_CSD_BLOCKNR(pSd->CSD);
}
if (pSd->bCardType == CARD_UNKNOWN) {
return SDMMC_ERROR_NOT_INITIALIZED;
}
/* Automatically select the max clock */
dwClock = pSd->dwTranSpeed;
_HwSetClock(pSd, &dwClock);
TRACE_WARNING_WP("-I- Set SD/MMC clock to %uK\n\r", dwClock/1000);
pSd->dwCurrSpeed = dwClock;
return 0;
}
/**
* De-initialize the driver. Invoked when SD card disconnected.
* \param pSd Pointer to a SD card driver instance.
*/
void SD_DeInit(sSdCard *pSd)
{
if (pSd->pDrv)
{
_HwReset(pSd);
_SdParamReset(pSd);
}
}
/**
* Return type of the card.
* \param pSd Pointer to \ref sSdCard instance.
* \sa sdmmc_cardtype
*/
uint8_t SD_GetCardType(sSdCard *pSd)
{
assert( pSd != NULL ) ;
return pSd->bCardType;
}
/**
* Return size of the SD/MMC card, in KB.
* \param pSd Pointer to \ref sSdCard instance.
*/
uint32_t SD_GetTotalSizeKB(sSdCard *pSd)
{
assert( pSd != NULL ) ;
if (pSd->dwTotalSize == 0xFFFFFFFF) {
return pSd->dwNbBlocks / 2;
}
return pSd->dwTotalSize / 1024;
}
/**
* Return reported block size of the SD/MMC card.
* (SD/MMC access block size is always 512B for R/W).
* \param pSd Pointer to \ref sSdCard instance.
*/
uint32_t SD_GetBlockSize( sSdCard *pSd )
{
assert( pSd != NULL ) ;
return pSd->wBlockSize;
}
/**
* Return reported number of blocks for the SD/MMC card.
* \param pSd Pointer to \ref sSdCard instance.
*/
uint32_t SD_GetNumberBlocks( sSdCard *pSd )
{
assert( pSd != NULL ) ;
return pSd->dwNbBlocks ;
}
/**
* Read one or more bytes from SDIO card, using RW_DIRECT command.
* \param pSd Pointer to SdCard instance.
* \param functionNum Function number.
* \param address First register address to read from.
* \param pData Pointer to data buffer.
* \param size Buffer size, number of bytes to read.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
*/
uint8_t SDIO_ReadDirect(sSdCard *pSd,
uint8_t functionNum,
uint32_t address,
uint8_t *pData,
uint32_t size)
{
uint8_t error;
uint32_t status;
assert( pSd != NULL ) ;
if (pSd->bCardType & CARD_TYPE_bmSDIO) {
if (size == 0) return SDMMC_ERROR_PARAM;
while(size --) {
status = 0;
error = Cmd52(pSd, 0, functionNum, 0, address ++, &status);
if (pData) *pData ++ = (uint8_t)status;
if (error) {
TRACE_ERROR("IO_RdDirect.Cmd52: %d\n\r", error);
return SDMMC_ERROR;
}
else if (status & STATUS_SDIO_R5) {
TRACE_ERROR("RD_DIRECT(%u, %u) st %x\n\r",
address, size, status);
return SDMMC_ERROR;
}
}
}
else {
return SDMMC_ERROR_NOT_SUPPORT;
}
return 0;
}
/**
* Write one byte to SDIO card, using RW_DIRECT command.
* \param pSd Pointer to SdCard instance.
* \param functionNum Function number.
* \param address Register address to write to.
* \param dataByte Data to write.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
*/
uint8_t SDIO_WriteDirect(sSdCard *pSd,
uint8_t functionNum,
uint32_t address,
uint8_t dataByte)
{
uint8_t error;
uint32_t status;
assert( pSd != NULL ) ;
if (pSd->bCardType & CARD_TYPE_bmSDIO) {
status = dataByte;
error = Cmd52(pSd, 1, functionNum, 0, address, &status);
if (error) {
TRACE_ERROR("SDIO_WrDirect.Cmd52: %d\n\r", error);
return SDMMC_ERROR;
}
else if (status & STATUS_SDIO_R5) {
TRACE_ERROR("WR_DIRECT(%u) st %x\n\r",
address, status);
return SDMMC_ERROR;
}
}
else {
return SDMMC_ERROR_NOT_SUPPORT;
}
return 0;
}
/**
* Read byte by byte from SDIO card, using RW_EXTENDED command.
* \param pSd Pointer to SdCard instance.
* \param functionNum Function number.
* \param address First byte address of data in SDIO card.
* \param isFixedAddress During transfer the data address is never increased.
* \param pData Pointer to data buffer.
* \param size Size of data to read (1 ~ 512).
* \param fCallback Callback function invoked when transfer finished.
* \param pArg Pointer to callback argument.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
*/
uint8_t SDIO_ReadBytes(sSdCard *pSd,
uint8_t functionNum,
uint32_t address,
uint8_t isFixedAddress,
uint8_t *pData,
uint16_t size,
fSdmmcCallback fCallback,
void* pArg)
{
uint8_t error;
uint32_t status;
uint8_t ucBlockMode = 0;
assert( pSd != NULL ) ;
if (pSd->bCardType & CARD_TYPE_bmSDIO) {
if (size == 0) return SDMMC_ERROR_PARAM;
error = Cmd53(pSd,
0, functionNum, ucBlockMode, !isFixedAddress,
address, pData, size,
&status,
fCallback, pArg);
if (error) {
TRACE_ERROR("IO_RdBytes.Cmd53: %d\n\r", error);
return SDMMC_ERROR;
}
else if (status & STATUS_SDIO_R5) {
TRACE_ERROR("RD_EXT st %x\n\r", status);
return SDMMC_ERROR;
}
}
else {
return SDMMC_ERROR_NOT_SUPPORT;
}
return 0;
}
/**
* Write byte by byte to SDIO card, using RW_EXTENDED command.
* \param pSd Pointer to SdCard instance.
* \param functionNum Function number.
* \param address First byte address of data in SDIO card.
* \param isFixedAddress During transfer the data address is never increased.
* \param pData Pointer to data buffer.
* \param size Size of data to write (1 ~ 512).
* \param fCallback Callback function invoked when transfer finished.
* \param pArg Pointer to callback argument.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
*/
uint8_t SDIO_WriteBytes(sSdCard *pSd,
uint8_t functionNum,
uint32_t address,
uint8_t isFixedAddress,
uint8_t *pData,
uint16_t size,
fSdmmcCallback fCallback,
void* pArg)
{
uint8_t error;
uint32_t status;
assert( pSd != NULL ) ;
if (pSd->bCardType & CARD_TYPE_bmSDIO) {
if (size == 0) return SDMMC_ERROR_PARAM;
error = Cmd53(pSd,
1, functionNum, 0, !isFixedAddress,
address, pData, size,
(uint32_t*)&status,
fCallback, pArg);
Delay(100);
if (error) {
TRACE_ERROR("IO_WrBytes.Cmd53: %d\n\r", error);
return SDMMC_ERROR;
}
else if (status & STATUS_SDIO_R5) {
TRACE_ERROR("WR_EXT st %x\n\r", status);
return SDMMC_ERROR;
}
}
else {
return SDMMC_ERROR_NOT_SUPPORT;
}
return 0;
}
/**
* Display SDIO card informations (CIS, tuple ...)
* \param pSd Pointer to \ref sSdCard instance.
*/
void SDIO_DumpCardInformation(sSdCard * pSd)
{
uint32_t tmp = 0, addrCIS = 0, addrManfID = 0, addrFunc0 = 0;
uint8_t *p = (uint8_t*)&tmp;
uint8_t buf[8];
switch(pSd->bCardType)
{
case CARD_SDIO:
TRACE_INFO("** SDIO ONLY card\n\r");
break;
case CARD_SDCOMBO: case CARD_SDHCCOMBO:
TRACE_INFO("** SDIO Combo card\n\r");
break;
default:
TRACE_INFO("** NO SDIO\n\r");
return;
}
/* CCCR */
TRACE_INFO("====== CCCR ======\n\r");
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CCCR_REG, p, 1);
TRACE_INFO(".SDIO %02lX\n\r", (tmp & SDIO_SDIO) >> 4);
TRACE_INFO(".CCCR %02lX\n\r", (tmp & SDIO_CCCR) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_SD_REV_REG, p, 1);
TRACE_INFO(".SD %02lX\n\r", (tmp & SDIO_SD) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IOE_REG, p, 1);
TRACE_INFO(".IOE %02lX\n\r", (tmp & SDIO_IOE) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IOR_REG, p, 1);
TRACE_INFO(".IOR %02lX\n\r", (tmp & SDIO_IOR) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_IEN_REG, p, 1);
TRACE_INFO(".IEN %02lX\n\r", (tmp & SDIO_IEN) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_INT_REG, p, 1);
TRACE_INFO(".INT %u\n\r", (tmp & SDIO_INT));
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_BUS_CTRL_REG, p, 1);
TRACE_INFO(".CD %lx\n\r", (tmp & SDIO_CD) >> 7);
TRACE_INFO(".SCSI %lx\n\r", (tmp & SDIO_SCSI) >> 6);
TRACE_INFO(".ECSI %lx\n\r", (tmp & SDIO_ECSI) >> 5);
TRACE_INFO(".BUS_WIDTH %lx\n\r", (tmp & SDIO_BUSWIDTH) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CAP_REG, p, 1);
TRACE_INFO(".4BLS %lx\n\r", (tmp & SDIO_4BLS) >> 7);
TRACE_INFO(".LSC %lx\n\r", (tmp & SDIO_LSC) >> 6);
TRACE_INFO(".E4MI %lx\n\r", (tmp & SDIO_E4MI) >> 5);
TRACE_INFO(".S4MI %lx\n\r", (tmp & SDIO_S4MI) >> 4);
TRACE_INFO(".SBS %lx\n\r", (tmp & SDIO_SBS) >> 3);
TRACE_INFO(".SRW %lx\n\r", (tmp & SDIO_SRW) >> 2);
TRACE_INFO(".SMB %lx\n\r", (tmp & SDIO_SMB) >> 1);
TRACE_INFO(".SDC %lx\n\r", (tmp & SDIO_SDC) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_CIS_PTR_REG, p, 3);
TRACE_INFO(".CIS_PTR %06X\n\r", tmp);
addrCIS = tmp; tmp = 0;
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_BUS_SUSP_REG, p, 1);
TRACE_INFO(".BR %lx\n\r", (tmp & SDIO_BR) >> 1);
TRACE_INFO(".BS %lx\n\r", (tmp & SDIO_BS) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_FUN_SEL_REG, p, 1);
TRACE_INFO(".DF %lx\n\r", (tmp & SDIO_DF) >> 7);
TRACE_INFO(".FS %lx\n\r", (tmp & SDIO_FS) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_EXEC_REG, p, 1);
TRACE_INFO(".EX %lx\n\r", (tmp & SDIO_EX));
TRACE_INFO(".EXM %lx\n\r", (tmp & SDIO_EXM) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_READY_REG, p, 1);
TRACE_INFO(".RF %lx\n\r", (tmp & SDIO_RF));
TRACE_INFO(".RFM %lx\n\r", (tmp & SDIO_RFM) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_FN0_BLKSIZ_REG, p, 2);
TRACE_INFO(".FN0_SIZE %u(%04X)\n\r", tmp, tmp);
tmp = 0;
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_POWER_REG, p, 1);
TRACE_INFO(".EMPC %lx\n\r", (tmp & SDIO_EMPC) >> 1);
TRACE_INFO(".SMPC %lx\n\r", (tmp & SDIO_SMPC) >> 0);
SDIO_ReadDirect(pSd, SDIO_CIA, SDIO_HS_REG, p, 1);
TRACE_INFO(".EHS %lx\n\r", (tmp & SDIO_EHS) >> 1);
TRACE_INFO(".SHS %lx\n\r", (tmp & SDIO_SHS) >> 0);
/* Metaformat */
SdioFindTuples(pSd, addrCIS, 128, &addrManfID, &addrFunc0);
if (addrManfID != 0) {
SDIO_ReadDirect(pSd, SDIO_CIA, addrManfID, buf, 6);
TRACE_INFO("==== CISTPL_MANFID ====\n\r");
TRACE_INFO("._MANF %04X\n\r", buf[2] + (buf[3] << 8));
TRACE_INFO("._CARD %04X\n\r", buf[4] + (buf[5] << 8));
}
if (addrFunc0 != 0) {
SDIO_ReadDirect(pSd, SDIO_CIA, addrFunc0, buf, 6);
TRACE_INFO("== CISTPL_FUNCE Fun0 ==\n\r");
TRACE_INFO("._FN0_BLK_SIZE %d(0x%04X)\n\r",
buf[3] + (buf[4] << 8), buf[3] + (buf[4] << 8));
TRACE_INFO("._MAX_TRAN_SPEED %02X\n\r", buf[5]);
}
}
/**
*/
static void _DumpREG(void* pREG, uint32_t dwSize)
{
uint8_t *p = (uint8_t*)pREG;
uint32_t i;
for (i = 0; i < dwSize; i ++)
{
if ((i % 16) == 0) printf("\n\r [%04X]", i);
printf(" %02X", p[i]);
}
printf("\n\r");
}
/**
* Display the content of the CID register
* \param pCID Pointer to CID data.
*/
void SD_DumpCID(void* pCID)
{
TRACE_INFO("======= CID =======");
_DumpREG(pCID, 128/8);
TRACE_INFO("===================\n\r");
TRACE_INFO(" .MID Manufacturer ID %02X\n\r",
SD_CID_MID(pCID));
TRACE_INFO(" .CBX Card/BGA (eMMC) %X\n\r",
eMMC_CID_CBX(pCID));
TRACE_INFO(" .OID OEM/Application ID (SD) %c%c\n\r",
(char)SD_CID_OID1(pCID),
(char)SD_CID_OID0(pCID));
TRACE_INFO(" .OID OEM/Application ID (MMC) %x\n\r",
eMMC_CID_OID(pCID));
TRACE_INFO(" .PNM Product name (SD) %c%c%c%c%c\n\r",
(char)SD_CID_PNM4(pCID),
(char)SD_CID_PNM3(pCID),
(char)SD_CID_PNM2(pCID),
(char)SD_CID_PNM1(pCID),
(char)SD_CID_PNM0(pCID));
TRACE_INFO(" .PNM Product name (MMC) %c%c%c%c%c%c\n\r",
(char)MMC_CID_PNM5(pCID),
(char)MMC_CID_PNM4(pCID),
(char)MMC_CID_PNM3(pCID),
(char)MMC_CID_PNM2(pCID),
(char)MMC_CID_PNM1(pCID),
(char)MMC_CID_PNM0(pCID));
TRACE_INFO(" .PRV Product revision (SD) %x\n\r",
SD_CID_PRV(pCID));
TRACE_INFO(" .PRV Product revision (MMC) %x\n\r",
MMC_CID_PRV(pCID));
TRACE_INFO(" .PSN Product serial number (SD) %02X%02X%02X%02X\n\r",
SD_CID_PSN3(pCID),
SD_CID_PSN2(pCID),
SD_CID_PSN1(pCID),
SD_CID_PSN0(pCID));
TRACE_INFO(" .PSN Product serial number (MMC) %02X%02X%02X%02X\n\r",
MMC_CID_PSN3(pCID),
MMC_CID_PSN2(pCID),
MMC_CID_PSN1(pCID),
MMC_CID_PSN0(pCID));
TRACE_INFO(" .MDT Manufacturing date (SD) %04d/%02d\n\r",
(uint16_t)(SD_CID_MDT_Y(pCID) + 2000),
(uint8_t)SD_CID_MDT_M(pCID));
TRACE_INFO(" .MDT Manufacturing date (MMC) %04d/%02d\n\r",
(uint16_t)(MMC_CID_MDT_Y(pCID) + 1997),
(uint8_t)SD_CID_MDT_M(pCID));
TRACE_INFO(" .CRC checksum %02X\n\r",
SD_CID_CRC(pCID));
}
/**
* Display the content of the CSD register
* \param pSd Pointer to \ref sSdCard instance.
*/
void SD_DumpCSD(void* pCSD)
{
TRACE_INFO("======== CSD ========");
_DumpREG(pCSD, 128/8);
TRACE_INFO("===================\n\r");
TRACE_INFO(" .CSD_STRUCTURE 0x%x\r\n", SD_CSD_STRUCTURE(pCSD));
TRACE_INFO(" .SPEC_VERS (eMMC) 0x%x\r\n", MMC_CSD_SPEC_VERS(pCSD));
TRACE_INFO(" .TAAC 0x%X\r\n", SD_CSD_TAAC(pCSD) );
TRACE_INFO(" .NSAC 0x%X\r\n", SD_CSD_NSAC(pCSD) );
TRACE_INFO(" .TRAN_SPEED 0x%X\r\n", SD_CSD_TRAN_SPEED(pCSD) );
TRACE_INFO(" .CCC 0x%X\r\n", SD_CSD_CCC(pCSD) );
TRACE_INFO(" .READ_BL_LEN 0x%X\r\n", SD_CSD_READ_BL_LEN(pCSD) );
TRACE_INFO(" .READ_BL_PARTIAL 0x%X\r\n", SD_CSD_READ_BL_PARTIAL(pCSD) );
TRACE_INFO(" .WRITE_BLK_MISALIGN 0x%X\r\n", SD_CSD_WRITE_BLK_MISALIGN(pCSD));
TRACE_INFO(" .READ_BLK_MISALIGN 0x%X\r\n", SD_CSD_READ_BLK_MISALIGN(pCSD) );
TRACE_INFO(" .DSR_IMP 0x%X\r\n", SD_CSD_DSR_IMP(pCSD) );
TRACE_INFO(" .C_SIZE 0x%X\r\n", SD_CSD_C_SIZE(pCSD) );
TRACE_INFO(" .C_SIZE_HC 0x%X\r\n", SD2_CSD_C_SIZE(pCSD) );
TRACE_INFO(" .VDD_R_CURR_MIN 0x%X\r\n", SD_CSD_VDD_R_CURR_MIN(pCSD) );
TRACE_INFO(" .VDD_R_CURR_MAX 0x%X\r\n", SD_CSD_VDD_R_CURR_MAX(pCSD) );
TRACE_INFO(" .VDD_W_CURR_MIN 0x%X\r\n", SD_CSD_VDD_W_CURR_MIN(pCSD) );
TRACE_INFO(" .VDD_W_CURR_MAX 0x%X\r\n", SD_CSD_VDD_W_CURR_MAX(pCSD) );
TRACE_INFO(" .C_SIZE_MULT 0x%X\r\n", SD_CSD_C_SIZE_MULT(pCSD) );
TRACE_INFO(" .ERASE_BLK_EN 0x%X\r\n", SD_CSD_ERASE_BLK_EN(pCSD) );
TRACE_INFO(" .SECTOR_SIZE 0x%X\r\n", SD_CSD_SECTOR_SIZE(pCSD) );
TRACE_INFO(" .WP_GRP_SIZE 0x%X\r\n", SD_CSD_WP_GRP_SIZE(pCSD) );
TRACE_INFO(" .WP_GRP_ENABLE 0x%X\r\n", SD_CSD_WP_GRP_ENABLE(pCSD) );
TRACE_INFO(" .R2W_FACTOR 0x%X\r\n", SD_CSD_R2W_FACTOR(pCSD) );
TRACE_INFO(" .WRITE_BL_LEN 0x%X\r\n", SD_CSD_WRITE_BL_LEN(pCSD) );
TRACE_INFO(" .WRITE_BL_PARTIAL 0x%X\r\n", SD_CSD_WRITE_BL_PARTIAL(pCSD) );
TRACE_INFO(" .FILE_FORMAT_GRP 0x%X\r\n", SD_CSD_FILE_FORMAT_GRP(pCSD) );
TRACE_INFO(" .COPY 0x%X\r\n", SD_CSD_COPY(pCSD) );
TRACE_INFO(" .PERM_WRITE_PROTECT 0x%X\r\n", SD_CSD_PERM_WRITE_PROTECT(pCSD));
TRACE_INFO(" .TMP_WRITE_PROTECT 0x%X\r\n", SD_CSD_TMP_WRITE_PROTECT(pCSD) );
TRACE_INFO(" .FILE_FORMAT 0x%X\r\n", SD_CSD_FILE_FORMAT(pCSD) );
TRACE_INFO(" .ECC (MMC) 0x%X\r\n", MMC_CSD_ECC(pCSD) );
TRACE_INFO(" .CRC 0x%X\r\n", SD_CSD_CRC(pCSD) );
TRACE_INFO(" .MULT 0x%X\r\n", SD_CSD_MULT(pCSD) );
TRACE_INFO(" .BLOCKNR 0x%X\r\n", SD_CSD_BLOCKNR(pCSD) );
TRACE_INFO(" .BLOCKNR_HC 0x%X\r\n", SD_CSD_BLOCKNR_HC(pCSD) );
TRACE_INFO(" .BLOCK_LEN 0x%X\r\n", SD_CSD_BLOCK_LEN(pCSD) );
TRACE_INFO(" -TOTAL_SIZE 0x%X\r\n", SD_CSD_TOTAL_SIZE(pCSD) );
TRACE_INFO(" -TOTAL_SIZE_HC 0x%X\r\n", SD_CSD_TOTAL_SIZE_HC(pCSD) );
}
/**
* Display the content of the EXT_CSD register
* \param pExtCSD Pointer to extended CSD data.
*/
void SD_DumpExtCSD(void* pExtCSD)
{
/* Remove warnings */
pExtCSD = pExtCSD;
TRACE_INFO("======= EXT_CSD =======");
TRACE_INFO_WP("\n\r");
TRACE_INFO(" .S_CMD_SET : 0x%X\n\r",
MMC_EXT_S_CMD_SET(pExtCSD));
TRACE_INFO(" .BOOT_INFO : 0x%X\n\r",
MMC_EXT_BOOT_INFO(pExtCSD));
TRACE_INFO(" .BOOT_SIZE_MULTI : 0x%X\n\r",
MMC_EXT_BOOT_SIZE_MULTI(pExtCSD));
TRACE_INFO(" .ACC_SIZE : 0x%X\n\r",
MMC_EXT_ACC_SIZE(pExtCSD));
TRACE_INFO(" .HC_ERASE_GRP_SIZE : 0x%X\n\r",
MMC_EXT_HC_ERASE_GRP_SIZE(pExtCSD));
TRACE_INFO(" .ERASE_TIMEOUT_MULT : 0x%X\n\r",
MMC_EXT_ERASE_TIMEOUT_MULT(pExtCSD));
TRACE_INFO(" .REL_WR_SEC_C : 0x%X\n\r",
MMC_EXT_REL_WR_SEC_C(pExtCSD));
TRACE_INFO(" .HC_WP_GRP_SIZE : 0x%X\n\r",
MMC_EXT_HC_WP_GRP_SIZE(pExtCSD));
TRACE_INFO(" .S_C_VCC : 0x%X\n\r",
MMC_EXT_S_C_VCC(pExtCSD));
TRACE_INFO(" .S_C_VCCQ : 0x%X\n\r",
MMC_EXT_S_C_VCCQ(pExtCSD));
TRACE_INFO(" .S_A_TIMEOUT : 0x%X\n\r",
MMC_EXT_S_A_TIMEOUT(pExtCSD));
TRACE_INFO(" .SEC_COUNT : 0x%X\n\r",
MMC_EXT_SEC_COUNT(pExtCSD));
TRACE_INFO(" .MIN_PERF_W_8_52 : 0x%X\n\r",
MMC_EXT_MIN_PERF_W_8_52(pExtCSD));
TRACE_INFO(" .MIN_PERF_R_8_52 : 0x%X\n\r",
MMC_EXT_MIN_PERF_R_8_52(pExtCSD));
TRACE_INFO(" .MIN_PERF_W_8_26_4_52 : 0x%X\n\r",
MMC_EXT_MIN_PERF_W_8_26_4_52(pExtCSD));
TRACE_INFO(" .MIN_PERF_R_8_26_4_52 : 0x%X\n\r",
MMC_EXT_MIN_PERF_R_8_26_4_52(pExtCSD));
TRACE_INFO(" .MIN_PERF_W_4_26 : 0x%X\n\r",
MMC_EXT_MIN_PERF_W_4_26(pExtCSD));
TRACE_INFO(" .MIN_PERF_R_4_26 : 0x%X\n\r",
MMC_EXT_MIN_PERF_R_4_26(pExtCSD));
TRACE_INFO(" .PWR_CL_26_360 : 0x%X\n\r",
MMC_EXT_PWR_CL_26_360(pExtCSD));
TRACE_INFO(" .PWR_CL_52_360 : 0x%X\n\r",
MMC_EXT_PWR_CL_52_360(pExtCSD));
TRACE_INFO(" .PWR_CL_26_195 : 0x%X\n\r",
MMC_EXT_PWR_CL_26_195(pExtCSD));
TRACE_INFO(" .PWR_CL_52_195 : 0x%X\n\r",
MMC_EXT_PWR_CL_52_195(pExtCSD));
TRACE_INFO(" .CARD_TYPE : 0x%X\n\r",
MMC_EXT_CARD_TYPE(pExtCSD));
TRACE_INFO(" .CSD_STRUCTURE : 0x%X\n\r",
MMC_EXT_CSD_STRUCTURE(pExtCSD));
TRACE_INFO(" .EXT_CSD_REV : 0x%X\n\r",
MMC_EXT_EXT_CSD_REV(pExtCSD));
TRACE_INFO(" .CMD_SET : 0x%X\n\r",
MMC_EXT_CMD_SET(pExtCSD));
TRACE_INFO(" .CMD_SET_REV : 0x%X\n\r",
MMC_EXT_CMD_SET_REV(pExtCSD));
TRACE_INFO(" .POWER_CLASS : 0x%X\n\r",
MMC_EXT_POWER_CLASS(pExtCSD));
TRACE_INFO(" .HS_TIMING : 0x%X\n\r",
MMC_EXT_HS_TIMING(pExtCSD));
TRACE_INFO(" .BUS_WIDTH : 0x%X\n\r",
MMC_EXT_BUS_WIDTH(pExtCSD));
TRACE_INFO(" .ERASED_MEM_CONT : 0x%X\n\r",
MMC_EXT_ERASED_MEM_CONT(pExtCSD));
TRACE_INFO(" .BOOT_CONFIG : 0x%X\n\r",
MMC_EXT_BOOT_CONFIG(pExtCSD));
TRACE_INFO(" .BOOT_BUS_WIDTH : 0x%X\n\r",
MMC_EXT_BOOT_BUS_WIDTH(pExtCSD));
TRACE_INFO(" .ERASE_GROUP_DEF : 0x%X\n\r",
MMC_EXT_ERASE_GROUP_DEF(pExtCSD));
}
/**
* Display the content of the SCR register
* \param pSCR Pointer to SCR data.
*/
void SD_DumpSCR(void *pSCR)
{
/* Remove warnings */
pSCR = pSCR;
TRACE_INFO("========== SCR ==========");
TRACE_INFO_WP("\n\r");
TRACE_INFO(" .SCR_STRUCTURE :0x%X\n\r",
SD_SCR_STRUCTURE(pSCR));
TRACE_INFO(" .SD_SPEC :0x%X\n\r",
SD_SCR_SD_SPEC(pSCR));
TRACE_INFO(" .DATA_STAT_AFTER_ERASE :0x%X\n\r",
SD_SCR_DATA_STAT_AFTER_ERASE(pSCR));
TRACE_INFO(" .SD_SECURITY :0x%X\n\r",
SD_SCR_SD_SECURITY(pSCR));
TRACE_INFO(" .SD_BUS_WIDTHS :0x%X\n\r",
SD_SCR_SD_BUS_WIDTHS(pSCR));
}
/**
* Display the content of the SD Status
* \param pSdST Pointer to SD card status data.
*/
void SD_DumpSdStatus(void* pSdST)
{
/* Remove warnings */
pSdST = pSdST;
TRACE_INFO("=========== STAT ============");
TRACE_INFO_WP("\n\r");
TRACE_INFO(" .DAT_BUS_WIDTH :0x%X\n\r",
SD_ST_DAT_BUS_WIDTH(pSdST));
TRACE_INFO(" .SECURED_MODE :0x%X\n\r",
SD_ST_SECURED_MODE(pSdST));
TRACE_INFO(" .SD_CARD_TYPE :0x%X\n\r",
SD_ST_CARD_TYPE(pSdST));
TRACE_INFO(" .SIZE_OF_PROTECTED_AREA :0x%X\n\r",
SD_ST_SIZE_OF_PROTECTED_AREA(pSdST));
TRACE_INFO(" .SPEED_CLASS :0x%X\n\r",
SD_ST_SPEED_CLASS(pSdST));
TRACE_INFO(" .PERFORMANCE_MOVE :0x%X\n\r",
SD_ST_PERFORMANCE_MOVE(pSdST));
TRACE_INFO(" .AU_SIZE :0x%X\n\r",
SD_ST_AU_SIZE(pSdST));
TRACE_INFO(" .ERASE_SIZE :0x%X\n\r",
SD_ST_ERASE_SIZE(pSdST));
TRACE_INFO(" .ERASE_TIMEOUT :0x%X\n\r",
SD_ST_ERASE_TIMEOUT(pSdST));
TRACE_INFO(" .ERASE_OFFSET :0x%X\n\r",
SD_ST_ERASE_OFFSET(pSdST));
}
/**@}*/