[edk2] [PATCH v2 edk2-platforms 10/20] Platform/Broadcom/RPi3: Add Base MMC driver

Pete Batard pete at akeo.ie
Mon Dec 10 04:38:43 PST 2018


Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Pete Batard <pete at akeo.ie>
---
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c     | 163 ++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c       | 256 +++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c               | 458 +++++++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h               | 533 +++++++++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c        | 473 ++++++++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c          | 169 ++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf          |  58 ++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c | 993 ++++++++++++++++++++
 Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h             |  53 ++
 Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h         | 187 ++++
 10 files changed, 3343 insertions(+)

diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c
new file mode 100644
index 000000000000..80eb4ff9a870
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c
@@ -0,0 +1,163 @@
+/** @file
+ *
+ *  Component Name Protocol implementation for the MMC DXE driver
+ *
+ *  Copyright (c) 2011, ARM 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 "Mmc.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gMmcComponentName = {
+  MmcGetDriverName,
+  MmcGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMmcComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) MmcGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) MmcGetControllerName,
+  "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE
+mMmcDriverNameTable[] = {
+  {"eng;en", L"MMC/SD Card Interface Driver"},
+  {NULL,  NULL}
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This                  A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  Language              A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+  @param  DriverName            A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mMmcDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gMmcComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This                  A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  ControllerHandle      The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param  ChildHandle           The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param  Language              A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+  @param  ControllerName        A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c
new file mode 100644
index 000000000000..f0ff16708b67
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c
@@ -0,0 +1,256 @@
+/** @file
+ *
+ *  Diagnostics Protocol implementation for the MMC DXE driver
+ *
+ *  Copyright (c) 2011-2014, ARM 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 <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+
+#include "Mmc.h"
+
+#define DIAGNOSTIC_LOGBUFFER_MAXCHAR  1024
+
+CHAR16* mLogBuffer = NULL;
+UINTN   mLogRemainChar = 0;
+
+CHAR16*
+DiagnosticInitLog (
+  UINTN MaxBufferChar
+  )
+{
+  mLogRemainChar = MaxBufferChar;
+  mLogBuffer = AllocatePool ((UINTN)MaxBufferChar * sizeof (CHAR16));
+  return mLogBuffer;
+}
+
+UINTN
+DiagnosticLog (
+  CONST CHAR16* Str
+  )
+{
+  UINTN len = StrLen (Str);
+  if (len < mLogRemainChar) {
+    StrCpyS (mLogBuffer, mLogRemainChar, Str);
+    mLogRemainChar -= len;
+    mLogBuffer += len;
+    return len;
+  } else {
+    return 0;
+  }
+}
+
+VOID
+GenerateRandomBuffer (
+  VOID* Buffer,
+  UINTN BufferSize
+  )
+{
+  UINT64  i;
+  UINT64* Buffer64 = (UINT64*)Buffer;
+
+  for (i = 0; i < (BufferSize >> 3); i++) {
+    *Buffer64 = i | (~i << 32);
+    Buffer64++;
+  }
+}
+
+BOOLEAN
+CompareBuffer (
+  VOID  *BufferA,
+  VOID  *BufferB,
+  UINTN BufferSize
+  )
+{
+  UINTN i;
+  UINT64* BufferA64 = (UINT64*)BufferA;
+  UINT64* BufferB64 = (UINT64*)BufferB;
+
+  for (i = 0; i < (BufferSize >> 3); i++) {
+    if (*BufferA64 != *BufferB64) {
+      DEBUG ((DEBUG_ERROR, "CompareBuffer: Error at %i", i));
+      DEBUG ((DEBUG_ERROR, "(0x%lX) != (0x%lX)\n", *BufferA64, *BufferB64));
+      return FALSE;
+    }
+    BufferA64++;
+    BufferB64++;
+  }
+  return TRUE;
+}
+
+EFI_STATUS
+MmcReadWriteDataTest (
+  MMC_HOST_INSTANCE *MmcHostInstance,
+  EFI_LBA           Lba,
+  UINTN             BufferSize
+  )
+{
+  VOID                        *BackBuffer;
+  VOID                        *WriteBuffer;
+  VOID                        *ReadBuffer;
+  EFI_STATUS                  Status;
+
+  // Check if a Media is Present
+  if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
+    DiagnosticLog (L"ERROR: No Media Present\n");
+    return EFI_NO_MEDIA;
+  }
+
+  if (MmcHostInstance->State != MmcTransferState) {
+    DiagnosticLog (L"ERROR: Not ready for Transfer state\n");
+    return EFI_NOT_READY;
+  }
+
+  BackBuffer = AllocatePool (BufferSize);
+  WriteBuffer = AllocatePool (BufferSize);
+  ReadBuffer = AllocatePool (BufferSize);
+
+  // Read (and save) buffer at a specific location
+  Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Read Block (1)\n");
+    return Status;
+  }
+
+  // Write buffer at the same location
+  GenerateRandomBuffer (WriteBuffer,BufferSize);
+  Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,WriteBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Write Block (1)\n");
+    return Status;
+  }
+
+  // Read the buffer at the same location
+  Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Read Block (2)\n");
+    return Status;
+  }
+
+  // Check that is conform
+  if (!CompareBuffer (ReadBuffer,WriteBuffer,BufferSize)) {
+    DiagnosticLog (L"ERROR: Fail to Read/Write Block (1)\n");
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Restore content at the original location
+  Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Write Block (2)\n");
+    return Status;
+  }
+
+  // Read the restored content
+  Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Read Block (3)\n");
+    return Status;
+  }
+
+  // Check the content is correct
+  if (!CompareBuffer (ReadBuffer,BackBuffer,BufferSize)) {
+    DiagnosticLog (L"ERROR: Fail to Read/Write Block (2)\n");
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+MmcDriverDiagnosticsRunDiagnostics (
+  IN  EFI_DRIVER_DIAGNOSTICS_PROTOCOL               *This,
+  IN  EFI_HANDLE                                    ControllerHandle,
+  IN  EFI_HANDLE                                    ChildHandle  OPTIONAL,
+  IN  EFI_DRIVER_DIAGNOSTIC_TYPE                    DiagnosticType,
+  IN  CHAR8                                         *Language,
+  OUT EFI_GUID                                      **ErrorType,
+  OUT UINTN                                         *BufferSize,
+  OUT CHAR16                                        **Buffer
+  )
+{
+  LIST_ENTRY              *CurrentLink;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_STATUS              Status;
+
+  if ((Language         == NULL) ||
+      (ErrorType        == NULL) ||
+      (Buffer           == NULL) ||
+      (ControllerHandle == NULL) ||
+      (BufferSize       == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check Language is supported (i.e. is "en-*" - only English is supported)
+  if (AsciiStrnCmp (Language, "en", 2) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = EFI_SUCCESS;
+  *ErrorType  = NULL;
+  *BufferSize = DIAGNOSTIC_LOGBUFFER_MAXCHAR;
+  *Buffer = DiagnosticInitLog (DIAGNOSTIC_LOGBUFFER_MAXCHAR);
+
+  DiagnosticLog (L"MMC Driver Diagnostics\n");
+
+  // Find the MMC Host instance on which we have been asked to run diagnostics
+  MmcHostInstance = NULL;
+  CurrentLink = mMmcHostPool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
+    MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
+    ASSERT(MmcHostInstance != NULL);
+    if (MmcHostInstance->MmcHandle == ControllerHandle) {
+      break;
+    }
+    CurrentLink = CurrentLink->ForwardLink;
+  }
+
+  // If we didn't find the controller, return EFI_UNSUPPORTED
+  if ((MmcHostInstance == NULL)
+      || (MmcHostInstance->MmcHandle != ControllerHandle)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  // LBA=1 Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, 1, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=2 Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: Second Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, 2, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=10 Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: Any Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock >> 1, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=LastBlock Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: Last Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=1 Size=2*BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block / 2 BlockSSize\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, 1, 2 * MmcHostInstance->BlockIo.Media->BlockSize);
+
+  return Status;
+}
+
+//
+// EFI Driver Diagnostics 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2 = {
+  (EFI_DRIVER_DIAGNOSTICS2_RUN_DIAGNOSTICS) MmcDriverDiagnosticsRunDiagnostics,
+  "en"
+};
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c
new file mode 100644
index 000000000000..d90cf3018251
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c
@@ -0,0 +1,458 @@
+/** @file
+ *
+ *  Main file of the MMC Dxe driver. The driver entrypoint is defined into this file.
+ *
+ *  Copyright (c) 2011-2013, ARM 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 <Protocol/DevicePath.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+
+#include "Mmc.h"
+
+EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = {
+  SIGNATURE_32('m','m','c','o'),            // MediaId
+  TRUE,                                     // RemovableMedia
+  FALSE,                                    // MediaPresent
+  FALSE,                                    // LogicalPartition
+  FALSE,                                    // ReadOnly
+  FALSE,                                    // WriteCaching
+  512,                                      // BlockSize
+  4,                                        // IoAlign
+  0,                                        // Pad
+  0                                         // LastBlock
+};
+
+//
+// This device structure is serviced as a header.
+// Its next field points to the first root bridge device node.
+//
+LIST_ENTRY  mMmcHostPool;
+
+/**
+  Event triggered by the timer to check if any cards have been removed
+  or if new ones have been plugged in
+**/
+
+EFI_EVENT gCheckCardsEvent;
+
+/**
+  Initialize the MMC Host Pool to support multiple MMC devices
+**/
+VOID
+InitializeMmcHostPool (
+  VOID
+  )
+{
+  InitializeListHead (&mMmcHostPool);
+}
+
+/**
+  Insert a new Mmc Host controller to the pool
+**/
+VOID
+InsertMmcHost (
+  IN MMC_HOST_INSTANCE      *MmcHostInstance
+  )
+{
+  InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link));
+}
+
+/*
+  Remove a new Mmc Host controller to the pool
+*/
+VOID
+RemoveMmcHost (
+  IN MMC_HOST_INSTANCE      *MmcHostInstance
+  )
+{
+  RemoveEntryList (&(MmcHostInstance->Link));
+}
+
+MMC_HOST_INSTANCE* CreateMmcHostInstance (
+  IN EFI_MMC_HOST_PROTOCOL* MmcHost
+  )
+{
+  EFI_STATUS          Status;
+  MMC_HOST_INSTANCE*  MmcHostInstance;
+  EFI_DEVICE_PATH_PROTOCOL    *NewDevicePathNode;
+  EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
+
+  MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE));
+  if (MmcHostInstance == NULL) {
+    return NULL;
+  }
+
+  MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE;
+
+  MmcHostInstance->State = MmcHwInitializationState;
+
+  MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate);
+  if (MmcHostInstance->BlockIo.Media == NULL) {
+    goto FREE_INSTANCE;
+  }
+
+  MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION;
+  MmcHostInstance->BlockIo.Reset = MmcReset;
+  MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks;
+  MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks;
+  MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks;
+
+  MmcHostInstance->MmcHost = MmcHost;
+
+  // Create DevicePath for the new MMC Host
+  Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode);
+  if (EFI_ERROR (Status)) {
+    goto FREE_MEDIA;
+  }
+
+  DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH);
+  if (DevicePath == NULL) {
+    goto FREE_MEDIA;
+  }
+
+  SetDevicePathEndNode (DevicePath);
+  MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode);
+
+  // Publish BlockIO protocol interface
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                &MmcHostInstance->MmcHandle,
+                &gEfiBlockIoProtocolGuid,&MmcHostInstance->BlockIo,
+                &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
+                NULL
+                );
+  if (EFI_ERROR(Status)) {
+    goto FREE_DEVICE_PATH;
+  }
+
+  return MmcHostInstance;
+
+FREE_DEVICE_PATH:
+  FreePool(DevicePath);
+
+FREE_MEDIA:
+  FreePool(MmcHostInstance->BlockIo.Media);
+
+FREE_INSTANCE:
+  FreePool(MmcHostInstance);
+
+  return NULL;
+}
+
+EFI_STATUS DestroyMmcHostInstance (
+  IN MMC_HOST_INSTANCE* MmcHostInstance
+  )
+{
+  EFI_STATUS Status;
+
+  // Uninstall Protocol Interfaces
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+        MmcHostInstance->MmcHandle,
+        &gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo),
+        &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
+        NULL
+        );
+  ASSERT_EFI_ERROR (Status);
+
+  // Free Memory allocated for the instance
+  if (MmcHostInstance->BlockIo.Media) {
+    FreePool(MmcHostInstance->BlockIo.Media);
+  }
+  if (MmcHostInstance->CardInfo.ECSDData) {
+    FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD)));
+  }
+  FreePool (MmcHostInstance);
+
+  return Status;
+}
+
+/**
+  This function checks if the controller implement the Mmc Host and the Device Path Protocols
+**/
+EFI_STATUS
+EFIAPI
+MmcDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                      Status;
+  //EFI_DEVICE_PATH_PROTOCOL        *ParentDevicePath;
+  EFI_MMC_HOST_PROTOCOL           *MmcHost;
+  EFI_DEV_PATH_PTR                Node;
+
+  //
+  // Check RemainingDevicePath validation
+  //
+  if (RemainingDevicePath != NULL) {
+    //
+    // Check if RemainingDevicePath is the End of Device Path Node,
+    // if yes, go on checking other conditions
+    //
+    if (!IsDevicePathEnd (RemainingDevicePath)) {
+      //
+      // If RemainingDevicePath isn't the End of Device Path Node,
+      // check its validation
+      //
+      Node.DevPath = RemainingDevicePath;
+      if (Node.DevPath->Type != HARDWARE_DEVICE_PATH ||
+        Node.DevPath->SubType != HW_VENDOR_DP      ||
+        DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) {
+          return EFI_UNSUPPORTED;
+      }
+    }
+  }
+
+  //
+  // Check if Mmc Host protocol is installed by platform
+  //
+  Status = gBS->OpenProtocol (
+                Controller,
+                &gRaspberryPiMmcHostProtocolGuid,
+                (VOID **) &MmcHost,
+                This->DriverBindingHandle,
+                Controller,
+                EFI_OPEN_PROTOCOL_BY_DRIVER
+                );
+  if (Status == EFI_ALREADY_STARTED) {
+    return EFI_SUCCESS;
+  }
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Close the Mmc Host used to perform the supported test
+  //
+  gBS->CloseProtocol (
+      Controller,
+      &gRaspberryPiMmcHostProtocolGuid,
+      This->DriverBindingHandle,
+      Controller
+      );
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+MmcDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS              Status;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+
+  //
+  // Check RemainingDevicePath validation
+  //
+  if (RemainingDevicePath != NULL) {
+    //
+    // Check if RemainingDevicePath is the End of Device Path Node,
+    // if yes, return EFI_SUCCESS
+    //
+    if (IsDevicePathEnd (RemainingDevicePath)) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  //
+  // Get the Mmc Host protocol
+  //
+  Status = gBS->OpenProtocol (
+                Controller,
+                &gRaspberryPiMmcHostProtocolGuid,
+                (VOID **) &MmcHost,
+                This->DriverBindingHandle,
+                Controller,
+                EFI_OPEN_PROTOCOL_BY_DRIVER
+                );
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_ALREADY_STARTED) {
+      return EFI_SUCCESS;
+    }
+    return Status;
+  }
+
+  MmcHostInstance = CreateMmcHostInstance(MmcHost);
+  if (MmcHostInstance != NULL) {
+    // Add the handle to the pool
+    InsertMmcHost (MmcHostInstance);
+
+    MmcHostInstance->Initialized = FALSE;
+
+    // Detect card presence now
+    CheckCardsCallback (NULL, NULL);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+MmcDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
+  IN  EFI_HANDLE                    Controller,
+  IN  UINTN                         NumberOfChildren,
+  IN  EFI_HANDLE                    *ChildHandleBuffer
+  )
+{
+  EFI_STATUS          Status = EFI_SUCCESS;
+  LIST_ENTRY          *CurrentLink;
+  MMC_HOST_INSTANCE   *MmcHostInstance;
+
+  MMC_TRACE("MmcDriverBindingStop()");
+
+  // For each MMC instance
+  CurrentLink = mMmcHostPool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
+    MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
+    ASSERT(MmcHostInstance != NULL);
+
+    // Close gRaspberryPiMmcHostProtocolGuid
+    Status = gBS->CloseProtocol (
+                Controller,
+                &gRaspberryPiMmcHostProtocolGuid,(VOID **) &MmcHostInstance->MmcHost,
+                This->DriverBindingHandle
+                );
+
+    // Remove MMC Host Instance from the pool
+    RemoveMmcHost (MmcHostInstance);
+
+    // Destroy MmcHostInstance
+    DestroyMmcHostInstance (MmcHostInstance);
+  }
+
+  return Status;
+}
+
+VOID
+EFIAPI
+CheckCardsCallback (
+  IN  EFI_EVENT   Event,
+  IN  VOID        *Context
+  )
+{
+  LIST_ENTRY          *CurrentLink;
+  MMC_HOST_INSTANCE   *MmcHostInstance;
+  EFI_STATUS          Status;
+
+  CurrentLink = mMmcHostPool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) {
+    MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
+    ASSERT(MmcHostInstance != NULL);
+
+    if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) {
+      MmcHostInstance->State = MmcHwInitializationState;
+      MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized;
+      MmcHostInstance->Initialized = !MmcHostInstance->Initialized;
+
+      if (MmcHostInstance->BlockIo.Media->MediaPresent) {
+        InitializeMmcDevice (MmcHostInstance);
+      }
+
+      Status = gBS->ReinstallProtocolInterface (
+                    (MmcHostInstance->MmcHandle),
+                    &gEfiBlockIoProtocolGuid,
+                    &(MmcHostInstance->BlockIo),
+                    &(MmcHostInstance->BlockIo)
+                    );
+
+      if (EFI_ERROR(Status)) {
+        Print(L"MMC Card: Error reinstalling BlockIo interface\n");
+      }
+    }
+
+    CurrentLink = CurrentLink->ForwardLink;
+  }
+}
+
+
+EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = {
+  MmcDriverBindingSupported,
+  MmcDriverBindingStart,
+  MmcDriverBindingStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+MmcDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  //
+  // Initializes MMC Host pool
+  //
+  InitializeMmcHostPool ();
+
+  //
+  // Install driver model protocol(s).
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gMmcDriverBinding,
+           ImageHandle,
+           &gMmcComponentName,
+           &gMmcComponentName2
+           );
+  ASSERT_EFI_ERROR (Status);
+
+  // Install driver diagnostics
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                &ImageHandle,
+                &gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2,
+                NULL
+                );
+  ASSERT_EFI_ERROR (Status);
+
+  // Use a timer to detect if a card has been plugged in or removed
+  Status = gBS->CreateEvent (
+                EVT_NOTIFY_SIGNAL | EVT_TIMER,
+                TPL_CALLBACK,
+                CheckCardsCallback,
+                NULL,
+                &gCheckCardsEvent);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->SetTimer(
+                gCheckCardsEvent,
+                TimerPeriodic,
+                (UINT64)(10*1000*200)); // 200 ms
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h
new file mode 100644
index 000000000000..a18f4a82d903
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h
@@ -0,0 +1,533 @@
+/** @file
+ *
+ *  Main Header file for the MMC DXE driver
+ *
+ *  Copyright (c) 2011-2015, ARM 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 __MMC_H
+#define __MMC_H
+
+#include <Uefi.h>
+
+#include <Protocol/DiskIo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PiMmcHost.h>
+
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define MMC_TRACE(txt)  DEBUG((DEBUG_BLKIO, "MMC: " txt "\n"))
+
+#define MMC_IOBLOCKS_READ       0
+#define MMC_IOBLOCKS_WRITE      1
+
+#define MMC_OCR_POWERUP             0x80000000
+
+#define MMC_OCR_ACCESS_MASK         0x3     /* bit[30-29] */
+#define MMC_OCR_ACCESS_BYTE         0x1     /* bit[29] */
+#define MMC_OCR_ACCESS_SECTOR       0x2     /* bit[30] */
+
+#define MMC_CSD_GET_CCC(Response)    (Response[2] >> 20)
+#define MMC_CSD_GET_TRANSPEED(Response)    (Response[3] & 0xFF)
+#define MMC_CSD_GET_READBLLEN(Response)    ((Response[2] >> 16) & 0xF)
+#define MMC_CSD_GET_WRITEBLLEN(Response)  ((Response[0] >> 22) & 0xF)
+#define MMC_CSD_GET_FILEFORMAT(Response)  ((Response[0] >> 10) & 0x3)
+#define MMC_CSD_GET_FILEFORMATGRP(Response)  ((Response[0] >> 15) & 0x1)
+#define MMC_CSD_GET_DEVICESIZE(csd)         (((Response[1] >> 30) & 0x3) | ((Response[2] & 0x3FF) << 2))
+#define HC_MMC_CSD_GET_DEVICESIZE(Response)    ((Response[1] >> 16) | ((Response[2] & 0x3F) << 16));
+#define MMC_CSD_GET_DEVICESIZEMULT(csd)     ((Response[1] >> 15) & 0x7)
+
+#define MMC_R0_READY_FOR_DATA               (1 << 8)
+
+#define MMC_R0_CURRENTSTATE(Response)       ((Response[0] >> 9) & 0xF)
+
+#define MMC_R0_STATE_IDLE       0
+#define MMC_R0_STATE_READY      1
+#define MMC_R0_STATE_IDENT      2
+#define MMC_R0_STATE_STDBY      3
+#define MMC_R0_STATE_TRAN       4
+#define MMC_R0_STATE_DATA       5
+#define MMC_R0_STATE_RECV       6
+#define MMC_R0_STATE_PROG       7
+#define MMC_R0_STATE_DIS        8
+
+
+#define EMMC_CMD6_ARG_ACCESS(x)             (((x) & 0x3) << 24)
+#define EMMC_CMD6_ARG_INDEX(x)              (((x) & 0xFF) << 16)
+#define EMMC_CMD6_ARG_VALUE(x)              (((x) & 0xFF) << 8)
+#define EMMC_CMD6_ARG_CMD_SET(x)            (((x) & 0x7) << 0)
+
+#define SWITCH_CMD_DATA_LENGTH              64
+#define SD_HIGH_SPEED_SUPPORTED             0x200
+#define SD_DEFAULT_SPEED                    25000000
+#define SD_HIGH_SPEED                       50000000
+#define SWITCH_CMD_SUCCESS_MASK             0xf
+
+#define BUSWIDTH_4                          4
+
+typedef enum {
+  UNKNOWN_CARD,
+  MMC_CARD,              //MMC card
+  MMC_CARD_HIGH,         //MMC Card with High capacity
+  EMMC_CARD,             //eMMC 4.41 card
+  SD_CARD,               //SD 1.1 card
+  SD_CARD_2,             //SD 2.0 or above standard card
+  SD_CARD_2_HIGH         //SD 2.0 or above high capacity card
+} CARD_TYPE;
+
+typedef struct {
+  UINT32  Reserved0:   7; // 0
+  UINT32  V170_V195:   1; // 1.70V - 1.95V
+  UINT32  V200_V260:   7; // 2.00V - 2.60V
+  UINT32  V270_V360:   9; // 2.70V - 3.60V
+  UINT32  RESERVED_1:  5; // Reserved
+  UINT32  AccessMode:  2; // 00b (byte mode), 10b (sector mode)
+  UINT32  PowerUp:     1; // This bit is set to LOW if the card has not finished the power up routine
+} OCR;
+
+typedef struct {
+  UINT8   SD_SPEC:               4; // SD Memory Card - Spec. Version [59:56]
+  UINT8   SCR_STRUCTURE:         4; // SCR Structure [63:60]
+  UINT8   SD_BUS_WIDTHS:         4; // DAT Bus widths supported [51:48]
+  UINT8   DATA_STAT_AFTER_ERASE: 1; // Data Status after erases [55]
+  UINT8   SD_SECURITY:           3; // CPRM Security Support [54:52]
+  UINT8   EX_SECURITY_1:         1; // Extended Security Support [43]
+  UINT8   SD_SPEC4:              1; // Spec. Version 4.00 or higher [42]
+  UINT8   RESERVED_1:            2; // Reserved [41:40]
+  UINT8   SD_SPEC3:              1; // Spec. Version 3.00 or higher [47]
+  UINT8   EX_SECURITY_2:         3; // Extended Security Support [46:44]
+  UINT8   CMD_SUPPORT:           4; // Command Support bits [35:32]
+  UINT8   RESERVED_2:            4; // Reserved [39:36]
+  UINT32  RESERVED_3;               // Manufacturer Usage [31:0]
+} SCR;
+
+typedef struct {
+  UINT32  NOT_USED;   // 1 [0:0]
+  UINT32  CRC;        // CRC7 checksum [7:1]
+
+  UINT32  MDT;        // Manufacturing date [19:8]
+  UINT32  RESERVED_1; // Reserved [23:20]
+  UINT32  PSN;        // Product serial number [55:24]
+  UINT8   PRV;        // Product revision [63:56]
+  UINT8   PNM[5];     // Product name [64:103]
+  UINT16  OID;        // OEM/Application ID [119:104]
+  UINT8   MID;        // Manufacturer ID [127:120]
+} CID;
+
+typedef struct {
+  UINT8   NOT_USED:           1; // Not used, always 1 [0:0]
+  UINT8   CRC:                7; // CRC [7:1]
+
+  UINT8   RESERVED_1:         2; // Reserved [9:8]
+  UINT8   FILE_FORMAT:        2; // File format [11:10]
+  UINT8   TMP_WRITE_PROTECT:  1; // Temporary write protection [12:12]
+  UINT8   PERM_WRITE_PROTECT: 1; // Permanent write protection [13:13]
+  UINT8   COPY:               1; // Copy flag (OTP) [14:14]
+  UINT8   FILE_FORMAT_GRP:    1; // File format group [15:15]
+
+  UINT16  RESERVED_2:         5; // Reserved [20:16]
+  UINT16  WRITE_BL_PARTIAL:   1; // Partial blocks for write allowed [21:21]
+  UINT16  WRITE_BL_LEN:       4; // Max. write data block length [25:22]
+  UINT16  R2W_FACTOR:         3; // Write speed factor [28:26]
+  UINT16  RESERVED_3:         2; // Reserved [30:29]
+  UINT16  WP_GRP_ENABLE:      1; // Write protect group enable [31:31]
+
+  UINT32  WP_GRP_SIZE:        7; // Write protect group size [38:32]
+  UINT32  SECTOR_SIZE:        7; // Erase sector size [45:39]
+  UINT32  ERASE_BLK_EN:       1; // Erase single block enable [46:46]
+  UINT32  C_SIZE_MULT:        3; // Device size multiplier [49:47]
+  UINT32  VDD_W_CURR_MAX:     3; // Max. write current @ VDD max [52:50]
+  UINT32  VDD_W_CURR_MIN:     3; // Max. write current @ VDD min [55:53]
+  UINT32  VDD_R_CURR_MAX:     3; // Max. read current @ VDD max [58:56]
+  UINT32  VDD_R_CURR_MIN:     3; // Max. read current @ VDD min [61:59]
+  UINT32  C_SIZELow2:         2; // Device size [63:62]
+
+  UINT32  C_SIZEHigh10:       10;// Device size [73:64]
+  UINT32  RESERVED_4:         2; // Reserved [75:74]
+  UINT32  DSR_IMP:            1; // DSR implemented [76:76]
+  UINT32  READ_BLK_MISALIGN:  1; // Read block misalignment [77:77]
+  UINT32  WRITE_BLK_MISALIGN: 1; // Write block misalignment [78:78]
+  UINT32  READ_BL_PARTIAL:    1; // Partial blocks for read allowed [79:79]
+  UINT32  READ_BL_LEN:        4; // Max. read data block length [83:80]
+  UINT32  CCC:                12;// Card command classes [95:84]
+
+  UINT8   TRAN_SPEED          ;  // Max. bus clock frequency [103:96]
+  UINT8   NSAC                ;  // Data read access-time 2 in CLK cycles (NSAC*100) [111:104]
+  UINT8   TAAC                ;  // Data read access-time 1 [119:112]
+
+  UINT8   RESERVED_5:         2; // Reserved [121:120]
+  UINT8   SPEC_VERS:          4; // System specification version [125:122]
+  UINT8   CSD_STRUCTURE:      2; // CSD structure [127:126]
+} CSD;
+
+typedef struct {
+  UINT8   RESERVED_1[16];                     // Reserved [15:0]
+  UINT8   SECURE_REMOVAL_TYPE;                // Secure Removal Type [16:16]
+  UINT8   PRODUCT_STATE_AWARENESS_ENABLEMENT; // Product state awareness enablement [17:17]
+  UINT8   MAX_PRE_LOADING_DATA_SIZE[4];       // MAX pre loading data size [21:18]
+  UINT8   PRE_LOADING_DATA_SIZE[4];           // Pre loading data size [25:22]
+  UINT8   FFU_STATUS;                         // FFU Status [26:26]
+  UINT8   RESERVED_2[2];                      // Reserved [28:27]
+  UINT8   MODE_OPERATION_CODES;               // Mode operation codes [29:29]
+  UINT8   MODE_CONFIG;                        // Mode config [30:30]
+  UINT8   RESERVED_3;                         // Reserved [31:31]
+  UINT8   FLUSH_CACHE;                        // Flushing of the cache [32:32]
+  UINT8   CACHE_CTRL;                         // Control to turn the cache ON/OFF [33:33]
+  UINT8   POWER_OFF_NOTIFICATION;             // Power Off Notification [34:34]
+  UINT8   PACKED_FAILURE_INDEX;               // Packed command failure index [35:35]
+  UINT8   PACKED_COMMAND_STATUS;              // Packed command status [36:36]
+  UINT8   CONTEXT_CONF[15];                   // Context configuration [51:37]
+  UINT8   EXT_PARTITIONS_ATTRIBUTE[2];        // Extended partitions attribute [53:52]
+  UINT8   EXCEPTION_EVENTS_STATUS[2];         // Exception events status [55:54]
+  UINT8   EXCEPTION_EVENTS_CTRL[2];           // Exception events control [57:56]
+  UINT8   DYNCAP_NEEDED;                      // Number of addressed group to be released [58:58]
+  UINT8   CLASS_6_CTRL;                       // Class 6 commands control [59:59]
+  UINT8   INI_TIMEOUT_EMU;                    // 1st initialization after disabling sector size emulation [60:60]
+  UINT8   DATA_SECTOR_SIZE;                   // Sector size [61:61]
+  UINT8   USE_NATIVE_SECTOR;                  // Sector size emulation [62:62]
+  UINT8   NATIVE_SECTOR_SIZE;                 // Native sector size [63:63]
+  UINT8   VENDOR_SPECIFIC_FIELD[64];          // Vendor specific fields [127:64]
+  UINT8   RESERVED_4[2];                      // Reserved [129:128]
+  UINT8   PROGRAM_CID_CSD_DDR_SUPPORT;        // Program CID/CSD in DDR mode support [130:130]
+  UINT8   PERIODIC_WAKEUP;                    // Periodic wake-up [131:131]
+  UINT8   TCASE_SUPPORT;                      // Package case temperature is controlled [132:132]
+  UINT8   PRODUCTION_STATE_AWARENESS;         // Production state awareness [133:133]
+  UINT8   SECTOR_BAD_BLK_MGMNT;               // Bad block management mode [134:134]
+  UINT8   RESERVED_5;                         // Reserved [135:135]
+  UINT8   ENH_START_ADDR[4];                  // Enhanced user data start address [139:136]
+  UINT8   ENH_SIZE_MULT[3];                   // Enhanced user data area size [142:140]
+  UINT8   GP_SIZE_MULT[12];                   // General purpose partition size [154:143]
+  UINT8   PARTITION_SETTING_COMPLETED;        // Partitioning setting [155:155]
+  UINT8   PARTITIONS_ATTRIBUTE;               // Partitions attribute [156:156]
+  UINT8   MAX_ENH_SIZE_MULT[3];               // Max enhanced area size [159:157]
+  UINT8   PARTITIONING_SUPPORT;               // Partitioning [160:160]
+  UINT8   HPI_MGMT;                           // HPI management [161:161]
+  UINT8   RST_N_FUNCTION;                     // H/W reset function [162:162]
+  UINT8   BKOPS_EN;                           // Enable background operations handshake [163:163]
+  UINT8   BKOPS_START;                        // Manually start background operations [164:164]
+  UINT8   SANITIZE_START;                     // Start sanitize operation [165:165]
+  UINT8   WR_REL_PARAM;                       // Write reliability parameter register [166:166]
+  UINT8   WR_REL_SET;                         // Write reliability setting register [167:167]
+  UINT8   RPMB_SIZE_MULT;                     // RPMB size [168:168]
+  UINT8   FW_CONFIG;                          // FW configuration [169:169]
+  UINT8   RESERVED_6;                         // Reserved [170:170]
+  UINT8   USER_WP;                            // User area write protection register [171:171]
+  UINT8   RESERVED_7;                         // Reserved [172:172]
+  UINT8   BOOT_WP;                            // Boot area write protection register [173:173]
+  UINT8   BOOT_WP_STATUS;                     // Boot write protection register [174:174]
+  UINT8   ERASE_GROUP_DEF;                    // High-density erase group definition [175:175]
+  UINT8   RESERVED_8;                         // Reserved [176:176]
+  UINT8   BOOT_BUS_CONDITIONS;                // Boot bus conditions [177:177]
+  UINT8   BOOT_CONFIG_PROT;                   // Boot config protection [178:178]
+  UINT8   PARTITION_CONFIG;                   // Partition config [179:179]
+  UINT8   RESERVED_9;                         // Reserved [180:180]
+  UINT8   ERASED_MEM_CONT;                    // Erased memory content [181:181]
+  UINT8   RESERVED_10;                        // Reserved [182:182]
+  UINT8   BUS_WIDTH;                          // Bus width mode [183:183]
+  UINT8   RESERVED_11;                        // Reserved [184:184]
+  UINT8   HS_TIMING;                          // High-speed interface timing [185:185]
+  UINT8   RESERVED_12;                        // Reserved [186:186]
+  UINT8   POWER_CLASS;                        // Power class [187:187]
+  UINT8   RESERVED_13;                        // Reserved [188:188]
+  UINT8   CMD_SET_REV;                        // Command set revision [189:189]
+  UINT8   RESERVED_14;                        // Reserved [190:190]
+  UINT8   CMD_SET;                            // Command set [191:191]
+  UINT8   EXT_CSD_REV;                        // Extended CSD revision [192:192]
+  UINT8   RESERVED_15;                        // Reserved [193:193]
+  UINT8   CSD_STRUCTURE;                      // CSD Structure [194:194]
+  UINT8   RESERVED_16;                        // Reserved [195:195]
+  UINT8   DEVICE_TYPE;                        // Device type [196:196]
+  UINT8   DRIVER_STRENGTH;                    // I/O Driver strength [197:197]
+  UINT8   OUT_OF_INTERRUPT_TIME;              // Out-of-interrupt busy timing [198:198]
+  UINT8   PARTITION_SWITCH_TIME;              // Partition switching timing [199:199]
+  UINT8   PWR_CL_52_195;                      // Power class for 52MHz at 1.95V 1 R [200:200]
+  UINT8   PWR_CL_26_195;                      // Power class for 26MHz at 1.95V 1 R [201:201]
+  UINT8   PWR_CL_52_360;                      // Power class for 52MHz at 3.6V 1 R [202:202]
+  UINT8   PWR_CL_26_360;                      // Power class for 26MHz at 3.6V 1 R [203:203]
+  UINT8   RESERVED_17;                        // Reserved [204:204]
+  UINT8   MIN_PERF_R_4_26;                    // Minimum read performance for 4bit at 26MHz [205:205]
+  UINT8   MIN_PERF_W_4_26;                    // Minimum write performance for 4bit at 26MHz [206:206]
+  UINT8   MIN_PERF_R_8_26_4_52;               // Minimum read performance for 8bit at 26MHz, for 4bit at 52MHz [207:207]
+  UINT8   MIN_PERF_W_8_26_4_52;               // Minimum write performance for 8bit at 26MHz, for 4bit at 52MHz [208:208]
+  UINT8   MIN_PERF_R_8_52;                    // Minimum read performance for 8bit at 52MHz [209:209]
+  UINT8   MIN_PERF_W_8_52;                    // Minimum write performance for 8bit at 52MHz [210:210]
+  UINT8   RESERVED_18;                        // Reserved [211:211]
+  UINT32  SECTOR_COUNT;                       // Sector count [215:212]
+  UINT8   SLEEP_NOTIFICATION_TIME;            // Sleep notification timout [216:216]
+  UINT8   S_A_TIMEOUT;                        // Sleep/awake timeout [217:217]
+  UINT8   PRODUCTION_STATE_AWARENESS_TIMEOUT; // Production state awareness timeout [218:218]
+  UINT8   S_C_VCCQ;                           // Sleep current (VCCQ) [219:219]
+  UINT8   S_C_VCC;                            // Sleep current (VCC) [220:220]
+  UINT8   HC_WP_GRP_SIZE;                     // High-capacity write protect group size [221:221]
+  UINT8   REL_WR_SECTOR_C;                    // Reliable write sector count [222:222]
+  UINT8   ERASE_TIMEOUT_MULT;                 // High-capacity erase timeout [223:223]
+  UINT8   HC_ERASE_GRP_SIZE;                  // High-capacity erase unit size [224:224]
+  UINT8   ACC_SIZE;                           // Access size [225:225]
+  UINT8   BOOT_SIZE_MULTI;                    // Boot partition size [226:226]
+  UINT8   RESERVED_19;                        // Reserved [227:227]
+  UINT8   BOOT_INFO;                          // Boot information [228:228]
+  UINT8   SECURE_TRIM_MULT;                   // Secure TRIM Multiplier [229:229]
+  UINT8   SECURE_ERASE_MULT;                  // Secure Erase Multiplier [230:230]
+  UINT8   SECURE_FEATURE_SUPPORT;             // Secure Feature Support [231:231]
+  UINT8   TRIM_MULT;                          // TRIM Multiplier [232:232]
+  UINT8   RESERVED_20;                        // Reserved [233:233]
+  UINT8   MIN_PREF_DDR_R_8_52;                // Minimum read performance for 8bit at 52MHz in DDR mode [234:234]
+  UINT8   MIN_PREF_DDR_W_8_52;                // Minimum write performance for 8bit at 52MHz in DDR mode [235:235]
+  UINT8   PWR_CL_200_130;                     // Power class for 200MHz at VCCQ=1.3V, VCC=3.6V [236:236]
+  UINT8   PWR_CL_200_195;                     // Power class for 200MHz at VCCQ=1.95V, VCC=3.6V [237:237]
+  UINT8   PWR_CL_DDR_52_195;                  // Power class for 52MHz, DDR at 1.95V [238:238]
+  UINT8   PWR_CL_DDR_52_360;                  // Power class for 52Mhz, DDR at 3.6V [239:239]
+  UINT8   RESERVED_21;                        // Reserved [240:240]
+  UINT8   INI_TIMEOUT_AP;                     // 1st initialization time after partitioning [241:241]
+  UINT8   CORRECTLY_PRG_SECTORS_NUM[4];       // Number of correctly programmed sectors [245:242]
+  UINT8   BKOPS_STATUS;                       // Background operations status [246:246]
+  UINT8   POWER_OFF_LONG_TIME;                // Power off notification (long) timeout [247:247]
+  UINT8   GENERIC_CMD6_TIME;                  // Generic CMD6 timeout [248:248]
+  UINT8   CACHE_SIZE[4];                      // Cache size [252:249]
+  UINT8   PWR_CL_DDR_200_360;                 // Power class for 200MHz, DDR at VCC=3.6V [253:253]
+  UINT8   FIRMWARE_VERSION[8];                // Firmware version [261:254]
+  UINT8   DEVICE_VERSION[2];                  // Device version [263:262]
+  UINT8   OPTIMAL_TRIM_UNIT_SIZE;             // Optimal trim unit size [264:264]
+  UINT8   OPTIMAL_WRITE_SIZE;                 // Optimal write size [265:265]
+  UINT8   OPTIMAL_READ_SIZE;                  // Optimal read size [266:266]
+  UINT8   PRE_EOL_INFO;                       // Pre EOL information [267:267]
+  UINT8   DEVICE_LIFE_TIME_EST_TYP_A;         // Device life time estimation type A [268:268]
+  UINT8   DEVICE_LIFE_TIME_EST_TYP_B;         // Device life time estimation type B [269:269]
+  UINT8   VENDOR_PROPRIETARY_HEALTH_REPORT[32];         // Vendor proprietary health report [301:270]
+  UINT8   NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED[4]; // Number of FW sectors correctly programmed [305:302]
+  UINT8   RESERVED_22[181];                   // Reserved [486:306]
+  UINT8   FFU_ARG[4];                         // FFU argument [490:487]
+  UINT8   OPERATION_CODE_TIMEOUT;             // Operation codes timeout [491:491]
+  UINT8   FFU_FEATURES;                       // FFU features [492:492]
+  UINT8   SUPPORTED_MODES;                    // Supported modes [493:493]
+  UINT8   EXT_SUPPORT;                        // Extended partitions attribute support [494:494]
+  UINT8   LARGE_UNIT_SIZE_M1;                 // Large unit size [495:495]
+  UINT8   CONTEXT_CAPABILITIES;               // Context management capabilities [496:496]
+  UINT8   TAG_RES_SIZE;                       // Tag resource size [497:497]
+  UINT8   TAG_UNIT_SIZE;                      // Tag unit size [498:498]
+  UINT8   DATA_TAG_SUPPORT;                   // Data tag support [499:499]
+  UINT8   MAX_PACKED_WRITES;                  // Max packed write commands [500:500]
+  UINT8   MAX_PACKED_READS;                   // Max packed read commands [501:501]
+  UINT8   BKOPS_SUPPORT;                      // Background operations support [502:502]
+  UINT8   HPI_FEATURES;                       // HPI features [503:503]
+  UINT8   S_CMD_SET;                          // Supported command sets [504:504]
+  UINT8   EXT_SECURITY_ERR;                   // Extended security commands error [505:505]
+  UINT8   RESERVED_23[6];                     // Reserved [511:506]
+} ECSD;
+
+typedef struct  {
+  UINT16    RCA;
+  CARD_TYPE CardType;
+  OCR       OCRData;
+  CID       CIDData;
+  CSD       CSDData;
+  ECSD      *ECSDData;                         // MMC V4 extended card specific
+} CARD_INFO;
+
+typedef struct _MMC_HOST_INSTANCE {
+  UINTN                     Signature;
+  LIST_ENTRY                Link;
+  EFI_HANDLE                MmcHandle;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+
+  MMC_STATE                 State;
+  EFI_BLOCK_IO_PROTOCOL     BlockIo;
+  CARD_INFO                 CardInfo;
+  EFI_MMC_HOST_PROTOCOL     *MmcHost;
+
+  BOOLEAN                   Initialized;
+} MMC_HOST_INSTANCE;
+
+#define MMC_HOST_INSTANCE_SIGNATURE                 SIGNATURE_32('m', 'm', 'c', 'h')
+#define MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS(a)     CR (a, MMC_HOST_INSTANCE, BlockIo, MMC_HOST_INSTANCE_SIGNATURE)
+#define MMC_HOST_INSTANCE_FROM_LINK(a)              CR (a, MMC_HOST_INSTANCE, Link, MMC_HOST_INSTANCE_SIGNATURE)
+
+
+EFI_STATUS
+EFIAPI
+MmcGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+MmcGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+extern EFI_COMPONENT_NAME_PROTOCOL  gMmcComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gMmcComponentName2;
+
+extern EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2;
+
+extern LIST_ENTRY mMmcHostPool;
+
+/**
+  Reset the block device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.Reset().
+  It resets the block device hardware.
+  ExtendedVerification is ignored in this implementation.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  ExtendedVerification   Indicates that the driver may perform a more exhaustive
+                                 verification operation of the device during reset.
+
+  @retval EFI_SUCCESS            The block device was reset.
+  @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcReset (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN BOOLEAN                  ExtendedVerification
+  );
+
+/**
+  Reads the requested number of blocks from the device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks().
+  It reads the requested number of blocks from the device.
+  All the blocks are read, or an error is returned.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  MediaId                The media ID that the read request is for.
+  @param  Lba                    The starting logical block address to read from on the device.
+  @param  BufferSize             The size of the Buffer in bytes.
+                                 This must be a multiple of the intrinsic block size of the device.
+  @param  Buffer                 A pointer to the destination buffer for the data. The caller is
+                                 responsible for either having implicit or explicit ownership of the buffer.
+
+  @retval EFI_SUCCESS            The data was read correctly from the device.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to perform the read operation.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+  @retval EFI_MEDIA_CHANGED      The MediaId is not for the current media.
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic block size of the device.
+  @retval EFI_INVALID_PARAMETER  The read request contains LBAs that are not valid,
+                                 or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcReadBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer
+  );
+
+/**
+  Writes a specified number of blocks to the device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks().
+  It writes a specified number of blocks to the device.
+  All blocks are written, or an error is returned.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  MediaId                The media ID that the write request is for.
+  @param  Lba                    The starting logical block address to be written.
+  @param  BufferSize             The size of the Buffer in bytes.
+                                 This must be a multiple of the intrinsic block size of the device.
+  @param  Buffer                 Pointer to the source buffer for the data.
+
+  @retval EFI_SUCCESS            The data were written correctly to the device.
+  @retval EFI_WRITE_PROTECTED    The device cannot be written to.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+  @retval EFI_MEDIA_CHANGED      The MediaId is not for the current media.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to perform the write operation.
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic
+                                 block size of the device.
+  @retval EFI_INVALID_PARAMETER  The write request contains LBAs that are not valid,
+                                 or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcWriteBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  IN VOID                     *Buffer
+  );
+
+/**
+  Flushes all modified data to a physical block device.
+
+  @param  This                   Indicates a pointer to the calling context.
+
+  @retval EFI_SUCCESS            All outstanding data were written correctly to the device.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to write data.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcFlushBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This
+  );
+
+EFI_STATUS
+MmcNotifyState (
+  IN MMC_HOST_INSTANCE      *MmcHostInstance,
+  IN MMC_STATE               State
+  );
+
+EFI_STATUS
+InitializeMmcDevice (
+  IN  MMC_HOST_INSTANCE     *MmcHost
+  );
+
+VOID
+EFIAPI
+CheckCardsCallback (
+  IN  EFI_EVENT   Event,
+  IN  VOID        *Context
+  );
+
+VOID
+PrintCSD (
+  IN UINT32* Csd
+  );
+
+VOID
+PrintRCA (
+  IN UINT32 Rca
+  );
+
+VOID
+PrintOCR (
+  IN UINT32 Ocr
+  );
+
+VOID
+PrintResponseR1 (
+  IN  UINT32 Response
+  );
+
+VOID
+PrintCID (
+  IN UINT32* Cid
+  );
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c
new file mode 100644
index 000000000000..54cdbde95292
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c
@@ -0,0 +1,473 @@
+/** @file
+ *
+ *  Copyright (c) 2011-2015, ARM 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 "Mmc.h"
+
+#define MMCI0_BLOCKLEN 512
+#define MMCI0_TIMEOUT  1000
+
+STATIC
+EFI_STATUS
+R1TranAndReady(UINT32 *Response)
+{
+  if ((*Response & MMC_R0_READY_FOR_DATA) != 0 &&
+      MMC_R0_CURRENTSTATE(Response) == MMC_R0_STATE_TRAN) {
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_READY;
+}
+
+STATIC
+EFI_STATUS
+ValidateWrittenBlockCount(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance,
+  IN  UINTN Count,
+  OUT UINTN *TransferredBlocks
+  )
+{
+  UINT32 R1;
+  UINT8 Data[4];
+  EFI_STATUS Status;
+  UINT32 BlocksWritten;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  if (MmcHostInstance->CardInfo.CardType == MMC_CARD ||
+      MmcHostInstance->CardInfo.CardType == MMC_CARD_HIGH ||
+      MmcHostInstance->CardInfo.CardType == EMMC_CARD) {
+    /*
+     * Not on MMC.
+     */
+    return EFI_SUCCESS;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_ACMD22, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, &R1);
+  Status = R1TranAndReady(&R1);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Read Data
+  Status = MmcHost->ReadBlockData (MmcHost, 0, sizeof(Data),
+                                   (VOID *) Data);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  /*
+   * Big Endian.
+   */
+  BlocksWritten = ((UINT32) Data[0] << 24) |
+    ((UINT32) Data[1] << 16) |
+    ((UINT32) Data[2] << 8) |
+    ((UINT32) Data[3] << 0);
+  if (BlocksWritten != Count) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): expected %u != gotten %u\n",
+            __FUNCTION__, __LINE__, Count, BlocksWritten));
+    if (BlocksWritten == 0) {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  *TransferredBlocks = BlocksWritten;
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+WaitUntilTran(
+  IN MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  INTN Timeout;
+  UINT32 Response[1];
+  EFI_STATUS Status = EFI_SUCCESS;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Timeout = MMCI0_TIMEOUT;
+  while(Timeout--) {
+    /*
+     * We expect CMD13 to timeout while card is programming,
+     * because the card holds DAT0 low (busy).
+     */
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD13,
+       MmcHostInstance->CardInfo.RCA << 16);
+    if (EFI_ERROR(Status) && Status != EFI_TIMEOUT) {
+      DEBUG ((DEBUG_ERROR, "%a(%u) CMD13 failed: %r\n",
+              __FUNCTION__, __LINE__, Status));
+      break;
+    }
+
+    if (Status == EFI_SUCCESS) {
+      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
+      Status = R1TranAndReady(Response);
+      if (!EFI_ERROR(Status)) {
+        break;
+      }
+    }
+
+    gBS->Stall(1000);
+  }
+
+  if (0 == Timeout) {
+    DEBUG ((DEBUG_ERROR, "%a(%u) card is busy\n", __FUNCTION__, __LINE__));
+    return EFI_NOT_READY;
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+MmcNotifyState (
+  IN MMC_HOST_INSTANCE *MmcHostInstance,
+  IN MMC_STATE State
+  )
+{
+  MmcHostInstance->State = State;
+  return MmcHostInstance->MmcHost->NotifyState (MmcHostInstance->MmcHost, State);
+}
+
+EFI_STATUS
+EFIAPI
+MmcReset (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN BOOLEAN                  ExtendedVerification
+  )
+{
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+
+  MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
+
+  if (MmcHostInstance->MmcHost == NULL) {
+    // Nothing to do
+    return EFI_SUCCESS;
+  }
+
+  // If a card is not present then clear all media settings
+  if (!MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost)) {
+    MmcHostInstance->BlockIo.Media->MediaPresent = FALSE;
+    MmcHostInstance->BlockIo.Media->LastBlock    = 0;
+    MmcHostInstance->BlockIo.Media->BlockSize    = 512;  // Should be zero but there is a bug in DiskIo
+    MmcHostInstance->BlockIo.Media->ReadOnly     = FALSE;
+
+    // Indicate that the driver requires initialization
+    MmcHostInstance->State = MmcHwInitializationState;
+
+    return EFI_SUCCESS;
+  }
+
+  // Implement me. Either send a CMD0 (could not work for some MMC host) or just turn off/turn
+  //      on power and restart Identification mode
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MmcDetectCard (
+  EFI_MMC_HOST_PROTOCOL     *MmcHost
+  )
+{
+  if (!MmcHost->IsCardPresent (MmcHost)) {
+    return EFI_NO_MEDIA;
+  } else {
+    return EFI_SUCCESS;
+  }
+}
+
+EFI_STATUS
+MmcStopTransmission (
+  EFI_MMC_HOST_PROTOCOL     *MmcHost
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  Response[4];
+  // Command 12 - Stop transmission (ends read or write)
+  // Normally only needed for streaming transfers or after error.
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
+  if (!EFI_ERROR (Status)) {
+    MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
+  }
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+MmcTransferBlock (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINTN                    Cmd,
+  IN UINTN                    Transfer,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer,
+  OUT UINTN                   *TransferredSize
+  )
+{
+  EFI_STATUS              Status;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  UINTN                   CmdArg;
+
+  MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  MmcHost = MmcHostInstance->MmcHost;
+
+  //Set command argument based on the card access mode (Byte mode or Block mode)
+  if ((MmcHostInstance->CardInfo.OCRData.AccessMode & MMC_OCR_ACCESS_MASK) ==
+      MMC_OCR_ACCESS_SECTOR) {
+    CmdArg = Lba;
+  } else {
+    CmdArg = Lba * This->Media->BlockSize;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__,
+            MMC_INDX(Cmd), Status));
+    return Status;
+  }
+
+  if (Transfer == MMC_IOBLOCKS_READ) {
+    Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer);
+  } else {
+    Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer);
+    if (!EFI_ERROR (Status)) {
+      Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "%a() : Error MmcProgrammingState\n", __func__));
+        return Status;
+      }
+    }
+  }
+
+  if (EFI_ERROR (Status) ||
+      BufferSize > This->Media->BlockSize) {
+    /*
+     * CMD12 needs to be set for multiblock (to transition from
+     * RECV to PROG) or for errors.
+     */
+    EFI_STATUS Status2 = MmcStopTransmission (MmcHost);
+    if (EFI_ERROR (Status2)) {
+      DEBUG ((DEBUG_ERROR, "MmcIoBlocks() : CMD12 error on Status %r: %r\n",
+              Status, Status2));
+      return Status2;
+    }
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_BLKIO, "%a(): Error %a Block Data and Status = %r\n",
+              __func__, Transfer == MMC_IOBLOCKS_READ ? "Read" : "Write",
+              Status));
+      return Status;
+    }
+
+    ASSERT (Cmd == MMC_CMD25 || Cmd == MMC_CMD18);
+  }
+
+  //
+  // For reads, should be already in TRAN. For writes, wait
+  // until programming finishes.
+  //
+  Status = WaitUntilTran(MmcHostInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "WaitUntilTran after write failed\n"));
+    return Status;
+  }
+
+  Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIoBlocks() : Error MmcTransferState\n"));
+    return Status;
+  }
+
+  if (Transfer != MMC_IOBLOCKS_READ) {
+    UINTN BlocksWritten = 0;
+
+    Status = ValidateWrittenBlockCount (MmcHostInstance,
+                                        BufferSize /
+                                        This->Media->BlockSize,
+                                        &BlocksWritten);
+    *TransferredSize = BlocksWritten *
+      This->Media->BlockSize;
+  } else {
+    *TransferredSize = BufferSize;
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+MmcIoBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINTN                    Transfer,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer
+  )
+{
+  EFI_STATUS              Status;
+  UINTN                   Cmd;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  UINTN                   BytesRemainingToBeTransfered;
+  UINTN                   BlockCount;
+  UINTN                   ConsumeSize;
+
+  BlockCount = 1;
+  MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  ASSERT (MmcHostInstance != NULL);
+  MmcHost = MmcHostInstance->MmcHost;
+  ASSERT (MmcHost);
+
+  if (This->Media->MediaId != MediaId) {
+    return EFI_MEDIA_CHANGED;
+  }
+
+  if ((MmcHost == NULL) || (Buffer == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check if a Card is Present
+  if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (PcdGet32(PcdMmcDisableMulti) == 0 &&
+      MMC_HOST_HAS_ISMULTIBLOCK(MmcHost) &&
+      MmcHost->IsMultiBlock(MmcHost)) {
+    BlockCount = (BufferSize + This->Media->BlockSize - 1) / This->Media->BlockSize;
+  }
+
+  // All blocks must be within the device
+  if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) {
+    return EFI_WRITE_PROTECTED;
+  }
+
+  // Reading 0 Byte is valid
+  if (BufferSize == 0) {
+    return EFI_SUCCESS;
+  }
+
+  // The buffer size must be an exact multiple of the block size
+  if ((BufferSize % This->Media->BlockSize) != 0) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // Check the alignment
+  if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BytesRemainingToBeTransfered = BufferSize;
+  while (BytesRemainingToBeTransfered > 0) {
+    Status = WaitUntilTran(MmcHostInstance);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "WaitUntilTran before IO failed"));
+      return Status;
+    }
+
+    if (Transfer == MMC_IOBLOCKS_READ) {
+      if (BlockCount == 1) {
+        // Read a single block
+        Cmd = MMC_CMD17;
+      } else {
+        // Read multiple blocks
+        Cmd = MMC_CMD18;
+      }
+    } else {
+      if (BlockCount == 1) {
+        // Write a single block
+        Cmd = MMC_CMD24;
+      } else {
+        // Write multiple blocks
+        Cmd = MMC_CMD25;
+      }
+    }
+
+    ConsumeSize = BlockCount * This->Media->BlockSize;
+    if (BytesRemainingToBeTransfered < ConsumeSize) {
+      ConsumeSize = BytesRemainingToBeTransfered;
+    }
+
+    Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer, &ConsumeSize);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status));
+      return Status;
+    }
+
+    BytesRemainingToBeTransfered -= ConsumeSize;
+    if (BytesRemainingToBeTransfered > 0) {
+      Lba    += BlockCount;
+      Buffer = (UINT8 *)Buffer + ConsumeSize;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+MmcReadBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer
+  )
+{
+  return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer);
+}
+
+EFI_STATUS
+EFIAPI
+MmcWriteBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  IN VOID                     *Buffer
+  )
+{
+  return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer);
+}
+
+EFI_STATUS
+EFIAPI
+MmcFlushBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This
+  )
+{
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c
new file mode 100644
index 000000000000..ea53700caaf8
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c
@@ -0,0 +1,169 @@
+/** @file
+ *
+ *  Copyright (c) 2011-2013, ARM 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 "Mmc.h"
+
+#if !defined(MDEPKG_NDEBUG)
+CONST CHAR8* mStrUnit[] = { "100kbit/s", "1Mbit/s", "10Mbit/s", "100MBit/s",
+                            "Unknown", "Unknown", "Unknown", "Unknown" };
+CONST CHAR8* mStrValue[] = { "1.0", "1.2", "1.3", "1.5", "2.0", "2.5",
+                             "3.0", "3.5", "4.0", "4.5", "5.0", "5.5",
+                             "6.0", "7.0", "8.0" };
+#endif
+
+VOID
+PrintCID (
+  IN UINT32* Cid
+  )
+{
+  DEBUG ((DEBUG_ERROR, "- PrintCID\n"));
+  DEBUG ((DEBUG_ERROR, "\t- Manufacturing date: %d/%d\n", (Cid[0] >> 8) & 0xF, (Cid[0] >> 12) & 0xFF));
+  DEBUG ((DEBUG_ERROR, "\t- Product serial number: 0x%X%X\n", Cid[1] & 0xFFFFFF, (Cid[0] >> 24) & 0xFF));
+  DEBUG ((DEBUG_ERROR, "\t- Product revision: %d\n", Cid[1] >> 24));
+  //DEBUG ((DEBUG_ERROR, "\t- Product name: %s\n", (char*)(Cid + 2)));
+  DEBUG ((DEBUG_ERROR, "\t- OEM ID: %c%c\n", (Cid[3] >> 8) & 0xFF, (Cid[3] >> 16) & 0xFF));
+}
+
+
+VOID
+PrintCSD (
+  IN UINT32* Csd
+  )
+{
+  UINTN Value;
+
+  if (((Csd[2] >> 30) & 0x3) == 0) {
+    DEBUG ((DEBUG_ERROR, "- PrintCSD Version 1.01-1.10/Version 2.00/Standard Capacity\n"));
+  } else if (((Csd[2] >> 30) & 0x3) == 1) {
+    DEBUG ((DEBUG_ERROR, "- PrintCSD Version 2.00/High Capacity\n"));
+  } else {
+    DEBUG ((DEBUG_ERROR, "- PrintCSD Version Higher than v3.3\n"));
+  }
+
+  DEBUG ((DEBUG_ERROR, "\t- Supported card command class: 0x%X\n", MMC_CSD_GET_CCC (Csd)));
+  DEBUG ((DEBUG_ERROR, "\t- Max Speed: %a * %a\n",mStrValue[(MMC_CSD_GET_TRANSPEED (Csd) >> 3) & 0xF],mStrUnit[MMC_CSD_GET_TRANSPEED (Csd) & 7]));
+  DEBUG ((DEBUG_ERROR, "\t- Maximum Read Data Block: %d\n",2 << (MMC_CSD_GET_READBLLEN (Csd)-1)));
+  DEBUG ((DEBUG_ERROR, "\t- Maximum Write Data Block: %d\n",2 << (MMC_CSD_GET_WRITEBLLEN (Csd)-1)));
+
+  if (!MMC_CSD_GET_FILEFORMATGRP (Csd)) {
+    Value = MMC_CSD_GET_FILEFORMAT (Csd);
+    if (Value == 0) {
+      DEBUG ((DEBUG_ERROR, "\t- Format (0): Hard disk-like file system with partition table\n"));
+    } else if (Value == 1) {
+      DEBUG ((DEBUG_ERROR, "\t- Format (1): DOS FAT (floppy-like) with boot sector only (no partition table)\n"));
+    } else if (Value == 2) {
+      DEBUG ((DEBUG_ERROR, "\t- Format (2): Universal File Format\n"));
+    } else {
+      DEBUG ((DEBUG_ERROR, "\t- Format (3): Others/Unknown\n"));
+    }
+  } else {
+    DEBUG ((DEBUG_ERROR, "\t- Format: Reserved\n"));
+  }
+}
+
+VOID
+PrintRCA (
+  IN UINT32 Rca
+  )
+{
+  DEBUG ((DEBUG_ERROR, "- PrintRCA: 0x%X\n", Rca));
+  DEBUG ((DEBUG_ERROR, "\t- Status: 0x%X\n", Rca & 0xFFFF));
+  DEBUG ((DEBUG_ERROR, "\t- RCA: 0x%X\n", (Rca >> 16) & 0xFFFF));
+}
+
+VOID
+PrintOCR (
+  IN UINT32 Ocr
+  )
+{
+  UINTN MinV;
+  UINTN MaxV;
+  UINTN Volts;
+  UINTN Loop;
+
+  MinV  = 36;  // 3.6
+  MaxV  = 20;  // 2.0
+  Volts = 20;  // 2.0
+
+  // The MMC register bits [23:8] indicate the working range of the card
+  for (Loop = 8; Loop < 24; Loop++) {
+    if (Ocr & (1 << Loop)) {
+      if (MinV > Volts) {
+        MinV = Volts;
+      }
+      if (MaxV < Volts) {
+        MaxV = Volts + 1;
+      }
+    }
+    Volts++;
+  }
+
+  DEBUG ((DEBUG_ERROR, "- PrintOCR Ocr (0x%X)\n",Ocr));
+  DEBUG ((DEBUG_ERROR, "\t- Card operating voltage: %d.%d to %d.%d\n", MinV/10, MinV % 10, MaxV/10, MaxV % 10));
+  if (((Ocr >> 29) & 3) == 0) {
+    DEBUG ((DEBUG_ERROR, "\t- AccessMode: Byte Mode\n"));
+  } else {
+    DEBUG ((DEBUG_ERROR, "\t- AccessMode: Block Mode (0x%X)\n", ((Ocr >> 29) & 3)));
+  }
+
+  if (Ocr & MMC_OCR_POWERUP) {
+    DEBUG ((DEBUG_ERROR, "\t- PowerUp\n"));
+  } else {
+    DEBUG ((DEBUG_ERROR, "\t- Voltage Not Supported\n"));
+  }
+}
+
+VOID
+PrintResponseR1 (
+  IN  UINT32 Response
+  )
+{
+  DEBUG ((DEBUG_INFO, "Response: 0x%X\n", Response));
+  if (Response & MMC_R0_READY_FOR_DATA) {
+    DEBUG ((DEBUG_INFO, "\t- READY_FOR_DATA\n"));
+  }
+
+  switch ((Response >> 9) & 0xF) {
+  case 0:
+    DEBUG ((DEBUG_INFO, "\t- State: Idle\n"));
+    break;
+  case 1:
+    DEBUG ((DEBUG_INFO, "\t- State: Ready\n"));
+    break;
+  case 2:
+    DEBUG ((DEBUG_INFO, "\t- State: Ident\n"));
+    break;
+  case 3:
+    DEBUG ((DEBUG_INFO, "\t- State: StandBy\n"));
+    break;
+  case 4:
+    DEBUG ((DEBUG_INFO, "\t- State: Tran\n"));
+    break;
+  case 5:
+    DEBUG ((DEBUG_INFO, "\t- State: Data\n"));
+    break;
+  case 6:
+    DEBUG ((DEBUG_INFO, "\t- State: Rcv\n"));
+    break;
+  case 7:
+    DEBUG ((DEBUG_INFO, "\t- State: Prg\n"));
+    break;
+  case 8:
+    DEBUG ((DEBUG_INFO, "\t- State: Dis\n"));
+    break;
+  default:
+    DEBUG ((DEBUG_INFO, "\t- State: Reserved\n"));
+    break;
+  }
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf
new file mode 100644
index 000000000000..fd91f47aeaf0
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf
@@ -0,0 +1,58 @@
+#/** @file
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2011-2015, ARM 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                      = PiMmcDxe
+  FILE_GUID                      = b6f44cc0-9e45-11df-be21-0002a5f5f51b
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = MmcDxeInitialize
+
+[Sources.common]
+  ComponentName.c
+  Mmc.c
+  MmcBlockIo.c
+  MmcIdentification.c
+  MmcDebug.c
+  Diagnostics.c
+
+[Packages]
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+
+[Protocols]
+  gEfiDiskIoProtocolGuid
+  gEfiBlockIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverDiagnostics2ProtocolGuid
+  gRaspberryPiMmcHostProtocolGuid
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdMmcForce1Bit
+  gRaspberryPiTokenSpaceGuid.PcdMmcForceDefaultSpeed
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdDefaultSpeedMHz
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdHighSpeedMHz
+  gRaspberryPiTokenSpaceGuid.PcdMmcDisableMulti
+
+[Depex]
+  TRUE
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c
new file mode 100644
index 000000000000..ef819fb753c6
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c
@@ -0,0 +1,993 @@
+/** @file
+ *
+ *  Copyright (c) 2011-2015, ARM 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/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+
+#include "Mmc.h"
+
+typedef union {
+  UINT32 Raw;
+  OCR    Ocr;
+} OCR_RESPONSE;
+
+#define MAX_RETRY_COUNT         1000
+#define CMD_RETRY_COUNT         20
+#define RCA_SHIFT_OFFSET        16
+#define EMMC_CARD_SIZE          512
+#define EMMC_ECSD_SIZE_OFFSET   53
+
+#define EXTCSD_BUS_WIDTH        183
+#define EXTCSD_HS_TIMING        185
+
+#define EMMC_TIMING_BACKWARD    0
+#define EMMC_TIMING_HS          1
+#define EMMC_TIMING_HS200       2
+#define EMMC_TIMING_HS400       3
+
+#define EMMC_BUS_WIDTH_1BIT     0
+#define EMMC_BUS_WIDTH_4BIT     1
+#define EMMC_BUS_WIDTH_8BIT     2
+#define EMMC_BUS_WIDTH_DDR_4BIT 5
+#define EMMC_BUS_WIDTH_DDR_8BIT 6
+
+#define EMMC_SWITCH_ERROR       (1 << 7)
+
+#define SD_BUS_WIDTH_1BIT       (1 << 0)
+#define SD_BUS_WIDTH_4BIT       (1 << 2)
+
+#define SD_CCC_SWITCH           (1 << 10)
+
+#define DEVICE_STATE(x)         (((x) >> 9) & 0xf)
+typedef enum _EMMC_DEVICE_STATE {
+  EMMC_IDLE_STATE = 0,
+  EMMC_READY_STATE,
+  EMMC_IDENT_STATE,
+  EMMC_STBY_STATE,
+  EMMC_TRAN_STATE,
+  EMMC_DATA_STATE,
+  EMMC_RCV_STATE,
+  EMMC_PRG_STATE,
+  EMMC_DIS_STATE,
+  EMMC_BTST_STATE,
+  EMMC_SLP_STATE
+} EMMC_DEVICE_STATE;
+
+UINT32 mEmmcRcaCount = 0;
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcGetDeviceState (
+  IN  MMC_HOST_INSTANCE    *MmcHostInstance,
+  OUT EMMC_DEVICE_STATE    *State
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EFI_STATUS Status;
+  UINT32     Data, RCA;
+
+  if (State == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Host = MmcHostInstance->MmcHost;
+  RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
+  Status = Host->SendCommand (Host, MMC_CMD13, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to get card status, Status=%r.\n", Status));
+    return Status;
+  }
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R1, &Data);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to get response of CMD13, Status=%r.\n", Status));
+    return Status;
+  }
+  if (Data & EMMC_SWITCH_ERROR) {
+    DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to switch expected mode, Status=%r.\n", Status));
+    return EFI_DEVICE_ERROR;
+  }
+  *State = DEVICE_STATE(Data);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcSetEXTCSD (
+  IN MMC_HOST_INSTANCE     *MmcHostInstance,
+  UINT32                   ExtCmdIndex,
+  UINT32                   Value
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EMMC_DEVICE_STATE     State;
+  EFI_STATUS Status;
+  UINT32     Argument;
+
+  Host  = MmcHostInstance->MmcHost;
+  Argument = EMMC_CMD6_ARG_ACCESS(3) | EMMC_CMD6_ARG_INDEX(ExtCmdIndex) |
+             EMMC_CMD6_ARG_VALUE(Value) | EMMC_CMD6_ARG_CMD_SET(1);
+  Status = Host->SendCommand (Host, MMC_CMD6, Argument);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcSetEXTCSD(): Failed to send CMD6, Status=%r.\n", Status));
+    return Status;
+  }
+  // Make sure device exiting prog mode
+  do {
+    Status = EmmcGetDeviceState (MmcHostInstance, &State);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcSetEXTCSD(): Failed to get device state, Status=%r.\n", Status));
+      return Status;
+    }
+  } while (State == EMMC_PRG_STATE);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcIdentificationMode (
+  IN MMC_HOST_INSTANCE     *MmcHostInstance,
+  IN OCR_RESPONSE           Response
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EFI_BLOCK_IO_MEDIA    *Media;
+  EFI_STATUS Status;
+  EMMC_DEVICE_STATE     State;
+  UINT32     RCA;
+
+  Host  = MmcHostInstance->MmcHost;
+  Media = MmcHostInstance->BlockIo.Media;
+
+  // Fetch card identity register
+  Status = Host->SendCommand (Host, MMC_CMD2, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to send CMD2, Status=%r.\n", Status));
+    return Status;
+  }
+
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CIDData));
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): CID retrieval error, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Assign a relative address value to the card
+  MmcHostInstance->CardInfo.RCA = ++mEmmcRcaCount; // TODO: might need a more sophisticated way of doing this
+  RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
+  Status = Host->SendCommand (Host, MMC_CMD3, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): RCA set error, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Fetch card specific data
+  Status = Host->SendCommand (Host, MMC_CMD9, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to send CMD9, Status=%r.\n", Status));
+    return Status;
+  }
+
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CSDData));
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): CSD retrieval error, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Select the card
+  Status = Host->SendCommand (Host, MMC_CMD7, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status));
+  }
+
+  if (MMC_HOST_HAS_SETIOS(Host)) {
+    // Set 1-bit bus width
+    Status = Host->SetIos (Host, 0, 1, EMMCBACKWARD);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Set 1-bit bus width error, Status=%r.\n", Status));
+      return Status;
+    }
+
+    // Set 1-bit bus width for EXTCSD
+    Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_1BIT);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Set extcsd bus width error, Status=%r.\n", Status));
+      return Status;
+    }
+  }
+
+  // Fetch ECSD
+  MmcHostInstance->CardInfo.ECSDData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (ECSD)));
+  if (MmcHostInstance->CardInfo.ECSDData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  Status = Host->SendCommand (Host, MMC_CMD8, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): ECSD fetch error, Status=%r.\n", Status));
+  }
+
+  Status = Host->ReadBlockData (Host, 0, 512, (UINT32 *)MmcHostInstance->CardInfo.ECSDData);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): ECSD read error, Status=%r.\n", Status));
+    goto FreePageExit;
+  }
+
+  // Make sure device exiting data mode
+  do {
+    Status = EmmcGetDeviceState (MmcHostInstance, &State);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to get device state, Status=%r.\n", Status));
+      goto FreePageExit;
+    }
+  } while (State == EMMC_DATA_STATE);
+
+  // Set up media
+  Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards
+  Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;
+  Media->ReadOnly = MmcHostInstance->CardInfo.CSDData.PERM_WRITE_PROTECT;
+  Media->LogicalBlocksPerPhysicalBlock = 1;
+  Media->IoAlign = 4;
+  // Compute last block using bits [215:212] of the ECSD
+  Media->LastBlock = MmcHostInstance->CardInfo.ECSDData->SECTOR_COUNT - 1; // eMMC isn't supposed to report this for
+  // Cards <2GB in size, but the model does.
+
+  // Setup card type
+  MmcHostInstance->CardInfo.CardType = EMMC_CARD;
+  return EFI_SUCCESS;
+
+FreePageExit:
+  FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD)));
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+InitializeEmmcDevice (
+  IN  MMC_HOST_INSTANCE   *MmcHostInstance
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EFI_STATUS Status = EFI_SUCCESS;
+  ECSD       *ECSDData;
+  UINT32     BusClockFreq, Idx, BusMode;
+  UINT32     BusWidth = 8;
+  UINT32     TimingMode[4] = {EMMCHS52DDR1V2, EMMCHS52DDR1V8, EMMCHS52, EMMCHS26};
+
+  Host  = MmcHostInstance->MmcHost;
+  ECSDData = MmcHostInstance->CardInfo.ECSDData;
+  if (ECSDData->DEVICE_TYPE == EMMCBACKWARD){
+    return EFI_SUCCESS;
+  }
+
+  if (PcdGet32(PcdMmcForceDefaultSpeed)) {
+    DEBUG((DEBUG_WARN, "Forcing default speed mode\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (PcdGet32(PcdMmcForce1Bit)) {
+    DEBUG((DEBUG_WARN, "Forcing 1 bit mode\n"));
+    BusWidth = 1;
+  }
+
+  if (!MMC_HOST_HAS_SETIOS(Host)) {
+    DEBUG((DEBUG_ERROR, "Controller doesn't support speed / bus width change\n"));
+    return EFI_SUCCESS;
+  }
+
+  Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_HS_TIMING, EMMC_TIMING_HS);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to switch high speed mode, Status:%r.\n", Status));
+    return Status;
+  }
+
+  for (Idx = 0; Idx < 4; Idx++) {
+    switch (TimingMode[Idx]) {
+    case EMMCHS52DDR1V2:
+    case EMMCHS52DDR1V8:
+    case EMMCHS52:
+      BusClockFreq = 52000000;
+      break;
+    case EMMCHS26:
+      BusClockFreq = 26000000;
+      break;
+    default:
+      return EFI_UNSUPPORTED;
+    }
+    Status = Host->SetIos (Host, BusClockFreq, BusWidth, TimingMode[Idx]);
+    if (!EFI_ERROR (Status)) {
+      switch (TimingMode[Idx]) {
+      case EMMCHS52DDR1V2:
+      case EMMCHS52DDR1V8:
+        BusMode = EMMC_BUS_WIDTH_DDR_8BIT;
+        break;
+      case EMMCHS52:
+      case EMMCHS26:
+        BusMode = EMMC_BUS_WIDTH_8BIT;
+        break;
+      default:
+        return EFI_UNSUPPORTED;
+      }
+      Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, BusMode);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to set EXTCSD bus width, Status:%r\n", Status));
+      }
+      return Status;
+    }
+  }
+  return Status;
+}
+
+STATIC
+UINT32
+SdSwitchCmdArgument (
+  IN     UINT32  AccessMode,
+  IN     UINT32  CommandSystem,
+  IN     UINT32  DriveStrength,
+  IN     UINT32  PowerLimit,
+  IN     BOOLEAN Mode
+  )
+{
+  return (AccessMode & 0xF) | ((PowerLimit & 0xF) << 4) |               \
+    ((DriveStrength & 0xF) << 8) | ((DriveStrength & 0xF) << 12) |      \
+    (Mode ? BIT31 : 0);
+}
+
+STATIC
+EFI_STATUS
+SdSelect (
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  /*
+   * Moves a card from standby to transfer state.
+   */
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD7,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error: %r\n",
+           __FUNCTION__, Status));
+  }
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SdDeselect (
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  /*
+   * Moves a card from transfer to standby.
+   */
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD7, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error: %r\n",
+           __FUNCTION__, Status));
+  }
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SdGetCsd(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance,
+  IN  UINT32 *Response,
+  IN  BOOLEAN Print
+  )
+{
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD9,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", __FUNCTION__,
+           __LINE__, Status));
+    return Status;
+  }
+
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CSD,
+                                     Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error %r\n", __FUNCTION__,
+           __LINE__, Status));
+    return Status;
+  }
+
+  if (Print) {
+    PrintCSD (Response);
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SdSet4Bit(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  UINT32 CmdArg;
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  if (PcdGet32(PcdMmcForce1Bit)) {
+    DEBUG((DEBUG_WARN, "Forcing 1 bit mode\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (!MMC_HOST_HAS_SETIOS(MmcHost)) {
+    DEBUG((DEBUG_WARN, "Controller doesn't support bus width change\n"));
+    return EFI_SUCCESS;
+  }
+
+  CmdArg = MmcHostInstance->CardInfo.RCA << 16;
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  /* Width: 4 */
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, 2);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  Status = MmcHost->SetIos (MmcHost, 0, BUSWIDTH_4, EMMCBACKWARD);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SdSetSpeed(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance,
+  IN  BOOLEAN CccSwitch
+  )
+{
+  UINT32 Speed;
+  UINT32 CmdArg;
+  EFI_STATUS Status;
+  UINT32 Buffer[16];
+  UINT32 Response[4];
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  if (!MMC_HOST_HAS_SETIOS(MmcHost)) {
+    DEBUG((DEBUG_WARN, "Controller doesn't support speed change\n"));
+    return EFI_SUCCESS;
+  }
+
+  Speed = PcdGet32(PcdMmcSdDefaultSpeedMHz) * 1000000;
+  if (Speed == 0) {
+    Speed = SD_DEFAULT_SPEED;
+  } else {
+    DEBUG((DEBUG_INFO, "Using default speed override %u Hz\n",
+           Speed));
+  }
+
+  /*
+   * First set base speed. We'll then try HS.
+   */
+  Status = MmcHost->SetIos (MmcHost, Speed, 0, EMMCBACKWARD);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error setting speed %u: %r\n",
+           __FUNCTION__, Speed, Status));
+    return Status;
+  }
+
+  if (PcdGet32(PcdMmcForceDefaultSpeed)) {
+    DEBUG((DEBUG_WARN, "Forcing default speed mode\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (!CccSwitch) {
+    return EFI_SUCCESS;
+  }
+
+  /* Query. */
+  CmdArg = SdSwitchCmdArgument(0xf, 0xf, 0xf, 0xf, FALSE);
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  } else {
+    Status = MmcHost->ReadBlockData (MmcHost, 0, SWITCH_CMD_DATA_LENGTH,
+                                     Buffer);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+              __FUNCTION__, __LINE__, Status));
+      return Status;
+    }
+  }
+
+  if (!(Buffer[3] & SD_HIGH_SPEED_SUPPORTED)) {
+    DEBUG((DEBUG_ERROR, "%a: High Speed not supported by Card\n"));
+    return EFI_SUCCESS;
+  }
+
+  /* Switch to high speed. */
+  CmdArg = SdSwitchCmdArgument(1, 0xf, 0xf, 0xf, TRUE);
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  } else {
+    Status = MmcHost->ReadBlockData (MmcHost, 0,
+                                     SWITCH_CMD_DATA_LENGTH, Buffer);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+              __FUNCTION__, __LINE__, Status));
+      return Status;
+    }
+
+    if ((Buffer[4] & SWITCH_CMD_SUCCESS_MASK) != 0x1) {
+      DEBUG((DEBUG_ERROR, "Problem switching SD card into HS mode\n"));
+      DEBUG((DEBUG_ERROR, "%08x %08x %08x %08x\n",
+             Buffer[0], Buffer[1], Buffer[2], Buffer[3]));
+      DEBUG((DEBUG_ERROR, "%08x %08x %08x %08x\n",
+             Buffer[4], Buffer[5], Buffer[6], Buffer[8]));
+      return Status;
+    }
+  }
+
+  DEBUG((DEBUG_ERROR, "Dumping CSD after high-speed switch\n"));
+  SdDeselect(MmcHostInstance);
+  SdGetCsd(MmcHostInstance, Response, TRUE);
+  SdSelect(MmcHostInstance);
+
+  Speed = PcdGet32(PcdMmcSdHighSpeedMHz) * 1000000;
+  if (Speed == 0) {
+    Speed = SD_HIGH_SPEED;
+  } else {
+    DEBUG((DEBUG_INFO, "Using high speed override %u Hz\n",
+           Speed));
+  }
+
+  Status = MmcHost->SetIos (MmcHost, Speed, 0, EMMCBACKWARD);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error setting speed %u: %r\n",
+           __FUNCTION__, Speed, Status));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+InitializeSdMmcDevice (
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  UINT32        Response[4];
+  UINT32        Buffer[128];
+  UINTN         BlockSize;
+  UINTN         CardSize;
+  UINTN         NumBlocks;
+  BOOLEAN       CccSwitch;
+  SCR           Scr;
+  EFI_STATUS    Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = SdGetCsd (MmcHostInstance, Response, TRUE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (MMC_CSD_GET_CCC(Response) & SD_CCC_SWITCH) {
+    CccSwitch = TRUE;
+  } else {
+    CccSwitch = FALSE;
+  }
+
+  if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {
+    CardSize = HC_MMC_CSD_GET_DEVICESIZE (Response);
+    NumBlocks = ((CardSize + 1) * 1024);
+    BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);
+  } else {
+    CardSize = MMC_CSD_GET_DEVICESIZE (Response);
+    NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT (Response) + 2));
+    BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);
+  }
+
+  // For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.
+  if (BlockSize > 512) {
+    NumBlocks = MultU64x32 (NumBlocks, BlockSize / 512);
+    BlockSize = 512;
+  }
+
+  MmcHostInstance->BlockIo.Media->LastBlock    = (NumBlocks - 1);
+  MmcHostInstance->BlockIo.Media->BlockSize    = BlockSize;
+  MmcHostInstance->BlockIo.Media->ReadOnly     = MmcHost->IsReadOnly (MmcHost);
+  MmcHostInstance->BlockIo.Media->MediaPresent = TRUE;
+  MmcHostInstance->BlockIo.Media->MediaId++;
+
+  Status = SdSelect(MmcHostInstance);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+  if ((Response[0] & MMC_STATUS_APP_CMD) == 0) {
+    return EFI_SUCCESS;
+  }
+
+  /* SCR */
+  Status = MmcHost->SendCommand (MmcHost, MMC_ACMD51, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(MMC_ACMD51): Error and Status = %r\n", __func__, Status));
+    return Status;
+  } else {
+    Status = MmcHost->ReadBlockData (MmcHost, 0, 8, Buffer);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(MMC_ACMD51): ReadBlockData Error and Status = %r\n", __func__, Status));
+      return Status;
+    }
+    CopyMem (&Scr, Buffer, 8);
+    if (Scr.SD_SPEC == 2) {
+      if (Scr.SD_SPEC3 == 1) {
+        if (Scr.SD_SPEC4 == 1) {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 4.xx\n"));
+        } else {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 3.0x\n"));
+        }
+      } else {
+        if (Scr.SD_SPEC4 == 0) {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 2.0\n"));
+       } else {
+         DEBUG ((DEBUG_ERROR, "Found invalid SD Card\n"));
+        }
+      }
+    } else {
+      if ((Scr.SD_SPEC3 == 0) && (Scr.SD_SPEC4 == 0)) {
+        if (Scr.SD_SPEC == 1) {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 1.10\n"));
+        } else {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 1.0\n"));
+        }
+      } else {
+        DEBUG ((DEBUG_ERROR, "Found invalid SD Card\n"));
+      }
+    }
+  }
+
+  Status = SdSetSpeed(MmcHostInstance, CccSwitch);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  if (Scr.SD_BUS_WIDTHS & SD_BUS_WIDTH_4BIT) {
+    Status = SdSet4Bit(MmcHostInstance);
+    if (EFI_ERROR(Status)) {
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MmcIdentificationMode (
+  IN MMC_HOST_INSTANCE     *MmcHostInstance
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  Response[4];
+  UINTN                   Timeout;
+  UINTN                   CmdArg;
+  BOOLEAN                 IsHCS;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  OCR_RESPONSE            OcrResponse;
+
+  MmcHost = MmcHostInstance->MmcHost;
+  CmdArg = 0;
+  IsHCS = FALSE;
+
+  if (MmcHost == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // We can get into this function if we restart the identification mode
+  if (MmcHostInstance->State == MmcHwInitializationState) {
+    // Initialize the MMC Host HW
+    Status = MmcNotifyState (MmcHostInstance, MmcHwInitializationState);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcHwInitializationState, Status=%r.\n", Status));
+      return Status;
+    }
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD0): Error, Status=%r.\n", Status));
+    return Status;
+  }
+  Status = MmcNotifyState (MmcHostInstance, MmcIdleState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcIdleState, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Send CMD1 to get OCR (MMC)
+  // This command only valid for MMC and eMMC
+  Timeout = MAX_RETRY_COUNT;
+  do {
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);
+    if (EFI_ERROR (Status))
+      break;
+    Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, (UINT32 *)&OcrResponse);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
+      return Status;
+    }
+    Timeout--;
+  } while (!OcrResponse.Ocr.PowerUp && (Timeout > 0));
+  if (Status == EFI_SUCCESS) {
+    if (!OcrResponse.Ocr.PowerUp) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD1): Card initialisation failure, Status=%r.\n", Status));
+      return EFI_DEVICE_ERROR;
+    }
+    OcrResponse.Ocr.PowerUp = 0;
+    if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB) {
+      MmcHostInstance->CardInfo.OCRData.AccessMode = BIT1;
+    }
+    else {
+      MmcHostInstance->CardInfo.OCRData.AccessMode = 0x0;
+    }
+    // Check whether MMC or eMMC
+    if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB ||
+        OcrResponse.Raw == EMMC_CMD1_CAPACITY_LESS_THAN_2GB) {
+      return EmmcIdentificationMode (MmcHostInstance, OcrResponse);
+    }
+  }
+
+  // Are we using SDIO ?
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD5, 0);
+  if (Status == EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD5): Error - SDIO not supported, Status=%r.\n", Status));
+    return EFI_UNSUPPORTED;
+  }
+
+  // Check which kind of card we are using. Ver2.00 or later SD Memory Card (PL180 is SD v1.1)
+  CmdArg = (0x0UL << 12 | BIT8 | 0xCEUL << 0);
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD8, CmdArg);
+  if (Status == EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "Card is SD2.0 => Supports high capacity\n"));
+    IsHCS = TRUE;
+    Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R7, Response);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive response to CMD8, Status=%r.\n", Status));
+      return Status;
+    }
+    PrintResponseR1 (Response[0]);
+    // Check if it is valid response
+    if (Response[0] != CmdArg) {
+      DEBUG ((DEBUG_ERROR, "The Card is not usable\n"));
+      return EFI_UNSUPPORTED;
+    }
+  } else {
+    DEBUG ((DEBUG_ERROR, "Not a SD2.0 Card\n"));
+  }
+
+  // We need to wait for the MMC or SD card is ready => (gCardInfo.OCRData.PowerUp == 1)
+  Timeout = MAX_RETRY_COUNT;
+  while (Timeout > 0) {
+    // SD Card or MMC Card ? CMD55 indicates to the card that the next command is an application specific command
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, 0);
+    if (Status == EFI_SUCCESS) {
+      DEBUG ((DEBUG_INFO, "Card should be SD\n"));
+      if (IsHCS) {
+        MmcHostInstance->CardInfo.CardType = SD_CARD_2;
+      } else {
+        MmcHostInstance->CardInfo.CardType = SD_CARD;
+      }
+
+      // Note: The first time CmdArg will be zero
+      CmdArg = ((UINTN *) &(MmcHostInstance->CardInfo.OCRData))[0];
+      if (IsHCS) {
+        CmdArg |= BIT30;
+      }
+      Status = MmcHost->SendCommand (MmcHost, MMC_ACMD41, CmdArg);
+      if (!EFI_ERROR (Status)) {
+        Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
+          return Status;
+        }
+        ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];
+      }
+    } else {
+      DEBUG ((DEBUG_INFO, "Card should be MMC\n"));
+      MmcHostInstance->CardInfo.CardType = MMC_CARD;
+
+      Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, 0x800000);
+      if (!EFI_ERROR (Status)) {
+        Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
+          return Status;
+        }
+        ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];
+      }
+    }
+
+    if (!EFI_ERROR (Status)) {
+      if (!MmcHostInstance->CardInfo.OCRData.PowerUp) {
+        gBS->Stall (1);
+        Timeout--;
+      } else {
+        if ((MmcHostInstance->CardInfo.CardType == SD_CARD_2) && (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1)) {
+          MmcHostInstance->CardInfo.CardType = SD_CARD_2_HIGH;
+          DEBUG ((DEBUG_ERROR, "High capacity card.\n"));
+        }
+        break;  // The MMC/SD card is ready. Continue the Identification Mode
+      }
+    } else {
+      gBS->Stall (1);
+      Timeout--;
+    }
+  }
+
+  if (Timeout == 0) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(): No Card\n"));
+    return EFI_NO_MEDIA;
+  } else {
+    PrintOCR (Response[0]);
+  }
+
+  Status = MmcNotifyState (MmcHostInstance, MmcReadyState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcReadyState\n"));
+    return Status;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD2, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD2): Error\n"));
+    return Status;
+  }
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CID, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive CID, Status=%r.\n", Status));
+    return Status;
+  }
+
+  PrintCID (Response);
+
+  Status = MmcHost->NotifyState (MmcHost, MmcIdentificationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcIdentificationState\n"));
+    return Status;
+  }
+
+  //
+  // Note, SD specifications say that "if the command execution causes a state change, it
+  // will be visible to the host in the response to the next command"
+  // The status returned for this CMD3 will be 2 - identification
+  //
+  CmdArg = 1;
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD3, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD3): Error\n"));
+    return Status;
+  }
+
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_RCA, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive RCA, Status=%r.\n", Status));
+    return Status;
+  }
+  PrintRCA (Response[0]);
+
+  // For MMC card, RCA is assigned by CMD3 while CMD3 dumps the RCA for SD card
+  if (MmcHostInstance->CardInfo.CardType != MMC_CARD) {
+    MmcHostInstance->CardInfo.RCA = Response[0] >> 16;
+  } else {
+    MmcHostInstance->CardInfo.RCA = CmdArg;
+  }
+  Status = MmcNotifyState (MmcHostInstance, MmcStandByState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcStandByState\n"));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+InitializeMmcDevice (
+  IN  MMC_HOST_INSTANCE   *MmcHostInstance
+  )
+{
+  EFI_STATUS              Status;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  UINTN                   BlockCount;
+
+  BlockCount = 1;
+  MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcIdentificationMode (MmcHostInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "InitializeMmcDevice(): Error in Identification Mode, Status=%r\n", Status));
+    return Status;
+  }
+
+  Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "InitializeMmcDevice(): Error MmcTransferState, Status=%r\n", Status));
+    return Status;
+  }
+
+  if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {
+    Status = InitializeSdMmcDevice (MmcHostInstance);
+  } else {
+    Status = InitializeEmmcDevice (MmcHostInstance);
+  }
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Set Block Length
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD16, MmcHostInstance->BlockIo.Media->BlockSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "InitializeMmcDevice(MMC_CMD16): Error MmcHostInstance->BlockIo.Media->BlockSize: %d and Error = %r\n",
+                        MmcHostInstance->BlockIo.Media->BlockSize, Status));
+    return Status;
+  }
+
+  // Block Count (not used). Could return an error for SD card
+  if (MmcHostInstance->CardInfo.CardType == MMC_CARD) {
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD23, BlockCount);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "InitializeMmcDevice(MMC_CMD23): Error, Status=%r\n", Status));
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h b/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h
new file mode 100644
index 000000000000..dfad80c719e1
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h
@@ -0,0 +1,53 @@
+/** @file
+ *
+ *  Copyright (c) 2015-2016, Linaro. All rights reserved.
+ *  Copyright (c) 2015-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.
+ *
+ **/
+
+#ifndef __DW_USB_H__
+#define __DW_USB_H__
+
+//
+// Protocol GUID
+//
+#define DW_USB_PROTOCOL_GUID { 0x109fa264, 0x7811, 0x4862, { 0xa9, 0x73, 0x4a, 0xb2, 0xef, 0x2e, 0xe2, 0xff }}
+
+//
+// Protocol interface structure
+//
+typedef struct _DW_USB_PROTOCOL  DW_USB_PROTOCOL;
+
+#define USB_HOST_MODE    0
+#define USB_DEVICE_MODE  1
+#define USB_CABLE_NOT_ATTACHED  2
+
+typedef
+EFI_STATUS
+(EFIAPI *DW_USB_GET_SERIAL_NO) (
+  OUT CHAR16                           *SerialNo,
+  OUT UINT8                            *Length
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *DW_USB_PHY_INIT) (
+  IN UINT8                           Mode
+  );
+
+struct _DW_USB_PROTOCOL {
+  DW_USB_GET_SERIAL_NO                 Get;
+  DW_USB_PHY_INIT                      PhyInit;
+};
+
+extern EFI_GUID gDwUsbProtocolGuid;
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h b/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h
new file mode 100644
index 000000000000..c0e65687e8d4
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h
@@ -0,0 +1,187 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2011-2014, ARM 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 __PI_MMC_HOST_H__
+#define __PI_MMC_HOST_H__
+
+///
+/// Global ID for the MMC Host Protocol
+///
+#define RASPBERRY_PI_MMC_HOST_PROTOCOL_GUID \
+  { 0x3e591c00, 0x9e4a, 0x11df, {0x92, 0x44, 0x00, 0x02, 0xA5, 0xF5, 0xF5, 0x1B } }
+
+#define MMC_RESPONSE_TYPE_R1        0
+#define MMC_RESPONSE_TYPE_R1b       0
+#define MMC_RESPONSE_TYPE_R2        1
+#define MMC_RESPONSE_TYPE_R3        0
+#define MMC_RESPONSE_TYPE_R6        0
+#define MMC_RESPONSE_TYPE_R7        0
+#define MMC_RESPONSE_TYPE_OCR       0
+#define MMC_RESPONSE_TYPE_CID       1
+#define MMC_RESPONSE_TYPE_CSD       1
+#define MMC_RESPONSE_TYPE_RCA       0
+
+typedef UINT32  MMC_RESPONSE_TYPE;
+
+typedef UINT32 MMC_CMD;
+
+#define MMC_CMD_WAIT_RESPONSE      (1 << 16)
+#define MMC_CMD_LONG_RESPONSE      (1 << 17)
+#define MMC_CMD_NO_CRC_RESPONSE    (1 << 18)
+
+#define MMC_INDX(Index)       ((Index) & 0xFFFF)
+#define MMC_GET_INDX(MmcCmd)  ((MmcCmd) & 0xFFFF)
+
+#define MMC_CMD0              (MMC_INDX(0) | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD1              (MMC_INDX(1) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD2              (MMC_INDX(2) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE)
+#define MMC_CMD3              (MMC_INDX(3) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD5              (MMC_INDX(5) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD6              (MMC_INDX(6) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD7              (MMC_INDX(7) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD8              (MMC_INDX(8) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD9              (MMC_INDX(9) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE)
+#define MMC_CMD11             (MMC_INDX(11) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD12             (MMC_INDX(12) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD13             (MMC_INDX(13) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD16             (MMC_INDX(16) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD17             (MMC_INDX(17) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD18             (MMC_INDX(18) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD20             (MMC_INDX(20) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD23             (MMC_INDX(23) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD24             (MMC_INDX(24) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD25             (MMC_INDX(25) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD55             (MMC_INDX(55) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_ACMD22            (MMC_INDX(22) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_ACMD41            (MMC_INDX(41) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_ACMD51            (MMC_INDX(51) | MMC_CMD_WAIT_RESPONSE)
+
+// Valid responses for CMD1 in eMMC
+#define EMMC_CMD1_CAPACITY_LESS_THAN_2GB 0x00FF8080 // Capacity <= 2GB, byte addressing used
+#define EMMC_CMD1_CAPACITY_GREATER_THAN_2GB 0x40FF8080 // Capacity > 2GB, 512-byte sector addressing used
+
+#define MMC_STATUS_APP_CMD    (1 << 5)
+
+typedef enum _MMC_STATE {
+    MmcInvalidState = 0,
+    MmcHwInitializationState,
+    MmcIdleState,
+    MmcReadyState,
+    MmcIdentificationState,
+    MmcStandByState,
+    MmcTransferState,
+    MmcSendingDataState,
+    MmcReceiveDataState,
+    MmcProgrammingState,
+    MmcDisconnectState,
+} MMC_STATE;
+
+#define EMMCBACKWARD         (0)
+#define EMMCHS26             (1 << 0)      // High-Speed @26MHz at rated device voltages
+#define EMMCHS52             (1 << 1)      // High-Speed @52MHz at rated device voltages
+#define EMMCHS52DDR1V8       (1 << 2)      // High-Speed Dual Data Rate @52MHz 1.8V or 3V I/O
+#define EMMCHS52DDR1V2       (1 << 3)      // High-Speed Dual Data Rate @52MHz 1.2V I/O
+#define EMMCHS200SDR1V8      (1 << 4)      // HS200 Single Data Rate @200MHz 1.8V I/O
+#define EMMCHS200SDR1V2      (1 << 5)      // HS200 Single Data Rate @200MHz 1.2V I/O
+#define EMMCHS400DDR1V8      (1 << 6)      // HS400 Dual Data Rate @400MHz 1.8V I/O
+#define EMMCHS400DDR1V2      (1 << 7)      // HS400 Dual Data Rate @400MHz 1.2V I/O
+
+///
+/// Forward declaration for EFI_MMC_HOST_PROTOCOL
+///
+typedef struct _EFI_MMC_HOST_PROTOCOL  EFI_MMC_HOST_PROTOCOL;
+
+typedef BOOLEAN (EFIAPI *MMC_ISCARDPRESENT) (
+  IN  EFI_MMC_HOST_PROTOCOL   *This
+  );
+
+typedef BOOLEAN (EFIAPI *MMC_ISREADONLY) (
+  IN  EFI_MMC_HOST_PROTOCOL   *This
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_BUILDDEVICEPATH) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePath
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_NOTIFYSTATE) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  MMC_STATE                 State
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_SENDCOMMAND) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  MMC_CMD                   Cmd,
+  IN  UINT32                    Argument
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_RECEIVERESPONSE) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  MMC_RESPONSE_TYPE         Type,
+  IN  UINT32                    *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_READBLOCKDATA) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  EFI_LBA                   Lba,
+  IN  UINTN                     Length,
+  OUT UINT32                    *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_WRITEBLOCKDATA) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  EFI_LBA                   Lba,
+  IN  UINTN                     Length,
+  IN  UINT32                    *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_SETIOS) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  UINT32                    BusClockFreq,
+  IN  UINT32                    BusWidth,
+  IN  UINT32                    TimingMode
+  );
+
+typedef BOOLEAN (EFIAPI *MMC_ISMULTIBLOCK) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This
+  );
+
+struct _EFI_MMC_HOST_PROTOCOL {
+  UINT32                  Revision;
+  MMC_ISCARDPRESENT       IsCardPresent;
+  MMC_ISREADONLY          IsReadOnly;
+  MMC_BUILDDEVICEPATH     BuildDevicePath;
+
+  MMC_NOTIFYSTATE         NotifyState;
+
+  MMC_SENDCOMMAND         SendCommand;
+  MMC_RECEIVERESPONSE     ReceiveResponse;
+
+  MMC_READBLOCKDATA       ReadBlockData;
+  MMC_WRITEBLOCKDATA      WriteBlockData;
+
+  MMC_SETIOS              SetIos;
+  MMC_ISMULTIBLOCK        IsMultiBlock;
+};
+
+#define MMC_HOST_PROTOCOL_REVISION    0x00010002    // 1.2
+
+#define MMC_HOST_HAS_SETIOS(Host)     (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \
+                                       Host->SetIos != NULL)
+#define MMC_HOST_HAS_ISMULTIBLOCK(Host) (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \
+                                         Host->IsMultiBlock != NULL)
+
+#endif
+
-- 
2.17.0.windows.1



More information about the edk2-devel mailing list