mirror of https://github.com/acidanthera/audk.git
1715 lines
46 KiB
C
1715 lines
46 KiB
C
/** @file
|
|
|
|
MMC/SD transfer specific functions
|
|
|
|
Copyright (c) 2013-2015 Intel Corporation.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "SDMediaDevice.h"
|
|
|
|
/**
|
|
Check card status, print the debug info and check the error
|
|
|
|
@param Status Status got from card status register.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CheckCardStatus (
|
|
IN UINT32 Status
|
|
)
|
|
{
|
|
CARD_STATUS *CardStatus;
|
|
CardStatus = (CARD_STATUS*)(&Status);
|
|
|
|
if (CardStatus->ADDRESS_OUT_OF_RANGE) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ADDRESS_OUT_OF_RANGE\n"));
|
|
}
|
|
|
|
if (CardStatus->ADDRESS_MISALIGN) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ADDRESS_MISALIGN\n"));
|
|
}
|
|
|
|
if (CardStatus->BLOCK_LEN_ERROR) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: BLOCK_LEN_ERROR\n"));
|
|
}
|
|
|
|
if (CardStatus->ERASE_SEQ_ERROR) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ERASE_SEQ_ERROR\n"));
|
|
}
|
|
|
|
if (CardStatus->ERASE_PARAM) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ERASE_PARAM\n"));
|
|
}
|
|
|
|
if (CardStatus->WP_VIOLATION) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: WP_VIOLATION\n"));
|
|
}
|
|
|
|
if (CardStatus->CARD_IS_LOCKED) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: CARD_IS_LOCKED\n"));
|
|
}
|
|
|
|
if (CardStatus->LOCK_UNLOCK_FAILED) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: LOCK_UNLOCK_FAILED\n"));
|
|
}
|
|
|
|
if (CardStatus->COM_CRC_ERROR) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: COM_CRC_ERROR\n"));
|
|
}
|
|
|
|
if (CardStatus->ILLEGAL_COMMAND) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ILLEGAL_COMMAND\n"));
|
|
}
|
|
|
|
if (CardStatus->CARD_ECC_FAILED) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: CARD_ECC_FAILED\n"));
|
|
}
|
|
|
|
if (CardStatus->CC_ERROR) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: CC_ERROR\n"));
|
|
}
|
|
|
|
if (CardStatus->ERROR) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ERROR\n"));
|
|
}
|
|
|
|
if (CardStatus->UNDERRUN) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: UNDERRUN\n"));
|
|
}
|
|
|
|
if (CardStatus->OVERRUN) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: OVERRUN\n"));
|
|
}
|
|
|
|
if (CardStatus->CID_CSD_OVERWRITE) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: CID_CSD_OVERWRITE\n"));
|
|
}
|
|
|
|
if (CardStatus->WP_ERASE_SKIP) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: WP_ERASE_SKIP\n"));
|
|
}
|
|
|
|
if (CardStatus->ERASE_RESET) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: ERASE_RESET\n"));
|
|
}
|
|
|
|
if (CardStatus->SWITCH_ERROR) {
|
|
DEBUG ((EFI_D_ERROR, "CardStatus: SWITCH_ERROR\n"));
|
|
}
|
|
|
|
if ((Status & 0xFCFFA080) != 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Send command by using Host IO protocol
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param CommandIndex The command index to set the command index field of command register.
|
|
@param Argument Command argument to set the argument field of command register.
|
|
@param DataType TRANSFER_TYPE, indicates no data, data in or data out.
|
|
@param Buffer Contains the data read from / write to the device.
|
|
@param BufferSize The size of the buffer.
|
|
@param ResponseType RESPONSE_TYPE.
|
|
@param TimeOut Time out value in 1 ms unit.
|
|
@param ResponseData Depending on the ResponseType, such as CSD or card status.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SendCommand (
|
|
IN CARD_DATA *CardData,
|
|
IN UINT16 CommandIndex,
|
|
IN UINT32 Argument,
|
|
IN TRANSFER_TYPE DataType,
|
|
IN UINT8 *Buffer, OPTIONAL
|
|
IN UINT32 BufferSize,
|
|
IN RESPONSE_TYPE ResponseType,
|
|
IN UINT32 TimeOut,
|
|
OUT UINT32 *ResponseData
|
|
)
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
|
|
SDHostIo = CardData->SDHostIo;
|
|
if (CardData->CardType != MMCCard && CardData->CardType != MMCCardHighCap) {
|
|
CommandIndex |= AUTO_CMD12_ENABLE;
|
|
}
|
|
|
|
Status = SDHostIo->SendCommand (
|
|
SDHostIo,
|
|
CommandIndex,
|
|
Argument,
|
|
DataType,
|
|
Buffer,
|
|
BufferSize,
|
|
ResponseType,
|
|
TimeOut,
|
|
ResponseData
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) {
|
|
ASSERT(ResponseData != NULL);
|
|
Status = CheckCardStatus (*ResponseData);
|
|
}
|
|
} else {
|
|
SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Send the card APP_CMD command with the following command indicated by CommandIndex
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
@param CommandIndex The command index to set the command index field of command register.
|
|
@param Argument Command argument to set the argument field of command register.
|
|
@param DataType TRANSFER_TYPE, indicates no data, data in or data out.
|
|
@param Buffer Contains the data read from / write to the device.
|
|
@param BufferSize The size of the buffer.
|
|
@param ResponseType RESPONSE_TYPE.
|
|
@param TimeOut Time out value in 1 ms unit.
|
|
@param ResponseData Depending on the ResponseType, such as CSD or card status.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SendAppCommand (
|
|
IN CARD_DATA *CardData,
|
|
IN UINT16 CommandIndex,
|
|
IN UINT32 Argument,
|
|
IN TRANSFER_TYPE DataType,
|
|
IN UINT8 *Buffer, OPTIONAL
|
|
IN UINT32 BufferSize,
|
|
IN RESPONSE_TYPE ResponseType,
|
|
IN UINT32 TimeOut,
|
|
OUT UINT32 *ResponseData
|
|
)
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
|
|
UINT8 Index;
|
|
|
|
SDHostIo = CardData->SDHostIo;
|
|
Status = EFI_SUCCESS;
|
|
|
|
for (Index = 0; Index < 2; Index++) {
|
|
Status = SDHostIo->SendCommand (
|
|
SDHostIo,
|
|
APP_CMD,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = CheckCardStatus (*(UINT32*)&(CardData->CardStatus));
|
|
if (CardData->CardStatus.SAPP_CMD != 1) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
}
|
|
if (!EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
} else {
|
|
SDHostIo->ResetSDHost (SDHostIo, Reset_Auto);
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
if (CardData->CardType != MMCCard && CardData->CardType != MMCCardHighCap) {
|
|
CommandIndex |= AUTO_CMD12_ENABLE;
|
|
}
|
|
|
|
Status = SDHostIo->SendCommand (
|
|
SDHostIo,
|
|
CommandIndex,
|
|
Argument,
|
|
DataType,
|
|
Buffer,
|
|
BufferSize,
|
|
ResponseType,
|
|
TimeOut,
|
|
ResponseData
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) {
|
|
ASSERT(ResponseData != NULL);
|
|
Status = CheckCardStatus (*ResponseData);
|
|
}
|
|
} else {
|
|
SDHostIo->ResetSDHost (SDHostIo, Reset_Auto);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Send the card FAST_IO command
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
@param RegisterAddress Register Address.
|
|
@param RegisterData Pointer to register Data.
|
|
@param Write TRUE for write, FALSE for read.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FastIO (
|
|
IN CARD_DATA *CardData,
|
|
IN UINT8 RegisterAddress,
|
|
IN OUT UINT8 *RegisterData,
|
|
IN BOOLEAN Write
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Argument;
|
|
UINT32 Data;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (RegisterData == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
Argument = (CardData->Address << 16) | (RegisterAddress << 8);
|
|
if (Write) {
|
|
Argument |= BIT15 | (*RegisterData);
|
|
}
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
FAST_IO,
|
|
Argument,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR4,
|
|
TIMEOUT_COMMAND,
|
|
&Data
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if ((Data & BIT15) == 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!Write) {
|
|
*RegisterData = (UINT8)Data;
|
|
}
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Send the card GO_INACTIVE_STATE command.
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
|
|
@return EFI_SUCCESS
|
|
@return others
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PutCardInactive (
|
|
IN CARD_DATA *CardData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
GO_INACTIVE_STATE,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseNo,
|
|
TIMEOUT_COMMAND,
|
|
NULL
|
|
);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Get card interested information for CSD rergister
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CaculateCardParameter (
|
|
IN CARD_DATA *CardData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Frequency;
|
|
UINT32 Multiple;
|
|
UINT32 CSize;
|
|
CSD_SDV2 *CsdSDV2;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
switch (CardData->CSDRegister.TRAN_SPEED & 0x7) {
|
|
case 0:
|
|
Frequency = 100 * 1000;
|
|
break;
|
|
|
|
case 1:
|
|
Frequency = 1 * 1000 * 1000;
|
|
break;
|
|
|
|
case 2:
|
|
Frequency = 10 * 1000 * 1000;
|
|
break;
|
|
|
|
case 3:
|
|
Frequency = 100 * 1000 * 1000;
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
switch ((CardData->CSDRegister.TRAN_SPEED >> 3) & 0xF) {
|
|
case 1:
|
|
Multiple = 10;
|
|
break;
|
|
|
|
case 2:
|
|
Multiple = 12;
|
|
break;
|
|
|
|
case 3:
|
|
Multiple = 13;
|
|
break;
|
|
|
|
case 4:
|
|
Multiple = 15;
|
|
break;
|
|
|
|
case 5:
|
|
Multiple = 20;
|
|
break;
|
|
|
|
case 6:
|
|
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) {
|
|
Multiple = 26;
|
|
} else {
|
|
Multiple = 25;
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
Multiple = 30;
|
|
break;
|
|
|
|
case 8:
|
|
Multiple = 35;
|
|
break;
|
|
|
|
case 9:
|
|
Multiple = 40;
|
|
break;
|
|
|
|
case 10:
|
|
Multiple = 45;
|
|
break;
|
|
|
|
case 11:
|
|
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) {
|
|
Multiple = 52;
|
|
} else {
|
|
Multiple = 50;
|
|
}
|
|
break;
|
|
|
|
case 12:
|
|
Multiple = 55;
|
|
break;
|
|
|
|
case 13:
|
|
Multiple = 60;
|
|
break;
|
|
|
|
case 14:
|
|
Multiple = 70;
|
|
break;
|
|
|
|
case 15:
|
|
Multiple = 80;
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
Frequency = Frequency * Multiple / 10;
|
|
CardData->MaxFrequency = Frequency;
|
|
|
|
CardData->BlockLen = 1 << CardData->CSDRegister.READ_BL_LEN;
|
|
|
|
if (CardData->CardType == SDMemoryCard2High) {
|
|
ASSERT(CardData->CSDRegister.CSD_STRUCTURE == 1);
|
|
CsdSDV2 = (CSD_SDV2*)&CardData->CSDRegister;
|
|
//
|
|
// The SD Spec 2.0 says (CSize + 1) * 512K is the total size, so block numbber is (CSize + 1) * 1K
|
|
// the K here means 1024 not 1000
|
|
//
|
|
CardData->BlockNumber = DivU64x32 (MultU64x32 (CsdSDV2->C_SIZE + 1, 512 * 1024) , CardData->BlockLen);
|
|
} else {
|
|
//
|
|
// For MMC card > 2G, the block number will be recaculate later
|
|
//
|
|
CSize = CardData->CSDRegister.C_SIZELow2 | (CardData->CSDRegister.C_SIZEHigh10 << 2);
|
|
CardData->BlockNumber = MultU64x32 (LShiftU64 (1, CardData->CSDRegister.C_SIZE_MULT + 2), CSize + 1);
|
|
}
|
|
|
|
//
|
|
//For >= 2G card, BlockLen may be 1024, but the transfer size is still 512 bytes
|
|
//
|
|
if (CardData->BlockLen > 512) {
|
|
CardData->BlockNumber = DivU64x32 (MultU64x32 (CardData->BlockNumber, CardData->BlockLen), 512);
|
|
CardData->BlockLen = 512;
|
|
}
|
|
|
|
DEBUG((
|
|
EFI_D_INFO,
|
|
"CalculateCardParameter: Card Size: 0x%lx\n", MultU64x32 (CardData->BlockNumber, CardData->BlockLen)
|
|
));
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Test the bus width setting for MMC card.It is used only for verification purpose.
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
@param Width 1, 4, 8 bits.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
MMCCardBusWidthTest (
|
|
IN CARD_DATA *CardData,
|
|
IN UINT32 Width
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 Data;
|
|
UINT64 Value;
|
|
|
|
ASSERT(CardData != NULL);
|
|
|
|
|
|
Value = 0;
|
|
|
|
switch (Width) {
|
|
case 1:
|
|
Data = 0x80;
|
|
break;
|
|
|
|
case 4:
|
|
Data = 0x5A;
|
|
break;
|
|
|
|
case 8:
|
|
Data = 0xAA55;
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
CopyMem (CardData->AlignedBuffer, &Data, Width);
|
|
Status = SendCommand (
|
|
CardData,
|
|
BUSTEST_W,
|
|
0,
|
|
OutData,
|
|
CardData->AlignedBuffer,
|
|
Width,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "MMCCardBusWidthTest:SendCommand BUSTEST_W 0x%x\n", *(UINT32*)&(CardData->CardStatus)));
|
|
goto Exit;
|
|
}
|
|
|
|
gBS->Stall (10 * 1000);
|
|
|
|
Data = 0;
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
BUSTEST_R,
|
|
0,
|
|
InData,
|
|
CardData->AlignedBuffer,
|
|
Width,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "MMCCardBusWidthTest:SendCommand BUSTEST_R 0x%x\n", *(UINT32*)&(CardData->CardStatus)));
|
|
goto Exit;
|
|
}
|
|
CopyMem (&Data, CardData->AlignedBuffer, Width);
|
|
|
|
switch (Width) {
|
|
case 1:
|
|
Value = (~(Data ^ 0x80)) & 0xC0;
|
|
break;
|
|
case 4:
|
|
Value = (~(Data ^ 0x5A)) & 0xFF;
|
|
break;
|
|
case 8:
|
|
Value = (~(Data ^ 0xAA55)) & 0xFFFF;
|
|
break;
|
|
}
|
|
|
|
if (Value == 0) {
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This function can detect these card types:
|
|
1. MMC card
|
|
2. SD 1.1 card
|
|
3. SD 2.0 standard card
|
|
3. SD 2.0 high capacity card
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
|
|
@return EFI_SUCCESS
|
|
@return others
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetCardType (
|
|
IN CARD_DATA *CardData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
|
|
UINT32 Argument;
|
|
UINT32 ResponseData;
|
|
UINT32 Count;
|
|
BOOLEAN SDCommand8Support;
|
|
|
|
|
|
SDHostIo = CardData->SDHostIo;
|
|
|
|
//
|
|
// Reset the card
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
GO_IDLE_STATE,
|
|
0,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseNo,
|
|
TIMEOUT_COMMAND,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "GO_IDLE_STATE Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
//No spec requirment, can be adjusted
|
|
//
|
|
gBS->Stall (10 * 1000);
|
|
|
|
|
|
//
|
|
// Only 2.7V - 3.6V is supported for SD2.0, only SD 2.0 card can pass
|
|
// MMC and SD1.1 card will fail this command
|
|
//
|
|
Argument = (VOLTAGE_27_36 << 8) | CHECK_PATTERN;
|
|
ResponseData = 0;
|
|
SDCommand8Support = FALSE;
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_IF_COND,
|
|
Argument,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR7,
|
|
TIMEOUT_COMMAND,
|
|
&ResponseData
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status != EFI_TIMEOUT) {
|
|
DEBUG((EFI_D_ERROR, "SEND_IF_COND Fail, none time out error\n"));
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
if (ResponseData != Argument) {
|
|
DEBUG((EFI_D_ERROR, "SEND_IF_COND Fail, respond data does not match send data\n"));
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto Exit;
|
|
}
|
|
SDCommand8Support = TRUE;
|
|
}
|
|
|
|
|
|
Argument = 0;
|
|
if (SDHostIo->HostCapability.V30Support == TRUE) {
|
|
Argument |= BIT17 | BIT18;
|
|
} else if (SDHostIo->HostCapability.V33Support == TRUE) {
|
|
Argument |= BIT20 | BIT21;
|
|
}
|
|
|
|
if (SDCommand8Support) {
|
|
//
|
|
//If command SD_SEND_OP_COND sucessed, it should be set.
|
|
// SD 1.1 card will ignore it
|
|
// SD 2.0 standard card will repsond with CCS 0, SD high capacity card will respond with CCS 1
|
|
// CCS is BIT30 of OCR
|
|
Argument |= BIT30;
|
|
}
|
|
|
|
|
|
Count = 20;
|
|
//
|
|
//Only SD card will respond to this command, and spec says the card only checks condition at first ACMD41 command
|
|
//
|
|
do {
|
|
Status = SendAppCommand (
|
|
CardData,
|
|
SD_SEND_OP_COND,
|
|
Argument,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR3,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->OCRRegister)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
if ((Status == EFI_TIMEOUT) && (!SDCommand8Support)) {
|
|
CardData->CardType = MMCCard;
|
|
Status = EFI_SUCCESS;
|
|
DEBUG((EFI_D_INFO, "SD_SEND_OP_COND, MMC card was identified\n"));
|
|
} else {
|
|
//
|
|
// Not as expected, MMC card should has no response, which means timeout.
|
|
// SD card should pass this command
|
|
//
|
|
DEBUG((EFI_D_ERROR, "SD_SEND_OP_COND Fail, check whether it is neither a MMC card nor a SD card\n"));
|
|
}
|
|
goto Exit;
|
|
}
|
|
//
|
|
//Avoid waiting if sucess. Busy bit 0 means not ready
|
|
//
|
|
if (CardData->OCRRegister.Busy == 1) {
|
|
break;
|
|
}
|
|
|
|
gBS->Stall (50 * 1000);
|
|
Count--;
|
|
if (Count == 0) {
|
|
DEBUG((EFI_D_ERROR, "Card is always in busy state\n"));
|
|
Status = EFI_TIMEOUT;
|
|
goto Exit;
|
|
}
|
|
} while (1);
|
|
|
|
//
|
|
//Check supported voltage
|
|
//
|
|
Argument = 0;
|
|
if (SDHostIo->HostCapability.V30Support == TRUE) {
|
|
if ((CardData->OCRRegister.V270_V360 & BIT2) == BIT2) {
|
|
Argument |= BIT17;
|
|
} else if ((CardData->OCRRegister.V270_V360 & BIT3) == BIT3) {
|
|
Argument |= BIT18;
|
|
}
|
|
} else if (SDHostIo->HostCapability.V33Support == TRUE) {
|
|
if ((CardData->OCRRegister.V270_V360 & BIT5) == BIT5) {
|
|
Argument |= BIT20;
|
|
} else if ((CardData->OCRRegister.V270_V360 & BIT6) == BIT6) {
|
|
Argument |= BIT21;
|
|
}
|
|
}
|
|
|
|
if (Argument == 0) {
|
|
//
|
|
//No matched support voltage
|
|
//
|
|
PutCardInactive (CardData);
|
|
DEBUG((EFI_D_ERROR, "No matched voltage for this card\n"));
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
CardData->CardType = SDMemoryCard;
|
|
if (SDCommand8Support == TRUE) {
|
|
CardData->CardType = SDMemoryCard2;
|
|
DEBUG((EFI_D_INFO, "SD_SEND_OP_COND, SD 2.0 or above standard card was identified\n"));
|
|
}
|
|
|
|
if ((CardData->OCRRegister.AccessMode & BIT1) == BIT1) {
|
|
CardData->CardType = SDMemoryCard2High;
|
|
DEBUG((EFI_D_INFO, "SD_SEND_OP_COND, SD 2.0 or above high capacity card was identified\n"));
|
|
}
|
|
|
|
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
MMC card high/low voltage selection function
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_BAD_BUFFER_SIZE
|
|
|
|
**/
|
|
EFI_STATUS
|
|
MMCCardVoltageSelection (
|
|
IN CARD_DATA *CardData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 Retry;
|
|
UINT32 TimeOut;
|
|
|
|
Status = EFI_SUCCESS;
|
|
//
|
|
//First try the high voltage, then if supported choose the low voltage
|
|
//
|
|
|
|
for (Retry = 0; Retry < 3; Retry++) {
|
|
//
|
|
// To bring back the normal MMC card to work
|
|
// after sending the SD command. Otherwise some
|
|
// card could not work
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
GO_IDLE_STATE,
|
|
0,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseNo,
|
|
TIMEOUT_COMMAND,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "GO_IDLE_STATE Fail Status = 0x%x\n", Status));
|
|
continue;
|
|
}
|
|
//
|
|
//CE-ATA device needs long delay
|
|
//
|
|
gBS->Stall ((Retry + 1) * 50 * 1000);
|
|
|
|
//
|
|
//Get OCR register to check voltage support, first time the OCR is 0
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_OP_COND,
|
|
0,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR3,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->OCRRegister)
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Retry == 3) {
|
|
DEBUG((EFI_D_ERROR, "SEND_OP_COND Fail Status = 0x%x\n", Status));
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
//TimeOut Value, 5000 * 100 * 1000 = 5 s
|
|
//
|
|
TimeOut = 5000;
|
|
|
|
do {
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_OP_COND,
|
|
0x40300000,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR3,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->OCRRegister)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SEND_OP_COND Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
gBS->Stall (1 * 1000);
|
|
TimeOut--;
|
|
if (TimeOut == 0) {
|
|
Status = EFI_TIMEOUT;
|
|
DEBUG((EFI_D_ERROR, "Card is always in busy state\n"));
|
|
goto Exit;
|
|
}
|
|
} while (CardData->OCRRegister.Busy != 1);
|
|
|
|
if (CardData->OCRRegister.AccessMode == 2) // eMMC Card uses Sector Addressing - High Capacity
|
|
{
|
|
DEBUG((EFI_D_INFO, "eMMC Card is High Capacity\n"));
|
|
CardData->CardType = MMCCardHighCap;
|
|
}
|
|
|
|
Exit:
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
This function set the bus and device width for MMC card
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
@param Width 1, 4, 8 bits.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
MMCCardSetBusWidth (
|
|
IN CARD_DATA *CardData,
|
|
IN UINT8 BusWidth,
|
|
IN BOOLEAN EnableDDRMode
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
|
|
SWITCH_ARGUMENT SwitchArgument;
|
|
UINT8 Value;
|
|
|
|
SDHostIo = CardData->SDHostIo;
|
|
Value = 0;
|
|
switch (BusWidth) {
|
|
case 8:
|
|
if (EnableDDRMode)
|
|
Value = 6;
|
|
else
|
|
Value = 2;
|
|
break;
|
|
|
|
case 4:
|
|
if (EnableDDRMode)
|
|
Value = 5;
|
|
else
|
|
Value = 1;
|
|
break;
|
|
|
|
case 1:
|
|
if (EnableDDRMode) // Bus width 1 is not supported in ddr mode
|
|
return EFI_UNSUPPORTED;
|
|
Value = 0;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
|
|
ZeroMem(&SwitchArgument, sizeof (SWITCH_ARGUMENT));
|
|
SwitchArgument.CmdSet = 0;
|
|
SwitchArgument.Value = Value;
|
|
SwitchArgument.Index = (UINT32)((UINTN)
|
|
(&(CardData->ExtCSDRegister.BUS_WIDTH)) - (UINTN)(&(CardData->ExtCSDRegister)));
|
|
SwitchArgument.Access = WriteByte_Mode;
|
|
Status = SendCommand (
|
|
CardData,
|
|
SWITCH,
|
|
*(UINT32*)&SwitchArgument,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1b,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_STATUS,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SWITCH %d bits Fail\n", BusWidth));
|
|
goto Exit;
|
|
} else {
|
|
DEBUG((EFI_D_ERROR, "MMCCardSetBusWidth:SWITCH Card Status:0x%x\n", *(UINT32*)&(CardData->CardStatus)));
|
|
Status = SDHostIo->SetBusWidth (SDHostIo, BusWidth);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SWITCH set %d bits Fail\n", BusWidth));
|
|
goto Exit;
|
|
}
|
|
gBS->Stall (5 * 1000);
|
|
}
|
|
}
|
|
|
|
if (!EnableDDRMode) { // CMD19 and CMD14 are illegal commands in ddr mode
|
|
//if (EFI_ERROR (Status)) {
|
|
// DEBUG((EFI_D_ERROR, "MMCCardBusWidthTest: Fail to enable high speed mode\n"));
|
|
// goto Exit;
|
|
//}
|
|
|
|
Status = MMCCardBusWidthTest (CardData, BusWidth);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "MMCCardBusWidthTest %d bit Fail\n", BusWidth));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
CardData->CurrentBusWidth = BusWidth;
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
MMC/SD card init function
|
|
|
|
@param CardData Pointer to CARD_DATA.
|
|
|
|
@return EFI_SUCCESS
|
|
@return others
|
|
|
|
**/
|
|
EFI_STATUS
|
|
MMCSDCardInit (
|
|
IN CARD_DATA *CardData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
|
|
SWITCH_ARGUMENT SwitchArgument;
|
|
UINT32 Data;
|
|
UINT32 Argument;
|
|
UINT32 nIndex;
|
|
UINT8 PowerValue;
|
|
BOOLEAN EnableDDRMode;
|
|
|
|
ASSERT(CardData != NULL);
|
|
SDHostIo = CardData->SDHostIo;
|
|
EnableDDRMode = FALSE;
|
|
|
|
CardData->CardType = UnknownCard;
|
|
Status = GetCardType (CardData);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
DEBUG((DEBUG_INFO, "CardData->CardType 0x%x\n", CardData->CardType));
|
|
|
|
ASSERT (CardData->CardType != UnknownCard);
|
|
//
|
|
//MMC, SD card need host auto stop command support
|
|
//
|
|
SDHostIo->EnableAutoStopCmd (SDHostIo, TRUE);
|
|
|
|
if (CardData->CardType == MMCCard) {
|
|
Status = MMCCardVoltageSelection (CardData);
|
|
if (EFI_ERROR(Status)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get CID Register
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
ALL_SEND_CID,
|
|
0,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR2,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CIDRegister)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "ALL_SEND_CID Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
} else {
|
|
// Dump out the Card ID data
|
|
DEBUG((EFI_D_INFO, "Product Name: "));
|
|
for ( nIndex=0; nIndex<6; nIndex++ ) {
|
|
DEBUG((EFI_D_INFO, "%c", CardData->CIDRegister.PNM[nIndex]));
|
|
}
|
|
DEBUG((EFI_D_INFO, "\nApplication ID : %d\n", CardData->CIDRegister.OID));
|
|
DEBUG((EFI_D_INFO, "Manufacturer ID: %d\n", CardData->CIDRegister.MID));
|
|
DEBUG((EFI_D_INFO, "Revision ID : %d\n", CardData->CIDRegister.PRV));
|
|
DEBUG((EFI_D_INFO, "Serial Number : %d\n", CardData->CIDRegister.PSN));
|
|
}
|
|
|
|
//
|
|
//SET_RELATIVE_ADDR
|
|
//
|
|
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) {
|
|
//
|
|
//Hard code the RCA address
|
|
//
|
|
CardData->Address = 1;
|
|
|
|
//
|
|
// Set RCA Register
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SET_RELATIVE_ADDR,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_RELATIVE_ADDR Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
Data = 0;
|
|
Status = SendCommand (
|
|
CardData,
|
|
SET_RELATIVE_ADDR,
|
|
0,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR6,
|
|
TIMEOUT_COMMAND,
|
|
&Data
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_RELATIVE_ADDR Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
CardData->Address = (UINT16)(Data >> 16);
|
|
*(UINT32*)&CardData->CardStatus = Data & 0x1FFF;
|
|
CardData->CardStatus.ERROR = (Data >> 13) & 0x1;
|
|
CardData->CardStatus.ILLEGAL_COMMAND = (Data >> 14) & 0x1;
|
|
CardData->CardStatus.COM_CRC_ERROR = (Data >> 15) & 0x1;
|
|
Status = CheckCardStatus (*(UINT32*)&CardData->CardStatus);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_RELATIVE_ADDR Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get CSD Register
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_CSD,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR2,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CSDRegister)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SEND_CSD Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
DEBUG((EFI_D_INFO, "CardData->CSDRegister.SPEC_VERS = 0x%x\n", CardData->CSDRegister.SPEC_VERS));
|
|
DEBUG((EFI_D_INFO, "CardData->CSDRegister.CSD_STRUCTURE = 0x%x\n", CardData->CSDRegister.CSD_STRUCTURE));
|
|
|
|
Status = CaculateCardParameter (CardData);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// It is platform and hardware specific, need hadrware engineer input
|
|
//
|
|
if (CardData->CSDRegister.DSR_IMP == 1) {
|
|
//
|
|
// Default is 0x404
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SET_DSR,
|
|
(DEFAULT_DSR_VALUE << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseNo,
|
|
TIMEOUT_COMMAND,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_DSR Fail Status = 0x%x\n", Status));
|
|
//
|
|
// Assume can operate even fail
|
|
//
|
|
}
|
|
}
|
|
//
|
|
//Change clock frequency from 400KHz to max supported when not in high speed mode
|
|
//
|
|
Status = SDHostIo->SetClockFrequency (SDHostIo, CardData->MaxFrequency);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "MMCSDCardInit:Fail to SetClockFrequency \n"));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
//Put the card into tran state
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SELECT_DESELECT_CARD,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SELECT_DESELECT_CARD Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// No spec requirment, can be adjusted
|
|
//
|
|
gBS->Stall (5 * 1000);
|
|
//
|
|
// No need to do so
|
|
//
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_STATUS,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SELECT_DESELECT_CARD SEND_STATUS Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
//
|
|
//if the SPEC_VERS indicates a version 4.0 or higher
|
|
//The card is a high speed card and support Switch
|
|
//and Send_ext_csd command
|
|
//otherwise it is an old card
|
|
//
|
|
|
|
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) {
|
|
//
|
|
//Only V4.0 and above supports more than 1 bits and high speed
|
|
//
|
|
if (CardData->CSDRegister.SPEC_VERS >= 4) {
|
|
//
|
|
//Get ExtCSDRegister
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_EXT_CSD,
|
|
0x0,
|
|
InData,
|
|
CardData->AlignedBuffer,
|
|
sizeof (EXT_CSD),
|
|
ResponseR1,
|
|
TIMEOUT_DATA,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SEND_EXT_CSD Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
CopyMem (&(CardData->ExtCSDRegister), CardData->AlignedBuffer, sizeof (EXT_CSD));
|
|
|
|
//
|
|
// Recaculate the block number for >2G MMC card
|
|
//
|
|
Data = (CardData->ExtCSDRegister.SEC_COUNT[0]) |
|
|
(CardData->ExtCSDRegister.SEC_COUNT[1] << 8) |
|
|
(CardData->ExtCSDRegister.SEC_COUNT[2] << 16) |
|
|
(CardData->ExtCSDRegister.SEC_COUNT[3] << 24);
|
|
|
|
if (Data != 0) {
|
|
CardData->BlockNumber = Data;
|
|
}
|
|
DEBUG((DEBUG_INFO, "CardData->BlockNumber %d\n", Data));
|
|
DEBUG((EFI_D_ERROR, "CardData->ExtCSDRegister.CARD_TYPE -> %d\n", (UINTN)CardData->ExtCSDRegister.CARD_TYPE));
|
|
if ((CardData->ExtCSDRegister.CARD_TYPE & BIT2)||
|
|
(CardData->ExtCSDRegister.CARD_TYPE & BIT3)) {
|
|
//DEBUG((DEBUG_INFO, "To enable DDR mode\n"));
|
|
//EnableDDRMode = TRUE;
|
|
}
|
|
//
|
|
// Check current chipset capability and the plugged-in card
|
|
// whether supports HighSpeed
|
|
//
|
|
if (SDHostIo->HostCapability.HighSpeedSupport) {
|
|
|
|
//
|
|
//Change card timing to high speed interface timing
|
|
//
|
|
ZeroMem(&SwitchArgument, sizeof (SWITCH_ARGUMENT));
|
|
SwitchArgument.CmdSet = 0;
|
|
SwitchArgument.Value = 1;
|
|
SwitchArgument.Index = (UINT32)((UINTN)
|
|
(&(CardData->ExtCSDRegister.HS_TIMING)) - (UINTN)(&(CardData->ExtCSDRegister)));
|
|
SwitchArgument.Access = WriteByte_Mode;
|
|
Status = SendCommand (
|
|
CardData,
|
|
SWITCH,
|
|
*(UINT32*)&SwitchArgument,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1b,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "MMCSDCardInit:SWITCH frequency Fail Status = 0x%x\n", Status));
|
|
}
|
|
|
|
gBS->Stall (5 * 1000);
|
|
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_STATUS,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (EnableDDRMode) {
|
|
DEBUG((EFI_D_ERROR, "Enable ddr mode on host controller\n"));
|
|
SDHostIo->SetDDRMode (SDHostIo, TRUE);
|
|
} else {
|
|
DEBUG((EFI_D_ERROR, "Enable high speed mode on host controller\n"));
|
|
SDHostIo->SetHighSpeedMode (SDHostIo, TRUE);
|
|
}
|
|
//
|
|
// Change host clock to support high speed and enable chispet to
|
|
// support speed
|
|
//
|
|
if ((CardData->ExtCSDRegister.CARD_TYPE & BIT1) != 0) {
|
|
Status = SDHostIo->SetClockFrequency (SDHostIo, FREQUENCY_MMC_PP_HIGH);
|
|
} else if ((CardData->ExtCSDRegister.CARD_TYPE & BIT0) != 0) {
|
|
Status = SDHostIo->SetClockFrequency (SDHostIo, FREQUENCY_MMC_PP);
|
|
} else {
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "MMCSDCardInit:Fail to SetClockFrequency \n"));
|
|
goto Exit;
|
|
}
|
|
//
|
|
// It seems no need to stall after changing bus freqeuncy.
|
|
// It is said that the freqeuncy can be changed at any time. Just appends 8 clocks after command.
|
|
// But SetClock alreay has delay.
|
|
//
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Prefer wide bus width for performance
|
|
//
|
|
//
|
|
// Set to BusWidth bits mode, only version 4.0 or above support more than 1 bits
|
|
//
|
|
if (SDHostIo->HostCapability.BusWidth8 == TRUE) {
|
|
Status = MMCCardSetBusWidth (CardData, 8, EnableDDRMode);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// CE-ATA may support 8 bits and 4 bits, but has no software method for detection
|
|
//
|
|
Status = MMCCardSetBusWidth (CardData, 4, EnableDDRMode);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
} else if (SDHostIo->HostCapability.BusWidth4 == TRUE) {
|
|
Status = MMCCardSetBusWidth (CardData, 4, EnableDDRMode);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
PowerValue = 0;
|
|
|
|
if (CardData->CurrentBusWidth == 8) {
|
|
if ((CardData->ExtCSDRegister.CARD_TYPE & BIT1) != 0) {
|
|
PowerValue = CardData->ExtCSDRegister.PWR_CL_52_360;
|
|
PowerValue = PowerValue >> 4;
|
|
} else if ((CardData->ExtCSDRegister.CARD_TYPE & BIT0) != 0) {
|
|
PowerValue = CardData->ExtCSDRegister.PWR_CL_26_360;
|
|
PowerValue = PowerValue >> 4;
|
|
}
|
|
} else if (CardData->CurrentBusWidth == 4) {
|
|
if ((CardData->ExtCSDRegister.CARD_TYPE & BIT1) != 0) {
|
|
PowerValue = CardData->ExtCSDRegister.PWR_CL_52_360;
|
|
PowerValue = PowerValue & 0xF;
|
|
} else if ((CardData->ExtCSDRegister.CARD_TYPE & BIT0) != 0) {
|
|
PowerValue = CardData->ExtCSDRegister.PWR_CL_26_360;
|
|
PowerValue = PowerValue & 0xF;
|
|
}
|
|
}
|
|
|
|
if (PowerValue != 0) {
|
|
//
|
|
//Update Power Class
|
|
//
|
|
ZeroMem(&SwitchArgument, sizeof (SWITCH_ARGUMENT));
|
|
SwitchArgument.CmdSet = 0;
|
|
SwitchArgument.Value = PowerValue;
|
|
SwitchArgument.Index = (UINT32)((UINTN)
|
|
(&(CardData->ExtCSDRegister.POWER_CLASS)) - (UINTN)(&(CardData->ExtCSDRegister)));
|
|
SwitchArgument.Access = WriteByte_Mode;
|
|
Status = SendCommand (
|
|
CardData,
|
|
SWITCH,
|
|
*(UINT32*)&SwitchArgument,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1b,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = SendCommand (
|
|
CardData,
|
|
SEND_STATUS,
|
|
(CardData->Address << 16),
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SWITCH Power Class Fail Status = 0x%x\n", Status));
|
|
}
|
|
//gBS->Stall (10 * 1000);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
DEBUG((EFI_D_ERROR, "MMC Card version %d only supportes 1 bits at lower transfer speed\n",CardData->CSDRegister.SPEC_VERS));
|
|
}
|
|
} else {
|
|
//
|
|
// Pin 1, at power up this line has a 50KOhm pull up enabled in the card.
|
|
// This pull-up should be disconnected by the user, during regular data transfer,
|
|
// with SET_CLR_CARD_DETECT (ACMD42) command
|
|
//
|
|
Status = SendAppCommand (
|
|
CardData,
|
|
SET_CLR_CARD_DETECT,
|
|
0,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_CLR_CARD_DETECT Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
//
|
|
// Don't rely on SCR and SD status, some cards have unexpected SCR.
|
|
// It only sets private section, the other bits are 0
|
|
// such as Sandisk Ultra II 4.0G, KinSton mini SD 128M, Toshiba 2.0GB
|
|
// Some card even fail this command, KinSton SD 4GB
|
|
//
|
|
Status = SendAppCommand (
|
|
CardData,
|
|
SEND_SCR,
|
|
0,
|
|
InData,
|
|
(UINT8*)&(CardData->SCRRegister),
|
|
sizeof(SCR),
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// SD memory card at least supports 1 and 4 bits.
|
|
//
|
|
// ASSERT ((CardData->SCRRegister.SD_BUS_WIDTH & (BIT0 | BIT2)) == (BIT0 | BIT2));
|
|
*/
|
|
|
|
//
|
|
// Set Bus Width to 4
|
|
//
|
|
Status = SendAppCommand (
|
|
CardData,
|
|
SET_BUS_WIDTH,
|
|
SD_BUS_WIDTH_4,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_BUS_WIDTH 4 bits Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
Status = SDHostIo->SetBusWidth (SDHostIo, 4);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
CardData->CurrentBusWidth = 4;
|
|
|
|
|
|
if ((SDHostIo->HostCapability.HighSpeedSupport == FALSE) ||
|
|
((CardData->CSDRegister.CCC & BIT10) != BIT10)) {
|
|
//
|
|
// Host must support high speed
|
|
// Card must support Switch function
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
//Mode = 0, group 1, function 1, check operation
|
|
//
|
|
Argument = 0xFFFF01;
|
|
ZeroMem (&CardData->SwitchStatus, sizeof (SWITCH_STATUS));
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
SWITCH_FUNC,
|
|
Argument,
|
|
InData,
|
|
CardData->AlignedBuffer,
|
|
sizeof (SWITCH_STATUS),
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
CopyMem (&(CardData->SwitchStatus), CardData->AlignedBuffer, sizeof (SWITCH_STATUS));
|
|
|
|
if ((CardData->SwitchStatus.DataStructureVersion == 0x0) ||
|
|
((CardData->SwitchStatus.Group1BusyStatus & BIT1) != BIT1)) {
|
|
//
|
|
// 1. SD 1.1 card does not suppport busy bit
|
|
// 2. Ready state
|
|
//
|
|
//
|
|
|
|
//
|
|
//Mode = 1, group 1, function 1, BIT31 set means set mode
|
|
//
|
|
Argument = 0xFFFF01 | BIT31;
|
|
ZeroMem (&CardData->SwitchStatus, sizeof (SWITCH_STATUS));
|
|
|
|
Status = SendCommand (
|
|
CardData,
|
|
SWITCH_FUNC,
|
|
Argument,
|
|
InData,
|
|
CardData->AlignedBuffer,
|
|
sizeof (SWITCH_STATUS),
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
CopyMem (&(CardData->SwitchStatus), CardData->AlignedBuffer, sizeof (SWITCH_STATUS));
|
|
|
|
if ((CardData->SwitchStatus.DataStructureVersion == 0x0) ||
|
|
((CardData->SwitchStatus.Group1BusyStatus & BIT1) != BIT1)) {
|
|
//
|
|
// 1. SD 1.1 card does not suppport busy bit
|
|
// 2. Ready state
|
|
//
|
|
|
|
//
|
|
// 8 clocks, (1/ 25M) * 8 ==> 320 us, so 1ms > 0.32 ms
|
|
//
|
|
gBS->Stall (1000);
|
|
|
|
//
|
|
//Change host clock
|
|
//
|
|
Status = SDHostIo->SetClockFrequency (SDHostIo, FREQUENCY_SD_PP_HIGH);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if (!((CardData->ExtCSDRegister.CARD_TYPE & BIT2) ||
|
|
(CardData->ExtCSDRegister.CARD_TYPE & BIT3))) {
|
|
|
|
//
|
|
// Set Block Length, to improve compatibility in case of some cards
|
|
//
|
|
Status = SendCommand (
|
|
CardData,
|
|
SET_BLOCKLEN,
|
|
512,
|
|
NoData,
|
|
NULL,
|
|
0,
|
|
ResponseR1,
|
|
TIMEOUT_COMMAND,
|
|
(UINT32*)&(CardData->CardStatus)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG((EFI_D_ERROR, "SET_BLOCKLEN Fail Status = 0x%x\n", Status));
|
|
goto Exit;
|
|
}
|
|
}
|
|
SDHostIo->SetBlockLength (SDHostIo, 512);
|
|
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|