C++程序  |  2002行  |  61.6 KB

/** @file
  This driver is used to manage Designware SD/MMC PCI host controllers.

  It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use.

  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>

  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 "DwMmcHcDxe.h"

/**
  Dump the content of SD/MMC host controller's Capability Register.

  @param[in]  Slot            The slot number of the SD card to send the command to.
  @param[in]  Capability      The buffer to store the capability data.

**/
VOID
DumpCapabilityReg (
  IN UINT8                Slot,
  IN DW_MMC_HC_SLOT_CAP   *Capability
  )
{
  //
  // Dump Capability Data
  //
  DEBUG ((DEBUG_INFO, " == Slot [%d] Capability is 0x%x ==\n", Slot, Capability));
  DEBUG ((DEBUG_INFO, "   Base Clk Freq     %dKHz\n", Capability->BaseClkFreq));
  DEBUG ((DEBUG_INFO, "   BusWidth          %d\n", Capability->BusWidth));
  DEBUG ((DEBUG_INFO, "   HighSpeed Support %a\n", Capability->HighSpeed ? "TRUE" : "FALSE"));
  DEBUG ((DEBUG_INFO, "   Voltage 1.8       %a\n", Capability->Voltage18 ? "TRUE" : "FALSE"));
  DEBUG ((DEBUG_INFO, "   64-bit Sys Bus    %a\n", Capability->SysBus64 ? "TRUE" : "FALSE"));
  DEBUG ((DEBUG_INFO, "   SlotType          "));
  if (Capability->SlotType == 0x00) {
    DEBUG ((DEBUG_INFO, "%a\n", "Removable Slot"));
  } else if (Capability->SlotType == 0x01) {
    DEBUG ((DEBUG_INFO, "%a\n", "Embedded Slot"));
  } else if (Capability->SlotType == 0x02) {
    DEBUG ((DEBUG_INFO, "%a\n", "Shared Bus Slot"));
  } else {
    DEBUG ((DEBUG_INFO, "%a\n", "Reserved"));
  }
  DEBUG ((DEBUG_INFO, "   SDR50  Support    %a\n", Capability->Sdr50 ? "TRUE" : "FALSE"));
  DEBUG ((DEBUG_INFO, "   SDR104 Support    %a\n", Capability->Sdr104 ? "TRUE" : "FALSE"));
  DEBUG ((DEBUG_INFO, "   DDR50  Support    %a\n", Capability->Ddr50 ? "TRUE" : "FALSE"));
  return;
}

/**
  Read SlotInfo register from SD/MMC host controller pci config space.

  @param[in]  PciIo        The PCI IO protocol instance.
  @param[out] FirstBar     The buffer to store the first BAR value.
  @param[out] SlotNum      The buffer to store the supported slot number.

  @retval EFI_SUCCESS      The operation succeeds.
  @retval Others           The operation fails.

**/
EFI_STATUS
EFIAPI
DwMmcHcGetSlotInfo (
  IN     EFI_PCI_IO_PROTOCOL   *PciIo,
     OUT UINT8                 *FirstBar,
     OUT UINT8                 *SlotNum
  )
{
  EFI_STATUS                   Status;
  DW_MMC_HC_SLOT_INFO          SlotInfo;

  Status = PciIo->Pci.Read (
                        PciIo,
                        EfiPciIoWidthUint8,
                        DW_MMC_HC_SLOT_OFFSET,
                        sizeof (SlotInfo),
                        &SlotInfo
                        );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  *FirstBar = SlotInfo.FirstBar;
  *SlotNum  = SlotInfo.SlotNum + 1;
  ASSERT ((*FirstBar + *SlotNum) < DW_MMC_HC_MAX_SLOT);
  return EFI_SUCCESS;
}

/**
  Read/Write specified SD/MMC host controller mmio register.

  @param[in]      PciIo        The PCI IO protocol instance.
  @param[in]      BarIndex     The BAR index of the standard PCI Configuration
                               header to use as the base address for the memory
                               operation to perform.
  @param[in]      Offset       The offset within the selected BAR to start the
                               memory operation.
  @param[in]      Read         A boolean to indicate it's read or write operation.
  @param[in]      Count        The width of the mmio register in bytes.
                               Must be 1, 2 , 4 or 8 bytes.
  @param[in, out] Data         For read operations, the destination buffer to store
                               the results. For write operations, the source buffer
                               to write data from. The caller is responsible for
                               having ownership of the data buffer and ensuring its
                               size not less than Count bytes.

  @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid.
  @retval EFI_SUCCESS           The read/write operation succeeds.
  @retval Others                The read/write operation fails.

**/
EFI_STATUS
EFIAPI
DwMmcHcRwMmio (
  IN     EFI_PCI_IO_PROTOCOL   *PciIo,
  IN     UINT8                 BarIndex,
  IN     UINT32                Offset,
  IN     BOOLEAN               Read,
  IN     UINT8                 Count,
  IN OUT VOID                  *Data
  )
{
  EFI_STATUS                   Status;

  if ((PciIo == NULL) || (Data == NULL))  {
    return EFI_INVALID_PARAMETER;
  }

  if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) {
    return EFI_INVALID_PARAMETER;
  }

  if (Read) {
    Status = PciIo->Mem.Read (
                          PciIo,
                          EfiPciIoWidthUint8,
                          BarIndex,
                          (UINT64) Offset,
                          Count,
                          Data
                          );
  } else {
    Status = PciIo->Mem.Write (
                          PciIo,
                          EfiPciIoWidthUint8,
                          BarIndex,
                          (UINT64) Offset,
                          Count,
                          Data
                          );
  }

  return Status;
}

/**
  Do OR operation with the value of the specified SD/MMC host controller mmio register.

  @param[in] PciIo             The PCI IO protocol instance.
  @param[in] BarIndex          The BAR index of the standard PCI Configuration
                               header to use as the base address for the memory
                               operation to perform.
  @param[in] Offset            The offset within the selected BAR to start the
                               memory operation.
  @param[in] Count             The width of the mmio register in bytes.
                               Must be 1, 2 , 4 or 8 bytes.
  @param[in] OrData            The pointer to the data used to do OR operation.
                               The caller is responsible for having ownership of
                               the data buffer and ensuring its size not less than
                               Count bytes.

  @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid.
  @retval EFI_SUCCESS           The OR operation succeeds.
  @retval Others                The OR operation fails.

**/
EFI_STATUS
EFIAPI
DwMmcHcOrMmio (
  IN  EFI_PCI_IO_PROTOCOL      *PciIo,
  IN  UINT8                    BarIndex,
  IN  UINT32                   Offset,
  IN  UINT8                    Count,
  IN  VOID                     *OrData
  )
{
  EFI_STATUS                   Status;
  UINT64                       Data;
  UINT64                       Or;

  Status = DwMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Data);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (Count == 1) {
    Or = *(UINT8*) OrData;
  } else if (Count == 2) {
    Or = *(UINT16*) OrData;
  } else if (Count == 4) {
    Or = *(UINT32*) OrData;
  } else if (Count == 8) {
    Or = *(UINT64*) OrData;
  } else {
    return EFI_INVALID_PARAMETER;
  }

  Data  |= Or;
  Status = DwMmcHcRwMmio (PciIo, BarIndex, Offset, FALSE, Count, &Data);

  return Status;
}

/**
  Do AND operation with the value of the specified SD/MMC host controller mmio register.

  @param[in] PciIo             The PCI IO protocol instance.
  @param[in] BarIndex          The BAR index of the standard PCI Configuration
                               header to use as the base address for the memory
                               operation to perform.
  @param[in] Offset            The offset within the selected BAR to start the
                               memory operation.
  @param[in] Count             The width of the mmio register in bytes.
                               Must be 1, 2 , 4 or 8 bytes.
  @param[in] AndData           The pointer to the data used to do AND operation.
                               The caller is responsible for having ownership of
                               the data buffer and ensuring its size not less than
                               Count bytes.

  @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid.
  @retval EFI_SUCCESS           The AND operation succeeds.
  @retval Others                The AND operation fails.

**/
EFI_STATUS
EFIAPI
DwMmcHcAndMmio (
  IN  EFI_PCI_IO_PROTOCOL      *PciIo,
  IN  UINT8                    BarIndex,
  IN  UINT32                   Offset,
  IN  UINT8                    Count,
  IN  VOID                     *AndData
  )
{
  EFI_STATUS                   Status;
  UINT64                       Data;
  UINT64                       And;

  Status = DwMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Data);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (Count == 1) {
    And = *(UINT8*) AndData;
  } else if (Count == 2) {
    And = *(UINT16*) AndData;
  } else if (Count == 4) {
    And = *(UINT32*) AndData;
  } else if (Count == 8) {
    And = *(UINT64*) AndData;
  } else {
    return EFI_INVALID_PARAMETER;
  }

  Data  &= And;
  Status = DwMmcHcRwMmio (PciIo, BarIndex, Offset, FALSE, Count, &Data);

  return Status;
}

/**
  Wait for the value of the specified MMIO register set to the test value.

  @param[in]  PciIo         The PCI IO protocol instance.
  @param[in]  BarIndex      The BAR index of the standard PCI Configuration
                            header to use as the base address for the memory
                            operation to perform.
  @param[in]  Offset        The offset within the selected BAR to start the
                            memory operation.
  @param[in]  Count         The width of the mmio register in bytes.
                            Must be 1, 2, 4 or 8 bytes.
  @param[in]  MaskValue     The mask value of memory.
  @param[in]  TestValue     The test value of memory.

  @retval EFI_NOT_READY     The MMIO register hasn't set to the expected value.
  @retval EFI_SUCCESS       The MMIO register has expected value.
  @retval Others            The MMIO operation fails.

**/
EFI_STATUS
EFIAPI
DwMmcHcCheckMmioSet (
  IN  EFI_PCI_IO_PROTOCOL       *PciIo,
  IN  UINT8                     BarIndex,
  IN  UINT32                    Offset,
  IN  UINT8                     Count,
  IN  UINT64                    MaskValue,
  IN  UINT64                    TestValue
  )
{
  EFI_STATUS            Status;
  UINT64                Value;

  //
  // Access PCI MMIO space to see if the value is the tested one.
  //
  Value  = 0;
  Status = DwMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Value);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Value &= MaskValue;

  if (Value == TestValue) {
    return EFI_SUCCESS;
  }

  return EFI_NOT_READY;
}

/**
  Wait for the value of the specified MMIO register set to the test value.

  @param[in]  PciIo         The PCI IO protocol instance.
  @param[in]  BarIndex      The BAR index of the standard PCI Configuration
                            header to use as the base address for the memory
                            operation to perform.
  @param[in]  Offset        The offset within the selected BAR to start the
                            memory operation.
  @param[in]  Count         The width of the mmio register in bytes.
                            Must be 1, 2, 4 or 8 bytes.
  @param[in]  MaskValue     The mask value of memory.
  @param[in]  TestValue     The test value of memory.
  @param[in]  Timeout       The time out value for wait memory set, uses 1
                            microsecond as a unit.

  @retval EFI_TIMEOUT       The MMIO register hasn't expected value in timeout
                            range.
  @retval EFI_SUCCESS       The MMIO register has expected value.
  @retval Others            The MMIO operation fails.

**/
EFI_STATUS
EFIAPI
DwMmcHcWaitMmioSet (
  IN  EFI_PCI_IO_PROTOCOL       *PciIo,
  IN  UINT8                     BarIndex,
  IN  UINT32                    Offset,
  IN  UINT8                     Count,
  IN  UINT64                    MaskValue,
  IN  UINT64                    TestValue,
  IN  UINT64                    Timeout
  )
{
  EFI_STATUS            Status;
  BOOLEAN               InfiniteWait;

  if (Timeout == 0) {
    InfiniteWait = TRUE;
  } else {
    InfiniteWait = FALSE;
  }

  while (InfiniteWait || (Timeout > 0)) {
    Status = DwMmcHcCheckMmioSet (
               PciIo,
               BarIndex,
               Offset,
               Count,
               MaskValue,
               TestValue
               );
    if (Status != EFI_NOT_READY) {
      return Status;
    }

    //
    // Stall for 1 microsecond.
    //
    gBS->Stall (1);

    Timeout--;
  }

  return EFI_TIMEOUT;
}

/**
  Set all interrupt status bits in Normal and Error Interrupt Status Enable
  register.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.

  @retval EFI_SUCCESS       The operation executes successfully.
  @retval Others            The operation fails.

**/
EFI_STATUS
DwMmcHcEnableInterrupt (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot
  )
{
  EFI_STATUS                Status;
  UINT32                    IntStatus;
  UINT32                    IdIntEn;
  UINT32                    IdSts;

  //
  // Enable all bits in Interrupt Mask Register
  //
  IntStatus = 0;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_INTMASK, FALSE, sizeof (IntStatus), &IntStatus);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Clear status in Interrupt Status Register
  //
  IntStatus = ~0;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_RINTSTS, FALSE, sizeof (IntStatus), &IntStatus);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  IdIntEn = ~0;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_IDINTEN, FALSE, sizeof (IdIntEn), &IdIntEn);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcReset: init dma interrupts fail: %r\n", Status));
    return Status;
  }

  IdSts = ~0;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_IDSTS, FALSE, sizeof (IdSts), &IdSts);
  return Status;
}

/**
  Get the capability data from the specified slot.

  @param[in]  PciIo           The PCI IO protocol instance.
  @param[in]  Slot            The slot number of the SD card to send the command to.
  @param[out] Capability      The buffer to store the capability data.

  @retval EFI_SUCCESS         The operation executes successfully.
  @retval Others              The operation fails.

**/
EFI_STATUS
DwMmcHcGetCapability (
  IN     EFI_PCI_IO_PROTOCOL  *PciIo,
  IN     EFI_HANDLE           Controller,
  IN     UINT8                Slot,
     OUT DW_MMC_HC_SLOT_CAP   *Capacity
  )
{
  PLATFORM_DW_MMC_PROTOCOL    *PlatformDwMmc;
  EFI_STATUS                Status;

  if (Capacity == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  Status = gBS->LocateProtocol (
                  &gPlatformDwMmcProtocolGuid,
                  NULL,
                  (VOID **) &PlatformDwMmc
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Status = PlatformDwMmc->GetCapability (Controller, Slot, Capacity);
  return Status;
}

/**
  Get the maximum current capability data from the specified slot.

  @param[in]  PciIo           The PCI IO protocol instance.
  @param[in]  Slot            The slot number of the SD card to send the command to.
  @param[out] MaxCurrent      The buffer to store the maximum current capability data.

  @retval EFI_SUCCESS         The operation executes successfully.
  @retval Others              The operation fails.

**/
EFI_STATUS
DwMmcHcGetMaxCurrent (
  IN     EFI_PCI_IO_PROTOCOL  *PciIo,
  IN     UINT8                Slot,
     OUT UINT64               *MaxCurrent
  )
{
  return EFI_SUCCESS;
}

/**
  Detect whether there is a SD/MMC card attached at the specified SD/MMC host controller
  slot.

  Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details.

  @param[in]  PciIo         The PCI IO protocol instance.
  @param[in]  Slot          The slot number of the SD card to send the command to.
  @param[out] MediaPresent  The pointer to the media present boolean value.

  @retval EFI_SUCCESS       There is no media change happened.
  @retval EFI_MEDIA_CHANGED There is media change happened.
  @retval Others            The detection fails.

**/
EFI_STATUS
DwMmcHcCardDetect (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
     OUT BOOLEAN            *MediaPresent
  )
{
  PLATFORM_DW_MMC_PROTOCOL  *PlatformDwMmc;
  EFI_STATUS                Status;

  if (MediaPresent == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  Status = gBS->LocateProtocol (
                  &gPlatformDwMmcProtocolGuid,
                  NULL,
                  (VOID **) &PlatformDwMmc
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  *MediaPresent = PlatformDwMmc->CardDetect (Slot);
  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
DwMmcHcUpdateClock (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot
  )
{
  EFI_STATUS                Status;
  UINT32                    Cmd;
  UINT32                    IntStatus;

  Cmd = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
        BIT_CMD_START;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  while (1) {
    Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CMD, TRUE, sizeof (Cmd), &Cmd);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    if (!(Cmd & CMD_START_BIT)) {
      break;
    }
    Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_RINTSTS, TRUE, sizeof (IntStatus), &IntStatus);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    if (IntStatus & DW_MMC_INT_HLE) {
      DEBUG ((DEBUG_ERROR, "DwMmcHcUpdateClock: failed to update mmc clock frequency\n"));
      return EFI_DEVICE_ERROR;
    }
  }
  return EFI_SUCCESS;

}

/**
  Stop SD/MMC card clock.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.

  @retval EFI_SUCCESS       Succeed to stop SD/MMC clock.
  @retval Others            Fail to stop SD/MMC clock.

**/
EFI_STATUS
DwMmcHcStopClock (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot
  )
{
  EFI_STATUS                Status;
  UINT32                    ClkEna;

  // Disable MMC clock first
  ClkEna = 0;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKENA, FALSE, sizeof (ClkEna), &ClkEna);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Status = DwMmcHcUpdateClock (PciIo, Slot);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  return Status;
}

/**
  SD/MMC card clock supply.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] ClockFreq      The max clock frequency to be set. The unit is KHz.
  @param[in] Capability     The capability of the slot.

  @retval EFI_SUCCESS       The clock is supplied successfully.
  @retval Others            The clock isn't supplied successfully.

**/
EFI_STATUS
DwMmcHcClockSupply (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
  IN UINT64                 ClockFreq,
  IN DW_MMC_HC_SLOT_CAP     Capability
  )
{
  EFI_STATUS                Status;
  UINT32                    BaseClkFreq;
  UINT32                    SettingFreq;
  UINT32                    Divisor;
  UINT32                    Remainder;
  UINT32                    MmcStatus;
  UINT32                    ClkEna;
  UINT32                    ClkSrc;

  //
  // Calculate a divisor for SD clock frequency
  //
  ASSERT (Capability.BaseClkFreq != 0);

  BaseClkFreq = Capability.BaseClkFreq;
  if (ClockFreq == 0) {
    return EFI_INVALID_PARAMETER;
  }

  if (ClockFreq > BaseClkFreq) {
    ClockFreq = BaseClkFreq;
  }

  //
  // Calculate the divisor of base frequency.
  //
  Divisor     = 0;
  SettingFreq = BaseClkFreq;
  while (ClockFreq < SettingFreq) {
    Divisor++;

    SettingFreq = BaseClkFreq / (2 * Divisor);
    Remainder   = BaseClkFreq % (2 * Divisor);
    if ((ClockFreq == SettingFreq) && (Remainder == 0)) {
      break;
    }
    if ((ClockFreq == SettingFreq) && (Remainder != 0)) {
      SettingFreq ++;
    }
  }

  DEBUG ((DEBUG_INFO, "BaseClkFreq %dKHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq));

  // Wait until MMC is idle
  do {
    Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_STATUS, TRUE, sizeof (MmcStatus), &MmcStatus);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  } while (MmcStatus & DW_MMC_STS_DATA_BUSY);

  do {
    Status = DwMmcHcStopClock (PciIo, Slot);
  } while (EFI_ERROR (Status));

  do {
    ClkSrc = 0;
    Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKSRC, FALSE, sizeof (ClkSrc), &ClkSrc);
    if (EFI_ERROR (Status)) {
      continue;
    }
    // Set clock divisor
    Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKDIV, FALSE, sizeof (Divisor), &Divisor);
    if (EFI_ERROR (Status)) {
      continue;
    }
    // Enable MMC clock
    ClkEna = 1;
    Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKENA, FALSE, sizeof (ClkEna), &ClkEna);
    if (EFI_ERROR (Status)) {
      continue;
    }
    Status = DwMmcHcUpdateClock (PciIo, Slot);
  } while (EFI_ERROR (Status));

  return EFI_SUCCESS;
}

/**
  SD/MMC bus power control.

  Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] PowerCtrl      The value setting to the power control register.

  @retval TRUE              There is a SD/MMC card attached.
  @retval FALSE             There is no a SD/MMC card attached.

**/
EFI_STATUS
DwMmcHcPowerControl (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
  IN UINT8                  PowerCtrl
  )
{
  return EFI_SUCCESS;
}

/**
  Set the SD/MMC bus width.

  Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] BusWidth       The bus width used by the SD/MMC device, it must be 1, 4 or 8.

  @retval EFI_SUCCESS       The bus width is set successfully.
  @retval Others            The bus width isn't set successfully.

**/
EFI_STATUS
DwMmcHcSetBusWidth (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
  IN BOOLEAN                IsDdr,
  IN UINT16                 BusWidth
  )
{
  EFI_STATUS                Status;
  UINT32                    Ctype;
  UINT32                    Uhs;

  switch (BusWidth) {
  case 1:
    Ctype = MMC_1BIT_MODE;
    break;
  case 4:
    Ctype = MMC_4BIT_MODE;
    break;
  case 8:
    Ctype = MMC_8BIT_MODE;
    break;
  default:
    return EFI_INVALID_PARAMETER;
  }
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CTYPE, FALSE, sizeof (Ctype), &Ctype);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_UHSREG, TRUE, sizeof (Uhs), &Uhs);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  if (IsDdr) {
    Uhs |= UHS_DDR_MODE;
  } else {
    Uhs &= ~(UHS_DDR_MODE);
  }
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_UHSREG, FALSE, sizeof (Uhs), &Uhs);
  return Status;
}

/**
  Supply SD/MMC card with lowest clock frequency at initialization.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] Capability     The capability of the slot.

  @retval EFI_SUCCESS       The clock is supplied successfully.
  @retval Others            The clock isn't supplied successfully.

**/
EFI_STATUS
DwMmcHcInitClockFreq (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
  IN DW_MMC_HC_SLOT_CAP     Capability
  )
{
  EFI_STATUS                Status;
  UINT32                    InitFreq;

  //
  // Calculate a divisor for SD clock frequency
  //
  if (Capability.BaseClkFreq == 0) {
    //
    // Don't support get Base Clock Frequency information via another method
    //
    return EFI_UNSUPPORTED;
  }
  //
  // Supply 400KHz clock frequency at initialization phase.
  //
  InitFreq = DWMMC_INIT_CLOCK_FREQ;
  Status = DwMmcHcClockSupply (PciIo, Slot, InitFreq, Capability);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  MicroSecondDelay (100);
  return Status;
}

/**
  Supply SD/MMC card with maximum voltage at initialization.

  Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] Capability     The capability of the slot.

  @retval EFI_SUCCESS       The voltage is supplied successfully.
  @retval Others            The voltage isn't supplied successfully.

**/
EFI_STATUS
DwMmcHcInitPowerVoltage (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
  IN DW_MMC_HC_SLOT_CAP     Capability
  )
{
  EFI_STATUS                Status;
  UINT32                    Data;

  Data = 0x1;
  Status  = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_PWREN, FALSE, sizeof (Data), &Data);

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: enable power fails: %r\n", Status));
    return Status;
  }

  Data = DW_MMC_CTRL_RESET_ALL;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CTRL, FALSE, sizeof (Data), &Data);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: reset fails: %r\n", Status));
    return Status;
  }
  Status = DwMmcHcWaitMmioSet (
             PciIo,
             Slot,
             DW_MMC_CTRL,
             sizeof (Data),
             DW_MMC_CTRL_RESET_ALL,
             0x00,
             DW_MMC_HC_GENERIC_TIMEOUT
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_INFO, "DwMmcHcInitPowerVoltage: reset done with %r\n", Status));
    return Status;
  }

  Data = DW_MMC_CTRL_INT_EN;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CTRL, FALSE, sizeof (Data), &Data);
  return Status;
}

/**
  Initialize the Timeout Control register with most conservative value at initialization.

  Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.

  @retval EFI_SUCCESS       The timeout control register is configured successfully.
  @retval Others            The timeout control register isn't configured successfully.

**/
EFI_STATUS
DwMmcHcInitTimeoutCtrl (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot
  )
{
  EFI_STATUS                Status;
  UINT32                    Data;

  Data = ~0;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_TMOUT, FALSE, sizeof (Data), &Data);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcInitTimeoutCtrl: set timeout fails: %r\n", Status));
    return Status;
  }

  Data = 0x00FFFFFF;
  Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_DEBNCE, FALSE, sizeof (Data), &Data);
  return Status;
}

/**
  Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value
  at initialization.

  @param[in] PciIo          The PCI IO protocol instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] Capability     The capability of the slot.

  @retval EFI_SUCCESS       The host controller is initialized successfully.
  @retval Others            The host controller isn't initialized successfully.

**/
EFI_STATUS
DwMmcHcInitHost (
  IN EFI_PCI_IO_PROTOCOL    *PciIo,
  IN UINT8                  Slot,
  IN DW_MMC_HC_SLOT_CAP     Capability
  )
{
  EFI_STATUS       Status;

  Status = DwMmcHcInitPowerVoltage (PciIo, Slot, Capability);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  return Status;
}

EFI_STATUS
DwMmcHcStartDma (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_PCI_IO_PROTOCOL                 *PciIo;
  UINT32                              Ctrl;
  UINT32                              Bmod;

  PciIo  = Trb->Private->PciIo;

  // Reset DMA
  Ctrl = DW_MMC_CTRL_DMA_RESET;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CTRL, FALSE, sizeof (Ctrl), &Ctrl);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: reset fails: %r\n", Status));
    return Status;
  }
  Status = DwMmcHcWaitMmioSet (
             PciIo,
             Trb->Slot,
             DW_MMC_CTRL,
             sizeof (Ctrl),
             DW_MMC_CTRL_DMA_RESET,
             0x00,
             DW_MMC_HC_GENERIC_TIMEOUT
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_INFO, "DwMmcHcStartDma: reset done with %r\n", Status));
    return Status;
  }
  Bmod = DW_MMC_IDMAC_SWRESET;
  Status = DwMmcHcOrMmio (PciIo, Trb->Slot, DW_MMC_BMOD, sizeof (Bmod), &Bmod);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: set BMOD fail: %r\n", Status));
    return Status;
  }

  // Select IDMAC
  Ctrl = DW_MMC_CTRL_IDMAC_EN;
  Status = DwMmcHcOrMmio (PciIo, Trb->Slot, DW_MMC_CTRL, sizeof (Ctrl), &Ctrl);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: init IDMAC fail: %r\n", Status));
    return Status;
  }

  // Enable IDMAC
  Bmod = DW_MMC_IDMAC_ENABLE | DW_MMC_IDMAC_FB;
  Status = DwMmcHcOrMmio (PciIo, Trb->Slot, DW_MMC_BMOD, sizeof (Bmod), &Bmod);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set BMOD failure: %r\n", Status));
    return Status;
  }
  return Status;
}

EFI_STATUS
DwMmcHcStopDma (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_PCI_IO_PROTOCOL                 *PciIo;
  UINT32                              Ctrl;
  UINT32                              Bmod;

  PciIo  = Trb->Private->PciIo;

  // Disable and reset IDMAC
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CTRL, TRUE, sizeof (Ctrl), &Ctrl);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Ctrl &= ~DW_MMC_CTRL_IDMAC_EN;
  Ctrl |= DW_MMC_CTRL_DMA_RESET;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CTRL, FALSE, sizeof (Ctrl), &Ctrl);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  // Stop IDMAC
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BMOD, TRUE, sizeof (Bmod), &Bmod);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Bmod &= ~(DW_MMC_BMOD_FB | DW_MMC_BMOD_DE);
  Bmod |= DW_MMC_BMOD_SWR;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BMOD, FALSE, sizeof (Bmod), &Bmod);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  return Status;
}

/**
  Build DMA descriptor table for transfer.

  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

  @retval EFI_SUCCESS       The DMA descriptor table is created successfully.
  @retval Others            The DMA descriptor table isn't created successfully.

**/
EFI_STATUS
BuildDmaDescTable (
  IN DW_MMC_HC_TRB          *Trb
  )
{
  EFI_PHYSICAL_ADDRESS      Data;
  UINT64                    DataLen;
  UINT64                    Entries;
  UINT32                    Index;
  UINT64                    Remaining;
  UINTN                     TableSize;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  EFI_STATUS                Status;
  UINTN                     Bytes;
  UINTN                     Blocks;
  DW_MMC_HC_DMA_DESC_LINE   *DmaDesc;
  UINT32                    DmaDescPhy;
  UINT32                    Idsts;
  UINT32                    BytCnt;
  UINT32                    BlkSize;

  Data    = Trb->DataPhy;
  DataLen = Trb->DataLen;
  PciIo   = Trb->Private->PciIo;
  //
  // Only support 32bit DMA Descriptor Table
  //
  if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0)
  // for 32-bit address descriptor table.
  //
  if ((Data & (BIT0 | BIT1)) != 0) {
    DEBUG ((DEBUG_INFO, "The buffer [0x%x] to construct DMA desc is not aligned to 4 bytes boundary!\n", Data));
  }

  Entries   = (DataLen + DWMMC_DMA_BUF_SIZE - 1) / DWMMC_DMA_BUF_SIZE;
  TableSize = Entries * sizeof (DW_MMC_HC_DMA_DESC_LINE);
  Blocks    = (DataLen + DW_MMC_BLOCK_SIZE - 1) / DW_MMC_BLOCK_SIZE;

  Trb->DmaDescPages = (UINT32)EFI_SIZE_TO_PAGES (Entries * DWMMC_DMA_BUF_SIZE);
  Status = PciIo->AllocateBuffer (
                    PciIo,
                    AllocateAnyPages,
                    EfiBootServicesData,
                    EFI_SIZE_TO_PAGES (TableSize),
                    (VOID **)&Trb->DmaDesc,
                    0
                    );
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }
  ZeroMem (Trb->DmaDesc, TableSize);
  Bytes  = TableSize;
  Status = PciIo->Map (
                    PciIo,
                    EfiPciIoOperationBusMasterCommonBuffer,
                    Trb->DmaDesc,
                    &Bytes,
                    &Trb->DmaDescPhy,
                    &Trb->DmaMap
                    );

  if (EFI_ERROR (Status) || (Bytes != TableSize)) {
    //
    // Map error or unable to map the whole RFis buffer into a contiguous region.
    //
    PciIo->FreeBuffer (
             PciIo,
             EFI_SIZE_TO_PAGES (TableSize),
             Trb->DmaDesc
             );
    return EFI_OUT_OF_RESOURCES;
  }

  if ((UINT64)(UINTN)Trb->DmaDescPhy > 0x100000000ul) {
    //
    // The DMA doesn't support 64bit addressing.
    //
    PciIo->Unmap (
      PciIo,
      Trb->DmaMap
    );
#if 0
    PciIo->FreeBuffer (
      PciIo,
      EFI_SIZE_TO_PAGES (TableSize),
      Trb->DmaDesc
    );
#endif
    return EFI_DEVICE_ERROR;
  }

  if (DataLen < DW_MMC_BLOCK_SIZE) {
    BlkSize = DataLen;
    BytCnt = DataLen;
    Remaining = DataLen;
  } else {
    BlkSize = DW_MMC_BLOCK_SIZE;
    BytCnt = DW_MMC_BLOCK_SIZE * Blocks;
    Remaining = DW_MMC_BLOCK_SIZE * Blocks;
  }
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BLKSIZ, FALSE, sizeof (BlkSize), &BlkSize);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "BuildDmaDescTable: set block size fails: %r\n", Status));
    return Status;
  }
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BYTCNT, FALSE, sizeof (BytCnt), &BytCnt);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  DmaDesc = Trb->DmaDesc;
  for (Index = 0; Index < Entries; Index++, DmaDesc++) {
    DmaDesc->Des0 = DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_CH |
                    DW_MMC_IDMAC_DES0_DIC;
    DmaDesc->Des1 = DW_MMC_IDMAC_DES1_BS1 (DWMMC_DMA_BUF_SIZE);
    // Buffer Address
    DmaDesc->Des2 = (UINT32)((UINTN)Trb->DataPhy + (DWMMC_DMA_BUF_SIZE * Index));
    // Next Descriptor Address
    DmaDesc->Des3 = (UINT32)((UINTN)Trb->DmaDescPhy + sizeof (DW_MMC_HC_DMA_DESC_LINE) * (Index + 1));
    Remaining = Remaining - DWMMC_DMA_BUF_SIZE;
  }
  // First Descriptor
  Trb->DmaDesc[0].Des0 |= DW_MMC_IDMAC_DES0_FS;
  // Last Descriptor
  Trb->DmaDesc[Entries - 1].Des0 &= ~(DW_MMC_IDMAC_DES0_CH | DW_MMC_IDMAC_DES0_DIC);
  Trb->DmaDesc[Entries - 1].Des0 |= DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_LD;
  Trb->DmaDesc[Entries - 1].Des1 = DW_MMC_IDMAC_DES1_BS1 (Remaining + DWMMC_DMA_BUF_SIZE);
  // Set the next field of the Last Descriptor
  Trb->DmaDesc[Entries - 1].Des3 = 0;
  DmaDescPhy = (UINT32)Trb->DmaDescPhy;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_DBADDR, FALSE, sizeof (DmaDescPhy), &DmaDescPhy);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  ArmDataSynchronizationBarrier ();
  ArmInstructionSynchronizationBarrier ();
  // Clear interrupts
  Idsts = ~0;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_IDSTS, FALSE, sizeof (Idsts), &Idsts);
  return Status;
}

EFI_STATUS
ReadFifo (
  IN DW_MMC_HC_TRB          *Trb
  )
{
  EFI_STATUS                Status;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  UINT32                    Data;
  UINT32                    Received;
  UINT32                    Count;
  UINT32                    Intsts;
  UINT32                    Sts;
  UINT32                    FifoCount;
  UINT32                    Index;     // count with bytes
  UINT32                    Ascending;
  UINT32                    Descending;

  PciIo   = Trb->Private->PciIo;
  Received = 0;
  Count = 0;
  Index = 0;
  Ascending = 0;
  Descending = ((Trb->DataLen + 3) & ~3) - 4;
  do {
    Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, TRUE, sizeof (Intsts), &Intsts);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "ReadFifo: failed to read RINTSTS, Status:%r\n", Status));
      return Status;
    }
    if (Trb->DataLen && ((Intsts & DW_MMC_INT_RXDR) || (Intsts & DW_MMC_INT_DTO))) {
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_STATUS, TRUE, sizeof (Sts), &Sts);
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_ERROR, "ReadFifo: failed to read STATUS, Status:%r\n", Status));
        return Status;
      }
      // Convert to bytes
      FifoCount = GET_STS_FIFO_COUNT (Sts) << 2;
      if ((FifoCount == 0) && (Received < Trb->DataLen)) {
        continue;
      }
      Index = 0;
      Count = (MIN (FifoCount, Trb->DataLen) + 3) & ~3;
      while (Index < Count) {
        Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_FIFO_START, TRUE, sizeof (Data), &Data);
        if (EFI_ERROR (Status)) {
          DEBUG ((DEBUG_ERROR, "ReadFifo: failed to read FIFO, Status:%r\n", Status));
          return Status;
        }
        if (Trb->UseBE) {
          *(UINT32 *)((UINTN)Trb->Data + Descending) = SwapBytes32 (Data);
          Descending = Descending - 4;
        } else {
          *(UINT32 *)((UINTN)Trb->Data + Ascending) = Data;
          Ascending += 4;
        }
        Index += 4;
        Received += 4;
      } // while
    } // if
  } while (((Intsts & DW_MMC_INT_CMD_DONE) == 0) || (Received < Trb->DataLen));
  // Clear RINTSTS
  Intsts = ~0;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, FALSE, sizeof (Intsts), &Intsts);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ReadFifo: failed to write RINTSTS, Status:%r\n", Status));
    return Status;
  }
  return EFI_SUCCESS;
}

/**
  Create a new TRB for the SD/MMC cmd request.

  @param[in] Private        A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] Packet         A pointer to the SD command data structure.
  @param[in] Event          If Event is NULL, blocking I/O is performed. If Event is
                            not NULL, then nonblocking I/O is performed, and Event
                            will be signaled when the Packet completes.

  @return Created Trb or NULL.

**/
DW_MMC_HC_TRB *
DwMmcCreateTrb (
  IN DW_MMC_HC_PRIVATE_DATA              *Private,
  IN UINT8                               Slot,
  IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet,
  IN EFI_EVENT                           Event
  )
{
  DW_MMC_HC_TRB                 *Trb;
  EFI_STATUS                    Status;
  EFI_TPL                       OldTpl;
  EFI_PCI_IO_PROTOCOL_OPERATION Flag;
  EFI_PCI_IO_PROTOCOL           *PciIo;
  UINTN                         MapLength;

  Trb = AllocateZeroPool (sizeof (DW_MMC_HC_TRB));
  if (Trb == NULL) {
    return NULL;
  }

  Trb->Signature = DW_MMC_HC_TRB_SIG;
  Trb->Slot      = Slot;
  Trb->BlockSize = 0x200;
  Trb->Packet    = Packet;
  Trb->Event     = Event;
  Trb->Started   = FALSE;
  Trb->Timeout   = Packet->Timeout;
  Trb->Private   = Private;

  if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) {
    Trb->Data    = Packet->InDataBuffer;
    Trb->DataLen = Packet->InTransferLength;
    Trb->Read    = TRUE;
    ZeroMem (Trb->Data, Trb->DataLen);
  } else if ((Packet->OutTransferLength != 0) && (Packet->OutDataBuffer != NULL)) {
    Trb->Data    = Packet->OutDataBuffer;
    Trb->DataLen = Packet->OutTransferLength;
    Trb->Read    = FALSE;
  } else if ((Packet->InTransferLength == 0) && (Packet->OutTransferLength == 0)) {
    Trb->Data    = NULL;
    Trb->DataLen = 0;
  } else {
    goto Error;
  }

  if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) &&
       (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) ||
      ((Private->Slot[Trb->Slot].CardType == SdCardType) &&
       (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) {
    Trb->Mode = SdMmcPioMode;
  } else {
    if (Trb->Read) {
      Flag = EfiPciIoOperationBusMasterWrite;
    } else {
      Flag = EfiPciIoOperationBusMasterRead;
    }

    PciIo = Private->PciIo;
#if 0
    if ((Private->Slot[Trb->Slot].CardType == SdCardType) &&
        (Trb->DataLen != 0) &&
        (Trb->DataLen <= DWMMC_FIFO_THRESHOLD)) {
#else
    if (Private->Slot[Trb->Slot].CardType == SdCardType) {
#endif
      Trb->UseFifo = TRUE;
    } else {
      Trb->UseFifo = FALSE;
      if (Trb->DataLen) {
        MapLength = Trb->DataLen;
        Status = PciIo->Map (
                          PciIo,
                          Flag,
                          Trb->Data,
                          &MapLength,
                          &Trb->DataPhy,
                          &Trb->DataMap
                          );
        if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) {
          Status = EFI_BAD_BUFFER_SIZE;
          goto Error;
        }

        Status = BuildDmaDescTable (Trb);
        if (EFI_ERROR (Status)) {
          PciIo->Unmap (PciIo, Trb->DataMap);
          goto Error;
        }
        Status = DwMmcHcStartDma (Private, Trb);
        if (EFI_ERROR (Status)) {
          PciIo->Unmap (PciIo, Trb->DataMap);
          goto Error;
        }
      }
    }
  } // TuningBlock

  if (Event != NULL) {
    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
    InsertTailList (&Private->Queue, &Trb->TrbList);
    gBS->RestoreTPL (OldTpl);
  }

  return Trb;

Error:
  //DwMmcFreeTrb (Trb);
  return NULL;
}

/**
  Free the resource used by the TRB.

  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

**/
VOID
DwMmcFreeTrb (
  IN DW_MMC_HC_TRB           *Trb
  )
{
  EFI_PCI_IO_PROTOCOL        *PciIo;

  PciIo = Trb->Private->PciIo;

  if (Trb->DmaMap != NULL) {
    PciIo->Unmap (
      PciIo,
      Trb->DmaMap
    );
  }
#if 0
  // Free is handled in Unmap().
  if (Trb->DmaDesc != NULL) {
    PciIo->FreeBuffer (
      PciIo,
      Trb->DmaDescPages,
      Trb->DmaDesc
    );
  }
#endif
  if (Trb->DataMap != NULL) {
    PciIo->Unmap (
      PciIo,
      Trb->DataMap
    );
  }
  FreePool (Trb);
  return;
}

/**
  Check if the env is ready for execute specified TRB.

  @param[in] Private        A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

  @retval EFI_SUCCESS       The env is ready for TRB execution.
  @retval EFI_NOT_READY     The env is not ready for TRB execution.
  @retval Others            Some erros happen.

**/
EFI_STATUS
DwMmcCheckTrbEnv (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  return EFI_SUCCESS;
}

/**
  Wait for the env to be ready for execute specified TRB.

  @param[in] Private        A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

  @retval EFI_SUCCESS       The env is ready for TRB execution.
  @retval EFI_TIMEOUT       The env is not ready for TRB execution in time.
  @retval Others            Some erros happen.

**/
EFI_STATUS
DwMmcWaitTrbEnv (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
  UINT64                              Timeout;
  BOOLEAN                             InfiniteWait;

  //
  // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register
  //
  Packet  = Trb->Packet;
  Timeout = Packet->Timeout;
  if (Timeout == 0) {
    InfiniteWait = TRUE;
  } else {
    InfiniteWait = FALSE;
  }

  while (InfiniteWait || (Timeout > 0)) {
    //
    // Check Trb execution result by reading Normal Interrupt Status register.
    //
    Status = DwMmcCheckTrbEnv (Private, Trb);
    if (Status != EFI_NOT_READY) {
      return Status;
    }
    //
    // Stall for 1 microsecond.
    //
    gBS->Stall (1);

    Timeout--;
  }

  return EFI_TIMEOUT;
}

EFI_STATUS
DwEmmcExecTrb (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
  EFI_PCI_IO_PROTOCOL                 *PciIo;
  UINT32                              Cmd;
  UINT32                              MmcStatus;
  UINT32                              IntStatus;
  UINT32                              Argument;
  UINT32                              ErrMask;

  Packet = Trb->Packet;
  PciIo  = Trb->Private->PciIo;

  // Wait until MMC is idle
  do {
    Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_STATUS, TRUE, sizeof (MmcStatus), &MmcStatus);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  } while (MmcStatus & DW_MMC_STS_DATA_BUSY);

  IntStatus = ~0;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, FALSE, sizeof (IntStatus), &IntStatus);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex);
  if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) ||
      (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) {
    switch (Packet->SdMmcCmdBlk->CommandIndex) {
    case EMMC_SET_RELATIVE_ADDR:
      Cmd |= BIT_CMD_SEND_INIT;
      break;
    case EMMC_SEND_STATUS:
      Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE;
      break;
    case EMMC_STOP_TRANSMISSION:
      Cmd |= BIT_CMD_STOP_ABORT_CMD;
      break;
    }
    if (Packet->InTransferLength) {
      Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
    } else if (Packet->OutTransferLength) {
      Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
    }
    Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
  } else {
    switch (Packet->SdMmcCmdBlk->CommandIndex) {
    case EMMC_GO_IDLE_STATE:
      Cmd |= BIT_CMD_SEND_INIT;
      break;
    case EMMC_SEND_OP_COND:
      Cmd |= BIT_CMD_RESPONSE_EXPECT;
      break;
    case EMMC_ALL_SEND_CID:
      Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE |
             BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT;
      break;
    }
  }
  switch (Packet->SdMmcCmdBlk->ResponseType) {
  case SdMmcResponseTypeR2:
    Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_LONG_RESPONSE;
    break;
  case SdMmcResponseTypeR3:
    Cmd |= BIT_CMD_RESPONSE_EXPECT;
    break;
  }
  Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START;

  Argument = Packet->SdMmcCmdBlk->CommandArgument;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMDARG, FALSE, sizeof (Argument), &Argument);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO |
            DW_MMC_INT_RCRC | DW_MMC_INT_RE;
  ErrMask |= DW_MMC_INT_DCRC | DW_MMC_INT_DRT | DW_MMC_INT_SBE;
  do {
    MicroSecondDelay (500);
    Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, TRUE, sizeof (IntStatus), &IntStatus);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    if (IntStatus & ErrMask) {
      return EFI_DEVICE_ERROR;
    }
    if (IntStatus & DW_MMC_INT_DTO) {  // Transfer Done
      break;
    }
  } while (!(IntStatus & DW_MMC_INT_CMD_DONE));
  switch (Packet->SdMmcCmdBlk->ResponseType) {
    case SdMmcResponseTypeR1:
    case SdMmcResponseTypeR1b:
    case SdMmcResponseTypeR3:
    case SdMmcResponseTypeR4:
    case SdMmcResponseTypeR5:
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      break;
    case SdMmcResponseTypeR2:
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP1, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp1), &Packet->SdMmcStatusBlk->Resp1);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP2, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp2), &Packet->SdMmcStatusBlk->Resp2);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP3, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp3), &Packet->SdMmcStatusBlk->Resp3);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      break;
  }

  //
  // The workaround on EMMC_SEND_CSD is used to be compatible with SDHC.
  //
  if (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_CSD) {
    {
      UINT32   Buf[4];
      ZeroMem (Buf, sizeof (Buf));
      CopyMem ((UINT8 *)Buf, (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1, sizeof (Buf) - 1);
      CopyMem ((UINT8 *)&Packet->SdMmcStatusBlk->Resp0, (UINT8 *)Buf, sizeof (Buf) - 1);
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
DwSdExecTrb (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
  EFI_PCI_IO_PROTOCOL                 *PciIo;
  UINT32                              Cmd;
  UINT32                              MmcStatus;
  UINT32                              IntStatus;
  UINT32                              Argument;
  UINT32                              ErrMask;
  UINT32                              Timeout;
  UINT32                              Idsts;
  UINT32                              BytCnt;
  UINT32                              BlkSize;

  Packet = Trb->Packet;
  PciIo  = Trb->Private->PciIo;

  ArmDataSynchronizationBarrier ();
  ArmInstructionSynchronizationBarrier ();
  // Wait until MMC is idle
  do {
    Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_STATUS, TRUE, sizeof (MmcStatus), &MmcStatus);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  } while (MmcStatus & DW_MMC_STS_DATA_BUSY);

  IntStatus = ~0;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, FALSE, sizeof (IntStatus), &IntStatus);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex);
  if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) ||
      (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) {
    switch (Packet->SdMmcCmdBlk->CommandIndex) {
    case SD_SET_RELATIVE_ADDR:
      Cmd |= BIT_CMD_SEND_INIT;
      break;
    case SD_STOP_TRANSMISSION:
      Cmd |= BIT_CMD_STOP_ABORT_CMD;
      break;
    case SD_SEND_SCR:
      Trb->UseBE = TRUE;
      break;
    }
    if (Packet->InTransferLength) {
      Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
    } else if (Packet->OutTransferLength) {
      Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
    }
    Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
  } else {
    switch (Packet->SdMmcCmdBlk->CommandIndex) {
    case SD_GO_IDLE_STATE:
      Cmd |= BIT_CMD_SEND_INIT;
      break;
    }
  }
  switch (Packet->SdMmcCmdBlk->ResponseType) {
  case SdMmcResponseTypeR2:
    Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_LONG_RESPONSE;
    break;
  case SdMmcResponseTypeR3:
    Cmd |= BIT_CMD_RESPONSE_EXPECT;
    break;
  case SdMmcResponseTypeR1b:
  case SdMmcResponseTypeR4:
  case SdMmcResponseTypeR6:
  case SdMmcResponseTypeR7:
    Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
    break;
  }
  Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START;

  if (Trb->UseFifo == TRUE) {
    BytCnt = Packet->InTransferLength;
    Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BYTCNT, FALSE, sizeof (BytCnt), &BytCnt);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    if (Packet->InTransferLength > DW_MMC_BLOCK_SIZE) {
      BlkSize = DW_MMC_BLOCK_SIZE;
    } else {
      BlkSize = Packet->InTransferLength;
    }
    Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BLKSIZ, FALSE, sizeof (BlkSize), &BlkSize);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set block size fails: %r\n", Status));
      return Status;
    }
  }

  Argument = Packet->SdMmcCmdBlk->CommandArgument;
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMDARG, FALSE, sizeof (Argument), &Argument);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  ArmDataSynchronizationBarrier ();
  ArmInstructionSynchronizationBarrier ();
  Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  ArmDataSynchronizationBarrier ();
  ArmInstructionSynchronizationBarrier ();

  ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO |
            DW_MMC_INT_RCRC | DW_MMC_INT_RE;
  ErrMask |= DW_MMC_INT_DRT | DW_MMC_INT_SBE;
  if (Packet->InTransferLength || Packet->OutTransferLength) {
    ErrMask |= DW_MMC_INT_DCRC;
  }
  if (Trb->UseFifo == TRUE) {
    Status = ReadFifo (Trb);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  } else {
    Timeout = 10000;
    do {
      if (--Timeout == 0) {
        break;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, TRUE, sizeof (IntStatus), &IntStatus);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      if (IntStatus & ErrMask) {
        return EFI_DEVICE_ERROR;
      }
      if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) == 0)) {  // Transfer Done
        MicroSecondDelay (10);
        continue;
      } else {
        MicroSecondDelay (10);
      }
    } while (!(IntStatus & DW_MMC_INT_CMD_DONE));
    if (Packet->InTransferLength) {
      do {
        Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts);
        if (EFI_ERROR (Status)) {
          return Status;
        }
      } while ((Idsts & DW_MMC_IDSTS_RI) == 0);
      Status = DwMmcHcStopDma (Private, Trb);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } else if (Packet->OutTransferLength) {
      do {
        Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts);
        if (EFI_ERROR (Status)) {
          return Status;
        }
      } while ((Idsts & DW_MMC_IDSTS_TI) == 0);
      Status = DwMmcHcStopDma (Private, Trb);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } // Packet->InTransferLength
  } // UseFifo
  switch (Packet->SdMmcCmdBlk->ResponseType) {
    case SdMmcResponseTypeR1:
    case SdMmcResponseTypeR1b:
    case SdMmcResponseTypeR3:
    case SdMmcResponseTypeR4:
    case SdMmcResponseTypeR5:
    case SdMmcResponseTypeR6:
    case SdMmcResponseTypeR7:
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      break;
    case SdMmcResponseTypeR2:
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP1, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp1), &Packet->SdMmcStatusBlk->Resp1);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP2, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp2), &Packet->SdMmcStatusBlk->Resp2);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP3, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp3), &Packet->SdMmcStatusBlk->Resp3);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      break;
  }

  //
  // The workaround on SD_SEND_CSD is used to be compatible with SDHC.
  //
  if (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_CSD) {
    {
      UINT32   Buf[4];
      ZeroMem (Buf, sizeof (Buf));
      CopyMem ((UINT8 *)Buf, (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1, sizeof (Buf) - 1);
      CopyMem ((UINT8 *)&Packet->SdMmcStatusBlk->Resp0, (UINT8 *)Buf, sizeof (Buf) - 1);
    }
  }

  return EFI_SUCCESS;
}

/**
  Execute the specified TRB.

  @param[in] Private        A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

  @retval EFI_SUCCESS       The TRB is sent to host controller successfully.
  @retval Others            Some erros happen when sending this request to the host controller.

**/
EFI_STATUS
DwMmcExecTrb (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status = EFI_SUCCESS;
  UINT32                              Slot;

  Slot = Trb->Slot;
  if (Private->Slot[Slot].CardType == EmmcCardType) {
    Status = DwEmmcExecTrb (Private, Trb);
  } else if (Private->Slot[Slot].CardType == SdCardType) {
    Status = DwSdExecTrb (Private, Trb);
  } else {
    ASSERT (0);
  }
  return Status;
}

/**
  Check the TRB execution result.

  @param[in] Private        A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

  @retval EFI_SUCCESS       The TRB is executed successfully.
  @retval EFI_NOT_READY     The TRB is not completed for execution.
  @retval Others            Some erros happen when executing this request.

**/
EFI_STATUS
DwMmcCheckTrbResult (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
  UINT32                              Idsts;

  Packet  = Trb->Packet;
  if (Trb->UseFifo == TRUE) {
    return EFI_SUCCESS;
  }
  if (Packet->InTransferLength) {
    do {
      Status = DwMmcHcRwMmio (Private->PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } while ((Idsts & BIT1) == 0);
  } else if (Packet->OutTransferLength) {
    do {
      Status = DwMmcHcRwMmio (Private->PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } while ((Idsts & BIT0) == 0);
  } else {
    return EFI_SUCCESS;
  }
  Idsts = ~0;
  Status = DwMmcHcRwMmio (Private->PciIo, Trb->Slot, DW_MMC_IDSTS, FALSE, sizeof (Idsts), &Idsts);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  return EFI_SUCCESS;
}

/**
  Wait for the TRB execution result.

  @param[in] Private        A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
  @param[in] Trb            The pointer to the DW_MMC_HC_TRB instance.

  @retval EFI_SUCCESS       The TRB is executed successfully.
  @retval Others            Some erros happen when executing this request.

**/
EFI_STATUS
DwMmcWaitTrbResult (
  IN DW_MMC_HC_PRIVATE_DATA           *Private,
  IN DW_MMC_HC_TRB                    *Trb
  )
{
  EFI_STATUS                          Status;
  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
  UINT64                              Timeout;
  BOOLEAN                             InfiniteWait;

  Packet = Trb->Packet;
  //
  // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register
  //
  Timeout = Packet->Timeout;
  if (Timeout == 0) {
    InfiniteWait = TRUE;
  } else {
    InfiniteWait = FALSE;
  }

  while (InfiniteWait || (Timeout > 0)) {
    //
    // Check Trb execution result by reading Normal Interrupt Status register.
    //
    Status = DwMmcCheckTrbResult (Private, Trb);
    if (Status != EFI_NOT_READY) {
      return Status;
    }
    //
    // Stall for 1 microsecond.
    //
    gBS->Stall (1);

    Timeout--;
  }

  return EFI_TIMEOUT;
}