diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c new file mode 100644 index 00000000..ecf3eb36 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c @@ -0,0 +1,799 @@ +/** @file +* +* Copyright (c) 2011-2015, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#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_INFO, "%a : High Speed not supported by Card\n", __FUNCTION__)); + } else { + 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; +} |