/** @file

MMC/SD transfer specific functions

Copyright (c) 2013-2015 Intel Corporation.

SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#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;
}

