C++程序  |  757行  |  18.6 KB

/** @file
  This file implement the MMC Host Protocol for the DesignWare SD.

  Copyright (c) 2014-2016, Linaro Limited. All rights reserved.
  Copyright (c) 2014-2016, Hisilicon Limited. All rights reserved.

  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include <Library/BaseMemoryLib.h>
#include <Library/CacheMaintenanceLib.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h>
#include <Library/IoLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/TimerLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/MmcHost.h>

#include <Library/PrintLib.h>
#include <Library/SerialPortLib.h>

#include "DwSd.h"

#define DWSD_DESC_PAGE		1
#define DWSD_BLOCK_SIZE	        512
#define DWSD_DMA_BUF_SIZE	(512 * 8)

#define DWSD_DMA_THRESHOLD	16

#define MAX_IDLE_LOOPS		1000000

typedef struct {
  UINT32		Des0;
  UINT32		Des1;
  UINT32		Des2;
  UINT32		Des3;
} DWSD_IDMAC_DESCRIPTOR;

EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
DWSD_IDMAC_DESCRIPTOR     *gpIdmacDesc;
EFI_GUID mDwSdDevicePathGuid = EFI_CALLER_ID_GUID;
STATIC UINT32 mDwSdCommand;
STATIC UINT32 mDwSdArgument;

EFI_STATUS
DwSdSendCommand (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN MMC_CMD                    MmcCmd,
  IN UINT32                     Argument
  );
EFI_STATUS
DwSdReceiveResponse (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN MMC_RESPONSE_TYPE          Type,
  IN UINT32*                    Buffer
  );

EFI_STATUS
DwSdReadBlockData (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN EFI_LBA                    Lba,
  IN UINTN                      Length,
  IN UINT32*                    Buffer
  );

BOOLEAN
DwSdIsPowerOn (
  VOID
  )
{
    return TRUE;
}

EFI_STATUS
DwSdInitialize (
  VOID
  )
{
    DEBUG ((EFI_D_BLKIO, "DwSdInitialize()"));
    return EFI_SUCCESS;
}

BOOLEAN
DwSdIsCardPresent (
  IN EFI_MMC_HOST_PROTOCOL     *This
  )
{
  UINT32    Value;

  /*
   * FIXME
   * At first, reading GPIO pin shouldn't exist in SD driver. We need to
   * add some callbacks to handle settings for hardware platform.
   * In the second, reading GPIO pin should be based on GPIO driver. Now
   * GPIO driver could only be used for one PL061 gpio controller. And it's
   * used to detect jumper setting. As a workaround, we have to read the gpio
   * register instead at here.
   *
   */
  Value = MmioRead32 (0xf8012000 + (1 << 2));
  if (Value)
    return FALSE;
  return TRUE;
}

BOOLEAN
DwSdIsReadOnly (
  IN EFI_MMC_HOST_PROTOCOL     *This
  )
{
  /* FIXME */
  return FALSE;
}

EFI_STATUS
DwSdBuildDevicePath (
  IN EFI_MMC_HOST_PROTOCOL      *This,
  IN EFI_DEVICE_PATH_PROTOCOL   **DevicePath
  )
{
  EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;

  NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH));
  CopyGuid (& ((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &mDwSdDevicePathGuid);

  *DevicePath = NewDevicePathNode;
  return EFI_SUCCESS;
}

EFI_STATUS
DwSdUpdateClock (
  VOID
  )
{
  UINT32 Data;

  /* CMD_UPDATE_CLK */
  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
	 BIT_CMD_START;
  MmioWrite32 (DWSD_CMD, Data);
  while (1) {
    Data = MmioRead32 (DWSD_CMD);
    if (!(Data & CMD_START_BIT))
      break;
    Data = MmioRead32 (DWSD_RINTSTS);
    if (Data & DWSD_INT_HLE)
    {
      Print (L"failed to update mmc clock frequency\n");
      return EFI_DEVICE_ERROR;
    }
  }
  return EFI_SUCCESS;
}

EFI_STATUS
DwSdSetClock (
  IN UINTN                     ClockFreq
  )
{
  UINT32 Divider, Rate, Data, Count;
  EFI_STATUS Status;
  BOOLEAN Found = FALSE;

  for (Divider = 1; Divider < 256; Divider++) {
    Rate = PcdGet32 (PcdDwSdDxeClockFrequencyInHz);
    if ((Rate / (2 * Divider)) <= ClockFreq) {
      Found = TRUE;
      break;
    }
  }
  if (Found == FALSE)
    return EFI_NOT_FOUND;

  // Wait until MMC is idle
  Count = 0;
  do {
    Data = MmioRead32 (DWSD_STATUS);
    if (Count++ > MAX_IDLE_LOOPS)
      break;
  } while (Data & DWSD_STS_DATA_BUSY);

  // Disable MMC clock first
  MmioWrite32 (DWSD_CLKENA, 0);
  Status = DwSdUpdateClock ();
  ASSERT (!EFI_ERROR (Status));

  MmioWrite32 (DWSD_CLKDIV, Divider);
  Status = DwSdUpdateClock ();
  ASSERT (!EFI_ERROR (Status));

  // Enable MMC clock
  MmioWrite32 (DWSD_CLKENA, 1);
  MmioWrite32 (DWSD_CLKSRC, 0);
  Status = DwSdUpdateClock ();
  ASSERT (!EFI_ERROR (Status));
  return EFI_SUCCESS;
}

EFI_STATUS
DwSdNotifyState (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN MMC_STATE                 State
  )
{
  UINT32      Data;
  EFI_STATUS  Status;

  switch (State) {
  case MmcInvalidState:
    ASSERT (0);
    break;
  case MmcHwInitializationState:
    MmioWrite32 (DWSD_PWREN, 1);

    // If device already turn on then restart it
    Data = DWSD_CTRL_RESET_ALL;
    MmioWrite32 (DWSD_CTRL, Data);
    do {
      // Wait until reset operation finished
      Data = MmioRead32 (DWSD_CTRL);
    } while (Data & DWSD_CTRL_RESET_ALL);

    MmioWrite32 (DWSD_RINTSTS, ~0);
    MmioWrite32 (DWSD_INTMASK, 0);
    MmioWrite32 (DWSD_TMOUT, ~0);
    MmioWrite32 (DWSD_IDINTEN, 0);
    MmioWrite32 (DWSD_BMOD, DWSD_IDMAC_SWRESET);

    MmioWrite32 (DWSD_BLKSIZ, DWSD_BLOCK_SIZE);
    do {
      Data = MmioRead32 (DWSD_BMOD);
    } while (Data & DWSD_IDMAC_SWRESET);


    Data = DWSD_DMA_BURST_SIZE(2) | DWSD_FIFO_TWMARK(8) | DWSD_FIFO_RWMARK(7) | (2 << 28);
    MmioWrite32 (DWSD_FIFOTH, Data);
    Data = DWSD_CARD_RD_THR(512) | DWSD_CARD_RD_THR_EN;
    MmioWrite32 (DWSD_CARDTHRCTL, Data);

    // Set Data Length & Data Timer
    MmioWrite32 (DWSD_CTYPE, 0);
    MmioWrite32 (DWSD_DEBNCE, 0x00ffffff);

    // Setup clock that could not be higher than 400KHz.
    Status = DwSdSetClock (400000);
    ASSERT (!EFI_ERROR (Status));
    MicroSecondDelay (100);

    break;
  case MmcIdleState:
    break;
  case MmcReadyState:
    break;
  case MmcIdentificationState:
    break;
  case MmcStandByState:
    break;
  case MmcTransferState:
    break;
  case MmcSendingDataState:
    break;
  case MmcReceiveDataState:
    break;
  case MmcProgrammingState:
    break;
  case MmcDisconnectState:
    break;
  default:
    ASSERT (0);
  }
  return EFI_SUCCESS;
}

EFI_STATUS
SendCommand (
  IN MMC_CMD                    MmcCmd,
  IN UINT32                     Argument
  )
{
  UINT32      Data, ErrMask, Count;

  MmioWrite32 (DWSD_RINTSTS, ~0);
  MmioWrite32 (DWSD_CMDARG, Argument);
  MicroSecondDelay(500);
  // Wait until MMC is idle
  Count = 0;
  do {
    Data = MmioRead32 (DWSD_STATUS);
    if (Count++ > MAX_IDLE_LOOPS)
      break;
  } while (Data & DWSD_STS_DATA_BUSY);

  MmioWrite32 (DWSD_CMD, MmcCmd);

  ErrMask = DWSD_INT_EBE | DWSD_INT_HLE | DWSD_INT_RTO |
            DWSD_INT_RCRC | DWSD_INT_RE;
  ErrMask |= DWSD_INT_DCRC | DWSD_INT_DRT | DWSD_INT_SBE;
  do {
    MicroSecondDelay(500);
    Data = MmioRead32 (DWSD_RINTSTS);

    if (Data & ErrMask) {
      DEBUG ((EFI_D_ERROR, "Data:%x, ErrMask:%x, TBBCNT:%x, TCBCNT:%x, BYTCNT:%x, BLKSIZ:%x\n", Data, ErrMask, MmioRead32 (DWSD_TBBCNT), MmioRead32 (DWSD_TCBCNT), MmioRead32 (DWSD_BYTCNT), MmioRead32 (DWSD_BLKSIZ)));
      return EFI_DEVICE_ERROR;
    }
    if (Data & DWSD_INT_DTO)	// Transfer Done
      break;
  } while (!(Data & DWSD_INT_CMD_DONE));
  MmcCmd &= 0x3f;
  if (MmcCmd == 17)
    MicroSecondDelay(100);
  else if (MmcCmd != 13)
    MicroSecondDelay(5000);

  return EFI_SUCCESS;
}

UINTN ACmd = 0;

EFI_STATUS
DwSdSendCommand (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN MMC_CMD                    MmcCmd,
  IN UINT32                     Argument
  )
{
  UINT32       Cmd = 0;
  EFI_STATUS   Status = EFI_SUCCESS;
  BOOLEAN      Pending = FALSE;
  UINT32       Data;

  switch (MMC_GET_INDX(MmcCmd)) {
  case MMC_INDX(0):
    //Cmd = BIT_CMD_SEND_INIT;
    Cmd = BIT_CMD_WAIT_PRVDATA_COMPLETE;
    break;
  case MMC_INDX(1):
    Cmd = BIT_CMD_RESPONSE_EXPECT;
    break;
  case MMC_INDX(2):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE |
           BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_WAIT_PRVDATA_COMPLETE;
    break;
  case MMC_INDX(3):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_WAIT_PRVDATA_COMPLETE;
    break;
  case MMC_INDX(6):
    if (!ACmd) {
      Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
	    BIT_CMD_DATA_EXPECTED | BIT_CMD_WAIT_PRVDATA_COMPLETE;
    } else {
      Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
	    BIT_CMD_WAIT_PRVDATA_COMPLETE;
    }
    if (!ACmd)
      Pending = TRUE;
    break;
  case MMC_INDX(7):
    if (Argument)
        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
	       BIT_CMD_WAIT_PRVDATA_COMPLETE;
    else
        Cmd = 0;
    break;
  case MMC_INDX(8):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_READ |
           BIT_CMD_WAIT_PRVDATA_COMPLETE;
    break;
  case MMC_INDX(9):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_LONG_RESPONSE | BIT_CMD_WAIT_PRVDATA_COMPLETE;
    break;
  case MMC_INDX(12):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_STOP_ABORT_CMD;
    break;
  case MMC_INDX(13):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
    break;
  case MMC_INDX(17):
  case MMC_INDX(18):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
           BIT_CMD_WAIT_PRVDATA_COMPLETE;
    Pending = TRUE;
    Data = MmioRead32 (DWSD_CTRL);
    Data |= DWSD_CTRL_FIFO_RESET;
    MmioWrite32 (DWSD_CTRL, Data);
    while (MmioRead32 (DWSD_CTRL) & DWSD_CTRL_FIFO_RESET) {
    };
    break;
  case MMC_INDX(24):
  case MMC_INDX(25):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE |
           BIT_CMD_WAIT_PRVDATA_COMPLETE;
    Pending = TRUE;
    break;
  case MMC_INDX(30):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_DATA_EXPECTED;
    break;
  case MMC_INDX(41):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_WAIT_PRVDATA_COMPLETE;
    break;
  case MMC_INDX(51):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
           BIT_CMD_WAIT_PRVDATA_COMPLETE;
    Pending = TRUE;
    break;
  case MMC_INDX(55):
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
    ACmd = 1;
    break;
  default:
    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
    break;
  }

  Cmd |= MMC_GET_INDX(MmcCmd) | BIT_CMD_USE_HOLD_REG | BIT_CMD_START;
  if (Pending) {
    mDwSdCommand = Cmd;
    mDwSdArgument = Argument;
  } else {
    mDwSdCommand = 0;
    mDwSdArgument = 0;
    Status = SendCommand (Cmd, Argument);
  }
  /* Clear ACMD */
  if (MMC_GET_INDX(MmcCmd) != MMC_INDX(55))
    ACmd = 0;
  return Status;
}

EFI_STATUS
DwSdReceiveResponse (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN MMC_RESPONSE_TYPE          Type,
  IN UINT32*                    Buffer
  )
{
  if (Buffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (   (Type == MMC_RESPONSE_TYPE_R1)
      || (Type == MMC_RESPONSE_TYPE_R1b)
      || (Type == MMC_RESPONSE_TYPE_R3)
      || (Type == MMC_RESPONSE_TYPE_R6)
      || (Type == MMC_RESPONSE_TYPE_R7))
  {
    Buffer[0] = MmioRead32 (DWSD_RESP0);
  } else if (Type == MMC_RESPONSE_TYPE_R2) {
    Buffer[0] = MmioRead32 (DWSD_RESP0);
    Buffer[1] = MmioRead32 (DWSD_RESP1);
    Buffer[2] = MmioRead32 (DWSD_RESP2);
    Buffer[3] = MmioRead32 (DWSD_RESP3);
  }
  return EFI_SUCCESS;
}

EFI_STATUS
PrepareDmaData (
  IN DWSD_IDMAC_DESCRIPTOR*    IdmacDesc,
  IN UINTN                      Length,
  IN UINT32*                    Buffer
  )
{
  UINTN  Cnt, Idx, LastIdx, Blks;

  if (Length % 4) {
    DEBUG ((EFI_D_ERROR, "Length isn't aligned with 4\n"));
    return EFI_BAD_BUFFER_SIZE;
  }
  if (Length < DWSD_DMA_THRESHOLD) {
    return EFI_BUFFER_TOO_SMALL;
  }
  Cnt = (Length + DWSD_DMA_BUF_SIZE - 1) / DWSD_DMA_BUF_SIZE;
  Blks = (Length + DWSD_BLOCK_SIZE - 1 ) / DWSD_BLOCK_SIZE;
  Length = DWSD_BLOCK_SIZE * Blks;

  for (Idx = 0; Idx < Cnt; Idx++) {
    (IdmacDesc + Idx)->Des0 = DWSD_IDMAC_DES0_OWN | DWSD_IDMAC_DES0_CH |
	    		      DWSD_IDMAC_DES0_DIC;
    (IdmacDesc + Idx)->Des1 = DWSD_IDMAC_DES1_BS1(DWSD_DMA_BUF_SIZE);
    /* Buffer Address */
    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWSD_DMA_BUF_SIZE * Idx);
    /* Next Descriptor Address */
    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
   	                               (sizeof(DWSD_IDMAC_DESCRIPTOR) * (Idx + 1)));
  }
  /* First Descriptor */
  IdmacDesc->Des0 |= DWSD_IDMAC_DES0_FS;
  /* Last Descriptor */
  LastIdx = Cnt - 1;
  (IdmacDesc + LastIdx)->Des0 |= DWSD_IDMAC_DES0_LD;
  (IdmacDesc + LastIdx)->Des0 &= ~(DWSD_IDMAC_DES0_DIC | DWSD_IDMAC_DES0_CH);
  (IdmacDesc + LastIdx)->Des1 = DWSD_IDMAC_DES1_BS1(Length -
   		                (LastIdx * DWSD_DMA_BUF_SIZE));
  /* Set the Next field of Last Descriptor */
  (IdmacDesc + LastIdx)->Des3 = 0;
  MmioWrite32 (DWSD_DBADDR, (UINT32)((UINTN)IdmacDesc));

  return EFI_SUCCESS;
}

VOID
StartDma (
  UINTN    Length
  )
{
  UINT32 Data;

  Data = MmioRead32 (DWSD_CTRL);
  Data |= DWSD_CTRL_INT_EN | DWSD_CTRL_DMA_EN | DWSD_CTRL_IDMAC_EN;
  MmioWrite32 (DWSD_CTRL, Data);
  Data = MmioRead32 (DWSD_BMOD);
  Data |= DWSD_IDMAC_ENABLE | DWSD_IDMAC_FB;
  MmioWrite32 (DWSD_BMOD, Data);

  MmioWrite32 (DWSD_BLKSIZ, DWSD_BLOCK_SIZE);
  MmioWrite32 (DWSD_BYTCNT, Length);
}

STATIC
EFI_STATUS
ReadFifo (
  IN UINTN                      Length,
  IN UINT32*                    Buffer
  )
{
  UINT32      Data, Received, Count;

  Received = 0;
  Count = (Length + 3) / 4;
  while (Received < Count) {
    Data = MmioRead32 (DWSD_RINTSTS);
    if (Data & DWSD_INT_CMD_DONE) {
      *(Buffer + Received) = MmioRead32 (DWSD_FIFO_START);
      Received++;
    } else {
      DEBUG ((EFI_D_ERROR, "Received:%d, RINTSTS:%x\n", Received, Data));
    }
  }
  while (1) {
    Data = MmioRead32 (DWSD_RINTSTS);
    if (Data & DWSD_INT_DTO)
      break;
  }
  /* FIXME */
  MicroSecondDelay (1000);
  return EFI_SUCCESS;
}

EFI_STATUS
DwSdReadBlockData (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN EFI_LBA                    Lba,
  IN UINTN                      Length,
  IN UINT32*                   Buffer
  )
{
  EFI_STATUS  Status;
  UINT32      DescPages, CountPerPage, Count, Data;
  EFI_TPL     Tpl;

  Tpl = gBS->RaiseTPL (TPL_NOTIFY);

  CountPerPage = EFI_PAGE_SIZE / 16;
  Count = (Length + DWSD_DMA_BUF_SIZE - 1) / DWSD_DMA_BUF_SIZE;
  DescPages = (Count + CountPerPage - 1) / CountPerPage;

  InvalidateDataCacheRange (Buffer, Length);

  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_BUFFER_TOO_SMALL) {
      Data = MmioRead32 (DWSD_CTRL);
      Data |= DWSD_CTRL_FIFO_RESET;
      MmioWrite32 (DWSD_CTRL, Data);
      while (MmioRead32 (DWSD_CTRL) & DWSD_CTRL_FIFO_RESET) {
      };

      Status = SendCommand (mDwSdCommand, mDwSdArgument);
      if (EFI_ERROR (Status)) {
	DEBUG ((EFI_D_ERROR, "Failed to read data from FIFO, mDwSdCommand:%x, mDwSdArgument:%x, Status:%r\n", mDwSdCommand, mDwSdArgument, Status));
	goto out;
      }
      Status = ReadFifo (Length, Buffer);
    }
    goto out;
  } else {

    WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
    StartDma (Length);

    Status = SendCommand (mDwSdCommand, mDwSdArgument);
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "Failed to read data, mDwSdCommand:%x, mDwSdArgument:%x, Status:%r\n", mDwSdCommand, mDwSdArgument, Status));
      goto out;
    }

    /* Wait until data transfer finished */
    while (MmioRead32 (DWSD_TCBCNT) < MmioRead32 (DWSD_BYTCNT)) {
    }

  }
out:
  // Restore Tpl
  gBS->RestoreTPL (Tpl);
  return Status;
}

EFI_STATUS
DwSdWriteBlockData (
  IN EFI_MMC_HOST_PROTOCOL     *This,
  IN EFI_LBA                    Lba,
  IN UINTN                      Length,
  IN UINT32*                    Buffer
  )
{
  EFI_STATUS  Status;
  UINT32      DescPages, CountPerPage, Count;
  EFI_TPL     Tpl;

  Tpl = gBS->RaiseTPL (TPL_NOTIFY);

  CountPerPage = EFI_PAGE_SIZE / 16;
  Count = (Length + DWSD_DMA_BUF_SIZE - 1) / DWSD_DMA_BUF_SIZE;
  DescPages = (Count + CountPerPage - 1) / CountPerPage;

  WriteBackDataCacheRange (Buffer, Length);

  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
  if (EFI_ERROR (Status))
    goto out;

  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
  StartDma (Length);

  Status = SendCommand (mDwSdCommand, mDwSdArgument);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to write data, mDwSdCommand:%x, mDwSdArgument:%x, Status:%r\n", mDwSdCommand, mDwSdArgument, Status));
    goto out;
  }
out:
  // Restore Tpl
  gBS->RestoreTPL (Tpl);
  return Status;
}

EFI_STATUS
DwSdSetIos (
  IN EFI_MMC_HOST_PROTOCOL      *This,
  IN  UINT32                    BusClockFreq,
  IN  UINT32                    BusWidth,
  IN  UINT32                    TimingMode
  )
{
  EFI_STATUS Status = EFI_SUCCESS;
  UINT32    Data;

  if (TimingMode != EMMCBACKWARD) {
    Data = MmioRead32 (DWSD_UHSREG);
    switch (TimingMode) {
    case EMMCHS52DDR1V2:
    case EMMCHS52DDR1V8:
      Data |= 1 << 16;
      break;
    case EMMCHS52:
    case EMMCHS26:
      Data &= ~(1 << 16);
      break;
    default:
      return EFI_UNSUPPORTED;
    }
    MmioWrite32 (DWSD_UHSREG, Data);
  }

  switch (BusWidth) {
  case 1:
    MmioWrite32 (DWSD_CTYPE, 0);
    break;
  case 4:
    MmioWrite32 (DWSD_CTYPE, 1);
    break;
  case 8:
    MmioWrite32 (DWSD_CTYPE, 1 << 16);
    break;
  default:
    return EFI_UNSUPPORTED;
  }
  if (BusClockFreq) {
    Status = DwSdSetClock (BusClockFreq);
  }
  return Status;
}

BOOLEAN
DwSdIsMultiBlock (
  IN EFI_MMC_HOST_PROTOCOL      *This
  )
{
  return TRUE;
}

EFI_MMC_HOST_PROTOCOL gMciHost = {
  MMC_HOST_PROTOCOL_REVISION,
  DwSdIsCardPresent,
  DwSdIsReadOnly,
  DwSdBuildDevicePath,
  DwSdNotifyState,
  DwSdSendCommand,
  DwSdReceiveResponse,
  DwSdReadBlockData,
  DwSdWriteBlockData,
  DwSdSetIos,
  DwSdIsMultiBlock
};

EFI_STATUS
DwSdDxeInitialize (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_STATUS    Status;
  EFI_HANDLE    Handle;

  Handle = NULL;

  DEBUG ((EFI_D_BLKIO, "DwSdDxeInitialize()\n"));

  //Publish Component Name, BlockIO protocol interfaces
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Handle,
                  &gEfiMmcHostProtocolGuid,         &gMciHost,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  return EFI_SUCCESS;
}