Meenakshi Aggarwal ea21f1d98d SD : Updated CMD 6 implememtation.
For setting high speed in SD card,
First CMD 6 (Switch) is send to check if card supports High Speed and
Second command is send to switch card to high speed mode.

In current inplementation, CMD 6 was sent only once to switch the
card into HS mode without checking if card supports HS or not, which is
not as per specification and also we are not setting the HS i.e. 50000000
but directly asking the card to switch to 26000000 which is incorrect as
SD card supports either 25000000 or 50000000.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
Reviewed-by: Jun Nie <jun.nie@linaro.org>
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
2017-09-07 17:06:44 +01:00

807 lines
26 KiB
C
Executable File

/** @file
*
* Copyright (c) 2011-2015, ARM Limited. All rights reserved.
*
* 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 <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/TimerLib.h>
#include "Mmc.h"
typedef union {
UINT32 Raw;
OCR Ocr;
} OCR_RESPONSE;
#define MAX_RETRY_COUNT 1000
#define CMD_RETRY_COUNT 20
#define RCA_SHIFT_OFFSET 16
#define EMMC_CARD_SIZE 512
#define EMMC_ECSD_SIZE_OFFSET 53
#define EXTCSD_BUS_WIDTH 183
#define EXTCSD_HS_TIMING 185
#define EMMC_TIMING_BACKWARD 0
#define EMMC_TIMING_HS 1
#define EMMC_TIMING_HS200 2
#define EMMC_TIMING_HS400 3
#define EMMC_BUS_WIDTH_1BIT 0
#define EMMC_BUS_WIDTH_4BIT 1
#define EMMC_BUS_WIDTH_8BIT 2
#define EMMC_BUS_WIDTH_DDR_4BIT 5
#define EMMC_BUS_WIDTH_DDR_8BIT 6
#define EMMC_SWITCH_ERROR (1 << 7)
#define SD_BUS_WIDTH_1BIT (1 << 0)
#define SD_BUS_WIDTH_4BIT (1 << 2)
#define SD_CCC_SWITCH (1 << 10)
#define DEVICE_STATE(x) (((x) >> 9) & 0xf)
typedef enum _EMMC_DEVICE_STATE {
EMMC_IDLE_STATE = 0,
EMMC_READY_STATE,
EMMC_IDENT_STATE,
EMMC_STBY_STATE,
EMMC_TRAN_STATE,
EMMC_DATA_STATE,
EMMC_RCV_STATE,
EMMC_PRG_STATE,
EMMC_DIS_STATE,
EMMC_BTST_STATE,
EMMC_SLP_STATE
} EMMC_DEVICE_STATE;
UINT32 mEmmcRcaCount = 0;
STATIC
EFI_STATUS
EFIAPI
EmmcGetDeviceState (
IN MMC_HOST_INSTANCE *MmcHostInstance,
OUT EMMC_DEVICE_STATE *State
)
{
EFI_MMC_HOST_PROTOCOL *Host;
EFI_STATUS Status;
UINT32 Data, RCA;
if (State == NULL) {
return EFI_INVALID_PARAMETER;
}
Host = MmcHostInstance->MmcHost;
RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
Status = Host->SendCommand (Host, MMC_CMD13, RCA);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to get card status, Status=%r.\n", Status));
return Status;
}
Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R1, &Data);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to get response of CMD13, Status=%r.\n", Status));
return Status;
}
if (Data & EMMC_SWITCH_ERROR) {
DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to switch expected mode, Status=%r.\n", Status));
return EFI_DEVICE_ERROR;
}
*State = DEVICE_STATE(Data);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
EmmcSetEXTCSD (
IN MMC_HOST_INSTANCE *MmcHostInstance,
UINT32 ExtCmdIndex,
UINT32 Value
)
{
EFI_MMC_HOST_PROTOCOL *Host;
EMMC_DEVICE_STATE State;
EFI_STATUS Status;
UINT32 Argument;
Host = MmcHostInstance->MmcHost;
Argument = EMMC_CMD6_ARG_ACCESS(3) | EMMC_CMD6_ARG_INDEX(ExtCmdIndex) |
EMMC_CMD6_ARG_VALUE(Value) | EMMC_CMD6_ARG_CMD_SET(1);
Status = Host->SendCommand (Host, MMC_CMD6, Argument);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcSetEXTCSD(): Failed to send CMD6, Status=%r.\n", Status));
return Status;
}
// Make sure device exiting prog mode
do {
Status = EmmcGetDeviceState (MmcHostInstance, &State);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcSetEXTCSD(): Failed to get device state, Status=%r.\n", Status));
return Status;
}
} while (State == EMMC_PRG_STATE);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
EmmcIdentificationMode (
IN MMC_HOST_INSTANCE *MmcHostInstance,
IN OCR_RESPONSE Response
)
{
EFI_MMC_HOST_PROTOCOL *Host;
EFI_BLOCK_IO_MEDIA *Media;
EFI_STATUS Status;
EMMC_DEVICE_STATE State;
UINT32 RCA;
Host = MmcHostInstance->MmcHost;
Media = MmcHostInstance->BlockIo.Media;
// Fetch card identity register
Status = Host->SendCommand (Host, MMC_CMD2, 0);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to send CMD2, Status=%r.\n", Status));
return Status;
}
Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CIDData));
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): CID retrieval error, Status=%r.\n", Status));
return Status;
}
// Assign a relative address value to the card
MmcHostInstance->CardInfo.RCA = ++mEmmcRcaCount; // TODO: might need a more sophisticated way of doing this
RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
Status = Host->SendCommand (Host, MMC_CMD3, RCA);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): RCA set error, Status=%r.\n", Status));
return Status;
}
// Fetch card specific data
Status = Host->SendCommand (Host, MMC_CMD9, RCA);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to send CMD9, Status=%r.\n", Status));
return Status;
}
Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CSDData));
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): CSD retrieval error, Status=%r.\n", Status));
return Status;
}
// Select the card
Status = Host->SendCommand (Host, MMC_CMD7, RCA);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status));
}
if (MMC_HOST_HAS_SETIOS(Host)) {
// Set 1-bit bus width
Status = Host->SetIos (Host, 0, 1, EMMCBACKWARD);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Set 1-bit bus width error, Status=%r.\n", Status));
return Status;
}
// Set 1-bit bus width for EXTCSD
Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_1BIT);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Set extcsd bus width error, Status=%r.\n", Status));
return Status;
}
}
// Fetch ECSD
MmcHostInstance->CardInfo.ECSDData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (ECSD)));
if (MmcHostInstance->CardInfo.ECSDData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Host->SendCommand (Host, MMC_CMD8, 0);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): ECSD fetch error, Status=%r.\n", Status));
}
Status = Host->ReadBlockData (Host, 0, 512, (UINT32 *)MmcHostInstance->CardInfo.ECSDData);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): ECSD read error, Status=%r.\n", Status));
goto FreePageExit;
}
// Make sure device exiting data mode
do {
Status = EmmcGetDeviceState (MmcHostInstance, &State);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to get device state, Status=%r.\n", Status));
goto FreePageExit;
}
} while (State == EMMC_DATA_STATE);
// Set up media
Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards
Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;
Media->ReadOnly = MmcHostInstance->CardInfo.CSDData.PERM_WRITE_PROTECT;
Media->LogicalBlocksPerPhysicalBlock = 1;
Media->IoAlign = 4;
// Compute last block using bits [215:212] of the ECSD
Media->LastBlock = MmcHostInstance->CardInfo.ECSDData->SECTOR_COUNT - 1; // eMMC isn't supposed to report this for
// Cards <2GB in size, but the model does.
// Setup card type
MmcHostInstance->CardInfo.CardType = EMMC_CARD;
return EFI_SUCCESS;
FreePageExit:
FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD)));
return Status;
}
STATIC
EFI_STATUS
InitializeEmmcDevice (
IN MMC_HOST_INSTANCE *MmcHostInstance
)
{
EFI_MMC_HOST_PROTOCOL *Host;
EFI_STATUS Status = EFI_SUCCESS;
ECSD *ECSDData;
UINT32 BusClockFreq, Idx, BusMode;
UINT32 TimingMode[4] = {EMMCHS52DDR1V2, EMMCHS52DDR1V8, EMMCHS52, EMMCHS26};
Host = MmcHostInstance->MmcHost;
ECSDData = MmcHostInstance->CardInfo.ECSDData;
if (ECSDData->DEVICE_TYPE == EMMCBACKWARD)
return EFI_SUCCESS;
if (!MMC_HOST_HAS_SETIOS(Host)) {
return EFI_SUCCESS;
}
Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_HS_TIMING, EMMC_TIMING_HS);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to switch high speed mode, Status:%r.\n", Status));
return Status;
}
for (Idx = 0; Idx < 4; Idx++) {
switch (TimingMode[Idx]) {
case EMMCHS52DDR1V2:
case EMMCHS52DDR1V8:
case EMMCHS52:
BusClockFreq = 52000000;
break;
case EMMCHS26:
BusClockFreq = 26000000;
break;
default:
return EFI_UNSUPPORTED;
}
Status = Host->SetIos (Host, BusClockFreq, 8, TimingMode[Idx]);
if (!EFI_ERROR (Status)) {
switch (TimingMode[Idx]) {
case EMMCHS52DDR1V2:
case EMMCHS52DDR1V8:
BusMode = EMMC_BUS_WIDTH_DDR_8BIT;
break;
case EMMCHS52:
case EMMCHS26:
BusMode = EMMC_BUS_WIDTH_8BIT;
break;
default:
return EFI_UNSUPPORTED;
}
Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, BusMode);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to set EXTCSD bus width, Status:%r\n", Status));
}
return Status;
}
}
return Status;
}
STATIC
UINT32
CreateSwitchCmdArgument (
IN UINT32 Mode,
IN UINT8 Group,
IN UINT8 Value
)
{
UINT32 Argument;
Argument = Mode << 31 | 0x00FFFFFF;
Argument &= ~(0xF << (Group * 4));
Argument |= Value << (Group * 4);
return Argument;
}
STATIC
EFI_STATUS
InitializeSdMmcDevice (
IN MMC_HOST_INSTANCE *MmcHostInstance
)
{
UINT32 CmdArg;
UINT32 Response[4];
UINT32 Buffer[128];
UINT32 Speed;
UINTN BlockSize;
UINTN CardSize;
UINTN NumBlocks;
BOOLEAN CccSwitch;
SCR Scr;
EFI_STATUS Status;
EFI_MMC_HOST_PROTOCOL *MmcHost;
Speed = SD_DEFAULT_SPEED;
MmcHost = MmcHostInstance->MmcHost;
// Send a command to get Card specific data
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand (MmcHost, MMC_CMD9, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeSdMmcDevice(MMC_CMD9): Error, Status=%r\n", Status));
return Status;
}
// Read Response
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CSD, Response);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeSdMmcDevice(): Failed to receive CSD, Status=%r\n", Status));
return Status;
}
PrintCSD (Response);
if (MMC_CSD_GET_CCC(Response) & SD_CCC_SWITCH) {
CccSwitch = TRUE;
} else {
CccSwitch = FALSE;
}
if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {
CardSize = HC_MMC_CSD_GET_DEVICESIZE (Response);
NumBlocks = ((CardSize + 1) * 1024);
BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);
} else {
CardSize = MMC_CSD_GET_DEVICESIZE (Response);
NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT (Response) + 2));
BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);
}
// For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.
if (BlockSize > 512) {
NumBlocks = MultU64x32 (NumBlocks, BlockSize / 512);
BlockSize = 512;
}
MmcHostInstance->BlockIo.Media->LastBlock = (NumBlocks - 1);
MmcHostInstance->BlockIo.Media->BlockSize = BlockSize;
MmcHostInstance->BlockIo.Media->ReadOnly = MmcHost->IsReadOnly (MmcHost);
MmcHostInstance->BlockIo.Media->MediaPresent = TRUE;
MmcHostInstance->BlockIo.Media->MediaId++;
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand (MmcHost, MMC_CMD7, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeSdMmcDevice(MMC_CMD7): Error and Status = %r\n", Status));
return Status;
}
Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
if ((Response[0] & MMC_STATUS_APP_CMD) == 0) {
return EFI_SUCCESS;
}
/* SCR */
Status = MmcHost->SendCommand (MmcHost, MMC_ACMD51, 0);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "%a(MMC_ACMD51): Error and Status = %r\n", __func__, Status));
return Status;
} else {
Status = MmcHost->ReadBlockData (MmcHost, 0, 8, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "%a(MMC_ACMD51): ReadBlockData Error and Status = %r\n", __func__, Status));
return Status;
}
CopyMem (&Scr, Buffer, 8);
if (Scr.SD_SPEC == 2) {
if (Scr.SD_SPEC3 == 1) {
if (Scr.SD_SPEC4 == 1) {
DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 4.xx\n"));
} else {
DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 3.0x\n"));
}
} else {
if (Scr.SD_SPEC4 == 0) {
DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 2.0\n"));
} else {
DEBUG ((EFI_D_ERROR, "Found invalid SD Card\n"));
}
}
} else {
if ((Scr.SD_SPEC3 == 0) && (Scr.SD_SPEC4 == 0)) {
if (Scr.SD_SPEC == 1) {
DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 1.10\n"));
} else {
DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 1.0\n"));
}
} else {
DEBUG ((EFI_D_ERROR, "Found invalid SD Card\n"));
}
}
}
if (CccSwitch) {
/* SD Switch, Mode:0, Group:0, Value:0 */
CmdArg = CreateSwitchCmdArgument(0, 0, 0);
Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD6): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
} else {
Status = MmcHost->ReadBlockData (MmcHost, 0, SWITCH_CMD_DATA_LENGTH, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD6): ReadBlockData Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
}
if (!(Buffer[3] & SD_HIGH_SPEED_SUPPORTED)) {
DEBUG ((DEBUG_ERROR, "%a : High Speed not supported by Card %r\n", __FUNCTION__, Status));
return Status;
}
Speed = SD_HIGH_SPEED;
/* SD Switch, Mode:1, Group:0, Value:1 */
CmdArg = CreateSwitchCmdArgument(1, 0, 1);
Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD6): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
} else {
Status = MmcHost->ReadBlockData (MmcHost, 0, SWITCH_CMD_DATA_LENGTH, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD6): ReadBlockData Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
if ((Buffer[4] & SWITCH_CMD_SUCCESS_MASK) != 0x01000000) {
DEBUG((DEBUG_ERROR, "Problem switching SD card into high-speed mode\n"));
return Status;
}
}
}
if (Scr.SD_BUS_WIDTHS & SD_BUS_WIDTH_4BIT) {
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
/* Width: 4 */
Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, 2);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (MMC_CMD6): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
}
if (MMC_HOST_HAS_SETIOS(MmcHost)) {
Status = MmcHost->SetIos (MmcHost, Speed, BUSWIDTH_4, EMMCBACKWARD);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a (SetIos): Error and Status = %r\n", __FUNCTION__, Status));
return Status;
}
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
MmcIdentificationMode (
IN MMC_HOST_INSTANCE *MmcHostInstance
)
{
EFI_STATUS Status;
UINT32 Response[4];
UINTN Timeout;
UINTN CmdArg;
BOOLEAN IsHCS;
EFI_MMC_HOST_PROTOCOL *MmcHost;
OCR_RESPONSE OcrResponse;
MmcHost = MmcHostInstance->MmcHost;
CmdArg = 0;
IsHCS = FALSE;
if (MmcHost == NULL) {
return EFI_INVALID_PARAMETER;
}
// We can get into this function if we restart the identification mode
if (MmcHostInstance->State == MmcHwInitializationState) {
// Initialize the MMC Host HW
Status = MmcNotifyState (MmcHostInstance, MmcHwInitializationState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcHwInitializationState, Status=%r.\n", Status));
return Status;
}
}
Status = MmcHost->SendCommand (MmcHost, MMC_CMD0, 0);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD0): Error, Status=%r.\n", Status));
return Status;
}
Status = MmcNotifyState (MmcHostInstance, MmcIdleState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcIdleState, Status=%r.\n", Status));
return Status;
}
// Send CMD1 to get OCR (MMC)
// This command only valid for MMC and eMMC
Timeout = MAX_RETRY_COUNT;
do {
Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);
if (EFI_ERROR (Status))
break;
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, (UINT32 *)&OcrResponse);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
return Status;
}
Timeout--;
} while (!OcrResponse.Ocr.PowerUp && (Timeout > 0));
if (Status == EFI_SUCCESS) {
if (!OcrResponse.Ocr.PowerUp) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD1): Card initialisation failure, Status=%r.\n", Status));
return EFI_DEVICE_ERROR;
}
OcrResponse.Ocr.PowerUp = 0;
if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB) {
MmcHostInstance->CardInfo.OCRData.AccessMode = BIT1;
}
else {
MmcHostInstance->CardInfo.OCRData.AccessMode = 0x0;
}
// Check whether MMC or eMMC
if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB ||
OcrResponse.Raw == EMMC_CMD1_CAPACITY_LESS_THAN_2GB) {
return EmmcIdentificationMode (MmcHostInstance, OcrResponse);
}
}
// Are we using SDIO ?
Status = MmcHost->SendCommand (MmcHost, MMC_CMD5, 0);
if (Status == EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD5): Error - SDIO not supported, Status=%r.\n", Status));
return EFI_UNSUPPORTED;
}
// Check which kind of card we are using. Ver2.00 or later SD Memory Card (PL180 is SD v1.1)
CmdArg = (0x0UL << 12 | BIT8 | 0xCEUL << 0);
Status = MmcHost->SendCommand (MmcHost, MMC_CMD8, CmdArg);
if (Status == EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Card is SD2.0 => Supports high capacity\n"));
IsHCS = TRUE;
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R7, Response);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive response to CMD8, Status=%r.\n", Status));
return Status;
}
PrintResponseR1 (Response[0]);
// Check if it is valid response
if (Response[0] != CmdArg) {
DEBUG ((EFI_D_ERROR, "The Card is not usable\n"));
return EFI_UNSUPPORTED;
}
} else {
DEBUG ((EFI_D_ERROR, "Not a SD2.0 Card\n"));
}
// We need to wait for the MMC or SD card is ready => (gCardInfo.OCRData.PowerUp == 1)
Timeout = MAX_RETRY_COUNT;
while (Timeout > 0) {
// SD Card or MMC Card ? CMD55 indicates to the card that the next command is an application specific command
Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, 0);
if (Status == EFI_SUCCESS) {
DEBUG ((EFI_D_INFO, "Card should be SD\n"));
if (IsHCS) {
MmcHostInstance->CardInfo.CardType = SD_CARD_2;
} else {
MmcHostInstance->CardInfo.CardType = SD_CARD;
}
// Note: The first time CmdArg will be zero
CmdArg = ((UINTN *) &(MmcHostInstance->CardInfo.OCRData))[0];
if (IsHCS) {
CmdArg |= BIT30;
}
Status = MmcHost->SendCommand (MmcHost, MMC_ACMD41, CmdArg);
if (!EFI_ERROR (Status)) {
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
return Status;
}
((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];
}
} else {
DEBUG ((EFI_D_INFO, "Card should be MMC\n"));
MmcHostInstance->CardInfo.CardType = MMC_CARD;
Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, 0x800000);
if (!EFI_ERROR (Status)) {
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
return Status;
}
((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];
}
}
if (!EFI_ERROR (Status)) {
if (!MmcHostInstance->CardInfo.OCRData.PowerUp) {
gBS->Stall (1);
Timeout--;
} else {
if ((MmcHostInstance->CardInfo.CardType == SD_CARD_2) && (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1)) {
MmcHostInstance->CardInfo.CardType = SD_CARD_2_HIGH;
DEBUG ((EFI_D_ERROR, "High capacity card.\n"));
}
break; // The MMC/SD card is ready. Continue the Identification Mode
}
} else {
gBS->Stall (1);
Timeout--;
}
}
if (Timeout == 0) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(): No Card\n"));
return EFI_NO_MEDIA;
} else {
PrintOCR (Response[0]);
}
Status = MmcNotifyState (MmcHostInstance, MmcReadyState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcReadyState\n"));
return Status;
}
Status = MmcHost->SendCommand (MmcHost, MMC_CMD2, 0);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD2): Error\n"));
return Status;
}
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CID, Response);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive CID, Status=%r.\n", Status));
return Status;
}
PrintCID (Response);
Status = MmcHost->NotifyState (MmcHost, MmcIdentificationState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcIdentificationState\n"));
return Status;
}
//
// Note, SD specifications say that "if the command execution causes a state change, it
// will be visible to the host in the response to the next command"
// The status returned for this CMD3 will be 2 - identification
//
CmdArg = 1;
Status = MmcHost->SendCommand (MmcHost, MMC_CMD3, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD3): Error\n"));
return Status;
}
Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_RCA, Response);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive RCA, Status=%r.\n", Status));
return Status;
}
PrintRCA (Response[0]);
// For MMC card, RCA is assigned by CMD3 while CMD3 dumps the RCA for SD card
if (MmcHostInstance->CardInfo.CardType != MMC_CARD) {
MmcHostInstance->CardInfo.RCA = Response[0] >> 16;
} else {
MmcHostInstance->CardInfo.RCA = CmdArg;
}
Status = MmcNotifyState (MmcHostInstance, MmcStandByState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcStandByState\n"));
return Status;
}
return EFI_SUCCESS;
}
EFI_STATUS
InitializeMmcDevice (
IN MMC_HOST_INSTANCE *MmcHostInstance
)
{
EFI_STATUS Status;
EFI_MMC_HOST_PROTOCOL *MmcHost;
UINTN BlockCount;
BlockCount = 1;
MmcHost = MmcHostInstance->MmcHost;
Status = MmcIdentificationMode (MmcHostInstance);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeMmcDevice(): Error in Identification Mode, Status=%r\n", Status));
return Status;
}
Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeMmcDevice(): Error MmcTransferState, Status=%r\n", Status));
return Status;
}
if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {
Status = InitializeSdMmcDevice (MmcHostInstance);
} else {
Status = InitializeEmmcDevice (MmcHostInstance);
}
if (EFI_ERROR (Status)) {
return Status;
}
// Set Block Length
Status = MmcHost->SendCommand (MmcHost, MMC_CMD16, MmcHostInstance->BlockIo.Media->BlockSize);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeMmcDevice(MMC_CMD16): Error MmcHostInstance->BlockIo.Media->BlockSize: %d and Error = %r\n",
MmcHostInstance->BlockIo.Media->BlockSize, Status));
return Status;
}
// Block Count (not used). Could return an error for SD card
if (MmcHostInstance->CardInfo.CardType == MMC_CARD) {
Status = MmcHost->SendCommand (MmcHost, MMC_CMD23, BlockCount);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "InitializeMmcDevice(MMC_CMD23): Error, Status=%r\n", Status));
return Status;
}
}
return EFI_SUCCESS;
}