diff mbox

[Linaro-uefi,2/3] Drivers/Mmc/DwEmmc: add designware emmc support

Message ID 1479885149-25537-3-git-send-email-haojian.zhuang@linaro.org
State Superseded
Headers show

Commit Message

Haojian Zhuang Nov. 23, 2016, 7:12 a.m. UTC
Support designware emmc controller.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
---
 Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
 4 files changed, 876 insertions(+)
 create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
 create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
 create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
 create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf

Comments

Ard Biesheuvel Nov. 23, 2016, 9:15 a.m. UTC | #1
On 23 November 2016 at 07:12, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
> Support designware emmc controller.
>

Can we use the same driver for the MMC and the SD?

> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> ---
>  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
>  4 files changed, 876 insertions(+)
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmc.h b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
> new file mode 100644
> index 0000000..999b584
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
> @@ -0,0 +1,128 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Linaro Limited. All rights reserved.
> +*  Copyright (c) 2014, 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.
> +*
> +**/
> +
> +
> +#ifndef __DWEMMC_H__
> +#define __DWEMMC_H__
> +
> +#include <Protocol/EmbeddedGpio.h>
> +
> +// DW MMC Registers
> +#define DWEMMC_CTRL            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
> +#define DWEMMC_PWREN           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
> +#define DWEMMC_CLKDIV          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
> +#define DWEMMC_CLKSRC          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
> +#define DWEMMC_CLKENA          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
> +#define DWEMMC_TMOUT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
> +#define DWEMMC_CTYPE           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
> +#define DWEMMC_BLKSIZ          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
> +#define DWEMMC_BYTCNT          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
> +#define DWEMMC_INTMASK         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
> +#define DWEMMC_CMDARG          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
> +#define DWEMMC_CMD             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
> +#define DWEMMC_RESP0           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
> +#define DWEMMC_RESP1           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
> +#define DWEMMC_RESP2           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
> +#define DWEMMC_RESP3           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
> +#define DWEMMC_RINTSTS         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
> +#define DWEMMC_STATUS          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
> +#define DWEMMC_FIFOTH          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
> +#define DWEMMC_DEBNCE          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
> +#define DWEMMC_UHSREG          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
> +#define DWEMMC_BMOD            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
> +#define DWEMMC_DBADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
> +#define DWEMMC_IDSTS           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
> +#define DWEMMC_IDINTEN         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
> +#define DWEMMC_DSCADDR         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
> +#define DWEMMC_BUFADDR         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
> +#define DWEMMC_CARDTHRCTL      ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
> +
> +#define CMD_UPDATE_CLK                         0x80202000
> +#define CMD_START_BIT                          (1 << 31)
> +
> +#define MMC_8BIT_MODE                          (1 << 16)
> +
> +#define BIT_CMD_RESPONSE_EXPECT                        (1 << 6)
> +#define BIT_CMD_LONG_RESPONSE                  (1 << 7)
> +#define BIT_CMD_CHECK_RESPONSE_CRC             (1 << 8)
> +#define BIT_CMD_DATA_EXPECTED                  (1 << 9)
> +#define BIT_CMD_READ                           (0 << 10)
> +#define BIT_CMD_WRITE                          (1 << 10)
> +#define BIT_CMD_BLOCK_TRANSFER                 (0 << 11)
> +#define BIT_CMD_STREAM_TRANSFER                        (1 << 11)
> +#define BIT_CMD_SEND_AUTO_STOP                 (1 << 12)
> +#define BIT_CMD_WAIT_PRVDATA_COMPLETE          (1 << 13)
> +#define BIT_CMD_STOP_ABORT_CMD                 (1 << 14)
> +#define BIT_CMD_SEND_INIT                      (1 << 15)
> +#define BIT_CMD_UPDATE_CLOCK_ONLY              (1 << 21)
> +#define BIT_CMD_READ_CEATA_DEVICE              (1 << 22)
> +#define BIT_CMD_CCS_EXPECTED                   (1 << 23)
> +#define BIT_CMD_ENABLE_BOOT                    (1 << 24)
> +#define BIT_CMD_EXPECT_BOOT_ACK                        (1 << 25)
> +#define BIT_CMD_DISABLE_BOOT                   (1 << 26)
> +#define BIT_CMD_MANDATORY_BOOT                 (0 << 27)
> +#define BIT_CMD_ALTERNATE_BOOT                 (1 << 27)
> +#define BIT_CMD_VOLT_SWITCH                    (1 << 28)
> +#define BIT_CMD_USE_HOLD_REG                   (1 << 29)
> +#define BIT_CMD_START                          (1 << 31)
> +
> +#define DWEMMC_INT_EBE                         (1 << 15)       /* End-bit Err */
> +#define DWEMMC_INT_SBE                         (1 << 13)       /* Start-bit  Err */
> +#define DWEMMC_INT_HLE                         (1 << 12)       /* Hardware-lock Err */
> +#define DWEMMC_INT_FRUN                                (1 << 11)       /* FIFO UN/OV RUN */
> +#define DWEMMC_INT_DRT                         (1 << 9)        /* Data timeout */
> +#define DWEMMC_INT_RTO                         (1 << 8)        /* Response timeout */
> +#define DWEMMC_INT_DCRC                                (1 << 7)        /* Data CRC err */
> +#define DWEMMC_INT_RCRC                                (1 << 6)        /* Response CRC err */
> +#define DWEMMC_INT_RXDR                                (1 << 5)
> +#define DWEMMC_INT_TXDR                                (1 << 4)
> +#define DWEMMC_INT_DTO                         (1 << 3)        /* Data trans over */
> +#define DWEMMC_INT_CMD_DONE                    (1 << 2)
> +#define DWEMMC_INT_RE                          (1 << 1)
> +
> +#define DWEMMC_IDMAC_DES0_DIC                  (1 << 1)
> +#define DWEMMC_IDMAC_DES0_LD                   (1 << 2)
> +#define DWEMMC_IDMAC_DES0_FS                   (1 << 3)
> +#define DWEMMC_IDMAC_DES0_CH                   (1 << 4)
> +#define DWEMMC_IDMAC_DES0_ER                   (1 << 5)
> +#define DWEMMC_IDMAC_DES0_CES                  (1 << 30)
> +#define DWEMMC_IDMAC_DES0_OWN                  (1 << 31)
> +#define DWEMMC_IDMAC_DES1_BS1(x)               ((x) & 0x1fff)
> +#define DWEMMC_IDMAC_DES2_BS2(x)               (((x) & 0x1fff) << 13)
> +#define DWEMMC_IDMAC_SWRESET                   (1 << 0)
> +#define DWEMMC_IDMAC_FB                                (1 << 1)
> +#define DWEMMC_IDMAC_ENABLE                    (1 << 7)
> +
> +#define EMMC_FIX_RCA                           6
> +
> +/* bits in MMC0_CTRL */
> +#define DWEMMC_CTRL_RESET                      (1 << 0)
> +#define DWEMMC_CTRL_FIFO_RESET                 (1 << 1)
> +#define DWEMMC_CTRL_DMA_RESET                  (1 << 2)
> +#define DWEMMC_CTRL_INT_EN                     (1 << 4)
> +#define DWEMMC_CTRL_DMA_EN                     (1 << 5)
> +#define DWEMMC_CTRL_IDMAC_EN                   (1 << 25)
> +#define DWEMMC_CTRL_RESET_ALL                  (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
> +
> +#define DWEMMC_STS_DATA_BUSY                   (1 << 9)
> +
> +#define DWEMMC_FIFO_TWMARK(x)                  (x & 0xfff)
> +#define DWEMMC_FIFO_RWMARK(x)                  ((x & 0x1ff) << 16)
> +#define DWEMMC_DMA_BURST_SIZE(x)               ((x & 0x7) << 28)
> +
> +#define DWEMMC_CARD_RD_THR(x)                  ((x & 0xfff) << 16)
> +#define DWEMMC_CARD_RD_THR_EN                  (1 << 0)
> +
> +#endif  // __DWEMMC_H__
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
> new file mode 100644
> index 0000000..cf75e1c
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
> @@ -0,0 +1,651 @@
> +/** @file
> +  This file implement the MMC Host Protocol for the DesignWare eMMC.
> +
> +  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 "DwEmmc.h"
> +
> +#define DWEMMC_DESC_PAGE               1
> +#define DWEMMC_BLOCK_SIZE              512
> +#define DWEMMC_DMA_BUF_SIZE            (512 * 8)
> +#define DWEMMC_MAX_DESC_PAGES          512
> +
> +typedef struct {
> +  UINT32               Des0;
> +  UINT32               Des1;
> +  UINT32               Des2;
> +  UINT32               Des3;
> +} DWEMMC_IDMAC_DESCRIPTOR;
> +
> +EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
> +DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
> +EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
> +STATIC UINT32 mDwEmmcCommand;
> +STATIC UINT32 mDwEmmcArgument;
> +
> +EFI_STATUS
> +DwEmmcReadBlockData (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN EFI_LBA                    Lba,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  );
> +
> +BOOLEAN
> +DwEmmcIsPowerOn (
> +  VOID
> +  )
> +{
> +    return TRUE;
> +}
> +
> +EFI_STATUS
> +DwEmmcInitialize (
> +  VOID
> +  )
> +{
> +    DEBUG ((EFI_D_BLKIO, "DwEmmcInitialize()"));
> +    return EFI_SUCCESS;
> +}
> +
> +BOOLEAN
> +DwEmmcIsCardPresent (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +BOOLEAN
> +DwEmmcIsReadOnly (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return FALSE;
> +}
> +
> +BOOLEAN
> +DwEmmcIsDmaSupported (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +EFI_STATUS
> +DwEmmcBuildDevicePath (
> +  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, &mDwEmmcDevicePathGuid);
> +
> +  *DevicePath = NewDevicePathNode;
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcUpdateClock (
> +  VOID
> +  )
> +{
> +  UINT32 Data;
> +
> +  /* CMD_UPDATE_CLK */
> +  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
> +        BIT_CMD_START;
> +  MmioWrite32 (DWEMMC_CMD, Data);
> +  while (1) {
> +    Data = MmioRead32 (DWEMMC_CMD);
> +    if (!(Data & CMD_START_BIT)) {
> +      break;
> +    }
> +    Data = MmioRead32 (DWEMMC_RINTSTS);
> +    if (Data & DWEMMC_INT_HLE) {
> +      Print (L"failed to update mmc clock frequency\n");
> +      return EFI_DEVICE_ERROR;
> +    }
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcSetClock (
> +  IN UINTN                     ClockFreq
> +  )
> +{
> +  UINT32 Divider, Rate, Data;
> +  EFI_STATUS Status;
> +  BOOLEAN Found = FALSE;
> +
> +  for (Divider = 1; Divider < 256; Divider++) {
> +    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
> +    if ((Rate / (2 * Divider)) <= ClockFreq) {
> +      Found = TRUE;
> +      break;
> +    }
> +  }
> +  if (Found == FALSE) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  // Wait until MMC is idle
> +  do {
> +    Data = MmioRead32 (DWEMMC_STATUS);
> +  } while (Data & DWEMMC_STS_DATA_BUSY);
> +
> +  // Disable MMC clock first
> +  MmioWrite32 (DWEMMC_CLKENA, 0);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +
> +  MmioWrite32 (DWEMMC_CLKDIV, Divider);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +
> +  // Enable MMC clock
> +  MmioWrite32 (DWEMMC_CLKENA, 1);
> +  MmioWrite32 (DWEMMC_CLKSRC, 0);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcNotifyState (
> +  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 (DWEMMC_PWREN, 1);
> +
> +    // If device already turn on then restart it
> +    Data = DWEMMC_CTRL_RESET_ALL;
> +    MmioWrite32 (DWEMMC_CTRL, Data);
> +    do {
> +      // Wait until reset operation finished
> +      Data = MmioRead32 (DWEMMC_CTRL);
> +    } while (Data & DWEMMC_CTRL_RESET_ALL);
> +
> +    // Setup clock that could not be higher than 400KHz.
> +    Status = DwEmmcSetClock (400000);
> +    ASSERT (!EFI_ERROR (Status));
> +    MicroSecondDelay (100);
> +
> +    MmioWrite32 (DWEMMC_RINTSTS, ~0);
> +    MmioWrite32 (DWEMMC_INTMASK, 0);
> +    MmioWrite32 (DWEMMC_TMOUT, ~0);
> +    MmioWrite32 (DWEMMC_IDINTEN, 0);
> +    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
> +
> +    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
> +    do {
> +      Data = MmioRead32 (DWEMMC_BMOD);
> +    } while (Data & DWEMMC_IDMAC_SWRESET);
> +    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;
> +}
> +
> +// Need to prepare DMA buffer first before sending commands to MMC card
> +BOOLEAN
> +IsPendingReadCommand (
> +  IN MMC_CMD                    MmcCmd
> +  )
> +{
> +  UINTN  Mask;
> +
> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
> +  if ((MmcCmd & Mask) == Mask) {
> +    return TRUE;
> +  }
> +  return FALSE;
> +}
> +
> +BOOLEAN
> +IsPendingWriteCommand (
> +  IN MMC_CMD                    MmcCmd
> +  )
> +{
> +  UINTN  Mask;
> +
> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
> +  if ((MmcCmd & Mask) == Mask) {
> +    return TRUE;
> +  }
> +  return FALSE;
> +}
> +
> +EFI_STATUS
> +SendCommand (
> +  IN MMC_CMD                    MmcCmd,
> +  IN UINT32                     Argument
> +  )
> +{
> +  UINT32      Data, ErrMask;
> +
> +  // Wait until MMC is idle
> +  do {
> +    Data = MmioRead32 (DWEMMC_STATUS);
> +  } while (Data & DWEMMC_STS_DATA_BUSY);
> +
> +  MmioWrite32 (DWEMMC_RINTSTS, ~0);
> +  MmioWrite32 (DWEMMC_CMDARG, Argument);
> +  MmioWrite32 (DWEMMC_CMD, MmcCmd);
> +
> +  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
> +            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
> +  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
> +  do {
> +    MicroSecondDelay(500);
> +    Data = MmioRead32 (DWEMMC_RINTSTS);
> +
> +    if (Data & ErrMask) {
> +      return EFI_DEVICE_ERROR;
> +    }
> +    if (Data & DWEMMC_INT_DTO) {       // Transfer Done
> +      break;
> +    }
> +  } while (!(Data & DWEMMC_INT_CMD_DONE));
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcSendCommand (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN MMC_CMD                    MmcCmd,
> +  IN UINT32                     Argument
> +  )
> +{
> +  UINT32       Cmd = 0;
> +  EFI_STATUS   Status = EFI_SUCCESS;
> +
> +  switch (MMC_GET_INDX(MmcCmd)) {
> +  case MMC_INDX(0):
> +    Cmd = BIT_CMD_SEND_INIT;
> +    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_SEND_INIT;
> +    break;
> +  case MMC_INDX(3):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_SEND_INIT;
> +    break;
> +  case MMC_INDX(7):
> +    if (Argument)
> +        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
> +    else
> +        Cmd = 0;
> +    break;
> +  case MMC_INDX(8):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | 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;
> +    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 |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(16):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    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;
> +    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;
> +    break;
> +  case MMC_INDX(30):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED;
> +    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 (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
> +    mDwEmmcCommand = Cmd;
> +    mDwEmmcArgument = Argument;
> +  } else {
> +    Status = SendCommand (Cmd, Argument);
> +  }
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcReceiveResponse (
> +  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 (DWEMMC_RESP0);
> +  } else if (Type == MMC_RESPONSE_TYPE_R2) {
> +    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
> +    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
> +    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
> +    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +PrepareDmaData (
> +  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  )
> +{
> +  UINTN  Cnt, Blks, Idx, LastIdx;
> +
> +  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
> +  Length = DWEMMC_BLOCK_SIZE * Blks;
> +
> +  for (Idx = 0; Idx < Cnt; Idx++) {
> +    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
> +                              DWEMMC_IDMAC_DES0_DIC;
> +    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
> +    /* Buffer Address */
> +    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
> +    /* Next Descriptor Address */
> +    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
> +                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
> +  }
> +  /* First Descriptor */
> +  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
> +  /* Last Descriptor */
> +  LastIdx = Cnt - 1;
> +  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
> +  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
> +  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
> +                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
> +  /* Set the Next field of Last Descriptor */
> +  (IdmacDesc + LastIdx)->Des3 = 0;
> +  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
> +
> +  return EFI_SUCCESS;
> +}
> +
> +VOID
> +StartDma (
> +  UINTN    Length
> +  )
> +{
> +  UINT32 Data;
> +
> +  Data = MmioRead32 (DWEMMC_CTRL);
> +  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
> +  MmioWrite32 (DWEMMC_CTRL, Data);
> +  Data = MmioRead32 (DWEMMC_BMOD);
> +  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
> +  MmioWrite32 (DWEMMC_BMOD, Data);
> +
> +  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
> +  MmioWrite32 (DWEMMC_BYTCNT, Length);
> +}
> +
> +EFI_STATUS
> +DwEmmcReadBlockData (
> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  DescPages = (Count + CountPerPage - 1) / CountPerPage;
> +
> +  InvalidateDataCacheRange (Buffer, Length);
> +
> +  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
> +  if (EFI_ERROR (Status)) {
> +    goto out;
> +  }
> +
> +  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
> +  StartDma (Length);
> +
> +  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((EFI_D_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
> +    goto out;
> +  }
> +out:
> +  // Restore Tpl
> +  gBS->RestoreTPL (Tpl);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcWriteBlockData (
> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_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 (mDwEmmcCommand, mDwEmmcArgument);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((EFI_D_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
> +    goto out;
> +  }
> +out:
> +  // Restore Tpl
> +  gBS->RestoreTPL (Tpl);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcSetIos (
> +  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 (DWEMMC_UHSREG);
> +    switch (TimingMode) {
> +    case EMMCHS52DDR1V2:
> +    case EMMCHS52DDR1V8:
> +      Data |= 1 << 16;
> +      break;
> +    case EMMCHS52:
> +    case EMMCHS26:
> +      Data &= ~(1 << 16);
> +      break;
> +    default:
> +      return EFI_UNSUPPORTED;
> +    }
> +    MmioWrite32 (DWEMMC_UHSREG, Data);
> +  }
> +
> +  switch (BusWidth) {
> +  case 1:
> +    MmioWrite32 (DWEMMC_CTYPE, 0);
> +    break;
> +  case 4:
> +    MmioWrite32 (DWEMMC_CTYPE, 1);
> +    break;
> +  case 8:
> +    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
> +    break;
> +  default:
> +    return EFI_UNSUPPORTED;
> +  }
> +  if (BusClockFreq) {
> +    Status = DwEmmcSetClock (BusClockFreq);
> +  }
> +  return Status;
> +}
> +
> +BOOLEAN
> +DwEmmcIsMultiBlock (
> +  IN EFI_MMC_HOST_PROTOCOL      *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +EFI_MMC_HOST_PROTOCOL gMciHost = {
> +  MMC_HOST_PROTOCOL_REVISION,
> +  DwEmmcIsCardPresent,
> +  DwEmmcIsReadOnly,
> +  DwEmmcBuildDevicePath,
> +  DwEmmcNotifyState,
> +  DwEmmcSendCommand,
> +  DwEmmcReceiveResponse,
> +  DwEmmcReadBlockData,
> +  DwEmmcWriteBlockData,
> +  DwEmmcSetIos,
> +  DwEmmcIsMultiBlock
> +};
> +
> +EFI_STATUS
> +DwEmmcDxeInitialize (
> +  IN EFI_HANDLE         ImageHandle,
> +  IN EFI_SYSTEM_TABLE   *SystemTable
> +  )
> +{
> +  EFI_STATUS    Status;
> +  EFI_HANDLE    Handle;
> +
> +  Handle = NULL;
> +
> +  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
> +  if (gpIdmacDesc == NULL) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  DEBUG ((EFI_D_BLKIO, "DwEmmcDxeInitialize()\n"));
> +
> +  //Publish Component Name, BlockIO protocol interfaces
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &Handle,
> +                  &gEfiMmcHostProtocolGuid,         &gMciHost,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> new file mode 100644
> index 0000000..b281b93
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> @@ -0,0 +1,42 @@
> +#/** @file
> +# Framework Module Development Environment Industry Standards
> +#
> +# This Package provides headers and libraries that conform to EFI/PI Industry standards.
> +# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
> +# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR>
> +# Copyright (c) 2015-2016, Linaro. 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.
> +#
> +#**/
> +
> +[Defines]
> +  DEC_SPECIFICATION              = 0x00010005
> +  PACKAGE_NAME                   = OpenPlatformDriversMmcDwEmmcDxePkg
> +  PACKAGE_GUID                   = 3869905e-c96c-4d20-9bfb-3b9d71bb900c
> +  PACKAGE_VERSION                = 0.1
> +
> +
> +################################################################################
> +#
> +# Include Section - list of Include Paths that are provided by this package.
> +#                   Comments are used for Keywords and Module Types.
> +#
> +# Supported Module Types:
> +#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
> +#
> +################################################################################
> +
> +[Guids.common]
> +  gDwEmmcDxeTokenSpaceGuid     = { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}
> +
> +[PcdsFixedAtBuild.common]
> +  # DwEmmc Driver PCDs
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> new file mode 100644
> index 0000000..f23494c
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> @@ -0,0 +1,55 @@
> +#/** @file
> +#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
> +#
> +#  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.
> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005
> +  BASE_NAME                      = DwEmmcDxe
> +  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +
> +  ENTRY_POINT                    = DwEmmcDxeInitialize
> +
> +[Sources.common]
> +  DwEmmcDxe.c
> +
> +[Packages]
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdePkg/MdePkg.dec
> +  OpenPlatformPkg/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> +
> +[LibraryClasses]
> +  ArmLib
> +  BaseLib
> +  BaseMemoryLib
> +  CacheMaintenanceLib
> +  IoLib
> +  MemoryAllocationLib
> +  TimerLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Protocols]
> +  gEfiCpuArchProtocolGuid
> +  gEfiDevicePathProtocolGuid
> +  gEfiMmcHostProtocolGuid
> +
> +[Pcd]
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
> +
> +[Depex]
> +  TRUE
> --
> 2.7.4
>
Haojian Zhuang Nov. 23, 2016, 9:31 a.m. UTC | #2
On 23 November 2016 at 17:15, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 23 November 2016 at 07:12, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>> Support designware emmc controller.
>>
>
> Can we use the same driver for the MMC and the SD?
>

How to make the driver to be enumerated twice?

Best Regards
Haojian

>> Contributed-under: TianoCore Contribution Agreement 1.0
>> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
>> ---
>>  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
>>  4 files changed, 876 insertions(+)
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>>
Ard Biesheuvel Nov. 23, 2016, 9:32 a.m. UTC | #3
On 23 November 2016 at 09:31, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
> On 23 November 2016 at 17:15, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>> On 23 November 2016 at 07:12, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>>> Support designware emmc controller.
>>>
>>
>> Can we use the same driver for the MMC and the SD?
>>
>
> How to make the driver to be enumerated twice?
>

Use the UEFI driver model
Haojian Zhuang Nov. 23, 2016, 9:33 a.m. UTC | #4
On 23 November 2016 at 17:32, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 23 November 2016 at 09:31, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>> On 23 November 2016 at 17:15, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>>> On 23 November 2016 at 07:12, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>>>> Support designware emmc controller.
>>>>
>>>
>>> Can we use the same driver for the MMC and the SD?
>>>
>>
>> How to make the driver to be enumerated twice?
>>
>
> Use the UEFI driver model

It's a bit abstract. Is there any example in UEFI?

Best Regards
Haojian
Ard Biesheuvel Nov. 23, 2016, 9:34 a.m. UTC | #5
On 23 November 2016 at 09:33, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
> On 23 November 2016 at 17:32, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>> On 23 November 2016 at 09:31, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>>> On 23 November 2016 at 17:15, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>>>> On 23 November 2016 at 07:12, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>>>>> Support designware emmc controller.
>>>>>
>>>>
>>>> Can we use the same driver for the MMC and the SD?
>>>>
>>>
>>> How to make the driver to be enumerated twice?
>>>
>>
>> Use the UEFI driver model
>
> It's a bit abstract. Is there any example in UEFI?
>

There are many examples in UEFI.

A simple example is
OvmfPkg/VirtioRngDxe/VirtioRng.c
Leif Lindholm Nov. 29, 2016, 8:01 p.m. UTC | #6
On Wed, Nov 23, 2016 at 03:12:28PM +0800, Haojian Zhuang wrote:
> Support designware emmc controller.
> 
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> ---
>  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
>  4 files changed, 876 insertions(+)
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> 
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmc.h b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
> new file mode 100644
> index 0000000..999b584
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
> @@ -0,0 +1,128 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Linaro Limited. All rights reserved.
> +*  Copyright (c) 2014, 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.
> +*
> +**/
> +
> +
> +#ifndef __DWEMMC_H__
> +#define __DWEMMC_H__
> +
> +#include <Protocol/EmbeddedGpio.h>
> +
> +// DW MMC Registers
> +#define DWEMMC_CTRL		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)

So, we already have tons of drivers that use a Pcd for a base address
so that only one of them can exist in a system. This is not cause
for rejecting a driver.

But I would be very, very happy if you had time to look into Ard's
recent non-discoverable patchset, and perhaps the way the guys at
SemiHalf have used it for their Armada 70x0 port.

This driver would be a lot nicer if written to that interface.

> +#define DWEMMC_PWREN		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
> +#define DWEMMC_CLKDIV		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
> +#define DWEMMC_CLKSRC		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
> +#define DWEMMC_CLKENA		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)

Tabs again. Please untabify.

> +#define DWEMMC_TMOUT		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
> +#define DWEMMC_CTYPE		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
> +#define DWEMMC_BLKSIZ		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
> +#define DWEMMC_BYTCNT		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
> +#define DWEMMC_INTMASK		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
> +#define DWEMMC_CMDARG		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
> +#define DWEMMC_CMD		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
> +#define DWEMMC_RESP0		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
> +#define DWEMMC_RESP1		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
> +#define DWEMMC_RESP2		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
> +#define DWEMMC_RESP3		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
> +#define DWEMMC_RINTSTS		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
> +#define DWEMMC_STATUS		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
> +#define DWEMMC_FIFOTH		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
> +#define DWEMMC_DEBNCE		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
> +#define DWEMMC_UHSREG		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
> +#define DWEMMC_BMOD		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
> +#define DWEMMC_DBADDR		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
> +#define DWEMMC_IDSTS		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
> +#define DWEMMC_IDINTEN		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
> +#define DWEMMC_DSCADDR		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
> +#define DWEMMC_BUFADDR		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
> +#define DWEMMC_CARDTHRCTL	((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
> +
> +#define CMD_UPDATE_CLK				0x80202000
> +#define CMD_START_BIT				(1 << 31)
> +
> +#define MMC_8BIT_MODE				(1 << 16)
> +
> +#define BIT_CMD_RESPONSE_EXPECT			(1 << 6)
> +#define BIT_CMD_LONG_RESPONSE			(1 << 7)
> +#define BIT_CMD_CHECK_RESPONSE_CRC		(1 << 8)
> +#define BIT_CMD_DATA_EXPECTED			(1 << 9)
> +#define BIT_CMD_READ				(0 << 10)
> +#define BIT_CMD_WRITE				(1 << 10)
> +#define BIT_CMD_BLOCK_TRANSFER			(0 << 11)
> +#define BIT_CMD_STREAM_TRANSFER			(1 << 11)
> +#define BIT_CMD_SEND_AUTO_STOP			(1 << 12)
> +#define BIT_CMD_WAIT_PRVDATA_COMPLETE		(1 << 13)
> +#define BIT_CMD_STOP_ABORT_CMD			(1 << 14)
> +#define BIT_CMD_SEND_INIT			(1 << 15)
> +#define BIT_CMD_UPDATE_CLOCK_ONLY		(1 << 21)
> +#define BIT_CMD_READ_CEATA_DEVICE		(1 << 22)
> +#define BIT_CMD_CCS_EXPECTED			(1 << 23)
> +#define BIT_CMD_ENABLE_BOOT			(1 << 24)
> +#define BIT_CMD_EXPECT_BOOT_ACK			(1 << 25)
> +#define BIT_CMD_DISABLE_BOOT			(1 << 26)
> +#define BIT_CMD_MANDATORY_BOOT			(0 << 27)
> +#define BIT_CMD_ALTERNATE_BOOT			(1 << 27)
> +#define BIT_CMD_VOLT_SWITCH			(1 << 28)
> +#define BIT_CMD_USE_HOLD_REG			(1 << 29)
> +#define BIT_CMD_START				(1 << 31)
> +
> +#define DWEMMC_INT_EBE				(1 << 15)	/* End-bit Err */
> +#define DWEMMC_INT_SBE				(1 << 13)	/* Start-bit  Err */
> +#define DWEMMC_INT_HLE				(1 << 12)	/* Hardware-lock Err */
> +#define DWEMMC_INT_FRUN				(1 << 11)	/* FIFO UN/OV RUN */
> +#define DWEMMC_INT_DRT				(1 << 9)	/* Data timeout */
> +#define DWEMMC_INT_RTO				(1 << 8)	/* Response timeout */
> +#define DWEMMC_INT_DCRC				(1 << 7)	/* Data CRC err */
> +#define DWEMMC_INT_RCRC				(1 << 6)	/* Response CRC err */
> +#define DWEMMC_INT_RXDR				(1 << 5)
> +#define DWEMMC_INT_TXDR				(1 << 4)
> +#define DWEMMC_INT_DTO				(1 << 3)	/* Data trans over */
> +#define DWEMMC_INT_CMD_DONE			(1 << 2)
> +#define DWEMMC_INT_RE				(1 << 1)
> +
> +#define DWEMMC_IDMAC_DES0_DIC			(1 << 1)
> +#define DWEMMC_IDMAC_DES0_LD			(1 << 2)
> +#define DWEMMC_IDMAC_DES0_FS			(1 << 3)
> +#define DWEMMC_IDMAC_DES0_CH			(1 << 4)
> +#define DWEMMC_IDMAC_DES0_ER			(1 << 5)
> +#define DWEMMC_IDMAC_DES0_CES			(1 << 30)
> +#define DWEMMC_IDMAC_DES0_OWN			(1 << 31)
> +#define DWEMMC_IDMAC_DES1_BS1(x)		((x) & 0x1fff)
> +#define DWEMMC_IDMAC_DES2_BS2(x)		(((x) & 0x1fff) << 13)
> +#define DWEMMC_IDMAC_SWRESET			(1 << 0)
> +#define DWEMMC_IDMAC_FB				(1 << 1)
> +#define DWEMMC_IDMAC_ENABLE			(1 << 7)
> +
> +#define EMMC_FIX_RCA				6
> +
> +/* bits in MMC0_CTRL */
> +#define DWEMMC_CTRL_RESET			(1 << 0)
> +#define DWEMMC_CTRL_FIFO_RESET			(1 << 1)
> +#define DWEMMC_CTRL_DMA_RESET			(1 << 2)
> +#define DWEMMC_CTRL_INT_EN			(1 << 4)
> +#define DWEMMC_CTRL_DMA_EN			(1 << 5)
> +#define DWEMMC_CTRL_IDMAC_EN			(1 << 25)
> +#define DWEMMC_CTRL_RESET_ALL			(DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
> +
> +#define DWEMMC_STS_DATA_BUSY			(1 << 9)
> +
> +#define DWEMMC_FIFO_TWMARK(x)			(x & 0xfff)
> +#define DWEMMC_FIFO_RWMARK(x)			((x & 0x1ff) << 16)
> +#define DWEMMC_DMA_BURST_SIZE(x)		((x & 0x7) << 28)
> +
> +#define DWEMMC_CARD_RD_THR(x)			((x & 0xfff) << 16)
> +#define DWEMMC_CARD_RD_THR_EN			(1 << 0)
> +
> +#endif  // __DWEMMC_H__
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
> new file mode 100644
> index 0000000..cf75e1c
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
> @@ -0,0 +1,651 @@
> +/** @file
> +  This file implement the MMC Host Protocol for the DesignWare eMMC.
> +
> +  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>

Could you insert these two, alphabetically sorted, in the already
sorted block above?

> +
> +#include "DwEmmc.h"
> +
> +#define DWEMMC_DESC_PAGE		1
> +#define DWEMMC_BLOCK_SIZE		512
> +#define DWEMMC_DMA_BUF_SIZE		(512 * 8)
> +#define DWEMMC_MAX_DESC_PAGES   	512
> +
> +typedef struct {
> +  UINT32		Des0;
> +  UINT32		Des1;
> +  UINT32		Des2;
> +  UINT32		Des3;
> +} DWEMMC_IDMAC_DESCRIPTOR;
> +
> +EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
> +DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
> +EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
> +STATIC UINT32 mDwEmmcCommand;
> +STATIC UINT32 mDwEmmcArgument;
> +
> +EFI_STATUS
> +DwEmmcReadBlockData (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN EFI_LBA                    Lba,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  );
> +
> +BOOLEAN
> +DwEmmcIsPowerOn (
> +  VOID
> +  )
> +{
> +    return TRUE;
> +}
> +
> +EFI_STATUS
> +DwEmmcInitialize (
> +  VOID
> +  )
> +{
> +    DEBUG ((EFI_D_BLKIO, "DwEmmcInitialize()"));

BaseTools/Scripts/PatchCheck.py will warn you that EFI_D_BLKIO is no
longer the preferred macro to use, and that DEBUG_BLKIO is better.
It would be good if you could run the set through PatchCheck for v2.

> +    return EFI_SUCCESS;
> +}
> +
> +BOOLEAN
> +DwEmmcIsCardPresent (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +BOOLEAN
> +DwEmmcIsReadOnly (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return FALSE;
> +}
> +
> +BOOLEAN
> +DwEmmcIsDmaSupported (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +EFI_STATUS
> +DwEmmcBuildDevicePath (
> +  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, &mDwEmmcDevicePathGuid);
> +
> +  *DevicePath = NewDevicePathNode;
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcUpdateClock (
> +  VOID
> +  )
> +{
> +  UINT32 Data;
> +
> +  /* CMD_UPDATE_CLK */
> +  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
> +	 BIT_CMD_START;
> +  MmioWrite32 (DWEMMC_CMD, Data);
> +  while (1) {
> +    Data = MmioRead32 (DWEMMC_CMD);
> +    if (!(Data & CMD_START_BIT)) {
> +      break;
> +    }
> +    Data = MmioRead32 (DWEMMC_RINTSTS);
> +    if (Data & DWEMMC_INT_HLE) {
> +      Print (L"failed to update mmc clock frequency\n");
> +      return EFI_DEVICE_ERROR;
> +    }
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcSetClock (
> +  IN UINTN                     ClockFreq
> +  )
> +{
> +  UINT32 Divider, Rate, Data;
> +  EFI_STATUS Status;
> +  BOOLEAN Found = FALSE;
> +
> +  for (Divider = 1; Divider < 256; Divider++) {
> +    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
> +    if ((Rate / (2 * Divider)) <= ClockFreq) {
> +      Found = TRUE;
> +      break;
> +    }
> +  }
> +  if (Found == FALSE) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  // Wait until MMC is idle
> +  do {
> +    Data = MmioRead32 (DWEMMC_STATUS);
> +  } while (Data & DWEMMC_STS_DATA_BUSY);
> +
> +  // Disable MMC clock first
> +  MmioWrite32 (DWEMMC_CLKENA, 0);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +
> +  MmioWrite32 (DWEMMC_CLKDIV, Divider);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +
> +  // Enable MMC clock
> +  MmioWrite32 (DWEMMC_CLKENA, 1);
> +  MmioWrite32 (DWEMMC_CLKSRC, 0);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcNotifyState (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN MMC_STATE                 State
> +  )
> +{
> +  UINT32      Data;
> +  EFI_STATUS  Status;
> +
> +  switch (State) {
> +  case MmcInvalidState:
> +    ASSERT (0);

Should this not return something other than EFI_SUCCESS for RELEASE
builds?

> +    break;
> +  case MmcHwInitializationState:
> +    MmioWrite32 (DWEMMC_PWREN, 1);
> +
> +    // If device already turn on then restart it
> +    Data = DWEMMC_CTRL_RESET_ALL;
> +    MmioWrite32 (DWEMMC_CTRL, Data);
> +    do {
> +      // Wait until reset operation finished
> +      Data = MmioRead32 (DWEMMC_CTRL);
> +    } while (Data & DWEMMC_CTRL_RESET_ALL);
> +
> +    // Setup clock that could not be higher than 400KHz.
> +    Status = DwEmmcSetClock (400000);
> +    ASSERT (!EFI_ERROR (Status));
> +    MicroSecondDelay (100);

Why the delay?
Does this also need a barrier?

> +
> +    MmioWrite32 (DWEMMC_RINTSTS, ~0);
> +    MmioWrite32 (DWEMMC_INTMASK, 0);
> +    MmioWrite32 (DWEMMC_TMOUT, ~0);
> +    MmioWrite32 (DWEMMC_IDINTEN, 0);
> +    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
> +
> +    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
> +    do {
> +      Data = MmioRead32 (DWEMMC_BMOD);
> +    } while (Data & DWEMMC_IDMAC_SWRESET);
> +    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;
> +}
> +
> +// Need to prepare DMA buffer first before sending commands to MMC card
> +BOOLEAN
> +IsPendingReadCommand (
> +  IN MMC_CMD                    MmcCmd
> +  )
> +{
> +  UINTN  Mask;
> +
> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
> +  if ((MmcCmd & Mask) == Mask) {
> +    return TRUE;
> +  }
> +  return FALSE;
> +}
> +
> +BOOLEAN
> +IsPendingWriteCommand (
> +  IN MMC_CMD                    MmcCmd
> +  )
> +{
> +  UINTN  Mask;
> +
> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
> +  if ((MmcCmd & Mask) == Mask) {
> +    return TRUE;
> +  }
> +  return FALSE;
> +}
> +
> +EFI_STATUS
> +SendCommand (
> +  IN MMC_CMD                    MmcCmd,
> +  IN UINT32                     Argument
> +  )
> +{
> +  UINT32      Data, ErrMask;
> +
> +  // Wait until MMC is idle
> +  do {
> +    Data = MmioRead32 (DWEMMC_STATUS);
> +  } while (Data & DWEMMC_STS_DATA_BUSY);
> +
> +  MmioWrite32 (DWEMMC_RINTSTS, ~0);
> +  MmioWrite32 (DWEMMC_CMDARG, Argument);
> +  MmioWrite32 (DWEMMC_CMD, MmcCmd);
> +
> +  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
> +            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
> +  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
> +  do {
> +    MicroSecondDelay(500);
> +    Data = MmioRead32 (DWEMMC_RINTSTS);
> +
> +    if (Data & ErrMask) {
> +      return EFI_DEVICE_ERROR;
> +    }
> +    if (Data & DWEMMC_INT_DTO) {	// Transfer Done
> +      break;
> +    }
> +  } while (!(Data & DWEMMC_INT_CMD_DONE));
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcSendCommand (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN MMC_CMD                    MmcCmd,
> +  IN UINT32                     Argument
> +  )
> +{
> +  UINT32       Cmd = 0;
> +  EFI_STATUS   Status = EFI_SUCCESS;
> +
> +  switch (MMC_GET_INDX(MmcCmd)) {
> +  case MMC_INDX(0):
> +    Cmd = BIT_CMD_SEND_INIT;
> +    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_SEND_INIT;
> +    break;
> +  case MMC_INDX(3):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_SEND_INIT;
> +    break;
> +  case MMC_INDX(7):
> +    if (Argument)
> +        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
> +    else
> +        Cmd = 0;
> +    break;
> +  case MMC_INDX(8):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | 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;
> +    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 |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(16):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    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;
> +    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;
> +    break;
> +  case MMC_INDX(30):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED;
> +    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 (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
> +    mDwEmmcCommand = Cmd;
> +    mDwEmmcArgument = Argument;
> +  } else {
> +    Status = SendCommand (Cmd, Argument);
> +  }
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcReceiveResponse (
> +  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 (DWEMMC_RESP0);
> +  } else if (Type == MMC_RESPONSE_TYPE_R2) {
> +    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
> +    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
> +    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
> +    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +PrepareDmaData (
> +  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  )
> +{
> +  UINTN  Cnt, Blks, Idx, LastIdx;
> +
> +  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
> +  Length = DWEMMC_BLOCK_SIZE * Blks;
> +
> +  for (Idx = 0; Idx < Cnt; Idx++) {
> +    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
> +                              DWEMMC_IDMAC_DES0_DIC;
> +    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
> +    /* Buffer Address */
> +    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
> +    /* Next Descriptor Address */
> +    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
> +                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
> +  }
> +  /* First Descriptor */
> +  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
> +  /* Last Descriptor */
> +  LastIdx = Cnt - 1;
> +  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
> +  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
> +  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
> +                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
> +  /* Set the Next field of Last Descriptor */
> +  (IdmacDesc + LastIdx)->Des3 = 0;
> +  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
> +
> +  return EFI_SUCCESS;
> +}
> +
> +VOID
> +StartDma (
> +  UINTN    Length
> +  )
> +{
> +  UINT32 Data;
> +
> +  Data = MmioRead32 (DWEMMC_CTRL);
> +  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
> +  MmioWrite32 (DWEMMC_CTRL, Data);
> +  Data = MmioRead32 (DWEMMC_BMOD);
> +  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
> +  MmioWrite32 (DWEMMC_BMOD, Data);
> +
> +  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
> +  MmioWrite32 (DWEMMC_BYTCNT, Length);
> +}
> +
> +EFI_STATUS
> +DwEmmcReadBlockData (
> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  DescPages = (Count + CountPerPage - 1) / CountPerPage;
> +
> +  InvalidateDataCacheRange (Buffer, Length);
> +
> +  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
> +  if (EFI_ERROR (Status)) {
> +    goto out;
> +  }
> +
> +  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
> +  StartDma (Length);
> +
> +  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((EFI_D_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
> +    goto out;
> +  }
> +out:
> +  // Restore Tpl
> +  gBS->RestoreTPL (Tpl);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcWriteBlockData (
> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_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 (mDwEmmcCommand, mDwEmmcArgument);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((EFI_D_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
> +    goto out;
> +  }
> +out:
> +  // Restore Tpl
> +  gBS->RestoreTPL (Tpl);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcSetIos (
> +  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 (DWEMMC_UHSREG);
> +    switch (TimingMode) {
> +    case EMMCHS52DDR1V2:
> +    case EMMCHS52DDR1V8:
> +      Data |= 1 << 16;
> +      break;
> +    case EMMCHS52:
> +    case EMMCHS26:
> +      Data &= ~(1 << 16);
> +      break;
> +    default:
> +      return EFI_UNSUPPORTED;
> +    }
> +    MmioWrite32 (DWEMMC_UHSREG, Data);
> +  }
> +
> +  switch (BusWidth) {
> +  case 1:
> +    MmioWrite32 (DWEMMC_CTYPE, 0);
> +    break;
> +  case 4:
> +    MmioWrite32 (DWEMMC_CTYPE, 1);
> +    break;
> +  case 8:
> +    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
> +    break;
> +  default:
> +    return EFI_UNSUPPORTED;
> +  }
> +  if (BusClockFreq) {
> +    Status = DwEmmcSetClock (BusClockFreq);
> +  }
> +  return Status;
> +}
> +
> +BOOLEAN
> +DwEmmcIsMultiBlock (
> +  IN EFI_MMC_HOST_PROTOCOL      *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +EFI_MMC_HOST_PROTOCOL gMciHost = {
> +  MMC_HOST_PROTOCOL_REVISION,
> +  DwEmmcIsCardPresent,
> +  DwEmmcIsReadOnly,
> +  DwEmmcBuildDevicePath,
> +  DwEmmcNotifyState,
> +  DwEmmcSendCommand,
> +  DwEmmcReceiveResponse,
> +  DwEmmcReadBlockData,
> +  DwEmmcWriteBlockData,
> +  DwEmmcSetIos,
> +  DwEmmcIsMultiBlock
> +};
> +
> +EFI_STATUS
> +DwEmmcDxeInitialize (
> +  IN EFI_HANDLE         ImageHandle,
> +  IN EFI_SYSTEM_TABLE   *SystemTable
> +  )
> +{
> +  EFI_STATUS    Status;
> +  EFI_HANDLE    Handle;
> +
> +  Handle = NULL;
> +
> +  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
> +  if (gpIdmacDesc == NULL) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  DEBUG ((EFI_D_BLKIO, "DwEmmcDxeInitialize()\n"));
> +
> +  //Publish Component Name, BlockIO protocol interfaces
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &Handle,
> +                  &gEfiMmcHostProtocolGuid,         &gMciHost,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> new file mode 100644
> index 0000000..b281b93
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> @@ -0,0 +1,42 @@
> +#/** @file
> +# Framework Module Development Environment Industry Standards
> +#
> +# This Package provides headers and libraries that conform to EFI/PI Industry standards.
> +# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
> +# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR>
> +# Copyright (c) 2015-2016, Linaro. 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.
> +#
> +#**/
> +
> +[Defines]
> +  DEC_SPECIFICATION              = 0x00010005
> +  PACKAGE_NAME                   = OpenPlatformDriversMmcDwEmmcDxePkg

Can probably drop "OpenPlatformDrivers".

> +  PACKAGE_GUID                   = 3869905e-c96c-4d20-9bfb-3b9d71bb900c
> +  PACKAGE_VERSION                = 0.1
> +
> +
> +################################################################################
> +#
> +# Include Section - list of Include Paths that are provided by this package.
> +#                   Comments are used for Keywords and Module Types.
> +#
> +# Supported Module Types:
> +#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
> +#
> +################################################################################
> +
> +[Guids.common]
> +  gDwEmmcDxeTokenSpaceGuid	= { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}

Again, this looks a bit funky to me.
Is this working around some missing [Protocols] dependency?

Regards,

Leif

> +
> +[PcdsFixedAtBuild.common]
> +  # DwEmmc Driver PCDs
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002
> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> new file mode 100644
> index 0000000..f23494c
> --- /dev/null
> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> @@ -0,0 +1,55 @@
> +#/** @file
> +#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
> +#
> +#  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.
> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005
> +  BASE_NAME                      = DwEmmcDxe
> +  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +
> +  ENTRY_POINT                    = DwEmmcDxeInitialize
> +
> +[Sources.common]
> +  DwEmmcDxe.c
> +
> +[Packages]
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdePkg/MdePkg.dec
> +  OpenPlatformPkg/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> +
> +[LibraryClasses]
> +  ArmLib
> +  BaseLib
> +  BaseMemoryLib
> +  CacheMaintenanceLib
> +  IoLib
> +  MemoryAllocationLib
> +  TimerLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Protocols]
> +  gEfiCpuArchProtocolGuid
> +  gEfiDevicePathProtocolGuid
> +  gEfiMmcHostProtocolGuid
> +
> +[Pcd]
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
> +
> +[Depex]
> +  TRUE
> -- 
> 2.7.4
>
Ard Biesheuvel Nov. 29, 2016, 8:06 p.m. UTC | #7
On 29 November 2016 at 20:01, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> On Wed, Nov 23, 2016 at 03:12:28PM +0800, Haojian Zhuang wrote:
>> Support designware emmc controller.
>>
>> Contributed-under: TianoCore Contribution Agreement 1.0
>> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
>> ---
>>  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
>>  4 files changed, 876 insertions(+)
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>>
>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmc.h b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>> new file mode 100644
>> index 0000000..999b584
>> --- /dev/null
>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>> @@ -0,0 +1,128 @@
>> +/** @file
>> +*
>> +*  Copyright (c) 2014, Linaro Limited. All rights reserved.
>> +*  Copyright (c) 2014, 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.
>> +*
>> +**/
>> +
>> +
>> +#ifndef __DWEMMC_H__
>> +#define __DWEMMC_H__
>> +
>> +#include <Protocol/EmbeddedGpio.h>
>> +
>> +// DW MMC Registers
>> +#define DWEMMC_CTRL          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
>
> So, we already have tons of drivers that use a Pcd for a base address
> so that only one of them can exist in a system. This is not cause
> for rejecting a driver.
>

The existing HiKey port has two versions of this driver, one for the
eMMC and one for the SD, where both have deviated from the common
ancestor to address various issues that existed either in the code or
in the hardware.

So I agree we should not reject this contribution, but at the same
time we should not accept the forked SD version.

> But I would be very, very happy if you had time to look into Ard's
> recent non-discoverable patchset, and perhaps the way the guys at
> SemiHalf have used it for their Armada 70x0 port.
>
> This driver would be a lot nicer if written to that interface.
>
>> +#define DWEMMC_PWREN         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
>> +#define DWEMMC_CLKDIV                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
>> +#define DWEMMC_CLKSRC                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
>> +#define DWEMMC_CLKENA                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
>
> Tabs again. Please untabify.
>
>> +#define DWEMMC_TMOUT         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
>> +#define DWEMMC_CTYPE         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
>> +#define DWEMMC_BLKSIZ                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
>> +#define DWEMMC_BYTCNT                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
>> +#define DWEMMC_INTMASK               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
>> +#define DWEMMC_CMDARG                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
>> +#define DWEMMC_CMD           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
>> +#define DWEMMC_RESP0         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
>> +#define DWEMMC_RESP1         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
>> +#define DWEMMC_RESP2         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
>> +#define DWEMMC_RESP3         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
>> +#define DWEMMC_RINTSTS               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
>> +#define DWEMMC_STATUS                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
>> +#define DWEMMC_FIFOTH                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
>> +#define DWEMMC_DEBNCE                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
>> +#define DWEMMC_UHSREG                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
>> +#define DWEMMC_BMOD          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
>> +#define DWEMMC_DBADDR                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
>> +#define DWEMMC_IDSTS         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
>> +#define DWEMMC_IDINTEN               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
>> +#define DWEMMC_DSCADDR               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
>> +#define DWEMMC_BUFADDR               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
>> +#define DWEMMC_CARDTHRCTL    ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
>> +
>> +#define CMD_UPDATE_CLK                               0x80202000
>> +#define CMD_START_BIT                                (1 << 31)
>> +
>> +#define MMC_8BIT_MODE                                (1 << 16)
>> +
>> +#define BIT_CMD_RESPONSE_EXPECT                      (1 << 6)
>> +#define BIT_CMD_LONG_RESPONSE                        (1 << 7)
>> +#define BIT_CMD_CHECK_RESPONSE_CRC           (1 << 8)
>> +#define BIT_CMD_DATA_EXPECTED                        (1 << 9)
>> +#define BIT_CMD_READ                         (0 << 10)
>> +#define BIT_CMD_WRITE                                (1 << 10)
>> +#define BIT_CMD_BLOCK_TRANSFER                       (0 << 11)
>> +#define BIT_CMD_STREAM_TRANSFER                      (1 << 11)
>> +#define BIT_CMD_SEND_AUTO_STOP                       (1 << 12)
>> +#define BIT_CMD_WAIT_PRVDATA_COMPLETE                (1 << 13)
>> +#define BIT_CMD_STOP_ABORT_CMD                       (1 << 14)
>> +#define BIT_CMD_SEND_INIT                    (1 << 15)
>> +#define BIT_CMD_UPDATE_CLOCK_ONLY            (1 << 21)
>> +#define BIT_CMD_READ_CEATA_DEVICE            (1 << 22)
>> +#define BIT_CMD_CCS_EXPECTED                 (1 << 23)
>> +#define BIT_CMD_ENABLE_BOOT                  (1 << 24)
>> +#define BIT_CMD_EXPECT_BOOT_ACK                      (1 << 25)
>> +#define BIT_CMD_DISABLE_BOOT                 (1 << 26)
>> +#define BIT_CMD_MANDATORY_BOOT                       (0 << 27)
>> +#define BIT_CMD_ALTERNATE_BOOT                       (1 << 27)
>> +#define BIT_CMD_VOLT_SWITCH                  (1 << 28)
>> +#define BIT_CMD_USE_HOLD_REG                 (1 << 29)
>> +#define BIT_CMD_START                                (1 << 31)
>> +
>> +#define DWEMMC_INT_EBE                               (1 << 15)       /* End-bit Err */
>> +#define DWEMMC_INT_SBE                               (1 << 13)       /* Start-bit  Err */
>> +#define DWEMMC_INT_HLE                               (1 << 12)       /* Hardware-lock Err */
>> +#define DWEMMC_INT_FRUN                              (1 << 11)       /* FIFO UN/OV RUN */
>> +#define DWEMMC_INT_DRT                               (1 << 9)        /* Data timeout */
>> +#define DWEMMC_INT_RTO                               (1 << 8)        /* Response timeout */
>> +#define DWEMMC_INT_DCRC                              (1 << 7)        /* Data CRC err */
>> +#define DWEMMC_INT_RCRC                              (1 << 6)        /* Response CRC err */
>> +#define DWEMMC_INT_RXDR                              (1 << 5)
>> +#define DWEMMC_INT_TXDR                              (1 << 4)
>> +#define DWEMMC_INT_DTO                               (1 << 3)        /* Data trans over */
>> +#define DWEMMC_INT_CMD_DONE                  (1 << 2)
>> +#define DWEMMC_INT_RE                                (1 << 1)
>> +
>> +#define DWEMMC_IDMAC_DES0_DIC                        (1 << 1)
>> +#define DWEMMC_IDMAC_DES0_LD                 (1 << 2)
>> +#define DWEMMC_IDMAC_DES0_FS                 (1 << 3)
>> +#define DWEMMC_IDMAC_DES0_CH                 (1 << 4)
>> +#define DWEMMC_IDMAC_DES0_ER                 (1 << 5)
>> +#define DWEMMC_IDMAC_DES0_CES                        (1 << 30)
>> +#define DWEMMC_IDMAC_DES0_OWN                        (1 << 31)
>> +#define DWEMMC_IDMAC_DES1_BS1(x)             ((x) & 0x1fff)
>> +#define DWEMMC_IDMAC_DES2_BS2(x)             (((x) & 0x1fff) << 13)
>> +#define DWEMMC_IDMAC_SWRESET                 (1 << 0)
>> +#define DWEMMC_IDMAC_FB                              (1 << 1)
>> +#define DWEMMC_IDMAC_ENABLE                  (1 << 7)
>> +
>> +#define EMMC_FIX_RCA                         6
>> +
>> +/* bits in MMC0_CTRL */
>> +#define DWEMMC_CTRL_RESET                    (1 << 0)
>> +#define DWEMMC_CTRL_FIFO_RESET                       (1 << 1)
>> +#define DWEMMC_CTRL_DMA_RESET                        (1 << 2)
>> +#define DWEMMC_CTRL_INT_EN                   (1 << 4)
>> +#define DWEMMC_CTRL_DMA_EN                   (1 << 5)
>> +#define DWEMMC_CTRL_IDMAC_EN                 (1 << 25)
>> +#define DWEMMC_CTRL_RESET_ALL                        (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
>> +
>> +#define DWEMMC_STS_DATA_BUSY                 (1 << 9)
>> +
>> +#define DWEMMC_FIFO_TWMARK(x)                        (x & 0xfff)
>> +#define DWEMMC_FIFO_RWMARK(x)                        ((x & 0x1ff) << 16)
>> +#define DWEMMC_DMA_BURST_SIZE(x)             ((x & 0x7) << 28)
>> +
>> +#define DWEMMC_CARD_RD_THR(x)                        ((x & 0xfff) << 16)
>> +#define DWEMMC_CARD_RD_THR_EN                        (1 << 0)
>> +
>> +#endif  // __DWEMMC_H__
>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>> new file mode 100644
>> index 0000000..cf75e1c
>> --- /dev/null
>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>> @@ -0,0 +1,651 @@
>> +/** @file
>> +  This file implement the MMC Host Protocol for the DesignWare eMMC.
>> +
>> +  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>
>
> Could you insert these two, alphabetically sorted, in the already
> sorted block above?
>
>> +
>> +#include "DwEmmc.h"
>> +
>> +#define DWEMMC_DESC_PAGE             1
>> +#define DWEMMC_BLOCK_SIZE            512
>> +#define DWEMMC_DMA_BUF_SIZE          (512 * 8)
>> +#define DWEMMC_MAX_DESC_PAGES        512
>> +
>> +typedef struct {
>> +  UINT32             Des0;
>> +  UINT32             Des1;
>> +  UINT32             Des2;
>> +  UINT32             Des3;
>> +} DWEMMC_IDMAC_DESCRIPTOR;
>> +
>> +EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
>> +DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
>> +EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
>> +STATIC UINT32 mDwEmmcCommand;
>> +STATIC UINT32 mDwEmmcArgument;
>> +
>> +EFI_STATUS
>> +DwEmmcReadBlockData (
>> +  IN EFI_MMC_HOST_PROTOCOL     *This,
>> +  IN EFI_LBA                    Lba,
>> +  IN UINTN                      Length,
>> +  IN UINT32*                    Buffer
>> +  );
>> +
>> +BOOLEAN
>> +DwEmmcIsPowerOn (
>> +  VOID
>> +  )
>> +{
>> +    return TRUE;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcInitialize (
>> +  VOID
>> +  )
>> +{
>> +    DEBUG ((EFI_D_BLKIO, "DwEmmcInitialize()"));
>
> BaseTools/Scripts/PatchCheck.py will warn you that EFI_D_BLKIO is no
> longer the preferred macro to use, and that DEBUG_BLKIO is better.
> It would be good if you could run the set through PatchCheck for v2.
>
>> +    return EFI_SUCCESS;
>> +}
>> +
>> +BOOLEAN
>> +DwEmmcIsCardPresent (
>> +  IN EFI_MMC_HOST_PROTOCOL     *This
>> +  )
>> +{
>> +  return TRUE;
>> +}
>> +
>> +BOOLEAN
>> +DwEmmcIsReadOnly (
>> +  IN EFI_MMC_HOST_PROTOCOL     *This
>> +  )
>> +{
>> +  return FALSE;
>> +}
>> +
>> +BOOLEAN
>> +DwEmmcIsDmaSupported (
>> +  IN EFI_MMC_HOST_PROTOCOL     *This
>> +  )
>> +{
>> +  return TRUE;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcBuildDevicePath (
>> +  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, &mDwEmmcDevicePathGuid);
>> +
>> +  *DevicePath = NewDevicePathNode;
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcUpdateClock (
>> +  VOID
>> +  )
>> +{
>> +  UINT32 Data;
>> +
>> +  /* CMD_UPDATE_CLK */
>> +  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
>> +      BIT_CMD_START;
>> +  MmioWrite32 (DWEMMC_CMD, Data);
>> +  while (1) {
>> +    Data = MmioRead32 (DWEMMC_CMD);
>> +    if (!(Data & CMD_START_BIT)) {
>> +      break;
>> +    }
>> +    Data = MmioRead32 (DWEMMC_RINTSTS);
>> +    if (Data & DWEMMC_INT_HLE) {
>> +      Print (L"failed to update mmc clock frequency\n");
>> +      return EFI_DEVICE_ERROR;
>> +    }
>> +  }
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcSetClock (
>> +  IN UINTN                     ClockFreq
>> +  )
>> +{
>> +  UINT32 Divider, Rate, Data;
>> +  EFI_STATUS Status;
>> +  BOOLEAN Found = FALSE;
>> +
>> +  for (Divider = 1; Divider < 256; Divider++) {
>> +    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
>> +    if ((Rate / (2 * Divider)) <= ClockFreq) {
>> +      Found = TRUE;
>> +      break;
>> +    }
>> +  }
>> +  if (Found == FALSE) {
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  // Wait until MMC is idle
>> +  do {
>> +    Data = MmioRead32 (DWEMMC_STATUS);
>> +  } while (Data & DWEMMC_STS_DATA_BUSY);
>> +
>> +  // Disable MMC clock first
>> +  MmioWrite32 (DWEMMC_CLKENA, 0);
>> +  Status = DwEmmcUpdateClock ();
>> +  ASSERT (!EFI_ERROR (Status));
>> +
>> +  MmioWrite32 (DWEMMC_CLKDIV, Divider);
>> +  Status = DwEmmcUpdateClock ();
>> +  ASSERT (!EFI_ERROR (Status));
>> +
>> +  // Enable MMC clock
>> +  MmioWrite32 (DWEMMC_CLKENA, 1);
>> +  MmioWrite32 (DWEMMC_CLKSRC, 0);
>> +  Status = DwEmmcUpdateClock ();
>> +  ASSERT (!EFI_ERROR (Status));
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcNotifyState (
>> +  IN EFI_MMC_HOST_PROTOCOL     *This,
>> +  IN MMC_STATE                 State
>> +  )
>> +{
>> +  UINT32      Data;
>> +  EFI_STATUS  Status;
>> +
>> +  switch (State) {
>> +  case MmcInvalidState:
>> +    ASSERT (0);
>
> Should this not return something other than EFI_SUCCESS for RELEASE
> builds?
>
>> +    break;
>> +  case MmcHwInitializationState:
>> +    MmioWrite32 (DWEMMC_PWREN, 1);
>> +
>> +    // If device already turn on then restart it
>> +    Data = DWEMMC_CTRL_RESET_ALL;
>> +    MmioWrite32 (DWEMMC_CTRL, Data);
>> +    do {
>> +      // Wait until reset operation finished
>> +      Data = MmioRead32 (DWEMMC_CTRL);
>> +    } while (Data & DWEMMC_CTRL_RESET_ALL);
>> +
>> +    // Setup clock that could not be higher than 400KHz.
>> +    Status = DwEmmcSetClock (400000);
>> +    ASSERT (!EFI_ERROR (Status));
>> +    MicroSecondDelay (100);
>
> Why the delay?
> Does this also need a barrier?
>
>> +
>> +    MmioWrite32 (DWEMMC_RINTSTS, ~0);
>> +    MmioWrite32 (DWEMMC_INTMASK, 0);
>> +    MmioWrite32 (DWEMMC_TMOUT, ~0);
>> +    MmioWrite32 (DWEMMC_IDINTEN, 0);
>> +    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
>> +
>> +    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
>> +    do {
>> +      Data = MmioRead32 (DWEMMC_BMOD);
>> +    } while (Data & DWEMMC_IDMAC_SWRESET);
>> +    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;
>> +}
>> +
>> +// Need to prepare DMA buffer first before sending commands to MMC card
>> +BOOLEAN
>> +IsPendingReadCommand (
>> +  IN MMC_CMD                    MmcCmd
>> +  )
>> +{
>> +  UINTN  Mask;
>> +
>> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
>> +  if ((MmcCmd & Mask) == Mask) {
>> +    return TRUE;
>> +  }
>> +  return FALSE;
>> +}
>> +
>> +BOOLEAN
>> +IsPendingWriteCommand (
>> +  IN MMC_CMD                    MmcCmd
>> +  )
>> +{
>> +  UINTN  Mask;
>> +
>> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
>> +  if ((MmcCmd & Mask) == Mask) {
>> +    return TRUE;
>> +  }
>> +  return FALSE;
>> +}
>> +
>> +EFI_STATUS
>> +SendCommand (
>> +  IN MMC_CMD                    MmcCmd,
>> +  IN UINT32                     Argument
>> +  )
>> +{
>> +  UINT32      Data, ErrMask;
>> +
>> +  // Wait until MMC is idle
>> +  do {
>> +    Data = MmioRead32 (DWEMMC_STATUS);
>> +  } while (Data & DWEMMC_STS_DATA_BUSY);
>> +
>> +  MmioWrite32 (DWEMMC_RINTSTS, ~0);
>> +  MmioWrite32 (DWEMMC_CMDARG, Argument);
>> +  MmioWrite32 (DWEMMC_CMD, MmcCmd);
>> +
>> +  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
>> +            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
>> +  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
>> +  do {
>> +    MicroSecondDelay(500);
>> +    Data = MmioRead32 (DWEMMC_RINTSTS);
>> +
>> +    if (Data & ErrMask) {
>> +      return EFI_DEVICE_ERROR;
>> +    }
>> +    if (Data & DWEMMC_INT_DTO) {     // Transfer Done
>> +      break;
>> +    }
>> +  } while (!(Data & DWEMMC_INT_CMD_DONE));
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcSendCommand (
>> +  IN EFI_MMC_HOST_PROTOCOL     *This,
>> +  IN MMC_CMD                    MmcCmd,
>> +  IN UINT32                     Argument
>> +  )
>> +{
>> +  UINT32       Cmd = 0;
>> +  EFI_STATUS   Status = EFI_SUCCESS;
>> +
>> +  switch (MMC_GET_INDX(MmcCmd)) {
>> +  case MMC_INDX(0):
>> +    Cmd = BIT_CMD_SEND_INIT;
>> +    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_SEND_INIT;
>> +    break;
>> +  case MMC_INDX(3):
>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>> +           BIT_CMD_SEND_INIT;
>> +    break;
>> +  case MMC_INDX(7):
>> +    if (Argument)
>> +        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
>> +    else
>> +        Cmd = 0;
>> +    break;
>> +  case MMC_INDX(8):
>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>> +           BIT_CMD_DATA_EXPECTED | 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;
>> +    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 |
>> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
>> +    break;
>> +  case MMC_INDX(16):
>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
>> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
>> +    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;
>> +    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;
>> +    break;
>> +  case MMC_INDX(30):
>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>> +           BIT_CMD_DATA_EXPECTED;
>> +    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 (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
>> +    mDwEmmcCommand = Cmd;
>> +    mDwEmmcArgument = Argument;
>> +  } else {
>> +    Status = SendCommand (Cmd, Argument);
>> +  }
>> +  return Status;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcReceiveResponse (
>> +  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 (DWEMMC_RESP0);
>> +  } else if (Type == MMC_RESPONSE_TYPE_R2) {
>> +    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
>> +    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
>> +    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
>> +    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
>> +  }
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +PrepareDmaData (
>> +  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
>> +  IN UINTN                      Length,
>> +  IN UINT32*                    Buffer
>> +  )
>> +{
>> +  UINTN  Cnt, Blks, Idx, LastIdx;
>> +
>> +  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
>> +  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
>> +  Length = DWEMMC_BLOCK_SIZE * Blks;
>> +
>> +  for (Idx = 0; Idx < Cnt; Idx++) {
>> +    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
>> +                              DWEMMC_IDMAC_DES0_DIC;
>> +    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
>> +    /* Buffer Address */
>> +    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
>> +    /* Next Descriptor Address */
>> +    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
>> +                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
>> +  }
>> +  /* First Descriptor */
>> +  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
>> +  /* Last Descriptor */
>> +  LastIdx = Cnt - 1;
>> +  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
>> +  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
>> +  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
>> +                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
>> +  /* Set the Next field of Last Descriptor */
>> +  (IdmacDesc + LastIdx)->Des3 = 0;
>> +  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +VOID
>> +StartDma (
>> +  UINTN    Length
>> +  )
>> +{
>> +  UINT32 Data;
>> +
>> +  Data = MmioRead32 (DWEMMC_CTRL);
>> +  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
>> +  MmioWrite32 (DWEMMC_CTRL, Data);
>> +  Data = MmioRead32 (DWEMMC_BMOD);
>> +  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
>> +  MmioWrite32 (DWEMMC_BMOD, Data);
>> +
>> +  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
>> +  MmioWrite32 (DWEMMC_BYTCNT, Length);
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcReadBlockData (
>> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
>> +  DescPages = (Count + CountPerPage - 1) / CountPerPage;
>> +
>> +  InvalidateDataCacheRange (Buffer, Length);
>> +
>> +  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
>> +  if (EFI_ERROR (Status)) {
>> +    goto out;
>> +  }
>> +
>> +  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
>> +  StartDma (Length);
>> +
>> +  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
>> +  if (EFI_ERROR (Status)) {
>> +    DEBUG ((EFI_D_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
>> +    goto out;
>> +  }
>> +out:
>> +  // Restore Tpl
>> +  gBS->RestoreTPL (Tpl);
>> +  return Status;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcWriteBlockData (
>> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_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 (mDwEmmcCommand, mDwEmmcArgument);
>> +  if (EFI_ERROR (Status)) {
>> +    DEBUG ((EFI_D_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
>> +    goto out;
>> +  }
>> +out:
>> +  // Restore Tpl
>> +  gBS->RestoreTPL (Tpl);
>> +  return Status;
>> +}
>> +
>> +EFI_STATUS
>> +DwEmmcSetIos (
>> +  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 (DWEMMC_UHSREG);
>> +    switch (TimingMode) {
>> +    case EMMCHS52DDR1V2:
>> +    case EMMCHS52DDR1V8:
>> +      Data |= 1 << 16;
>> +      break;
>> +    case EMMCHS52:
>> +    case EMMCHS26:
>> +      Data &= ~(1 << 16);
>> +      break;
>> +    default:
>> +      return EFI_UNSUPPORTED;
>> +    }
>> +    MmioWrite32 (DWEMMC_UHSREG, Data);
>> +  }
>> +
>> +  switch (BusWidth) {
>> +  case 1:
>> +    MmioWrite32 (DWEMMC_CTYPE, 0);
>> +    break;
>> +  case 4:
>> +    MmioWrite32 (DWEMMC_CTYPE, 1);
>> +    break;
>> +  case 8:
>> +    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
>> +    break;
>> +  default:
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +  if (BusClockFreq) {
>> +    Status = DwEmmcSetClock (BusClockFreq);
>> +  }
>> +  return Status;
>> +}
>> +
>> +BOOLEAN
>> +DwEmmcIsMultiBlock (
>> +  IN EFI_MMC_HOST_PROTOCOL      *This
>> +  )
>> +{
>> +  return TRUE;
>> +}
>> +
>> +EFI_MMC_HOST_PROTOCOL gMciHost = {
>> +  MMC_HOST_PROTOCOL_REVISION,
>> +  DwEmmcIsCardPresent,
>> +  DwEmmcIsReadOnly,
>> +  DwEmmcBuildDevicePath,
>> +  DwEmmcNotifyState,
>> +  DwEmmcSendCommand,
>> +  DwEmmcReceiveResponse,
>> +  DwEmmcReadBlockData,
>> +  DwEmmcWriteBlockData,
>> +  DwEmmcSetIos,
>> +  DwEmmcIsMultiBlock
>> +};
>> +
>> +EFI_STATUS
>> +DwEmmcDxeInitialize (
>> +  IN EFI_HANDLE         ImageHandle,
>> +  IN EFI_SYSTEM_TABLE   *SystemTable
>> +  )
>> +{
>> +  EFI_STATUS    Status;
>> +  EFI_HANDLE    Handle;
>> +
>> +  Handle = NULL;
>> +
>> +  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
>> +  if (gpIdmacDesc == NULL) {
>> +    return EFI_BUFFER_TOO_SMALL;
>> +  }
>> +
>> +  DEBUG ((EFI_D_BLKIO, "DwEmmcDxeInitialize()\n"));
>> +
>> +  //Publish Component Name, BlockIO protocol interfaces
>> +  Status = gBS->InstallMultipleProtocolInterfaces (
>> +                  &Handle,
>> +                  &gEfiMmcHostProtocolGuid,         &gMciHost,
>> +                  NULL
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>> new file mode 100644
>> index 0000000..b281b93
>> --- /dev/null
>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>> @@ -0,0 +1,42 @@
>> +#/** @file
>> +# Framework Module Development Environment Industry Standards
>> +#
>> +# This Package provides headers and libraries that conform to EFI/PI Industry standards.
>> +# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
>> +# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR>
>> +# Copyright (c) 2015-2016, Linaro. 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.
>> +#
>> +#**/
>> +
>> +[Defines]
>> +  DEC_SPECIFICATION              = 0x00010005
>> +  PACKAGE_NAME                   = OpenPlatformDriversMmcDwEmmcDxePkg
>
> Can probably drop "OpenPlatformDrivers".
>
>> +  PACKAGE_GUID                   = 3869905e-c96c-4d20-9bfb-3b9d71bb900c
>> +  PACKAGE_VERSION                = 0.1
>> +
>> +
>> +################################################################################
>> +#
>> +# Include Section - list of Include Paths that are provided by this package.
>> +#                   Comments are used for Keywords and Module Types.
>> +#
>> +# Supported Module Types:
>> +#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
>> +#
>> +################################################################################
>> +
>> +[Guids.common]
>> +  gDwEmmcDxeTokenSpaceGuid   = { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}
>
> Again, this looks a bit funky to me.
> Is this working around some missing [Protocols] dependency?
>
> Regards,
>
> Leif
>
>> +
>> +[PcdsFixedAtBuild.common]
>> +  # DwEmmc Driver PCDs
>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002
>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>> new file mode 100644
>> index 0000000..f23494c
>> --- /dev/null
>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>> @@ -0,0 +1,55 @@
>> +#/** @file
>> +#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
>> +#
>> +#  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.
>> +#
>> +#**/
>> +
>> +[Defines]
>> +  INF_VERSION                    = 0x00010005
>> +  BASE_NAME                      = DwEmmcDxe
>> +  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
>> +  MODULE_TYPE                    = DXE_DRIVER
>> +  VERSION_STRING                 = 1.0
>> +
>> +  ENTRY_POINT                    = DwEmmcDxeInitialize
>> +
>> +[Sources.common]
>> +  DwEmmcDxe.c
>> +
>> +[Packages]
>> +  EmbeddedPkg/EmbeddedPkg.dec
>> +  MdePkg/MdePkg.dec
>> +  OpenPlatformPkg/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>> +
>> +[LibraryClasses]
>> +  ArmLib
>> +  BaseLib
>> +  BaseMemoryLib
>> +  CacheMaintenanceLib
>> +  IoLib
>> +  MemoryAllocationLib
>> +  TimerLib
>> +  UefiDriverEntryPoint
>> +  UefiLib
>> +
>> +[Protocols]
>> +  gEfiCpuArchProtocolGuid
>> +  gEfiDevicePathProtocolGuid
>> +  gEfiMmcHostProtocolGuid
>> +
>> +[Pcd]
>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
>> +
>> +[Depex]
>> +  TRUE
>> --
>> 2.7.4
>>
Marcin Wojtas Nov. 30, 2016, 8:24 a.m. UTC | #8
Hi,

A tiny remark - how about moving this driver to SdMmc?

Best regards,
Marcin

2016-11-29 21:06 GMT+01:00 Ard Biesheuvel <ard.biesheuvel@linaro.org>:
> On 29 November 2016 at 20:01, Leif Lindholm <leif.lindholm@linaro.org> wrote:
>> On Wed, Nov 23, 2016 at 03:12:28PM +0800, Haojian Zhuang wrote:
>>> Support designware emmc controller.
>>>
>>> Contributed-under: TianoCore Contribution Agreement 1.0
>>> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
>>> ---
>>>  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
>>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
>>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
>>>  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
>>>  4 files changed, 876 insertions(+)
>>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>>>  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>>>
>>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmc.h b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>>> new file mode 100644
>>> index 0000000..999b584
>>> --- /dev/null
>>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
>>> @@ -0,0 +1,128 @@
>>> +/** @file
>>> +*
>>> +*  Copyright (c) 2014, Linaro Limited. All rights reserved.
>>> +*  Copyright (c) 2014, 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.
>>> +*
>>> +**/
>>> +
>>> +
>>> +#ifndef __DWEMMC_H__
>>> +#define __DWEMMC_H__
>>> +
>>> +#include <Protocol/EmbeddedGpio.h>
>>> +
>>> +// DW MMC Registers
>>> +#define DWEMMC_CTRL          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
>>
>> So, we already have tons of drivers that use a Pcd for a base address
>> so that only one of them can exist in a system. This is not cause
>> for rejecting a driver.
>>
>
> The existing HiKey port has two versions of this driver, one for the
> eMMC and one for the SD, where both have deviated from the common
> ancestor to address various issues that existed either in the code or
> in the hardware.
>
> So I agree we should not reject this contribution, but at the same
> time we should not accept the forked SD version.
>
>> But I would be very, very happy if you had time to look into Ard's
>> recent non-discoverable patchset, and perhaps the way the guys at
>> SemiHalf have used it for their Armada 70x0 port.
>>
>> This driver would be a lot nicer if written to that interface.
>>
>>> +#define DWEMMC_PWREN         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
>>> +#define DWEMMC_CLKDIV                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
>>> +#define DWEMMC_CLKSRC                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
>>> +#define DWEMMC_CLKENA                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
>>
>> Tabs again. Please untabify.
>>
>>> +#define DWEMMC_TMOUT         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
>>> +#define DWEMMC_CTYPE         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
>>> +#define DWEMMC_BLKSIZ                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
>>> +#define DWEMMC_BYTCNT                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
>>> +#define DWEMMC_INTMASK               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
>>> +#define DWEMMC_CMDARG                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
>>> +#define DWEMMC_CMD           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
>>> +#define DWEMMC_RESP0         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
>>> +#define DWEMMC_RESP1         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
>>> +#define DWEMMC_RESP2         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
>>> +#define DWEMMC_RESP3         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
>>> +#define DWEMMC_RINTSTS               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
>>> +#define DWEMMC_STATUS                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
>>> +#define DWEMMC_FIFOTH                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
>>> +#define DWEMMC_DEBNCE                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
>>> +#define DWEMMC_UHSREG                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
>>> +#define DWEMMC_BMOD          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
>>> +#define DWEMMC_DBADDR                ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
>>> +#define DWEMMC_IDSTS         ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
>>> +#define DWEMMC_IDINTEN               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
>>> +#define DWEMMC_DSCADDR               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
>>> +#define DWEMMC_BUFADDR               ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
>>> +#define DWEMMC_CARDTHRCTL    ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
>>> +
>>> +#define CMD_UPDATE_CLK                               0x80202000
>>> +#define CMD_START_BIT                                (1 << 31)
>>> +
>>> +#define MMC_8BIT_MODE                                (1 << 16)
>>> +
>>> +#define BIT_CMD_RESPONSE_EXPECT                      (1 << 6)
>>> +#define BIT_CMD_LONG_RESPONSE                        (1 << 7)
>>> +#define BIT_CMD_CHECK_RESPONSE_CRC           (1 << 8)
>>> +#define BIT_CMD_DATA_EXPECTED                        (1 << 9)
>>> +#define BIT_CMD_READ                         (0 << 10)
>>> +#define BIT_CMD_WRITE                                (1 << 10)
>>> +#define BIT_CMD_BLOCK_TRANSFER                       (0 << 11)
>>> +#define BIT_CMD_STREAM_TRANSFER                      (1 << 11)
>>> +#define BIT_CMD_SEND_AUTO_STOP                       (1 << 12)
>>> +#define BIT_CMD_WAIT_PRVDATA_COMPLETE                (1 << 13)
>>> +#define BIT_CMD_STOP_ABORT_CMD                       (1 << 14)
>>> +#define BIT_CMD_SEND_INIT                    (1 << 15)
>>> +#define BIT_CMD_UPDATE_CLOCK_ONLY            (1 << 21)
>>> +#define BIT_CMD_READ_CEATA_DEVICE            (1 << 22)
>>> +#define BIT_CMD_CCS_EXPECTED                 (1 << 23)
>>> +#define BIT_CMD_ENABLE_BOOT                  (1 << 24)
>>> +#define BIT_CMD_EXPECT_BOOT_ACK                      (1 << 25)
>>> +#define BIT_CMD_DISABLE_BOOT                 (1 << 26)
>>> +#define BIT_CMD_MANDATORY_BOOT                       (0 << 27)
>>> +#define BIT_CMD_ALTERNATE_BOOT                       (1 << 27)
>>> +#define BIT_CMD_VOLT_SWITCH                  (1 << 28)
>>> +#define BIT_CMD_USE_HOLD_REG                 (1 << 29)
>>> +#define BIT_CMD_START                                (1 << 31)
>>> +
>>> +#define DWEMMC_INT_EBE                               (1 << 15)       /* End-bit Err */
>>> +#define DWEMMC_INT_SBE                               (1 << 13)       /* Start-bit  Err */
>>> +#define DWEMMC_INT_HLE                               (1 << 12)       /* Hardware-lock Err */
>>> +#define DWEMMC_INT_FRUN                              (1 << 11)       /* FIFO UN/OV RUN */
>>> +#define DWEMMC_INT_DRT                               (1 << 9)        /* Data timeout */
>>> +#define DWEMMC_INT_RTO                               (1 << 8)        /* Response timeout */
>>> +#define DWEMMC_INT_DCRC                              (1 << 7)        /* Data CRC err */
>>> +#define DWEMMC_INT_RCRC                              (1 << 6)        /* Response CRC err */
>>> +#define DWEMMC_INT_RXDR                              (1 << 5)
>>> +#define DWEMMC_INT_TXDR                              (1 << 4)
>>> +#define DWEMMC_INT_DTO                               (1 << 3)        /* Data trans over */
>>> +#define DWEMMC_INT_CMD_DONE                  (1 << 2)
>>> +#define DWEMMC_INT_RE                                (1 << 1)
>>> +
>>> +#define DWEMMC_IDMAC_DES0_DIC                        (1 << 1)
>>> +#define DWEMMC_IDMAC_DES0_LD                 (1 << 2)
>>> +#define DWEMMC_IDMAC_DES0_FS                 (1 << 3)
>>> +#define DWEMMC_IDMAC_DES0_CH                 (1 << 4)
>>> +#define DWEMMC_IDMAC_DES0_ER                 (1 << 5)
>>> +#define DWEMMC_IDMAC_DES0_CES                        (1 << 30)
>>> +#define DWEMMC_IDMAC_DES0_OWN                        (1 << 31)
>>> +#define DWEMMC_IDMAC_DES1_BS1(x)             ((x) & 0x1fff)
>>> +#define DWEMMC_IDMAC_DES2_BS2(x)             (((x) & 0x1fff) << 13)
>>> +#define DWEMMC_IDMAC_SWRESET                 (1 << 0)
>>> +#define DWEMMC_IDMAC_FB                              (1 << 1)
>>> +#define DWEMMC_IDMAC_ENABLE                  (1 << 7)
>>> +
>>> +#define EMMC_FIX_RCA                         6
>>> +
>>> +/* bits in MMC0_CTRL */
>>> +#define DWEMMC_CTRL_RESET                    (1 << 0)
>>> +#define DWEMMC_CTRL_FIFO_RESET                       (1 << 1)
>>> +#define DWEMMC_CTRL_DMA_RESET                        (1 << 2)
>>> +#define DWEMMC_CTRL_INT_EN                   (1 << 4)
>>> +#define DWEMMC_CTRL_DMA_EN                   (1 << 5)
>>> +#define DWEMMC_CTRL_IDMAC_EN                 (1 << 25)
>>> +#define DWEMMC_CTRL_RESET_ALL                        (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
>>> +
>>> +#define DWEMMC_STS_DATA_BUSY                 (1 << 9)
>>> +
>>> +#define DWEMMC_FIFO_TWMARK(x)                        (x & 0xfff)
>>> +#define DWEMMC_FIFO_RWMARK(x)                        ((x & 0x1ff) << 16)
>>> +#define DWEMMC_DMA_BURST_SIZE(x)             ((x & 0x7) << 28)
>>> +
>>> +#define DWEMMC_CARD_RD_THR(x)                        ((x & 0xfff) << 16)
>>> +#define DWEMMC_CARD_RD_THR_EN                        (1 << 0)
>>> +
>>> +#endif  // __DWEMMC_H__
>>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>>> new file mode 100644
>>> index 0000000..cf75e1c
>>> --- /dev/null
>>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
>>> @@ -0,0 +1,651 @@
>>> +/** @file
>>> +  This file implement the MMC Host Protocol for the DesignWare eMMC.
>>> +
>>> +  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>
>>
>> Could you insert these two, alphabetically sorted, in the already
>> sorted block above?
>>
>>> +
>>> +#include "DwEmmc.h"
>>> +
>>> +#define DWEMMC_DESC_PAGE             1
>>> +#define DWEMMC_BLOCK_SIZE            512
>>> +#define DWEMMC_DMA_BUF_SIZE          (512 * 8)
>>> +#define DWEMMC_MAX_DESC_PAGES        512
>>> +
>>> +typedef struct {
>>> +  UINT32             Des0;
>>> +  UINT32             Des1;
>>> +  UINT32             Des2;
>>> +  UINT32             Des3;
>>> +} DWEMMC_IDMAC_DESCRIPTOR;
>>> +
>>> +EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
>>> +DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
>>> +EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
>>> +STATIC UINT32 mDwEmmcCommand;
>>> +STATIC UINT32 mDwEmmcArgument;
>>> +
>>> +EFI_STATUS
>>> +DwEmmcReadBlockData (
>>> +  IN EFI_MMC_HOST_PROTOCOL     *This,
>>> +  IN EFI_LBA                    Lba,
>>> +  IN UINTN                      Length,
>>> +  IN UINT32*                    Buffer
>>> +  );
>>> +
>>> +BOOLEAN
>>> +DwEmmcIsPowerOn (
>>> +  VOID
>>> +  )
>>> +{
>>> +    return TRUE;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcInitialize (
>>> +  VOID
>>> +  )
>>> +{
>>> +    DEBUG ((EFI_D_BLKIO, "DwEmmcInitialize()"));
>>
>> BaseTools/Scripts/PatchCheck.py will warn you that EFI_D_BLKIO is no
>> longer the preferred macro to use, and that DEBUG_BLKIO is better.
>> It would be good if you could run the set through PatchCheck for v2.
>>
>>> +    return EFI_SUCCESS;
>>> +}
>>> +
>>> +BOOLEAN
>>> +DwEmmcIsCardPresent (
>>> +  IN EFI_MMC_HOST_PROTOCOL     *This
>>> +  )
>>> +{
>>> +  return TRUE;
>>> +}
>>> +
>>> +BOOLEAN
>>> +DwEmmcIsReadOnly (
>>> +  IN EFI_MMC_HOST_PROTOCOL     *This
>>> +  )
>>> +{
>>> +  return FALSE;
>>> +}
>>> +
>>> +BOOLEAN
>>> +DwEmmcIsDmaSupported (
>>> +  IN EFI_MMC_HOST_PROTOCOL     *This
>>> +  )
>>> +{
>>> +  return TRUE;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcBuildDevicePath (
>>> +  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, &mDwEmmcDevicePathGuid);
>>> +
>>> +  *DevicePath = NewDevicePathNode;
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcUpdateClock (
>>> +  VOID
>>> +  )
>>> +{
>>> +  UINT32 Data;
>>> +
>>> +  /* CMD_UPDATE_CLK */
>>> +  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
>>> +      BIT_CMD_START;
>>> +  MmioWrite32 (DWEMMC_CMD, Data);
>>> +  while (1) {
>>> +    Data = MmioRead32 (DWEMMC_CMD);
>>> +    if (!(Data & CMD_START_BIT)) {
>>> +      break;
>>> +    }
>>> +    Data = MmioRead32 (DWEMMC_RINTSTS);
>>> +    if (Data & DWEMMC_INT_HLE) {
>>> +      Print (L"failed to update mmc clock frequency\n");
>>> +      return EFI_DEVICE_ERROR;
>>> +    }
>>> +  }
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcSetClock (
>>> +  IN UINTN                     ClockFreq
>>> +  )
>>> +{
>>> +  UINT32 Divider, Rate, Data;
>>> +  EFI_STATUS Status;
>>> +  BOOLEAN Found = FALSE;
>>> +
>>> +  for (Divider = 1; Divider < 256; Divider++) {
>>> +    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
>>> +    if ((Rate / (2 * Divider)) <= ClockFreq) {
>>> +      Found = TRUE;
>>> +      break;
>>> +    }
>>> +  }
>>> +  if (Found == FALSE) {
>>> +    return EFI_NOT_FOUND;
>>> +  }
>>> +
>>> +  // Wait until MMC is idle
>>> +  do {
>>> +    Data = MmioRead32 (DWEMMC_STATUS);
>>> +  } while (Data & DWEMMC_STS_DATA_BUSY);
>>> +
>>> +  // Disable MMC clock first
>>> +  MmioWrite32 (DWEMMC_CLKENA, 0);
>>> +  Status = DwEmmcUpdateClock ();
>>> +  ASSERT (!EFI_ERROR (Status));
>>> +
>>> +  MmioWrite32 (DWEMMC_CLKDIV, Divider);
>>> +  Status = DwEmmcUpdateClock ();
>>> +  ASSERT (!EFI_ERROR (Status));
>>> +
>>> +  // Enable MMC clock
>>> +  MmioWrite32 (DWEMMC_CLKENA, 1);
>>> +  MmioWrite32 (DWEMMC_CLKSRC, 0);
>>> +  Status = DwEmmcUpdateClock ();
>>> +  ASSERT (!EFI_ERROR (Status));
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcNotifyState (
>>> +  IN EFI_MMC_HOST_PROTOCOL     *This,
>>> +  IN MMC_STATE                 State
>>> +  )
>>> +{
>>> +  UINT32      Data;
>>> +  EFI_STATUS  Status;
>>> +
>>> +  switch (State) {
>>> +  case MmcInvalidState:
>>> +    ASSERT (0);
>>
>> Should this not return something other than EFI_SUCCESS for RELEASE
>> builds?
>>
>>> +    break;
>>> +  case MmcHwInitializationState:
>>> +    MmioWrite32 (DWEMMC_PWREN, 1);
>>> +
>>> +    // If device already turn on then restart it
>>> +    Data = DWEMMC_CTRL_RESET_ALL;
>>> +    MmioWrite32 (DWEMMC_CTRL, Data);
>>> +    do {
>>> +      // Wait until reset operation finished
>>> +      Data = MmioRead32 (DWEMMC_CTRL);
>>> +    } while (Data & DWEMMC_CTRL_RESET_ALL);
>>> +
>>> +    // Setup clock that could not be higher than 400KHz.
>>> +    Status = DwEmmcSetClock (400000);
>>> +    ASSERT (!EFI_ERROR (Status));
>>> +    MicroSecondDelay (100);
>>
>> Why the delay?
>> Does this also need a barrier?
>>
>>> +
>>> +    MmioWrite32 (DWEMMC_RINTSTS, ~0);
>>> +    MmioWrite32 (DWEMMC_INTMASK, 0);
>>> +    MmioWrite32 (DWEMMC_TMOUT, ~0);
>>> +    MmioWrite32 (DWEMMC_IDINTEN, 0);
>>> +    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
>>> +
>>> +    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
>>> +    do {
>>> +      Data = MmioRead32 (DWEMMC_BMOD);
>>> +    } while (Data & DWEMMC_IDMAC_SWRESET);
>>> +    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;
>>> +}
>>> +
>>> +// Need to prepare DMA buffer first before sending commands to MMC card
>>> +BOOLEAN
>>> +IsPendingReadCommand (
>>> +  IN MMC_CMD                    MmcCmd
>>> +  )
>>> +{
>>> +  UINTN  Mask;
>>> +
>>> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
>>> +  if ((MmcCmd & Mask) == Mask) {
>>> +    return TRUE;
>>> +  }
>>> +  return FALSE;
>>> +}
>>> +
>>> +BOOLEAN
>>> +IsPendingWriteCommand (
>>> +  IN MMC_CMD                    MmcCmd
>>> +  )
>>> +{
>>> +  UINTN  Mask;
>>> +
>>> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
>>> +  if ((MmcCmd & Mask) == Mask) {
>>> +    return TRUE;
>>> +  }
>>> +  return FALSE;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +SendCommand (
>>> +  IN MMC_CMD                    MmcCmd,
>>> +  IN UINT32                     Argument
>>> +  )
>>> +{
>>> +  UINT32      Data, ErrMask;
>>> +
>>> +  // Wait until MMC is idle
>>> +  do {
>>> +    Data = MmioRead32 (DWEMMC_STATUS);
>>> +  } while (Data & DWEMMC_STS_DATA_BUSY);
>>> +
>>> +  MmioWrite32 (DWEMMC_RINTSTS, ~0);
>>> +  MmioWrite32 (DWEMMC_CMDARG, Argument);
>>> +  MmioWrite32 (DWEMMC_CMD, MmcCmd);
>>> +
>>> +  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
>>> +            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
>>> +  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
>>> +  do {
>>> +    MicroSecondDelay(500);
>>> +    Data = MmioRead32 (DWEMMC_RINTSTS);
>>> +
>>> +    if (Data & ErrMask) {
>>> +      return EFI_DEVICE_ERROR;
>>> +    }
>>> +    if (Data & DWEMMC_INT_DTO) {     // Transfer Done
>>> +      break;
>>> +    }
>>> +  } while (!(Data & DWEMMC_INT_CMD_DONE));
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcSendCommand (
>>> +  IN EFI_MMC_HOST_PROTOCOL     *This,
>>> +  IN MMC_CMD                    MmcCmd,
>>> +  IN UINT32                     Argument
>>> +  )
>>> +{
>>> +  UINT32       Cmd = 0;
>>> +  EFI_STATUS   Status = EFI_SUCCESS;
>>> +
>>> +  switch (MMC_GET_INDX(MmcCmd)) {
>>> +  case MMC_INDX(0):
>>> +    Cmd = BIT_CMD_SEND_INIT;
>>> +    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_SEND_INIT;
>>> +    break;
>>> +  case MMC_INDX(3):
>>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>>> +           BIT_CMD_SEND_INIT;
>>> +    break;
>>> +  case MMC_INDX(7):
>>> +    if (Argument)
>>> +        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
>>> +    else
>>> +        Cmd = 0;
>>> +    break;
>>> +  case MMC_INDX(8):
>>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>>> +           BIT_CMD_DATA_EXPECTED | 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;
>>> +    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 |
>>> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
>>> +    break;
>>> +  case MMC_INDX(16):
>>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>>> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
>>> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
>>> +    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;
>>> +    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;
>>> +    break;
>>> +  case MMC_INDX(30):
>>> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
>>> +           BIT_CMD_DATA_EXPECTED;
>>> +    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 (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
>>> +    mDwEmmcCommand = Cmd;
>>> +    mDwEmmcArgument = Argument;
>>> +  } else {
>>> +    Status = SendCommand (Cmd, Argument);
>>> +  }
>>> +  return Status;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcReceiveResponse (
>>> +  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 (DWEMMC_RESP0);
>>> +  } else if (Type == MMC_RESPONSE_TYPE_R2) {
>>> +    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
>>> +    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
>>> +    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
>>> +    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
>>> +  }
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +PrepareDmaData (
>>> +  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
>>> +  IN UINTN                      Length,
>>> +  IN UINT32*                    Buffer
>>> +  )
>>> +{
>>> +  UINTN  Cnt, Blks, Idx, LastIdx;
>>> +
>>> +  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
>>> +  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
>>> +  Length = DWEMMC_BLOCK_SIZE * Blks;
>>> +
>>> +  for (Idx = 0; Idx < Cnt; Idx++) {
>>> +    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
>>> +                              DWEMMC_IDMAC_DES0_DIC;
>>> +    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
>>> +    /* Buffer Address */
>>> +    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
>>> +    /* Next Descriptor Address */
>>> +    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
>>> +                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
>>> +  }
>>> +  /* First Descriptor */
>>> +  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
>>> +  /* Last Descriptor */
>>> +  LastIdx = Cnt - 1;
>>> +  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
>>> +  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
>>> +  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
>>> +                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
>>> +  /* Set the Next field of Last Descriptor */
>>> +  (IdmacDesc + LastIdx)->Des3 = 0;
>>> +  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +VOID
>>> +StartDma (
>>> +  UINTN    Length
>>> +  )
>>> +{
>>> +  UINT32 Data;
>>> +
>>> +  Data = MmioRead32 (DWEMMC_CTRL);
>>> +  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
>>> +  MmioWrite32 (DWEMMC_CTRL, Data);
>>> +  Data = MmioRead32 (DWEMMC_BMOD);
>>> +  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
>>> +  MmioWrite32 (DWEMMC_BMOD, Data);
>>> +
>>> +  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
>>> +  MmioWrite32 (DWEMMC_BYTCNT, Length);
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcReadBlockData (
>>> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
>>> +  DescPages = (Count + CountPerPage - 1) / CountPerPage;
>>> +
>>> +  InvalidateDataCacheRange (Buffer, Length);
>>> +
>>> +  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
>>> +  if (EFI_ERROR (Status)) {
>>> +    goto out;
>>> +  }
>>> +
>>> +  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
>>> +  StartDma (Length);
>>> +
>>> +  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
>>> +  if (EFI_ERROR (Status)) {
>>> +    DEBUG ((EFI_D_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
>>> +    goto out;
>>> +  }
>>> +out:
>>> +  // Restore Tpl
>>> +  gBS->RestoreTPL (Tpl);
>>> +  return Status;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcWriteBlockData (
>>> +  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_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 (mDwEmmcCommand, mDwEmmcArgument);
>>> +  if (EFI_ERROR (Status)) {
>>> +    DEBUG ((EFI_D_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
>>> +    goto out;
>>> +  }
>>> +out:
>>> +  // Restore Tpl
>>> +  gBS->RestoreTPL (Tpl);
>>> +  return Status;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +DwEmmcSetIos (
>>> +  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 (DWEMMC_UHSREG);
>>> +    switch (TimingMode) {
>>> +    case EMMCHS52DDR1V2:
>>> +    case EMMCHS52DDR1V8:
>>> +      Data |= 1 << 16;
>>> +      break;
>>> +    case EMMCHS52:
>>> +    case EMMCHS26:
>>> +      Data &= ~(1 << 16);
>>> +      break;
>>> +    default:
>>> +      return EFI_UNSUPPORTED;
>>> +    }
>>> +    MmioWrite32 (DWEMMC_UHSREG, Data);
>>> +  }
>>> +
>>> +  switch (BusWidth) {
>>> +  case 1:
>>> +    MmioWrite32 (DWEMMC_CTYPE, 0);
>>> +    break;
>>> +  case 4:
>>> +    MmioWrite32 (DWEMMC_CTYPE, 1);
>>> +    break;
>>> +  case 8:
>>> +    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
>>> +    break;
>>> +  default:
>>> +    return EFI_UNSUPPORTED;
>>> +  }
>>> +  if (BusClockFreq) {
>>> +    Status = DwEmmcSetClock (BusClockFreq);
>>> +  }
>>> +  return Status;
>>> +}
>>> +
>>> +BOOLEAN
>>> +DwEmmcIsMultiBlock (
>>> +  IN EFI_MMC_HOST_PROTOCOL      *This
>>> +  )
>>> +{
>>> +  return TRUE;
>>> +}
>>> +
>>> +EFI_MMC_HOST_PROTOCOL gMciHost = {
>>> +  MMC_HOST_PROTOCOL_REVISION,
>>> +  DwEmmcIsCardPresent,
>>> +  DwEmmcIsReadOnly,
>>> +  DwEmmcBuildDevicePath,
>>> +  DwEmmcNotifyState,
>>> +  DwEmmcSendCommand,
>>> +  DwEmmcReceiveResponse,
>>> +  DwEmmcReadBlockData,
>>> +  DwEmmcWriteBlockData,
>>> +  DwEmmcSetIos,
>>> +  DwEmmcIsMultiBlock
>>> +};
>>> +
>>> +EFI_STATUS
>>> +DwEmmcDxeInitialize (
>>> +  IN EFI_HANDLE         ImageHandle,
>>> +  IN EFI_SYSTEM_TABLE   *SystemTable
>>> +  )
>>> +{
>>> +  EFI_STATUS    Status;
>>> +  EFI_HANDLE    Handle;
>>> +
>>> +  Handle = NULL;
>>> +
>>> +  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
>>> +  if (gpIdmacDesc == NULL) {
>>> +    return EFI_BUFFER_TOO_SMALL;
>>> +  }
>>> +
>>> +  DEBUG ((EFI_D_BLKIO, "DwEmmcDxeInitialize()\n"));
>>> +
>>> +  //Publish Component Name, BlockIO protocol interfaces
>>> +  Status = gBS->InstallMultipleProtocolInterfaces (
>>> +                  &Handle,
>>> +                  &gEfiMmcHostProtocolGuid,         &gMciHost,
>>> +                  NULL
>>> +                  );
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>>> new file mode 100644
>>> index 0000000..b281b93
>>> --- /dev/null
>>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>>> @@ -0,0 +1,42 @@
>>> +#/** @file
>>> +# Framework Module Development Environment Industry Standards
>>> +#
>>> +# This Package provides headers and libraries that conform to EFI/PI Industry standards.
>>> +# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
>>> +# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR>
>>> +# Copyright (c) 2015-2016, Linaro. 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.
>>> +#
>>> +#**/
>>> +
>>> +[Defines]
>>> +  DEC_SPECIFICATION              = 0x00010005
>>> +  PACKAGE_NAME                   = OpenPlatformDriversMmcDwEmmcDxePkg
>>
>> Can probably drop "OpenPlatformDrivers".
>>
>>> +  PACKAGE_GUID                   = 3869905e-c96c-4d20-9bfb-3b9d71bb900c
>>> +  PACKAGE_VERSION                = 0.1
>>> +
>>> +
>>> +################################################################################
>>> +#
>>> +# Include Section - list of Include Paths that are provided by this package.
>>> +#                   Comments are used for Keywords and Module Types.
>>> +#
>>> +# Supported Module Types:
>>> +#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
>>> +#
>>> +################################################################################
>>> +
>>> +[Guids.common]
>>> +  gDwEmmcDxeTokenSpaceGuid   = { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}
>>
>> Again, this looks a bit funky to me.
>> Is this working around some missing [Protocols] dependency?
>>
>> Regards,
>>
>> Leif
>>
>>> +
>>> +[PcdsFixedAtBuild.common]
>>> +  # DwEmmc Driver PCDs
>>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
>>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002
>>> diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>>> new file mode 100644
>>> index 0000000..f23494c
>>> --- /dev/null
>>> +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
>>> @@ -0,0 +1,55 @@
>>> +#/** @file
>>> +#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
>>> +#
>>> +#  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.
>>> +#
>>> +#**/
>>> +
>>> +[Defines]
>>> +  INF_VERSION                    = 0x00010005
>>> +  BASE_NAME                      = DwEmmcDxe
>>> +  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
>>> +  MODULE_TYPE                    = DXE_DRIVER
>>> +  VERSION_STRING                 = 1.0
>>> +
>>> +  ENTRY_POINT                    = DwEmmcDxeInitialize
>>> +
>>> +[Sources.common]
>>> +  DwEmmcDxe.c
>>> +
>>> +[Packages]
>>> +  EmbeddedPkg/EmbeddedPkg.dec
>>> +  MdePkg/MdePkg.dec
>>> +  OpenPlatformPkg/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
>>> +
>>> +[LibraryClasses]
>>> +  ArmLib
>>> +  BaseLib
>>> +  BaseMemoryLib
>>> +  CacheMaintenanceLib
>>> +  IoLib
>>> +  MemoryAllocationLib
>>> +  TimerLib
>>> +  UefiDriverEntryPoint
>>> +  UefiLib
>>> +
>>> +[Protocols]
>>> +  gEfiCpuArchProtocolGuid
>>> +  gEfiDevicePathProtocolGuid
>>> +  gEfiMmcHostProtocolGuid
>>> +
>>> +[Pcd]
>>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
>>> +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
>>> +
>>> +[Depex]
>>> +  TRUE
>>> --
>>> 2.7.4
>>>
> _______________________________________________
> Linaro-uefi mailing list
> Linaro-uefi@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/linaro-uefi
Haojian Zhuang Nov. 30, 2016, 12:01 p.m. UTC | #9
On 30 November 2016 at 16:24, Marcin Wojtas <mw@semihalf.com> wrote:
> Hi,
>
> A tiny remark - how about moving this driver to SdMmc?
>

Hi Marcin,

I tried it a few months ago. But Intel guys claimed SdMmc only
supports SDHC. They don't
want to support any vendor's IP that isn't compatible with SDHC.

Best Regards
Haojian
Ard Biesheuvel Nov. 30, 2016, 12:19 p.m. UTC | #10
> On 30 Nov 2016, at 12:01, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
> 
>> On 30 November 2016 at 16:24, Marcin Wojtas <mw@semihalf.com> wrote:
>> Hi,
>> 
>> A tiny remark - how about moving this driver to SdMmc?
>> 
> 
> Hi Marcin,
> 
> I tried it a few months ago. But Intel guys claimed SdMmc only
> supports SDHC. They don't
> want to support any vendor's IP that isn't compatible with SDHC.
> 

There is a new UEFI pass through protocol for SD/MMC, and there is a driver inplementation producing this protocol based on PCI controllers that adhere to the SDHCI spec.

I guess Marcin's comment was about the protocol, and not the driver. The legacy Mmc host protocol predates the new UEFI one, and may be useful going forward for mobile platforms where the UEFI varstore needs to be hosted on MMC

For new generic inplementations, SD/MMC controller drivers should
a) be based on the UEFI driver model, and
b) implement the new SD/MMC pass through protocol, not the legacy Mmc host protocol
Marcin Wojtas Nov. 30, 2016, 12:39 p.m. UTC | #11
Hi,

2016-11-30 13:19 GMT+01:00 Ard Biesheuvel <ard.biesheuvel@linaro.org>:
>
>> On 30 Nov 2016, at 12:01, Haojian Zhuang <haojian.zhuang@linaro.org> wrote:
>>
>>> On 30 November 2016 at 16:24, Marcin Wojtas <mw@semihalf.com> wrote:
>>> Hi,
>>>
>>> A tiny remark - how about moving this driver to SdMmc?
>>>
>>
>> Hi Marcin,
>>
>> I tried it a few months ago. But Intel guys claimed SdMmc only
>> supports SDHC. They don't
>> want to support any vendor's IP that isn't compatible with SDHC.
>>
>
> There is a new UEFI pass through protocol for SD/MMC, and there is a driver inplementation producing this protocol based on PCI controllers that adhere to the SDHCI spec.
>
> I guess Marcin's comment was about the protocol, and not the driver. The legacy Mmc host protocol predates the new UEFI one, and may be useful going forward for mobile platforms where the UEFI varstore needs to be hosted on MMC
>
> For new generic inplementations, SD/MMC controller drivers should
> a) be based on the UEFI driver model, and
> b) implement the new SD/MMC pass through protocol, not the legacy Mmc host protocol
>

Correct. I also meant to place driver in Drivers/SdMmc directory,
regardless implemetation details.

Best regards,
Marcin
Haojian Zhuang Dec. 6, 2016, 6:29 a.m. UTC | #12
On 30 November 2016 at 04:01, Leif Lindholm <leif.lindholm@linaro.org> wrote:
>
> On Wed, Nov 23, 2016 at 03:12:28PM +0800, Haojian Zhuang wrote:
> > Support designware emmc controller.
> >
> > Contributed-under: TianoCore Contribution Agreement 1.0
> > Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> > ---
> >  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
> >  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
> >  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
> >  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
> >  4 files changed, 876 insertions(+)
> >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
> >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
> >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> >
> > +    // Setup clock that could not be higher than 400KHz.
> > +    Status = DwEmmcSetClock (400000);
> > +    ASSERT (!EFI_ERROR (Status));
> > +    MicroSecondDelay (100);
>
> Why the delay?
> Does this also need a barrier?
>

It's used to wait clock stable. It's not related to memory barrier.
I'll add comments.

> > +
> > +[Guids.common]
> > +  gDwEmmcDxeTokenSpaceGuid   = { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}
>
> Again, this looks a bit funky to me.
> Is this working around some missing [Protocols] dependency?
>

I also need to quote two PCD values in DSC file. If I removed
gDwEmmcDxeTokenSpaceGuid, how could I quote PCD values in DSC file?

>
> > +
> > +[PcdsFixedAtBuild.common]
> > +  # DwEmmc Driver PCDs
> > +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
> > +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002

Best Regards
Haojian
Leif Lindholm Dec. 6, 2016, 9:51 a.m. UTC | #13
On Tue, Dec 06, 2016 at 02:29:17PM +0800, Haojian Zhuang wrote:
> On 30 November 2016 at 04:01, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> >
> > On Wed, Nov 23, 2016 at 03:12:28PM +0800, Haojian Zhuang wrote:
> > > Support designware emmc controller.
> > >
> > > Contributed-under: TianoCore Contribution Agreement 1.0
> > > Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> > > ---
> > >  Drivers/Mmc/DwEmmcDxe/DwEmmc.h      | 128 +++++++
> > >  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c   | 651 ++++++++++++++++++++++++++++++++++++
> > >  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec |  42 +++
> > >  Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf |  55 +++
> > >  4 files changed, 876 insertions(+)
> > >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h
> > >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
> > >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
> > >  create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
> > >
> > > +    // Setup clock that could not be higher than 400KHz.
> > > +    Status = DwEmmcSetClock (400000);
> > > +    ASSERT (!EFI_ERROR (Status));
> > > +    MicroSecondDelay (100);
> >
> > Why the delay?
> > Does this also need a barrier?
> >
> 
> It's used to wait clock stable. It's not related to memory barrier.
> I'll add comments.

Thanks!

> > > +
> > > +[Guids.common]
> > > +  gDwEmmcDxeTokenSpaceGuid   = { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}
> >
> > Again, this looks a bit funky to me.
> > Is this working around some missing [Protocols] dependency?
> >
> 
> I also need to quote two PCD values in DSC file. If I removed
> gDwEmmcDxeTokenSpaceGuid, how could I quote PCD values in DSC file?

No, sorry, this was me being confused.
I interpreted this as being another case of what I commented on in
1/3, but this is completely correct.

Regards,

Leif

> >
> > > +
> > > +[PcdsFixedAtBuild.common]
> > > +  # DwEmmc Driver PCDs
> > > +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
> > > +  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002
> 
> Best Regards
> Haojian
diff mbox

Patch

diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmc.h b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
new file mode 100644
index 0000000..999b584
--- /dev/null
+++ b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h
@@ -0,0 +1,128 @@ 
+/** @file
+*
+*  Copyright (c) 2014, Linaro Limited. All rights reserved.
+*  Copyright (c) 2014, 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.
+*
+**/
+
+
+#ifndef __DWEMMC_H__
+#define __DWEMMC_H__
+
+#include <Protocol/EmbeddedGpio.h>
+
+// DW MMC Registers
+#define DWEMMC_CTRL		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
+#define DWEMMC_PWREN		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
+#define DWEMMC_CLKDIV		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
+#define DWEMMC_CLKSRC		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
+#define DWEMMC_CLKENA		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
+#define DWEMMC_TMOUT		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
+#define DWEMMC_CTYPE		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
+#define DWEMMC_BLKSIZ		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
+#define DWEMMC_BYTCNT		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
+#define DWEMMC_INTMASK		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
+#define DWEMMC_CMDARG		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
+#define DWEMMC_CMD		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
+#define DWEMMC_RESP0		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
+#define DWEMMC_RESP1		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
+#define DWEMMC_RESP2		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
+#define DWEMMC_RESP3		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
+#define DWEMMC_RINTSTS		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
+#define DWEMMC_STATUS		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
+#define DWEMMC_FIFOTH		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
+#define DWEMMC_DEBNCE		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
+#define DWEMMC_UHSREG		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
+#define DWEMMC_BMOD		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
+#define DWEMMC_DBADDR		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
+#define DWEMMC_IDSTS		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
+#define DWEMMC_IDINTEN		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
+#define DWEMMC_DSCADDR		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
+#define DWEMMC_BUFADDR		((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
+#define DWEMMC_CARDTHRCTL	((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
+
+#define CMD_UPDATE_CLK				0x80202000
+#define CMD_START_BIT				(1 << 31)
+
+#define MMC_8BIT_MODE				(1 << 16)
+
+#define BIT_CMD_RESPONSE_EXPECT			(1 << 6)
+#define BIT_CMD_LONG_RESPONSE			(1 << 7)
+#define BIT_CMD_CHECK_RESPONSE_CRC		(1 << 8)
+#define BIT_CMD_DATA_EXPECTED			(1 << 9)
+#define BIT_CMD_READ				(0 << 10)
+#define BIT_CMD_WRITE				(1 << 10)
+#define BIT_CMD_BLOCK_TRANSFER			(0 << 11)
+#define BIT_CMD_STREAM_TRANSFER			(1 << 11)
+#define BIT_CMD_SEND_AUTO_STOP			(1 << 12)
+#define BIT_CMD_WAIT_PRVDATA_COMPLETE		(1 << 13)
+#define BIT_CMD_STOP_ABORT_CMD			(1 << 14)
+#define BIT_CMD_SEND_INIT			(1 << 15)
+#define BIT_CMD_UPDATE_CLOCK_ONLY		(1 << 21)
+#define BIT_CMD_READ_CEATA_DEVICE		(1 << 22)
+#define BIT_CMD_CCS_EXPECTED			(1 << 23)
+#define BIT_CMD_ENABLE_BOOT			(1 << 24)
+#define BIT_CMD_EXPECT_BOOT_ACK			(1 << 25)
+#define BIT_CMD_DISABLE_BOOT			(1 << 26)
+#define BIT_CMD_MANDATORY_BOOT			(0 << 27)
+#define BIT_CMD_ALTERNATE_BOOT			(1 << 27)
+#define BIT_CMD_VOLT_SWITCH			(1 << 28)
+#define BIT_CMD_USE_HOLD_REG			(1 << 29)
+#define BIT_CMD_START				(1 << 31)
+
+#define DWEMMC_INT_EBE				(1 << 15)	/* End-bit Err */
+#define DWEMMC_INT_SBE				(1 << 13)	/* Start-bit  Err */
+#define DWEMMC_INT_HLE				(1 << 12)	/* Hardware-lock Err */
+#define DWEMMC_INT_FRUN				(1 << 11)	/* FIFO UN/OV RUN */
+#define DWEMMC_INT_DRT				(1 << 9)	/* Data timeout */
+#define DWEMMC_INT_RTO				(1 << 8)	/* Response timeout */
+#define DWEMMC_INT_DCRC				(1 << 7)	/* Data CRC err */
+#define DWEMMC_INT_RCRC				(1 << 6)	/* Response CRC err */
+#define DWEMMC_INT_RXDR				(1 << 5)
+#define DWEMMC_INT_TXDR				(1 << 4)
+#define DWEMMC_INT_DTO				(1 << 3)	/* Data trans over */
+#define DWEMMC_INT_CMD_DONE			(1 << 2)
+#define DWEMMC_INT_RE				(1 << 1)
+
+#define DWEMMC_IDMAC_DES0_DIC			(1 << 1)
+#define DWEMMC_IDMAC_DES0_LD			(1 << 2)
+#define DWEMMC_IDMAC_DES0_FS			(1 << 3)
+#define DWEMMC_IDMAC_DES0_CH			(1 << 4)
+#define DWEMMC_IDMAC_DES0_ER			(1 << 5)
+#define DWEMMC_IDMAC_DES0_CES			(1 << 30)
+#define DWEMMC_IDMAC_DES0_OWN			(1 << 31)
+#define DWEMMC_IDMAC_DES1_BS1(x)		((x) & 0x1fff)
+#define DWEMMC_IDMAC_DES2_BS2(x)		(((x) & 0x1fff) << 13)
+#define DWEMMC_IDMAC_SWRESET			(1 << 0)
+#define DWEMMC_IDMAC_FB				(1 << 1)
+#define DWEMMC_IDMAC_ENABLE			(1 << 7)
+
+#define EMMC_FIX_RCA				6
+
+/* bits in MMC0_CTRL */
+#define DWEMMC_CTRL_RESET			(1 << 0)
+#define DWEMMC_CTRL_FIFO_RESET			(1 << 1)
+#define DWEMMC_CTRL_DMA_RESET			(1 << 2)
+#define DWEMMC_CTRL_INT_EN			(1 << 4)
+#define DWEMMC_CTRL_DMA_EN			(1 << 5)
+#define DWEMMC_CTRL_IDMAC_EN			(1 << 25)
+#define DWEMMC_CTRL_RESET_ALL			(DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
+
+#define DWEMMC_STS_DATA_BUSY			(1 << 9)
+
+#define DWEMMC_FIFO_TWMARK(x)			(x & 0xfff)
+#define DWEMMC_FIFO_RWMARK(x)			((x & 0x1ff) << 16)
+#define DWEMMC_DMA_BURST_SIZE(x)		((x & 0x7) << 28)
+
+#define DWEMMC_CARD_RD_THR(x)			((x & 0xfff) << 16)
+#define DWEMMC_CARD_RD_THR_EN			(1 << 0)
+
+#endif  // __DWEMMC_H__
diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
new file mode 100644
index 0000000..cf75e1c
--- /dev/null
+++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c
@@ -0,0 +1,651 @@ 
+/** @file
+  This file implement the MMC Host Protocol for the DesignWare eMMC.
+
+  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 "DwEmmc.h"
+
+#define DWEMMC_DESC_PAGE		1
+#define DWEMMC_BLOCK_SIZE		512
+#define DWEMMC_DMA_BUF_SIZE		(512 * 8)
+#define DWEMMC_MAX_DESC_PAGES   	512
+
+typedef struct {
+  UINT32		Des0;
+  UINT32		Des1;
+  UINT32		Des2;
+  UINT32		Des3;
+} DWEMMC_IDMAC_DESCRIPTOR;
+
+EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
+DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
+EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
+STATIC UINT32 mDwEmmcCommand;
+STATIC UINT32 mDwEmmcArgument;
+
+EFI_STATUS
+DwEmmcReadBlockData (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN EFI_LBA                    Lba,
+  IN UINTN                      Length,
+  IN UINT32*                    Buffer
+  );
+
+BOOLEAN
+DwEmmcIsPowerOn (
+  VOID
+  )
+{
+    return TRUE;
+}
+
+EFI_STATUS
+DwEmmcInitialize (
+  VOID
+  )
+{
+    DEBUG ((EFI_D_BLKIO, "DwEmmcInitialize()"));
+    return EFI_SUCCESS;
+}
+
+BOOLEAN
+DwEmmcIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return TRUE;
+}
+
+BOOLEAN
+DwEmmcIsReadOnly (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return FALSE;
+}
+
+BOOLEAN
+DwEmmcIsDmaSupported (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return TRUE;
+}
+
+EFI_STATUS
+DwEmmcBuildDevicePath (
+  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, &mDwEmmcDevicePathGuid);
+
+  *DevicePath = NewDevicePathNode;
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcUpdateClock (
+  VOID
+  )
+{
+  UINT32 Data;
+
+  /* CMD_UPDATE_CLK */
+  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
+	 BIT_CMD_START;
+  MmioWrite32 (DWEMMC_CMD, Data);
+  while (1) {
+    Data = MmioRead32 (DWEMMC_CMD);
+    if (!(Data & CMD_START_BIT)) {
+      break;
+    }
+    Data = MmioRead32 (DWEMMC_RINTSTS);
+    if (Data & DWEMMC_INT_HLE) {
+      Print (L"failed to update mmc clock frequency\n");
+      return EFI_DEVICE_ERROR;
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcSetClock (
+  IN UINTN                     ClockFreq
+  )
+{
+  UINT32 Divider, Rate, Data;
+  EFI_STATUS Status;
+  BOOLEAN Found = FALSE;
+
+  for (Divider = 1; Divider < 256; Divider++) {
+    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
+    if ((Rate / (2 * Divider)) <= ClockFreq) {
+      Found = TRUE;
+      break;
+    }
+  }
+  if (Found == FALSE) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Wait until MMC is idle
+  do {
+    Data = MmioRead32 (DWEMMC_STATUS);
+  } while (Data & DWEMMC_STS_DATA_BUSY);
+
+  // Disable MMC clock first
+  MmioWrite32 (DWEMMC_CLKENA, 0);
+  Status = DwEmmcUpdateClock ();
+  ASSERT (!EFI_ERROR (Status));
+
+  MmioWrite32 (DWEMMC_CLKDIV, Divider);
+  Status = DwEmmcUpdateClock ();
+  ASSERT (!EFI_ERROR (Status));
+
+  // Enable MMC clock
+  MmioWrite32 (DWEMMC_CLKENA, 1);
+  MmioWrite32 (DWEMMC_CLKSRC, 0);
+  Status = DwEmmcUpdateClock ();
+  ASSERT (!EFI_ERROR (Status));
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcNotifyState (
+  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 (DWEMMC_PWREN, 1);
+
+    // If device already turn on then restart it
+    Data = DWEMMC_CTRL_RESET_ALL;
+    MmioWrite32 (DWEMMC_CTRL, Data);
+    do {
+      // Wait until reset operation finished
+      Data = MmioRead32 (DWEMMC_CTRL);
+    } while (Data & DWEMMC_CTRL_RESET_ALL);
+
+    // Setup clock that could not be higher than 400KHz.
+    Status = DwEmmcSetClock (400000);
+    ASSERT (!EFI_ERROR (Status));
+    MicroSecondDelay (100);
+
+    MmioWrite32 (DWEMMC_RINTSTS, ~0);
+    MmioWrite32 (DWEMMC_INTMASK, 0);
+    MmioWrite32 (DWEMMC_TMOUT, ~0);
+    MmioWrite32 (DWEMMC_IDINTEN, 0);
+    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
+
+    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
+    do {
+      Data = MmioRead32 (DWEMMC_BMOD);
+    } while (Data & DWEMMC_IDMAC_SWRESET);
+    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;
+}
+
+// Need to prepare DMA buffer first before sending commands to MMC card
+BOOLEAN
+IsPendingReadCommand (
+  IN MMC_CMD                    MmcCmd
+  )
+{
+  UINTN  Mask;
+
+  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
+  if ((MmcCmd & Mask) == Mask) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+BOOLEAN
+IsPendingWriteCommand (
+  IN MMC_CMD                    MmcCmd
+  )
+{
+  UINTN  Mask;
+
+  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
+  if ((MmcCmd & Mask) == Mask) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+EFI_STATUS
+SendCommand (
+  IN MMC_CMD                    MmcCmd,
+  IN UINT32                     Argument
+  )
+{
+  UINT32      Data, ErrMask;
+
+  // Wait until MMC is idle
+  do {
+    Data = MmioRead32 (DWEMMC_STATUS);
+  } while (Data & DWEMMC_STS_DATA_BUSY);
+
+  MmioWrite32 (DWEMMC_RINTSTS, ~0);
+  MmioWrite32 (DWEMMC_CMDARG, Argument);
+  MmioWrite32 (DWEMMC_CMD, MmcCmd);
+
+  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
+            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
+  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
+  do {
+    MicroSecondDelay(500);
+    Data = MmioRead32 (DWEMMC_RINTSTS);
+
+    if (Data & ErrMask) {
+      return EFI_DEVICE_ERROR;
+    }
+    if (Data & DWEMMC_INT_DTO) {	// Transfer Done
+      break;
+    }
+  } while (!(Data & DWEMMC_INT_CMD_DONE));
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcSendCommand (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_CMD                    MmcCmd,
+  IN UINT32                     Argument
+  )
+{
+  UINT32       Cmd = 0;
+  EFI_STATUS   Status = EFI_SUCCESS;
+
+  switch (MMC_GET_INDX(MmcCmd)) {
+  case MMC_INDX(0):
+    Cmd = BIT_CMD_SEND_INIT;
+    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_SEND_INIT;
+    break;
+  case MMC_INDX(3):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_SEND_INIT;
+    break;
+  case MMC_INDX(7):
+    if (Argument)
+        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
+    else
+        Cmd = 0;
+    break;
+  case MMC_INDX(8):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED | 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;
+    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 |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    break;
+  case MMC_INDX(16):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    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;
+    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;
+    break;
+  case MMC_INDX(30):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED;
+    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 (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
+    mDwEmmcCommand = Cmd;
+    mDwEmmcArgument = Argument;
+  } else {
+    Status = SendCommand (Cmd, Argument);
+  }
+  return Status;
+}
+
+EFI_STATUS
+DwEmmcReceiveResponse (
+  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 (DWEMMC_RESP0);
+  } else if (Type == MMC_RESPONSE_TYPE_R2) {
+    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
+    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
+    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
+    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
+  }
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+PrepareDmaData (
+  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
+  IN UINTN                      Length,
+  IN UINT32*                    Buffer
+  )
+{
+  UINTN  Cnt, Blks, Idx, LastIdx;
+
+  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
+  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
+  Length = DWEMMC_BLOCK_SIZE * Blks;
+
+  for (Idx = 0; Idx < Cnt; Idx++) {
+    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
+                              DWEMMC_IDMAC_DES0_DIC;
+    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
+    /* Buffer Address */
+    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
+    /* Next Descriptor Address */
+    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
+                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
+  }
+  /* First Descriptor */
+  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
+  /* Last Descriptor */
+  LastIdx = Cnt - 1;
+  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
+  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
+  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
+                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
+  /* Set the Next field of Last Descriptor */
+  (IdmacDesc + LastIdx)->Des3 = 0;
+  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
+
+  return EFI_SUCCESS;
+}
+
+VOID
+StartDma (
+  UINTN    Length
+  )
+{
+  UINT32 Data;
+
+  Data = MmioRead32 (DWEMMC_CTRL);
+  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
+  MmioWrite32 (DWEMMC_CTRL, Data);
+  Data = MmioRead32 (DWEMMC_BMOD);
+  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
+  MmioWrite32 (DWEMMC_BMOD, Data);
+
+  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
+  MmioWrite32 (DWEMMC_BYTCNT, Length);
+}
+
+EFI_STATUS
+DwEmmcReadBlockData (
+  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
+  DescPages = (Count + CountPerPage - 1) / CountPerPage;
+
+  InvalidateDataCacheRange (Buffer, Length);
+
+  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
+  if (EFI_ERROR (Status)) {
+    goto out;
+  }
+
+  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
+  StartDma (Length);
+
+  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
+    goto out;
+  }
+out:
+  // Restore Tpl
+  gBS->RestoreTPL (Tpl);
+  return Status;
+}
+
+EFI_STATUS
+DwEmmcWriteBlockData (
+  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 + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_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 (mDwEmmcCommand, mDwEmmcArgument);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
+    goto out;
+  }
+out:
+  // Restore Tpl
+  gBS->RestoreTPL (Tpl);
+  return Status;
+}
+
+EFI_STATUS
+DwEmmcSetIos (
+  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 (DWEMMC_UHSREG);
+    switch (TimingMode) {
+    case EMMCHS52DDR1V2:
+    case EMMCHS52DDR1V8:
+      Data |= 1 << 16;
+      break;
+    case EMMCHS52:
+    case EMMCHS26:
+      Data &= ~(1 << 16);
+      break;
+    default:
+      return EFI_UNSUPPORTED;
+    }
+    MmioWrite32 (DWEMMC_UHSREG, Data);
+  }
+
+  switch (BusWidth) {
+  case 1:
+    MmioWrite32 (DWEMMC_CTYPE, 0);
+    break;
+  case 4:
+    MmioWrite32 (DWEMMC_CTYPE, 1);
+    break;
+  case 8:
+    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
+    break;
+  default:
+    return EFI_UNSUPPORTED;
+  }
+  if (BusClockFreq) {
+    Status = DwEmmcSetClock (BusClockFreq);
+  }
+  return Status;
+}
+
+BOOLEAN
+DwEmmcIsMultiBlock (
+  IN EFI_MMC_HOST_PROTOCOL      *This
+  )
+{
+  return TRUE;
+}
+
+EFI_MMC_HOST_PROTOCOL gMciHost = {
+  MMC_HOST_PROTOCOL_REVISION,
+  DwEmmcIsCardPresent,
+  DwEmmcIsReadOnly,
+  DwEmmcBuildDevicePath,
+  DwEmmcNotifyState,
+  DwEmmcSendCommand,
+  DwEmmcReceiveResponse,
+  DwEmmcReadBlockData,
+  DwEmmcWriteBlockData,
+  DwEmmcSetIos,
+  DwEmmcIsMultiBlock
+};
+
+EFI_STATUS
+DwEmmcDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+  EFI_HANDLE    Handle;
+
+  Handle = NULL;
+
+  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
+  if (gpIdmacDesc == NULL) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  DEBUG ((EFI_D_BLKIO, "DwEmmcDxeInitialize()\n"));
+
+  //Publish Component Name, BlockIO protocol interfaces
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEfiMmcHostProtocolGuid,         &gMciHost,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
new file mode 100644
index 0000000..b281b93
--- /dev/null
+++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
@@ -0,0 +1,42 @@ 
+#/** @file
+# Framework Module Development Environment Industry Standards
+#
+# This Package provides headers and libraries that conform to EFI/PI Industry standards.
+# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR>
+# Copyright (c) 2015-2016, Linaro. 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.
+#
+#**/
+
+[Defines]
+  DEC_SPECIFICATION              = 0x00010005
+  PACKAGE_NAME                   = OpenPlatformDriversMmcDwEmmcDxePkg
+  PACKAGE_GUID                   = 3869905e-c96c-4d20-9bfb-3b9d71bb900c
+  PACKAGE_VERSION                = 0.1
+
+
+################################################################################
+#
+# Include Section - list of Include Paths that are provided by this package.
+#                   Comments are used for Keywords and Module Types.
+#
+# Supported Module Types:
+#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
+#
+################################################################################
+
+[Guids.common]
+  gDwEmmcDxeTokenSpaceGuid	= { 0x6fdd76a9, 0xf220, 0x4f1d,  { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }}
+
+[PcdsFixedAtBuild.common]
+  # DwEmmc Driver PCDs
+  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001
+  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002
diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
new file mode 100644
index 0000000..f23494c
--- /dev/null
+++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
@@ -0,0 +1,55 @@ 
+#/** @file
+#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
+#
+#  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.
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = DwEmmcDxe
+  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = DwEmmcDxeInitialize
+
+[Sources.common]
+  DwEmmcDxe.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  OpenPlatformPkg/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec
+
+[LibraryClasses]
+  ArmLib
+  BaseLib
+  BaseMemoryLib
+  CacheMaintenanceLib
+  IoLib
+  MemoryAllocationLib
+  TimerLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiCpuArchProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiMmcHostProtocolGuid
+
+[Pcd]
+  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
+  gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
+
+[Depex]
+  TRUE