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>
807 lines
26 KiB
C
Executable File
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;
|
|
}
|