* [PATCH v2 1/3] EmbeddedPkg: add NonDiscoverableDeviceDxe driver
2018-08-15 7:36 [PATCH v2 0/3] add DwMmcHcDxe driver Haojian Zhuang
@ 2018-08-15 7:36 ` Haojian Zhuang
2018-08-15 7:36 ` [PATCH v2 2/3] EmbeddedPkg: add PlatformDwMmc protocol Haojian Zhuang
2018-08-15 7:36 ` [PATCH v2 3/3] EmbeddedPkg/Drivers: add DwMmcHcDxe driver Haojian Zhuang
2 siblings, 0 replies; 5+ messages in thread
From: Haojian Zhuang @ 2018-08-15 7:36 UTC (permalink / raw)
To: edk2-devel
It's used to create NonDiscoverableDevice in embedded platform. Since
there's no PCI bus.
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Chris Co <Christopher.Co@microsoft.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
---
EmbeddedPkg/EmbeddedPkg.dec | 1 +
EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf | 52 ++
EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h | 92 ++
EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c | 124 +++
EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c | 243 +++++
EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c | 972 ++++++++++++++++++++
6 files changed, 1484 insertions(+)
diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
index 28a143865d0e..6a80f31e95c0 100644
--- a/EmbeddedPkg/EmbeddedPkg.dec
+++ b/EmbeddedPkg/EmbeddedPkg.dec
@@ -85,6 +85,7 @@ [Protocols.common]
gPlatformGpioProtocolGuid = { 0x52ce9845, 0x5af4, 0x43e2, {0xba, 0xfd, 0x23, 0x08, 0x12, 0x54, 0x7a, 0xc2 }}
gPlatformVirtualKeyboardProtocolGuid = { 0x0e3606d2, 0x1dc3, 0x4e6f, { 0xbe, 0x65, 0x39, 0x49, 0x82, 0xa2, 0x65, 0x47 }}
gAndroidBootImgProtocolGuid = { 0x9859bb19, 0x407c, 0x4f8b, {0xbc, 0xe1, 0xf8, 0xda, 0x65, 0x65, 0xf4, 0xa5 }}
+ gEmbeddedNonDiscoverableIoProtocolGuid = { 0x6937742f, 0xf611, 0x4a40, { 0xb1, 0xc6, 0xe7, 0xb4, 0x6e, 0x3c, 0x6e, 0x32 }}
[Ppis]
gEdkiiEmbeddedGpioPpiGuid = { 0x21c3b115, 0x4e0b, 0x470c, { 0x85, 0xc7, 0xe1, 0x05, 0xa5, 0x75, 0xc9, 0x7b }}
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf
new file mode 100644
index 000000000000..b3f7c8bc2976
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf
@@ -0,0 +1,52 @@
+## @file
+# I/O driver for non-discoverable devices.
+#
+# Copyright (C) 2016-2018, Linaro Ltd.
+#
+# 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 = 0x0001001a
+ BASE_NAME = NonDiscoverableDeviceDxe
+ FILE_GUID = 66c8ca38-4c1e-4730-8c77-6c248ad89abd
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = NonDiscoverableDeviceDxeEntryPoint
+
+[Sources]
+ ComponentName.c
+ NonDiscoverableDeviceDxe.c
+ NonDiscoverableDeviceIo.c
+ NonDiscoverableDeviceIo.h
+
+[Packages]
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEdkiiNonDiscoverableDeviceProtocolGuid ## TO_START
+ gEfiCpuArchProtocolGuid ## CONSUMES
+ gEmbeddedNonDiscoverableIoProtocolGuid
+
+[Guids]
+ gEdkiiNonDiscoverableNvmeDeviceGuid ## CONSUMES ## GUID
+ gEdkiiNonDiscoverableSdhciDeviceGuid ## CONSUMES ## GUID
+ gEdkiiNonDiscoverableUfsDeviceGuid ## CONSUMES ## GUID
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h
new file mode 100644
index 000000000000..faa0bfcc17d4
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h
@@ -0,0 +1,92 @@
+/** @file
+
+ Copyright (C) 2016-2018, Linaro Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __NON_DISCOVERABLE_DEVICE_IO_H__
+#define __NON_DISCOVERABLE_DEVICE_IO_H__
+
+#include <Library/DebugLib.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/Cpu.h>
+#include <Protocol/DeviceIo.h>
+#include <Protocol/NonDiscoverableDevice.h>
+
+#define NON_DISCOVERABLE_IO_DEVICE_SIG SIGNATURE_32 ('N', 'D', 'I', 'D')
+
+#define NON_DISCOVERABLE_IO_DEVICE_FROM_IO(IoPointer) \
+ CR (IoPointer, NON_DISCOVERABLE_IO_DEVICE, Io, \
+ NON_DISCOVERABLE_IO_DEVICE_SIG)
+
+extern EFI_CPU_ARCH_PROTOCOL *mCpu;
+
+typedef struct {
+ //
+ // The linked-list next pointer
+ //
+ LIST_ENTRY List;
+ //
+ // The address of the uncached allocation
+ //
+ VOID *HostAddress;
+ //
+ // The number of pages in the allocation
+ //
+ UINTN NumPages;
+ //
+ // The attributes of the allocation
+ //
+ UINT64 Attributes;
+} NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION;
+
+typedef struct {
+ UINT32 Signature;
+ //
+ // The bound non-discoverable device protocol instance
+ //
+ NON_DISCOVERABLE_DEVICE *Device;
+ //
+ // The exposed I/O protocol instance.
+ //
+ EFI_DEVICE_IO_PROTOCOL Io;
+ //
+ // The I/O attributes for this device
+ //
+ UINT64 Attributes;
+ //
+ // Whether this device has been enabled
+ //
+ BOOLEAN Enabled;
+ //
+ // Linked list to keep track of uncached allocations performed
+ // on behalf of this device
+ //
+ LIST_ENTRY UncachedAllocationList;
+} NON_DISCOVERABLE_IO_DEVICE;
+
+/**
+ Initialize Io Protocol.
+
+ @param Device Point to NON_DISCOVERABLE_IO_DEVICE instance.
+
+**/
+VOID
+InitializeIoProtocol (
+ NON_DISCOVERABLE_IO_DEVICE *Device
+ );
+
+extern EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2;
+
+#endif /* __NON_DISCOVERABLE_DEVICE_IO_H__ */
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c
new file mode 100644
index 000000000000..613938697ee4
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c
@@ -0,0 +1,124 @@
+/** @file
+
+ Copyright (C) 2016-2018, Linaro Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/UefiLib.h>
+
+#include "NonDiscoverableDeviceIo.h"
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+ { "eng;en", L"I/O protocol emulation driver for non-discoverable devices" },
+ { NULL, NULL }
+};
+
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+
+/**
+ Retrieves a Unicode string that is the user readable name of the UEFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that 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.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gComponentName) // Iso639Language
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an UEFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param DeviceHandle 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 three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that 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.
+ @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableGetDeviceName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
+ &NonDiscoverableGetDriverName,
+ &NonDiscoverableGetDeviceName,
+ "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &NonDiscoverableGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &NonDiscoverableGetDeviceName,
+ "en" // SupportedLanguages, RFC 4646 language codes
+};
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c
new file mode 100644
index 000000000000..654b33002346
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c
@@ -0,0 +1,243 @@
+/** @file
+
+ Copyright (C) 2016-2018, Linaro Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/DriverBinding.h>
+
+#include "NonDiscoverableDeviceIo.h"
+
+
+EFI_CPU_ARCH_PROTOCOL *mCpu;
+
+//
+// We only support the following device types
+//
+STATIC
+CONST EFI_GUID * CONST
+SupportedNonDiscoverableDevices[] = {
+ &gEdkiiNonDiscoverableSdhciDeviceGuid,
+ &gEdkiiNonDiscoverableUfsDeviceGuid,
+};
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+// The implementation follows:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01
+// - 5.1.3.4 OpenProtocol() and CloseProtocol()
+// - UEFI Spec 2.3.1 + Errata C
+// - 6.3 Protocol Handler Services
+//
+
+/**
+ Supported function of Driver Binding protocol for this driver.
+ Test to see if this driver supports ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param DeviceHandle Handle of device to test.
+ @param RemainingDevicePath A pointer to the device path.
+ it should be ignored by device driver.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval other This driver does not support this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableIoDeviceSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ NON_DISCOVERABLE_DEVICE *Device;
+ EFI_STATUS Status;
+ INTN Idx;
+
+ Status = gBS->OpenProtocol (DeviceHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid, (VOID **)&Device,
+ This->DriverBindingHandle, DeviceHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EFI_UNSUPPORTED;
+ for (Idx = 0; Idx < ARRAY_SIZE (SupportedNonDiscoverableDevices); Idx++) {
+ if (CompareGuid (Device->Type, SupportedNonDiscoverableDevices [Idx])) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto CloseProtocol;
+ }
+
+CloseProtocol:
+ gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ return Status;
+}
+
+/**
+ This routine is called right after the .Supported() called and
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param DeviceHandle Handle of device to bind driver to.
+ @param RemainingDevicePath A pointer to the device path.
+ it should be ignored by device driver.
+
+ @retval EFI_SUCCESS This driver is added to this device.
+ @retval other Some error occurs when binding this driver to this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableIoDeviceStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+
+ Dev = AllocateZeroPool (sizeof *Dev);
+ if (Dev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->OpenProtocol (DeviceHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ (VOID **)&Dev->Device, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ goto FreeDev;
+ }
+
+ Dev->Signature = NON_DISCOVERABLE_IO_DEVICE_SIG;
+
+ InitializeIoProtocol (Dev);
+
+ Status = gBS->InstallProtocolInterface (
+ &DeviceHandle,
+ &gEmbeddedNonDiscoverableIoProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Dev->Io
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseProtocol;
+ }
+ return EFI_SUCCESS;
+
+CloseProtocol:
+ gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+FreeDev:
+ FreePool (Dev);
+
+ return Status;
+}
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param DeviceHandle Handle of device to stop driver on.
+ @param NumberOfChildren Not used.
+ @param ChildHandleBuffer Not used.
+
+ @retval EFI_SUCCESS This driver is removed from this device.
+ @retval other Some error occurs when removing this driver from this
+ device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableIoDeviceStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+
+ gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ return EFI_SUCCESS;
+}
+
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+ &NonDiscoverableIoDeviceSupported,
+ &NonDiscoverableIoDeviceStart,
+ &NonDiscoverableIoDeviceStop,
+ 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
+ NULL,
+ NULL
+};
+
+/**
+ Entry point of this driver.
+
+ @param ImageHandle Image handle this driver.
+ @param SystemTable Pointer to the System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurred when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+NonDiscoverableDeviceDxeEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
+ ASSERT_EFI_ERROR(Status);
+
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDriverBinding,
+ ImageHandle,
+ &gComponentName,
+ &gComponentName2
+ );
+}
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c
new file mode 100644
index 000000000000..178f10b216b9
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c
@@ -0,0 +1,972 @@
+/** @file
+
+ Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
+ Copyright (c) 2016-2018, Linaro, Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <PiDxe.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/IoMmu.h>
+#include <Protocol/NonDiscoverableDevice.h>
+
+#include "NonDiscoverableDeviceIo.h"
+
+typedef struct {
+ EFI_PHYSICAL_ADDRESS AllocAddress;
+ VOID *HostAddress;
+ EFI_IO_OPERATION_TYPE Operation;
+ UINTN NumberOfBytes;
+} NON_DISCOVERABLE_IO_DEVICE_MAP_INFO;
+
+/**
+ Enable a driver to access controller registers in the memory or I/O space.
+
+ @param Width Signifies the width of the memory or I/O operations.
+ @param Count The number of memory or I/O operations to perform.
+ @param DstStride The stride of the destination buffer.
+ @param Dst For read operations, the destination buffer to store
+ the results. For write operations, the destination
+ buffer to write data to.
+ @param SrcStride The stride of the source buffer.
+ @param Src For read operations, the source buffer to read data
+ from. For write operations, the source buffer to write
+ data from.
+
+ @retval EFI_SUCCESS The data was read from or written to the
+ controller.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMemRW (
+ IN EFI_IO_WIDTH Width,
+ IN UINTN Count,
+ IN UINTN DstStride,
+ IN VOID *Dst,
+ IN UINTN SrcStride,
+ OUT CONST VOID *Src
+ )
+{
+ volatile UINT8 *Dst8;
+ volatile UINT16 *Dst16;
+ volatile UINT32 *Dst32;
+ volatile CONST UINT8 *Src8;
+ volatile CONST UINT16 *Src16;
+ volatile CONST UINT32 *Src32;
+
+ //
+ // Loop for each iteration and move the data
+ //
+ switch (Width & 0x3) {
+ case IO_UINT8:
+ Dst8 = (UINT8 *)Dst;
+ Src8 = (UINT8 *)Src;
+ for (;Count > 0; Count--, Dst8 += DstStride, Src8 += SrcStride) {
+ *Dst8 = *Src8;
+ }
+ break;
+ case IO_UINT16:
+ Dst16 = (UINT16 *)Dst;
+ Src16 = (UINT16 *)Src;
+ for (;Count > 0; Count--, Dst16 += DstStride, Src16 += SrcStride) {
+ *Dst16 = *Src16;
+ }
+ break;
+ case IO_UINT32:
+ Dst32 = (UINT32 *)Dst;
+ Src32 = (UINT32 *)Src;
+ for (;Count > 0; Count--, Dst32 += DstStride, Src32 += SrcStride) {
+ *Dst32 = *Src32;
+ }
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Enable a driver to access controller registers in the memory or I/O space.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Width Signifies the width of the memory or I/O
+ operations.
+ @param Offset The offset to start the memory or I/O operation.
+ @param Count The number of memory or I/O operations to
+ perform.
+ @param Buffer For read operations, the destination buffer to
+ store the results. For write operations, the
+ source buffer to write data from.
+
+ @retval EFI_SUCCESS The data was read from or written to the
+ controller.
+ @retval EFI_UNSUPPORTED The address range specified by Offset, Width,
+ and Count is not valid.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMemRead (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_IO_WIDTH Width,
+ IN UINT64 Offset,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ UINTN AlignMask;
+ UINT64 Address;
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+ Desc = Dev->Device->Resources;
+ Address = Desc->AddrRangeMin + Offset;
+
+ if (Address + (Count << (Width & 0x3)) > Desc->AddrRangeMax) {
+ return EFI_UNSUPPORTED;
+ }
+
+ AlignMask = (1 << (Width & 0x03)) - 1;
+ if ((UINTN)Address & AlignMask) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Width) {
+ case IO_UINT8:
+ case IO_UINT16:
+ case IO_UINT32:
+ case IO_UINT64:
+ return IoMemRW (Width, Count, 1, Buffer, 1, (VOID *)Address);
+ default:
+ break;
+ }
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Enable a driver to access controller registers in the memory or I/O space.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Width Signifies the width of the memory or I/O
+ operations.
+ @param Offset The offset to start the memory or I/O operation.
+ @param Count The number of memory or I/O operations to
+ perform.
+ @param Buffer For read operations, the destination buffer to
+ store the results. For write operations, the
+ source buffer to write data from.
+
+ @retval EFI_SUCCESS The data was read from or written to the
+ controller.
+ @retval EFI_UNSUPPORTED The address range specified by Offset, Width,
+ and Count is not valid.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMemWrite (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_IO_WIDTH Width,
+ IN UINT64 Offset,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ UINTN AlignMask;
+ UINT64 Address;
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+ Desc = Dev->Device->Resources;
+
+ Address = Desc->AddrRangeMin + Offset;
+ if (Address + (Count << (Width & 0x3)) > Desc->AddrRangeMax) {
+ return EFI_UNSUPPORTED;
+ }
+
+ AlignMask = (1 << (Width & 0x03)) - 1;
+ if ((UINTN)Address & AlignMask) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Width) {
+ case IO_UINT8:
+ case IO_UINT16:
+ case IO_UINT32:
+ case IO_UINT64:
+ return IoMemRW (Width, Count, 1, (VOID *)Address, 1, Buffer);
+ default:
+ break;
+ }
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Enable a driver to access controller registers in the memory or I/O space.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Width Signifies the width of the memory or I/O
+ operations.
+ @param Offset The offset to start the memory or I/O operation.
+ @param Count The number of memory or I/O operations to
+ perform.
+ @param Buffer For read operations, the destination buffer
+ to store the results. For write operations,
+ the source buffer to write data from.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoIoRead (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_IO_WIDTH Width,
+ IN UINT64 Offset,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Enable a driver to access controller registers in the memory or I/O space.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Width Signifies the width of the memory or I/O
+ operations.
+ @param Offset The offset to start the memory or I/O operation.
+ @param Count The number of memory or I/O operations to
+ perform.
+ @param Buffer For read operations, the destination buffer to
+ store the results. For write operations, the
+ source buffer to write data from.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoIoWrite (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_IO_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Provides the controller-specific addresses needed to access system memory.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Operation Indicates if the bus master is going to read
+ or write to system memory.
+ @param HostAddress The system memory address to map to the
+ controller.
+ @param NumberOfBytes On input the number of bytes to map. On output
+ the number of bytes that were mapped.
+ @param DeviceAddress The resulting map address for the bus master
+ controller to use to access the hosts
+ HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned
+ NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
+ buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested
+ address.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoMap (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_IO_OPERATION_TYPE Operation,
+ IN EFI_PHYSICAL_ADDRESS *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo;
+
+ //
+ // If HostAddress exceeds 4 GB, and this device does not support 64-bit DMA
+ // addressing, we need to allocate a bounce buffer and copy over the data.
+ //
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+ if ((EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB) {
+ //
+ // Bounce buffering is not possible for consistent mappings
+ //
+ if (Operation == EfiBusMasterCommonBuffer) {
+ return EFI_UNSUPPORTED;
+ }
+
+ MapInfo = AllocatePool (sizeof *MapInfo);
+ if (MapInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MapInfo->AllocAddress = MAX_UINT32;
+ MapInfo->HostAddress = HostAddress;
+ MapInfo->Operation = Operation;
+ MapInfo->NumberOfBytes = *NumberOfBytes;
+
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiBootServicesData,
+ EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+ &MapInfo->AllocAddress
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // If we fail here, it is likely because the system has no memory below
+ // 4 GB to begin with. There is not much we can do about that other than
+ // fail the map request.
+ //
+ FreePool (MapInfo);
+ return EFI_DEVICE_ERROR;
+ }
+ if (Operation == EfiBusMasterRead) {
+ gBS->CopyMem (
+ (VOID *)(UINTN)MapInfo->AllocAddress,
+ HostAddress,
+ *NumberOfBytes
+ );
+ }
+ *DeviceAddress = MapInfo->AllocAddress;
+ *Mapping = MapInfo;
+ } else {
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+ *Mapping = NULL;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoUnmap (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo;
+
+ MapInfo = Mapping;
+ if (MapInfo != NULL) {
+ if (MapInfo->Operation == EfiBusMasterWrite) {
+ gBS->CopyMem (
+ MapInfo->HostAddress,
+ (VOID *)(UINTN)MapInfo->AllocAddress,
+ MapInfo->NumberOfBytes
+ );
+ }
+ gBS->FreePages (
+ MapInfo->AllocAddress,
+ EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes)
+ );
+ FreePool (MapInfo);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocates pages.
+
+ @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.
+ @param Type This parameter is not used and must be ignored.
+ @param MemoryType The type of memory to allocate,
+ EfiBootServicesData or EfiRuntimeServicesData.
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory
+ address of the allocated range.
+ @param Attributes The requested bit mask of attributes for the
+ allocated range.
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal
+ attribute bits are MEMORY_WRITE_COMBINE and
+ MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoAllocateBuffer (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ IN OUT EFI_PHYSICAL_ADDRESS *HostAddress
+ )
+{
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ EFI_ALLOCATE_TYPE AllocType;
+ EFI_PHYSICAL_ADDRESS AllocAddress;
+
+ //
+ // Allocate below 4 GB if the dual address cycle attribute has not
+ // been set. If the system has no memory available below 4 GB, there
+ // is little we can do except propagate the error.
+ //
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+ if ((Dev->Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
+ AllocAddress = MAX_UINT32;
+ AllocType = AllocateMaxAddress;
+ } else {
+ AllocType = AllocateAnyPages;
+ }
+
+ Status = gBS->AllocatePages (AllocType, MemoryType, Pages, &AllocAddress);
+ if (!EFI_ERROR (Status)) {
+ *HostAddress = AllocAddress;
+ }
+ return Status;
+}
+
+/**
+ Frees memory that was allocated in function CoherentIoAllocateBuffer ().
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the
+ allocated range.
+
+ @retval EFI_SUCCESS The requested memory pages were freed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoFreeBuffer (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN UINTN Pages,
+ IN EFI_PHYSICAL_ADDRESS HostAddress
+ )
+{
+ FreePages ((VOID *)HostAddress, Pages);
+ return EFI_SUCCESS;
+}
+
+/**
+ Frees memory that was allocated in function NonCoherentIoAllocateBuffer ().
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated
+ range.
+
+ @retval EFI_SUCCESS The requested memory pages were freed.
+ @retval others The operation contain some errors.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoFreeBuffer (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN UINTN Pages,
+ IN EFI_PHYSICAL_ADDRESS HostAddress
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;
+ BOOLEAN Found;
+
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+ Found = FALSE;
+ Alloc = NULL;
+
+ //
+ // Find the uncached allocation list entry associated
+ // with this allocation
+ //
+ for (Entry = Dev->UncachedAllocationList.ForwardLink;
+ Entry != &Dev->UncachedAllocationList;
+ Entry = Entry->ForwardLink) {
+
+ Alloc = BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION, List);
+ if (Alloc->HostAddress == (VOID *)HostAddress && Alloc->NumPages == Pages) {
+ //
+ // We are freeing the exact allocation we were given
+ // before by AllocateBuffer()
+ //
+ Found = TRUE;
+ break;
+ }
+ }
+
+ if (!Found) {
+ ASSERT_EFI_ERROR (EFI_NOT_FOUND);
+ return EFI_NOT_FOUND;
+ }
+
+ RemoveEntryList (&Alloc->List);
+
+ Status = gDS->SetMemorySpaceAttributes (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ Alloc->Attributes);
+ if (EFI_ERROR (Status)) {
+ goto FreeAlloc;
+ }
+
+ //
+ // If we fail to restore the original attributes, it is better to leak the
+ // memory than to return it to the heap
+ //
+ FreePages ((VOID *)HostAddress, Pages);
+
+FreeAlloc:
+ FreePool (Alloc);
+ return Status;
+}
+
+/**
+ Allocates pages.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Type This parameter is not used and must be ignored.
+ @param MemoryType The type of memory to allocate,
+ EfiBootServicesData or EfiRuntimeServicesData.
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory
+ address of the allocated range.
+ @param Attributes The requested bit mask of attributes for the
+ allocated range.
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal
+ attribute bits are MEMORY_WRITE_COMBINE and
+ MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoAllocateBuffer (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ IN OUT EFI_PHYSICAL_ADDRESS *HostAddress
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
+ EFI_STATUS Status;
+ UINT64 MemType;
+ NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;
+ EFI_PHYSICAL_ADDRESS AllocAddress;
+
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+ Status = CoherentIoAllocateBuffer (
+ This,
+ Type,
+ MemoryType,
+ Pages,
+ &AllocAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gDS->GetMemorySpaceDescriptor (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+ &GcdDescriptor
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeBuffer;
+ }
+
+ if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto FreeBuffer;
+ }
+
+ //
+ // Set the preferred memory attributes
+ //
+ if ((GcdDescriptor.Capabilities & EFI_MEMORY_UC) == 0) {
+ //
+ // Use write combining if it was requested, or if it is the only
+ // type supported by the region.
+ //
+ MemType = EFI_MEMORY_WC;
+ } else {
+ MemType = EFI_MEMORY_UC;
+ }
+
+ Alloc = AllocatePool (sizeof *Alloc);
+ if (Alloc == NULL) {
+ goto FreeBuffer;
+ }
+
+ Alloc->HostAddress = (VOID *)AllocAddress;
+ Alloc->NumPages = Pages;
+ Alloc->Attributes = GcdDescriptor.Attributes;
+
+ //
+ // Record this allocation in the linked list, so we
+ // can restore the memory space attributes later
+ //
+ InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List);
+
+ Status = gDS->SetMemorySpaceAttributes (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ MemType
+ );
+ if (EFI_ERROR (Status)) {
+ goto RemoveList;
+ }
+
+ Status = mCpu->FlushDataCache (
+ mCpu,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ EfiCpuFlushTypeInvalidate);
+ if (EFI_ERROR (Status)) {
+ goto RemoveList;
+ }
+
+ *HostAddress = AllocAddress;
+
+ return EFI_SUCCESS;
+
+RemoveList:
+ RemoveEntryList (&Alloc->List);
+ FreePool (Alloc);
+
+FreeBuffer:
+ CoherentIoFreeBuffer (This, Pages, AllocAddress);
+ return Status;
+}
+
+/**
+ Provides the controller-specific addresses needed to access system memory.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Operation Indicates if the bus master is going to read or
+ write to system memory.
+ @param HostAddress The system memory address to map to the
+ controller.
+ @param NumberOfBytes On input the number of bytes to map. On output
+ the number of bytes that were mapped.
+ @param DeviceAddress The resulting map address for the bus master
+ controller to use to access the hosts
+ HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned
+ NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
+ buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested
+ address.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoMap (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN EFI_IO_OPERATION_TYPE Operation,
+ IN EFI_PHYSICAL_ADDRESS *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE *Dev;
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo;
+ UINTN AlignMask;
+ EFI_PHYSICAL_ADDRESS AllocAddress;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
+ BOOLEAN Bounce;
+
+ MapInfo = AllocatePool (sizeof *MapInfo);
+ if (MapInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MapInfo->HostAddress = HostAddress;
+ MapInfo->Operation = Operation;
+ MapInfo->NumberOfBytes = *NumberOfBytes;
+
+ Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+ //
+ // If this device does not support 64-bit DMA addressing, we need to allocate
+ // a bounce buffer and copy over the data in case HostAddress >= 4 GB.
+ //
+ Bounce = ((Dev->Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB);
+
+ if (!Bounce) {
+ switch (Operation) {
+ case EfiBusMasterRead:
+ case EfiBusMasterWrite:
+ //
+ // For streaming DMA, it is sufficient if the buffer is aligned to
+ // the CPUs DMA buffer alignment.
+ //
+ AlignMask = mCpu->DmaBufferAlignment - 1;
+ if ((((UINTN) HostAddress | *NumberOfBytes) & AlignMask) == 0) {
+ break;
+ }
+ // fall through
+
+ case EfiBusMasterCommonBuffer:
+ //
+ // Check whether the host address refers to an uncached mapping.
+ //
+ Status = gDS->GetMemorySpaceDescriptor (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+ &GcdDescriptor
+ );
+ if (EFI_ERROR (Status) ||
+ (GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0) {
+ Bounce = TRUE;
+ }
+ break;
+
+ default:
+ ASSERT (FALSE);
+ }
+ }
+
+ if (Bounce) {
+ if (Operation == EfiBusMasterCommonBuffer) {
+ Status = EFI_DEVICE_ERROR;
+ goto FreeMapInfo;
+ }
+
+ Status = NonCoherentIoAllocateBuffer (
+ This,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+ &AllocAddress);
+ if (EFI_ERROR (Status)) {
+ goto FreeMapInfo;
+ }
+ MapInfo->AllocAddress = AllocAddress;
+ if (Operation == EfiBusMasterRead) {
+ gBS->CopyMem ((VOID *)AllocAddress, HostAddress, *NumberOfBytes);
+ }
+ *DeviceAddress = MapInfo->AllocAddress;
+ } else {
+ MapInfo->AllocAddress = 0;
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+
+ //
+ // We are not using a bounce buffer: the mapping is sufficiently
+ // aligned to allow us to simply flush the caches. Note that cleaning
+ // the caches is necessary for both data directions:
+ // - for bus master read, we want the latest data to be present
+ // in main memory
+ // - for bus master write, we don't want any stale dirty cachelines that
+ // may be written back unexpectedly, and clobber the data written to
+ // main memory by the device.
+ //
+ mCpu->FlushDataCache (
+ mCpu,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+ *NumberOfBytes,
+ EfiCpuFlushTypeWriteBack
+ );
+ }
+
+ *Mapping = MapInfo;
+ return EFI_SUCCESS;
+
+FreeMapInfo:
+ FreePool (MapInfo);
+
+ return Status;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param This A pointer to the EFI_DEVICE_IO_PROTOCOL
+ instance.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoUnmap (
+ IN EFI_DEVICE_IO_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo;
+
+ if (Mapping == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ MapInfo = Mapping;
+ if (MapInfo->AllocAddress != 0) {
+ //
+ // We are using a bounce buffer: copy back the data if necessary,
+ // and free the buffer.
+ //
+ if (MapInfo->Operation == EfiBusMasterWrite) {
+ gBS->CopyMem (
+ MapInfo->HostAddress,
+ (VOID *)(UINTN)MapInfo->AllocAddress,
+ MapInfo->NumberOfBytes
+ );
+ }
+ NonCoherentIoFreeBuffer (
+ This,
+ EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+ MapInfo->AllocAddress
+ );
+ } else {
+ //
+ // We are *not* using a bounce buffer: if this is a bus master write,
+ // we have to invalidate the caches so the CPU will see the uncached
+ // data written by the device.
+ //
+ if (MapInfo->Operation == EfiBusMasterWrite) {
+ mCpu->FlushDataCache (
+ mCpu,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress,
+ MapInfo->NumberOfBytes,
+ EfiCpuFlushTypeInvalidate
+ );
+ }
+ }
+ FreePool (MapInfo);
+ return EFI_SUCCESS;
+}
+
+STATIC CONST EFI_DEVICE_IO_PROTOCOL IoTemplate =
+{
+ { IoMemRead, IoMemWrite },
+ { IoIoRead, IoIoWrite },
+ { 0, 0 },
+ CoherentIoMap,
+ 0,
+ CoherentIoUnmap,
+ CoherentIoAllocateBuffer,
+ 0,
+ CoherentIoFreeBuffer,
+};
+
+/**
+ Initialize DevIo Protocol.
+
+ @param Dev Point to NON_DISCOVERABLE_IO_DEVICE instance.
+
+**/
+VOID
+InitializeIoProtocol (
+ NON_DISCOVERABLE_IO_DEVICE *Dev
+ )
+{
+ InitializeListHead (&Dev->UncachedAllocationList);
+
+ //
+ // Copy protocol structure
+ //
+ CopyMem(&Dev->Io, &IoTemplate, sizeof (IoTemplate));
+
+ if (Dev->Device->DmaType == NonDiscoverableDeviceDmaTypeNonCoherent) {
+ Dev->Io.AllocateBuffer = NonCoherentIoAllocateBuffer;
+ Dev->Io.FreeBuffer = NonCoherentIoFreeBuffer;
+ Dev->Io.Map = NonCoherentIoMap;
+ Dev->Io.Unmap = NonCoherentIoUnmap;
+ } else {
+ Dev->Io.AllocateBuffer = CoherentIoAllocateBuffer;
+ Dev->Io.FreeBuffer = CoherentIoFreeBuffer;
+ Dev->Io.Map = CoherentIoMap;
+ Dev->Io.Unmap = CoherentIoUnmap;
+ }
+}
--
2.7.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 3/3] EmbeddedPkg/Drivers: add DwMmcHcDxe driver
2018-08-15 7:36 [PATCH v2 0/3] add DwMmcHcDxe driver Haojian Zhuang
2018-08-15 7:36 ` [PATCH v2 1/3] EmbeddedPkg: add NonDiscoverableDeviceDxe driver Haojian Zhuang
2018-08-15 7:36 ` [PATCH v2 2/3] EmbeddedPkg: add PlatformDwMmc protocol Haojian Zhuang
@ 2018-08-15 7:36 ` Haojian Zhuang
2021-02-19 9:07 ` [edk2-devel] " Loh, Tien Hock
2 siblings, 1 reply; 5+ messages in thread
From: Haojian Zhuang @ 2018-08-15 7:36 UTC (permalink / raw)
To: edk2-devel
Add the driver of Designware eMMC/SD controller. It's based on
NonDiscoverableDeviceDxe driver.
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Chris Co <Christopher.Co@microsoft.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
---
EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec | 40 +
EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf | 69 +
EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h | 815 +++++++
EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h | 983 ++++++++
EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c | 214 ++
EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c | 1295 +++++++++++
EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c | 2366 ++++++++++++++++++++
EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c | 1042 +++++++++
EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c | 1104 +++++++++
9 files changed, 7928 insertions(+)
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec
new file mode 100644
index 000000000000..cf85ccb1a030
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec
@@ -0,0 +1,40 @@
+#/** @file
+# Framework Module Development Environment Industry Standards
+#
+# This Package provides headers and libraries that conform to EFI/PI Industry standards.
+# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR>
+# Copyright (c) 2018, Linaro. All rights reserved.<BR>
+#
+# This program and the accompanying materials are licensed and made available under
+# the terms and conditions of the BSD License which accompanies this distribution.
+# The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#**/
+
+[Defines]
+ DEC_SPECIFICATION = 0x00010019
+ PACKAGE_NAME = DwMmcHcDxePkg
+ PACKAGE_GUID = e73097ce-1fe2-41a6-a930-3136bc6d23ef
+ PACKAGE_VERSION = 0.1
+
+
+################################################################################
+#
+# Include Section - list of Include Paths that are provided by this package.
+# Comments are used for Keywords and Module Types.
+#
+# Supported Module Types:
+# BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
+#
+################################################################################
+
+[Guids.common]
+ gDwMmcHcDxeTokenSpaceGuid = { 0x576c132e, 0x7d51, 0x4abb, { 0xbc, 0x60, 0x13, 0x08, 0x04, 0x0e, 0x90, 0x92 }}
+
+[Protocols.common]
+ gPlatformDwMmcProtocolGuid = { 0x1d6dfde5, 0x76a7, 0x4404, { 0x85, 0x74, 0x7a, 0xdf, 0x1a, 0x8a, 0xa2, 0x0d }}
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf
new file mode 100644
index 000000000000..699de99e22b1
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf
@@ -0,0 +1,69 @@
+## @file
+# DwSdMmcHcDxe driver is used to manage those host controllers which comply with
+# Designware SD Host Controller.
+#
+# It will produce EFI_SD_MMC_PASS_THRU_PROTOCOL to allow sending SD/MMC/eMMC cmds
+# to specified devices from upper layer.
+#
+# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+# Copyright (C) 2016, Marvell International Ltd. All rights reserved.<BR>
+# Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = DwMmcHcDxe
+ MODULE_UNI_FILE = DwMmcHcDxe.uni
+ FILE_GUID = 9be4d260-208c-4efe-a524-0b5d3bf77f9d
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = InitializeDwMmcHcDxe
+
+[Sources]
+ ComponentName.c
+ DwMmcHcDxe.c
+ DwMmcHcDxe.h
+ DwMmcHci.c
+ DwMmcHci.h
+ EmmcDevice.c
+ SdDevice.c
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmLib
+ BaseLib
+ BaseMemoryLib
+ CacheMaintenanceLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ TimerLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+ UefiRuntimeServicesTableLib
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ## TO_START
+ gEfiPciIoProtocolGuid ## TO_START
+ gEfiSdMmcPassThruProtocolGuid ## BY_START
+ gEmbeddedNonDiscoverableIoProtocolGuid
+ gPlatformDwMmcProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ DwMmcHcDxeExtra.uni
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h
new file mode 100644
index 000000000000..d17604d51304
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h
@@ -0,0 +1,815 @@
+/** @file
+
+ Provides some data structure definitions used by the Designware SD/MMC
+ host controller driver.
+
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2018, Linaro Ltd. All rigths reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _DW_MMC_HC_DXE_H_
+#define _DW_MMC_HC_DXE_H_
+
+#include <Uefi.h>
+
+#include <Library/UefiLib.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/DeviceIo.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/SdMmcPassThru.h>
+
+#include "DwMmcHci.h"
+
+extern EFI_COMPONENT_NAME_PROTOCOL gDwMmcHcComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gDwMmcHcComponentName2;
+extern EFI_DRIVER_BINDING_PROTOCOL gDwMmcHcDriverBinding;
+
+#define DW_MMC_HC_PRIVATE_SIGNATURE SIGNATURE_32 ('d', 'w', 's', 'd')
+
+#define DW_MMC_HC_PRIVATE_FROM_THIS(a) \
+ CR(a, DW_MMC_HC_PRIVATE_DATA, PassThru, DW_MMC_HC_PRIVATE_SIGNATURE)
+
+//
+// Generic time out value, 1 microsecond as unit.
+//
+#define DW_MMC_HC_GENERIC_TIMEOUT (1 * 1000 * 1000)
+
+//
+// SD/MMC async transfer timer interval, set by experience.
+// The unit is 100us, takes 1ms as interval.
+//
+#define DW_MMC_HC_ASYNC_TIMER EFI_TIMER_PERIOD_MILLISECONDS(1)
+//
+// SD/MMC removable device enumeration timer interval, set by experience.
+// The unit is 100us, takes 100ms as interval.
+//
+#define DW_MMC_HC_ENUM_TIMER EFI_TIMER_PERIOD_MILLISECONDS(100)
+
+typedef struct {
+ BOOLEAN Enable;
+ EFI_SD_MMC_SLOT_TYPE SlotType;
+ BOOLEAN MediaPresent;
+ BOOLEAN Initialized;
+ SD_MMC_CARD_TYPE CardType;
+} DW_MMC_HC_SLOT;
+
+typedef struct {
+ UINTN Signature;
+
+ EFI_HANDLE ControllerHandle;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+
+ EFI_SD_MMC_PASS_THRU_PROTOCOL PassThru;
+
+ PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc;
+ //
+ // The field is used to record the previous slot in GetNextSlot().
+ //
+ UINT8 PreviousSlot;
+ //
+ // For Non-blocking operation.
+ //
+ EFI_EVENT TimerEvent;
+ //
+ // For Sd removable device enumeration.
+ //
+ EFI_EVENT ConnectEvent;
+ LIST_ENTRY Queue;
+
+ DW_MMC_HC_SLOT Slot[DW_MMC_HC_MAX_SLOT];
+ DW_MMC_HC_SLOT_CAP Capability[DW_MMC_HC_MAX_SLOT];
+ UINT64 MaxCurrent[DW_MMC_HC_MAX_SLOT];
+
+ UINT32 ControllerVersion;
+} DW_MMC_HC_PRIVATE_DATA;
+
+#define DW_MMC_HC_TRB_SIG SIGNATURE_32 ('D', 'T', 'R', 'B')
+
+//
+// TRB (Transfer Request Block) contains information for the cmd request.
+//
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY TrbList;
+
+ UINT8 Slot;
+ UINT16 BlockSize;
+
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ VOID *Data;
+ UINT32 DataLen;
+ BOOLEAN Read;
+ EFI_PHYSICAL_ADDRESS DataPhy;
+ VOID *DataMap;
+ DW_MMC_HC_TRANSFER_MODE Mode;
+
+ EFI_EVENT Event;
+ BOOLEAN Started;
+ UINT64 Timeout;
+
+ DW_MMC_HC_DMA_DESC_LINE *DmaDesc;
+ EFI_PHYSICAL_ADDRESS DmaDescPhy;
+ UINT32 DmaDescPages;
+ VOID *DmaMap;
+
+ BOOLEAN UseFifo;
+ BOOLEAN UseBE; // Big-endian
+
+ DW_MMC_HC_PRIVATE_DATA *Private;
+} DW_MMC_HC_TRB;
+
+#define DW_MMC_HC_TRB_FROM_THIS(a) \
+ CR(a, DW_MMC_HC_TRB, TrbList, DW_MMC_HC_TRB_SIG)
+
+//
+// Task for Non-blocking mode.
+//
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ UINT8 Slot;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ BOOLEAN IsStart;
+ EFI_EVENT Event;
+ UINT64 RetryTimes;
+ BOOLEAN InfiniteWait;
+ VOID *Map;
+ VOID *MapAddress;
+} DW_MMC_HC_QUEUE;
+
+//
+// Prototypes
+//
+/**
+ Execute card identification procedure.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+
+ @retval EFI_SUCCESS The card is identified correctly.
+ @retval Others The card can't be identified.
+
+**/
+typedef
+EFI_STATUS
+(*DWMMC_CARD_TYPE_DETECT_ROUTINE) (
+ IN DW_MMC_HC_PRIVATE_DATA *Private
+ );
+
+/**
+ Sends SD command to an SD card that is attached to the SD controller.
+
+ The PassThru() function sends the SD command specified by Packet to the SD
+ card specified by Slot.
+
+ If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned.
+
+ If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is
+ returned.
+
+ If Slot is not in a valid range for the SD controller, then
+ EFI_INVALID_PARAMETER is returned.
+
+ If Packet defines a data command but both InDataBuffer and OutDataBuffer are
+ NULL, EFI_INVALID_PARAMETER is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot The slot number of the SD card to send the
+ command to.
+ @param[in,out] Packet A pointer to the SD command data structure.
+ @param[in] Event If Event is NULL, blocking I/O is performed. If
+ Event is not NULL, then nonblocking I/O is
+ performed, and Event will be signaled when the
+ Packet completes.
+
+ @retval EFI_SUCCESS The SD Command Packet was sent by the host.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send
+ the SD command Packet.
+ @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is
+ invalid.
+ @retval EFI_INVALID_PARAMETER Packet defines a data command but both
+ InDataBuffer and OutDataBuffer are NULL.
+ @retval EFI_NO_MEDIA SD Device not present in the Slot.
+ @retval EFI_UNSUPPORTED The command described by the SD Command Packet
+ is not supported by the host controller.
+ @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength
+ exceeds the limit supported by SD card ( i.e. if
+ the number of bytes exceed the Last LBA).
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruPassThru (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN UINT8 Slot,
+ IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+/**
+ Used to retrieve next slot numbers supported by the SD controller. The
+ function returns information about all available slots (populated or
+ not-populated).
+
+ The GetNextSlot() function retrieves the next slot number on an SD controller.
+ If on input Slot is 0xFF, then the slot number of the first slot on the SD
+ controller is returned.
+
+ If Slot is a slot number that was returned on a previous call to
+ GetNextSlot(), then the slot number of the next slot on the SD controller is
+ returned.
+
+ If Slot is not 0xFF and Slot was not returned on a previous call to
+ GetNextSlot(), EFI_INVALID_PARAMETER is returned.
+
+ If Slot is the slot number of the last slot on the SD controller, then
+ EFI_NOT_FOUND is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in,out] Slot On input, a pointer to a slot number on the SD
+ controller.
+ On output, a pointer to the next slot number on
+ the SD controller.
+ An input value of 0xFF retrieves the first slot
+ number on the SD controller.
+
+ @retval EFI_SUCCESS The next slot number on the SD controller was
+ returned in Slot.
+ @retval EFI_NOT_FOUND There are no more slots on this SD controller.
+ @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a
+ previous call to GetNextSlot().
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruGetNextSlot (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 *Slot
+ );
+
+/**
+ Used to allocate and build a device path node for an SD card on the SD
+ controller.
+
+ The BuildDevicePath() function allocates and builds a single device node
+ for the SD
+ card specified by Slot.
+
+ If the SD card specified by Slot is not present on the SD controller, then
+ EFI_NOT_FOUND is returned.
+
+ If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned.
+
+ If there are not enough resources to allocate the device path node, then
+ EFI_OUT_OF_RESOURCES is returned.
+
+ Otherwise, DevicePath is allocated with the boot service AllocatePool(), the
+ contents of DevicePath are initialized to describe the SD card specified by
+ Slot, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot Specifies the slot number of the SD card for
+ which a device path node is to be allocated and
+ built.
+ @param[in,out] DevicePath A pointer to a single device path node that
+ describes the SD card specified by Slot. This
+ function is responsible for allocating the
+ buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's responsibi-
+ lity to free DevicePath when the caller is
+ finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SD card
+ specified by Slot was allocated and returned in
+ DevicePath.
+ @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on
+ the SD controller.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate
+ DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruBuildDevicePath (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN UINT8 Slot,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ This function retrieves an SD card slot number based on the input device path.
+
+ The GetSlotNumber() function retrieves slot number for the SD card specified
+ by the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is
+ returned.
+
+ If DevicePath is not a device path node type that the SD Pass Thru driver
+ supports, EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] DevicePath A pointer to the device path node that describes
+ a SD card on the SD controller.
+ @param[out] Slot On return, points to the slot number of an SD
+ card on the SD controller.
+
+ @retval EFI_SUCCESS SD card slot number is returned in Slot.
+ @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL.
+ @retval EFI_UNSUPPORTED DevicePath is not a device path node type that
+ the SD Pass Thru driver supports.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruGetSlotNumber (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 *Slot
+ );
+
+/**
+ Resets an SD card that is connected to the SD controller.
+
+ The ResetDevice() function resets the SD card specified by Slot.
+
+ If this SD controller does not support a device reset operation,
+ EFI_UNSUPPORTED is returned.
+
+ If Slot is not in a valid slot number for this SD controller,
+ EFI_INVALID_PARAMETER is returned.
+
+ If the device reset operation is completed, EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot Specifies the slot number of the SD card to be
+ reset.
+
+ @retval EFI_SUCCESS The SD card specified by Slot was reset.
+ @retval EFI_UNSUPPORTED The SD controller does not support a device
+ reset operation.
+ @retval EFI_INVALID_PARAMETER Slot number is invalid.
+ @retval EFI_NO_MEDIA SD Device not present in the Slot.
+ @retval EFI_DEVICE_ERROR The reset command failed due to a device error
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruResetDevice (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN UINT8 Slot
+ );
+
+//
+// Driver model protocol interfaces
+//
+/**
+ Tests to see if this driver supports a given controller. If a child device is
+ provided, it further tests to see if this driver supports creating a handle
+ for the specified child device.
+
+ This function checks to see if the driver specified by This supports the
+ device specified by ControllerHandle. Drivers will typically use the device
+ path attached to ControllerHandle and/or the services from the bus I/O
+ abstraction attached to ControllerHandle to determine if the driver supports
+ ControllerHandle. This function may be called many times during platform
+ initialization. In order to reduce boot times, the tests performed by this
+ function must be very small, and take as little time as possible to execute.
+ This function must not change the state of any hardware devices, and this
+ function must be aware that the device specified by ControllerHandle may
+ already be managed by the same driver or a different driver. This function
+ must match its calls to AllocatePages() with FreePages(), AllocatePool() with
+ FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if
+ a protocol is already in the opened state, then it must not be closed with
+ CloseProtocol(). This is required to guarantee the state of ControllerHandle
+ is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to test. This
+ handle must support a protocol interface that
+ supplies an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter
+ is not NULL, then the bus driver must deter-
+ mine if the bus controller specified by
+ ControllerHandle and the child controller
+ specified by RemainingDevicePath are both
+ supported by this bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the
+ driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by a different driver or an application that
+ requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the
+ driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service
+ ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been
+ moved into this common boot service. It is legal to call Start() from other
+ locations,
+ but the following calling restrictions must be followed or the system behavior
+ will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a natural-
+ ly aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified
+ by This must have been called with the same calling parameters, and
+ Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to start. This
+ handle must support a protocol interface that
+ supplies an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus dri-
+ vers. For a bus driver, if this parameter is
+ NULL, then handles for all the children of
+ Controller are created by this driver.
+ If this parameter is not NULL and the first
+ Device Path Node is not the End of Device
+ Path Node, then only the handle for the
+ child device specified by the first Device
+ Path Node of RemainingDevicePath is created
+ by this driver.
+ If the first Device Path Node of
+ RemainingDevicePath is the End of Device Path
+ Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a
+ device error. Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service
+ DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been
+ moved into this common boot service. It is legal to call Stop() from other
+ locations, but the following calling restrictions must be followed or the
+ system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
+ call to this same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in
+ this driver's Start() function, and the Start() function must have called
+ OpenProtocol() on ControllerHandle with an Attribute of
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle
+ must support a bus specific I/O protocol for the
+ driver to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in
+ ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be
+ NULL if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+//
+// EFI Component Name Functions
+//
+/**
+ 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[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] 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[out] 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
+DwMmcHcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ 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[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] 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[in] 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[in] 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[out] 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
+DwMmcHcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+/**
+ Create a new TRB for the SD/MMC cmd request.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Slot The slot number of the SD card to send the command
+ to.
+ @param[in] Packet A pointer to the SD command data structure.
+ @param[in] Event If Event is NULL, blocking I/O is performed.
+ If Event is not NULL, then nonblocking I/O is
+ performed, and Event will be signaled when the
+ Packet completes.
+
+ @return Created Trb or NULL.
+
+**/
+DW_MMC_HC_TRB *
+DwMmcCreateTrb (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN UINT8 Slot,
+ IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event
+ );
+
+/**
+ Free the resource used by the TRB.
+
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+**/
+VOID
+DwMmcFreeTrb (
+ IN DW_MMC_HC_TRB *Trb
+ );
+
+/**
+ Check if the env is ready for execute specified TRB.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The env is ready for TRB execution.
+ @retval EFI_NOT_READY The env is not ready for TRB execution.
+ @retval Others Some erros happen.
+
+**/
+EFI_STATUS
+DwMmcCheckTrbEnv (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ );
+
+/**
+ Wait for the env to be ready for execute specified TRB.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The env is ready for TRB execution.
+ @retval EFI_TIMEOUT The env is not ready for TRB execution in time.
+ @retval Others Some erros happen.
+
+**/
+EFI_STATUS
+DwMmcWaitTrbEnv (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ );
+
+/**
+ Execute the specified TRB.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The TRB is sent to host controller successfully.
+ @retval Others Some erros happen when sending this request to the
+ host controller.
+
+**/
+EFI_STATUS
+DwMmcExecTrb (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ );
+
+/**
+ Check the TRB execution result.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The TRB is executed successfully.
+ @retval EFI_NOT_READY The TRB is not completed for execution.
+ @retval Others Some erros happen when executing this request.
+
+**/
+EFI_STATUS
+DwMmcCheckTrbResult (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ );
+
+/**
+ Wait for the TRB execution result.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The TRB is executed successfully.
+ @retval Others Some erros happen when executing this request.
+
+**/
+EFI_STATUS
+DwMmcWaitTrbResult (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ );
+
+/**
+ Execute EMMC device identification procedure.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+
+ @retval EFI_SUCCESS There is a EMMC card.
+ @retval Others There is not a EMMC card.
+
+**/
+EFI_STATUS
+EmmcIdentification (
+ IN DW_MMC_HC_PRIVATE_DATA *Private
+ );
+
+/**
+ Execute EMMC device identification procedure.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+
+ @retval EFI_SUCCESS There is a EMMC card.
+ @retval Others There is not a EMMC card.
+
+**/
+EFI_STATUS
+SdCardIdentification (
+ IN DW_MMC_HC_PRIVATE_DATA *Private
+ );
+
+#endif /* _DW_MMC_HC_DXE_H_ */
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h
new file mode 100644
index 000000000000..e4fe26e7d2e8
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h
@@ -0,0 +1,983 @@
+/** @file
+
+ Provides some data structure definitions used by the SD/MMC host controller
+ driver.
+
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _DW_MMC_HCI_H_
+#define _DW_MMC_HCI_H_
+
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/TimerLib.h>
+
+#include <Protocol/PlatformDwMmc.h>
+
+//
+// SD Host Controller SlotInfo Register Offset
+//
+#define DW_MMC_HC_SLOT_OFFSET 0x40
+
+#define DW_MMC_HC_MAX_SLOT 1
+
+//
+// SD Host Controller MMIO Register Offset
+//
+#define DW_MMC_CTRL 0x000
+#define DW_MMC_PWREN 0x004
+#define DW_MMC_CLKDIV 0x008
+#define DW_MMC_CLKSRC 0x00c
+#define DW_MMC_CLKENA 0x010
+#define DW_MMC_TMOUT 0x014
+#define DW_MMC_CTYPE 0x018
+#define DW_MMC_BLKSIZ 0x01c
+#define DW_MMC_BYTCNT 0x020
+#define DW_MMC_INTMASK 0x024
+#define DW_MMC_CMDARG 0x028
+#define DW_MMC_CMD 0x02c
+#define DW_MMC_RESP0 0x030
+#define DW_MMC_RESP1 0x034
+#define DW_MMC_RESP2 0x038
+#define DW_MMC_RESP3 0x03c
+#define DW_MMC_RINTSTS 0x044
+#define DW_MMC_STATUS 0x048
+#define DW_MMC_FIFOTH 0x04c
+#define DW_MMC_GPIO 0x058
+#define DW_MMC_DEBNCE 0x064
+#define DW_MMC_USRID 0x068
+#define DW_MMC_VERID 0x06c
+#define DW_MMC_HCON 0x070
+#define DW_MMC_UHSREG 0x074
+#define DW_MMC_BMOD 0x080
+#define DW_MMC_DBADDR 0x088
+#define DW_MMC_IDSTS 0x08c
+#define DW_MMC_IDINTEN 0x090
+#define DW_MMC_DSCADDR 0x094
+#define DW_MMC_BUFADDR 0x098
+#define DW_MMC_CARDTHRCTL 0x100
+#define DW_MMC_UHSREG_EXT 0x108
+#define DW_MMC_ENABLE_SHIFT 0x110
+#define DW_MMC_FIFO_START 0x200
+
+#define GET_IDSTS_DMAC_FSM(x) (((x) >> 13) & 0xf)
+#define IDSTS_FSM_DMA_IDLE 0
+#define IDSTS_FSM_DMA_SUSPEND 1
+#define IDSTS_FSM_DESC_RD 2
+#define IDSTS_FSM_DESC_CHK 3
+#define IDSTS_FSM_DMA_RD_REQ_WAIT 4
+#define IDSTS_FSM_DMA_WR_REQ_WAIT 5
+#define IDSTS_FSM_DMA_RD 6
+#define IDSTS_FSM_DMA_WR 7
+#define IDSTS_FSM_DESC_CLOSE 8
+#define IDSTS_FSM_MASK 0xf
+
+#define CMD_UPDATE_CLK 0x80202000
+#define CMD_START_BIT (1 << 31)
+
+#define MMC_8BIT_MODE (1 << 16)
+#define MMC_4BIT_MODE (1 << 0)
+#define MMC_1BIT_MODE 0
+
+#define DW_MMC_BLOCK_SIZE 512
+
+#define CMD_INDEX_MASK 0x3F
+#define BIT_CMD_RESPONSE_EXPECT (1 << 6)
+#define BIT_CMD_LONG_RESPONSE (1 << 7)
+#define BIT_CMD_CHECK_RESPONSE_CRC (1 << 8)
+#define BIT_CMD_DATA_EXPECTED (1 << 9)
+#define BIT_CMD_READ (0 << 10)
+#define BIT_CMD_WRITE (1 << 10)
+#define BIT_CMD_BLOCK_TRANSFER (0 << 11)
+#define BIT_CMD_STREAM_TRANSFER (1 << 11)
+#define BIT_CMD_SEND_AUTO_STOP (1 << 12)
+#define BIT_CMD_WAIT_PRVDATA_COMPLETE (1 << 13)
+#define BIT_CMD_STOP_ABORT_CMD (1 << 14)
+#define BIT_CMD_SEND_INIT (1 << 15)
+#define BIT_CMD_UPDATE_CLOCK_ONLY (1 << 21)
+#define BIT_CMD_READ_CEATA_DEVICE (1 << 22)
+#define BIT_CMD_CCS_EXPECTED (1 << 23)
+#define BIT_CMD_ENABLE_BOOT (1 << 24)
+#define BIT_CMD_EXPECT_BOOT_ACK (1 << 25)
+#define BIT_CMD_DISABLE_BOOT (1 << 26)
+#define BIT_CMD_MANDATORY_BOOT (0 << 27)
+#define BIT_CMD_ALTERNATE_BOOT (1 << 27)
+#define BIT_CMD_VOLT_SWITCH (1 << 28)
+#define BIT_CMD_USE_HOLD_REG (1 << 29)
+#define BIT_CMD_START (1 << 31)
+
+#define CMD_INDEX(x) ((x) & CMD_INDEX_MASK)
+
+#define DW_MMC_INT_EBE (1 << 15) /* End-bit Err */
+#define DW_MMC_INT_SBE (1 << 13) /* Start-bit Err */
+#define DW_MMC_INT_HLE (1 << 12) /* Hardware-lock Err */
+#define DW_MMC_INT_FRUN (1 << 11) /* FIFO UN/OV RUN */
+#define DW_MMC_INT_DRT (1 << 9) /* Data timeout */
+#define DW_MMC_INT_RTO (1 << 8) /* Response timeout */
+#define DW_MMC_INT_DCRC (1 << 7) /* Data CRC err */
+#define DW_MMC_INT_RCRC (1 << 6) /* Response CRC err */
+#define DW_MMC_INT_RXDR (1 << 5) /* Receive FIFO data request */
+#define DW_MMC_INT_TXDR (1 << 4) /* Transmit FIFO data request */
+#define DW_MMC_INT_DTO (1 << 3) /* Data trans over */
+#define DW_MMC_INT_CMD_DONE (1 << 2) /* Command done */
+#define DW_MMC_INT_RE (1 << 1) /* Response error */
+
+#define DW_MMC_IDMAC_DES0_DIC (1 << 1)
+#define DW_MMC_IDMAC_DES0_LD (1 << 2)
+#define DW_MMC_IDMAC_DES0_FS (1 << 3)
+#define DW_MMC_IDMAC_DES0_CH (1 << 4)
+#define DW_MMC_IDMAC_DES0_ER (1 << 5)
+#define DW_MMC_IDMAC_DES0_CES (1 << 30)
+#define DW_MMC_IDMAC_DES0_OWN (1 << 31)
+#define DW_MMC_IDMAC_DES1_BS1(x) ((x) & 0x1fff)
+#define DW_MMC_IDMAC_DES2_BS2(x) (((x) & 0x1fff) << 13)
+#define DW_MMC_IDMAC_SWRESET (1 << 0)
+#define DW_MMC_IDMAC_FB (1 << 1)
+#define DW_MMC_IDMAC_ENABLE (1 << 7)
+
+#define DW_MMC_CTRL_RESET (1 << 0)
+#define DW_MMC_CTRL_FIFO_RESET (1 << 1)
+#define DW_MMC_CTRL_DMA_RESET (1 << 2)
+#define DW_MMC_CTRL_INT_EN (1 << 4)
+#define DW_MMC_CTRL_DMA_EN (1 << 5)
+#define DW_MMC_CTRL_IDMAC_EN (1 << 25)
+#define DW_MMC_CTRL_RESET_ALL (DW_MMC_CTRL_RESET | DW_MMC_CTRL_FIFO_RESET | DW_MMC_CTRL_DMA_RESET)
+
+#define DW_MMC_STS_DATA_BUSY (1 << 9)
+#define DW_MMC_STS_FIFO_COUNT(x) (((x) & 0x1fff) << 17) /* Number of filled locations in FIFO */
+#define GET_STS_FIFO_COUNT(x) (((x) >> 17) & 0x1fff)
+
+#define DW_MMC_BMOD_SWR (1 << 0) /* Software Reset */
+#define DW_MMC_BMOD_FB (1 << 1) /* Fix Burst */
+#define DW_MMC_BMOD_DE (1 << 7) /* IDMAC Enable */
+
+#define DW_MMC_IDSTS_TI (1 << 0) /* Transmit Interrupt */
+#define DW_MMC_IDSTS_RI (1 << 1) /* Receive Interrupt */
+
+#define DW_MMC_FIFO_TWMARK(x) ((x) & 0xfff)
+#define DW_MMC_FIFO_RWMARK(x) (((x) & 0x1ff) << 16)
+#define DW_MMC_DMA_BURST_SIZE(x) (((x) & 0x7) << 28)
+
+#define DW_MMC_CARD_RD_THR(x) (((x) & 0xfff) << 16)
+#define DW_MMC_CARD_RD_THR_EN (1 << 0)
+
+#define UHS_DDR_MODE (1 << 16)
+
+#define GENCLK_DIV 7
+
+#define DW_MMC_GPIO_CLK_DIV(x) (((x) & 0xf) << 8)
+#define DW_MMC_GPIO_USE_SAMPLE_DLY(x) (((x) & 1) << 13)
+#define DW_MMC_GPIO_CLK_ENABLE BIT16
+
+#define UHSEXT_SAMPLE_PHASE(x) (((x) & 0x1f) << 16)
+#define UHSEXT_SAMPLE_DRVPHASE(x) (((x) & 0x1f) << 21)
+#define UHSEXT_SAMPLE_DLY(x) (((x) & 0x1f) << 26)
+
+#define DWMMC_DMA_BUF_SIZE (512 * 8)
+#define DWMMC_FIFO_THRESHOLD 16
+
+#define DWMMC_INIT_CLOCK_FREQ 400 /* KHz */
+
+//
+// The transfer modes supported by SD Host Controller
+// Simplified Spec 3.0 Table 1-2
+//
+typedef enum {
+ SdMmcNoData,
+ SdMmcPioMode,
+ SdMmcSdmaMode,
+ SdMmcAdmaMode
+} DW_MMC_HC_TRANSFER_MODE;
+
+//
+// The maximum data length of each descriptor line
+//
+#define ADMA_MAX_DATA_PER_LINE 0x10000
+
+typedef struct {
+ UINT32 Des0;
+ UINT32 Des1;
+ UINT32 Des2;
+ UINT32 Des3;
+} DW_MMC_HC_DMA_DESC_LINE;
+
+#define SD_MMC_SDMA_BOUNDARY 512 * 1024
+#define SD_MMC_SDMA_ROUND_UP(x, n) (((x) + n) & ~(n - 1))
+
+typedef struct {
+ UINT8 FirstBar:3; // bit 0:2
+ UINT8 Reserved:1; // bit 3
+ UINT8 SlotNum:3; // bit 4:6
+ UINT8 Reserved1:1; // bit 7
+} DW_MMC_HC_SLOT_INFO;
+
+/**
+ Dump the content of SD/MMC host controller's Capability Register.
+
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] Capability The buffer to store the capability data.
+
+**/
+VOID
+DumpCapabilityReg (
+ IN UINT8 Slot,
+ IN DW_MMC_HC_SLOT_CAP *Capability
+ );
+
+#if 0
+/**
+ Read SlotInfo register from SD/MMC host controller pci config space.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[out] FirstBar The buffer to store the first BAR value.
+ @param[out] SlotNum The buffer to store the supported slot number.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcGetSlotInfo (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ OUT UINT8 *FirstBar,
+ OUT UINT8 *SlotNum
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Read/Write specified SD/MMC host controller mmio register.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] BarIndex The BAR index of the standard PCI Configuration
+ header to use as the base address for the memory
+ operation to perform.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Read A boolean to indicate it's read or write operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in, out] Data For read operations, the destination buffer to store
+ the results. For write operations, the source buffer
+ to write data from. The caller is responsible for
+ having ownership of the data buffer and ensuring its
+ size not less than Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid.
+ @retval EFI_SUCCESS The read/write operation succeeds.
+ @retval Others The read/write operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcRwMmio (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 BarIndex,
+ IN UINT32 Offset,
+ IN BOOLEAN Read,
+ IN UINT8 Count,
+ IN OUT VOID *Data
+ );
+#else
+/**
+ Read/Write specified SD/MMC host controller mmio register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Read A boolean to indicate it's read or write operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in, out] Data For read operations, the destination buffer to store
+ the results. For write operations, the source buffer
+ to write data from. The caller is responsible for
+ having ownership of the data buffer and ensuring its
+ size not less than Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid.
+ @retval EFI_SUCCESS The read/write operation succeeds.
+ @retval Others The read/write operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcRwMmio (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN BOOLEAN Read,
+ IN UINT8 Count,
+ IN OUT VOID *Data
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Do OR operation with the value of the specified SD/MMC host controller mmio register.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] BarIndex The BAR index of the standard PCI Configuration
+ header to use as the base address for the memory
+ operation to perform.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in] OrData The pointer to the data used to do OR operation.
+ The caller is responsible for having ownership of
+ the data buffer and ensuring its size not less than
+ Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid.
+ @retval EFI_SUCCESS The OR operation succeeds.
+ @retval Others The OR operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcOrMmio (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 BarIndex,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN VOID *OrData
+ );
+#else
+/**
+ Do OR operation with the value of the specified SD/MMC host controller mmio register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] BarIndex The BAR index of the standard PCI Configuration
+ header to use as the base address for the memory
+ operation to perform.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in] OrData The pointer to the data used to do OR operation.
+ The caller is responsible for having ownership of
+ the data buffer and ensuring its size not less than
+ Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid.
+ @retval EFI_SUCCESS The OR operation succeeds.
+ @retval Others The OR operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcOrMmio (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN VOID *OrData
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Do AND operation with the value of the specified SD/MMC host controller mmio register.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] BarIndex The BAR index of the standard PCI Configuration
+ header to use as the base address for the memory
+ operation to perform.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in] AndData The pointer to the data used to do AND operation.
+ The caller is responsible for having ownership of
+ the data buffer and ensuring its size not less than
+ Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid.
+ @retval EFI_SUCCESS The AND operation succeeds.
+ @retval Others The AND operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcAndMmio (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 BarIndex,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN VOID *AndData
+ );
+#else
+/**
+ Do AND operation with the value of the specified SD/MMC host controller mmio register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in] AndData The pointer to the data used to do AND operation.
+ The caller is responsible for having ownership of
+ the data buffer and ensuring its size not less than
+ Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid.
+ @retval EFI_SUCCESS The AND operation succeeds.
+ @retval Others The AND operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcAndMmio (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN VOID *AndData
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Wait for the value of the specified MMIO register set to the test value.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] BarIndex The BAR index of the standard PCI Configuration
+ header to use as the base address for the memory
+ operation to perform.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2, 4 or 8 bytes.
+ @param[in] MaskValue The mask value of memory.
+ @param[in] TestValue The test value of memory.
+ @param[in] Timeout The time out value for wait memory set, uses 1
+ microsecond as a unit.
+
+ @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout
+ range.
+ @retval EFI_SUCCESS The MMIO register has expected value.
+ @retval Others The MMIO operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcWaitMmioSet (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 BarIndex,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN UINT64 MaskValue,
+ IN UINT64 TestValue,
+ IN UINT64 Timeout
+ );
+#else
+/**
+ Wait for the value of the specified MMIO register set to the test value.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset within the selected BAR to start the
+ memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2, 4 or 8 bytes.
+ @param[in] MaskValue The mask value of memory.
+ @param[in] TestValue The test value of memory.
+ @param[in] Timeout The time out value for wait memory set, uses 1
+ microsecond as a unit.
+
+ @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout
+ range.
+ @retval EFI_SUCCESS The MMIO register has expected value.
+ @retval Others The MMIO operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcWaitMmioSet (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN UINT64 MaskValue,
+ IN UINT64 TestValue,
+ IN UINT64 Timeout
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Software reset the specified SD/MMC host controller.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+
+ @retval EFI_SUCCESS The software reset executes successfully.
+ @retval Others The software reset fails.
+
+**/
+EFI_STATUS
+DwMmcHcReset (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#else
+/**
+ Software reset the specified SD/MMC host controller.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+
+ @retval EFI_SUCCESS The software reset executes successfully.
+ @retval Others The software reset fails.
+
+**/
+EFI_STATUS
+DwMmcHcReset (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Set all interrupt status bits in Normal and Error Interrupt Status Enable
+ register.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+
+ @retval EFI_SUCCESS The operation executes successfully.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+DwMmcHcEnableInterrupt (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot
+ );
+#else
+/**
+ Set all interrupt status bits in Normal and Error Interrupt Status Enable
+ register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+
+ @retval EFI_SUCCESS The operation executes successfully.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+DwMmcHcEnableInterrupt (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Get the capability data from the specified slot.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[out] Capability The buffer to store the capability data.
+
+ @retval EFI_SUCCESS The operation executes successfully.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+DwMmcHcGetCapability (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_HANDLE Controller,
+ IN UINT8 Slot,
+ OUT DW_MMC_HC_SLOT_CAP *Capability
+ );
+#else
+/**
+ Get the capability data from the specified slot.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[out] Capability The buffer to store the capability data.
+
+ @retval EFI_SUCCESS The operation executes successfully.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+DwMmcHcGetCapability (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_HANDLE Controller,
+ IN UINT8 Slot,
+ OUT DW_MMC_HC_SLOT_CAP *Capability
+ );
+#endif
+
+#if 0
+/**
+ Get the maximum current capability data from the specified slot.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[out] MaxCurrent The buffer to store the maximum current capability data.
+
+ @retval EFI_SUCCESS The operation executes successfully.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+DwMmcHcGetMaxCurrent (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ OUT UINT64 *MaxCurrent
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Detect whether there is a SD/MMC card attached at the specified SD/MMC host controller
+ slot.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[out] MediaPresent The pointer to the media present boolean value.
+
+ @retval EFI_SUCCESS There is no media change happened.
+ @retval EFI_MEDIA_CHANGED There is media change happened.
+ @retval Others The detection fails.
+
+**/
+EFI_STATUS
+DwMmcHcCardDetect (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_HANDLE Controller,
+ IN UINT8 Slot,
+ OUT BOOLEAN *MediaPresent
+ );
+#else
+/**
+ Detect whether there is a SD/MMC card attached at the specified SD/MMC host controller
+ slot.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[out] MediaPresent The pointer to the media present boolean value.
+
+ @retval EFI_SUCCESS There is no media change happened.
+ @retval EFI_MEDIA_CHANGED There is media change happened.
+ @retval Others The detection fails.
+
+**/
+EFI_STATUS
+DwMmcHcCardDetect (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_HANDLE Controller,
+ IN UINT8 Slot,
+ OUT BOOLEAN *MediaPresent
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Stop SD/MMC card clock.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+
+ @retval EFI_SUCCESS Succeed to stop SD/MMC clock.
+ @retval Others Fail to stop SD/MMC clock.
+
+**/
+EFI_STATUS
+DwMmcHcStopClock (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot
+ );
+
+/**
+ SD/MMC card clock supply.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] ClockFreq The max clock frequency to be set. The unit is KHz.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The clock is supplied successfully.
+ @retval Others The clock isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcClockSupply (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN UINT64 ClockFreq,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#else
+/**
+ Stop SD/MMC card clock.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+
+ @retval EFI_SUCCESS Succeed to stop SD/MMC clock.
+ @retval Others Fail to stop SD/MMC clock.
+
+**/
+EFI_STATUS
+DwMmcHcStopClock (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ );
+
+/**
+ SD/MMC card clock supply.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] ClockFreq The max clock frequency to be set. The unit is KHz.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The clock is supplied successfully.
+ @retval Others The clock isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcClockSupply (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT64 ClockFreq,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#endif
+
+#if 0
+/**
+ SD/MMC bus power control.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] PowerCtrl The value setting to the power control register.
+
+ @retval TRUE There is a SD/MMC card attached.
+ @retval FALSE There is no a SD/MMC card attached.
+
+**/
+EFI_STATUS
+DwMmcHcPowerControl (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN UINT8 PowerCtrl
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Set the SD/MMC bus width.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] BusWidth The bus width used by the SD/MMC device, it must be 1, 4 or 8.
+
+ @retval EFI_SUCCESS The bus width is set successfully.
+ @retval Others The bus width isn't set successfully.
+
+**/
+EFI_STATUS
+DwMmcHcSetBusWidth (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN BOOLEAN IsDdr,
+ IN UINT16 BusWidth
+ );
+#else
+/**
+ Set the SD/MMC bus width.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] BusWidth The bus width used by the SD/MMC device, it must be 1, 4 or 8.
+
+ @retval EFI_SUCCESS The bus width is set successfully.
+ @retval Others The bus width isn't set successfully.
+
+**/
+EFI_STATUS
+DwMmcHcSetBusWidth (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN BOOLEAN IsDdr,
+ IN UINT16 BusWidth
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Supply SD/MMC card with lowest clock frequency at initialization.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The clock is supplied successfully.
+ @retval Others The clock isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitClockFreq (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#else
+/**
+ Supply SD/MMC card with lowest clock frequency at initialization.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The clock is supplied successfully.
+ @retval Others The clock isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitClockFreq (
+ IN EFI_DEVICE_IO_PROTOCOL *PciIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Supply SD/MMC card with maximum voltage at initialization.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The voltage is supplied successfully.
+ @retval Others The voltage isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitPowerVoltage (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#else
+/**
+ Supply SD/MMC card with maximum voltage at initialization.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The voltage is supplied successfully.
+ @retval Others The voltage isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitPowerVoltage (
+ IN EFI_DEVICE_IO_PROTOCOL *PciIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Initialize the Timeout Control register with most conservative value at initialization.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+
+ @retval EFI_SUCCESS The timeout control register is configured successfully.
+ @retval Others The timeout control register isn't configured successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitTimeoutCtrl (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot
+ );
+#else
+/**
+ Initialize the Timeout Control register with most conservative value at initialization.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+
+ @retval EFI_SUCCESS The timeout control register is configured successfully.
+ @retval Others The timeout control register isn't configured successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitTimeoutCtrl (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ );
+#endif
+
+#ifdef DWMMC_PCI
+/**
+ Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value
+ at initialization.
+
+ @param[in] PciIo The PCI IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command to.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The host controller is initialized successfully.
+ @retval Others The host controller isn't initialized successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitHost (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Slot,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#else
+/**
+ Initial SD/MMC host controller with lowest clock frequency, max power and
+ max timeout value at initialization.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The host controller is initialized successfully.
+ @retval Others The host controller isn't initialized successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitHost (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ );
+#endif
+
+#endif /* _DW_MMC_HCI_H_ */
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c b/EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c
new file mode 100644
index 000000000000..1edade69d091
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c
@@ -0,0 +1,214 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Designware SD/MMC host
+ controller driver.
+
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "DwMmcHcDxe.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDwMmcHcComponentName = {
+ DwMmcHcComponentNameGetDriverName,
+ DwMmcHcComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDwMmcHcComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DwMmcHcComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DwMmcHcComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDwMmcHcDriverNameTable[] = {
+ { "eng;en", L"Designware Sd/Mmc Host Controller Driver" },
+ { NULL , NULL }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDwMmcHcControllerNameTable[] = {
+ { "eng;en", L"Designware Sd/Mmc Host Controller" },
+ { 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[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] 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[out] 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
+DwMmcHcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDwMmcHcDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gDwMmcHcComponentName)
+ );
+}
+
+/**
+ 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[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] 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[in] 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[in] 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[out] 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
+DwMmcHcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+
+ if (Language == NULL || ControllerName == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // This is a device driver, so ChildHandle must be NULL.
+ //
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver is currently managing ControllerHandle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gDwMmcHcDriverBinding.DriverBindingHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDwMmcHcControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gDwMmcHcComponentName)
+ );
+}
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c
new file mode 100644
index 000000000000..6e8e3fac8d75
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c
@@ -0,0 +1,1295 @@
+/** @file
+ This driver is used to manage Designware SD/MMC host controller.
+
+ It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2016 Marvell International Ltd. All rigths reserved.<BR>
+ Copyright (C) 2018, Linaro Ltd. All rigths reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/PlatformDwMmc.h>
+
+#include "DwMmcHcDxe.h"
+
+//
+// Driver Global Variables
+//
+EFI_DRIVER_BINDING_PROTOCOL gDwMmcHcDriverBinding = {
+ DwMmcHcDriverBindingSupported,
+ DwMmcHcDriverBindingStart,
+ DwMmcHcDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+//
+// Template for Designware SD/MMC host controller private data.
+//
+DW_MMC_HC_PRIVATE_DATA gDwMmcHcTemplate = {
+ DW_MMC_HC_PRIVATE_SIGNATURE, // Signature
+ NULL, // ControllerHandle
+ NULL, // DevIo
+ { // PassThru
+ sizeof (UINT32),
+ DwMmcPassThruPassThru,
+ DwMmcPassThruGetNextSlot,
+ DwMmcPassThruBuildDevicePath,
+ DwMmcPassThruGetSlotNumber,
+ DwMmcPassThruResetDevice
+ },
+ NULL, // PlatformDwMmc
+ 0, // PreviousSlot
+ NULL, // TimerEvent
+ NULL, // ConnectEvent
+ // Queue
+ INITIALIZE_LIST_HEAD_VARIABLE (gDwMmcHcTemplate.Queue),
+ { // Slot
+ {0, UnknownSlot, 0, 0, 0}
+ },
+ { // Capability
+ {0}
+ },
+ { // MaxCurrent
+ 0
+ },
+ 0 // ControllerVersion
+};
+
+SD_DEVICE_PATH mSdDpTemplate = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_SD_DP,
+ {
+ (UINT8) (sizeof (SD_DEVICE_PATH)),
+ (UINT8) ((sizeof (SD_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0
+};
+
+EMMC_DEVICE_PATH mEmmcDpTemplate = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_EMMC_DP,
+ {
+ (UINT8) (sizeof (EMMC_DEVICE_PATH)),
+ (UINT8) ((sizeof (EMMC_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0
+};
+
+//
+// Prioritized function list to detect card type.
+// User could add other card detection logic here.
+//
+DWMMC_CARD_TYPE_DETECT_ROUTINE mCardTypeDetectRoutineTable[] = {
+ EmmcIdentification,
+ SdCardIdentification,
+ NULL
+};
+
+/**
+ The entry point for SD host controller driver, used to install this driver on the ImageHandle.
+
+ @param[in] ImageHandle The firmware allocated handle for this driver image.
+ @param[in] SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS Driver loaded.
+ @retval other Driver not loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeDwMmcHcDxe (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDwMmcHcDriverBinding,
+ ImageHandle,
+ &gDwMmcHcComponentName,
+ &gDwMmcHcComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/**
+ Call back function when the timer event is signaled.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the
+ Event.
+
+**/
+VOID
+EFIAPI
+ProcessAsyncTaskList (
+ IN EFI_EVENT Event,
+ IN VOID* Context
+ )
+{
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ LIST_ENTRY *Link;
+ DW_MMC_HC_TRB *Trb;
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ BOOLEAN InfiniteWait;
+ EFI_EVENT TrbEvent;
+
+ Private = (DW_MMC_HC_PRIVATE_DATA *)Context;
+
+ //
+ // Check if the first entry in the async I/O queue is done or not.
+ //
+ Status = EFI_SUCCESS;
+ Trb = NULL;
+ Link = GetFirstNode (&Private->Queue);
+ if (!IsNull (&Private->Queue, Link)) {
+ Trb = DW_MMC_HC_TRB_FROM_THIS (Link);
+ if (!Private->Slot[Trb->Slot].MediaPresent) {
+ Status = EFI_NO_MEDIA;
+ goto Done;
+ }
+ if (!Trb->Started) {
+ //
+ // Check whether the cmd/data line is ready for transfer.
+ //
+ Status = DwMmcCheckTrbEnv (Private, Trb);
+ if (!EFI_ERROR (Status)) {
+ Trb->Started = TRUE;
+ Status = DwMmcExecTrb (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else {
+ goto Done;
+ }
+ }
+ Status = DwMmcCheckTrbResult (Private, Trb);
+ }
+
+Done:
+ if ((Trb != NULL) && (Status == EFI_NOT_READY)) {
+ Packet = Trb->Packet;
+ if (Packet->Timeout == 0) {
+ InfiniteWait = TRUE;
+ } else {
+ InfiniteWait = FALSE;
+ }
+ if ((!InfiniteWait) && (Trb->Timeout-- == 0)) {
+ RemoveEntryList (Link);
+ Trb->Packet->TransactionStatus = EFI_TIMEOUT;
+ TrbEvent = Trb->Event;
+ DwMmcFreeTrb (Trb);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT\n",
+ TrbEvent
+ ));
+ gBS->SignalEvent (TrbEvent);
+ return;
+ }
+ }
+ if ((Trb != NULL) && (Status != EFI_NOT_READY)) {
+ RemoveEntryList (Link);
+ Trb->Packet->TransactionStatus = Status;
+ TrbEvent = Trb->Event;
+ DwMmcFreeTrb (Trb);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "ProcessAsyncTaskList(): Signal Event %p with %r\n",
+ TrbEvent,
+ Status
+ ));
+ gBS->SignalEvent (TrbEvent);
+ }
+ return;
+}
+
+/**
+ Sd removable device enumeration callback function when the timer event is signaled.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the
+ Event.
+
+**/
+VOID
+EFIAPI
+DwMmcHcEnumerateDevice (
+ IN EFI_EVENT Event,
+ IN VOID* Context
+ )
+{
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ EFI_STATUS Status;
+ BOOLEAN MediaPresent;
+ UINT32 RoutineNum;
+ DWMMC_CARD_TYPE_DETECT_ROUTINE *Routine;
+ UINTN Index;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NextLink;
+ DW_MMC_HC_TRB *Trb;
+ EFI_TPL OldTpl;
+
+ Private = (DW_MMC_HC_PRIVATE_DATA *)Context;
+
+ if ((Private->Slot[0].Enable) &&
+ (Private->Slot[0].SlotType == RemovableSlot)) {
+ Status = DwMmcHcCardDetect (
+ Private->DevIo,
+ Private->ControllerHandle,
+ 0,
+ &MediaPresent
+ );
+ if ((Status == EFI_MEDIA_CHANGED) && !MediaPresent) {
+ DEBUG ((
+ DEBUG_INFO,
+ "DwMmcHcEnumerateDevice: device disconnected at %p\n",
+ Private->DevIo
+ ));
+ Private->Slot[0].MediaPresent = FALSE;
+ //
+ // Signal all async task events at the slot with EFI_NO_MEDIA status.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ for (Link = GetFirstNode (&Private->Queue);
+ !IsNull (&Private->Queue, Link);
+ Link = NextLink) {
+ NextLink = GetNextNode (&Private->Queue, Link);
+ Trb = DW_MMC_HC_TRB_FROM_THIS (Link);
+ if (Trb->Slot == 0) {
+ RemoveEntryList (Link);
+ Trb->Packet->TransactionStatus = EFI_NO_MEDIA;
+ gBS->SignalEvent (Trb->Event);
+ DwMmcFreeTrb (Trb);
+ }
+ }
+ gBS->RestoreTPL (OldTpl);
+ //
+ // Notify the upper layer the connect state change through
+ // ReinstallProtocolInterface.
+ //
+ gBS->ReinstallProtocolInterface (
+ Private->ControllerHandle,
+ &gEfiSdMmcPassThruProtocolGuid,
+ &Private->PassThru,
+ &Private->PassThru
+ );
+ }
+ if ((Status == EFI_MEDIA_CHANGED) && MediaPresent) {
+ DEBUG ((
+ DEBUG_INFO,
+ "DwMmcHcEnumerateDevice: device connected at %p\n",
+ Private->DevIo
+ ));
+ //
+ // Initialize slot and start identification process for the new
+ // attached device
+ //
+ Status = DwMmcHcInitHost (Private->DevIo, Private->Capability[0]);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ //
+ // Reset the specified slot of the SD/MMC Pci Host Controller
+ //
+ Status = DwMmcHcReset (Private->DevIo, Private->Capability[0]);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ Private->Slot[0].MediaPresent = TRUE;
+ RoutineNum = sizeof (mCardTypeDetectRoutineTable) /
+ sizeof (DWMMC_CARD_TYPE_DETECT_ROUTINE);
+ for (Index = 0; Index < RoutineNum; Index++) {
+ Routine = &mCardTypeDetectRoutineTable[Index];
+ if (*Routine != NULL) {
+ Status = (*Routine) (Private);
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }
+ //
+ // This card doesn't get initialized correctly.
+ //
+ if (Index == RoutineNum) {
+ return;
+ }
+
+ //
+ // Notify the upper layer the connect state change through
+ // ReinstallProtocolInterface.
+ //
+ gBS->ReinstallProtocolInterface (
+ Private->ControllerHandle,
+ &gEfiSdMmcPassThruProtocolGuid,
+ &Private->PassThru,
+ &Private->PassThru
+ );
+ }
+ }
+
+ return;
+}
+
+/**
+ Reset the specified SD/MMC host controller and enable all interrupts.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+
+ @retval EFI_SUCCESS The software reset executes successfully.
+ @retval Others The software reset fails.
+
+**/
+EFI_STATUS
+DwMmcHcReset (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlkSize;
+
+ //
+ // Enable all interrupt after reset all.
+ //
+ Status = DwMmcHcEnableInterrupt (DevIo);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcReset: enable interrupts fail: %r\n", Status));
+ return Status;
+ }
+ Status = DwMmcHcInitTimeoutCtrl (DevIo);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BlkSize = DW_MMC_BLOCK_SIZE;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BLKSIZ,
+ FALSE,
+ sizeof (BlkSize),
+ &BlkSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set block size fails: %r\n", Status));
+ return Status;
+ }
+
+ Status = DwMmcHcInitClockFreq (DevIo, Capability);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = DwMmcHcSetBusWidth (DevIo, FALSE, 1);
+
+ return Status;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device
+ is provided, it further tests to see if this driver supports creating a
+ handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the
+ device specified by ControllerHandle. Drivers will typically use the device
+ path attached to ControllerHandle and/or the services from the bus I/O
+ abstraction attached to ControllerHandle to determine if the driver supports
+ ControllerHandle. This function may be called many times during platform
+ initialization. In order to reduce boot times, the tests performed by this
+ function must be very small, and take as little time as possible to execute.
+ This function must not change the state of any hardware devices, and this
+ function must be aware that the device specified by ControllerHandle may
+ already be managed by the same driver or a different driver. This function
+ must match its calls to AllocatePages() with FreePages(), AllocatePool() with
+ FreePool(), and OpenProtocol() with CloseProtocol(). Since ControllerHandle
+ may have been previously started by the same driver, if a protocol is already
+ in the opened state, then it must not be closed with CloseProtocol(). This is
+ required to guarantee the state of ControllerHandle is not modified by this
+ function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to test. This
+ handle must support a protocol interface that
+ supplies an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter
+ is not NULL, then the bus driver must deter-
+ mine if the bus controller specified by
+ ControllerHandle and the child controller
+ specified by RemainingDevicePath are both
+ supported by this bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the
+ driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by a different driver or an application that
+ requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the
+ driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcDriverBindingSupported (
+ 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_DEVICE_IO_PROTOCOL *DevIo;
+ PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc;
+
+ ParentDevicePath = NULL;
+
+ Status = gBS->LocateProtocol (
+ &gPlatformDwMmcProtocolGuid,
+ NULL,
+ (VOID **) &PlatformDwMmc
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID *) &ParentDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // EFI_ALREADY_STARTED is also an error.
+ //
+ return Status;
+ }
+ //
+ // Close the protocol because we don't use it here.
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // Now test the EmbeddedNonDiscoverableIoProtocol.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEmbeddedNonDiscoverableIoProtocolGuid,
+ (VOID **) &DevIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ gBS->CloseProtocol (
+ Controller,
+ &gEmbeddedNonDiscoverableIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ return EFI_SUCCESS;
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service
+ ConnectController().
+ As a result, much of the error checking on the parameters to Start() has
+ been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system
+ behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a
+ naturally aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver
+ specified by This must have been called with the same calling parameters,
+ and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to start. This
+ handle must support a protocol interface
+ that supplies an I/O abstraction to the
+ driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers.
+ For a bus driver, if this parameter is NULL,
+ then handles for all the children of
+ Controller are created by this driver.
+ If this parameter is not NULL and the first
+ Device Path Node is not the End of Device
+ Path Node, then only the handle for the
+ child device specified by the first Device
+ Path Node of RemainingDevicePath is created
+ by this driver.
+ If the first Device Path Node of
+ RemainingDevicePath is the End of Device Path
+ Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a
+ device error. Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ BOOLEAN MediaPresent;
+ DWMMC_CARD_TYPE_DETECT_ROUTINE *Routine;
+ UINT8 Index;
+ UINT32 RoutineNum;
+ PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc;
+
+ Status = gBS->LocateProtocol (
+ &gPlatformDwMmcProtocolGuid,
+ NULL,
+ (VOID **) &PlatformDwMmc
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEmbeddedNonDiscoverableIoProtocolGuid,
+ (VOID **) &DevIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private = AllocateCopyPool (sizeof (DW_MMC_HC_PRIVATE_DATA), &gDwMmcHcTemplate);
+ if (Private == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ Private->ControllerHandle = Controller;
+ Private->DevIo = DevIo;
+ Private->PlatformDwMmc = PlatformDwMmc;
+ InitializeListHead (&Private->Queue);
+
+ Status = DwMmcHcGetCapability (DevIo, Controller, 0, &Private->Capability[0]);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ if (Private->Capability[0].BaseClkFreq == 0) {
+ goto Done;
+ }
+
+ DumpCapabilityReg (0, &Private->Capability[0]);
+
+ MediaPresent = FALSE;
+ Status = DwMmcHcCardDetect (Private->DevIo, Controller, 0, &MediaPresent);
+ if (MediaPresent == FALSE) {
+ goto Done;
+ }
+
+ //
+ // Initialize slot and start identification process for the new attached device
+ //
+ Status = DwMmcHcInitHost (DevIo, Private->Capability[0]);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ //
+ // Reset HC
+ //
+ Status = DwMmcHcReset (DevIo, Private->Capability[0]);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Private->Slot[0].CardType = Private->Capability[0].CardType;
+ Private->Slot[0].Enable = TRUE;
+ Private->Slot[0].MediaPresent = TRUE;
+
+ RoutineNum = sizeof (mCardTypeDetectRoutineTable) / sizeof (DWMMC_CARD_TYPE_DETECT_ROUTINE);
+ for (Index = 0; Index < RoutineNum; Index++) {
+ Routine = &mCardTypeDetectRoutineTable[Index];
+ if (*Routine != NULL) {
+ Status = (*Routine) (Private);
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }
+
+ //
+ // Start the asynchronous I/O monitor
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ ProcessAsyncTaskList,
+ Private,
+ &Private->TimerEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Status = gBS->SetTimer (Private->TimerEvent, TimerPeriodic, DW_MMC_HC_ASYNC_TIMER);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ //
+ // Start the Sd removable device connection enumeration
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ DwMmcHcEnumerateDevice,
+ Private,
+ &Private->ConnectEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Status = gBS->SetTimer (Private->ConnectEvent, TimerPeriodic, DW_MMC_HC_ENUM_TIMER);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiSdMmcPassThruProtocolGuid,
+ &(Private->PassThru),
+ NULL
+ );
+
+ DEBUG ((DEBUG_INFO, "DwMmcHcDriverBindingStart: %r End on %x\n", Status, Controller));
+
+Done:
+ if (EFI_ERROR (Status)) {
+ if ((Private != NULL) && (Private->TimerEvent != NULL)) {
+ gBS->CloseEvent (Private->TimerEvent);
+ }
+
+ if ((Private != NULL) && (Private->ConnectEvent != NULL)) {
+ gBS->CloseEvent (Private->ConnectEvent);
+ }
+
+ if (Private != NULL) {
+ FreePool (Private);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service
+ DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been
+ moved into this common boot service. It is legal to call Stop() from other
+ locations, but the following calling restrictions must be followed or the
+ system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
+ call to this same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in
+ this driver's Start() function, and the Start() function must have called
+ OpenProtocol() on ControllerHandle with an Attribute of
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle
+ must support a bus specific I/O protocol for the
+ driver to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in
+ ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be
+ NULL if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NextLink;
+ DW_MMC_HC_TRB *Trb;
+
+ DEBUG ((DEBUG_INFO, "DwMmcHcDriverBindingStop: Start\n"));
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiSdMmcPassThruProtocolGuid,
+ (VOID**) &PassThru,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru);
+ //
+ // Close Non-Blocking timer and free Task list.
+ //
+ if (Private->TimerEvent != NULL) {
+ gBS->CloseEvent (Private->TimerEvent);
+ Private->TimerEvent = NULL;
+ }
+ if (Private->ConnectEvent != NULL) {
+ gBS->CloseEvent (Private->ConnectEvent);
+ Private->ConnectEvent = NULL;
+ }
+ //
+ // As the timer is closed, there is no needs to use TPL lock to
+ // protect the critical region "queue".
+ //
+ for (Link = GetFirstNode (&Private->Queue);
+ !IsNull (&Private->Queue, Link);
+ Link = NextLink) {
+ NextLink = GetNextNode (&Private->Queue, Link);
+ RemoveEntryList (Link);
+ Trb = DW_MMC_HC_TRB_FROM_THIS (Link);
+ Trb->Packet->TransactionStatus = EFI_ABORTED;
+ gBS->SignalEvent (Trb->Event);
+ DwMmcFreeTrb (Trb);
+ }
+
+ //
+ // Uninstall Block I/O protocol from the device handle
+ //
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiSdMmcPassThruProtocolGuid,
+ &(Private->PassThru)
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ FreePool (Private);
+
+ DEBUG ((DEBUG_INFO, "DwMmcHcDriverBindingStop: End with %r\n", Status));
+
+ return Status;
+}
+
+/**
+ Sends SD command to an SD card that is attached to the SD controller.
+
+ The PassThru() function sends the SD command specified by Packet to the SD
+ card specified by Slot.
+
+ If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned.
+
+ If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is
+ returned.
+
+ If Slot is not in a valid range for the SD controller, then
+ EFI_INVALID_PARAMETER is returned.
+
+ If Packet defines a data command but both InDataBuffer and OutDataBuffer are
+ NULL, EFI_INVALID_PARAMETER is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot The slot number of the SD card to send the
+ command to.
+ @param[in,out] Packet A pointer to the SD command data structure.
+ @param[in] Event If Event is NULL, blocking I/O is performed. If
+ Event is not NULL, then nonblocking I/O is
+ performed, and Event will be signaled when the
+ Packet completes.
+
+ @retval EFI_SUCCESS The SD Command Packet was sent by the host.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send
+ the SD command Packet.
+ @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is
+ invalid.
+ @retval EFI_INVALID_PARAMETER Packet defines a data command but both
+ InDataBuffer and OutDataBuffer are NULL.
+ @retval EFI_NO_MEDIA SD Device not present in the Slot.
+ @retval EFI_UNSUPPORTED The command described by the SD Command Packet
+ is not supported by the host controller.
+ @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength
+ exceeds the limit supported by SD card
+ ( i.e. if the number of bytes exceed the Last
+ LBA).
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruPassThru (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN UINT8 Slot,
+ IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ DW_MMC_HC_TRB *Trb;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->SdMmcCmdBlk == NULL) || (Packet->SdMmcStatusBlk == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (This);
+
+ if (!Private->Slot[Slot].Enable) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Private->Slot[Slot].MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ Trb = DwMmcCreateTrb (Private, Slot, Packet, Event);
+ if (Trb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Immediately return for async I/O.
+ //
+ if (Event != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Wait async I/O list is empty before execute sync I/O operation.
+ //
+ while (TRUE) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ if (IsListEmpty (&Private->Queue)) {
+ gBS->RestoreTPL (OldTpl);
+ break;
+ }
+ gBS->RestoreTPL (OldTpl);
+ }
+
+ Status = DwMmcWaitTrbEnv (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Status = DwMmcExecTrb (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Status = DwMmcWaitTrbResult (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+Done:
+ if (Trb != NULL) {
+ DwMmcFreeTrb (Trb);
+ }
+
+ return Status;
+}
+
+/**
+ Used to retrieve next slot numbers supported by the SD controller. The
+ function returns information about all available slots (populated or
+ not-populated).
+
+ The GetNextSlot() function retrieves the next slot number on an SD controller.
+ If on input Slot is 0xFF, then the slot number of the first slot on the SD
+ controller is returned.
+
+ If Slot is a slot number that was returned on a previous call to
+ GetNextSlot(), then the slot number of the next slot on the SD controller is
+ returned.
+
+ If Slot is not 0xFF and Slot was not returned on a previous call to
+ GetNextSlot(), EFI_INVALID_PARAMETER is returned.
+
+ If Slot is the slot number of the last slot on the SD controller, then
+ EFI_NOT_FOUND is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in,out] Slot On input, a pointer to a slot number on the SD
+ controller.
+ On output, a pointer to the next slot number on
+ the SD controller.
+ An input value of 0xFF retrieves the first slot
+ number on the SD controller.
+
+ @retval EFI_SUCCESS The next slot number on the SD controller was
+ returned in Slot.
+ @retval EFI_NOT_FOUND There are no more slots on this SD controller.
+ @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a
+ previous call to GetNextSlot().
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruGetNextSlot (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 *Slot
+ )
+{
+ DW_MMC_HC_PRIVATE_DATA *Private;
+
+ if ((This == NULL) || (Slot == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (This);
+
+ if (*Slot == 0xFF) {
+ if (Private->Slot[0].Enable) {
+ *Slot = 0;
+ Private->PreviousSlot = 0;
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+ } else if (*Slot == Private->PreviousSlot) {
+ return EFI_NOT_FOUND;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
+/**
+ Used to allocate and build a device path node for an SD card on the SD
+ controller.
+
+ The BuildDevicePath() function allocates and builds a single device node
+ for the SD card specified by Slot.
+
+ If the SD card specified by Slot is not present on the SD controller, then
+ EFI_NOT_FOUND is returned.
+
+ If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned.
+
+ If there are not enough resources to allocate the device path node, then
+ EFI_OUT_OF_RESOURCES is returned.
+
+ Otherwise, DevicePath is allocated with the boot service AllocatePool(),
+ the contents of DevicePath are initialized to describe the SD card specified
+ by Slot, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot Specifies the slot number of the SD card for
+ which a device path node is to be allocated and
+ built.
+ @param[in,out] DevicePath A pointer to a single device path node that
+ describes the SD card specified by Slot. This
+ function is responsible for allocating the
+ buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's responsi-
+ bility to free DevicePath when the caller is
+ finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SD card
+ specified by Slot was allocated and returned in
+ DevicePath.
+ @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on
+ the SD controller.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate
+ DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruBuildDevicePath (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN UINT8 Slot,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ SD_DEVICE_PATH *SdNode;
+ EMMC_DEVICE_PATH *EmmcNode;
+
+ if ((This == NULL) || (DevicePath == NULL) || (Slot >= DW_MMC_HC_MAX_SLOT)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (This);
+
+ if ((!Private->Slot[Slot].Enable) || (!Private->Slot[Slot].MediaPresent)) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (Private->Slot[Slot].CardType == SdCardType) {
+ SdNode = AllocateCopyPool (sizeof (SD_DEVICE_PATH), &mSdDpTemplate);
+ if (SdNode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ SdNode->SlotNumber = Slot;
+
+ *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) SdNode;
+ } else if (Private->Slot[Slot].CardType == EmmcCardType) {
+ EmmcNode = AllocateCopyPool (sizeof (EMMC_DEVICE_PATH), &mEmmcDpTemplate);
+ if (EmmcNode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ EmmcNode->SlotNumber = Slot;
+
+ *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) EmmcNode;
+ } else {
+ //
+ // Currently we only support SD and EMMC two device nodes.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function retrieves an SD card slot number based on the input device path.
+
+ The GetSlotNumber() function retrieves slot number for the SD card specified
+ by the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is
+ returned.
+
+ If DevicePath is not a device path node type that the SD Pass Thru driver
+ supports, EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] DevicePath A pointer to the device path node that describes
+ a SD card on the SD controller.
+ @param[out] Slot On return, points to the slot number of an SD
+ card on the SD controller.
+
+ @retval EFI_SUCCESS SD card slot number is returned in Slot.
+ @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL.
+ @retval EFI_UNSUPPORTED DevicePath is not a device path node type that
+ the SD Pass Thru driver supports.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruGetSlotNumber (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 *Slot
+ )
+{
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ SD_DEVICE_PATH *SdNode;
+ EMMC_DEVICE_PATH *EmmcNode;
+ UINT8 SlotNumber;
+
+ if ((This == NULL) || (DevicePath == NULL) || (Slot == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (This);
+
+ //
+ // Check whether the DevicePath belongs to SD_DEVICE_PATH or EMMC_DEVICE_PATH
+ //
+ if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
+ ((DevicePath->SubType != MSG_SD_DP) &&
+ (DevicePath->SubType != MSG_EMMC_DP)) ||
+ (DevicePathNodeLength(DevicePath) != sizeof(SD_DEVICE_PATH)) ||
+ (DevicePathNodeLength(DevicePath) != sizeof(EMMC_DEVICE_PATH))) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (DevicePath->SubType == MSG_SD_DP) {
+ SdNode = (SD_DEVICE_PATH *) DevicePath;
+ SlotNumber = SdNode->SlotNumber;
+ } else {
+ EmmcNode = (EMMC_DEVICE_PATH *) DevicePath;
+ SlotNumber = EmmcNode->SlotNumber;
+ }
+
+ if (SlotNumber >= DW_MMC_HC_MAX_SLOT) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (Private->Slot[SlotNumber].Enable) {
+ *Slot = SlotNumber;
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+}
+
+/**
+ Resets an SD card that is connected to the SD controller.
+
+ The ResetDevice() function resets the SD card specified by Slot.
+
+ If this SD controller does not support a device reset operation,
+ EFI_UNSUPPORTED is returned.
+
+ If Slot is not in a valid slot number for this SD controller,
+ EFI_INVALID_PARAMETER is returned.
+
+ If the device reset operation is completed, EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot Specifies the slot number of the SD card to be
+ reset.
+
+ @retval EFI_SUCCESS The SD card specified by Slot was reset.
+ @retval EFI_UNSUPPORTED The SD controller does not support a device
+ reset operation.
+ @retval EFI_INVALID_PARAMETER Slot number is invalid.
+ @retval EFI_NO_MEDIA SD Device not present in the Slot.
+ @retval EFI_DEVICE_ERROR The reset command failed due to a device error
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcPassThruResetDevice (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This,
+ IN UINT8 Slot
+ )
+{
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NextLink;
+ DW_MMC_HC_TRB *Trb;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (This);
+
+ if (!Private->Slot[Slot].Enable) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Private->Slot[Slot].MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Free all async I/O requests in the queue
+ //
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+ for (Link = GetFirstNode (&Private->Queue);
+ !IsNull (&Private->Queue, Link);
+ Link = NextLink) {
+ NextLink = GetNextNode (&Private->Queue, Link);
+ RemoveEntryList (Link);
+ Trb = DW_MMC_HC_TRB_FROM_THIS (Link);
+ Trb->Packet->TransactionStatus = EFI_ABORTED;
+ gBS->SignalEvent (Trb->Event);
+ DwMmcFreeTrb (Trb);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c
new file mode 100644
index 000000000000..c261495387e6
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c
@@ -0,0 +1,2366 @@
+/** @file
+ This driver is used to manage Designware SD/MMC PCI host controllers.
+
+ It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Emmc.h>
+#include <IndustryStandard/Sd.h>
+
+#include <Library/ArmLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "DwMmcHcDxe.h"
+
+/**
+ Dump the content of SD/MMC host controller's Capability Register.
+
+ @param[in] Slot The slot number of the SD card to send the
+ command to.
+ @param[in] Capability The buffer to store the capability data.
+
+**/
+VOID
+DumpCapabilityReg (
+ IN UINT8 Slot,
+ IN DW_MMC_HC_SLOT_CAP *Capability
+ )
+{
+ //
+ // Dump Capability Data
+ //
+ DEBUG ((
+ DEBUG_INFO,
+ " == Slot [%d] Capability is 0x%x ==\n",
+ Slot,
+ Capability
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " Base Clk Freq %dKHz\n",
+ Capability->BaseClkFreq
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " BusWidth %d\n",
+ Capability->BusWidth
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " HighSpeed Support %a\n",
+ Capability->HighSpeed ? "TRUE" : "FALSE"
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " Voltage 1.8 %a\n",
+ Capability->Voltage18 ? "TRUE" : "FALSE"
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " 64-bit Sys Bus %a\n",
+ Capability->SysBus64 ? "TRUE" : "FALSE"
+ ));
+ DEBUG ((DEBUG_INFO, " SlotType "));
+ if (Capability->SlotType == 0x00) {
+ DEBUG ((DEBUG_INFO, "%a\n", "Removable Slot"));
+ } else if (Capability->SlotType == 0x01) {
+ DEBUG ((DEBUG_INFO, "%a\n", "Embedded Slot"));
+ } else if (Capability->SlotType == 0x02) {
+ DEBUG ((DEBUG_INFO, "%a\n", "Shared Bus Slot"));
+ } else {
+ DEBUG ((DEBUG_INFO, "%a\n", "Reserved"));
+ }
+ DEBUG ((
+ DEBUG_INFO,
+ " SDR50 Support %a\n",
+ Capability->Sdr50 ? "TRUE" : "FALSE"
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " SDR104 Support %a\n",
+ Capability->Sdr104 ? "TRUE" : "FALSE"
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " DDR50 Support %a\n",
+ Capability->Ddr50 ? "TRUE" : "FALSE"
+ ));
+ return;
+}
+
+/**
+ Read/Write specified SD/MMC host controller mmio register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset to start the memory operation.
+ @param[in] Read A boolean to indicate it's read or write
+ operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in, out] Data For read operations, the destination buffer to
+ store the results. For write operations, the
+ source buffer to write data from. The caller is
+ responsible for having ownership of the data
+ buffer and ensuring its size not less than
+ Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The DevIo or Data is NULL or the Count is not
+ valid.
+ @retval EFI_SUCCESS The read/write operation succeeds.
+ @retval Others The read/write operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcRwMmio (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN BOOLEAN Read,
+ IN UINT8 Count,
+ IN OUT VOID *Data
+ )
+{
+ EFI_STATUS Status;
+
+ if ((DevIo == NULL) || (Data == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Count != 4) && (Count != 8)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Since there's FIFO in Designware controller, map it to 32-bit word only.
+ //
+ Count = Count / sizeof (UINT32);
+ if (Read) {
+ Status = DevIo->Mem.Read (
+ DevIo,
+ IO_UINT32,
+ (UINT64) Offset,
+ Count,
+ Data
+ );
+ } else {
+ Status = DevIo->Mem.Write (
+ DevIo,
+ IO_UINT32,
+ (UINT64) Offset,
+ Count,
+ Data
+ );
+ }
+
+ return Status;
+}
+
+/**
+ Do OR operation with the value of the specified SD/MMC host controller mmio
+ register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset to start the memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in] OrData The pointer to the data used to do OR operation.
+ The caller is responsible for having ownership of
+ the data buffer and ensuring its size not less
+ than Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The DevIo or OrData is NULL or the Count is not
+ valid.
+ @retval EFI_SUCCESS The OR operation succeeds.
+ @retval Others The OR operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcOrMmio (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN VOID *OrData
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Data;
+ UINT64 Or;
+
+ Status = DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Count == 1) {
+ Or = *(UINT8*) OrData;
+ } else if (Count == 2) {
+ Or = *(UINT16*) OrData;
+ } else if (Count == 4) {
+ Or = *(UINT32*) OrData;
+ } else if (Count == 8) {
+ Or = *(UINT64*) OrData;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Data |= Or;
+ Status = DwMmcHcRwMmio (DevIo, Offset, FALSE, Count, &Data);
+
+ return Status;
+}
+
+/**
+ Do AND operation with the value of the specified SD/MMC host controller mmio
+ register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset to start the memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2 , 4 or 8 bytes.
+ @param[in] AndData The pointer to the data used to do AND operation.
+ The caller is responsible for having ownership of
+ the data buffer and ensuring its size not less
+ than Count bytes.
+
+ @retval EFI_INVALID_PARAMETER The DevIo or AndData is NULL or the Count is
+ not valid.
+ @retval EFI_SUCCESS The AND operation succeeds.
+ @retval Others The AND operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcAndMmio (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN VOID *AndData
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Data;
+ UINT64 And;
+
+ Status = DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Count == 1) {
+ And = *(UINT8*) AndData;
+ } else if (Count == 2) {
+ And = *(UINT16*) AndData;
+ } else if (Count == 4) {
+ And = *(UINT32*) AndData;
+ } else if (Count == 8) {
+ And = *(UINT64*) AndData;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Data &= And;
+ Status = DwMmcHcRwMmio (DevIo, Offset, FALSE, Count, &Data);
+
+ return Status;
+}
+
+/**
+ Wait for the value of the specified MMIO register set to the test value.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset to start the memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2, 4 or 8 bytes.
+ @param[in] MaskValue The mask value of memory.
+ @param[in] TestValue The test value of memory.
+
+ @retval EFI_NOT_READY The MMIO register hasn't set to the expected value.
+ @retval EFI_SUCCESS The MMIO register has expected value.
+ @retval Others The MMIO operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcCheckMmioSet (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN UINT64 MaskValue,
+ IN UINT64 TestValue
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Value;
+
+ Value = 0;
+ Status = DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Value);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Value &= MaskValue;
+
+ if (Value == TestValue) {
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_READY;
+}
+
+/**
+ Wait for the value of the specified MMIO register set to the test value.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Offset The offset to start the memory operation.
+ @param[in] Count The width of the mmio register in bytes.
+ Must be 1, 2, 4 or 8 bytes.
+ @param[in] MaskValue The mask value of memory.
+ @param[in] TestValue The test value of memory.
+ @param[in] Timeout The time out value for wait memory set, uses 1
+ microsecond as a unit.
+
+ @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout
+ range.
+ @retval EFI_SUCCESS The MMIO register has expected value.
+ @retval Others The MMIO operation fails.
+
+**/
+EFI_STATUS
+EFIAPI
+DwMmcHcWaitMmioSet (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT32 Offset,
+ IN UINT8 Count,
+ IN UINT64 MaskValue,
+ IN UINT64 TestValue,
+ IN UINT64 Timeout
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN InfiniteWait;
+
+ if (Timeout == 0) {
+ InfiniteWait = TRUE;
+ } else {
+ InfiniteWait = FALSE;
+ }
+
+ while (InfiniteWait || (Timeout > 0)) {
+ Status = DwMmcHcCheckMmioSet (
+ DevIo,
+ Offset,
+ Count,
+ MaskValue,
+ TestValue
+ );
+ if (Status != EFI_NOT_READY) {
+ return Status;
+ }
+
+ //
+ // Stall for 1 microsecond.
+ //
+ gBS->Stall (1);
+
+ Timeout--;
+ }
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Set all interrupt status bits in Normal and Error Interrupt Status Enable
+ register.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+
+ @retval EFI_SUCCESS The operation executes successfully.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+DwMmcHcEnableInterrupt (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ )
+{
+ EFI_STATUS Status;
+ UINT32 IntStatus;
+ UINT32 IdIntEn;
+ UINT32 IdSts;
+
+ //
+ // Enable all bits in Interrupt Mask Register
+ //
+ IntStatus = 0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_INTMASK,
+ FALSE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Clear status in Interrupt Status Register
+ //
+ IntStatus = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ FALSE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ IdIntEn = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_IDINTEN,
+ FALSE,
+ sizeof (IdIntEn),
+ &IdIntEn
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "DwMmcHcEnableInterrupt: init dma interrupts fail: %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ IdSts = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_IDSTS,
+ FALSE,
+ sizeof (IdSts),
+ &IdSts
+ );
+ return Status;
+}
+
+EFI_STATUS
+DwMmcHcGetCapability (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_HANDLE Controller,
+ IN UINT8 Slot,
+ OUT DW_MMC_HC_SLOT_CAP *Capacity
+ )
+{
+ PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc;
+ EFI_STATUS Status;
+
+ if (Capacity == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Status = gBS->LocateProtocol (
+ &gPlatformDwMmcProtocolGuid,
+ NULL,
+ (VOID **) &PlatformDwMmc
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = PlatformDwMmc->GetCapability (Controller, Slot, Capacity);
+ return Status;
+}
+
+/**
+ Detect whether there is a SD/MMC card attached at the specified SD/MMC host
+ controller slot.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command
+ to.
+ @param[out] MediaPresent The pointer to the media present boolean value.
+
+ @retval EFI_SUCCESS There is no media change happened.
+ @retval EFI_MEDIA_CHANGED There is media change happened.
+ @retval Others The detection fails.
+
+**/
+EFI_STATUS
+DwMmcHcCardDetect (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_HANDLE Controller,
+ IN UINT8 Slot,
+ OUT BOOLEAN *MediaPresent
+ )
+{
+ PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc;
+ EFI_STATUS Status;
+
+ if (MediaPresent == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Status = gBS->LocateProtocol (
+ &gPlatformDwMmcProtocolGuid,
+ NULL,
+ (VOID **) &PlatformDwMmc
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ *MediaPresent = PlatformDwMmc->CardDetect (Controller, Slot);
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+DwMmcHcUpdateClock (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Cmd;
+ UINT32 IntStatus;
+
+ Cmd = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
+ BIT_CMD_START;
+ Status = DwMmcHcRwMmio (DevIo, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ while (1) {
+ Status = DwMmcHcRwMmio (DevIo, DW_MMC_CMD, TRUE, sizeof (Cmd), &Cmd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (!(Cmd & CMD_START_BIT)) {
+ break;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ TRUE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (IntStatus & DW_MMC_INT_HLE) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "DwMmcHcUpdateClock: failed to update mmc clock frequency\n"
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ return EFI_SUCCESS;
+
+}
+
+/**
+ Stop SD/MMC card clock.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+
+ @retval EFI_SUCCESS Succeed to stop SD/MMC clock.
+ @retval Others Fail to stop SD/MMC clock.
+
+**/
+EFI_STATUS
+DwMmcHcStopClock (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ )
+{
+ EFI_STATUS Status;
+ UINT32 ClkEna;
+
+ //
+ // Disable MMC clock first
+ //
+ ClkEna = 0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CLKENA,
+ FALSE,
+ sizeof (ClkEna),
+ &ClkEna
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcUpdateClock (DevIo);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ return Status;
+}
+
+/**
+ SD/MMC card clock supply.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] ClockFreq The max clock frequency to be set. The unit is KHz.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The clock is supplied successfully.
+ @retval Others The clock isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcClockSupply (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN UINT64 ClockFreq,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BaseClkFreq;
+ UINT32 SettingFreq;
+ UINT32 Divisor;
+ UINT32 Remainder;
+ UINT32 MmcStatus;
+ UINT32 ClkEna;
+ UINT32 ClkSrc;
+
+ //
+ // Calculate a divisor for SD clock frequency
+ //
+ ASSERT (Capability.BaseClkFreq != 0);
+
+ BaseClkFreq = Capability.BaseClkFreq;
+ if (ClockFreq == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ClockFreq > BaseClkFreq) {
+ ClockFreq = BaseClkFreq;
+ }
+
+ //
+ // Calculate the divisor of base frequency.
+ //
+ Divisor = 0;
+ SettingFreq = BaseClkFreq;
+ while (ClockFreq < SettingFreq) {
+ Divisor++;
+
+ SettingFreq = BaseClkFreq / (2 * Divisor);
+ Remainder = BaseClkFreq % (2 * Divisor);
+ if ((ClockFreq == SettingFreq) && (Remainder == 0)) {
+ break;
+ }
+ if ((ClockFreq == SettingFreq) && (Remainder != 0)) {
+ SettingFreq ++;
+ }
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "BaseClkFreq %dKHz Divisor %d ClockFreq %dKhz\n",
+ BaseClkFreq,
+ Divisor,
+ ClockFreq
+ ));
+
+ //
+ // Wait until MMC is idle
+ //
+ do {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_STATUS,
+ TRUE,
+ sizeof (MmcStatus),
+ &MmcStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while (MmcStatus & DW_MMC_STS_DATA_BUSY);
+
+ do {
+ Status = DwMmcHcStopClock (DevIo);
+ } while (EFI_ERROR (Status));
+
+ do {
+ ClkSrc = 0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CLKSRC,
+ FALSE,
+ sizeof (ClkSrc),
+ &ClkSrc
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ //
+ // Set clock divisor
+ //
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CLKDIV,
+ FALSE,
+ sizeof (Divisor),
+ &Divisor
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ //
+ // Enable MMC clock
+ //
+ ClkEna = 1;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CLKENA,
+ FALSE,
+ sizeof (ClkEna),
+ &ClkEna
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ Status = DwMmcHcUpdateClock (DevIo);
+ } while (EFI_ERROR (Status));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the SD/MMC bus width.
+
+ Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] IsDdr A boolean to indicate it's dual data rate or not.
+ @param[in] BusWidth The bus width used by the SD/MMC device, it must be
+ 1, 4 or 8.
+
+ @retval EFI_SUCCESS The bus width is set successfully.
+ @retval Others The bus width isn't set successfully.
+
+**/
+EFI_STATUS
+DwMmcHcSetBusWidth (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN BOOLEAN IsDdr,
+ IN UINT16 BusWidth
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Ctype;
+ UINT32 Uhs;
+
+ switch (BusWidth) {
+ case 1:
+ Ctype = MMC_1BIT_MODE;
+ break;
+ case 4:
+ Ctype = MMC_4BIT_MODE;
+ break;
+ case 8:
+ Ctype = MMC_8BIT_MODE;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CTYPE,
+ FALSE,
+ sizeof (Ctype),
+ &Ctype
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (DevIo, DW_MMC_UHSREG, TRUE, sizeof (Uhs), &Uhs);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (IsDdr) {
+ Uhs |= UHS_DDR_MODE;
+ } else {
+ Uhs &= ~(UHS_DDR_MODE);
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_UHSREG,
+ FALSE,
+ sizeof (Uhs),
+ &Uhs
+ );
+ return Status;
+}
+
+/**
+ Supply SD/MMC card with lowest clock frequency at initialization.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The clock is supplied successfully.
+ @retval Others The clock isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitClockFreq (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ )
+{
+ EFI_STATUS Status;
+ UINT32 InitFreq;
+
+ //
+ // Calculate a divisor for SD clock frequency
+ //
+ if (Capability.BaseClkFreq == 0) {
+ //
+ // Don't support get Base Clock Frequency information via another method
+ //
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Supply 400KHz clock frequency at initialization phase.
+ //
+ InitFreq = DWMMC_INIT_CLOCK_FREQ;
+ Status = DwMmcHcClockSupply (DevIo, InitFreq, Capability);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ MicroSecondDelay (100);
+ return Status;
+}
+
+/**
+ Supply SD/MMC card with maximum voltage at initialization.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The voltage is supplied successfully.
+ @retval Others The voltage isn't supplied successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitPowerVoltage (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+
+ Data = 0x1;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_PWREN,
+ FALSE,
+ sizeof (Data),
+ &Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "DwMmcHcInitPowerVoltage: enable power fails: %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Data = DW_MMC_CTRL_RESET_ALL;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CTRL,
+ FALSE,
+ sizeof (Data),
+ &Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: reset fails: %r\n", Status));
+ return Status;
+ }
+ Status = DwMmcHcWaitMmioSet (
+ DevIo,
+ DW_MMC_CTRL,
+ sizeof (Data),
+ DW_MMC_CTRL_RESET_ALL,
+ 0x00,
+ DW_MMC_HC_GENERIC_TIMEOUT
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "DwMmcHcInitPowerVoltage: reset done with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Data = DW_MMC_CTRL_INT_EN;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CTRL,
+ FALSE,
+ sizeof (Data),
+ &Data
+ );
+ return Status;
+}
+
+/**
+ Initialize the Timeout Control register with most conservative value at
+ initialization.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+
+ @retval EFI_SUCCESS The timeout control register is configured
+ successfully.
+ @retval Others The timeout control register isn't configured
+ successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitTimeoutCtrl (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+
+ Data = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_TMOUT,
+ FALSE,
+ sizeof (Data),
+ &Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "DwMmcHcInitTimeoutCtrl: set timeout fails: %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Data = 0x00FFFFFF;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_DEBNCE,
+ FALSE,
+ sizeof (Data),
+ &Data
+ );
+ return Status;
+}
+
+/**
+ Initial SD/MMC host controller with lowest clock frequency, max power and
+ max timeout value at initialization.
+
+ @param[in] DevIo The DEVICE IO protocol instance.
+ @param[in] Slot The slot number of the SD card to send the command
+ to.
+ @param[in] Capability The capability of the slot.
+
+ @retval EFI_SUCCESS The host controller is initialized successfully.
+ @retval Others The host controller isn't initialized successfully.
+
+**/
+EFI_STATUS
+DwMmcHcInitHost (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN DW_MMC_HC_SLOT_CAP Capability
+ )
+{
+ EFI_STATUS Status;
+
+ Status = DwMmcHcInitPowerVoltage (DevIo, Capability);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ return Status;
+}
+
+EFI_STATUS
+DwMmcHcStartDma (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ UINT32 Ctrl;
+ UINT32 Bmod;
+
+ DevIo = Trb->Private->DevIo;
+
+ //
+ // Reset DMA
+ //
+ Ctrl = DW_MMC_CTRL_DMA_RESET;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CTRL,
+ FALSE,
+ sizeof (Ctrl),
+ &Ctrl
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: reset fails: %r\n", Status));
+ return Status;
+ }
+ Status = DwMmcHcWaitMmioSet (
+ DevIo,
+ DW_MMC_CTRL,
+ sizeof (Ctrl),
+ DW_MMC_CTRL_DMA_RESET,
+ 0x00,
+ DW_MMC_HC_GENERIC_TIMEOUT
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "DwMmcHcStartDma: reset done with %r\n", Status));
+ return Status;
+ }
+ Bmod = DW_MMC_IDMAC_SWRESET;
+ Status = DwMmcHcOrMmio (DevIo, DW_MMC_BMOD, sizeof (Bmod), &Bmod);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: set BMOD fail: %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Select IDMAC
+ //
+ Ctrl = DW_MMC_CTRL_IDMAC_EN;
+ Status = DwMmcHcOrMmio (DevIo, DW_MMC_CTRL, sizeof (Ctrl), &Ctrl);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: init IDMAC fail: %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Enable IDMAC
+ //
+ Bmod = DW_MMC_IDMAC_ENABLE | DW_MMC_IDMAC_FB;
+ Status = DwMmcHcOrMmio (DevIo, DW_MMC_BMOD, sizeof (Bmod), &Bmod);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: set BMOD failure: %r\n", Status));
+ return Status;
+ }
+ return Status;
+}
+
+EFI_STATUS
+DwMmcHcStopDma (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ UINT32 Ctrl;
+ UINT32 Bmod;
+
+ DevIo = Trb->Private->DevIo;
+
+ //
+ // Disable and reset IDMAC
+ //
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CTRL,
+ TRUE,
+ sizeof (Ctrl),
+ &Ctrl
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Ctrl &= ~DW_MMC_CTRL_IDMAC_EN;
+ Ctrl |= DW_MMC_CTRL_DMA_RESET;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CTRL,
+ FALSE,
+ sizeof (Ctrl),
+ &Ctrl
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Stop IDMAC
+ //
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BMOD,
+ TRUE,
+ sizeof (Bmod),
+ &Bmod
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Bmod &= ~(DW_MMC_BMOD_FB | DW_MMC_BMOD_DE);
+ Bmod |= DW_MMC_BMOD_SWR;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BMOD,
+ FALSE,
+ sizeof (Bmod),
+ &Bmod
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ return Status;
+}
+
+/**
+ Build DMA descriptor table for transfer.
+
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The DMA descriptor table is created successfully.
+ @retval Others The DMA descriptor table isn't created successfully.
+
+**/
+EFI_STATUS
+BuildDmaDescTable (
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_PHYSICAL_ADDRESS Data;
+ UINT64 DataLen;
+ UINT64 Entries;
+ UINT32 Index;
+ UINT64 Remaining;
+ UINTN TableSize;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ EFI_STATUS Status;
+ UINTN Bytes;
+ UINTN Blocks;
+ DW_MMC_HC_DMA_DESC_LINE *DmaDesc;
+ UINT32 DmaDescPhy;
+ UINT32 Idsts;
+ UINT32 BytCnt;
+ UINT32 BlkSize;
+
+ Data = Trb->DataPhy;
+ DataLen = Trb->DataLen;
+ DevIo = Trb->Private->DevIo;
+ //
+ // Only support 32bit DMA Descriptor Table
+ //
+ if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Address field shall be set on 32-bit boundary (Lower 2-bit is always set
+ // to 0) for 32-bit address descriptor table.
+ //
+ if ((Data & (BIT0 | BIT1)) != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "The buffer [0x%x] to construct DMA desc is not aligned to 4 bytes!\n",
+ Data
+ ));
+ }
+
+ Entries = (DataLen + DWMMC_DMA_BUF_SIZE - 1) / DWMMC_DMA_BUF_SIZE;
+ TableSize = Entries * sizeof (DW_MMC_HC_DMA_DESC_LINE);
+ Blocks = (DataLen + DW_MMC_BLOCK_SIZE - 1) / DW_MMC_BLOCK_SIZE;
+
+ Trb->DmaDescPages = (UINT32)EFI_SIZE_TO_PAGES (Entries * DWMMC_DMA_BUF_SIZE);
+ Status = DevIo->AllocateBuffer (
+ DevIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ EFI_SIZE_TO_PAGES (TableSize),
+ (EFI_PHYSICAL_ADDRESS *)&Trb->DmaDesc
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ ZeroMem (Trb->DmaDesc, TableSize);
+ Bytes = TableSize;
+ Status = DevIo->Map (
+ DevIo,
+ EfiBusMasterCommonBuffer,
+ (EFI_PHYSICAL_ADDRESS *)Trb->DmaDesc,
+ &Bytes,
+ &Trb->DmaDescPhy,
+ &Trb->DmaMap
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != TableSize)) {
+ //
+ // Map error or unable to map the whole RFis buffer into a contiguous
+ // region.
+ //
+ DevIo->FreeBuffer (
+ DevIo,
+ EFI_SIZE_TO_PAGES (TableSize),
+ (EFI_PHYSICAL_ADDRESS)Trb->DmaDesc
+ );
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if ((UINT64)(UINTN)Trb->DmaDescPhy > 0x100000000ul) {
+ //
+ // The DMA doesn't support 64bit addressing.
+ //
+ DevIo->Unmap (
+ DevIo,
+ Trb->DmaMap
+ );
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (DataLen < DW_MMC_BLOCK_SIZE) {
+ BlkSize = DataLen;
+ BytCnt = DataLen;
+ Remaining = DataLen;
+ } else {
+ BlkSize = DW_MMC_BLOCK_SIZE;
+ BytCnt = DW_MMC_BLOCK_SIZE * Blocks;
+ Remaining = DW_MMC_BLOCK_SIZE * Blocks;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BLKSIZ,
+ FALSE,
+ sizeof (BlkSize),
+ &BlkSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "BuildDmaDescTable: set block size fails: %r\n",
+ Status
+ ));
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BYTCNT,
+ FALSE,
+ sizeof (BytCnt),
+ &BytCnt
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ DmaDesc = Trb->DmaDesc;
+ for (Index = 0; Index < Entries; Index++, DmaDesc++) {
+ DmaDesc->Des0 = DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_CH |
+ DW_MMC_IDMAC_DES0_DIC;
+ DmaDesc->Des1 = DW_MMC_IDMAC_DES1_BS1 (DWMMC_DMA_BUF_SIZE);
+ //
+ // Buffer Address
+ //
+ DmaDesc->Des2 = (UINT32)((UINTN)Trb->DataPhy +
+ (DWMMC_DMA_BUF_SIZE * Index));
+ //
+ // Next Descriptor Address
+ //
+ DmaDesc->Des3 = (UINT32)((UINTN)Trb->DmaDescPhy +
+ sizeof (DW_MMC_HC_DMA_DESC_LINE) * (Index + 1));
+ Remaining = Remaining - DWMMC_DMA_BUF_SIZE;
+ }
+ //
+ // First Descriptor
+ //
+ Trb->DmaDesc[0].Des0 |= DW_MMC_IDMAC_DES0_FS;
+ //
+ // Last Descriptor
+ //
+ Trb->DmaDesc[Entries - 1].Des0 &= ~(DW_MMC_IDMAC_DES0_CH |
+ DW_MMC_IDMAC_DES0_DIC);
+ Trb->DmaDesc[Entries - 1].Des0 |= DW_MMC_IDMAC_DES0_OWN |
+ DW_MMC_IDMAC_DES0_LD;
+ Trb->DmaDesc[Entries - 1].Des1 = DW_MMC_IDMAC_DES1_BS1 (Remaining +
+ DWMMC_DMA_BUF_SIZE);
+ //
+ // Set the next field of the Last Descriptor
+ //
+ Trb->DmaDesc[Entries - 1].Des3 = 0;
+ DmaDescPhy = (UINT32)Trb->DmaDescPhy;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_DBADDR,
+ FALSE,
+ sizeof (DmaDescPhy),
+ &DmaDescPhy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+ //
+ // Clear interrupts
+ //
+ Idsts = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_IDSTS,
+ FALSE,
+ sizeof (Idsts),
+ &Idsts
+ );
+ return Status;
+}
+
+EFI_STATUS
+ReadFifo (
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ UINT32 Data;
+ UINT32 Received;
+ UINT32 Count;
+ UINT32 Intsts;
+ UINT32 Sts;
+ UINT32 FifoCount;
+ UINT32 Index; /* count with bytes */
+ UINT32 Ascending;
+ UINT32 Descending;
+
+ DevIo = Trb->Private->DevIo;
+ Received = 0;
+ Count = 0;
+ Index = 0;
+ Ascending = 0;
+ Descending = ((Trb->DataLen + 3) & ~3) - 4;
+ do {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ TRUE,
+ sizeof (Intsts),
+ &Intsts
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ReadFifo: failed to read RINTSTS, Status:%r\n",
+ Status
+ ));
+ return Status;
+ }
+ if (Trb->DataLen && ((Intsts & DW_MMC_INT_RXDR) ||
+ (Intsts & DW_MMC_INT_DTO))) {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_STATUS,
+ TRUE,
+ sizeof (Sts),
+ &Sts
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ReadFifo: failed to read STATUS, Status:%r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // Convert to bytes
+ //
+ FifoCount = GET_STS_FIFO_COUNT (Sts) << 2;
+ if ((FifoCount == 0) && (Received < Trb->DataLen)) {
+ continue;
+ }
+ Index = 0;
+ Count = (MIN (FifoCount, Trb->DataLen) + 3) & ~3;
+ while (Index < Count) {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_FIFO_START,
+ TRUE,
+ sizeof (Data),
+ &Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ReadFifo: failed to read FIFO, Status:%r\n",
+ Status
+ ));
+ return Status;
+ }
+ if (Trb->UseBE) {
+ *(UINT32 *)((UINTN)Trb->Data + Descending) = SwapBytes32 (Data);
+ Descending = Descending - 4;
+ } else {
+ *(UINT32 *)((UINTN)Trb->Data + Ascending) = Data;
+ Ascending += 4;
+ }
+ Index += 4;
+ Received += 4;
+ } /* while */
+ } /* if */
+ } while (((Intsts & DW_MMC_INT_CMD_DONE) == 0) || (Received < Trb->DataLen));
+ //
+ // Clear RINTSTS
+ //
+ Intsts = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ FALSE,
+ sizeof (Intsts),
+ &Intsts
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ReadFifo: failed to write RINTSTS, Status:%r\n",
+ Status
+ ));
+ return Status;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Create a new TRB for the SD/MMC cmd request.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Slot The slot number of the SD card to send the command
+ to.
+ @param[in] Packet A pointer to the SD command data structure.
+ @param[in] Event If Event is NULL, blocking I/O is performed. If
+ Event is not NULL, then nonblocking I/O is
+ performed, and Event will be signaled when the
+ Packet completes.
+
+ @return Created Trb or NULL.
+
+**/
+DW_MMC_HC_TRB *
+DwMmcCreateTrb (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN UINT8 Slot,
+ IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event
+ )
+{
+ DW_MMC_HC_TRB *Trb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_IO_OPERATION_TYPE Flag;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ UINTN MapLength;
+
+ Trb = AllocateZeroPool (sizeof (DW_MMC_HC_TRB));
+ if (Trb == NULL) {
+ return NULL;
+ }
+
+ Trb->Signature = DW_MMC_HC_TRB_SIG;
+ Trb->Slot = Slot;
+ Trb->BlockSize = 0x200;
+ Trb->Packet = Packet;
+ Trb->Event = Event;
+ Trb->Started = FALSE;
+ Trb->Timeout = Packet->Timeout;
+ Trb->Private = Private;
+
+ if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) {
+ Trb->Data = Packet->InDataBuffer;
+ Trb->DataLen = Packet->InTransferLength;
+ Trb->Read = TRUE;
+ ZeroMem (Trb->Data, Trb->DataLen);
+ } else if (Packet->OutTransferLength && (Packet->OutDataBuffer != NULL)) {
+ Trb->Data = Packet->OutDataBuffer;
+ Trb->DataLen = Packet->OutTransferLength;
+ Trb->Read = FALSE;
+ } else if (!Packet->InTransferLength && !Packet->OutTransferLength) {
+ Trb->Data = NULL;
+ Trb->DataLen = 0;
+ } else {
+ goto Error;
+ }
+
+ if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) &&
+ (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) ||
+ ((Private->Slot[Trb->Slot].CardType == SdCardType) &&
+ (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) {
+ Trb->Mode = SdMmcPioMode;
+ } else {
+ if (Trb->Read) {
+ Flag = EfiBusMasterWrite;
+ } else {
+ Flag = EfiBusMasterRead;
+ }
+
+ DevIo = Private->DevIo;
+ if (Private->Slot[Trb->Slot].CardType == SdCardType) {
+ Trb->UseFifo = TRUE;
+ } else {
+ Trb->UseFifo = FALSE;
+ if (Trb->DataLen) {
+ MapLength = Trb->DataLen;
+ Status = DevIo->Map (
+ DevIo,
+ Flag,
+ Trb->Data,
+ &MapLength,
+ &Trb->DataPhy,
+ &Trb->DataMap
+ );
+ if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) {
+ Status = EFI_BAD_BUFFER_SIZE;
+ goto Error;
+ }
+
+ Status = BuildDmaDescTable (Trb);
+ if (EFI_ERROR (Status)) {
+ DevIo->Unmap (DevIo, Trb->DataMap);
+ goto Error;
+ }
+ Status = DwMmcHcStartDma (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ DevIo->Unmap (DevIo, Trb->DataMap);
+ goto Error;
+ }
+ }
+ }
+ } /* TuningBlock */
+
+ if (Event != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Private->Queue, &Trb->TrbList);
+ gBS->RestoreTPL (OldTpl);
+ }
+
+ return Trb;
+
+Error:
+ return NULL;
+}
+
+/**
+ Free the resource used by the TRB.
+
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+**/
+VOID
+DwMmcFreeTrb (
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+
+ DevIo = Trb->Private->DevIo;
+
+ if (Trb->DmaMap != NULL) {
+ DevIo->Unmap (DevIo, Trb->DmaMap);
+ }
+ if (Trb->DataMap != NULL) {
+ DevIo->Unmap (DevIo, Trb->DataMap);
+ }
+ FreePool (Trb);
+}
+
+/**
+ Check if the env is ready for execute specified TRB.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The env is ready for TRB execution.
+ @retval EFI_NOT_READY The env is not ready for TRB execution.
+ @retval Others Some erros happen.
+
+**/
+EFI_STATUS
+DwMmcCheckTrbEnv (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait for the env to be ready for execute specified TRB.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The env is ready for TRB execution.
+ @retval EFI_TIMEOUT The env is not ready for TRB execution in time.
+ @retval Others Some erros happen.
+
+**/
+EFI_STATUS
+DwMmcWaitTrbEnv (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ UINT64 Timeout;
+ BOOLEAN InfiniteWait;
+
+ //
+ // Wait Command Complete Interrupt Status bit in Normal Interrupt Status
+ // Register
+ //
+ Packet = Trb->Packet;
+ Timeout = Packet->Timeout;
+ if (Timeout == 0) {
+ InfiniteWait = TRUE;
+ } else {
+ InfiniteWait = FALSE;
+ }
+
+ while (InfiniteWait || (Timeout > 0)) {
+ //
+ // Check Trb execution result by reading Normal Interrupt Status register.
+ //
+ Status = DwMmcCheckTrbEnv (Private, Trb);
+ if (Status != EFI_NOT_READY) {
+ return Status;
+ }
+ //
+ // Stall for 1 microsecond.
+ //
+ gBS->Stall (1);
+
+ Timeout--;
+ }
+
+ return EFI_TIMEOUT;
+}
+
+EFI_STATUS
+DwEmmcExecTrb (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ UINT32 Cmd;
+ UINT32 MmcStatus;
+ UINT32 IntStatus;
+ UINT32 Argument;
+ UINT32 ErrMask;
+ UINT32 Timeout;
+
+ Packet = Trb->Packet;
+ DevIo = Trb->Private->DevIo;
+
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+ //
+ // Wait until MMC is idle
+ //
+ do {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_STATUS,
+ TRUE,
+ sizeof (MmcStatus),
+ &MmcStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while (MmcStatus & DW_MMC_STS_DATA_BUSY);
+
+ IntStatus = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ FALSE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex);
+ if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) ||
+ (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) {
+ switch (Packet->SdMmcCmdBlk->CommandIndex) {
+ case EMMC_SET_RELATIVE_ADDR:
+ Cmd |= BIT_CMD_SEND_INIT;
+ break;
+ case EMMC_SEND_STATUS:
+ Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE;
+ break;
+ case EMMC_STOP_TRANSMISSION:
+ Cmd |= BIT_CMD_STOP_ABORT_CMD;
+ break;
+ }
+ if (Packet->InTransferLength) {
+ Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED |
+ BIT_CMD_READ;
+ } else if (Packet->OutTransferLength) {
+ Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED |
+ BIT_CMD_WRITE;
+ }
+ Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
+ } else {
+ switch (Packet->SdMmcCmdBlk->CommandIndex) {
+ case EMMC_GO_IDLE_STATE:
+ Cmd |= BIT_CMD_SEND_INIT;
+ break;
+ case EMMC_SEND_OP_COND:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT;
+ break;
+ case EMMC_ALL_SEND_CID:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE |
+ BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT;
+ break;
+ }
+ }
+ switch (Packet->SdMmcCmdBlk->ResponseType) {
+ case SdMmcResponseTypeR2:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+ BIT_CMD_LONG_RESPONSE;
+ break;
+ case SdMmcResponseTypeR3:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT;
+ break;
+ }
+ Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START;
+
+ Argument = Packet->SdMmcCmdBlk->CommandArgument;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CMDARG,
+ FALSE,
+ sizeof (Argument),
+ &Argument
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CMD,
+ FALSE,
+ sizeof (Cmd),
+ &Cmd
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+
+ ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO |
+ DW_MMC_INT_RCRC | DW_MMC_INT_RE;
+ ErrMask |= DW_MMC_INT_DCRC | DW_MMC_INT_DRT | DW_MMC_INT_SBE;
+ do {
+ Timeout = 10000;
+ if (--Timeout == 0) {
+ break;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ TRUE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (IntStatus & ErrMask) {
+ return EFI_DEVICE_ERROR;
+ }
+ if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) == 0)) {
+ //
+ // Transfer Not Done
+ //
+ MicroSecondDelay (10);
+ continue;
+ }
+ MicroSecondDelay (10);
+ } while (!(IntStatus & DW_MMC_INT_CMD_DONE));
+ switch (Packet->SdMmcCmdBlk->ResponseType) {
+ case SdMmcResponseTypeR1:
+ case SdMmcResponseTypeR1b:
+ case SdMmcResponseTypeR3:
+ case SdMmcResponseTypeR4:
+ case SdMmcResponseTypeR5:
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP0,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp0),
+ &Packet->SdMmcStatusBlk->Resp0
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ break;
+ case SdMmcResponseTypeR2:
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP0,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp0),
+ &Packet->SdMmcStatusBlk->Resp0
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP1,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp1),
+ &Packet->SdMmcStatusBlk->Resp1
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP2,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp2),
+ &Packet->SdMmcStatusBlk->Resp2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP3,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp3),
+ &Packet->SdMmcStatusBlk->Resp3
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ break;
+ }
+
+ //
+ // The workaround on EMMC_SEND_CSD is used to be compatible with SDHC.
+ //
+ if (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_CSD) {
+ {
+ UINT32 Buf[4];
+ ZeroMem (Buf, sizeof (Buf));
+ CopyMem (
+ (UINT8 *)Buf,
+ (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1,
+ sizeof (Buf) - 1
+ );
+ CopyMem (
+ (UINT8 *)&Packet->SdMmcStatusBlk->Resp0,
+ (UINT8 *)Buf,
+ sizeof (Buf) - 1
+ );
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwSdExecTrb (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ UINT32 Cmd;
+ UINT32 MmcStatus;
+ UINT32 IntStatus;
+ UINT32 Argument;
+ UINT32 ErrMask;
+ UINT32 Timeout;
+ UINT32 Idsts;
+ UINT32 BytCnt;
+ UINT32 BlkSize;
+
+ Packet = Trb->Packet;
+ DevIo = Trb->Private->DevIo;
+
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+ //
+ // Wait until MMC is idle
+ //
+ do {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_STATUS,
+ TRUE,
+ sizeof (MmcStatus),
+ &MmcStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while (MmcStatus & DW_MMC_STS_DATA_BUSY);
+
+ IntStatus = ~0;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ FALSE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex);
+ if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) ||
+ (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) {
+ switch (Packet->SdMmcCmdBlk->CommandIndex) {
+ case SD_SET_RELATIVE_ADDR:
+ Cmd |= BIT_CMD_SEND_INIT;
+ break;
+ case SD_STOP_TRANSMISSION:
+ Cmd |= BIT_CMD_STOP_ABORT_CMD;
+ break;
+ case SD_SEND_SCR:
+ Trb->UseBE = TRUE;
+ break;
+ }
+ if (Packet->InTransferLength) {
+ Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED |
+ BIT_CMD_READ;
+ } else if (Packet->OutTransferLength) {
+ Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED |
+ BIT_CMD_WRITE;
+ }
+ Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+ BIT_CMD_SEND_AUTO_STOP;
+ } else {
+ switch (Packet->SdMmcCmdBlk->CommandIndex) {
+ case SD_GO_IDLE_STATE:
+ Cmd |= BIT_CMD_SEND_INIT;
+ break;
+ }
+ }
+ switch (Packet->SdMmcCmdBlk->ResponseType) {
+ case SdMmcResponseTypeR2:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+ BIT_CMD_LONG_RESPONSE;
+ break;
+ case SdMmcResponseTypeR3:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT;
+ break;
+ case SdMmcResponseTypeR1b:
+ case SdMmcResponseTypeR4:
+ case SdMmcResponseTypeR6:
+ case SdMmcResponseTypeR7:
+ Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
+ break;
+ }
+ Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START;
+
+ if (Trb->UseFifo == TRUE) {
+ BytCnt = Packet->InTransferLength;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BYTCNT,
+ FALSE,
+ sizeof (BytCnt),
+ &BytCnt
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (Packet->InTransferLength > DW_MMC_BLOCK_SIZE) {
+ BlkSize = DW_MMC_BLOCK_SIZE;
+ } else {
+ BlkSize = Packet->InTransferLength;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_BLKSIZ,
+ FALSE,
+ sizeof (BlkSize),
+ &BlkSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DwSdExecTrb: set block size fails: %r\n", Status));
+ return Status;
+ }
+ }
+
+ Argument = Packet->SdMmcCmdBlk->CommandArgument;
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CMDARG,
+ FALSE,
+ sizeof (Argument),
+ &Argument
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_CMD,
+ FALSE,
+ sizeof (Cmd),
+ &Cmd
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ArmDataSynchronizationBarrier ();
+ ArmInstructionSynchronizationBarrier ();
+
+ ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO |
+ DW_MMC_INT_RCRC | DW_MMC_INT_RE;
+ ErrMask |= DW_MMC_INT_DRT | DW_MMC_INT_SBE;
+ if (Packet->InTransferLength || Packet->OutTransferLength) {
+ ErrMask |= DW_MMC_INT_DCRC;
+ }
+ if (Trb->UseFifo == TRUE) {
+ Status = ReadFifo (Trb);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ Timeout = 10000;
+ do {
+ if (--Timeout == 0) {
+ break;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RINTSTS,
+ TRUE,
+ sizeof (IntStatus),
+ &IntStatus
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (IntStatus & ErrMask) {
+ return EFI_DEVICE_ERROR;
+ }
+ if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) == 0)) {
+ //
+ // Transfer not Done
+ //
+ MicroSecondDelay (10);
+ continue;
+ }
+ MicroSecondDelay (10);
+ } while (!(IntStatus & DW_MMC_INT_CMD_DONE));
+ if (Packet->InTransferLength) {
+ do {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_IDSTS,
+ TRUE,
+ sizeof (Idsts),
+ &Idsts
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while ((Idsts & DW_MMC_IDSTS_RI) == 0);
+ Status = DwMmcHcStopDma (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else if (Packet->OutTransferLength) {
+ do {
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_IDSTS,
+ TRUE,
+ sizeof (Idsts),
+ &Idsts
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while ((Idsts & DW_MMC_IDSTS_TI) == 0);
+ Status = DwMmcHcStopDma (Private, Trb);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } /* Packet->InTransferLength */
+ } /* UseFifo */
+ switch (Packet->SdMmcCmdBlk->ResponseType) {
+ case SdMmcResponseTypeR1:
+ case SdMmcResponseTypeR1b:
+ case SdMmcResponseTypeR3:
+ case SdMmcResponseTypeR4:
+ case SdMmcResponseTypeR5:
+ case SdMmcResponseTypeR6:
+ case SdMmcResponseTypeR7:
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP0,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp0),
+ &Packet->SdMmcStatusBlk->Resp0
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ break;
+ case SdMmcResponseTypeR2:
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP0,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp0),
+ &Packet->SdMmcStatusBlk->Resp0
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP1,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp1),
+ &Packet->SdMmcStatusBlk->Resp1
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP2,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp2),
+ &Packet->SdMmcStatusBlk->Resp2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = DwMmcHcRwMmio (
+ DevIo,
+ DW_MMC_RESP3,
+ TRUE,
+ sizeof (Packet->SdMmcStatusBlk->Resp3),
+ &Packet->SdMmcStatusBlk->Resp3
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ break;
+ }
+
+ //
+ // The workaround on SD_SEND_CSD is used to be compatible with SDHC.
+ //
+ if (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_CSD) {
+ {
+ UINT32 Buf[4];
+ ZeroMem (Buf, sizeof (Buf));
+ CopyMem (
+ (UINT8 *)Buf,
+ (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1,
+ sizeof (Buf) - 1
+ );
+ CopyMem (
+ (UINT8 *)&Packet->SdMmcStatusBlk->Resp0,
+ (UINT8 *)Buf,
+ sizeof (Buf) - 1
+ );
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Execute the specified TRB.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The TRB is sent to host controller successfully.
+ @retval Others Some erros happen when sending this request to the
+ host controller.
+
+**/
+EFI_STATUS
+DwMmcExecTrb (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ UINT32 Slot;
+
+ Slot = Trb->Slot;
+ if (Private->Slot[Slot].CardType == EmmcCardType) {
+ Status = DwEmmcExecTrb (Private, Trb);
+ } else if (Private->Slot[Slot].CardType == SdCardType) {
+ Status = DwSdExecTrb (Private, Trb);
+ } else {
+ ASSERT (0);
+ }
+ return Status;
+}
+
+/**
+ Check the TRB execution result.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The TRB is executed successfully.
+ @retval EFI_NOT_READY The TRB is not completed for execution.
+ @retval Others Some erros happen when executing this request.
+
+**/
+EFI_STATUS
+DwMmcCheckTrbResult (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ UINT32 Idsts;
+
+ Packet = Trb->Packet;
+ if (Trb->UseFifo == TRUE) {
+ return EFI_SUCCESS;
+ }
+ if (Packet->InTransferLength) {
+ do {
+ Status = DwMmcHcRwMmio (
+ Private->DevIo,
+ DW_MMC_IDSTS,
+ TRUE,
+ sizeof (Idsts),
+ &Idsts
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while ((Idsts & BIT1) == 0);
+ } else if (Packet->OutTransferLength) {
+ do {
+ Status = DwMmcHcRwMmio (
+ Private->DevIo,
+ DW_MMC_IDSTS,
+ TRUE,
+ sizeof (Idsts),
+ &Idsts
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } while ((Idsts & BIT0) == 0);
+ } else {
+ return EFI_SUCCESS;
+ }
+ Idsts = ~0;
+ Status = DwMmcHcRwMmio (
+ Private->DevIo,
+ DW_MMC_IDSTS,
+ FALSE,
+ sizeof (Idsts), &Idsts);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait for the TRB execution result.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+ @param[in] Trb The pointer to the DW_MMC_HC_TRB instance.
+
+ @retval EFI_SUCCESS The TRB is executed successfully.
+ @retval Others Some erros happen when executing this request.
+
+**/
+EFI_STATUS
+DwMmcWaitTrbResult (
+ IN DW_MMC_HC_PRIVATE_DATA *Private,
+ IN DW_MMC_HC_TRB *Trb
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet;
+ UINT64 Timeout;
+ BOOLEAN InfiniteWait;
+
+ Packet = Trb->Packet;
+ //
+ // Wait Command Complete Interrupt Status bit in Normal Interrupt Status
+ // Register
+ //
+ Timeout = Packet->Timeout;
+ if (Timeout == 0) {
+ InfiniteWait = TRUE;
+ } else {
+ InfiniteWait = FALSE;
+ }
+
+ while (InfiniteWait || (Timeout > 0)) {
+ //
+ // Check Trb execution result by reading Normal Interrupt Status register.
+ //
+ Status = DwMmcCheckTrbResult (Private, Trb);
+ if (Status != EFI_NOT_READY) {
+ return Status;
+ }
+ //
+ // Stall for 1 microsecond.
+ //
+ gBS->Stall (1);
+
+ Timeout--;
+ }
+
+ return EFI_TIMEOUT;
+}
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c b/EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c
new file mode 100644
index 000000000000..cf7c7195a569
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c
@@ -0,0 +1,1042 @@
+/** @file
+ This file provides some helper functions which are specific for EMMC device.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2018, Linaro. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Emmc.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "DwMmcHcDxe.h"
+
+#define EMMC_GET_STATE(x) (((x) >> 9) & 0xf)
+#define EMMC_STATE_IDLE 0
+#define EMMC_STATE_READY 1
+#define EMMC_STATE_IDENT 2
+#define EMMC_STATE_STBY 3
+#define EMMC_STATE_TRAN 4
+#define EMMC_STATE_DATA 5
+#define EMMC_STATE_RCV 6
+#define EMMC_STATE_PRG 7
+#define EMMC_STATE_DIS 8
+#define EMMC_STATE_BTST 9
+#define EMMC_STATE_SLP 10
+
+#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
+
+/**
+ Send command GO_IDLE_STATE (CMD0 with argument of 0x00000000) to the device to
+ make it go to Idle State.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Slot The slot number of the SD card to send the command
+ to.
+
+ @retval EFI_SUCCESS The EMMC device is reset correctly.
+ @retval Others The device reset fails.
+
+**/
+EFI_STATUS
+EmmcReset (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_GO_IDLE_STATE;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBc;
+ SdMmcCmdBlk.ResponseType = 0;
+ SdMmcCmdBlk.CommandArgument = 0;
+
+ gBS->Stall (1000);
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_OP_COND to the EMMC device to get the data of the OCR
+ register.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in, out] Argument On input, the argument of SEND_OP_COND is to send
+ to the device.
+ On output, the argument is the value of OCR
+ register.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcGetOcr (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN OUT UINT32 *Argument
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_OP_COND;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR3;
+ SdMmcCmdBlk.CommandArgument = *Argument;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
+ //
+ *Argument = SdMmcStatusBlk.Resp0;
+ }
+
+ return Status;
+}
+
+/**
+ Broadcast command ALL_SEND_CID to the bus to ask all the EMMC devices to send
+ the data of their CID registers.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcGetAllCid (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_ALL_SEND_CID;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = 0;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SET_RELATIVE_ADDR to the EMMC device to assign a Relative device
+ Address (RCA).
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSetRca (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SET_RELATIVE_ADDR;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_CSD to the EMMC device to get the data of the CSD register.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of selected device.
+ @param[out] Csd The buffer to store the content of the CSD register.
+ Note the caller should ignore the lowest byte of
+ this buffer as the content of this byte is
+ meaningless even if the operation succeeds.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcGetCsd (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ OUT EMMC_CSD *Csd
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_CSD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Copy 128bit data for CSD structure.
+ //
+ CopyMem ((VOID *)Csd + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SELECT_DESELECT_CARD to the EMMC device to select/deselect it.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of selected device.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSelect (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SELECT_DESELECT_CARD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_EXT_CSD to the EMMC device to get the data of the EXT_CSD
+ register.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[out] ExtCsd The buffer to store the content of the EXT_CSD
+ register.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcGetExtCsd (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ OUT EMMC_EXT_CSD *ExtCsd
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_EXT_CSD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = 0x00000000;
+
+ Packet.InDataBuffer = ExtCsd;
+ Packet.InTransferLength = sizeof (EMMC_EXT_CSD);
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ return Status;
+}
+
+/**
+ Send command SWITCH to the EMMC device to switch the mode of operation of the
+ selected Device or modifies the EXT_CSD registers.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Access The access mode of SWTICH command.
+ @param[in] Index The offset of the field to be access.
+ @param[in] Value The value to be set to the specified field of
+ EXT_CSD register.
+ @param[in] CmdSet The value of CmdSet field of EXT_CSD register.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSwitch (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 Access,
+ IN UINT8 Index,
+ IN UINT8 Value,
+ IN UINT8 CmdSet
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SWITCH;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;
+ SdMmcCmdBlk.CommandArgument = (Access << 24) | (Index << 16) | \
+ (Value << 8) | CmdSet;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_STATUS to the addressed EMMC device to get its status
+ register.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of addressed device.
+ @param[out] DevStatus The returned device status.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSendStatus (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ OUT UINT32 *DevStatus
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_STATUS;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ *DevStatus = SdMmcStatusBlk.Resp0;
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_TUNING_BLOCK to the EMMC device for HS200 optimal sampling
+ point detection.
+
+ It may be sent up to 40 times until the host finishes the tuning procedure.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] BusWidth The bus width to work.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSendTuningBlk (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 BusWidth
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+ UINT8 TuningBlock[128];
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_TUNING_BLOCK;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = 0;
+
+ Packet.InDataBuffer = TuningBlock;
+ if (BusWidth == 8) {
+ Packet.InTransferLength = sizeof (TuningBlock);
+ } else {
+ Packet.InTransferLength = 64;
+ }
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Tunning the clock to get HS200 optimal sampling point.
+
+ Command SEND_TUNING_BLOCK may be sent up to 40 times until the host finishes
+ the tuning procedure.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8.
+
+ @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] BusWidth The bus width to work.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcTuningClkForHs200 (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 BusWidth
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ Switch the bus width to specified width.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.9.
+
+ @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+ @param[in] IsDdr If TRUE, use dual data rate data simpling method.
+ Otherwise use single data rate data simpling method.
+ @param[in] BusWidth The bus width to be set, it could be 4 or 8.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSwitchBusWidth (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN BOOLEAN IsDdr,
+ IN UINT8 BusWidth
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Access;
+ UINT8 Index;
+ UINT8 Value;
+ UINT8 CmdSet;
+ UINT32 DevStatus;
+
+ //
+ // Write Byte, the Value field is written into the byte pointed by Index.
+ //
+ Access = 0x03;
+ Index = OFFSET_OF (EMMC_EXT_CSD, BusWidth);
+ if (BusWidth == 1) {
+ Value = 0;
+ } else {
+ if (BusWidth == 4) {
+ Value = 1;
+ } else if (BusWidth == 8) {
+ Value = 2;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IsDdr) {
+ Value += 4;
+ }
+ }
+
+ CmdSet = 0;
+ Status = EmmcSwitch (PassThru, Access, Index, Value, CmdSet);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "EmmcSwitchBusWidth: Switch to bus width %d fails with %r\n",
+ BusWidth,
+ Status
+ ));
+ return Status;
+ }
+
+ do {
+ Status = EmmcSendStatus (PassThru, Rca, &DevStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "EmmcSwitchBusWidth: Send status fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // Check the switch operation is really successful or not.
+ //
+ } while ((DevStatus & 0xf) == EMMC_STATE_PRG);
+
+ Status = DwMmcHcSetBusWidth (DevIo, IsDdr, BusWidth);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
+/**
+ Switch the clock frequency to the specified value.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.
+
+ @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+ @param[in] HsTiming The value to be written to HS_TIMING field of
+ EXT_CSD register.
+ @param[in] ClockFreq The max clock frequency to be set, the unit is MHz.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSwitchClockFreq (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN UINT8 HsTiming,
+ IN UINT32 ClockFreq
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Access;
+ UINT8 Index;
+ UINT8 Value;
+ UINT8 CmdSet;
+ UINT32 DevStatus;
+ DW_MMC_HC_PRIVATE_DATA *Private;
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru);
+ //
+ // Write Byte, the Value field is written into the byte pointed by Index.
+ //
+ Access = 0x03;
+ Index = OFFSET_OF (EMMC_EXT_CSD, HsTiming);
+ Value = HsTiming;
+ CmdSet = 0;
+
+ Status = EmmcSwitch (PassThru, Access, Index, Value, CmdSet);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "EmmcSwitchClockFreq: Switch to hstiming %d fails with %r\n",
+ HsTiming,
+ Status
+ ));
+ return Status;
+ }
+
+ Status = EmmcSendStatus (PassThru, Rca, &DevStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "EmmcSwitchClockFreq: Send status fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // Check the switch operation is really successful or not.
+ //
+ if ((DevStatus & BIT7) != 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "EmmcSwitchClockFreq: The switch operation fails as DevStatus 0x%08x\n",
+ DevStatus
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Convert the clock freq unit from MHz to KHz.
+ //
+ Status = DwMmcHcClockSupply (DevIo, ClockFreq * 1000, Private->Capability[0]);
+
+ return Status;
+}
+
+/**
+ Switch to the High Speed timing according to request.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8.
+
+ @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+ @param[in] ClockFreq The max clock frequency to be set.
+ @param[in] IsDdr If TRUE, use dual data rate data simpling method.
+ Otherwise use single data rate data simpling method.
+ @param[in] BusWidth The bus width to be set, it could be 4 or 8.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSwitchToHighSpeed (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN UINT32 ClockFreq,
+ IN BOOLEAN IsDdr,
+ IN UINT8 BusWidth
+ )
+{
+ EFI_STATUS Status;
+ UINT8 HsTiming;
+
+ HsTiming = 1;
+ Status = EmmcSwitchClockFreq (DevIo, PassThru, Rca, HsTiming, ClockFreq);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = EmmcSwitchBusWidth (DevIo, PassThru, Rca, IsDdr, BusWidth);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Switch to the HS200 timing according to request.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8.
+
+ @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+ @param[in] ClockFreq The max clock frequency to be set.
+ @param[in] BusWidth The bus width to be set, it could be 4 or 8.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSwitchToHS200 (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN UINT32 ClockFreq,
+ IN UINT8 BusWidth
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ Switch the high speed timing according to request.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8.
+
+ @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+EmmcSetBusMode (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca
+ )
+{
+ EFI_STATUS Status;
+ EMMC_CSD Csd;
+ EMMC_EXT_CSD ExtCsd;
+ UINT8 HsTiming;
+ BOOLEAN IsDdr;
+ UINT32 DevStatus;
+ UINT32 ClockFreq;
+ UINT8 BusWidth;
+ DW_MMC_HC_PRIVATE_DATA *Private;
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru);
+ ASSERT (Private->Capability[0].BaseClkFreq != 0);
+
+ Status = EmmcGetCsd (PassThru, Rca, &Csd);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetCsd fails with %r\n", Status));
+ return Status;
+ }
+
+ Status = EmmcSelect (PassThru, Rca);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: Select fails with %r\n", Status));
+ return Status;
+ }
+
+ do {
+ Status = EmmcSendStatus (PassThru, Rca, &DevStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "EmmcSetBusMode: Get Status fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ } while (EMMC_GET_STATE (DevStatus) != EMMC_STATE_TRAN);
+
+ BusWidth = 1;
+ Status = EmmcSwitchBusWidth (DevIo, PassThru, Rca, FALSE, BusWidth);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BusWidth = Private->Capability[0].BusWidth;
+ //
+ // Get Deivce_Type from EXT_CSD register.
+ //
+ Status = EmmcGetExtCsd (PassThru, &ExtCsd);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetExtCsd fails with %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Calculate supported bus speed/bus width/clock frequency.
+ //
+ HsTiming = 0;
+ IsDdr = FALSE;
+ ClockFreq = 0;
+ if (((ExtCsd.DeviceType & (BIT4 | BIT5)) != 0) &&
+ (Private->Capability[0].Sdr104 != 0)) {
+ HsTiming = 2;
+ IsDdr = FALSE;
+ ClockFreq = 200;
+ } else if (((ExtCsd.DeviceType & (BIT2 | BIT3)) != 0) &&
+ (Private->Capability[0].Ddr50 != 0)) {
+ HsTiming = 1;
+ IsDdr = TRUE;
+ ClockFreq = 52;
+ } else if (((ExtCsd.DeviceType & BIT1) != 0) &&
+ (Private->Capability[0].HighSpeed != 0)) {
+ HsTiming = 1;
+ IsDdr = FALSE;
+ ClockFreq = 52;
+ } else if (((ExtCsd.DeviceType & BIT0) != 0) &&
+ (Private->Capability[0].HighSpeed != 0)) {
+ HsTiming = 1;
+ IsDdr = FALSE;
+ ClockFreq = 26;
+ }
+
+ if ((ClockFreq == 0) || (HsTiming == 0)) {
+ //
+ // Continue using default setting.
+ //
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcSetBusMode: HsTiming %d ClockFreq %d BusWidth %d Ddr %a\n",
+ HsTiming,
+ ClockFreq,
+ BusWidth,
+ IsDdr ? "TRUE" : "FALSE"
+ ));
+
+ if (HsTiming == 2) {
+ //
+ // Execute HS200 timing switch procedure
+ //
+ Status = EmmcSwitchToHS200 (DevIo, PassThru, Rca, ClockFreq, BusWidth);
+ } else {
+ //
+ // Execute High Speed timing switch procedure
+ //
+ Status = EmmcSwitchToHighSpeed (
+ DevIo,
+ PassThru,
+ Rca,
+ ClockFreq,
+ IsDdr,
+ BusWidth
+ );
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcSetBusMode: Switch to %a %r\n",
+ (HsTiming == 3) ? "HS400" : ((HsTiming == 2) ? "HS200" : "HighSpeed"),
+ Status
+ ));
+
+ return Status;
+}
+
+/**
+ Execute EMMC device identification procedure.
+
+ Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
+
+ @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance.
+
+ @retval EFI_SUCCESS There is a EMMC card.
+ @retval Others There is not a EMMC card.
+
+**/
+EFI_STATUS
+EmmcIdentification (
+ IN DW_MMC_HC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ UINT32 Ocr;
+ UINT16 Rca;
+ UINT32 DevStatus;
+ UINT32 Timeout;
+
+ DevIo = Private->DevIo;
+ PassThru = &Private->PassThru;
+
+ Status = EmmcReset (PassThru);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcIdentification: Executing Cmd0 fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Timeout = 100;
+ do {
+ Ocr = EMMC_CMD1_CAPACITY_GREATER_THAN_2GB;
+ Status = EmmcGetOcr (PassThru, &Ocr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcIdentification: Executing Cmd1 fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ if (--Timeout <= 0) {
+ return EFI_DEVICE_ERROR;
+ }
+ MicroSecondDelay (100);
+ } while ((Ocr & BIT31) == 0);
+
+ Status = EmmcGetAllCid (PassThru);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcIdentification: Executing Cmd2 fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // valid RCA starts from 1.
+ // Here we takes a simple formula to calculate the RCA.
+ // Don't support multiple devices on the slot, that is
+ // shared bus slot feature.
+ //
+ Rca = 1;
+ Status = EmmcSetRca (PassThru, Rca);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcIdentification: Executing Cmd3 fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // Enter Data Tranfer Mode.
+ //
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcIdentification: Found a EMMC device at RCA [%d]\n",
+ Rca
+ ));
+ Private->Slot[0].CardType = EmmcCardType;
+
+ Status = EmmcSetBusMode (DevIo, PassThru, Rca);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Exit DATA Mode.
+ //
+ do {
+ Status = EmmcSendStatus (PassThru, Rca, &DevStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "EmmcSwitchBusWidth: Send status fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ } while ((DevStatus & 0xf) == EMMC_STATE_DATA);
+
+ return Status;
+}
diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c b/EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c
new file mode 100644
index 000000000000..58900b03417f
--- /dev/null
+++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c
@@ -0,0 +1,1104 @@
+/** @file
+ This file provides some helper functions which are specific for SD card
+ device.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2018, Linaro. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Sd.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+
+#include "DwMmcHcDxe.h"
+
+/**
+ Send command GO_IDLE_STATE to the device to make it go to Idle State.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+
+ @retval EFI_SUCCESS The SD device is reset correctly.
+ @retval Others The device reset fails.
+
+**/
+EFI_STATUS
+SdCardReset (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_GO_IDLE_STATE;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBc;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_IF_COND to the device to inquiry the SD Memory Card
+ interface condition.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] SupplyVoltage The supplied voltage by the host.
+ @param[in] CheckPattern The check pattern to be sent to the device.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardVoltageCheck (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 SupplyVoltage,
+ IN UINT8 CheckPattern
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_IF_COND;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR7;
+ SdMmcCmdBlk.CommandArgument = (SupplyVoltage << 8) | CheckPattern;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ if (!EFI_ERROR (Status)) {
+ if (SdMmcStatusBlk.Resp0 != SdMmcCmdBlk.CommandArgument) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Send command SDIO_SEND_OP_COND to the device to see whether it is SDIO device.
+
+ Refer to SDIO Simplified Spec 3 Section 3.2 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] VoltageWindow The supply voltage window.
+ @param[in] S18R The boolean to show if it should switch to 1.8v.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdioSendOpCond (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT32 VoltageWindow,
+ IN BOOLEAN S18R
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+ UINT32 Switch;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SDIO_SEND_OP_COND;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR4;
+
+ Switch = S18R ? BIT24 : 0;
+
+ SdMmcCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SD_SEND_OP_COND to the device to see whether it is SDIO device.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of addressed device.
+ @param[in] VoltageWindow The supply voltage window.
+ @param[in] S18R The boolean to show if it should switch to 1.8v.
+ @param[in] Xpc The boolean to show if it should provide 0.36w
+ power control.
+ @param[in] Hcs The boolean to show if it support host capacity
+ info.
+ @param[out] Ocr The buffer to store returned OCR register value.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSendOpCond (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN UINT32 VoltageWindow,
+ IN BOOLEAN S18R,
+ IN BOOLEAN Xpc,
+ IN BOOLEAN Hcs,
+ OUT UINT32 *Ocr
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+ UINT32 Switch;
+ UINT32 MaxPower;
+ UINT32 HostCapacity;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_APP_CMD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_OP_COND;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR3;
+
+ Switch = S18R ? BIT24 : 0;
+ MaxPower = Xpc ? BIT28 : 0;
+ HostCapacity = Hcs ? BIT30 : 0;
+
+ SdMmcCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch | \
+ MaxPower | HostCapacity;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ *Ocr = SdMmcStatusBlk.Resp0;
+ }
+
+ return Status;
+}
+
+/**
+ Broadcast command ALL_SEND_CID to the bus to ask all the SD devices to send
+ the data of their CID registers.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardAllSendCid (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_ALL_SEND_CID;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SET_RELATIVE_ADDR to the SD device to assign a Relative device
+ Address (RCA).
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[out] Rca The relative device address to assign.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSetRca (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ OUT UINT16 *Rca
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR6;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ *Rca = (UINT16)(SdMmcStatusBlk.Resp0 >> 16);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_CSD to the SD device to get the data of the CSD register.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of selected device.
+ @param[out] Csd The buffer to store the content of the CSD register.
+ Note the caller should ignore the lowest byte of
+ this buffer as the content of this byte is meaning-
+ less even if the operation succeeds.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardGetCsd (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ OUT SD_CSD *Csd
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_CSD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CSD) - 1);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_CSD to the SD device to get the data of the CSD register.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of selected device.
+ @param[out] Scr The buffer to store the content of the SCR register.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardGetScr (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ OUT SD_SCR *Scr
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_APP_CMD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_SCR;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+
+ Packet.InDataBuffer = Scr;
+ Packet.InTransferLength = sizeof (SD_SCR);
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SELECT_DESELECT_CARD to the SD device to select/deselect it.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of selected device.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSelect (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ if (Rca != 0) {
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;
+ }
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command VOLTAGE_SWITCH to the SD device to switch the voltage of the
+ device.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardVoltageSwitch (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_VOLTAGE_SWITCH;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = 0;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SET_BUS_WIDTH to the SD device to set the bus width.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of addressed device.
+ @param[in] BusWidth The bus width to be set, it could be 1 or 4.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSetBusWidth (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN UINT8 BusWidth
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_APP_CMD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SdMmcCmdBlk.CommandIndex = SD_SET_BUS_WIDTH;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+
+ if (BusWidth == 1) {
+ Value = 0;
+ } else if (BusWidth == 4) {
+ Value = 2;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ SdMmcCmdBlk.CommandArgument = Value & 0x3;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ return Status;
+}
+
+/**
+ Send command SWITCH_FUNC to the SD device to check switchable function or
+ switch card function.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] AccessMode The value for access mode group.
+ @param[in] CommandSystem The value for command set group.
+ @param[in] DriveStrength The value for drive length group.
+ @param[in] PowerLimit The value for power limit group.
+ @param[in] Mode Switch or check function.
+ @param[out] SwitchResp The return switch function status.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSwitch (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 AccessMode,
+ IN UINT8 CommandSystem,
+ IN UINT8 DriveStrength,
+ IN UINT8 PowerLimit,
+ IN BOOLEAN Mode,
+ OUT UINT8 *SwitchResp
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+ UINT32 ModeValue;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SWITCH_FUNC;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+
+ ModeValue = Mode ? BIT31 : 0;
+ SdMmcCmdBlk.CommandArgument = (AccessMode & 0xF) | \
+ ((PowerLimit & 0xF) << 4) | \
+ ((DriveStrength & 0xF) << 8) | \
+ ((DriveStrength & 0xF) << 12) | \
+ ModeValue;
+
+ Packet.InDataBuffer = SwitchResp;
+ Packet.InTransferLength = 64;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_STATUS to the addressed SD device to get its status
+ register.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address of addressed device.
+ @param[out] DevStatus The returned device status.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSendStatus (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ OUT UINT32 *DevStatus
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_STATUS;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ *DevStatus = SdMmcStatusBlk.Resp0;
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_TUNING_BLOCK to the SD device for HS200 optimal sampling
+ point detection.
+
+ It may be sent up to 40 times until the host finishes the tuning procedure.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSendTuningBlk (
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru
+ )
+{
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+ EFI_STATUS Status;
+ UINT8 TuningBlock[64];
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_TUNING_BLOCK;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = 0;
+
+ Packet.InDataBuffer = TuningBlock;
+ Packet.InTransferLength = sizeof (TuningBlock);
+
+ Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Switch the bus width to specified width.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and
+ SD Host Controller Simplified Spec 3.0 section Figure 3-7 for details.
+
+ @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+ @param[in] BusWidth The bus width to be set, it could be 4 or 8.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSwitchBusWidth (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN BOOLEAN IsDdr,
+ IN UINT8 BusWidth
+ )
+{
+ EFI_STATUS Status;
+ UINT32 DevStatus;
+
+ Status = SdCardSetBusWidth (PassThru, Rca, BusWidth);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSwitchBusWidth: Switch to bus width %d fails with %r\n",
+ BusWidth,
+ Status
+ ));
+ return Status;
+ }
+
+ Status = SdCardSendStatus (PassThru, Rca, &DevStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSwitchBusWidth: Send status fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // Check the switch operation is really successful or not.
+ //
+ if ((DevStatus >> 16) != 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSwitchBusWidth: The switch operation fails as DevStatus 0x%08x\n",
+ DevStatus
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = DwMmcHcSetBusWidth (DevIo, IsDdr, BusWidth);
+
+ return Status;
+}
+
+/**
+ Switch the high speed timing according to request.
+
+ Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details.
+
+ @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance.
+ @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL
+ instance.
+ @param[in] Rca The relative device address to be assigned.
+ @param[in] S18A The boolean to show if it's a UHS-I SD card.
+ @param[in] BusWidths The bus width of the SD card.
+
+ @retval EFI_SUCCESS The operation is done correctly.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+SdCardSetBusMode (
+ IN EFI_DEVICE_IO_PROTOCOL *DevIo,
+ IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT16 Rca,
+ IN BOOLEAN S18A,
+ IN UINT32 BusWidths
+ )
+{
+ EFI_STATUS Status;
+ DW_MMC_HC_SLOT_CAP *Capability;
+ UINT32 ClockFreq;
+ UINT8 AccessMode;
+ UINT8 SwitchResp[64];
+ DW_MMC_HC_PRIVATE_DATA *Private;
+ BOOLEAN IsDdr;
+
+ Private = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru);
+
+ Capability = &Private->Capability[0];
+
+ if ((Capability->BusWidth == 1) || (Capability->BusWidth == 4)) {
+ BusWidths &= Capability[0].BusWidth;
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSetBusMode: BusWidths (%d) in capability are wrong\n",
+ Capability->BusWidth
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BusWidths == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSetBusMode: Get wrong BusWidths:%d\n",
+ BusWidths
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->Capability[0].Ddr50) {
+ IsDdr = TRUE;
+ } else {
+ IsDdr = FALSE;
+ }
+
+ Status = SdCardSwitchBusWidth (DevIo, PassThru, Rca, IsDdr, BusWidths);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSetBusMode: Executing SdCardSwitchBusWidth fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ //
+ // Get the supported bus speed from SWITCH cmd return data group #1.
+ //
+ Status = SdCardSwitch (PassThru, 0xF, 0xF, 0xF, 0xF, FALSE, SwitchResp);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Calculate supported bus speed/bus width/clock frequency by host and device
+ // capability.
+ //
+ ClockFreq = 0;
+ if (S18A && (Capability->Sdr104 != 0) && ((SwitchResp[13] & BIT3) != 0)) {
+ ClockFreq = 208;
+ AccessMode = 3;
+ } else if (S18A && (Capability->Sdr50 != 0) &&
+ ((SwitchResp[13] & BIT2) != 0)) {
+ ClockFreq = 100;
+ AccessMode = 2;
+ } else if (S18A && (Capability->Ddr50 != 0) &&
+ ((SwitchResp[13] & BIT4) != 0)) {
+ ClockFreq = 50;
+ AccessMode = 4;
+ } else if ((SwitchResp[13] & BIT1) != 0) {
+ ClockFreq = 50;
+ AccessMode = 1;
+ } else {
+ ClockFreq = 25;
+ AccessMode = 0;
+ }
+
+ Status = SdCardSwitch (PassThru, AccessMode, 0xF, 0xF, 0xF, TRUE, SwitchResp);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((SwitchResp[16] & 0xF) != AccessMode) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardSetBusMode: Switch to AccessMode %d ClockFreq %d fails! The Switch response is 0x%1x\n",
+ AccessMode,
+ ClockFreq,
+ SwitchResp[16] & 0xF
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "SdCardSetBusMode: Switch to AccessMode %d ClockFreq %d \n",
+ AccessMode,
+ ClockFreq
+ ));
+
+ Status = DwMmcHcClockSupply (DevIo, ClockFreq * 1000, *Capability);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
+EFI_STATUS
+SdCardIdentification (
+ IN DW_MMC_HC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_IO_PROTOCOL *DevIo;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ UINT32 Ocr;
+ UINT16 Rca;
+ BOOLEAN Xpc;
+ BOOLEAN S18r;
+ UINT64 MaxCurrent;
+ SD_SCR Scr;
+ SD_CSD Csd;
+
+ DevIo = Private->DevIo;
+ PassThru = &Private->PassThru;
+ //
+ // 1. Send Cmd0 to the device
+ //
+ Status = SdCardReset (PassThru);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "SdCardIdentification: Executing Cmd0 fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ MicroSecondDelay (10000);
+ //
+ // 2. Send Cmd8 to the device
+ //
+ Status = SdCardVoltageCheck (PassThru, 0x1, 0xFF);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "SdCardIdentification: Executing Cmd8 fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+ //
+ // 3. Send Acmd41 with voltage window 0 to the device
+ //
+ Status = SdCardSendOpCond (PassThru, 0, 0, FALSE, FALSE, FALSE, &Ocr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "SdCardIdentification: Executing SdCardSendOpCond fails with %r\n",
+ Status
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Private->Capability[0].Voltage33 != 0) {
+ //
+ // Support 3.3V
+ //
+ MaxCurrent = ((UINT32)Private->MaxCurrent[0] & 0xFF) * 4;
+ S18r = FALSE;
+ } else if (Private->Capability[0].Voltage30 != 0) {
+ //
+ // Support 3.0V
+ //
+ MaxCurrent = (((UINT32)Private->MaxCurrent[0] >> 8) & 0xFF) * 4;
+ S18r = FALSE;
+ } else if (Private->Capability[0].Voltage18 != 0) {
+ //
+ // Support 1.8V
+ //
+ MaxCurrent = (((UINT32)Private->MaxCurrent[0] >> 16) & 0xFF) * 4;
+ S18r = TRUE;
+ } else {
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (MaxCurrent >= 150) {
+ Xpc = TRUE;
+ } else {
+ Xpc = FALSE;
+ }
+
+ //
+ // 4. Repeatly send Acmd41 with supply voltage window to the device.
+ // Note here we only support the cards complied with SD physical
+ // layer simplified spec version 2.0 and version 3.0 and above.
+ //
+ do {
+ Status = SdCardSendOpCond (PassThru, 0, Ocr, S18r, Xpc, TRUE, &Ocr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardIdentification: SdCardSendOpCond fails with %r Ocr %x, S18r %x, Xpc %x\n",
+ Status,
+ Ocr,
+ S18r,
+ Xpc
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+ } while ((Ocr & BIT31) == 0);
+
+ Status = SdCardAllSendCid (PassThru);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardIdentification: Executing SdCardAllSendCid fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = SdCardSetRca (PassThru, &Rca);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardIdentification: Executing SdCardSetRca fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = SdCardGetCsd (PassThru, Rca, &Csd);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardIdentification: Executing SdCardGetCsd fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = SdCardSelect (PassThru, Rca);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardIdentification: Selecting card fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = SdCardGetScr (PassThru, Rca, &Scr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "SdCardIdentification: Executing SdCardGetScr fails with %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ //
+ // Enter Data Tranfer Mode.
+ //
+ DEBUG ((DEBUG_INFO, "SdCardIdentification: Found a SD device\n"));
+ Private->Slot[0].CardType = SdCardType;
+
+ Status = SdCardSetBusMode (DevIo, PassThru, Rca, S18r, Scr.SdBusWidths);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private->Slot[0].Initialized = TRUE;
+
+ return Status;
+}
--
2.7.4
^ permalink raw reply related [flat|nested] 5+ messages in thread