Пример #1
0
/**
  Implements EFI_BLOCK_IO_PROTOCOL.Reset() function.

  @param  This                   The EFI_BLOCK_IO_PROTOCOL instance.
  @param  ExtendedVerification   Indicates that the driver may perform a more exhaustive.
                                 verification operation of the device during reset.
                                 (This parameter is ingored in this driver.)

  @retval EFI_SUCCESS                Success
**/
EFI_STATUS
EFIAPI
MMCSDBlockReset (
    IN  EFI_BLOCK_IO_PROTOCOL   *This,
    IN  BOOLEAN                 ExtendedVerification
)
{
    CARD_DATA                  *CardData;
    EFI_SD_HOST_IO_PROTOCOL    *SDHostIo;

    CardData  = CARD_DATA_FROM_THIS(This);
    SDHostIo = CardData->SDHostIo;

    return SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD);
}
Пример #2
0
/**
  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;
}
Пример #3
0
/**
  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;
}
Пример #4
0
/**
  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;
}
Пример #5
0
/**
  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;
        }
Пример #6
0
/**
  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;
  EFI_SD_HOST_IO_PROTOCOL    *SDHostIo;
  UINT8                      Index; 
  UINT8                      Retry; 
  UINT32                     TimeOut;


  SDHostIo = CardData->SDHostIo;
  Status   = EFI_SUCCESS;
  //
  //First try the high voltage, then if supported choose the low voltage
  // 
  for (Index = 0; Index < 2; Index++) { 

    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 (
                  SDHostIo,
                  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 (
                  SDHostIo,
                  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;
    }

    if (CardData->OCRRegister.V170_V195 == 1) {
      CardData->DualVoltage = TRUE;
    }
    if (CardData->OCRRegister.V270_V360 != 0x1F &&
        CardData->OCRRegister.V200_V260 != 0) {
      DEBUG((EFI_D_ERROR, "Incompatiable voltage device\n"));
      PutCardInactive (CardData);
      Status = EFI_INCOMPATIBLE_VERSION;
      goto Exit;
    }
 
    if (Index == 0) {
      //
      //Choose the high voltage first
      //
      CardData->OCRRegister.V170_V195 = 0;
    } else {
      //
      //Choose the low voltage
      //
      CardData->OCRRegister.V170_V195 = 1;
      CardData->OCRRegister.V270_V360 = 0;
    }

    //
    //TimeOut Value, 5000 * 100 * 1000 = 5 s
    // 
    TimeOut = 5000;

    do {
      Status  = SendCommand (
                  SDHostIo,
                  SEND_OP_COND,
                  *(UINT32*)&(CardData->OCRRegister),
                  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--;
      DEBUG((EFI_D_ERROR, "Card is always in busy state, timeout:%d(in 5000)\n", TimeOut));
      if (TimeOut == 0) {
        Status = EFI_TIMEOUT;
        goto Exit;
      }
    } while (CardData->OCRRegister.Busy != 1);

    if (CardData->DualVoltage == TRUE && SDHostIo->HostCapability.V18Support == TRUE) {
      //
      //Power Off the card and then power on into low voltage
      //
      SDHostIo->SetHostVoltage (SDHostIo, 0);
      gBS->Stall (1 * 1000);
      SDHostIo->SetHostVoltage (SDHostIo, 18);
    } else {
      //
      //Not support the low voltage, exit
      //
      break;
    }
  }

Exit:
  return Status;

}