audk/QuarkSocPkg/QuarkSouthCluster/Sdio/Dxe/SDMediaDeviceDxe/MMCSDTransfer.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;
}