summaryrefslogtreecommitdiff
path: root/MdeModulePkg
diff options
context:
space:
mode:
authorFeng Tian <feng.tian@intel.com>2015-04-29 02:42:58 +0000
committererictian <erictian@Edk2>2015-04-29 02:42:58 +0000
commit0591696eff9962b52b3b0137e865198096353573 (patch)
treef396a3f354f86c76f94f880201ec888557e1851b /MdeModulePkg
parent5230616612bfdaf388aef75a6eb921f7ca6dafcb (diff)
downloadedk2-platforms-0591696eff9962b52b3b0137e865198096353573.tar.xz
MdeModulePkg: Add UFS (Universal Flash Storage) Stack
It includes 4 drivers: 1. UfsPassThruDxe, which is a UEFI driver and consumes EFI_UFS_HOST_CONTROLLER_PROTOCOL and produces EFI_EXT_SCSI_PASS_THRU_PROTOCOL 2. UfsPciHcDxe, which is specific for pci-based UFS HC implementation and is a UEFI driver to produce EFI_UFS_HOST_CONTROLLER_PROTOCOL. 3. UfsBlockIoPei, which is a PEI driver and consumes EFI_UFS_HOST_CONTROLLER_PPI and produces EFI_PEI_VIRTUAL_BLOCK_IO_PPI. 4. UfsPciHcPei, which is specific for pci-based UFS HC implementation and is a PEI driver to produce EFI_UFS_HOST_CONTROLLER_PPI. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Feng Tian <feng.tian@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17246 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg')
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c225
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c699
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h453
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf56
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.unibin0 -> 1980 bytes
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.unibin0 -> 1336 bytes
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c152
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h62
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf56
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.unibin0 -> 2076 bytes
-rw-r--r--MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.unibin0 -> 1372 bytes
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c995
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h434
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf61
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.unibin0 -> 1844 bytes
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.unibin0 -> 1362 bytes
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c455
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h61
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c1787
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h1339
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c222
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c1054
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h727
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.unibin0 -> 2032 bytes
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf63
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.unibin0 -> 1346 bytes
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c2000
-rw-r--r--MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h1339
-rw-r--r--MdeModulePkg/Include/Ppi/UfsHostController.h60
-rw-r--r--MdeModulePkg/Include/Protocol/UfsHostController.h205
-rw-r--r--MdeModulePkg/MdeModulePkg.dec12
-rw-r--r--MdeModulePkg/MdeModulePkg.dsc4
-rw-r--r--MdeModulePkg/MdeModulePkg.unibin162360 -> 163392 bytes
33 files changed, 12521 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c
new file mode 100644
index 0000000000..1e2db1a876
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c
@@ -0,0 +1,225 @@
+/** @file
+ UfsHcDxe driver produces EFI_UFS_HOST_CONTROLLER_PROTOCOL. The upper layer module
+ uses it to query the MMIO base address of the UFS host controller.
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsPciHcDxe.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUfsHcComponentName = {
+ UfsHcComponentNameGetDriverName,
+ UfsHcComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUfsHcComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UfsHcComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UfsHcComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsHcDriverNameTable[] = {
+ {
+ "eng;en",
+ L"Universal Flash Storage (UFS) Pci Host Controller Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsHcControllerNameTable[] = {
+ {
+ "eng;en",
+ L"Universal Flash Storage (UFS) Pci 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
+UfsHcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUfsHcDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gUfsHcComponentName)
+ );
+}
+
+/**
+ 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 NULL.
+
+ @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
+UfsHcComponentNameGetControllerName (
+ 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 Controller Handle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gUfsHcDriverBinding.DriverBindingHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUfsHcControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gUfsHcComponentName)
+ );
+
+}
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c
new file mode 100644
index 0000000000..586a52b380
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c
@@ -0,0 +1,699 @@
+/** @file
+ UfsHcDxe driver is used to provide platform-dependent info, mainly UFS host controller
+ MMIO base, to upper layer UFS drivers.
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsPciHcDxe.h"
+
+//
+// NVM Express Driver Binding Protocol Instance
+//
+EFI_DRIVER_BINDING_PROTOCOL gUfsHcDriverBinding = {
+ UfsHcDriverBindingSupported,
+ UfsHcDriverBindingStart,
+ UfsHcDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+//
+// Template for Ufs host controller private data.
+//
+UFS_HOST_CONTROLLER_PRIVATE_DATA gUfsHcTemplate = {
+ UFS_HC_PRIVATE_DATA_SIGNATURE, // Signature
+ NULL, // Handle
+ { // UfsHcProtocol
+ UfsHcGetMmioBar,
+ UfsHcAllocateBuffer,
+ UfsHcFreeBuffer,
+ UfsHcMap,
+ UfsHcUnmap,
+ UfsHcFlush
+ },
+ NULL, // PciIo
+ 0 // PciAttributes
+};
+
+/**
+ Get the MMIO base of the UFS host controller.
+
+ @param[in] This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+ @param[out] MmioBar The MMIO base address of UFS host controller.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval others The operation fails.
+**/
+EFI_STATUS
+EFIAPI
+UfsHcGetMmioBar (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ OUT UINTN *MmioBar
+ )
+{
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (MmioBar == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This);
+ PciIo = Private->PciIo;
+
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ PCI_BASE_ADDRESSREG_OFFSET,
+ sizeof (UINT32),
+ MmioBar
+ );
+
+ if (!EFI_ERROR (Status)) {
+ *MmioBar &= (UINTN)~0xF;
+ }
+ return Status;
+}
+
+/**
+ Provides the UFS controller-specific addresses needed to access system memory.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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 UFS 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 UFS 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.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcMap (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN EDKII_UFS_HOST_CONTROLLER_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (HostAddress == NULL) || (NumberOfBytes == NULL) || (DeviceAddress == NULL) || (Mapping == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This);
+ PciIo = Private->PciIo;
+
+ Status = PciIo->Map (PciIo, Operation, HostAddress, NumberOfBytes, DeviceAddress, Mapping);
+ return Status;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcUnmap (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (Mapping == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This);
+ PciIo = Private->PciIo;
+
+ Status = PciIo->Unmap (PciIo, Mapping);
+ return Status;
+}
+
+/**
+ Allocates pages that are suitable for an EfiUfsHcOperationBusMasterCommonBuffer
+ mapping.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcAllocateBuffer (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ IN UINT64 Attributes
+ )
+{
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (HostAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This);
+ PciIo = Private->PciIo;
+
+ Status = PciIo->AllocateBuffer (PciIo, Type, MemoryType, Pages, HostAddress, Attributes);
+ return Status;
+}
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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 EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+ was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcFreeBuffer (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ )
+{
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (HostAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This);
+ PciIo = Private->PciIo;
+
+ Status = PciIo->FreeBuffer (PciIo, Pages, HostAddress);
+ return Status;
+}
+
+/**
+ Flushes all posted write transactions from the UFS bus to attached UFS device.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The posted write transactions were flushed from the UFS bus
+ to attached UFS device.
+ @retval EFI_DEVICE_ERROR The posted write transactions were not flushed from the UFS
+ bus to attached UFS device due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcFlush (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This
+ )
+{
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This);
+ PciIo = Private->PciIo;
+
+ Status = PciIo->Flush (PciIo);
+ 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 determine 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
+UfsHcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN UfsHcFound;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 PciData;
+
+ PciIo = NULL;
+ ParentDevicePath = NULL;
+ UfsHcFound = FALSE;
+
+ //
+ // UfsHcDxe is a device driver, and should ingore the
+ // "RemainingDevicePath" according to EFI spec
+ //
+ 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 EfiPciIoProtocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Now further check the PCI header: Base class (offset 0x0B) and
+ // Sub Class (offset 0x0A). This controller should be an UFS controller
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ 0,
+ sizeof (PciData),
+ &PciData
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Since we already got the PciData, we can close protocol to avoid to carry it on for multiple exit points.
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // Examine UFS Host Controller PCI Configuration table fields
+ //
+ if (PciData.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE) {
+ if (PciData.Hdr.ClassCode[1] == 0x09 ) { //UFS Controller Subclass
+ UfsHcFound = TRUE;
+ }
+ }
+
+ if (!UfsHcFound) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return Status;
+}
+
+
+/**
+ 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
+UfsHcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ UINT64 Supports;
+
+ PciIo = NULL;
+ Private = NULL;
+ Supports = 0;
+
+ //
+ // Now test and open the EfiPciIoProtocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ //
+ // Status == 0 - A normal execution flow, SUCCESS and the program proceeds.
+ // Status == ALREADY_STARTED - A non-zero Status code returned. It indicates
+ // that the protocol has been opened and should be treated as a
+ // normal condition and the program proceeds. The Protocol will not
+ // opened 'again' by this call.
+ // Status != ALREADY_STARTED - Error status, terminate program execution
+ //
+ if (EFI_ERROR (Status)) {
+ //
+ // EFI_ALREADY_STARTED is also an error
+ //
+ return Status;
+ }
+
+ Private = AllocateCopyPool (sizeof (UFS_HOST_CONTROLLER_PRIVATE_DATA), &gUfsHcTemplate);
+ if (Private == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ Private->PciIo = PciIo;
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationGet,
+ 0,
+ &Private->PciAttributes
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationSupported,
+ 0,
+ &Supports
+ );
+
+ if (!EFI_ERROR (Status)) {
+ Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ Supports,
+ NULL
+ );
+ } else {
+ goto Done;
+ }
+
+ ///
+ /// Install UFS_HOST_CONTROLLER protocol
+ ///
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ (VOID*)&(Private->UfsHc)
+ );
+
+Done:
+ if (EFI_ERROR (Status)) {
+ if ((Private != NULL) && (Private->PciAttributes != 0)) {
+ //
+ // Restore original PCI attributes
+ //
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationSet,
+ Private->PciAttributes,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ 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
+UfsHcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ UFS_HOST_CONTROLLER_PRIVATE_DATA *Private;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ ///
+ /// Get private data
+ ///
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ (VOID **) &UfsHc,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (UfsHc);
+
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ &(Private->UfsHc)
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Restore original PCI attributes
+ //
+ Status = Private->PciIo->Attributes (
+ Private->PciIo,
+ EfiPciIoAttributeOperationSet,
+ Private->PciAttributes,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Close protocols opened by UFS host controller driver
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ FreePool (Private);
+ }
+
+ return Status;
+}
+
+/**
+ The entry point for UFS 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
+UfsHcDriverEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gUfsHcDriverBinding,
+ ImageHandle,
+ &gUfsHcComponentName,
+ &gUfsHcComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h
new file mode 100644
index 0000000000..f3b7eab24a
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h
@@ -0,0 +1,453 @@
+/** @file
+ UfsHcDxe driver is used to provide platform-dependent info, mainly UFS host controller
+ MMIO base, to upper layer UFS drivers.
+
+ Copyright (c) 2014, Intel Corporation. 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 _EFI_UFS_HOST_CONTROLLER_H_
+#define _EFI_UFS_HOST_CONTROLLER_H_
+
+#include <Uefi.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/UfsHostController.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+
+extern EFI_DRIVER_BINDING_PROTOCOL gUfsHcDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gUfsHcComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gUfsHcComponentName2;
+
+//
+// Unique signature for private data structure.
+//
+#define UFS_HC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('U','F','S','H')
+
+typedef struct _UFS_HOST_CONTROLLER_PRIVATE_DATA UFS_HOST_CONTROLLER_PRIVATE_DATA;
+
+//
+// Nvme private data structure.
+//
+struct _UFS_HOST_CONTROLLER_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Handle;
+
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL UfsHc;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT64 PciAttributes;
+};
+
+#define UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC(a) \
+ CR (a, \
+ UFS_HOST_CONTROLLER_PRIVATE_DATA, \
+ UfsHc, \
+ UFS_HC_PRIVATE_DATA_SIGNATURE \
+ )
+
+/**
+ 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
+UfsHcComponentNameGetDriverName (
+ 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 NULL.
+
+ @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
+UfsHcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+/**
+ 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 determine 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
+UfsHcDriverBindingSupported (
+ 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 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
+UfsHcDriverBindingStart (
+ 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
+UfsHcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Get the MMIO base of the UFS host controller.
+
+ @param[in] This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+ @param[out] MmioBar The MMIO base address of UFS host controller.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval others The operation fails.
+**/
+EFI_STATUS
+EFIAPI
+UfsHcGetMmioBar (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ OUT UINTN *MmioBar
+ );
+
+/**
+ Provides the UFS controller-specific addresses needed to access system memory.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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 UFS 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 UFS 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.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcMap (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN EDKII_UFS_HOST_CONTROLLER_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcUnmap (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN VOID *Mapping
+ );
+
+/**
+ Allocates pages that are suitable for an EfiUfsHcOperationBusMasterCommonBuffer
+ mapping.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcAllocateBuffer (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ IN UINT64 Attributes
+ );
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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 EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+ was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcFreeBuffer (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ );
+
+/**
+ Flushes all posted write transactions from the UFS bus to attached UFS device.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The posted write transactions were flushed from the UFS bus
+ to attached UFS device.
+ @retval EFI_DEVICE_ERROR The posted write transactions were not flushed from the UFS
+ bus to attached UFS device due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsHcFlush (
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf
new file mode 100644
index 0000000000..c1ce9ea851
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf
@@ -0,0 +1,56 @@
+## @file
+# Component Description File For Universal Flash Storage Pci Host Controller Module.
+#
+# Copyright (c) 2014 - 2015, Intel Corporation. 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 = 0x00010005
+ BASE_NAME = UfsPciHcDxe
+ MODULE_UNI_FILE = UfsPciHcDxe.uni
+ FILE_GUID = AF43E178-C2E9-4712-A7CD-08BFDAC7482C
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 0.9
+ ENTRY_POINT = UfsHcDriverEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gUfsHcDriverBinding
+# COMPONENT_NAME = gUfsHcComponentName
+# COMPONENT_NAME2 = gUfsHcComponentName2
+
+[Sources]
+ ComponentName.c
+ UfsPciHcDxe.c
+ UfsPciHcDxe.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ DevicePathLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiLib
+
+[Protocols]
+ gEfiPciIoProtocolGuid ## TO_START
+ gEfiDevicePathProtocolGuid ## TO_START
+ gEdkiiUfsHostControllerProtocolGuid ## BY_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ UfsPciHcDxeExtra.uni \ No newline at end of file
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.uni b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.uni
new file mode 100644
index 0000000000..5b3a137d80
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.uni b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.uni
new file mode 100644
index 0000000000..fb359fd615
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c
new file mode 100644
index 0000000000..36325641e7
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c
@@ -0,0 +1,152 @@
+/** @file
+ UfsPciHcPei driver is used to provide platform-dependent info, mainly UFS host controller
+ MMIO base, to upper layer UFS drivers.
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsPciHcPei.h"
+
+EDKII_UFS_HOST_CONTROLLER_PPI mUfsHostControllerPpi = { GetUfsHcMmioBar };
+
+EFI_PEI_PPI_DESCRIPTOR mPpiList = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEdkiiPeiUfsHostControllerPpiGuid,
+ &mUfsHostControllerPpi
+};
+
+/**
+ Get the MMIO base address of UFS host controller.
+
+ @param[in] This The protocol instance pointer.
+ @param[in] ControllerId The ID of the UFS host controller.
+ @param[out] MmioBar Pointer to the UFS host controller MMIO base address.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUfsHcMmioBar (
+ IN EDKII_UFS_HOST_CONTROLLER_PPI *This,
+ IN UINT8 ControllerId,
+ OUT UINTN *MmioBar
+ )
+{
+ UFS_HC_PEI_PRIVATE_DATA *Private;
+
+ if ((This == NULL) || (MmioBar == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_HC_PEI_PRIVATE_DATA_FROM_THIS (This);
+
+ if (ControllerId >= Private->TotalUfsHcs) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *MmioBar = (UINTN)Private->UfsHcPciAddr[ControllerId];
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The user code starts with this function.
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS The driver is successfully initialized.
+ @retval Others Can't initialize the driver.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeUfsHcPeim (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_BOOT_MODE BootMode;
+ EFI_STATUS Status;
+ UINT16 Bus;
+ UINT16 Device;
+ UINT16 Function;
+ UINT32 Size;
+ UINT8 SubClass;
+ UINT8 BaseClass;
+ UFS_HC_PEI_PRIVATE_DATA *Private;
+
+ //
+ // Shadow this PEIM to run from memory
+ //
+ if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
+ return EFI_SUCCESS;
+ }
+
+ Status = PeiServicesGetBootMode (&BootMode);
+ ///
+ /// We do not export this in S3 boot path, because it is only for recovery.
+ ///
+ if (BootMode == BOOT_ON_S3_RESUME) {
+ return EFI_SUCCESS;
+ }
+
+ Private = (UFS_HC_PEI_PRIVATE_DATA *) AllocateZeroPool (sizeof (UFS_HC_PEI_PRIVATE_DATA));
+ if (Private == NULL) {
+ DEBUG ((EFI_D_ERROR, "Failed to allocate memory for UFS_HC_PEI_PRIVATE_DATA! \n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Private->Signature = UFS_HC_PEI_SIGNATURE;
+ Private->UfsHostControllerPpi = mUfsHostControllerPpi;
+ Private->PpiList = mPpiList;
+ Private->PpiList.Ppi = &Private->UfsHostControllerPpi;
+
+ for (Bus = 0; Bus < 256; Bus++) {
+ for (Device = 0; Device < 32; Device++) {
+ for (Function = 0; Function < 8; Function++) {
+ SubClass = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A));
+ BaseClass = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B));
+
+ if ((SubClass == 0x09) && (BaseClass == PCI_CLASS_MASS_STORAGE)) {
+ //
+ // Get the Ufs Pci host controller's MMIO region size.
+ //
+ PciAnd16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), (UINT16)~(EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE));
+ PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), 0xFFFFFFFF);
+ Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET));
+ //
+ // Assign resource to the Ufs Pci host controller's MMIO BAR.
+ // Enable the Ufs Pci host controller by setting BME and MSE bits of PCI_CMD register.
+ //
+ PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), (UINT32)(PcdGet32 (PcdUfsPciHostControllerMmioBase) + Size * Private->TotalUfsHcs));
+ PciOr16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), (EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE));
+ //
+ // Record the allocated Mmio base address.
+ //
+ Private->UfsHcPciAddr[Private->TotalUfsHcs] = PcdGet32 (PcdUfsPciHostControllerMmioBase) + Size * Private->TotalUfsHcs;
+ Private->TotalUfsHcs++;
+ ASSERT (Private->TotalUfsHcs < MAX_UFS_HCS);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Install Ufs Host Controller PPI
+ ///
+ Status = PeiServicesInstallPpi (&Private->PpiList);
+
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h
new file mode 100644
index 0000000000..2405dd76d8
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h
@@ -0,0 +1,62 @@
+/** @file
+ Copyright (c) 2014, Intel Corporation. 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 _UFS_PCI_HOST_CONTROLLER_PEI_H_
+#define _UFS_PCI_HOST_CONTROLLER_PEI_H_
+
+#include <PiPei.h>
+
+#include <Ppi/MasterBootMode.h>
+#include <Ppi/UfsHostController.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PciLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define UFS_HC_PEI_SIGNATURE SIGNATURE_32 ('U', 'F', 'S', 'P')
+#define MAX_UFS_HCS 8
+
+typedef struct {
+ UINTN Signature;
+ EDKII_UFS_HOST_CONTROLLER_PPI UfsHostControllerPpi;
+ EFI_PEI_PPI_DESCRIPTOR PpiList;
+ UINTN TotalUfsHcs;
+ UINTN UfsHcPciAddr[MAX_UFS_HCS];
+} UFS_HC_PEI_PRIVATE_DATA;
+
+#define UFS_HC_PEI_PRIVATE_DATA_FROM_THIS(a) CR (a, UFS_HC_PEI_PRIVATE_DATA, UfsHostControllerPpi, UFS_HC_PEI_SIGNATURE)
+
+/**
+ Get the MMIO base address of UFS host controller.
+
+ @param[in] This The protocol instance pointer.
+ @param[in] ControllerId The ID of the UFS host controller.
+ @param[out] MmioBar Pointer to the UFS host controller MMIO base address.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUfsHcMmioBar (
+ IN EDKII_UFS_HOST_CONTROLLER_PPI *This,
+ IN UINT8 ControllerId,
+ OUT UINTN *MmioBar
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf
new file mode 100644
index 0000000000..b16935628c
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf
@@ -0,0 +1,56 @@
+## @file
+# Component Description File For Universal Flash Storage Pci Host Controller Pei Module.
+#
+# Copyright (c) 2014 - 2015, Intel Corporation. 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 = 0x00010005
+ BASE_NAME = UfsPciHcPei
+ MODULE_UNI_FILE = UfsPciHcPei.uni
+ FILE_GUID = 905DC1AD-C44D-4965-98AC-B6B4444BFD65
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 0.9
+
+ ENTRY_POINT = InitializeUfsHcPeim
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ UfsPciHcPei.c
+ UfsPciHcPei.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ PciLib
+ DebugLib
+ PeiServicesLib
+ MemoryAllocationLib
+ PeimEntryPoint
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUfsPciHostControllerMmioBase ## CONSUMES
+
+[Ppis]
+ gEdkiiPeiUfsHostControllerPpiGuid ## PRODUCES
+
+[Depex]
+ gEfiPeiMasterBootModePpiGuid AND gEfiPeiMemoryDiscoveredPpiGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ UfsPciHcPeiExtra.uni \ No newline at end of file
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.uni b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.uni
new file mode 100644
index 0000000000..7b7ea3af7b
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.uni b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.uni
new file mode 100644
index 0000000000..4487eb968f
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c
new file mode 100644
index 0000000000..05730ff589
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c
@@ -0,0 +1,995 @@
+/** @file
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsBlockIoPei.h"
+
+//
+// Template for UFS HC Peim Private Data.
+//
+UFS_PEIM_HC_PRIVATE_DATA gUfsHcPeimTemplate = {
+ UFS_PEIM_HC_SIG, // Signature
+ NULL, // Controller
+ NULL, // Pool
+ { // BlkIoPpi
+ UfsBlockIoPeimGetDeviceNo,
+ UfsBlockIoPeimGetMediaInfo,
+ UfsBlockIoPeimReadBlocks
+ },
+ { // BlkIoPpiList
+ EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
+ &gEfiPeiVirtualBlockIoPpiGuid,
+ NULL
+ },
+ { // Media
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ },
+ {
+ UfsDevice,
+ TRUE,
+ 0,
+ 0x1000
+ }
+ },
+ 0, // UfsHcBase
+ 0, // Capabilities
+ 0, // TaskTag
+ 0, // UtpTrlBase
+ 0, // Nutrs
+ 0, // UtpTmrlBase
+ 0, // Nutmrs
+ { // Luns
+ {
+ UFS_LUN_0, // Ufs Common Lun 0
+ UFS_LUN_1, // Ufs Common Lun 1
+ UFS_LUN_2, // Ufs Common Lun 2
+ UFS_LUN_3, // Ufs Common Lun 3
+ UFS_LUN_4, // Ufs Common Lun 4
+ UFS_LUN_5, // Ufs Common Lun 5
+ UFS_LUN_6, // Ufs Common Lun 6
+ UFS_LUN_7, // Ufs Common Lun 7
+ },
+ 0x0000, // By default exposing all Luns.
+ 0x0
+ }
+};
+
+/**
+ Execute Request Sense SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[out] DataBuffer A pointer to output sense data.
+ @param[out] DataBufferLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimRequestSense (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ OUT VOID *DataBuffer,
+ OUT UINT32 *DataBufferLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_REQUEST_SENSE;
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.DataDirection = UfsDataIn;
+ Packet.InDataBuffer = DataBuffer;
+ Packet.InTransferLength = *DataBufferLength;
+ Packet.SenseData = NULL;
+ Packet.SenseDataLength = 0;
+
+ Status = UfsExecScsiCmds (Private,(UINT8)Lun, &Packet);
+
+ if (!EFI_ERROR (Status)) {
+ *DataBufferLength = Packet.InTransferLength;
+ }
+
+ return Status;
+}
+
+/**
+ Execute TEST UNITY READY SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[out] SenseData A pointer to output sense data.
+ @param[out] SenseDataLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimTestUnitReady (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ OUT VOID *SenseData, OPTIONAL
+ OUT UINT8 *SenseDataLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_TEST_UNIT_READY;
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.DataDirection = UfsNoData;
+ Packet.SenseData = SenseData;
+ Packet.SenseDataLength = *SenseDataLength;
+
+ Status = UfsExecScsiCmds (Private,(UINT8)Lun, &Packet);
+
+ if (*SenseDataLength != 0) {
+ *SenseDataLength = Packet.SenseDataLength;
+ }
+
+ return Status;
+}
+
+/**
+ Execute INQUIRY SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[out] Inquiry A pointer to Inquiry data buffer.
+ @param[out] InquiryLengths The length of output Inquiry data.
+ @param[out] SenseData A pointer to output sense data.
+ @param[out] SenseDataLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimInquiry (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ OUT VOID *Inquiry,
+ OUT UINT32 *InquiryLength,
+ OUT VOID *SenseData, OPTIONAL
+ OUT UINT8 *SenseDataLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_INQUIRY;
+ Cdb[4] = sizeof (EFI_SCSI_INQUIRY_DATA);
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.InDataBuffer = Inquiry;
+ Packet.InTransferLength = *InquiryLength;
+ Packet.DataDirection = UfsDataIn;
+ Packet.SenseData = SenseData;
+ Packet.SenseDataLength = *SenseDataLength;
+
+ Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
+
+ if (*SenseDataLength != 0) {
+ *SenseDataLength = Packet.SenseDataLength;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ *InquiryLength = Packet.InTransferLength;
+ }
+
+ return Status;
+}
+
+/**
+ Execute READ CAPACITY(10) SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[out] DataBuffer A pointer to READ_CAPACITY data buffer.
+ @param[out] DataLength The length of output READ_CAPACITY data.
+ @param[out] SenseData A pointer to output sense data.
+ @param[out] SenseDataLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimReadCapacity (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ OUT VOID *DataBuffer,
+ OUT UINT32 *DataLength,
+ OUT VOID *SenseData, OPTIONAL
+ OUT UINT8 *SenseDataLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_READ_CAPACITY;
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.InDataBuffer = DataBuffer;
+ Packet.InTransferLength = *DataLength;
+ Packet.DataDirection = UfsDataIn;
+ Packet.SenseData = SenseData;
+ Packet.SenseDataLength = *SenseDataLength;
+
+ Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
+
+ if (*SenseDataLength != 0) {
+ *SenseDataLength = Packet.SenseDataLength;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ *DataLength = Packet.InTransferLength;
+ }
+
+ return Status;
+}
+
+/**
+ Execute READ CAPACITY(16) SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[out] DataBuffer A pointer to READ_CAPACITY data buffer.
+ @param[out] DataLength The length of output READ_CAPACITY data.
+ @param[out] SenseData A pointer to output sense data.
+ @param[out] SenseDataLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimReadCapacity16 (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ OUT VOID *DataBuffer,
+ OUT UINT32 *DataLength,
+ OUT VOID *SenseData, OPTIONAL
+ OUT UINT8 *SenseDataLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_READ_CAPACITY16;
+ Cdb[1] = 0x10; // Service Action should be 0x10 for UFS device.
+ Cdb[13] = 0x20; // The maximum number of bytes for returned data.
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.InDataBuffer = DataBuffer;
+ Packet.InTransferLength = *DataLength;
+ Packet.DataDirection = UfsDataIn;
+ Packet.SenseData = SenseData;
+ Packet.SenseDataLength = *SenseDataLength;
+
+ Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
+
+ if (*SenseDataLength != 0) {
+ *SenseDataLength = Packet.SenseDataLength;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ *DataLength = Packet.InTransferLength;
+ }
+
+ return Status;
+}
+
+/**
+ Execute READ (10) SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[in] StartLba The start LBA.
+ @param[in] SectorNum The sector number to be read.
+ @param[out] DataBuffer A pointer to data buffer.
+ @param[out] DataLength The length of output data.
+ @param[out] SenseData A pointer to output sense data.
+ @param[out] SenseDataLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimRead10 (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ IN UINTN StartLba,
+ IN UINT32 SectorNum,
+ OUT VOID *DataBuffer,
+ OUT UINT32 *DataLength,
+ OUT VOID *SenseData, OPTIONAL
+ OUT UINT8 *SenseDataLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_READ10;
+ WriteUnaligned32 ((UINT32 *)&Cdb[2], SwapBytes32 ((UINT32) StartLba));
+ WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 ((UINT16) SectorNum));
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.InDataBuffer = DataBuffer;
+ Packet.InTransferLength = *DataLength;
+ Packet.DataDirection = UfsDataIn;
+ Packet.SenseData = SenseData;
+ Packet.SenseDataLength = *SenseDataLength;
+
+ Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
+
+ if (*SenseDataLength != 0) {
+ *SenseDataLength = Packet.SenseDataLength;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ *DataLength = Packet.InTransferLength;
+ }
+
+ return Status;
+}
+
+/**
+ Execute READ (16) SCSI command on a specific UFS device.
+
+ @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The lun on which the SCSI cmd executed.
+ @param[in] StartLba The start LBA.
+ @param[in] SectorNum The sector number to be read.
+ @param[out] DataBuffer A pointer to data buffer.
+ @param[out] DataLength The length of output data.
+ @param[out] SenseData A pointer to output sense data.
+ @param[out] SenseDataLength The length of output sense data.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsPeimRead16 (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINTN Lun,
+ IN UINTN StartLba,
+ IN UINT32 SectorNum,
+ OUT VOID *DataBuffer,
+ OUT UINT32 *DataLength,
+ OUT VOID *SenseData, OPTIONAL
+ OUT UINT8 *SenseDataLength
+ )
+{
+ UFS_SCSI_REQUEST_PACKET Packet;
+ UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN];
+ EFI_STATUS Status;
+
+ ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
+ ZeroMem (Cdb, sizeof (Cdb));
+
+ Cdb[0] = EFI_SCSI_OP_READ16;
+ WriteUnaligned64 ((UINT64 *)&Cdb[2], SwapBytes64 (StartLba));
+ WriteUnaligned32 ((UINT32 *)&Cdb[10], SwapBytes32 (SectorNum));
+
+ Packet.Timeout = UFS_TIMEOUT;
+ Packet.Cdb = Cdb;
+ Packet.CdbLength = sizeof (Cdb);
+ Packet.InDataBuffer = DataBuffer;
+ Packet.InTransferLength = *DataLength;
+ Packet.DataDirection = UfsDataIn;
+ Packet.SenseData = SenseData;
+ Packet.SenseDataLength = *SenseDataLength;
+
+ Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
+
+ if (*SenseDataLength != 0) {
+ *SenseDataLength = Packet.SenseDataLength;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ *DataLength = Packet.InTransferLength;
+ }
+
+ return Status;
+}
+
+/**
+ Parsing Sense Keys from sense data.
+
+ @param Media The pointer of EFI_PEI_BLOCK_IO_MEDIA
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA
+ @param NeedRetry The pointer of action which indicates what is need to retry
+
+ @retval EFI_DEVICE_ERROR Indicates that error occurs
+ @retval EFI_SUCCESS Successfully to complete the parsing
+
+**/
+EFI_STATUS
+UfsPeimParsingSenseKeys (
+ IN EFI_PEI_BLOCK_IO_MEDIA *Media,
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ OUT BOOLEAN *NeedRetry
+ )
+{
+ if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
+ (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {
+ Media->MediaPresent = FALSE;
+ *NeedRetry = FALSE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Is No Media\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
+ (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {
+ *NeedRetry = TRUE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Is Media Change\n"));
+ return EFI_SUCCESS;
+ }
+
+ if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
+ (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {
+ *NeedRetry = TRUE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n"));
+ return EFI_SUCCESS;
+ }
+
+ if ((SenseData->Sense_Key == EFI_SCSI_SK_MEDIUM_ERROR) ||
+ ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
+ (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN))) {
+ *NeedRetry = FALSE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Media Error\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (SenseData->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {
+ *NeedRetry = FALSE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Hardware Error\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
+ (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NOT_READY) &&
+ (SenseData->Addnl_Sense_Code_Qualifier == EFI_SCSI_ASCQ_IN_PROGRESS)) {
+ *NeedRetry = TRUE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n"));
+ return EFI_SUCCESS;
+ }
+
+ *NeedRetry = FALSE;
+ DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));
+ return EFI_DEVICE_ERROR;
+}
+
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. To the PEI ATAPI driver, it returns the number
+ of all the detected ATAPI devices it detects during the enumeration process.
+ To the PEI legacy floppy driver, it returns the number of all the legacy
+ devices it finds during its enumeration process. If no device is detected,
+ then the function will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsBlockIoPeimGetDeviceNo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ )
+{
+ //
+ // For Ufs device, it has up to 8 normal Luns plus some well-known Luns.
+ // At PEI phase, we will only expose normal Luns to user.
+ // For those disabled Lun, when user try to access it, the operation would fail.
+ //
+ *NumberBlockDevices = UFS_PEIM_MAX_LUNS;
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @par Note:
+ The MediaInfo structure describes an enumeration of possible block device
+ types. This enumeration exists because no device paths are actually passed
+ across interfaces that describe the type or class of hardware that is publishing
+ the block I/O interface. This enumeration will allow for policy decisions
+ in the Recovery PEIM, such as "Try to recover from legacy floppy first,
+ LS-120 second, CD-ROM third." If there are multiple partitions abstracted
+ by a given device type, they should be reported in ascending order; this
+ order also applies to nested partitions, such as legacy MBR, where the
+ outermost partitions would have precedence in the reporting order. The
+ same logic applies to systems such as IDE that have precedence relationships
+ like "Master/Slave" or "Primary/Secondary". The master device should be
+ reported first, the slave second.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsBlockIoPeimGetMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ EFI_STATUS Status;
+ UFS_PEIM_HC_PRIVATE_DATA *Private;
+ EFI_SCSI_SENSE_DATA SenseData;
+ UINT8 SenseDataLength;
+ EFI_SCSI_DISK_CAPACITY_DATA Capacity;
+ EFI_SCSI_DISK_CAPACITY_DATA16 Capacity16;
+ UINTN DataLength;
+ BOOLEAN NeedRetry;
+
+ Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
+ NeedRetry = TRUE;
+
+ if (DeviceIndex >= UFS_PEIM_MAX_LUNS) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ZeroMem (&SenseData, sizeof (SenseData));
+ ZeroMem (&Capacity, sizeof (Capacity));
+ ZeroMem (&Capacity16, sizeof (Capacity16));
+ SenseDataLength = sizeof (SenseData);
+ //
+ // First test unit ready
+ //
+ do {
+ Status = UfsPeimTestUnitReady (
+ Private,
+ DeviceIndex,
+ &SenseData,
+ &SenseDataLength
+ );
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (SenseDataLength == 0) {
+ continue;
+ }
+
+ Status = UfsPeimParsingSenseKeys (&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ } while (NeedRetry);
+
+ DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);
+ SenseDataLength = 0;
+ Status = UfsPeimReadCapacity (Private, DeviceIndex, &Capacity, (UINT32 *)&DataLength, NULL, &SenseDataLength);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((Capacity.LastLba3 == 0xff) && (Capacity.LastLba2 == 0xff) &&
+ (Capacity.LastLba1 == 0xff) && (Capacity.LastLba0 == 0xff)) {
+ DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);
+ SenseDataLength = 0;
+ Status = UfsPeimReadCapacity16 (Private, DeviceIndex, &Capacity16, (UINT32 *)&DataLength, NULL, &SenseDataLength);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ MediaInfo->LastBlock = (Capacity16.LastLba3 << 24) | (Capacity16.LastLba2 << 16) | (Capacity16.LastLba1 << 8) | Capacity16.LastLba0;
+ MediaInfo->LastBlock |= ((UINT64)Capacity16.LastLba7 << 56) | ((UINT64)Capacity16.LastLba6 << 48) | ((UINT64)Capacity16.LastLba5 << 40) | ((UINT64)Capacity16.LastLba4 << 32);
+ MediaInfo->BlockSize = (Capacity16.BlockSize3 << 24) | (Capacity16.BlockSize2 << 16) | (Capacity16.BlockSize1 << 8) | Capacity16.BlockSize0;
+ } else {
+ MediaInfo->LastBlock = (Capacity.LastLba3 << 24) | (Capacity.LastLba2 << 16) | (Capacity.LastLba1 << 8) | Capacity.LastLba0;
+ MediaInfo->BlockSize = (Capacity.BlockSize3 << 24) | (Capacity.BlockSize2 << 16) | (Capacity.BlockSize1 << 8) | Capacity.BlockSize0;
+ }
+
+ MediaInfo->DeviceType = UfsDevice;
+ MediaInfo->MediaPresent = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsBlockIoPeimReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN BlockSize;
+ UINTN NumberOfBlocks;
+ UFS_PEIM_HC_PRIVATE_DATA *Private;
+ EFI_SCSI_SENSE_DATA SenseData;
+ UINT8 SenseDataLength;
+ BOOLEAN NeedRetry;
+
+ Status = EFI_SUCCESS;
+ NeedRetry = TRUE;
+ Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
+
+ ZeroMem (&SenseData, sizeof (SenseData));
+ SenseDataLength = sizeof (SenseData);
+
+ //
+ // Check parameters
+ //
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (DeviceIndex >= UFS_PEIM_MAX_LUNS) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ BlockSize = Private->Media[DeviceIndex].BlockSize;
+
+ if (BufferSize % BlockSize != 0) {
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (StartLBA > Private->Media[DeviceIndex].LastBlock) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ NumberOfBlocks = BufferSize / BlockSize;
+
+ do {
+ Status = UfsPeimTestUnitReady (
+ Private,
+ DeviceIndex,
+ &SenseData,
+ &SenseDataLength
+ );
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (SenseDataLength == 0) {
+ continue;
+ }
+
+ Status = UfsPeimParsingSenseKeys (&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ } while (NeedRetry);
+
+ SenseDataLength = 0;
+ if (Private->Media[DeviceIndex].LastBlock != ~((UINTN)0)) {
+ Status = UfsPeimRead10 (
+ Private,
+ DeviceIndex,
+ (UINT32)StartLBA,
+ (UINT32)NumberOfBlocks,
+ Buffer,
+ (UINT32 *)&BufferSize,
+ NULL,
+ &SenseDataLength
+ );
+ } else {
+ Status = UfsPeimRead16 (
+ Private,
+ DeviceIndex,
+ (UINT32)StartLBA,
+ (UINT32)NumberOfBlocks,
+ Buffer,
+ (UINT32 *)&BufferSize,
+ NULL,
+ &SenseDataLength
+ );
+ }
+ return Status;
+}
+
+/**
+ The user code starts with this function.
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS The driver is successfully initialized.
+ @retval Others Can't initialize the driver.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeUfsBlockIoPeim (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+ UFS_PEIM_HC_PRIVATE_DATA *Private;
+ EDKII_UFS_HOST_CONTROLLER_PPI *UfsHcPpi;
+ UINT32 Index;
+ UFS_CONFIG_DESC Config;
+ UINTN MmioBase;
+ UINT8 Controller;
+
+ //
+ // Shadow this PEIM to run from memory
+ //
+ if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // locate ufs host controller PPI
+ //
+ Status = PeiServicesLocatePpi (
+ &gEdkiiPeiUfsHostControllerPpiGuid,
+ 0,
+ NULL,
+ (VOID **) &UfsHcPpi
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Controller = 0;
+ MmioBase = 0;
+ while (TRUE) {
+ Status = UfsHcPpi->GetUfsHcMmioBar (UfsHcPpi, Controller, &MmioBase);
+ //
+ // When status is error, meant no controller is found
+ //
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Private = AllocateCopyPool (sizeof (UFS_PEIM_HC_PRIVATE_DATA), &gUfsHcPeimTemplate);
+ if (Private == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
+ Private->UfsHcBase = MmioBase;
+
+ //
+ // Initialize the memory pool which will be used in all transactions.
+ //
+ Status = UfsPeimInitMemPool (Private);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ //
+ // Initialize UFS Host Controller H/W.
+ //
+ Status = UfsControllerInit (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsDevicePei: Host Controller Initialization Error, Status = %r\n", Status));
+ Controller++;
+ continue;
+ }
+
+ //
+ // UFS 2.0 spec Section 13.1.3.3:
+ // At the end of the UFS Interconnect Layer initialization on both host and device side,
+ // the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready.
+ //
+ Status = UfsExecNopCmds (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Sending NOP IN command Error, Status = %r\n", Status));
+ Controller++;
+ continue;
+ }
+
+ //
+ // The host enables the device initialization completion by setting fDeviceInit flag.
+ //
+ Status = UfsSetFlag (Private, UfsFlagDevInit);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Set fDeviceInit Flag Error, Status = %r\n", Status));
+ Controller++;
+ continue;
+ }
+
+ //
+ // Get Ufs Device's Lun Info by reading Configuration Descriptor.
+ //
+ Status = UfsRwDeviceDesc (Private, TRUE, UfsConfigDesc, 0, 0, &Config, sizeof (UFS_CONFIG_DESC));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Get Configuration Descriptor Error, Status = %r\n", Status));
+ Controller++;
+ continue;
+ }
+
+ for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) {
+ if (Config.UnitDescConfParams[Index].LunEn != 0) {
+ Private->Luns.BitMask |= (BIT0 << Index);
+ DEBUG ((EFI_D_INFO, "Ufs %d Lun %d is enabled\n", Controller, Index));
+ }
+ }
+
+ Status = PeiServicesInstallPpi (&Private->BlkIoPpiList);
+ Controller++;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h
new file mode 100644
index 0000000000..835b9c61cf
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h
@@ -0,0 +1,434 @@
+/** @file
+
+ Copyright (c) 2014, Intel Corporation. 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 _UFS_BLOCK_IO_PEI_H_
+#define _UFS_BLOCK_IO_PEI_H_
+
+#include <PiPei.h>
+
+#include <Ppi/UfsHostController.h>
+#include <Ppi/BlockIo.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+#include <Library/PeiServicesLib.h>
+
+#include <IndustryStandard/Scsi.h>
+
+#include "UfsHci.h"
+#include "UfsHcMem.h"
+
+#define UFS_PEIM_HC_SIG SIGNATURE_32 ('U', 'F', 'S', 'H')
+
+#define UFS_PEIM_MAX_LUNS 8
+
+typedef struct {
+ UINT8 Lun[UFS_PEIM_MAX_LUNS];
+ UINT16 BitMask:12; // Bit 0~7 is for common luns. Bit 8~11 is reserved for those well known luns
+ UINT16 Rsvd:4;
+} UFS_PEIM_EXPOSED_LUNS;
+
+typedef struct {
+ ///
+ /// The timeout, in 100 ns units, to use for the execution of this SCSI
+ /// Request Packet. A Timeout value of 0 means that this function
+ /// will wait indefinitely for the SCSI Request Packet to execute. If
+ /// Timeout is greater than zero, then this function will return
+ /// EFI_TIMEOUT if the time required to execute the SCSI
+ /// Request Packet is greater than Timeout.
+ ///
+ UINT64 Timeout;
+ ///
+ /// A pointer to the data buffer to transfer between the SCSI
+ /// controller and the SCSI device for read and bidirectional commands.
+ ///
+ VOID *InDataBuffer;
+ ///
+ /// A pointer to the data buffer to transfer between the SCSI
+ /// controller and the SCSI device for write or bidirectional commands.
+ ///
+ VOID *OutDataBuffer;
+ ///
+ /// A pointer to the sense data that was generated by the execution of
+ /// the SCSI Request Packet.
+ ///
+ VOID *SenseData;
+ ///
+ /// A pointer to buffer that contains the Command Data Block to
+ /// send to the SCSI device specified by Target and Lun.
+ ///
+ VOID *Cdb;
+ ///
+ /// On Input, the size, in bytes, of InDataBuffer. On output, the
+ /// number of bytes transferred between the SCSI controller and the SCSI device.
+ ///
+ UINT32 InTransferLength;
+ ///
+ /// On Input, the size, in bytes of OutDataBuffer. On Output, the
+ /// Number of bytes transferred between SCSI Controller and the SCSI device.
+ ///
+ UINT32 OutTransferLength;
+ ///
+ /// The length, in bytes, of the buffer Cdb. The standard values are 6,
+ /// 10, 12, and 16, but other values are possible if a variable length CDB is used.
+ ///
+ UINT8 CdbLength;
+ ///
+ /// The direction of the data transfer. 0 for reads, 1 for writes. A
+ /// value of 2 is Reserved for Bi-Directional SCSI commands.
+ ///
+ UINT8 DataDirection;
+ ///
+ /// On input, the length in bytes of the SenseData buffer. On
+ /// output, the number of bytes written to the SenseData buffer.
+ ///
+ UINT8 SenseDataLength;
+} UFS_SCSI_REQUEST_PACKET;
+
+typedef struct _UFS_PEIM_HC_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+
+ UFS_PEIM_MEM_POOL *Pool;
+
+ EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi;
+ EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList;
+ EFI_PEI_BLOCK_IO_MEDIA Media[UFS_PEIM_MAX_LUNS];
+
+ UINTN UfsHcBase;
+ UINT32 Capabilities;
+
+ UINT8 TaskTag;
+
+ VOID *UtpTrlBase;
+ UINT8 Nutrs;
+ VOID *UtpTmrlBase;
+ UINT8 Nutmrs;
+
+ UFS_PEIM_EXPOSED_LUNS Luns;
+} UFS_PEIM_HC_PRIVATE_DATA;
+
+#define UFS_TIMEOUT MultU64x32((UINT64)(3), 10000000)
+
+#define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8)
+
+#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
+
+#define GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS(a) CR (a, UFS_PEIM_HC_PRIVATE_DATA, BlkIoPpi, UFS_PEIM_HC_SIG)
+
+#define UFS_SCSI_OP_LENGTH_SIX 0x6
+#define UFS_SCSI_OP_LENGTH_TEN 0xa
+#define UFS_SCSI_OP_LENGTH_SIXTEEN 0x10
+
+typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET {
+ UINT64 Timeout;
+ VOID *InDataBuffer;
+ VOID *OutDataBuffer;
+ UINT8 Opcode;
+ UINT8 DescId;
+ UINT8 Index;
+ UINT8 Selector;
+ UINT32 InTransferLength;
+ UINT32 OutTransferLength;
+ UINT8 DataDirection;
+ UINT8 Ocs;
+} UFS_DEVICE_MANAGEMENT_REQUEST_PACKET;
+
+/**
+ Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the
+ UFS device.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsExecScsiCmds (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 Lun,
+ IN OUT UFS_SCSI_REQUEST_PACKET *Packet
+ );
+
+/**
+ Initialize the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+UfsControllerInit (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ );
+
+/**
+ Stop the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully.
+ @retval Others A device error occurred while stopping the controller.
+
+**/
+EFI_STATUS
+UfsControllerStop (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ );
+
+/**
+ Set specified flag to 1 on a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be set.
+
+ @retval EFI_SUCCESS The flag was set successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag.
+
+**/
+EFI_STATUS
+UfsSetFlag (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 FlagId
+ );
+
+/**
+ Read or write specified device descriptor of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] DescId The ID of device descriptor.
+ @param[in] Index The Index of device descriptor.
+ @param[in] Selector The Selector of device descriptor.
+ @param[in, out] Descriptor The buffer of device descriptor to be read or written.
+ @param[in] DescSize The size of device descriptor buffer.
+
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor.
+
+**/
+EFI_STATUS
+UfsRwDeviceDesc (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT VOID *Descriptor,
+ IN UINT32 DescSize
+ );
+
+/**
+ Sends NOP IN cmd to a UFS device for initialization process request.
+ For more details, please refer to UFS 2.0 spec Figure 13.3.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was
+ received successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute.
+
+**/
+EFI_STATUS
+UfsExecNopCmds (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ );
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. To the PEI ATAPI driver, it returns the number
+ of all the detected ATAPI devices it detects during the enumeration process.
+ To the PEI legacy floppy driver, it returns the number of all the legacy
+ devices it finds during its enumeration process. If no device is detected,
+ then the function will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsBlockIoPeimGetDeviceNo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ );
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @par Note:
+ The MediaInfo structure describes an enumeration of possible block device
+ types. This enumeration exists because no device paths are actually passed
+ across interfaces that describe the type or class of hardware that is publishing
+ the block I/O interface. This enumeration will allow for policy decisions
+ in the Recovery PEIM, such as "Try to recover from legacy floppy first,
+ LS-120 second, CD-ROM third." If there are multiple partitions abstracted
+ by a given device type, they should be reported in ascending order; this
+ order also applies to nested partitions, such as legacy MBR, where the
+ outermost partitions would have precedence in the reporting order. The
+ same logic applies to systems such as IDE that have precedence relationships
+ like "Master/Slave" or "Primary/Secondary". The master device should be
+ reported first, the slave second.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsBlockIoPeimGetMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsBlockIoPeimReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+/**
+ Initialize the memory management pool for the host controller.
+
+ @param Private The Ufs Peim driver private data.
+
+ @retval EFI_SUCCESS The memory pool is initialized.
+ @retval Others Fail to init the memory pool.
+
+**/
+EFI_STATUS
+UfsPeimInitMemPool (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ );
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool.
+ @param Size Size of the memory to allocate.
+
+ @return The allocated memory or NULL.
+
+**/
+VOID *
+UfsPeimAllocateMem (
+ IN UFS_PEIM_MEM_POOL *Pool,
+ IN UINTN Size
+ );
+
+/**
+ Free the allocated memory back to the memory pool.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The memory to free.
+ @param Size The size of the memory to free.
+
+**/
+VOID
+UfsPeimFreeMem (
+ IN UFS_PEIM_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf
new file mode 100644
index 0000000000..574e244d3a
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf
@@ -0,0 +1,61 @@
+## @file
+# Description file for the Universal Flash Storage (UFS) Peim driver.
+#
+# Copyright (c) 2014 - 2015, Intel Corporation. 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 = 0x00010005
+ BASE_NAME = UfsBlockIoPei
+ MODULE_UNI_FILE = UfsBlockIoPei.uni
+ FILE_GUID = BE189D38-C963-41CF-B695-D90E9E545A13
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 0.9
+
+ ENTRY_POINT = InitializeUfsBlockIoPeim
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ UfsBlockIoPei.c
+ UfsBlockIoPei.h
+ UfsHci.c
+ UfsHci.h
+ UfsHcMem.c
+ UfsHcMem.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ IoLib
+ TimerLib
+ BaseMemoryLib
+ PeimEntryPoint
+ PeiServicesLib
+ DebugLib
+
+[Ppis]
+ gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES
+ gEdkiiPeiUfsHostControllerPpiGuid ## CONSUMES
+
+[Depex]
+ gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiUfsHostControllerPpiGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ UfsBlockIoPeiExtra.uni
+
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.uni b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.uni
new file mode 100644
index 0000000000..23b6e633b9
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.uni b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.uni
new file mode 100644
index 0000000000..706fcdc8f4
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c
new file mode 100644
index 0000000000..cc6c3d4e3e
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c
@@ -0,0 +1,455 @@
+/** @file
+
+Copyright (c) 2014, Intel Corporation. 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 "UfsBlockIoPei.h"
+
+/**
+ Allocate a block of memory to be used by the buffer pool.
+
+ @param Pages How many pages to allocate.
+
+ @return The allocated memory block or NULL if failed.
+
+**/
+UFS_PEIM_MEM_BLOCK *
+UfsPeimAllocMemBlock (
+ IN UINTN Pages
+ )
+{
+ UFS_PEIM_MEM_BLOCK *Block;
+ EFI_STATUS Status;
+ VOID *TempPtr;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ TempPtr = NULL;
+ Block = NULL;
+
+ Status = PeiServicesAllocatePool (sizeof(UFS_PEIM_MEM_BLOCK), &TempPtr);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(UFS_PEIM_MEM_BLOCK));
+
+ //
+ // each bit in the bit array represents UFS_PEIM_MEM_UNIT
+ // bytes of memory in the memory block.
+ //
+ ASSERT (UFS_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
+
+ Block = (UFS_PEIM_MEM_BLOCK*)(UINTN)TempPtr;
+ Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
+ Block->BitsLen = Block->BufLen / (UFS_PEIM_MEM_UNIT * 8);
+
+ Status = PeiServicesAllocatePool (Block->BitsLen, &TempPtr);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ ZeroMem ((VOID*)(UINTN)TempPtr, Block->BitsLen);
+
+ Block->Bits = (UINT8*)(UINTN)TempPtr;
+
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ Pages,
+ &Address
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ ZeroMem ((VOID*)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages));
+
+ Block->Buf = (UINT8*)((UINTN)Address);
+ Block->Next = NULL;
+
+ return Block;
+}
+
+/**
+ Free the memory block from the memory pool.
+
+ @param Pool The memory pool to free the block from.
+ @param Block The memory block to free.
+
+**/
+VOID
+UfsPeimFreeMemBlock (
+ IN UFS_PEIM_MEM_POOL *Pool,
+ IN UFS_PEIM_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Pool != NULL) && (Block != NULL));
+}
+
+/**
+ Alloc some memory from the block.
+
+ @param Block The memory block to allocate memory from.
+ @param Units Number of memory units to allocate.
+
+ @return The pointer to the allocated memory. If couldn't allocate the needed memory,
+ the return value is NULL.
+
+**/
+VOID *
+UfsPeimAllocMemFromBlock (
+ IN UFS_PEIM_MEM_BLOCK *Block,
+ IN UINTN Units
+ )
+{
+ UINTN Byte;
+ UINT8 Bit;
+ UINTN StartByte;
+ UINT8 StartBit;
+ UINTN Available;
+ UINTN Count;
+
+ ASSERT ((Block != 0) && (Units != 0));
+
+ StartByte = 0;
+ StartBit = 0;
+ Available = 0;
+
+ for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
+ //
+ // If current bit is zero, the corresponding memory unit is
+ // available, otherwise we need to restart our searching.
+ // Available counts the consective number of zero bit.
+ //
+ if (!UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) {
+ Available++;
+
+ if (Available >= Units) {
+ break;
+ }
+
+ UFS_PEIM_NEXT_BIT (Byte, Bit);
+
+ } else {
+ UFS_PEIM_NEXT_BIT (Byte, Bit);
+
+ Available = 0;
+ StartByte = Byte;
+ StartBit = Bit;
+ }
+ }
+
+ if (Available < Units) {
+ return NULL;
+ }
+
+ //
+ // Mark the memory as allocated
+ //
+ Byte = StartByte;
+ Bit = StartBit;
+
+ for (Count = 0; Count < Units; Count++) {
+ ASSERT (!UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) UFS_PEIM_MEM_BIT (Bit));
+ UFS_PEIM_NEXT_BIT (Byte, Bit);
+ }
+
+ return Block->Buf + (StartByte * 8 + StartBit) * UFS_PEIM_MEM_UNIT;
+}
+
+/**
+ Insert the memory block to the pool's list of the blocks.
+
+ @param Head The head of the memory pool's block list.
+ @param Block The memory block to insert.
+
+**/
+VOID
+UfsPeimInsertMemBlockToPool (
+ IN UFS_PEIM_MEM_BLOCK *Head,
+ IN UFS_PEIM_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Head != NULL) && (Block != NULL));
+ Block->Next = Head->Next;
+ Head->Next = Block;
+}
+
+/**
+ Is the memory block empty?
+
+ @param Block The memory block to check.
+
+ @retval TRUE The memory block is empty.
+ @retval FALSE The memory block isn't empty.
+
+**/
+BOOLEAN
+UfsPeimIsMemBlockEmpty (
+ IN UFS_PEIM_MEM_BLOCK *Block
+ )
+{
+ UINTN Index;
+
+
+ for (Index = 0; Index < Block->BitsLen; Index++) {
+ if (Block->Bits[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Unlink the memory block from the pool's list.
+
+ @param Head The block list head of the memory's pool.
+ @param BlockToUnlink The memory block to unlink.
+
+**/
+VOID
+UfsPeimUnlinkMemBlock (
+ IN UFS_PEIM_MEM_BLOCK *Head,
+ IN UFS_PEIM_MEM_BLOCK *BlockToUnlink
+ )
+{
+ UFS_PEIM_MEM_BLOCK *Block;
+
+ ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ if (Block->Next == BlockToUnlink) {
+ Block->Next = BlockToUnlink->Next;
+ BlockToUnlink->Next = NULL;
+ break;
+ }
+ }
+}
+
+/**
+ Initialize the memory management pool for the host controller.
+
+ @param Private The Ufs Peim driver private data.
+
+ @retval EFI_SUCCESS The memory pool is initialized.
+ @retval Others Fail to init the memory pool.
+
+**/
+EFI_STATUS
+UfsPeimInitMemPool (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ UFS_PEIM_MEM_POOL *Pool;
+ EFI_STATUS Status;
+ VOID *TempPtr;
+
+ TempPtr = NULL;
+ Pool = NULL;
+
+ Status = PeiServicesAllocatePool (sizeof (UFS_PEIM_MEM_POOL), &TempPtr);
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (UFS_PEIM_MEM_POOL));
+
+ Pool = (UFS_PEIM_MEM_POOL *)((UINTN)TempPtr);
+
+ Pool->Head = UfsPeimAllocMemBlock (UFS_PEIM_MEM_DEFAULT_PAGES);
+
+ if (Pool->Head == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Private->Pool = Pool;
+ return EFI_SUCCESS;
+}
+
+/**
+ Release the memory management pool.
+
+ @param Pool The memory pool to free.
+
+ @retval EFI_DEVICE_ERROR Fail to free the memory pool.
+ @retval EFI_SUCCESS The memory pool is freed.
+
+**/
+EFI_STATUS
+UfsPeimFreeMemPool (
+ IN UFS_PEIM_MEM_POOL *Pool
+ )
+{
+ UFS_PEIM_MEM_BLOCK *Block;
+
+ ASSERT (Pool->Head != NULL);
+
+ //
+ // Unlink all the memory blocks from the pool, then free them.
+ // UfsPeimUnlinkMemBlock can't be used to unlink and free the
+ // first block.
+ //
+ for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
+ UfsPeimFreeMemBlock (Pool, Block);
+ }
+
+ UfsPeimFreeMemBlock (Pool, Pool->Head);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Pool The host controller's memory pool.
+ @param Size Size of the memory to allocate.
+
+ @return The allocated memory or NULL.
+
+**/
+VOID *
+UfsPeimAllocateMem (
+ IN UFS_PEIM_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+{
+ UFS_PEIM_MEM_BLOCK *Head;
+ UFS_PEIM_MEM_BLOCK *Block;
+ UFS_PEIM_MEM_BLOCK *NewBlock;
+ VOID *Mem;
+ UINTN AllocSize;
+ UINTN Pages;
+
+ Mem = NULL;
+ AllocSize = UFS_PEIM_MEM_ROUND (Size);
+ Head = Pool->Head;
+ ASSERT (Head != NULL);
+
+ //
+ // First check whether current memory blocks can satisfy the allocation.
+ //
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ Mem = UfsPeimAllocMemFromBlock (Block, AllocSize / UFS_PEIM_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ break;
+ }
+ }
+
+ if (Mem != NULL) {
+ return Mem;
+ }
+
+ //
+ // Create a new memory block if there is not enough memory
+ // in the pool. If the allocation size is larger than the
+ // default page number, just allocate a large enough memory
+ // block. Otherwise allocate default pages.
+ //
+ if (AllocSize > EFI_PAGES_TO_SIZE (UFS_PEIM_MEM_DEFAULT_PAGES)) {
+ Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
+ } else {
+ Pages = UFS_PEIM_MEM_DEFAULT_PAGES;
+ }
+
+ NewBlock = UfsPeimAllocMemBlock (Pages);
+ if (NewBlock == NULL) {
+ return NULL;
+ }
+
+ //
+ // Add the new memory block to the pool, then allocate memory from it
+ //
+ UfsPeimInsertMemBlockToPool (Head, NewBlock);
+ Mem = UfsPeimAllocMemFromBlock (NewBlock, AllocSize / UFS_PEIM_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ }
+
+ return Mem;
+}
+
+/**
+ Free the allocated memory back to the memory pool.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The memory to free.
+ @param Size The size of the memory to free.
+
+**/
+VOID
+UfsPeimFreeMem (
+ IN UFS_PEIM_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ UFS_PEIM_MEM_BLOCK *Head;
+ UFS_PEIM_MEM_BLOCK *Block;
+ UINT8 *ToFree;
+ UINTN AllocSize;
+ UINTN Byte;
+ UINTN Bit;
+ UINTN Count;
+
+ Head = Pool->Head;
+ AllocSize = UFS_PEIM_MEM_ROUND (Size);
+ ToFree = (UINT8 *) Mem;
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the memory to free.
+ //
+ if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
+ //
+ // compute the start byte and bit in the bit array
+ //
+ Byte = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) / 8;
+ Bit = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) % 8;
+
+ //
+ // reset associated bits in bit arry
+ //
+ for (Count = 0; Count < (AllocSize / UFS_PEIM_MEM_UNIT); Count++) {
+ ASSERT (UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ UFS_PEIM_MEM_BIT (Bit));
+ UFS_PEIM_NEXT_BIT (Byte, Bit);
+ }
+
+ break;
+ }
+ }
+
+ //
+ // If Block == NULL, it means that the current memory isn't
+ // in the host controller's pool. This is critical because
+ // the caller has passed in a wrong memory point
+ //
+ ASSERT (Block != NULL);
+
+ //
+ // Release the current memory block if it is empty and not the head
+ //
+ if ((Block != Head) && UfsPeimIsMemBlockEmpty (Block)) {
+ UfsPeimFreeMemBlock (Pool, Block);
+ }
+
+ return ;
+}
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h
new file mode 100644
index 0000000000..3c4b2407c8
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h
@@ -0,0 +1,61 @@
+/** @file
+
+Copyright (c) 2014, Intel Corporation. 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 _UFS_PEIM_MEM_H_
+#define _UFS_PEIM_MEM_H_
+
+#define UFS_PEIM_MEM_BIT(a) ((UINTN)(1 << (a)))
+
+#define UFS_PEIM_MEM_BIT_IS_SET(Data, Bit) \
+ ((BOOLEAN)(((Data) & UFS_PEIM_MEM_BIT(Bit)) == UFS_PEIM_MEM_BIT(Bit)))
+
+typedef struct _UFS_PEIM_MEM_BLOCK UFS_PEIM_MEM_BLOCK;
+
+struct _UFS_PEIM_MEM_BLOCK {
+ UINT8 *Bits; // Bit array to record which unit is allocated
+ UINTN BitsLen;
+ UINT8 *Buf;
+ UINTN BufLen; // Memory size in bytes
+ UFS_PEIM_MEM_BLOCK *Next;
+};
+
+typedef struct _UFS_PEIM_MEM_POOL {
+ UFS_PEIM_MEM_BLOCK *Head;
+} UFS_PEIM_MEM_POOL;
+
+//
+// Memory allocation unit, note that the value must meet UFS spec alignment requirement.
+//
+#define UFS_PEIM_MEM_UNIT 128
+
+#define UFS_PEIM_MEM_UNIT_MASK (UFS_PEIM_MEM_UNIT - 1)
+#define UFS_PEIM_MEM_DEFAULT_PAGES 16
+
+#define UFS_PEIM_MEM_ROUND(Len) (((Len) + UFS_PEIM_MEM_UNIT_MASK) & (~UFS_PEIM_MEM_UNIT_MASK))
+
+//
+// Advance the byte and bit to the next bit, adjust byte accordingly.
+//
+#define UFS_PEIM_NEXT_BIT(Byte, Bit) \
+ do { \
+ (Bit)++; \
+ if ((Bit) > 7) { \
+ (Byte)++; \
+ (Bit) = 0; \
+ } \
+ } while (0)
+
+#endif
+
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c
new file mode 100644
index 0000000000..68800570cb
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c
@@ -0,0 +1,1787 @@
+/** @file
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsBlockIoPei.h"
+
+/**
+ Wait for the value of the specified system memory set to the test value.
+
+ @param Address The system memory address to test.
+ @param MaskValue The mask value of memory.
+ @param TestValue The test value of memory.
+ @param Timeout The time out value for wait memory set, uses 100ns as a unit.
+
+ @retval EFI_TIMEOUT The system memory setting is time out.
+ @retval EFI_SUCCESS The system memory is correct set.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsWaitMemSet (
+ IN UINTN Address,
+ IN UINT32 MaskValue,
+ IN UINT32 TestValue,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Value;
+ UINT64 Delay;
+ BOOLEAN InfiniteWait;
+
+ if (Timeout == 0) {
+ InfiniteWait = TRUE;
+ } else {
+ InfiniteWait = FALSE;
+ }
+
+ Delay = DivU64x32 (Timeout, 10) + 1;
+
+ do {
+ //
+ // Access PCI MMIO space to see if the value is the tested one.
+ //
+ Value = MmioRead32 (Address) & MaskValue;
+
+ if (Value == TestValue) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stall for 1 microseconds.
+ //
+ MicroSecondDelay (1);
+
+ Delay--;
+
+ } while (InfiniteWait || (Delay > 0));
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Dump UIC command execution result for debugging.
+
+ @param[in] UicOpcode The executed UIC opcode.
+ @param[in] Result The result to be parsed.
+
+**/
+VOID
+DumpUicCmdExecResult (
+ IN UINT8 UicOpcode,
+ IN UINT8 Result
+ )
+{
+ if (UicOpcode <= UfsUicDmePeerSet) {
+ switch (Result) {
+ case 0x00:
+ break;
+ case 0x01:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x02:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n"));
+ break;
+ case 0x03:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x04:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x05:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_INDEX\n"));
+ break;
+ case 0x06:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x07:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n"));
+ break;
+ case 0x08:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n"));
+ break;
+ case 0x09:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BUSY\n"));
+ break;
+ case 0x0A:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - DME_FAILURE\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+ } else {
+ switch (Result) {
+ case 0x00:
+ break;
+ case 0x01:
+ DEBUG ((EFI_D_VERBOSE, "UIC control command fails - FAILURE\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+ }
+}
+
+/**
+ Dump QUERY RESPONSE UPIU result for debugging.
+
+ @param[in] Result The result to be parsed.
+
+**/
+VOID
+DumpQueryResponseResult (
+ IN UINT8 Result
+ )
+{
+ switch (Result) {
+ case 0xF6:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Readable\n"));
+ break;
+ case 0xF7:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Writeable\n"));
+ break;
+ case 0xF8:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Already Written\n"));
+ break;
+ case 0xF9:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Length\n"));
+ break;
+ case 0xFA:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Value\n"));
+ break;
+ case 0xFB:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Selector\n"));
+ break;
+ case 0xFC:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Index\n"));
+ break;
+ case 0xFD:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Idn\n"));
+ break;
+ case 0xFE:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Opcode\n"));
+ break;
+ case 0xFF:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with General Failure\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+}
+
+/**
+ Swap little endian to big endian.
+
+ @param[in, out] Buffer The data buffer. In input, it contains little endian data.
+ In output, it will become big endian.
+ @param[in] BufferSize The length of converted data.
+
+**/
+VOID
+SwapLittleEndianToBigEndian (
+ IN OUT UINT8 *Buffer,
+ IN UINT32 BufferSize
+ )
+{
+ UINT32 Index;
+ UINT8 Temp;
+ UINT32 SwapCount;
+
+ SwapCount = BufferSize / 2;
+ for (Index = 0; Index < SwapCount; Index++) {
+ Temp = Buffer[Index];
+ Buffer[Index] = Buffer[BufferSize - 1 - Index];
+ Buffer[BufferSize - 1 - Index] = Temp;
+ }
+}
+
+/**
+ Fill TSF field of QUERY REQUEST UPIU.
+
+ @param[in, out] TsfBase The base address of TSF field of QUERY REQUEST UPIU.
+ @param[in] Opcode The opcode of request.
+ @param[in] DescId The descriptor ID of request.
+ @param[in] Index The index of request.
+ @param[in] Selector The selector of request.
+ @param[in] Length The length of transferred data. The maximum is 4.
+ @param[in] Value The value of transferred data.
+
+**/
+VOID
+UfsFillTsfOfQueryReqUpiu (
+ IN OUT UTP_UPIU_TSF *TsfBase,
+ IN UINT8 Opcode,
+ IN UINT8 DescId OPTIONAL,
+ IN UINT8 Index OPTIONAL,
+ IN UINT8 Selector OPTIONAL,
+ IN UINT16 Length OPTIONAL,
+ IN UINT32 Value OPTIONAL
+ )
+{
+ ASSERT (TsfBase != NULL);
+ ASSERT (Opcode <= UtpQueryFuncOpcodeTogFlag);
+
+ TsfBase->Opcode = Opcode;
+ if (Opcode != UtpQueryFuncOpcodeNop) {
+ TsfBase->DescId = DescId;
+ TsfBase->Index = Index;
+ TsfBase->Selector = Selector;
+
+ if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) {
+ SwapLittleEndianToBigEndian ((UINT8*)&Length, sizeof (Length));
+ TsfBase->Length = Length;
+ }
+
+ if (Opcode == UtpQueryFuncOpcodeWrAttr) {
+ SwapLittleEndianToBigEndian ((UINT8*)&Value, sizeof (Value));
+ TsfBase->Value = Value;
+ }
+ }
+}
+
+/**
+ Initialize COMMAND UPIU.
+
+ @param[in, out] Command The base address of COMMAND UPIU.
+ @param[in] Lun The Lun on which the SCSI command is executed.
+ @param[in] TaskTag The task tag of request.
+ @param[in] Cdb The cdb buffer containing SCSI command.
+ @param[in] CdbLength The cdb length.
+ @param[in] DataDirection The direction of data transfer.
+ @param[in] ExpDataTranLen The expected transfer data length.
+
+ @retval EFI_SUCCESS The initialization succeed.
+
+**/
+EFI_STATUS
+UfsInitCommandUpiu (
+ IN OUT UTP_COMMAND_UPIU *Command,
+ IN UINT8 Lun,
+ IN UINT8 TaskTag,
+ IN UINT8 *Cdb,
+ IN UINT8 CdbLength,
+ IN UFS_DATA_DIRECTION DataDirection,
+ IN UINT32 ExpDataTranLen
+ )
+{
+ UINT8 Flags;
+
+ ASSERT ((Command != NULL) && (Cdb != NULL));
+
+ //
+ // Task attribute is hard-coded to Ordered.
+ //
+ if (DataDirection == UfsDataIn) {
+ Flags = BIT0 | BIT6;
+ } else if (DataDirection == UfsDataOut) {
+ Flags = BIT0 | BIT5;
+ } else {
+ Flags = BIT0;
+ }
+
+ //
+ // Fill UTP COMMAND UPIU associated fields.
+ //
+ Command->TransCode = 0x01;
+ Command->Flags = Flags;
+ Command->Lun = Lun;
+ Command->TaskTag = TaskTag;
+ Command->CmdSet = 0x00;
+ SwapLittleEndianToBigEndian ((UINT8*)&ExpDataTranLen, sizeof (ExpDataTranLen));
+ Command->ExpDataTranLen = ExpDataTranLen;
+
+ CopyMem (Command->Cdb, Cdb, CdbLength);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize UTP PRDT for data transfer.
+
+ @param[in] Prdt The base address of PRDT.
+ @param[in] Buffer The buffer to be read or written.
+ @param[in] BufferSize The data size to be read or written.
+
+ @retval EFI_SUCCESS The initialization succeed.
+
+**/
+EFI_STATUS
+UfsInitUtpPrdt (
+ IN UTP_TR_PRD *Prdt,
+ IN VOID *Buffer,
+ IN UINT32 BufferSize
+ )
+{
+ UINT32 PrdtIndex;
+ UINT32 RemainingLen;
+ UINT8 *Remaining;
+ UINTN PrdtNumber;
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
+
+ RemainingLen = BufferSize;
+ Remaining = Buffer;
+ PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
+
+ for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {
+ if (RemainingLen < UFS_MAX_DATA_LEN_PER_PRD) {
+ Prdt[PrdtIndex].DbCount = (UINT32)RemainingLen - 1;
+ } else {
+ Prdt[PrdtIndex].DbCount = UFS_MAX_DATA_LEN_PER_PRD - 1;
+ }
+
+ Prdt[PrdtIndex].DbAddr = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 2);
+ Prdt[PrdtIndex].DbAddrU = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 32);
+ RemainingLen -= UFS_MAX_DATA_LEN_PER_PRD;
+ Remaining += UFS_MAX_DATA_LEN_PER_PRD;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize QUERY REQUEST UPIU.
+
+ @param[in, out] QueryReq The base address of QUERY REQUEST UPIU.
+ @param[in] TaskTag The task tag of request.
+ @param[in] Opcode The opcode of request.
+ @param[in] DescId The descriptor ID of request.
+ @param[in] Index The index of request.
+ @param[in] Selector The selector of request.
+ @param[in] DataSize The data size to be read or written.
+ @param[in] Data The buffer to be read or written.
+
+ @retval EFI_SUCCESS The initialization succeed.
+
+**/
+EFI_STATUS
+UfsInitQueryRequestUpiu (
+ IN OUT UTP_QUERY_REQ_UPIU *QueryReq,
+ IN UINT8 TaskTag,
+ IN UINT8 Opcode,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN UINTN DataSize OPTIONAL,
+ IN UINT8 *Data OPTIONAL
+ )
+{
+ ASSERT (QueryReq != NULL);
+
+ QueryReq->TransCode = 0x16;
+ QueryReq->TaskTag = TaskTag;
+ if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeRdFlag) || (Opcode == UtpQueryFuncOpcodeRdAttr)) {
+ QueryReq->QueryFunc = QUERY_FUNC_STD_READ_REQ;
+ } else {
+ QueryReq->QueryFunc = QUERY_FUNC_STD_WRITE_REQ;
+ }
+
+ if (Opcode == UtpQueryFuncOpcodeWrAttr) {
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, *(UINT32*)Data);
+ } else if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) {
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, (UINT16)DataSize, 0);
+ } else {
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, 0);
+ }
+
+ if (Opcode == UtpQueryFuncOpcodeWrDesc) {
+ CopyMem (QueryReq + 1, Data, DataSize);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate COMMAND/RESPONSE UPIU for filling UTP TRD's command descriptor field.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The Lun on which the SCSI command is executed.
+ @param[in] Packet The pointer to the UFS_SCSI_REQUEST_PACKET data structure.
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.
+
+ @retval EFI_SUCCESS The creation succeed.
+ @retval EFI_DEVICE_ERROR The creation failed.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+
+**/
+EFI_STATUS
+UfsCreateScsiCommandDesc (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 Lun,
+ IN UFS_SCSI_REQUEST_PACKET *Packet,
+ IN UTP_TRD *Trd
+ )
+{
+ UINT8 *CommandDesc;
+ UINTN TotalLen;
+ UINTN PrdtNumber;
+ VOID *Buffer;
+ UINT32 Length;
+ UTP_COMMAND_UPIU *CommandUpiu;
+ UTP_TR_PRD *PrdtBase;
+ UFS_DATA_DIRECTION DataDirection;
+
+ ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL));
+
+ if (Packet->DataDirection == UfsDataIn) {
+ Buffer = Packet->InDataBuffer;
+ Length = Packet->InTransferLength;
+ DataDirection = UfsDataIn;
+ } else {
+ Buffer = Packet->OutDataBuffer;
+ Length = Packet->OutTransferLength;
+ DataDirection = UfsDataOut;
+ }
+
+ if (Length == 0) {
+ DataDirection = UfsNoData;
+ }
+
+ PrdtNumber = (UINTN)DivU64x32 ((UINT64)Length + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
+
+ TotalLen = ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)) + PrdtNumber * sizeof (UTP_TR_PRD);
+ CommandDesc = UfsPeimAllocateMem (Private->Pool, TotalLen);
+ if (CommandDesc == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CommandUpiu = (UTP_COMMAND_UPIU*)CommandDesc;
+ PrdtBase = (UTP_TR_PRD*)(CommandDesc + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));
+
+ UfsInitCommandUpiu (CommandUpiu, Lun, Private->TaskTag++, Packet->Cdb, Packet->CdbLength, DataDirection, Length);
+ UfsInitUtpPrdt (PrdtBase, Buffer, Length);
+
+ //
+ // Fill UTP_TRD associated fields
+ // NOTE: Some UFS host controllers request the Response UPIU and the Physical Region Description Table
+ // *MUST* be located at a 64-bit aligned boundary.
+ //
+ Trd->Int = UFS_INTERRUPT_COMMAND;
+ Trd->Dd = DataDirection;
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)(UINTN)CommandUpiu, 7);
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)(UINTN)CommandUpiu, 32);
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)), sizeof (UINT32));
+ Trd->PrdtL = (UINT16)PrdtNumber;
+ Trd->PrdtO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))), sizeof (UINT32));
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate QUERY REQUEST/QUERY RESPONSE UPIU for filling UTP TRD's command descriptor field.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Packet The pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET data structure.
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.
+
+ @retval EFI_SUCCESS The creation succeed.
+ @retval EFI_DEVICE_ERROR The creation failed.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+ @retval EFI_INVALID_PARAMETER The parameter passed in is invalid.
+
+**/
+EFI_STATUS
+UfsCreateDMCommandDesc (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet,
+ IN UTP_TRD *Trd
+ )
+{
+ UINT8 *CommandDesc;
+ UINTN TotalLen;
+ UTP_QUERY_REQ_UPIU *QueryReqUpiu;
+ UINT8 Opcode;
+ UINT32 DataSize;
+ UINT8 *Data;
+ UINT8 DataDirection;
+
+ ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL));
+
+ Opcode = Packet->Opcode;
+ if ((Opcode > UtpQueryFuncOpcodeTogFlag) || (Opcode == UtpQueryFuncOpcodeNop)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DataDirection = Packet->DataDirection;
+ if (DataDirection == UfsDataIn) {
+ DataSize = Packet->InTransferLength;
+ Data = Packet->InDataBuffer;
+ } else if (DataDirection == UfsDataOut) {
+ DataSize = Packet->OutTransferLength;
+ Data = Packet->OutDataBuffer;
+ } else {
+ DataSize = 0;
+ Data = NULL;
+ }
+
+ if (((Opcode != UtpQueryFuncOpcodeSetFlag) && (Opcode != UtpQueryFuncOpcodeClrFlag) && (Opcode != UtpQueryFuncOpcodeTogFlag))
+ && ((DataSize == 0) || (Data == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((Opcode == UtpQueryFuncOpcodeSetFlag) || (Opcode == UtpQueryFuncOpcodeClrFlag) || (Opcode == UtpQueryFuncOpcodeTogFlag))
+ && ((DataSize != 0) || (Data != NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Opcode == UtpQueryFuncOpcodeWrAttr) && (DataSize != sizeof (UINT32))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) {
+ TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize);
+ } else {
+ TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU));
+ }
+
+ CommandDesc = UfsPeimAllocateMem (Private->Pool, TotalLen);
+ if (CommandDesc == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize UTP QUERY REQUEST UPIU
+ //
+ QueryReqUpiu = (UTP_QUERY_REQ_UPIU*)CommandDesc;
+ UfsInitQueryRequestUpiu (
+ QueryReqUpiu,
+ Private->TaskTag++,
+ Opcode,
+ Packet->DescId,
+ Packet->Index,
+ Packet->Selector,
+ DataSize,
+ Data
+ );
+
+ //
+ // Fill UTP_TRD associated fields
+ // NOTE: Some UFS host controllers request the Query Response UPIU *MUST* be located at a 64-bit aligned boundary.
+ //
+ Trd->Int = UFS_INTERRUPT_COMMAND;
+ Trd->Dd = DataDirection;
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;
+ Trd->Ocs = 0x0F;
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)(UINTN)QueryReqUpiu, 7);
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)(UINTN)QueryReqUpiu, 32);
+ if (Opcode == UtpQueryFuncOpcodeWrDesc) {
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32));
+ } else {
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)), sizeof (UINT32));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate NOP IN and NOP OUT UPIU for filling UTP TRD's command descriptor field.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.
+
+ @retval EFI_SUCCESS The creation succeed.
+ @retval EFI_DEVICE_ERROR The creation failed.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+
+**/
+EFI_STATUS
+UfsCreateNopCommandDesc (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UTP_TRD *Trd
+ )
+{
+ UINT8 *CommandDesc;
+ UINTN TotalLen;
+ UTP_NOP_OUT_UPIU *NopOutUpiu;
+
+ ASSERT ((Private != NULL) && (Trd != NULL));
+
+ TotalLen = ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)) + ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU));
+ CommandDesc = UfsPeimAllocateMem (Private->Pool, TotalLen);
+ if (CommandDesc == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NopOutUpiu = (UTP_NOP_OUT_UPIU*)CommandDesc;
+
+ NopOutUpiu->TaskTag = Private->TaskTag++;
+
+ //
+ // Fill UTP_TRD associated fields
+ // NOTE: Some UFS host controllers request the Nop Out UPIU *MUST* be located at a 64-bit aligned boundary.
+ //
+ Trd->Int = UFS_INTERRUPT_COMMAND;
+ Trd->Dd = 0x00;
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)(UINTN)NopOutUpiu, 7);
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)(UINTN)NopOutUpiu, 32);
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)), sizeof (UINT32));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find out available slot in transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[out] Slot The available slot.
+
+ @retval EFI_SUCCESS The available slot was found successfully.
+
+**/
+EFI_STATUS
+UfsFindAvailableSlotInTrl (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ OUT UINT8 *Slot
+ )
+{
+ ASSERT ((Private != NULL) && (Slot != NULL));
+
+ //
+ // The simplest algo to always use slot 0.
+ // TODO: enhance it to support async transfer with multiple slot.
+ //
+ *Slot = 0;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find out available slot in task management transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[out] Slot The available slot.
+
+ @retval EFI_SUCCESS The available slot was found successfully.
+
+**/
+EFI_STATUS
+UfsFindAvailableSlotInTmrl (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ OUT UINT8 *Slot
+ )
+{
+ ASSERT ((Private != NULL) && (Slot != NULL));
+
+ //
+ // The simplest algo to always use slot 0.
+ // TODO: enhance it to support async transfer with multiple slot.
+ //
+ *Slot = 0;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Start specified slot in transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Slot The slot to be started.
+
+**/
+VOID
+UfsStartExecCmd (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 Slot
+ )
+{
+ UINTN UfsHcBase;
+ UINTN Address;
+ UINT32 Data;
+
+ UfsHcBase = Private->UfsHcBase;
+
+ Address = UfsHcBase + UFS_HC_UTRLRSR_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) {
+ MmioWrite32 (Address, UFS_HC_UTRLRSR);
+ }
+
+ Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ MmioWrite32 (Address, BIT0 << Slot);
+}
+
+/**
+ Stop specified slot in transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Slot The slot to be stop.
+
+**/
+VOID
+UfsStopExecCmd (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 Slot
+ )
+{
+ UINTN UfsHcBase;
+ UINTN Address;
+ UINT32 Data;
+
+ UfsHcBase = Private->UfsHcBase;
+
+ Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & (BIT0 << Slot)) != 0) {
+ Address = UfsHcBase + UFS_HC_UTRLCLR_OFFSET;
+ Data = MmioRead32 (Address);
+ MmioWrite32 (Address, (Data & ~(BIT0 << Slot)));
+ }
+}
+
+/**
+ Read or write specified device descriptor of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] DescId The ID of device descriptor.
+ @param[in] Index The Index of device descriptor.
+ @param[in] Selector The Selector of device descriptor.
+ @param[in, out] Descriptor The buffer of device descriptor to be read or written.
+ @param[in] DescSize The size of device descriptor buffer.
+
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor.
+
+**/
+EFI_STATUS
+UfsRwDeviceDesc (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT VOID *Descriptor,
+ IN UINT32 DescSize
+ )
+{
+ EFI_STATUS Status;
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ UINT8 *CmdDescBase;
+ UINT32 CmdDescSize;
+ UINT16 ReturnDataSize;
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ Packet.DataDirection = UfsDataIn;
+ Packet.InDataBuffer = Descriptor;
+ Packet.InTransferLength = DescSize;
+ Packet.Opcode = UtpQueryFuncOpcodeRdDesc;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ Packet.OutDataBuffer = Descriptor;
+ Packet.OutTransferLength = DescSize;
+ Packet.Opcode = UtpQueryFuncOpcodeWrDesc;
+ }
+ Packet.DescId = DescId;
+ Packet.Index = Index;
+ Packet.Selector = Selector;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7));
+ QueryResp = (UTP_QUERY_RESP_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32));
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (QueryResp->QueryResp != 0) {
+ DumpQueryResponseResult (QueryResp->QueryResp);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ ReturnDataSize = QueryResp->Tsf.Length;
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16));
+
+ if (Read) {
+ CopyMem (Packet.InDataBuffer, (QueryResp + 1), ReturnDataSize);
+ Packet.InTransferLength = ReturnDataSize;
+ } else {
+ Packet.OutTransferLength = ReturnDataSize;
+ }
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsStopExecCmd (Private, Slot);
+ UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize);
+
+ return Status;
+}
+
+/**
+ Read or write specified attribute of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] AttrId The ID of Attribute.
+ @param[in] Index The Index of Attribute.
+ @param[in] Selector The Selector of Attribute.
+ @param[in, out] Attributes The value of Attribute to be read or written.
+
+ @retval EFI_SUCCESS The Attribute was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the Attribute.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the Attribute.
+
+**/
+EFI_STATUS
+UfsRwAttributes (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 AttrId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT UINT32 *Attributes
+ )
+{
+ EFI_STATUS Status;
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ UINT8 *CmdDescBase;
+ UINT32 CmdDescSize;
+ UINT32 ReturnData;
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ Packet.DataDirection = UfsDataIn;
+ Packet.Opcode = UtpQueryFuncOpcodeRdAttr;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ Packet.Opcode = UtpQueryFuncOpcodeWrAttr;
+ }
+ Packet.DescId = AttrId;
+ Packet.Index = Index;
+ Packet.Selector = Selector;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7));
+ QueryResp = (UTP_QUERY_RESP_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32));
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (QueryResp->QueryResp != 0) {
+ DumpQueryResponseResult (QueryResp->QueryResp);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ ReturnData = QueryResp->Tsf.Value;
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnData, sizeof (UINT32));
+ *Attributes = ReturnData;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsStopExecCmd (Private, Slot);
+ UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize);
+
+ return Status;
+}
+
+/**
+ Read or write specified flag of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] FlagId The ID of flag to be read or written.
+ @param[in, out] Value The value to set or clear flag.
+
+ @retval EFI_SUCCESS The flag was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the flag.
+
+**/
+EFI_STATUS
+UfsRwFlags (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 FlagId,
+ IN OUT UINT8 *Value
+ )
+{
+ EFI_STATUS Status;
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ UINT8 *CmdDescBase;
+ UINT32 CmdDescSize;
+
+ if (Value == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ ASSERT (Value != NULL);
+ Packet.DataDirection = UfsDataIn;
+ Packet.Opcode = UtpQueryFuncOpcodeRdFlag;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ if (*Value == 1) {
+ Packet.Opcode = UtpQueryFuncOpcodeSetFlag;
+ } else if (*Value == 0) {
+ Packet.Opcode = UtpQueryFuncOpcodeClrFlag;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ Packet.DescId = FlagId;
+ Packet.Index = 0;
+ Packet.Selector = 0;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7));
+ QueryResp = (UTP_QUERY_RESP_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32));
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (QueryResp->QueryResp != 0) {
+ DumpQueryResponseResult (QueryResp->QueryResp);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ *Value = (UINT8)QueryResp->Tsf.Value;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsStopExecCmd (Private, Slot);
+ UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize);
+
+ return Status;
+}
+
+/**
+ Set specified flag to 1 on a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be set.
+
+ @retval EFI_SUCCESS The flag was set successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag.
+
+**/
+EFI_STATUS
+UfsSetFlag (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 FlagId
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Value = 1;
+ Status = UfsRwFlags (Private, FALSE, FlagId, &Value);
+
+ return Status;
+}
+
+/**
+ Clear specified flag to 0 on a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be cleared.
+
+ @retval EFI_SUCCESS The flag was cleared successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to clear the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of clearing the flag.
+
+**/
+EFI_STATUS
+UfsClearFlag (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 FlagId
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Value = 0;
+ Status = UfsRwFlags (Private, FALSE, FlagId, &Value);
+
+ return Status;
+}
+
+/**
+ Read specified flag from a UFS device.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be read.
+ @param[out] Value The flag's value.
+
+ @retval EFI_SUCCESS The flag was read successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to read the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of reading the flag.
+
+**/
+EFI_STATUS
+UfsReadFlag (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 FlagId,
+ OUT UINT8 *Value
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UfsRwFlags (Private, TRUE, FlagId, Value);
+
+ return Status;
+}
+
+/**
+ Sends NOP IN cmd to a UFS device for initialization process request.
+ For more details, please refer to UFS 2.0 spec Figure 13.3.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was
+ received successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute.
+
+**/
+EFI_STATUS
+UfsExecNopCmds (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UTP_NOP_IN_UPIU *NopInUpiu;
+ UINT8 *CmdDescBase;
+ UINT32 CmdDescSize;
+ UINTN Address;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ Status = UfsCreateNopCommandDesc (Private, Trd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7));
+ NopInUpiu = (UTP_NOP_IN_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32));
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (NopInUpiu->Resp != 0) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+
+Exit:
+ UfsStopExecCmd (Private, Slot);
+ UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize);
+
+ return Status;
+}
+
+/**
+ Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the
+ UFS device.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsExecScsiCmds (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 Lun,
+ IN OUT UFS_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UINT8 *CmdDescBase;
+ UINT32 CmdDescSize;
+ UTP_RESPONSE_UPIU *Response;
+ UINT16 SenseDataLen;
+ UINT32 ResTranCount;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CmdDescBase = (UINT8*)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7));
+ CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet->Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get sense data if exists
+ //
+ Response = (UTP_RESPONSE_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32));
+ SenseDataLen = Response->SenseDataLen;
+ SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));
+
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) {
+ CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen);
+ Packet->SenseDataLength = (UINT8)SenseDataLen;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ if (Response->Response != 0) {
+ DEBUG ((EFI_D_ERROR, "UfsExecScsiCmds() fails with Target Failure\n"));
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ if (Packet->DataDirection == UfsDataIn) {
+ if ((Response->Flags & BIT5) == BIT5) {
+ ResTranCount = Response->ResTranCount;
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));
+ Packet->InTransferLength -= ResTranCount;
+ }
+ } else if (Packet->DataDirection == UfsDataOut) {
+ if ((Response->Flags & BIT5) == BIT5) {
+ ResTranCount = Response->ResTranCount;
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));
+ Packet->OutTransferLength -= ResTranCount;
+ }
+ }
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsStopExecCmd (Private, Slot);
+ UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize);
+
+ return Status;
+}
+
+
+/**
+ Sent UIC DME_LINKSTARTUP command to start the link startup procedure.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+ @param[in] UicOpcode The opcode of the UIC command.
+ @param[in] Arg1 The value for 1st argument of the UIC command.
+ @param[in] Arg2 The value for 2nd argument of the UIC command.
+ @param[in] Arg3 The value for 3rd argument of the UIC command.
+
+ @return EFI_SUCCESS Successfully execute this UIC command and detect attached UFS device.
+ @return EFI_DEVICE_ERROR Fail to execute this UIC command and detect attached UFS device.
+ @return EFI_NOT_FOUND The presence of the UFS device isn't detected.
+
+**/
+EFI_STATUS
+UfsExecUicCommands (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private,
+ IN UINT8 UicOpcode,
+ IN UINT32 Arg1,
+ IN UINT32 Arg2,
+ IN UINT32 Arg3
+ )
+{
+ EFI_STATUS Status;
+ UINTN Address;
+ UINT32 Data;
+ UINTN UfsHcBase;
+
+ UfsHcBase = Private->UfsHcBase;
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) {
+ //
+ // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first.
+ //
+ MmioWrite32 (Address, Data);
+ }
+
+ //
+ // When programming UIC command registers, host software shall set the register UICCMD
+ // only after all the UIC command argument registers (UICCMDARG1, UICCMDARG2 and UICCMDARG3)
+ // are set.
+ //
+ Address = UfsHcBase + UFS_HC_UCMD_ARG1_OFFSET;
+ MmioWrite32 (Address, Arg1);
+
+ Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET;
+ MmioWrite32 (Address, Arg2);
+
+ Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET;
+ MmioWrite32 (Address, Arg3);
+
+ //
+ // Host software shall only set the UICCMD if HCS.UCRDY is set to 1.
+ //
+ Address = Private->UfsHcBase + UFS_HC_STATUS_OFFSET;
+ Status = UfsWaitMemSet (Address, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Address = UfsHcBase + UFS_HC_UIC_CMD_OFFSET;
+ MmioWrite32 (Address, (UINT32)UicOpcode);
+
+ //
+ // UFS 2.0 spec section 5.3.1 Offset:0x20 IS.Bit10 UIC Command Completion Status (UCCS)
+ // This bit is set to '1' by the host controller upon completion of a UIC command.
+ //
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;
+ Data = MmioRead32 (Address);
+ Status = UfsWaitMemSet (Address, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (UicOpcode != UfsUicDmeReset) {
+ Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & 0xFF) != 0) {
+ DEBUG_CODE_BEGIN();
+ DumpUicCmdExecResult (UicOpcode, (UINT8)(Data & 0xFF));
+ DEBUG_CODE_END();
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Check value of HCS.DP and make sure that there is a device attached to the Link.
+ //
+ Address = UfsHcBase + UFS_HC_STATUS_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_HCS_DP) == 0) {
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;
+ Status = UfsWaitMemSet (Address, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((EFI_D_INFO, "UfsblockioPei: found a attached UFS device\n"));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Enable the UFS host controller for accessing.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS host controller enabling was executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while enabling the UFS host controller.
+
+**/
+EFI_STATUS
+UfsEnableHostController (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN Address;
+ UINT32 Data;
+
+ //
+ // UFS 2.0 spec section 7.1.1 - Host Controller Initialization
+ //
+ // Reinitialize the UFS host controller if HCE bit of HC register is set.
+ //
+ Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) {
+ //
+ // Write a 0 to the HCE register at first to disable the host controller.
+ //
+ MmioWrite32 (Address, 0);
+ //
+ // Wait until HCE is read as '0' before continuing.
+ //
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Write a 1 to the HCE register to enable the UFS host controller.
+ //
+ MmioWrite32 (Address, UFS_HC_HCE_EN);
+ //
+ // Wait until HCE is read as '1' before continuing.
+ //
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detect if a UFS device attached.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS device detection was executed successfully.
+ @retval EFI_NOT_FOUND Not found a UFS device attached.
+ @retval EFI_DEVICE_ERROR A device error occurred while detecting the UFS device.
+
+**/
+EFI_STATUS
+UfsDeviceDetection (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ UINTN Retry;
+ EFI_STATUS Status;
+
+ //
+ // Start UFS device detection.
+ // Try up to 3 times for establishing data link with device.
+ //
+ for (Retry = 0; Retry < 3; Retry++) {
+ Status = UfsExecUicCommands (Private, UfsUicDmeLinkStartup, 0, 0, 0);
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ continue;
+ }
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Retry == 3) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize UFS task management request list related h/w context.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS task management list was initialzed successfully.
+ @retval EFI_DEVICE_ERROR The initialization fails.
+
+**/
+EFI_STATUS
+UfsInitTaskManagementRequestList (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ UINTN Address;
+ UINT32 Data;
+ UINT8 Nutmrs;
+ EFI_PHYSICAL_ADDRESS Buffer;
+ EFI_STATUS Status;
+
+ //
+ // Initial h/w and s/w context for future operations.
+ //
+ Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET;
+ Data = MmioRead32 (Address);
+ Private->Capabilities = Data;
+
+ //
+ // Allocate and initialize UTP Task Management Request List.
+ //
+ Nutmrs = (UINT8) (RShiftU64 ((Private->Capabilities & UFS_HC_CAP_NUTMRS), 16) + 1);
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ EFI_SIZE_TO_PAGES (Nutmrs * sizeof (UTP_TMRD)),
+ &Buffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ZeroMem ((VOID*)(UINTN)Buffer, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Nutmrs * sizeof (UTP_TMRD))));
+
+ //
+ // Program the UTP Task Management Request List Base Address and UTP Task Management
+ // Request List Base Address with a 64-bit address allocated at step 6.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTMRLBA_OFFSET;
+ MmioWrite32 (Address, (UINT32)(UINTN)Buffer);
+ Address = Private->UfsHcBase + UFS_HC_UTMRLBAU_OFFSET;
+ MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)Buffer, 32));
+ Private->UtpTmrlBase = (VOID*)(UINTN)Buffer;
+ Private->Nutmrs = Nutmrs;
+
+ //
+ // Enable the UTP Task Management Request List by setting the UTP Task Management
+ // Request List RunStop Register (UTMRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET;
+ MmioWrite32 (Address, UFS_HC_UTMRLRSR);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize UFS transfer request list related h/w context.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS transfer list was initialzed successfully.
+ @retval EFI_DEVICE_ERROR The initialization fails.
+
+**/
+EFI_STATUS
+UfsInitTransferRequestList (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ UINTN Address;
+ UINT32 Data;
+ UINT8 Nutrs;
+ EFI_PHYSICAL_ADDRESS Buffer;
+ EFI_STATUS Status;
+
+ //
+ // Initial h/w and s/w context for future operations.
+ //
+ Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET;
+ Data = MmioRead32 (Address);
+ Private->Capabilities = Data;
+
+ //
+ // Allocate and initialize UTP Transfer Request List.
+ //
+ Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1);
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ EFI_SIZE_TO_PAGES (Nutrs * sizeof (UTP_TRD)),
+ &Buffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ZeroMem ((VOID*)(UINTN)Buffer, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Nutrs * sizeof (UTP_TRD))));
+
+ //
+ // Program the UTP Transfer Request List Base Address and UTP Transfer Request List
+ // Base Address with a 64-bit address allocated at step 8.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLBA_OFFSET;
+ MmioWrite32 (Address, (UINT32)(UINTN)Buffer);
+ Address = Private->UfsHcBase + UFS_HC_UTRLBAU_OFFSET;
+ MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)Buffer, 32));
+ Private->UtpTrlBase = (VOID*)(UINTN)Buffer;
+ Private->Nutrs = Nutrs;
+
+ //
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List
+ // RunStop Register (UTRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET;
+ MmioWrite32 (Address, UFS_HC_UTRLRSR);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+UfsControllerInit (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UfsEnableHostController (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsDevicePei: Enable Host Controller Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsDeviceDetection (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsDevicePei: Device Detection Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsInitTaskManagementRequestList (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsDevicePei: Task management list initialization Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsInitTransferRequestList (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsDevicePei: Transfer list initialization Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "UfsDevicePei Finished\n"));
+ return EFI_SUCCESS;
+}
+
+/**
+ Stop the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully.
+ @retval Others A device error occurred while stopping the controller.
+
+**/
+EFI_STATUS
+UfsControllerStop (
+ IN UFS_PEIM_HC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN Address;
+ UINT32 Data;
+
+ //
+ // Enable the UTP Task Management Request List by setting the UTP Task Management
+ // Request List RunStop Register (UTMRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET;
+ MmioWrite32 (Address, 0);
+
+ //
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List
+ // RunStop Register (UTRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET;
+ MmioWrite32 (Address, 0);
+
+ //
+ // Write a 0 to the HCE register in order to disable the host controller.
+ //
+ Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET;
+ Data = MmioRead32 (Address);
+ ASSERT ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN);
+ MmioWrite32 (Address, 0);
+
+ //
+ // Wait until HCE is read as '0' before continuing.
+ //
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((EFI_D_INFO, "UfsDevicePei: Stop the UFS Host Controller\n"));
+
+ return EFI_SUCCESS;
+}
+
diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h
new file mode 100644
index 0000000000..a423a921de
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h
@@ -0,0 +1,1339 @@
+/** @file
+ UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface
+ for upper layer application to execute UFS-supported SCSI cmds.
+
+ Copyright (c) 2014, Intel Corporation. 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 _UFS_PASS_THRU_HCI_H_
+#define _UFS_PASS_THRU_HCI_H_
+
+//
+// Host Capabilities Register Offsets
+//
+#define UFS_HC_CAP_OFFSET 0x0000 // Controller Capabilities
+#define UFS_HC_VER_OFFSET 0x0008 // Version
+#define UFS_HC_DDID_OFFSET 0x0010 // Device ID and Device Class
+#define UFS_HC_PMID_OFFSET 0x0014 // Product ID and Manufacturer ID
+#define UFS_HC_AHIT_OFFSET 0x0018 // Auto-Hibernate Idle Timer
+//
+// Operation and Runtime Register Offsets
+//
+#define UFS_HC_IS_OFFSET 0x0020 // Interrupt Status
+#define UFS_HC_IE_OFFSET 0x0024 // Interrupt Enable
+#define UFS_HC_STATUS_OFFSET 0x0030 // Host Controller Status
+#define UFS_HC_ENABLE_OFFSET 0x0034 // Host Controller Enable
+#define UFS_HC_UECPA_OFFSET 0x0038 // Host UIC Error Code PHY Adapter Layer
+#define UFS_HC_UECDL_OFFSET 0x003c // Host UIC Error Code Data Link Layer
+#define UFS_HC_UECN_OFFSET 0x0040 // Host UIC Error Code Network Layer
+#define UFS_HC_UECT_OFFSET 0x0044 // Host UIC Error Code Transport Layer
+#define UFS_HC_UECDME_OFFSET 0x0048 // Host UIC Error Code DME
+#define UFS_HC_UTRIACR_OFFSET 0x004c // UTP Transfer Request Interrupt Aggregation Control Register
+//
+// UTP Transfer Register Offsets
+//
+#define UFS_HC_UTRLBA_OFFSET 0x0050 // UTP Transfer Request List Base Address
+#define UFS_HC_UTRLBAU_OFFSET 0x0054 // UTP Transfer Request List Base Address Upper 32-Bits
+#define UFS_HC_UTRLDBR_OFFSET 0x0058 // UTP Transfer Request List Door Bell Register
+#define UFS_HC_UTRLCLR_OFFSET 0x005c // UTP Transfer Request List CLear Register
+#define UFS_HC_UTRLRSR_OFFSET 0x0060 // UTP Transfer Request Run-Stop Register
+//
+// UTP Task Management Register Offsets
+//
+#define UFS_HC_UTMRLBA_OFFSET 0x0070 // UTP Task Management Request List Base Address
+#define UFS_HC_UTMRLBAU_OFFSET 0x0074 // UTP Task Management Request List Base Address Upper 32-Bits
+#define UFS_HC_UTMRLDBR_OFFSET 0x0078 // UTP Task Management Request List Door Bell Register
+#define UFS_HC_UTMRLCLR_OFFSET 0x007c // UTP Task Management Request List CLear Register
+#define UFS_HC_UTMRLRSR_OFFSET 0x0080 // UTP Task Management Run-Stop Register
+//
+// UIC Command Register Offsets
+//
+#define UFS_HC_UIC_CMD_OFFSET 0x0090 // UIC Command Register
+#define UFS_HC_UCMD_ARG1_OFFSET 0x0094 // UIC Command Argument 1
+#define UFS_HC_UCMD_ARG2_OFFSET 0x0098 // UIC Command Argument 2
+#define UFS_HC_UCMD_ARG3_OFFSET 0x009c // UIC Command Argument 3
+//
+// UMA Register Offsets
+//
+#define UFS_HC_UMA_OFFSET 0x00b0 // Reserved for Unified Memory Extension
+
+#define UFS_HC_HCE_EN BIT0
+#define UFS_HC_HCS_DP BIT0
+#define UFS_HC_HCS_UCRDY BIT3
+#define UFS_HC_IS_ULSS BIT8
+#define UFS_HC_IS_UCCS BIT10
+#define UFS_HC_CAP_64ADDR BIT24
+#define UFS_HC_CAP_NUTMRS (BIT16 | BIT17 | BIT18)
+#define UFS_HC_CAP_NUTRS (BIT0 | BIT1 | BIT2 | BIT3 | BIT4)
+#define UFS_HC_UTMRLRSR BIT0
+#define UFS_HC_UTRLRSR BIT0
+
+//
+// A maximum of length of 256KB is supported by PRDT entry
+//
+#define UFS_MAX_DATA_LEN_PER_PRD 0x40000
+
+#define UFS_STORAGE_COMMAND_TYPE 0x01
+
+#define UFS_REGULAR_COMMAND 0x00
+#define UFS_INTERRUPT_COMMAND 0x01
+
+#define UFS_LUN_0 0x00
+#define UFS_LUN_1 0x01
+#define UFS_LUN_2 0x02
+#define UFS_LUN_3 0x03
+#define UFS_LUN_4 0x04
+#define UFS_LUN_5 0x05
+#define UFS_LUN_6 0x06
+#define UFS_LUN_7 0x07
+#define UFS_WLUN_REPORT_LUNS 0x81
+#define UFS_WLUN_UFS_DEV 0xD0
+#define UFS_WLUN_BOOT 0xB0
+#define UFS_WLUN_RPMB 0xC4
+
+#pragma pack(1)
+
+//
+// UFSHCI 2.0 Spec Section 5.2.1 Offset 00h: CAP - Controller Capabilities
+//
+typedef struct {
+ UINT8 Nutrs:4; // Number of UTP Transfer Request Slots
+ UINT8 Rsvd1:4;
+
+ UINT8 NoRtt; // Number of outstanding READY TO TRANSFER (RTT) requests supported
+
+ UINT8 Nutmrs:3; // Number of UTP Task Management Request Slots
+ UINT8 Rsvd2:4;
+ UINT8 AutoHs:1; // Auto-Hibernation Support
+
+ UINT8 As64:1; // 64-bit addressing supported
+ UINT8 Oodds:1; // Out of order data delivery supported
+ UINT8 UicDmetms:1; // UIC DME_TEST_MODE command supported
+ UINT8 Ume:1; // Reserved for Unified Memory Extension
+ UINT8 Rsvd4:4;
+} UFS_HC_CAP;
+
+//
+// UFSHCI 2.0 Spec Section 5.2.2 Offset 08h: VER - UFS Version
+//
+typedef struct {
+ UINT8 Vs:4; // Version Suffix
+ UINT8 Mnr:4; // Minor version number
+
+ UINT8 Mjr; // Major version number
+
+ UINT16 Rsvd1;
+} UFS_HC_VER;
+
+//
+// UFSHCI 2.0 Spec Section 5.2.3 Offset 10h: HCPID - Host Controller Product ID
+//
+#define UFS_HC_PID UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.2.4 Offset 14h: HCMID - Host Controller Manufacturer ID
+//
+#define UFS_HC_MID UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.2.5 Offset 18h: AHIT - Auto-Hibernate Idle Timer
+//
+typedef struct {
+ UINT32 Ahitv:10; // Auto-Hibernate Idle Timer Value
+ UINT32 Ts:3; // Timer scale
+ UINT32 Rsvd1:19;
+} UFS_HC_AHIT;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.1 Offset 20h: IS - Interrupt Status
+//
+typedef struct {
+ UINT16 Utrcs:1; // UTP Transfer Request Completion Status
+ UINT16 Udepri:1; // UIC DME_ENDPOINT_RESET Indication
+ UINT16 Ue:1; // UIC Error
+ UINT16 Utms:1; // UIC Test Mode Status
+
+ UINT16 Upms:1; // UIC Power Mode Status
+ UINT16 Uhxs:1; // UIC Hibernate Exit Status
+ UINT16 Uhes:1; // UIC Hibernate Enter Status
+ UINT16 Ulls:1; // UIC Link Lost Status
+
+ UINT16 Ulss:1; // UIC Link Startup Status
+ UINT16 Utmrcs:1; // UTP Task Management Request Completion Status
+ UINT16 Uccs:1; // UIC Command Completion Status
+ UINT16 Dfes:1; // Device Fatal Error Status
+
+ UINT16 Utpes:1; // UTP Error Status
+ UINT16 Rsvd1:3;
+
+ UINT16 Hcfes:1; // Host Controller Fatal Error Status
+ UINT16 Sbfes:1; // System Bus Fatal Error Status
+ UINT16 Rsvd2:14;
+} UFS_HC_IS;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.2 Offset 24h: IE - Interrupt Enable
+//
+typedef struct {
+ UINT16 Utrce:1; // UTP Transfer Request Completion Enable
+ UINT16 Udeprie:1; // UIC DME_ENDPOINT_RESET Enable
+ UINT16 Uee:1; // UIC Error Enable
+ UINT16 Utmse:1; // UIC Test Mode Status Enable
+
+ UINT16 Upmse:1; // UIC Power Mode Status Enable
+ UINT16 Uhxse:1; // UIC Hibernate Exit Status Enable
+ UINT16 Uhese:1; // UIC Hibernate Enter Status Enable
+ UINT16 Ullse:1; // UIC Link Lost Status Enable
+
+ UINT16 Ulsse:1; // UIC Link Startup Status Enable
+ UINT16 Utmrce:1; // UTP Task Management Request Completion Enable
+ UINT16 Ucce:1; // UIC Command Completion Enable
+ UINT16 Dfee:1; // Device Fatal Error Enable
+
+ UINT16 Utpee:1; // UTP Error Enable
+ UINT16 Rsvd1:3;
+
+ UINT16 Hcfee:1; // Host Controller Fatal Error Enable
+ UINT16 Sbfee:1; // System Bus Fatal Error Enable
+ UINT16 Rsvd2:14;
+} UFS_HC_IE;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.3 Offset 30h: HCS - Host Controller Status
+//
+typedef struct {
+ UINT8 Dp:1; // Device Present
+ UINT8 UtrlRdy:1; // UTP Transfer Request List Ready
+ UINT8 UtmrlRdy:1; // UTP Task Management Request List Ready
+ UINT8 UcRdy:1; // UIC COMMAND Ready
+ UINT8 Rsvd1:4;
+
+ UINT8 Upmcrs:3; // UIC Power Mode Change Request Status
+ UINT8 Rsvd2:1; // UIC Hibernate Exit Status Enable
+ UINT8 Utpec:4; // UTP Error Code
+
+ UINT8 TtagUtpE; // Task Tag of UTP error
+ UINT8 TlunUtpE; // Target LUN of UTP error
+} UFS_HC_STATUS;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.4 Offset 34h: HCE - Host Controller Enable
+//
+typedef struct {
+ UINT32 Hce:1; // Host Controller Enable
+ UINT32 Rsvd1:31;
+} UFS_HC_ENABLE;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.5 Offset 38h: UECPA - Host UIC Error Code PHY Adapter Layer
+//
+typedef struct {
+ UINT32 Ec:5; // UIC PHY Adapter Layer Error Code
+ UINT32 Rsvd1:26;
+ UINT32 Err:1; // UIC PHY Adapter Layer Error
+} UFS_HC_UECPA;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.6 Offset 3ch: UECDL - Host UIC Error Code Data Link Layer
+//
+typedef struct {
+ UINT32 Ec:15; // UIC Data Link Layer Error Code
+ UINT32 Rsvd1:16;
+ UINT32 Err:1; // UIC Data Link Layer Error
+} UFS_HC_UECDL;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.7 Offset 40h: UECN - Host UIC Error Code Network Layer
+//
+typedef struct {
+ UINT32 Ec:3; // UIC Network Layer Error Code
+ UINT32 Rsvd1:28;
+ UINT32 Err:1; // UIC Network Layer Error
+} UFS_HC_UECN;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.8 Offset 44h: UECT - Host UIC Error Code Transport Layer
+//
+typedef struct {
+ UINT32 Ec:7; // UIC Transport Layer Error Code
+ UINT32 Rsvd1:24;
+ UINT32 Err:1; // UIC Transport Layer Error
+} UFS_HC_UECT;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.9 Offset 48h: UECDME - Host UIC Error Code
+//
+typedef struct {
+ UINT32 Ec:1; // UIC DME Error Code
+ UINT32 Rsvd1:30;
+ UINT32 Err:1; // UIC DME Error
+} UFS_HC_UECDME;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.10 Offset 4Ch: UTRIACR - UTP Transfer Request Interrupt Aggregation Control Register
+//
+typedef struct {
+ UINT8 IaToVal; // Interrupt aggregation timeout value
+
+ UINT8 IacTh:5; // Interrupt aggregation counter threshold
+ UINT8 Rsvd1:3;
+
+ UINT8 Ctr:1; // Counter and Timer Reset
+ UINT8 Rsvd2:3;
+ UINT8 Iasb:1; // Interrupt aggregation status bit
+ UINT8 Rsvd3:3;
+
+ UINT8 IapwEn:1; // Interrupt aggregation parameter write enable
+ UINT8 Rsvd4:6;
+ UINT8 IaEn:1; // Interrupt Aggregation Enable/Disable
+} UFS_HC_UTRIACR;
+
+//
+// UFSHCI 2.0 Spec Section 5.4.1 Offset 50h: UTRLBA - UTP Transfer Request List Base Address
+//
+typedef struct {
+ UINT32 Rsvd1:10;
+ UINT32 UtrlBa:22; // UTP Transfer Request List Base Address
+} UFS_HC_UTRLBA;
+
+//
+// UFSHCI 2.0 Spec Section 5.4.2 Offset 54h: UTRLBAU - UTP Transfer Request List Base Address Upper 32-bits
+//
+#define UFS_HC_UTRLBAU UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.4.3 Offset 58h: UTRLDBR - UTP Transfer Request List Door Bell Register
+//
+#define UFS_HC_UTRLDBR UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.4.4 Offset 5Ch: UTRLCLR - UTP Transfer Request List CLear Register
+//
+#define UFS_HC_UTRLCLR UINT32
+
+#if 0
+//
+// UFSHCI 2.0 Spec Section 5.4.5 Offset 60h: UTRLRSR - UTP Transfer Request List Run Stop Register
+//
+typedef struct {
+ UINT32 UtrlRsr:1; // UTP Transfer Request List Run-Stop Register
+ UINT32 Rsvd1:31;
+} UFS_HC_UTRLRSR;
+#endif
+
+//
+// UFSHCI 2.0 Spec Section 5.5.1 Offset 70h: UTMRLBA - UTP Task Management Request List Base Address
+//
+typedef struct {
+ UINT32 Rsvd1:10;
+ UINT32 UtmrlBa:22; // UTP Task Management Request List Base Address
+} UFS_HC_UTMRLBA;
+
+//
+// UFSHCI 2.0 Spec Section 5.5.2 Offset 74h: UTMRLBAU - UTP Task Management Request List Base Address Upper 32-bits
+//
+#define UFS_HC_UTMRLBAU UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.5.3 Offset 78h: UTMRLDBR - UTP Task Management Request List Door Bell Register
+//
+typedef struct {
+ UINT32 UtmrlDbr:8; // UTP Task Management Request List Door bell Register
+ UINT32 Rsvd1:24;
+} UFS_HC_UTMRLDBR;
+
+//
+// UFSHCI 2.0 Spec Section 5.5.4 Offset 7Ch: UTMRLCLR - UTP Task Management Request List CLear Register
+//
+typedef struct {
+ UINT32 UtmrlClr:8; // UTP Task Management List Clear Register
+ UINT32 Rsvd1:24;
+} UFS_HC_UTMRLCLR;
+
+#if 0
+//
+// UFSHCI 2.0 Spec Section 5.5.5 Offset 80h: UTMRLRSR - UTP Task Management Request List Run Stop Register
+//
+typedef struct {
+ UINT32 UtmrlRsr:1; // UTP Task Management Request List Run-Stop Register
+ UINT32 Rsvd1:31;
+} UFS_HC_UTMRLRSR;
+#endif
+
+//
+// UFSHCI 2.0 Spec Section 5.6.1 Offset 90h: UICCMD - UIC Command
+//
+typedef struct {
+ UINT32 CmdOp:8; // Command Opcode
+ UINT32 Rsvd1:24;
+} UFS_HC_UICCMD;
+
+//
+// UFSHCI 2.0 Spec Section 5.6.2 Offset 94h: UICCMDARG1 - UIC Command Argument 1
+//
+#define UFS_HC_UICCMD_ARG1 UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.6.2 Offset 98h: UICCMDARG2 - UIC Command Argument 2
+//
+#define UFS_HC_UICCMD_ARG2 UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.6.2 Offset 9ch: UICCMDARG3 - UIC Command Argument 3
+//
+#define UFS_HC_UICCMD_ARG3 UINT32
+
+//
+// UIC command opcodes
+//
+typedef enum {
+ UfsUicDmeGet = 0x01,
+ UfsUicDmeSet = 0x02,
+ UfsUicDmePeerGet = 0x03,
+ UfsUicDmePeerSet = 0x04,
+ UfsUicDmePwrOn = 0x10,
+ UfsUicDmePwrOff = 0x11,
+ UfsUicDmeEnable = 0x12,
+ UfsUicDmeReset = 0x14,
+ UfsUicDmeEndpointReset = 0x15,
+ UfsUicDmeLinkStartup = 0x16,
+ UfsUicDmeHibernateEnter = 0x17,
+ UfsUicDmeHibernateExit = 0x18,
+ UfsUicDmeTestMode = 0x1A
+} UFS_UIC_OPCODE;
+
+//
+// UTP Transfer Request Descriptor
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT32 Rsvd1:24;
+ UINT32 Int:1; /* Interrupt */
+ UINT32 Dd:2; /* Data Direction */
+ UINT32 Rsvd2:1;
+ UINT32 Ct:4; /* Command Type */
+
+ //
+ // DW1
+ //
+ UINT32 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT32 Ocs:8; /* Overall Command Status */
+ UINT32 Rsvd4:24;
+
+ //
+ // DW3
+ //
+ UINT32 Rsvd5;
+
+ //
+ // DW4
+ //
+ UINT32 Rsvd6:7;
+ UINT32 UcdBa:25; /* UTP Command Descriptor Base Address */
+
+ //
+ // DW5
+ //
+ UINT32 UcdBaU; /* UTP Command Descriptor Base Address Upper 32-bits */
+
+ //
+ // DW6
+ //
+ UINT16 RuL; /* Response UPIU Length */
+ UINT16 RuO; /* Response UPIU Offset */
+
+ //
+ // DW7
+ //
+ UINT16 PrdtL; /* PRDT Length */
+ UINT16 PrdtO; /* PRDT Offset */
+} UTP_TRD;
+
+typedef struct {
+ //
+ // DW0
+ //
+ UINT32 Rsvd1:2;
+ UINT32 DbAddr:30; /* Data Base Address */
+
+ //
+ // DW1
+ //
+ UINT32 DbAddrU; /* Data Base Address Upper 32-bits */
+
+ //
+ // DW2
+ //
+ UINT32 Rsvd2;
+
+ //
+ // DW3
+ //
+ UINT32 DbCount:18; /* Data Byte Count */
+ UINT32 Rsvd3:14;
+} UTP_TR_PRD;
+
+//
+// UFS 2.0 Spec Section 10.5.3 - UTP Command UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x01*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 CmdSet:4; /* Command Set Type */
+ UINT8 Rsvd1:4;
+ UINT8 Rsvd2;
+ UINT8 Rsvd3;
+ UINT8 Rsvd4;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd5;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 ExpDataTranLen; /* Expected Data Transfer Length - Big Endian */
+
+ //
+ // DW4 - DW7
+ //
+ UINT8 Cdb[16];
+} UTP_COMMAND_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.4 - UTP Response UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x21*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 CmdSet:4; /* Command Set Type */
+ UINT8 Rsvd1:4;
+ UINT8 Rsvd2;
+ UINT8 Response; /* Response */
+ UINT8 Status; /* Status */
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3
+ //
+ UINT32 ResTranCount; /* Residual Transfer Count - Big Endian */
+
+ //
+ // DW4 - DW7
+ //
+ UINT8 Rsvd3[16];
+
+ //
+ // Data Segment - Sense Data
+ //
+ UINT16 SenseDataLen; /* Sense Data Length - Big Endian */
+ UINT8 SenseData[18]; /* Sense Data */
+} UTP_RESPONSE_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.5 - UTP Data-Out UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x02*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd2;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3
+ //
+ UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 DataTranCount; /* Data Transfer Count - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd3[12];
+
+ //
+ // Data Segment - Data to be sent out
+ //
+ //UINT8 Data[]; /* Data to be sent out, maximum is 65535 bytes */
+} UTP_DATA_OUT_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.6 - UTP Data-In UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x22*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd2;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3
+ //
+ UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 DataTranCount; /* Data Transfer Count - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd3[12];
+
+ //
+ // Data Segment - Data to be read
+ //
+ //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */
+} UTP_DATA_IN_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.7 - UTP Ready-To-Transfer UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x31*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd2;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 DataTranCount; /* Data Transfer Count - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd3[12];
+
+ //
+ // Data Segment - Data to be read
+ //
+ //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */
+} UTP_RDY_TO_TRAN_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.8 - UTP Task Management Request UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x04*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1;
+ UINT8 TskManFunc; /* Task Management Function */
+ UINT8 Rsvd2[2];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd3;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 InputParam1; /* Input Parameter 1 - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 InputParam2; /* Input Parameter 2 - Big Endian */
+
+ //
+ // DW5
+ //
+ UINT32 InputParam3; /* Input Parameter 3 - Big Endian */
+
+ //
+ // DW6 - DW7
+ //
+ UINT8 Rsvd4[8];
+} UTP_TM_REQ_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.9 - UTP Task Management Response UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x24*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[2];
+ UINT8 Resp; /* Response */
+ UINT8 Rsvd2;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd3;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 OutputParam1; /* Output Parameter 1 - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 OutputParam2; /* Output Parameter 2 - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd4[12];
+} UTP_TM_RESP_UPIU;
+
+//
+// UTP Task Management Request Descriptor
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT32 Rsvd1:24;
+ UINT32 Int:1; /* Interrupt */
+ UINT32 Rsvd2:7;
+
+ //
+ // DW1
+ //
+ UINT32 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT32 Ocs:8; /* Overall Command Status */
+ UINT32 Rsvd4:24;
+
+ //
+ // DW3
+ //
+ UINT32 Rsvd5;
+
+ //
+ // DW4 - DW11
+ //
+ UTP_TM_REQ_UPIU TmReq; /* Task Management Request UPIU */
+
+ //
+ // DW12 - DW19
+ //
+ UTP_TM_RESP_UPIU TmResp; /* Task Management Response UPIU */
+} UTP_TMRD;
+
+
+typedef struct {
+ UINT8 Opcode;
+ UINT8 DescId;
+ UINT8 Index;
+ UINT8 Selector;
+ UINT16 Rsvd1;
+ UINT16 Length;
+ UINT32 Value;
+ UINT32 Rsvd2;
+} UTP_UPIU_TSF;
+
+//
+// UFS 2.0 Spec Section 10.5.10 - UTP Query Request UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x16*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2;
+ UINT8 QueryFunc; /* Query Function */
+ UINT8 Rsvd3[2];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd4;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3 - 6
+ //
+ UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */
+
+ //
+ // DW7
+ //
+ UINT8 Rsvd5[4];
+
+ //
+ // Data Segment - Data to be transferred
+ //
+ //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */
+} UTP_QUERY_REQ_UPIU;
+
+#define QUERY_FUNC_STD_READ_REQ 0x01
+#define QUERY_FUNC_STD_WRITE_REQ 0x81
+
+typedef enum {
+ UtpQueryFuncOpcodeNop = 0x00,
+ UtpQueryFuncOpcodeRdDesc = 0x01,
+ UtpQueryFuncOpcodeWrDesc = 0x02,
+ UtpQueryFuncOpcodeRdAttr = 0x03,
+ UtpQueryFuncOpcodeWrAttr = 0x04,
+ UtpQueryFuncOpcodeRdFlag = 0x05,
+ UtpQueryFuncOpcodeSetFlag = 0x06,
+ UtpQueryFuncOpcodeClrFlag = 0x07,
+ UtpQueryFuncOpcodeTogFlag = 0x08
+} UTP_QUERY_FUNC_OPCODE;
+
+//
+// UFS 2.0 Spec Section 10.5.11 - UTP Query Response UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x36*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2;
+ UINT8 QueryFunc; /* Query Function */
+ UINT8 QueryResp; /* Query Response */
+ UINT8 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3 - 6
+ //
+ UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */
+
+ //
+ // DW7
+ //
+ UINT8 Rsvd4[4];
+
+ //
+ // Data Segment - Data to be transferred
+ //
+ //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */
+} UTP_QUERY_RESP_UPIU;
+
+typedef enum {
+ UfsUtpQueryResponseSuccess = 0x00,
+ UfsUtpQueryResponseParamNotReadable = 0xF6,
+ UfsUtpQueryResponseParamNotWriteable = 0xF7,
+ UfsUtpQueryResponseParamAlreadyWritten = 0xF8,
+ UfsUtpQueryResponseInvalidLen = 0xF9,
+ UfsUtpQueryResponseInvalidVal = 0xFA,
+ UfsUtpQueryResponseInvalidSelector = 0xFB,
+ UfsUtpQueryResponseInvalidIndex = 0xFC,
+ UfsUtpQueryResponseInvalidIdn = 0xFD,
+ UfsUtpQueryResponseInvalidOpc = 0xFE,
+ UfsUtpQueryResponseGeneralFailure = 0xFF
+} UTP_QUERY_RESP_CODE;
+
+//
+// UFS 2.0 Spec Section 10.5.12 - UTP Reject UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x3F*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[2];
+ UINT8 Response; /* Response - 0x01 */
+ UINT8 Rsvd2;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information - 0x00 */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT8 HdrSts; /* Basic Header Status */
+ UINT8 Rsvd3;
+ UINT8 E2ESts; /* End-to-End Status */
+ UINT8 Rsvd4;
+
+ //
+ // DW4 - DW7
+ //
+ UINT8 Rsvd5[16];
+} UTP_REJ_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.13 - UTP NOP OUT UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x00*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd3;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3 - DW7
+ //
+ UINT8 Rsvd4[20];
+} UTP_NOP_OUT_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.14 - UTP NOP IN UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x20*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2[2];
+ UINT8 Resp; /* Response - 0x00 */
+ UINT8 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information - 0x00 */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3 - DW7
+ //
+ UINT8 Rsvd4[20];
+} UTP_NOP_IN_UPIU;
+
+//
+// UFS Descriptors
+//
+typedef enum {
+ UfsDeviceDesc = 0x00,
+ UfsConfigDesc = 0x01,
+ UfsUnitDesc = 0x02,
+ UfsInterConnDesc = 0x04,
+ UfsStringDesc = 0x05,
+ UfsGeometryDesc = 0x07,
+ UfsPowerDesc = 0x08
+} UFS_DESC_IDN;
+
+//
+// UFS 2.0 Spec Section 14.1.6.2 - Device Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 Device;
+ UINT8 DevClass;
+ UINT8 DevSubClass;
+ UINT8 Protocol;
+ UINT8 NumLun;
+ UINT8 NumWLun;
+ UINT8 BootEn;
+ UINT8 DescAccessEn;
+ UINT8 InitPowerMode;
+ UINT8 HighPriorityLun;
+ UINT8 SecureRemovalType;
+ UINT8 SecurityLun;
+ UINT8 BgOpsTermLat;
+ UINT8 InitActiveIccLevel;
+ UINT16 SpecVersion;
+ UINT16 ManufactureDate;
+ UINT8 ManufacturerName;
+ UINT8 ProductName;
+ UINT8 SerialName;
+ UINT8 OemId;
+ UINT16 ManufacturerId;
+ UINT8 Ud0BaseOffset;
+ UINT8 Ud0ConfParamLen;
+ UINT8 DevRttCap;
+ UINT16 PeriodicRtcUpdate;
+ UINT8 Rsvd1[17];
+ UINT8 Rsvd2[16];
+} UFS_DEV_DESC;
+
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 Rsvd1;
+ UINT8 BootEn;
+ UINT8 DescAccessEn;
+ UINT8 InitPowerMode;
+ UINT8 HighPriorityLun;
+ UINT8 SecureRemovalType;
+ UINT8 InitActiveIccLevel;
+ UINT16 PeriodicRtcUpdate;
+ UINT8 Rsvd2[5];
+} UFS_CONFIG_DESC_GEN_HEADER;
+
+typedef struct {
+ UINT8 LunEn;
+ UINT8 BootLunId;
+ UINT8 LunWriteProt;
+ UINT8 MemType;
+ UINT32 NumAllocUnits;
+ UINT8 DataReliability;
+ UINT8 LogicBlkSize;
+ UINT8 ProvisionType;
+ UINT16 CtxCap;
+ UINT8 Rsvd1[3];
+} UFS_UNIT_DESC_CONFIG_PARAMS;
+
+//
+// UFS 2.0 Spec Section 14.1.6.3 - Configuration Descriptor
+//
+typedef struct {
+ UFS_CONFIG_DESC_GEN_HEADER Header;
+ UFS_UNIT_DESC_CONFIG_PARAMS UnitDescConfParams[8];
+} UFS_CONFIG_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.4 - Geometry Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 MediaTech;
+ UINT8 Rsvd1;
+ UINT64 TotalRawDevCapacity;
+ UINT8 Rsvd2;
+ UINT32 SegSize;
+ UINT8 AllocUnitSize;
+ UINT8 MinAddrBlkSize;
+ UINT8 OptReadBlkSize;
+ UINT8 OptWriteBlkSize;
+ UINT8 MaxInBufSize;
+ UINT8 MaxOutBufSize;
+ UINT8 RpmbRwSize;
+ UINT8 Rsvd3;
+ UINT8 DataOrder;
+ UINT8 MaxCtxIdNum;
+ UINT8 SysDataTagUnitSize;
+ UINT8 SysDataResUnitSize;
+ UINT8 SupSecRemovalTypes;
+ UINT16 SupMemTypes;
+ UINT32 SysCodeMaxNumAllocUnits;
+ UINT16 SupCodeCapAdjFac;
+ UINT32 NonPersMaxNumAllocUnits;
+ UINT16 NonPersCapAdjFac;
+ UINT32 Enhance1MaxNumAllocUnits;
+ UINT16 Enhance1CapAdjFac;
+ UINT32 Enhance2MaxNumAllocUnits;
+ UINT16 Enhance2CapAdjFac;
+ UINT32 Enhance3MaxNumAllocUnits;
+ UINT16 Enhance3CapAdjFac;
+ UINT32 Enhance4MaxNumAllocUnits;
+ UINT16 Enhance4CapAdjFac;
+} UFS_GEOMETRY_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.5 - Unit Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 UnitIdx;
+ UINT8 LunEn;
+ UINT8 BootLunId;
+ UINT8 LunWriteProt;
+ UINT8 LunQueueDep;
+ UINT8 Rsvd1;
+ UINT8 MemType;
+ UINT8 DataReliability;
+ UINT8 LogicBlkSize;
+ UINT64 LogicBlkCount;
+ UINT32 EraseBlkSize;
+ UINT8 ProvisionType;
+ UINT64 PhyMemResCount;
+ UINT16 CtxCap;
+ UINT8 LargeUnitGranularity;
+} UFS_UNIT_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.6 - RPMB Unit Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 UnitIdx;
+ UINT8 LunEn;
+ UINT8 BootLunId;
+ UINT8 LunWriteProt;
+ UINT8 LunQueueDep;
+ UINT8 Rsvd1;
+ UINT8 MemType;
+ UINT8 Rsvd2;
+ UINT8 LogicBlkSize;
+ UINT64 LogicBlkCount;
+ UINT32 EraseBlkSize;
+ UINT8 ProvisionType;
+ UINT64 PhyMemResCount;
+ UINT8 Rsvd3[3];
+} UFS_RPMB_UNIT_DESC;
+
+typedef struct {
+ UINT16 Value:10;
+ UINT16 Rsvd1:4;
+ UINT16 Unit:2;
+} UFS_POWER_PARAM_ELEMENT;
+
+//
+// UFS 2.0 Spec Section 14.1.6.7 - Power Parameter Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UFS_POWER_PARAM_ELEMENT ActiveIccLevelVcc[16];
+ UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ[16];
+ UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ2[16];
+} UFS_POWER_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.8 - InterConnect Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT16 UniProVer;
+ UINT16 MphyVer;
+} UFS_INTER_CONNECT_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.9 - 14.1.6.12 - String Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ CHAR16 Unicode[126];
+} UFS_STRING_DESC;
+
+//
+// UFS 2.0 Spec Section 14.2 - Flags
+//
+typedef enum {
+ UfsFlagDevInit = 0x01,
+ UfsFlagPermWpEn = 0x02,
+ UfsFlagPowerOnWpEn = 0x03,
+ UfsFlagBgOpsEn = 0x04,
+ UfsFlagPurgeEn = 0x06,
+ UfsFlagPhyResRemoval = 0x08,
+ UfsFlagBusyRtc = 0x09,
+ UfsFlagPermDisFwUpdate = 0x0B
+} UFS_FLAGS_IDN;
+
+//
+// UFS 2.0 Spec Section 14.2 - Attributes
+//
+typedef enum {
+ UfsAttrBootLunEn = 0x00,
+ UfsAttrCurPowerMode = 0x02,
+ UfsAttrActiveIccLevel = 0x03,
+ UfsAttrOutOfOrderDataEn = 0x04,
+ UfsAttrBgOpStatus = 0x05,
+ UfsAttrPurgeStatus = 0x06,
+ UfsAttrMaxDataInSize = 0x07,
+ UfsAttrMaxDataOutSize = 0x08,
+ UfsAttrDynCapNeeded = 0x09,
+ UfsAttrRefClkFreq = 0x0a,
+ UfsAttrConfigDescLock = 0x0b,
+ UfsAttrMaxNumOfRtt = 0x0c,
+ UfsAttrExceptionEvtCtrl = 0x0d,
+ UfsAttrExceptionEvtSts = 0x0e,
+ UfsAttrSecondsPassed = 0x0f,
+ UfsAttrContextConf = 0x10,
+ UfsAttrCorrPrgBlkNum = 0x11
+} UFS_ATTR_IDN;
+
+typedef enum {
+ UfsNoData = 0,
+ UfsDataOut = 1,
+ UfsDataIn = 2,
+ UfsDdReserved
+} UFS_DATA_DIRECTION;
+
+
+#pragma pack()
+
+#endif
+
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c
new file mode 100644
index 0000000000..7da21110e3
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c
@@ -0,0 +1,222 @@
+/** @file
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsPassThru.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUfsPassThruComponentName = {
+ UfsPassThruComponentNameGetDriverName,
+ UfsPassThruComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UfsPassThruComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UfsPassThruComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsPassThruDriverNameTable[] = {
+ {
+ "eng;en",
+ L"Universal Flash Storage (UFS) Pass Thru Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsPassThruControllerNameTable[] = {
+ {
+ "eng;en",
+ L"Universal Flash Storage (UFS) 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
+UfsPassThruComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUfsPassThruDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gUfsPassThruComponentName)
+ );
+}
+
+/**
+ 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 NULL.
+
+ @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
+UfsPassThruComponentNameGetControllerName (
+ 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 Controller Handle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gUfsPassThruDriverBinding.DriverBindingHandle,
+ &gEdkiiUfsHostControllerProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUfsPassThruControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gUfsPassThruComponentName)
+ );
+}
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c
new file mode 100644
index 0000000000..306fd37a29
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c
@@ -0,0 +1,1054 @@
+/** @file
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsPassThru.h"
+
+//
+// Template for Ufs Pass Thru private data.
+//
+UFS_PASS_THRU_PRIVATE_DATA gUfsPassThruTemplate = {
+ UFS_PASS_THRU_SIG, // Signature
+ NULL, // Handle
+ { // ExtScsiPassThruMode
+ 0xFFFFFFFF,
+ //
+ // Note that the driver doesn't support ExtScsiPassThru non blocking I/O.
+ //
+ EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL,
+ sizeof (UINTN)
+ },
+ { // ExtScsiPassThru
+ NULL,
+ UfsPassThruPassThru,
+ UfsPassThruGetNextTargetLun,
+ UfsPassThruBuildDevicePath,
+ UfsPassThruGetTargetLun,
+ UfsPassThruResetChannel,
+ UfsPassThruResetTargetLun,
+ UfsPassThruGetNextTarget
+ },
+ 0, // UfsHostController
+ 0, // UfsHcBase
+ 0, // Capabilities
+ 0, // TaskTag
+ 0, // UtpTrlBase
+ 0, // Nutrs
+ 0, // TrlMapping
+ 0, // UtpTmrlBase
+ 0, // Nutmrs
+ 0, // TmrlMapping
+ { // Luns
+ {
+ UFS_LUN_0, // Ufs Common Lun 0
+ UFS_LUN_1, // Ufs Common Lun 1
+ UFS_LUN_2, // Ufs Common Lun 2
+ UFS_LUN_3, // Ufs Common Lun 3
+ UFS_LUN_4, // Ufs Common Lun 4
+ UFS_LUN_5, // Ufs Common Lun 5
+ UFS_LUN_6, // Ufs Common Lun 6
+ UFS_LUN_7, // Ufs Common Lun 7
+ UFS_WLUN_REPORT_LUNS, // Ufs Reports Luns Well Known Lun
+ UFS_WLUN_UFS_DEV, // Ufs Device Well Known Lun
+ UFS_WLUN_BOOT, // Ufs Boot Well Known Lun
+ UFS_WLUN_RPMB // RPMB Well Known Lun
+ },
+ 0x0000, // By default don't expose any Luns.
+ 0x0
+ }
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gUfsPassThruDriverBinding = {
+ UfsPassThruDriverBindingSupported,
+ UfsPassThruDriverBindingStart,
+ UfsPassThruDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+UFS_DEVICE_PATH mUfsDevicePathTemplate = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_UFS_DP,
+ {
+ (UINT8) (sizeof (UFS_DEVICE_PATH)),
+ (UINT8) ((sizeof (UFS_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0,
+ 0
+};
+
+UINT8 mUfsTargetId[TARGET_MAX_BYTES];
+
+/**
+ Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function
+ supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the
+ nonblocking I/O functionality is optional.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target The Target is an array of size TARGET_MAX_BYTES and it represents
+ the id of the SCSI device to send the SCSI Request Packet. Each
+ transport driver may choose to utilize a subset of this size to suit the needs
+ of transport target representation. For example, a Fibre Channel driver
+ may use only 8 bytes (WWN) to represent an FC target.
+ @param Lun The LUN of the SCSI device to send the SCSI Request Packet.
+ @param Packet A pointer to the SCSI Request Packet to send to the SCSI device
+ specified by Target and Lun.
+ @param Event If nonblocking I/O is not supported then Event is ignored, and blocking
+ I/O is performed. If Event is NULL, then blocking I/O is performed. If
+ Event is not NULL and non blocking I/O is supported, then
+ nonblocking I/O is performed, and Event will be signaled when the
+ SCSI Request Packet completes.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that
+ could be transferred is returned in InTransferLength. For write
+ and bi-directional commands, OutTransferLength bytes were
+ transferred by OutDataBuffer.
+ @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many
+ SCSI Request Packets already queued. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid.
+ @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported
+ by the host adapter. This includes the case of Bi-directional SCSI
+ commands not supported by the implementation. The SCSI Request
+ Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruPassThru (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ UINT8 UfsLun;
+ UINT16 Index;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);
+
+ if ((Packet == NULL) || (Packet->Cdb == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Don't support variable length CDB
+ //
+ if ((Packet->CdbLength != 6) && (Packet->CdbLength != 10) &&
+ (Packet->CdbLength != 12) && (Packet->CdbLength != 16)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->InDataBuffer, This->Mode->IoAlign)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->OutDataBuffer, This->Mode->IoAlign)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->SenseData, This->Mode->IoAlign)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // For UFS 2.0 compatible device, 0 is always used to represent the location of the UFS device.
+ //
+ SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0x00);
+ if ((Target == NULL) || (CompareMem(Target, mUfsTargetId, TARGET_MAX_BYTES) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // UFS 2.0 spec Section 10.6.7 - Translation of 8-bit UFS LUN to 64-bit SCSI LUN Address
+ // 0xC1 in the first 8 bits of the 64-bit address indicates a well known LUN address in the SAM SCSI format.
+ // The second 8 bits of the 64-bit address saves the corresponding 8-bit UFS LUN.
+ //
+ if ((UINT8)Lun == UFS_WLUN_PREFIX) {
+ UfsLun = BIT7 | (((UINT8*)&Lun)[1] & 0xFF);
+ } else if ((UINT8)Lun == 0) {
+ UfsLun = ((UINT8*)&Lun)[1] & 0xFF;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < UFS_MAX_LUNS; Index++) {
+ if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) {
+ continue;
+ }
+
+ if (Private->Luns.Lun[Index] == UfsLun) {
+ break;
+ }
+ }
+
+ if (Index == UFS_MAX_LUNS) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = UfsExecScsiCmds (Private, UfsLun, Packet);
+
+ return Status;
+}
+
+/**
+ Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These
+ can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal
+ Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the
+ Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI
+ channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target On input, a pointer to the Target ID (an array of size
+ TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel.
+ On output, a pointer to the Target ID (an array of
+ TARGET_MAX_BYTES) of the next SCSI device present on a SCSI
+ channel. An input value of 0xF(all bytes in the array are 0xF) in the
+ Target array retrieves the Target ID of the first SCSI device present on a
+ SCSI channel.
+ @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI
+ channel. On output, a pointer to the LUN of the next SCSI device present
+ on a SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI
+ channel was returned in Target and Lun.
+ @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were
+ not returned on a previous call to GetNextTargetLun().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target,
+ IN OUT UINT64 *Lun
+ )
+{
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ UINT8 UfsLun;
+ UINT16 Index;
+ UINT16 Next;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Target == NULL || Lun == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*Target == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ UfsLun = 0;
+ SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0xFF);
+ if (CompareMem (*Target, mUfsTargetId, TARGET_MAX_BYTES) == 0) {
+ //
+ // If the array is all 0xFF's, return the first exposed Lun to caller.
+ //
+ SetMem (*Target, TARGET_MAX_BYTES, 0x00);
+ for (Index = 0; Index < UFS_MAX_LUNS; Index++) {
+ if ((Private->Luns.BitMask & (BIT0 << Index)) != 0) {
+ UfsLun = Private->Luns.Lun[Index];
+ break;
+ }
+ }
+ if (Index != UFS_MAX_LUNS) {
+ *Lun = 0;
+ if ((UfsLun & BIT7) == BIT7) {
+ ((UINT8*)Lun)[0] = UFS_WLUN_PREFIX;
+ ((UINT8*)Lun)[1] = UfsLun & ~BIT7;
+ } else {
+ ((UINT8*)Lun)[1] = UfsLun;
+ }
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0x00);
+ if (CompareMem (*Target, mUfsTargetId, TARGET_MAX_BYTES) == 0) {
+ if (((UINT8*)Lun)[0] == UFS_WLUN_PREFIX) {
+ UfsLun = BIT7 | (((UINT8*)Lun)[1] & 0xFF);
+ } else if (((UINT8*)Lun)[0] == 0) {
+ UfsLun = ((UINT8*)Lun)[1] & 0xFF;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+ for (Index = 0; Index < UFS_MAX_LUNS; Index++) {
+ if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) {
+ continue;
+ }
+
+ if (Private->Luns.Lun[Index] != UfsLun) {
+ continue;
+ }
+
+ for (Next = Index + 1; Next < UFS_MAX_LUNS; Next++) {
+ if ((Private->Luns.BitMask & (BIT0 << Next)) != 0) {
+ UfsLun = Private->Luns.Lun[Next];
+ break;
+ }
+ }
+
+ if (Next == UFS_MAX_LUNS) {
+ return EFI_NOT_FOUND;
+ } else {
+ break;
+ }
+ }
+
+ if (Index != UFS_MAX_LUNS) {
+ *Lun = 0;
+ if ((UfsLun & BIT7) == BIT7) {
+ ((UINT8*)Lun)[0] = UFS_WLUN_PREFIX;
+ ((UINT8*)Lun)[1] = UfsLun & ~BIT7;
+ } else {
+ ((UINT8*)Lun)[1] = UfsLun;
+ }
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Used to allocate and build a device path node for a SCSI device on a SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the
+ Target ID of the SCSI device for which a device path node is to be
+ allocated and built. Transport drivers may chose to utilize a subset of
+ this size to suit the representation of targets. For example, a Fibre
+ Channel driver may use only 8 bytes (WWN) in the array to represent a
+ FC target.
+ @param Lun The LUN of the SCSI device for which a device path node is to be
+ allocated and built.
+ @param DevicePath A pointer to a single device path node that describes the SCSI device
+ specified by Target and Lun. This function is responsible for
+ allocating the buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's responsibility to free
+ DevicePath when the caller is finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SCSI device specified by
+ Target and Lun was allocated and returned in
+ DevicePath.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist
+ on the SCSI channel.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ EFI_DEV_PATH *DevicePathNode;
+ UINT8 UfsLun;
+ UINT16 Index;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);
+
+ //
+ // Validate parameters passed in.
+ //
+ SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0x00);
+ if (CompareMem (Target, mUfsTargetId, TARGET_MAX_BYTES) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UINT8)Lun == UFS_WLUN_PREFIX) {
+ UfsLun = BIT7 | (((UINT8*)&Lun)[1] & 0xFF);
+ } else if ((UINT8)Lun == 0) {
+ UfsLun = ((UINT8*)&Lun)[1] & 0xFF;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+ for (Index = 0; Index < UFS_MAX_LUNS; Index++) {
+ if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) {
+ continue;
+ }
+
+ if (Private->Luns.Lun[Index] == UfsLun) {
+ break;
+ }
+ }
+
+ if (Index == UFS_MAX_LUNS) {
+ return EFI_NOT_FOUND;
+ }
+
+ DevicePathNode = AllocateCopyPool (sizeof (UFS_DEVICE_PATH), &mUfsDevicePathTemplate);
+ if (DevicePathNode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DevicePathNode->Ufs.Pun = 0;
+ DevicePathNode->Ufs.Lun = UfsLun;
+
+ *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DevicePathNode;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Used to translate a device path node to a Target ID and LUN.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param DevicePath A pointer to a single device path node that describes the SCSI device
+ on the SCSI channel.
+ @param Target A pointer to the Target Array which represents the ID of a SCSI device
+ on the SCSI channel.
+ @param Lun A pointer to the LUN of a SCSI device on the SCSI channel.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and
+ LUN, and they were returned in Target and Lun.
+ @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL.
+ @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN
+ does not exist.
+ @retval EFI_UNSUPPORTED This driver does not support the device path node type in
+ DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **Target,
+ OUT UINT64 *Lun
+ )
+{
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ EFI_DEV_PATH *DevicePathNode;
+ UINT8 Pun;
+ UINT8 UfsLun;
+ UINT16 Index;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);
+
+ //
+ // Validate parameters passed in.
+ //
+ if (DevicePath == NULL || Target == NULL || Lun == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*Target == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether the DevicePath belongs to SCSI_DEVICE_PATH
+ //
+ if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_UFS_DP) ||
+ (DevicePathNodeLength(DevicePath) != sizeof(SCSI_DEVICE_PATH))) {
+ return EFI_UNSUPPORTED;
+ }
+
+ DevicePathNode = (EFI_DEV_PATH *) DevicePath;
+
+ Pun = (UINT8) DevicePathNode->Ufs.Pun;
+ UfsLun = (UINT8) DevicePathNode->Ufs.Lun;
+
+ if (Pun != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ for (Index = 0; Index < UFS_MAX_LUNS; Index++) {
+ if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) {
+ continue;
+ }
+
+ if (Private->Luns.Lun[Index] == UfsLun) {
+ break;
+ }
+ }
+
+ if (Index == UFS_MAX_LUNS) {
+ return EFI_NOT_FOUND;
+ }
+
+ SetMem (*Target, TARGET_MAX_BYTES, 0x00);
+ *Lun = 0;
+ if ((UfsLun & BIT7) == BIT7) {
+ ((UINT8*)Lun)[0] = UFS_WLUN_PREFIX;
+ ((UINT8*)Lun)[1] = UfsLun & ~BIT7;
+ } else {
+ ((UINT8*)Lun)[1] = UfsLun;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The SCSI channel was reset.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel.
+ @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel.
+ @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ )
+{
+ //
+ // Return success directly then upper layer driver could think reset channel operation is done.
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Resets a SCSI logical unit that is connected to a SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the
+ target port ID of the SCSI device containing the SCSI logical unit to
+ reset. Transport drivers may chose to utilize a subset of this array to suit
+ the representation of their targets.
+ @param Lun The LUN of the SCSI device to reset.
+
+ @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device
+ specified by Target and Lun.
+ @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device
+ specified by Target and Lun.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ )
+{
+ //
+ // Return success directly then upper layer driver could think reset target LUN operation is done.
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either
+ be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs
+ for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to
+ see if a SCSI device is actually present at that location on the SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel.
+ On output, a pointer to the Target ID (an array of
+ TARGET_MAX_BYTES) of the next SCSI device present on a SCSI
+ channel. An input value of 0xF(all bytes in the array are 0xF) in the
+ Target array retrieves the Target ID of the first SCSI device present on a
+ SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI
+ channel was returned in Target.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not
+ returned on a previous call to GetNextTarget().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target
+ )
+{
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);
+
+ if (Target == NULL || *Target == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0xFF);
+ if (CompareMem(*Target, mUfsTargetId, TARGET_MAX_BYTES) == 0) {
+ SetMem (*Target, TARGET_MAX_BYTES, 0x00);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ 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 determine 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
+UfsPassThruDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHostController;
+
+ //
+ // Ufs Pass Thru driver is a device driver, and should ingore the
+ // "RemainingDevicePath" according to UEFI spec
+ //
+ 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
+ );
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ (VOID **) &UfsHostController,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // EFI_ALREADY_STARTED is also an error
+ //
+ return Status;
+ }
+
+ //
+ // Close the I/O Abstraction(s) used to perform the supported test
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ 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
+UfsPassThruDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ UINTN UfsHcBase;
+ UINT32 Index;
+ UFS_CONFIG_DESC Config;
+
+ Status = EFI_SUCCESS;
+ UfsHc = NULL;
+ Private = NULL;
+ UfsHcBase = 0;
+
+ DEBUG ((EFI_D_INFO, "==UfsPassThru Start== Controller = %x\n", Controller));
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ (VOID **) &UfsHc,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Open Ufs Host Controller Protocol Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // Get the UFS Host Controller MMIO Bar Base Address.
+ //
+ Status = UfsHc->GetUfsHcMmioBar (UfsHc, &UfsHcBase);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Get Ufs Host Controller Mmio Bar Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // Initialize Ufs Pass Thru private data for managed UFS Host Controller.
+ //
+ Private = AllocateCopyPool (sizeof (UFS_PASS_THRU_PRIVATE_DATA), &gUfsPassThruTemplate);
+ if (Private == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ Private->ExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;
+ Private->UfsHostController = UfsHc;
+ Private->UfsHcBase = UfsHcBase;
+
+ //
+ // Initialize UFS Host Controller H/W.
+ //
+ Status = UfsControllerInit (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Host Controller Initialization Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // UFS 2.0 spec Section 13.1.3.3:
+ // At the end of the UFS Interconnect Layer initialization on both host and device side,
+ // the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready.
+ //
+ Status = UfsExecNopCmds (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Sending NOP IN command Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // The host enables the device initialization completion by setting fDeviceInit flag.
+ //
+ Status = UfsSetFlag (Private, UfsFlagDevInit);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Set fDeviceInit Flag Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // Get Ufs Device's Lun Info by reading Configuration Descriptor.
+ //
+ Status = UfsRwDeviceDesc (Private, TRUE, UfsConfigDesc, 0, 0, &Config, sizeof (UFS_CONFIG_DESC));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Ufs Get Configuration Descriptor Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // Check if 8 common luns are active and set corresponding bit mask.
+ // TODO: Parse device descriptor to decide if exposing RPMB LUN to upper layer for authentication access.
+ //
+ for (Index = 0; Index < 8; Index++) {
+ if (Config.UnitDescConfParams[Index].LunEn != 0) {
+ Private->Luns.BitMask |= (BIT0 << Index);
+ DEBUG ((EFI_D_INFO, "Ufs Lun %d is enabled\n", Index));
+ }
+ }
+
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &(Private->ExtScsiPassThru)
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+
+Error:
+ if (Private != NULL) {
+ if (Private->TmrlMapping != NULL) {
+ UfsHc->Unmap (UfsHc, Private->TmrlMapping);
+ }
+ if (Private->UtpTmrlBase != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutmrs * sizeof (UTP_TMRD)), Private->UtpTmrlBase);
+ }
+
+ if (Private->TrlMapping != NULL) {
+ UfsHc->Unmap (UfsHc, Private->TrlMapping);
+ }
+ if (Private->UtpTrlBase != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase);
+ }
+
+ FreePool (Private);
+ }
+
+ if (UfsHc != NULL) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ 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
+UfsPassThruDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ DEBUG ((EFI_D_INFO, "==UfsPassThru Stop== Controller Controller = %x\n", Controller));
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **) &ExtScsiPassThru,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (ExtScsiPassThru);
+ UfsHc = Private->UfsHostController;
+
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &(Private->ExtScsiPassThru)
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Stop Ufs Host Controller
+ //
+ Status = UfsControllerStop (Private);
+ ASSERT_EFI_ERROR (Status);
+
+ if (Private->TmrlMapping != NULL) {
+ UfsHc->Unmap (UfsHc, Private->TmrlMapping);
+ }
+ if (Private->UtpTmrlBase != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutmrs * sizeof (UTP_TMRD)), Private->UtpTmrlBase);
+ }
+
+ if (Private->TrlMapping != NULL) {
+ UfsHc->Unmap (UfsHc, Private->TrlMapping);
+ }
+ if (Private->UtpTrlBase != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase);
+ }
+
+ FreePool (Private);
+
+ //
+ // Close protocols opened by UfsPassThru controller driver
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEdkiiUfsHostControllerProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+ The user Entry Point for module UfsPassThru. The user code starts with this function.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeUfsPassThru (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install driver model protocol(s).
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gUfsPassThruDriverBinding,
+ ImageHandle,
+ &gUfsPassThruComponentName,
+ &gUfsPassThruComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
new file mode 100644
index 0000000000..c435fe19aa
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
@@ -0,0 +1,727 @@
+/** @file
+
+ Copyright (c) 2014, Intel Corporation. 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 _UFS_PASS_THRU_H_
+#define _UFS_PASS_THRU_H_
+
+#include <Uefi.h>
+
+#include <Protocol/ScsiPassThruExt.h>
+#include <Protocol/UfsHostController.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+
+#include "UfsPassThruHci.h"
+
+#define UFS_PASS_THRU_SIG SIGNATURE_32 ('U', 'F', 'S', 'P')
+
+//
+// Lun 0~7 is for 8 common luns.
+// Lun 8~11 is for those 4 well known luns (Refer to UFS 2.0 spec Table 10.58 for details):
+// Lun 8: REPORT LUNS
+// Lun 9: UFS DEVICE
+// Lun 10: BOOT
+// Lun 11: RPMB
+//
+#define UFS_MAX_LUNS 12
+#define UFS_WLUN_PREFIX 0xC1
+
+typedef struct {
+ UINT8 Lun[UFS_MAX_LUNS];
+ UINT16 BitMask:12; // Bit 0~7 is 1/1 mapping to common luns. Bit 8~11 is 1/1 mapping to well-known luns.
+ UINT16 Rsvd:4;
+} UFS_EXPOSED_LUNS;
+
+typedef struct _UFS_PASS_THRU_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Handle;
+ EFI_EXT_SCSI_PASS_THRU_MODE ExtScsiPassThruMode;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL ExtScsiPassThru;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHostController;
+ UINTN UfsHcBase;
+ UINT32 Capabilities;
+
+ UINT8 TaskTag;
+
+ VOID *UtpTrlBase;
+ UINT8 Nutrs;
+ VOID *TrlMapping;
+ VOID *UtpTmrlBase;
+ UINT8 Nutmrs;
+ VOID *TmrlMapping;
+
+ UFS_EXPOSED_LUNS Luns;
+} UFS_PASS_THRU_PRIVATE_DATA;
+
+#define UFS_TIMEOUT EFI_TIMER_PERIOD_SECONDS(3)
+
+#define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8)
+
+#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
+
+#define UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS(a) \
+ CR (a, \
+ UFS_PASS_THRU_PRIVATE_DATA, \
+ ExtScsiPassThru, \
+ UFS_PASS_THRU_SIG \
+ )
+
+typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET {
+ UINT64 Timeout;
+ VOID *InDataBuffer;
+ VOID *OutDataBuffer;
+ UINT8 Opcode;
+ UINT8 DescId;
+ UINT8 Index;
+ UINT8 Selector;
+ UINT32 InTransferLength;
+ UINT32 OutTransferLength;
+ UINT8 DataDirection;
+ UINT8 Ocs;
+} UFS_DEVICE_MANAGEMENT_REQUEST_PACKET;
+
+//
+// function prototype
+//
+/**
+ 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 determine 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
+UfsPassThruDriverBindingSupported (
+ 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 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
+UfsPassThruDriverBindingStart (
+ 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
+UfsPassThruDriverBindingStop (
+ 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
+UfsPassThruComponentNameGetDriverName (
+ 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 NULL.
+
+ @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
+UfsPassThruComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+/**
+ Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function
+ supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the
+ nonblocking I/O functionality is optional.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target The Target is an array of size TARGET_MAX_BYTES and it represents
+ the id of the SCSI device to send the SCSI Request Packet. Each
+ transport driver may choose to utilize a subset of this size to suit the needs
+ of transport target representation. For example, a Fibre Channel driver
+ may use only 8 bytes (WWN) to represent an FC target.
+ @param Lun The LUN of the SCSI device to send the SCSI Request Packet.
+ @param Packet A pointer to the SCSI Request Packet to send to the SCSI device
+ specified by Target and Lun.
+ @param Event If nonblocking I/O is not supported then Event is ignored, and blocking
+ I/O is performed. If Event is NULL, then blocking I/O is performed. If
+ Event is not NULL and non blocking I/O is supported, then
+ nonblocking I/O is performed, and Event will be signaled when the
+ SCSI Request Packet completes.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that
+ could be transferred is returned in InTransferLength. For write
+ and bi-directional commands, OutTransferLength bytes were
+ transferred by OutDataBuffer.
+ @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many
+ SCSI Request Packets already queued. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid.
+ @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported
+ by the host adapter. This includes the case of Bi-directional SCSI
+ commands not supported by the implementation. The SCSI Request
+ Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruPassThru (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+/**
+ Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These
+ can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal
+ Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the
+ Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI
+ channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target On input, a pointer to the Target ID (an array of size
+ TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel.
+ On output, a pointer to the Target ID (an array of
+ TARGET_MAX_BYTES) of the next SCSI device present on a SCSI
+ channel. An input value of 0xF(all bytes in the array are 0xF) in the
+ Target array retrieves the Target ID of the first SCSI device present on a
+ SCSI channel.
+ @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI
+ channel. On output, a pointer to the LUN of the next SCSI device present
+ on a SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI
+ channel was returned in Target and Lun.
+ @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were
+ not returned on a previous call to GetNextTargetLun().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target,
+ IN OUT UINT64 *Lun
+ );
+
+/**
+ Used to allocate and build a device path node for a SCSI device on a SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the
+ Target ID of the SCSI device for which a device path node is to be
+ allocated and built. Transport drivers may chose to utilize a subset of
+ this size to suit the representation of targets. For example, a Fibre
+ Channel driver may use only 8 bytes (WWN) in the array to represent a
+ FC target.
+ @param Lun The LUN of the SCSI device for which a device path node is to be
+ allocated and built.
+ @param DevicePath A pointer to a single device path node that describes the SCSI device
+ specified by Target and Lun. This function is responsible for
+ allocating the buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's responsibility to free
+ DevicePath when the caller is finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SCSI device specified by
+ Target and Lun was allocated and returned in
+ DevicePath.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist
+ on the SCSI channel.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Used to translate a device path node to a Target ID and LUN.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param DevicePath A pointer to a single device path node that describes the SCSI device
+ on the SCSI channel.
+ @param Target A pointer to the Target Array which represents the ID of a SCSI device
+ on the SCSI channel.
+ @param Lun A pointer to the LUN of a SCSI device on the SCSI channel.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and
+ LUN, and they were returned in Target and Lun.
+ @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL.
+ @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN
+ does not exist.
+ @retval EFI_UNSUPPORTED This driver does not support the device path node type in
+ DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **Target,
+ OUT UINT64 *Lun
+ );
+
+/**
+ Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The SCSI channel was reset.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel.
+ @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel.
+ @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ );
+
+/**
+ Resets a SCSI logical unit that is connected to a SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the
+ target port ID of the SCSI device containing the SCSI logical unit to
+ reset. Transport drivers may chose to utilize a subset of this array to suit
+ the representation of their targets.
+ @param Lun The LUN of the SCSI device to reset.
+
+ @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device
+ specified by Target and Lun.
+ @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device
+ specified by Target and Lun.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ );
+
+/**
+ Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either
+ be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs
+ for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to
+ see if a SCSI device is actually present at that location on the SCSI channel.
+
+ @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel.
+ On output, a pointer to the Target ID (an array of
+ TARGET_MAX_BYTES) of the next SCSI device present on a SCSI
+ channel. An input value of 0xF(all bytes in the array are 0xF) in the
+ Target array retrieves the Target ID of the first SCSI device present on a
+ SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI
+ channel was returned in Target.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not
+ returned on a previous call to GetNextTarget().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsPassThruGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target
+ );
+
+/**
+ Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the
+ UFS device.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsExecScsiCmds (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ );
+
+/**
+ Initialize the UFS host controller.
+
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NVM Express Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+UfsControllerInit (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ );
+
+/**
+ Stop the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully.
+ @retval Others A device error occurred while stopping the controller.
+
+**/
+EFI_STATUS
+UfsControllerStop (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ );
+
+/**
+ Allocate common buffer for host and UFS bus master access simultaneously.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Size The length of buffer to be allocated.
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.
+ @param[out] CmdDescPhyAddr The resulting map address for the UFS bus master to use to access the hosts CmdDescHost.
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The common buffer was allocated successfully.
+ @retval EFI_DEVICE_ERROR The allocation fails.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+
+**/
+EFI_STATUS
+UfsAllocateAlignCommonBuffer (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINTN Size,
+ OUT VOID **CmdDescHost,
+ OUT EFI_PHYSICAL_ADDRESS *CmdDescPhyAddr,
+ OUT VOID **CmdDescMapping
+ );
+
+/**
+ Set specified flag to 1 on a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be set.
+
+ @retval EFI_SUCCESS The flag was set successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag.
+
+**/
+EFI_STATUS
+UfsSetFlag (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 FlagId
+ );
+
+/**
+ Read or write specified device descriptor of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] DescId The ID of device descriptor.
+ @param[in] Index The Index of device descriptor.
+ @param[in] Selector The Selector of device descriptor.
+ @param[in, out] Descriptor The buffer of device descriptor to be read or written.
+ @param[in] DescSize The size of device descriptor buffer.
+
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor.
+
+**/
+EFI_STATUS
+UfsRwDeviceDesc (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT VOID *Descriptor,
+ IN UINT32 DescSize
+ );
+
+/**
+ Sends NOP IN cmd to a UFS device for initialization process request.
+ For more details, please refer to UFS 2.0 spec Figure 13.3.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was
+ received successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute.
+
+**/
+EFI_STATUS
+UfsExecNopCmds (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ );
+
+extern GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUfsPassThruComponentName;
+extern GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2;
+extern EFI_DRIVER_BINDING_PROTOCOL gUfsPassThruDriverBinding;
+
+#endif
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni
new file mode 100644
index 0000000000..1e627bd9cd
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf
new file mode 100644
index 0000000000..21b8e6c0c2
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf
@@ -0,0 +1,63 @@
+## @file
+# Description file for the Universal Flash Storage (UFS) Pass Thru driver.
+#
+# Copyright (c) 2014 - 2015, Intel Corporation. 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 = 0x00010005
+ BASE_NAME = UfsPassThruDxe
+ MODULE_UNI_FILE = UfsPassThru.uni
+ FILE_GUID = E7F1DFF9-DAB6-498A-9ADF-57F344EDDF57
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = InitializeUfsPassThru
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gUfsPassThruDriverBinding
+# COMPONENT_NAME = gUfsPassThruComponentName
+#
+
+[Sources]
+ ComponentName.c
+ UfsPassThru.c
+ UfsPassThru.h
+ UfsPassThruHci.c
+ UfsPassThruHci.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ UefiLib
+ BaseLib
+ UefiDriverEntryPoint
+ DebugLib
+ DevicePathLib
+ IoLib
+ TimerLib
+
+[Protocols]
+ gEfiExtScsiPassThruProtocolGuid ## BY_START
+ gEdkiiUfsHostControllerProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ UfsPassThruExtra.uni
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni
new file mode 100644
index 0000000000..771b7ba3b5
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
new file mode 100644
index 0000000000..4cc32f0727
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
@@ -0,0 +1,2000 @@
+/** @file
+ UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface
+ for upper layer application to execute UFS-supported SCSI cmds.
+
+ Copyright (c) 2014, Intel Corporation. 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 "UfsPassThru.h"
+
+/**
+ Wait for the value of the specified system memory set to the test value.
+
+ @param Address The system memory address to test.
+ @param MaskValue The mask value of memory.
+ @param TestValue The test value of memory.
+ @param Timeout The time out value for wait memory set, uses 100ns as a unit.
+
+ @retval EFI_TIMEOUT The system memory setting is time out.
+ @retval EFI_SUCCESS The system memory is correct set.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsWaitMemSet (
+ IN UINTN Address,
+ IN UINT32 MaskValue,
+ IN UINT32 TestValue,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Value;
+ UINT64 Delay;
+ BOOLEAN InfiniteWait;
+
+ if (Timeout == 0) {
+ InfiniteWait = TRUE;
+ } else {
+ InfiniteWait = FALSE;
+ }
+
+ Delay = DivU64x32 (Timeout, 10) + 1;
+
+ do {
+ //
+ // Access PCI MMIO space to see if the value is the tested one.
+ //
+ Value = MmioRead32 (Address) & MaskValue;
+
+ if (Value == TestValue) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stall for 1 microseconds.
+ //
+ MicroSecondDelay (1);
+
+ Delay--;
+
+ } while (InfiniteWait || (Delay > 0));
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Dump UIC command execution result for debugging.
+
+ @param[in] UicOpcode The executed UIC opcode.
+ @param[in] Result The result to be parsed.
+
+**/
+VOID
+DumpUicCmdExecResult (
+ IN UINT8 UicOpcode,
+ IN UINT8 Result
+ )
+{
+ if (UicOpcode <= UfsUicDmePeerSet) {
+ switch (Result) {
+ case 0x00:
+ break;
+ case 0x01:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x02:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n"));
+ break;
+ case 0x03:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x04:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x05:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_INDEX\n"));
+ break;
+ case 0x06:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x07:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n"));
+ break;
+ case 0x08:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n"));
+ break;
+ case 0x09:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BUSY\n"));
+ break;
+ case 0x0A:
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - DME_FAILURE\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+ } else {
+ switch (Result) {
+ case 0x00:
+ break;
+ case 0x01:
+ DEBUG ((EFI_D_VERBOSE, "UIC control command fails - FAILURE\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+ }
+}
+
+/**
+ Dump QUERY RESPONSE UPIU result for debugging.
+
+ @param[in] Result The result to be parsed.
+
+**/
+VOID
+DumpQueryResponseResult (
+ IN UINT8 Result
+ )
+{
+ switch (Result) {
+ case 0xF6:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Readable\n"));
+ break;
+ case 0xF7:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Writeable\n"));
+ break;
+ case 0xF8:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Already Written\n"));
+ break;
+ case 0xF9:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Length\n"));
+ break;
+ case 0xFA:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Value\n"));
+ break;
+ case 0xFB:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Selector\n"));
+ break;
+ case 0xFC:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Index\n"));
+ break;
+ case 0xFD:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Idn\n"));
+ break;
+ case 0xFE:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Opcode\n"));
+ break;
+ case 0xFF:
+ DEBUG ((EFI_D_VERBOSE, "Query Response with General Failure\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+}
+
+/**
+ Swap little endian to big endian.
+
+ @param[in, out] Buffer The data buffer. In input, it contains little endian data.
+ In output, it will become big endian.
+ @param[in] BufferSize The length of converted data.
+
+**/
+VOID
+SwapLittleEndianToBigEndian (
+ IN OUT UINT8 *Buffer,
+ IN UINT32 BufferSize
+ )
+{
+ UINT32 Index;
+ UINT8 Temp;
+ UINT32 SwapCount;
+
+ SwapCount = BufferSize / 2;
+ for (Index = 0; Index < SwapCount; Index++) {
+ Temp = Buffer[Index];
+ Buffer[Index] = Buffer[BufferSize - 1 - Index];
+ Buffer[BufferSize - 1 - Index] = Temp;
+ }
+}
+
+/**
+ Fill TSF field of QUERY REQUEST UPIU.
+
+ @param[in, out] TsfBase The base address of TSF field of QUERY REQUEST UPIU.
+ @param[in] Opcode The opcode of request.
+ @param[in] DescId The descriptor ID of request.
+ @param[in] Index The index of request.
+ @param[in] Selector The selector of request.
+ @param[in] Length The length of transferred data. The maximum is 4.
+ @param[in] Value The value of transferred data.
+
+**/
+VOID
+UfsFillTsfOfQueryReqUpiu (
+ IN OUT UTP_UPIU_TSF *TsfBase,
+ IN UINT8 Opcode,
+ IN UINT8 DescId OPTIONAL,
+ IN UINT8 Index OPTIONAL,
+ IN UINT8 Selector OPTIONAL,
+ IN UINT16 Length OPTIONAL,
+ IN UINT32 Value OPTIONAL
+ )
+{
+ ASSERT (TsfBase != NULL);
+ ASSERT (Opcode <= UtpQueryFuncOpcodeTogFlag);
+
+ TsfBase->Opcode = Opcode;
+ if (Opcode != UtpQueryFuncOpcodeNop) {
+ TsfBase->DescId = DescId;
+ TsfBase->Index = Index;
+ TsfBase->Selector = Selector;
+
+ if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) {
+ SwapLittleEndianToBigEndian ((UINT8*)&Length, sizeof (Length));
+ TsfBase->Length = Length;
+ }
+
+ if (Opcode == UtpQueryFuncOpcodeWrAttr) {
+ SwapLittleEndianToBigEndian ((UINT8*)&Value, sizeof (Value));
+ TsfBase->Value = Value;
+ }
+ }
+}
+
+/**
+ Initialize COMMAND UPIU.
+
+ @param[in, out] Command The base address of COMMAND UPIU.
+ @param[in] Lun The Lun on which the SCSI command is executed.
+ @param[in] TaskTag The task tag of request.
+ @param[in] Cdb The cdb buffer containing SCSI command.
+ @param[in] CdbLength The cdb length.
+ @param[in] DataDirection The direction of data transfer.
+ @param[in] ExpDataTranLen The expected transfer data length.
+
+ @retval EFI_SUCCESS The initialization succeed.
+
+**/
+EFI_STATUS
+UfsInitCommandUpiu (
+ IN OUT UTP_COMMAND_UPIU *Command,
+ IN UINT8 Lun,
+ IN UINT8 TaskTag,
+ IN UINT8 *Cdb,
+ IN UINT8 CdbLength,
+ IN UFS_DATA_DIRECTION DataDirection,
+ IN UINT32 ExpDataTranLen
+ )
+{
+ UINT8 Flags;
+
+ ASSERT ((Command != NULL) && (Cdb != NULL));
+
+ //
+ // Task attribute is hard-coded to Ordered.
+ //
+ if (DataDirection == UfsDataIn) {
+ Flags = BIT0 | BIT6;
+ } else if (DataDirection == UfsDataOut) {
+ Flags = BIT0 | BIT5;
+ } else {
+ Flags = BIT0;
+ }
+
+ //
+ // Fill UTP COMMAND UPIU associated fields.
+ //
+ Command->TransCode = 0x01;
+ Command->Flags = Flags;
+ Command->Lun = Lun;
+ Command->TaskTag = TaskTag;
+ Command->CmdSet = 0x00;
+ SwapLittleEndianToBigEndian ((UINT8*)&ExpDataTranLen, sizeof (ExpDataTranLen));
+ Command->ExpDataTranLen = ExpDataTranLen;
+
+ CopyMem (Command->Cdb, Cdb, CdbLength);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize UTP PRDT for data transfer.
+
+ @param[in] Prdt The base address of PRDT.
+ @param[in] Buffer The buffer to be read or written.
+ @param[in] BufferSize The data size to be read or written.
+
+ @retval EFI_SUCCESS The initialization succeed.
+
+**/
+EFI_STATUS
+UfsInitUtpPrdt (
+ IN UTP_TR_PRD *Prdt,
+ IN VOID *Buffer,
+ IN UINT32 BufferSize
+ )
+{
+ UINT32 PrdtIndex;
+ UINT32 RemainingLen;
+ UINT8 *Remaining;
+ UINTN PrdtNumber;
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
+
+ RemainingLen = BufferSize;
+ Remaining = Buffer;
+ PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
+
+ for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {
+ if (RemainingLen < UFS_MAX_DATA_LEN_PER_PRD) {
+ Prdt[PrdtIndex].DbCount = (UINT32)RemainingLen - 1;
+ } else {
+ Prdt[PrdtIndex].DbCount = UFS_MAX_DATA_LEN_PER_PRD - 1;
+ }
+
+ Prdt[PrdtIndex].DbAddr = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 2);
+ Prdt[PrdtIndex].DbAddrU = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 32);
+ RemainingLen -= UFS_MAX_DATA_LEN_PER_PRD;
+ Remaining += UFS_MAX_DATA_LEN_PER_PRD;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize QUERY REQUEST UPIU.
+
+ @param[in, out] QueryReq The base address of QUERY REQUEST UPIU.
+ @param[in] TaskTag The task tag of request.
+ @param[in] Opcode The opcode of request.
+ @param[in] DescId The descriptor ID of request.
+ @param[in] Index The index of request.
+ @param[in] Selector The selector of request.
+ @param[in] DataSize The data size to be read or written.
+ @param[in] Data The buffer to be read or written.
+
+ @retval EFI_SUCCESS The initialization succeed.
+
+**/
+EFI_STATUS
+UfsInitQueryRequestUpiu (
+ IN OUT UTP_QUERY_REQ_UPIU *QueryReq,
+ IN UINT8 TaskTag,
+ IN UINT8 Opcode,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN UINTN DataSize OPTIONAL,
+ IN UINT8 *Data OPTIONAL
+ )
+{
+ ASSERT (QueryReq != NULL);
+
+ QueryReq->TransCode = 0x16;
+ QueryReq->TaskTag = TaskTag;
+ if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeRdFlag) || (Opcode == UtpQueryFuncOpcodeRdAttr)) {
+ QueryReq->QueryFunc = QUERY_FUNC_STD_READ_REQ;
+ } else {
+ QueryReq->QueryFunc = QUERY_FUNC_STD_WRITE_REQ;
+ }
+
+ if (Opcode == UtpQueryFuncOpcodeWrAttr) {
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, *(UINT32*)Data);
+ } else if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) {
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, (UINT16)DataSize, 0);
+ } else {
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, 0);
+ }
+
+ if (Opcode == UtpQueryFuncOpcodeWrDesc) {
+ CopyMem (QueryReq + 1, Data, DataSize);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate COMMAND/RESPONSE UPIU for filling UTP TRD's command descriptor field.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Lun The Lun on which the SCSI command is executed.
+ @param[in] Packet The pointer to the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET data structure.
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The creation succeed.
+ @retval EFI_DEVICE_ERROR The creation failed.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+
+**/
+EFI_STATUS
+UfsCreateScsiCommandDesc (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Lun,
+ IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN UTP_TRD *Trd,
+ OUT VOID **CmdDescHost,
+ OUT VOID **CmdDescMapping
+ )
+{
+ UINTN TotalLen;
+ UINTN PrdtNumber;
+ UTP_COMMAND_UPIU *CommandUpiu;
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;
+ EFI_STATUS Status;
+ UINT32 DataLen;
+ UFS_DATA_DIRECTION DataDirection;
+
+ ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL));
+
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+ DataLen = Packet->InTransferLength;
+ DataDirection = UfsDataIn;
+ } else {
+ DataLen = Packet->OutTransferLength;
+ DataDirection = UfsDataOut;
+ }
+
+ if (DataLen == 0) {
+ DataDirection = UfsNoData;
+ }
+
+ PrdtNumber = (UINTN)DivU64x32 ((UINT64)DataLen + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
+
+ TotalLen = ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)) + PrdtNumber * sizeof (UTP_TR_PRD);
+
+ Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CommandUpiu = (UTP_COMMAND_UPIU*)*CmdDescHost;
+
+ UfsInitCommandUpiu (CommandUpiu, Lun, Private->TaskTag++, Packet->Cdb, Packet->CdbLength, DataDirection, DataLen);
+
+ //
+ // Fill UTP_TRD associated fields
+ // NOTE: Some UFS host controllers request the Response UPIU and the Physical Region Description Table
+ // *MUST* be located at a 64-bit aligned boundary.
+ //
+ Trd->Int = UFS_INTERRUPT_COMMAND;
+ Trd->Dd = DataDirection;
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7);
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32);
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)), sizeof (UINT32));
+ Trd->PrdtL = (UINT16)PrdtNumber;
+ Trd->PrdtO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))), sizeof (UINT32));
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate QUERY REQUEST/QUERY RESPONSE UPIU for filling UTP TRD's command descriptor field.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Packet The pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET data structure.
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The creation succeed.
+ @retval EFI_DEVICE_ERROR The creation failed.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+ @retval EFI_INVALID_PARAMETER The parameter passed in is invalid.
+
+**/
+EFI_STATUS
+UfsCreateDMCommandDesc (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet,
+ IN UTP_TRD *Trd,
+ OUT VOID **CmdDescHost,
+ OUT VOID **CmdDescMapping
+ )
+{
+ UINTN TotalLen;
+ UTP_QUERY_REQ_UPIU *QueryReqUpiu;
+ UINT8 Opcode;
+ UINT32 DataSize;
+ UINT8 *Data;
+ UINT8 DataDirection;
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;
+ EFI_STATUS Status;
+
+ ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL));
+
+ Opcode = Packet->Opcode;
+ if ((Opcode > UtpQueryFuncOpcodeTogFlag) || (Opcode == UtpQueryFuncOpcodeNop)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DataDirection = Packet->DataDirection;
+ if (DataDirection == UfsDataIn) {
+ DataSize = Packet->InTransferLength;
+ Data = Packet->InDataBuffer;
+ } else if (DataDirection == UfsDataOut) {
+ DataSize = Packet->OutTransferLength;
+ Data = Packet->OutDataBuffer;
+ } else {
+ DataSize = 0;
+ Data = NULL;
+ }
+
+ if (((Opcode != UtpQueryFuncOpcodeSetFlag) && (Opcode != UtpQueryFuncOpcodeClrFlag) && (Opcode != UtpQueryFuncOpcodeTogFlag))
+ && ((DataSize == 0) || (Data == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((Opcode == UtpQueryFuncOpcodeSetFlag) || (Opcode == UtpQueryFuncOpcodeClrFlag) || (Opcode == UtpQueryFuncOpcodeTogFlag))
+ && ((DataSize != 0) || (Data != NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Opcode == UtpQueryFuncOpcodeWrAttr) && (DataSize != sizeof (UINT32))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) {
+ TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize);
+ } else {
+ TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU));
+ }
+
+ Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Initialize UTP QUERY REQUEST UPIU
+ //
+ QueryReqUpiu = (UTP_QUERY_REQ_UPIU*)*CmdDescHost;
+ ASSERT (QueryReqUpiu != NULL);
+ UfsInitQueryRequestUpiu (
+ QueryReqUpiu,
+ Private->TaskTag++,
+ Opcode,
+ Packet->DescId,
+ Packet->Index,
+ Packet->Selector,
+ DataSize,
+ Data
+ );
+
+ //
+ // Fill UTP_TRD associated fields
+ // NOTE: Some UFS host controllers request the Query Response UPIU *MUST* be located at a 64-bit aligned boundary.
+ //
+ Trd->Int = UFS_INTERRUPT_COMMAND;
+ Trd->Dd = DataDirection;
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;
+ Trd->Ocs = 0x0F;
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7);
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32);
+ if (Opcode == UtpQueryFuncOpcodeWrDesc) {
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32));
+ } else {
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)), sizeof (UINT32));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate NOP IN and NOP OUT UPIU for filling UTP TRD's command descriptor field.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The creation succeed.
+ @retval EFI_DEVICE_ERROR The creation failed.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+
+**/
+EFI_STATUS
+UfsCreateNopCommandDesc (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UTP_TRD *Trd,
+ OUT VOID **CmdDescHost,
+ OUT VOID **CmdDescMapping
+ )
+{
+ UINTN TotalLen;
+ UTP_NOP_OUT_UPIU *NopOutUpiu;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;
+
+ ASSERT ((Private != NULL) && (Trd != NULL));
+
+ TotalLen = ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)) + ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU));
+ Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NopOutUpiu = (UTP_NOP_OUT_UPIU*)*CmdDescHost;
+ ASSERT (NopOutUpiu != NULL);
+ NopOutUpiu->TaskTag = Private->TaskTag++;
+
+ //
+ // Fill UTP_TRD associated fields
+ // NOTE: Some UFS host controllers request the Nop Out UPIU *MUST* be located at a 64-bit aligned boundary.
+ //
+ Trd->Int = UFS_INTERRUPT_COMMAND;
+ Trd->Dd = 0x00;
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7);
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32);
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)), sizeof (UINT32));
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)), sizeof (UINT32));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find out available slot in transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[out] Slot The available slot.
+
+ @retval EFI_SUCCESS The available slot was found successfully.
+
+**/
+EFI_STATUS
+UfsFindAvailableSlotInTrl (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ OUT UINT8 *Slot
+ )
+{
+ ASSERT ((Private != NULL) && (Slot != NULL));
+
+ //
+ // The simplest algo to always use slot 0.
+ // TODO: enhance it to support async transfer with multiple slot.
+ //
+ *Slot = 0;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find out available slot in task management transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[out] Slot The available slot.
+
+ @retval EFI_SUCCESS The available slot was found successfully.
+
+**/
+EFI_STATUS
+UfsFindAvailableSlotInTmrl (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ OUT UINT8 *Slot
+ )
+{
+ ASSERT ((Private != NULL) && (Slot != NULL));
+
+ //
+ // The simplest algo to always use slot 0.
+ // TODO: enhance it to support async transfer with multiple slot.
+ //
+ *Slot = 0;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Start specified slot in transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Slot The slot to be started.
+
+**/
+VOID
+UfsStartExecCmd (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Slot
+ )
+{
+ UINTN UfsHcBase;
+ UINTN Address;
+ UINT32 Data;
+
+ UfsHcBase = Private->UfsHcBase;
+
+ Address = UfsHcBase + UFS_HC_UTRLRSR_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) {
+ MmioWrite32 (Address, UFS_HC_UTRLRSR);
+ }
+
+ Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ MmioWrite32 (Address, BIT0 << Slot);
+}
+
+/**
+ Stop specified slot in transfer list of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Slot The slot to be stop.
+
+**/
+VOID
+UfsStopExecCmd (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Slot
+ )
+{
+ UINTN UfsHcBase;
+ UINTN Address;
+ UINT32 Data;
+
+ UfsHcBase = Private->UfsHcBase;
+
+ Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & (BIT0 << Slot)) != 0) {
+ Address = UfsHcBase + UFS_HC_UTRLCLR_OFFSET;
+ Data = MmioRead32 (Address);
+ MmioWrite32 (Address, (Data & ~(BIT0 << Slot)));
+ }
+}
+
+/**
+ Read or write specified device descriptor of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] DescId The ID of device descriptor.
+ @param[in] Index The Index of device descriptor.
+ @param[in] Selector The Selector of device descriptor.
+ @param[in, out] Descriptor The buffer of device descriptor to be read or written.
+ @param[in] DescSize The size of device descriptor buffer.
+
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor.
+
+**/
+EFI_STATUS
+UfsRwDeviceDesc (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT VOID *Descriptor,
+ IN UINT32 DescSize
+ )
+{
+ EFI_STATUS Status;
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ UINT32 CmdDescSize;
+ UINT16 ReturnDataSize;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ Packet.DataDirection = UfsDataIn;
+ Packet.InDataBuffer = Descriptor;
+ Packet.InTransferLength = DescSize;
+ Packet.Opcode = UtpQueryFuncOpcodeRdDesc;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ Packet.OutDataBuffer = Descriptor;
+ Packet.OutTransferLength = DescSize;
+ Packet.Opcode = UtpQueryFuncOpcodeWrDesc;
+ }
+ Packet.DescId = DescId;
+ Packet.Index = Index;
+ Packet.Selector = Selector;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ UfsHc = Private->UfsHostController;
+ QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));
+ ASSERT (QueryResp != NULL);
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (QueryResp->QueryResp != 0) {
+ DumpQueryResponseResult (QueryResp->QueryResp);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ ReturnDataSize = QueryResp->Tsf.Length;
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16));
+
+ if (Read) {
+ CopyMem (Packet.InDataBuffer, (QueryResp + 1), ReturnDataSize);
+ Packet.InTransferLength = ReturnDataSize;
+ } else {
+ Packet.OutTransferLength = ReturnDataSize;
+ }
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsHc->Flush (UfsHc);
+
+ UfsStopExecCmd (Private, Slot);
+
+ if (CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, CmdDescMapping);
+ }
+ if (CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);
+ }
+
+ return Status;
+}
+
+/**
+ Read or write specified attribute of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] AttrId The ID of Attribute.
+ @param[in] Index The Index of Attribute.
+ @param[in] Selector The Selector of Attribute.
+ @param[in, out] Attributes The value of Attribute to be read or written.
+
+ @retval EFI_SUCCESS The Attribute was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the Attribute.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the Attribute.
+
+**/
+EFI_STATUS
+UfsRwAttributes (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 AttrId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT UINT32 *Attributes
+ )
+{
+ EFI_STATUS Status;
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ UINT32 CmdDescSize;
+ UINT32 ReturnData;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ Packet.DataDirection = UfsDataIn;
+ Packet.Opcode = UtpQueryFuncOpcodeRdAttr;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ Packet.Opcode = UtpQueryFuncOpcodeWrAttr;
+ }
+ Packet.DescId = AttrId;
+ Packet.Index = Index;
+ Packet.Selector = Selector;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ UfsHc = Private->UfsHostController;
+ QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));
+ ASSERT (QueryResp != NULL);
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (QueryResp->QueryResp != 0) {
+ DumpQueryResponseResult (QueryResp->QueryResp);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ ReturnData = QueryResp->Tsf.Value;
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnData, sizeof (UINT32));
+ *Attributes = ReturnData;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsHc->Flush (UfsHc);
+
+ UfsStopExecCmd (Private, Slot);
+
+ if (CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, CmdDescMapping);
+ }
+
+ if (CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);
+ }
+
+ return Status;
+}
+
+/**
+ Read or write specified flag of a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Read The boolean variable to show r/w direction.
+ @param[in] FlagId The ID of flag to be read or written.
+ @param[in, out] Value The value to set or clear flag.
+
+ @retval EFI_SUCCESS The flag was read/written successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the flag.
+
+**/
+EFI_STATUS
+UfsRwFlags (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN BOOLEAN Read,
+ IN UINT8 FlagId,
+ IN OUT UINT8 *Value
+ )
+{
+ EFI_STATUS Status;
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ UINT32 CmdDescSize;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ if (Value == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ ASSERT (Value != NULL);
+ Packet.DataDirection = UfsDataIn;
+ Packet.Opcode = UtpQueryFuncOpcodeRdFlag;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ if (*Value == 1) {
+ Packet.Opcode = UtpQueryFuncOpcodeSetFlag;
+ } else if (*Value == 0) {
+ Packet.Opcode = UtpQueryFuncOpcodeClrFlag;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ Packet.DescId = FlagId;
+ Packet.Index = 0;
+ Packet.Selector = 0;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ UfsHc = Private->UfsHostController;
+ QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));
+ ASSERT (QueryResp != NULL);
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (QueryResp->QueryResp != 0) {
+ DumpQueryResponseResult (QueryResp->QueryResp);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ *Value = (UINT8)QueryResp->Tsf.Value;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsHc->Flush (UfsHc);
+
+ UfsStopExecCmd (Private, Slot);
+
+ if (CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, CmdDescMapping);
+ }
+ if (CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);
+ }
+
+ return Status;
+}
+
+/**
+ Set specified flag to 1 on a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be set.
+
+ @retval EFI_SUCCESS The flag was set successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag.
+
+**/
+EFI_STATUS
+UfsSetFlag (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 FlagId
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Value = 1;
+ Status = UfsRwFlags (Private, FALSE, FlagId, &Value);
+
+ return Status;
+}
+
+/**
+ Clear specified flag to 0 on a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be cleared.
+
+ @retval EFI_SUCCESS The flag was cleared successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to clear the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of clearing the flag.
+
+**/
+EFI_STATUS
+UfsClearFlag (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 FlagId
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Value = 0;
+ Status = UfsRwFlags (Private, FALSE, FlagId, &Value);
+
+ return Status;
+}
+
+/**
+ Read specified flag from a UFS device.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] FlagId The ID of flag to be read.
+ @param[out] Value The flag's value.
+
+ @retval EFI_SUCCESS The flag was read successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to read the flag.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of reading the flag.
+
+**/
+EFI_STATUS
+UfsReadFlag (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 FlagId,
+ OUT UINT8 *Value
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UfsRwFlags (Private, TRUE, FlagId, Value);
+
+ return Status;
+}
+
+/**
+ Sends NOP IN cmd to a UFS device for initialization process request.
+ For more details, please refer to UFS 2.0 spec Figure 13.3.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was
+ received successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute.
+
+**/
+EFI_STATUS
+UfsExecNopCmds (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UTP_NOP_IN_UPIU *NopInUpiu;
+ UINT32 CmdDescSize;
+ UINTN Address;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+ Status = UfsCreateNopCommandDesc (Private, Trd, &CmdDescHost, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ UfsHc = Private->UfsHostController;
+ NopInUpiu = (UTP_NOP_IN_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));
+ ASSERT (NopInUpiu != NULL);
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (NopInUpiu->Resp != 0) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+
+Exit:
+ UfsHc->Flush (UfsHc);
+
+ UfsStopExecCmd (Private, Slot);
+
+ if (CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, CmdDescMapping);
+ }
+ if (CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);
+ }
+
+ return Status;
+}
+
+/**
+ Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the
+ UFS device.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
+ commands, InTransferLength bytes were transferred from
+ InDataBuffer. For write and bi-directional commands,
+ OutTransferLength bytes were transferred by
+ OutDataBuffer.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
+ Packet.
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
+
+**/
+EFI_STATUS
+UfsExecScsiCmds (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINTN Address;
+ UINT32 CmdDescSize;
+ UTP_RESPONSE_UPIU *Response;
+ UINT16 SenseDataLen;
+ UINT32 ResTranCount;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ VOID *DataBufMapping;
+ VOID *DataBuf;
+ EFI_PHYSICAL_ADDRESS DataBufPhyAddr;
+ UINT32 DataLen;
+ UINTN MapLength;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+ EDKII_UFS_HOST_CONTROLLER_OPERATION Flag;
+ UFS_DATA_DIRECTION DataDirection;
+ UTP_TR_PRD *PrdtBase;
+
+ Trd = NULL;
+ CmdDescHost = NULL;
+ CmdDescMapping = NULL;
+ DataBufMapping = NULL;
+ DataBufPhyAddr = 0;
+ UfsHc = Private->UfsHostController;
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;
+
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd, &CmdDescHost, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD);
+
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+ DataBuf = Packet->InDataBuffer;
+ DataLen = Packet->InTransferLength;
+ DataDirection = UfsDataIn;
+ Flag = EdkiiUfsHcOperationBusMasterWrite;
+ } else {
+ DataBuf = Packet->OutDataBuffer;
+ DataLen = Packet->OutTransferLength;
+ DataDirection = UfsDataOut;
+ Flag = EdkiiUfsHcOperationBusMasterRead;
+ }
+
+ if (DataLen == 0) {
+ DataDirection = UfsNoData;
+ } else {
+ MapLength = DataLen;
+ Status = UfsHc->Map (
+ UfsHc,
+ Flag,
+ DataBuf,
+ &MapLength,
+ &DataBufPhyAddr,
+ &DataBufMapping
+ );
+
+ if (EFI_ERROR (Status) || (DataLen != MapLength)) {
+ goto Exit1;
+ }
+ }
+ //
+ // Fill PRDT table of Command UPIU for executed SCSI cmd.
+ //
+ PrdtBase = (UTP_TR_PRD*)((UINT8*)CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));
+ ASSERT (PrdtBase != NULL);
+ UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, Slot);
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET;
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet->Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get sense data if exists
+ //
+ Response = (UTP_RESPONSE_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));
+ ASSERT (Response != NULL);
+ SenseDataLen = Response->SenseDataLen;
+ SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));
+
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) {
+ CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen);
+ Packet->SenseDataLength = (UINT8)SenseDataLen;
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ Packet->TargetStatus = Response->Status;
+ if (Response->Response != 0) {
+ DEBUG ((EFI_D_ERROR, "UfsExecScsiCmds() fails with Target Failure\n"));
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (Trd->Ocs == 0) {
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+ if ((Response->Flags & BIT5) == BIT5) {
+ ResTranCount = Response->ResTranCount;
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));
+ Packet->InTransferLength -= ResTranCount;
+ }
+ } else {
+ if ((Response->Flags & BIT5) == BIT5) {
+ ResTranCount = Response->ResTranCount;
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));
+ Packet->OutTransferLength -= ResTranCount;
+ }
+ }
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+Exit:
+ UfsHc->Flush (UfsHc);
+
+ UfsStopExecCmd (Private, Slot);
+
+ if (DataBufMapping != NULL) {
+ UfsHc->Unmap (UfsHc, DataBufMapping);
+ }
+
+Exit1:
+ if (CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, CmdDescMapping);
+ }
+ if (CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);
+ }
+ return Status;
+}
+
+
+/**
+ Sent UIC DME_LINKSTARTUP command to start the link startup procedure.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] UicOpcode The opcode of the UIC command.
+ @param[in] Arg1 The value for 1st argument of the UIC command.
+ @param[in] Arg2 The value for 2nd argument of the UIC command.
+ @param[in] Arg3 The value for 3rd argument of the UIC command.
+
+ @return EFI_SUCCESS Successfully execute this UIC command and detect attached UFS device.
+ @return EFI_DEVICE_ERROR Fail to execute this UIC command and detect attached UFS device.
+ @return EFI_NOT_FOUND The presence of the UFS device isn't detected.
+
+**/
+EFI_STATUS
+UfsExecUicCommands (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 UicOpcode,
+ IN UINT32 Arg1,
+ IN UINT32 Arg2,
+ IN UINT32 Arg3
+ )
+{
+ EFI_STATUS Status;
+ UINTN Address;
+ UINT32 Data;
+ UINTN UfsHcBase;
+
+ UfsHcBase = Private->UfsHcBase;
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) {
+ //
+ // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first.
+ //
+ MmioWrite32 (Address, Data);
+ }
+
+ //
+ // When programming UIC command registers, host software shall set the register UICCMD
+ // only after all the UIC command argument registers (UICCMDARG1, UICCMDARG2 and UICCMDARG3)
+ // are set.
+ //
+ Address = UfsHcBase + UFS_HC_UCMD_ARG1_OFFSET;
+ MmioWrite32 (Address, Arg1);
+
+ Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET;
+ MmioWrite32 (Address, Arg2);
+
+ Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET;
+ MmioWrite32 (Address, Arg3);
+
+ //
+ // Host software shall only set the UICCMD if HCS.UCRDY is set to 1.
+ //
+ Address = Private->UfsHcBase + UFS_HC_STATUS_OFFSET;
+ Status = UfsWaitMemSet (Address, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Address = UfsHcBase + UFS_HC_UIC_CMD_OFFSET;
+ MmioWrite32 (Address, (UINT32)UicOpcode);
+
+ //
+ // UFS 2.0 spec section 5.3.1 Offset:0x20 IS.Bit10 UIC Command Completion Status (UCCS)
+ // This bit is set to '1' by the host controller upon completion of a UIC command.
+ //
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;
+ Data = MmioRead32 (Address);
+ Status = UfsWaitMemSet (Address, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (UicOpcode != UfsUicDmeReset) {
+ Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & 0xFF) != 0) {
+ DEBUG_CODE_BEGIN();
+ DumpUicCmdExecResult (UicOpcode, (UINT8)(Data & 0xFF));
+ DEBUG_CODE_END();
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Check value of HCS.DP and make sure that there is a device attached to the Link.
+ //
+ Address = UfsHcBase + UFS_HC_STATUS_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_HCS_DP) == 0) {
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;
+ Status = UfsWaitMemSet (Address, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((EFI_D_INFO, "UfsPassThruDxe: found a attached UFS device\n"));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate common buffer for host and UFS bus master access simultaneously.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Size The length of buffer to be allocated.
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.
+ @param[out] CmdDescPhyAddr The resulting map address for the UFS bus master to use to access the hosts CmdDescHost.
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The common buffer was allocated successfully.
+ @retval EFI_DEVICE_ERROR The allocation fails.
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.
+
+**/
+EFI_STATUS
+UfsAllocateAlignCommonBuffer (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINTN Size,
+ OUT VOID **CmdDescHost,
+ OUT EFI_PHYSICAL_ADDRESS *CmdDescPhyAddr,
+ OUT VOID **CmdDescMapping
+ )
+{
+ EFI_STATUS Status;
+ UINTN Bytes;
+ BOOLEAN Is32BitAddr;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ if ((Private->Capabilities & UFS_HC_CAP_64ADDR) == UFS_HC_CAP_64ADDR) {
+ Is32BitAddr = TRUE;
+ } else {
+ Is32BitAddr = FALSE;
+ }
+
+ UfsHc = Private->UfsHostController;
+ Status = UfsHc->AllocateBuffer (
+ UfsHc,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ EFI_SIZE_TO_PAGES (Size),
+ CmdDescHost,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ *CmdDescMapping = NULL;
+ *CmdDescHost = NULL;
+ *CmdDescPhyAddr = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size));
+ Status = UfsHc->Map (
+ UfsHc,
+ EdkiiUfsHcOperationBusMasterCommonBuffer,
+ *CmdDescHost,
+ &Bytes,
+ CmdDescPhyAddr,
+ CmdDescMapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)))) {
+ UfsHc->FreeBuffer (
+ UfsHc,
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)),
+ *CmdDescHost
+ );
+ *CmdDescHost = NULL;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (Is32BitAddr && ((*CmdDescPhyAddr) > 0x100000000ULL)) {
+ //
+ // The UFS host controller doesn't support 64bit addressing, so should not get a >4G UFS bus master address.
+ //
+ UfsHc->Unmap (
+ UfsHc,
+ *CmdDescMapping
+ );
+ UfsHc->FreeBuffer (
+ UfsHc,
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)),
+ *CmdDescHost
+ );
+ *CmdDescMapping = NULL;
+ *CmdDescHost = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+
+ ZeroMem (*CmdDescHost, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)));
+ return EFI_SUCCESS;
+}
+
+/**
+ Enable the UFS host controller for accessing.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS host controller enabling was executed successfully.
+ @retval EFI_DEVICE_ERROR A device error occurred while enabling the UFS host controller.
+
+**/
+EFI_STATUS
+UfsEnableHostController (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN Address;
+ UINT32 Data;
+
+ //
+ // UFS 2.0 spec section 7.1.1 - Host Controller Initialization
+ //
+ // Reinitialize the UFS host controller if HCE bit of HC register is set.
+ //
+ Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET;
+ Data = MmioRead32 (Address);
+ if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) {
+ //
+ // Write a 0 to the HCE register at first to disable the host controller.
+ //
+ MmioWrite32 (Address, 0);
+ //
+ // Wait until HCE is read as '0' before continuing.
+ //
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Write a 1 to the HCE register to enable the UFS host controller.
+ //
+ MmioWrite32 (Address, UFS_HC_HCE_EN);
+ //
+ // Wait until HCE is read as '1' before continuing.
+ //
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detect if a UFS device attached.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS device detection was executed successfully.
+ @retval EFI_NOT_FOUND Not found a UFS device attached.
+ @retval EFI_DEVICE_ERROR A device error occurred while detecting the UFS device.
+
+**/
+EFI_STATUS
+UfsDeviceDetection (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ UINTN Retry;
+ EFI_STATUS Status;
+
+ //
+ // Start UFS device detection.
+ // Try up to 3 times for establishing data link with device.
+ //
+ for (Retry = 0; Retry < 3; Retry++) {
+ Status = UfsExecUicCommands (Private, UfsUicDmeLinkStartup, 0, 0, 0);
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ continue;
+ }
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Retry == 3) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize UFS task management request list related h/w context.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS task management list was initialzed successfully.
+ @retval EFI_DEVICE_ERROR The initialization fails.
+
+**/
+EFI_STATUS
+UfsInitTaskManagementRequestList (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ UINTN Address;
+ UINT32 Data;
+ UINT8 Nutmrs;
+ VOID *CmdDescHost;
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;
+ VOID *CmdDescMapping;
+ EFI_STATUS Status;
+
+ //
+ // Initial h/w and s/w context for future operations.
+ //
+ CmdDescHost = NULL;
+ CmdDescMapping = NULL;
+ CmdDescPhyAddr = 0;
+ Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET;
+ Data = MmioRead32 (Address);
+ Private->Capabilities = Data;
+
+ //
+ // Allocate and initialize UTP Task Management Request List.
+ //
+ Nutmrs = (UINT8) (RShiftU64 ((Private->Capabilities & UFS_HC_CAP_NUTMRS), 16) + 1);
+ Status = UfsAllocateAlignCommonBuffer (Private, Nutmrs * sizeof (UTP_TMRD), &CmdDescHost, &CmdDescPhyAddr, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Program the UTP Task Management Request List Base Address and UTP Task Management
+ // Request List Base Address with a 64-bit address allocated at step 6.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTMRLBA_OFFSET;
+ MmioWrite32 (Address, (UINT32)(UINTN)CmdDescPhyAddr);
+ Address = Private->UfsHcBase + UFS_HC_UTMRLBAU_OFFSET;
+ MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32));
+ Private->UtpTmrlBase = CmdDescHost;
+ Private->Nutmrs = Nutmrs;
+ Private->TmrlMapping = CmdDescMapping;
+
+ //
+ // Enable the UTP Task Management Request List by setting the UTP Task Management
+ // Request List RunStop Register (UTMRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET;
+ MmioWrite32 (Address, UFS_HC_UTMRLRSR);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize UFS transfer request list related h/w context.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The UFS transfer list was initialzed successfully.
+ @retval EFI_DEVICE_ERROR The initialization fails.
+
+**/
+EFI_STATUS
+UfsInitTransferRequestList (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ UINTN Address;
+ UINT32 Data;
+ UINT8 Nutrs;
+ VOID *CmdDescHost;
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;
+ VOID *CmdDescMapping;
+ EFI_STATUS Status;
+
+ //
+ // Initial h/w and s/w context for future operations.
+ //
+ CmdDescHost = NULL;
+ CmdDescMapping = NULL;
+ CmdDescPhyAddr = 0;
+ Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET;
+ Data = MmioRead32 (Address);
+ Private->Capabilities = Data;
+
+ //
+ // Allocate and initialize UTP Transfer Request List.
+ //
+ Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1);
+ Status = UfsAllocateAlignCommonBuffer (Private, Nutrs * sizeof (UTP_TRD), &CmdDescHost, &CmdDescPhyAddr, &CmdDescMapping);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Program the UTP Transfer Request List Base Address and UTP Transfer Request List
+ // Base Address with a 64-bit address allocated at step 8.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLBA_OFFSET;
+ MmioWrite32 (Address, (UINT32)(UINTN)CmdDescPhyAddr);
+ Address = Private->UfsHcBase + UFS_HC_UTRLBAU_OFFSET;
+ MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32));
+ Private->UtpTrlBase = CmdDescHost;
+ Private->Nutrs = Nutrs;
+ Private->TrlMapping = CmdDescMapping;
+
+ //
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List
+ // RunStop Register (UTRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET;
+ MmioWrite32 (Address, UFS_HC_UTRLRSR);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+UfsControllerInit (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UfsEnableHostController (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Enable Host Controller Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsDeviceDetection (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Device Detection Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsInitTaskManagementRequestList (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Task management list initialization Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsInitTransferRequestList (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Transfer list initialization Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "UfsControllerInit Finished\n"));
+ return EFI_SUCCESS;
+}
+
+/**
+ Stop the UFS host controller.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully.
+ @retval Others A device error occurred while stopping the controller.
+
+**/
+EFI_STATUS
+UfsControllerStop (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN Address;
+ UINT32 Data;
+
+ //
+ // Enable the UTP Task Management Request List by setting the UTP Task Management
+ // Request List RunStop Register (UTMRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET;
+ MmioWrite32 (Address, 0);
+
+ //
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List
+ // RunStop Register (UTRLRSR) to '1'.
+ //
+ Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET;
+ MmioWrite32 (Address, 0);
+
+ //
+ // Write a 0 to the HCE register in order to disable the host controller.
+ //
+ Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET;
+ Data = MmioRead32 (Address);
+ ASSERT ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN);
+ MmioWrite32 (Address, 0);
+
+ //
+ // Wait until HCE is read as '0' before continuing.
+ //
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((EFI_D_INFO, "UfsController is stopped\n"));
+
+ return EFI_SUCCESS;
+}
+
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h
new file mode 100644
index 0000000000..a423a921de
--- /dev/null
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h
@@ -0,0 +1,1339 @@
+/** @file
+ UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface
+ for upper layer application to execute UFS-supported SCSI cmds.
+
+ Copyright (c) 2014, Intel Corporation. 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 _UFS_PASS_THRU_HCI_H_
+#define _UFS_PASS_THRU_HCI_H_
+
+//
+// Host Capabilities Register Offsets
+//
+#define UFS_HC_CAP_OFFSET 0x0000 // Controller Capabilities
+#define UFS_HC_VER_OFFSET 0x0008 // Version
+#define UFS_HC_DDID_OFFSET 0x0010 // Device ID and Device Class
+#define UFS_HC_PMID_OFFSET 0x0014 // Product ID and Manufacturer ID
+#define UFS_HC_AHIT_OFFSET 0x0018 // Auto-Hibernate Idle Timer
+//
+// Operation and Runtime Register Offsets
+//
+#define UFS_HC_IS_OFFSET 0x0020 // Interrupt Status
+#define UFS_HC_IE_OFFSET 0x0024 // Interrupt Enable
+#define UFS_HC_STATUS_OFFSET 0x0030 // Host Controller Status
+#define UFS_HC_ENABLE_OFFSET 0x0034 // Host Controller Enable
+#define UFS_HC_UECPA_OFFSET 0x0038 // Host UIC Error Code PHY Adapter Layer
+#define UFS_HC_UECDL_OFFSET 0x003c // Host UIC Error Code Data Link Layer
+#define UFS_HC_UECN_OFFSET 0x0040 // Host UIC Error Code Network Layer
+#define UFS_HC_UECT_OFFSET 0x0044 // Host UIC Error Code Transport Layer
+#define UFS_HC_UECDME_OFFSET 0x0048 // Host UIC Error Code DME
+#define UFS_HC_UTRIACR_OFFSET 0x004c // UTP Transfer Request Interrupt Aggregation Control Register
+//
+// UTP Transfer Register Offsets
+//
+#define UFS_HC_UTRLBA_OFFSET 0x0050 // UTP Transfer Request List Base Address
+#define UFS_HC_UTRLBAU_OFFSET 0x0054 // UTP Transfer Request List Base Address Upper 32-Bits
+#define UFS_HC_UTRLDBR_OFFSET 0x0058 // UTP Transfer Request List Door Bell Register
+#define UFS_HC_UTRLCLR_OFFSET 0x005c // UTP Transfer Request List CLear Register
+#define UFS_HC_UTRLRSR_OFFSET 0x0060 // UTP Transfer Request Run-Stop Register
+//
+// UTP Task Management Register Offsets
+//
+#define UFS_HC_UTMRLBA_OFFSET 0x0070 // UTP Task Management Request List Base Address
+#define UFS_HC_UTMRLBAU_OFFSET 0x0074 // UTP Task Management Request List Base Address Upper 32-Bits
+#define UFS_HC_UTMRLDBR_OFFSET 0x0078 // UTP Task Management Request List Door Bell Register
+#define UFS_HC_UTMRLCLR_OFFSET 0x007c // UTP Task Management Request List CLear Register
+#define UFS_HC_UTMRLRSR_OFFSET 0x0080 // UTP Task Management Run-Stop Register
+//
+// UIC Command Register Offsets
+//
+#define UFS_HC_UIC_CMD_OFFSET 0x0090 // UIC Command Register
+#define UFS_HC_UCMD_ARG1_OFFSET 0x0094 // UIC Command Argument 1
+#define UFS_HC_UCMD_ARG2_OFFSET 0x0098 // UIC Command Argument 2
+#define UFS_HC_UCMD_ARG3_OFFSET 0x009c // UIC Command Argument 3
+//
+// UMA Register Offsets
+//
+#define UFS_HC_UMA_OFFSET 0x00b0 // Reserved for Unified Memory Extension
+
+#define UFS_HC_HCE_EN BIT0
+#define UFS_HC_HCS_DP BIT0
+#define UFS_HC_HCS_UCRDY BIT3
+#define UFS_HC_IS_ULSS BIT8
+#define UFS_HC_IS_UCCS BIT10
+#define UFS_HC_CAP_64ADDR BIT24
+#define UFS_HC_CAP_NUTMRS (BIT16 | BIT17 | BIT18)
+#define UFS_HC_CAP_NUTRS (BIT0 | BIT1 | BIT2 | BIT3 | BIT4)
+#define UFS_HC_UTMRLRSR BIT0
+#define UFS_HC_UTRLRSR BIT0
+
+//
+// A maximum of length of 256KB is supported by PRDT entry
+//
+#define UFS_MAX_DATA_LEN_PER_PRD 0x40000
+
+#define UFS_STORAGE_COMMAND_TYPE 0x01
+
+#define UFS_REGULAR_COMMAND 0x00
+#define UFS_INTERRUPT_COMMAND 0x01
+
+#define UFS_LUN_0 0x00
+#define UFS_LUN_1 0x01
+#define UFS_LUN_2 0x02
+#define UFS_LUN_3 0x03
+#define UFS_LUN_4 0x04
+#define UFS_LUN_5 0x05
+#define UFS_LUN_6 0x06
+#define UFS_LUN_7 0x07
+#define UFS_WLUN_REPORT_LUNS 0x81
+#define UFS_WLUN_UFS_DEV 0xD0
+#define UFS_WLUN_BOOT 0xB0
+#define UFS_WLUN_RPMB 0xC4
+
+#pragma pack(1)
+
+//
+// UFSHCI 2.0 Spec Section 5.2.1 Offset 00h: CAP - Controller Capabilities
+//
+typedef struct {
+ UINT8 Nutrs:4; // Number of UTP Transfer Request Slots
+ UINT8 Rsvd1:4;
+
+ UINT8 NoRtt; // Number of outstanding READY TO TRANSFER (RTT) requests supported
+
+ UINT8 Nutmrs:3; // Number of UTP Task Management Request Slots
+ UINT8 Rsvd2:4;
+ UINT8 AutoHs:1; // Auto-Hibernation Support
+
+ UINT8 As64:1; // 64-bit addressing supported
+ UINT8 Oodds:1; // Out of order data delivery supported
+ UINT8 UicDmetms:1; // UIC DME_TEST_MODE command supported
+ UINT8 Ume:1; // Reserved for Unified Memory Extension
+ UINT8 Rsvd4:4;
+} UFS_HC_CAP;
+
+//
+// UFSHCI 2.0 Spec Section 5.2.2 Offset 08h: VER - UFS Version
+//
+typedef struct {
+ UINT8 Vs:4; // Version Suffix
+ UINT8 Mnr:4; // Minor version number
+
+ UINT8 Mjr; // Major version number
+
+ UINT16 Rsvd1;
+} UFS_HC_VER;
+
+//
+// UFSHCI 2.0 Spec Section 5.2.3 Offset 10h: HCPID - Host Controller Product ID
+//
+#define UFS_HC_PID UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.2.4 Offset 14h: HCMID - Host Controller Manufacturer ID
+//
+#define UFS_HC_MID UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.2.5 Offset 18h: AHIT - Auto-Hibernate Idle Timer
+//
+typedef struct {
+ UINT32 Ahitv:10; // Auto-Hibernate Idle Timer Value
+ UINT32 Ts:3; // Timer scale
+ UINT32 Rsvd1:19;
+} UFS_HC_AHIT;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.1 Offset 20h: IS - Interrupt Status
+//
+typedef struct {
+ UINT16 Utrcs:1; // UTP Transfer Request Completion Status
+ UINT16 Udepri:1; // UIC DME_ENDPOINT_RESET Indication
+ UINT16 Ue:1; // UIC Error
+ UINT16 Utms:1; // UIC Test Mode Status
+
+ UINT16 Upms:1; // UIC Power Mode Status
+ UINT16 Uhxs:1; // UIC Hibernate Exit Status
+ UINT16 Uhes:1; // UIC Hibernate Enter Status
+ UINT16 Ulls:1; // UIC Link Lost Status
+
+ UINT16 Ulss:1; // UIC Link Startup Status
+ UINT16 Utmrcs:1; // UTP Task Management Request Completion Status
+ UINT16 Uccs:1; // UIC Command Completion Status
+ UINT16 Dfes:1; // Device Fatal Error Status
+
+ UINT16 Utpes:1; // UTP Error Status
+ UINT16 Rsvd1:3;
+
+ UINT16 Hcfes:1; // Host Controller Fatal Error Status
+ UINT16 Sbfes:1; // System Bus Fatal Error Status
+ UINT16 Rsvd2:14;
+} UFS_HC_IS;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.2 Offset 24h: IE - Interrupt Enable
+//
+typedef struct {
+ UINT16 Utrce:1; // UTP Transfer Request Completion Enable
+ UINT16 Udeprie:1; // UIC DME_ENDPOINT_RESET Enable
+ UINT16 Uee:1; // UIC Error Enable
+ UINT16 Utmse:1; // UIC Test Mode Status Enable
+
+ UINT16 Upmse:1; // UIC Power Mode Status Enable
+ UINT16 Uhxse:1; // UIC Hibernate Exit Status Enable
+ UINT16 Uhese:1; // UIC Hibernate Enter Status Enable
+ UINT16 Ullse:1; // UIC Link Lost Status Enable
+
+ UINT16 Ulsse:1; // UIC Link Startup Status Enable
+ UINT16 Utmrce:1; // UTP Task Management Request Completion Enable
+ UINT16 Ucce:1; // UIC Command Completion Enable
+ UINT16 Dfee:1; // Device Fatal Error Enable
+
+ UINT16 Utpee:1; // UTP Error Enable
+ UINT16 Rsvd1:3;
+
+ UINT16 Hcfee:1; // Host Controller Fatal Error Enable
+ UINT16 Sbfee:1; // System Bus Fatal Error Enable
+ UINT16 Rsvd2:14;
+} UFS_HC_IE;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.3 Offset 30h: HCS - Host Controller Status
+//
+typedef struct {
+ UINT8 Dp:1; // Device Present
+ UINT8 UtrlRdy:1; // UTP Transfer Request List Ready
+ UINT8 UtmrlRdy:1; // UTP Task Management Request List Ready
+ UINT8 UcRdy:1; // UIC COMMAND Ready
+ UINT8 Rsvd1:4;
+
+ UINT8 Upmcrs:3; // UIC Power Mode Change Request Status
+ UINT8 Rsvd2:1; // UIC Hibernate Exit Status Enable
+ UINT8 Utpec:4; // UTP Error Code
+
+ UINT8 TtagUtpE; // Task Tag of UTP error
+ UINT8 TlunUtpE; // Target LUN of UTP error
+} UFS_HC_STATUS;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.4 Offset 34h: HCE - Host Controller Enable
+//
+typedef struct {
+ UINT32 Hce:1; // Host Controller Enable
+ UINT32 Rsvd1:31;
+} UFS_HC_ENABLE;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.5 Offset 38h: UECPA - Host UIC Error Code PHY Adapter Layer
+//
+typedef struct {
+ UINT32 Ec:5; // UIC PHY Adapter Layer Error Code
+ UINT32 Rsvd1:26;
+ UINT32 Err:1; // UIC PHY Adapter Layer Error
+} UFS_HC_UECPA;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.6 Offset 3ch: UECDL - Host UIC Error Code Data Link Layer
+//
+typedef struct {
+ UINT32 Ec:15; // UIC Data Link Layer Error Code
+ UINT32 Rsvd1:16;
+ UINT32 Err:1; // UIC Data Link Layer Error
+} UFS_HC_UECDL;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.7 Offset 40h: UECN - Host UIC Error Code Network Layer
+//
+typedef struct {
+ UINT32 Ec:3; // UIC Network Layer Error Code
+ UINT32 Rsvd1:28;
+ UINT32 Err:1; // UIC Network Layer Error
+} UFS_HC_UECN;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.8 Offset 44h: UECT - Host UIC Error Code Transport Layer
+//
+typedef struct {
+ UINT32 Ec:7; // UIC Transport Layer Error Code
+ UINT32 Rsvd1:24;
+ UINT32 Err:1; // UIC Transport Layer Error
+} UFS_HC_UECT;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.9 Offset 48h: UECDME - Host UIC Error Code
+//
+typedef struct {
+ UINT32 Ec:1; // UIC DME Error Code
+ UINT32 Rsvd1:30;
+ UINT32 Err:1; // UIC DME Error
+} UFS_HC_UECDME;
+
+//
+// UFSHCI 2.0 Spec Section 5.3.10 Offset 4Ch: UTRIACR - UTP Transfer Request Interrupt Aggregation Control Register
+//
+typedef struct {
+ UINT8 IaToVal; // Interrupt aggregation timeout value
+
+ UINT8 IacTh:5; // Interrupt aggregation counter threshold
+ UINT8 Rsvd1:3;
+
+ UINT8 Ctr:1; // Counter and Timer Reset
+ UINT8 Rsvd2:3;
+ UINT8 Iasb:1; // Interrupt aggregation status bit
+ UINT8 Rsvd3:3;
+
+ UINT8 IapwEn:1; // Interrupt aggregation parameter write enable
+ UINT8 Rsvd4:6;
+ UINT8 IaEn:1; // Interrupt Aggregation Enable/Disable
+} UFS_HC_UTRIACR;
+
+//
+// UFSHCI 2.0 Spec Section 5.4.1 Offset 50h: UTRLBA - UTP Transfer Request List Base Address
+//
+typedef struct {
+ UINT32 Rsvd1:10;
+ UINT32 UtrlBa:22; // UTP Transfer Request List Base Address
+} UFS_HC_UTRLBA;
+
+//
+// UFSHCI 2.0 Spec Section 5.4.2 Offset 54h: UTRLBAU - UTP Transfer Request List Base Address Upper 32-bits
+//
+#define UFS_HC_UTRLBAU UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.4.3 Offset 58h: UTRLDBR - UTP Transfer Request List Door Bell Register
+//
+#define UFS_HC_UTRLDBR UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.4.4 Offset 5Ch: UTRLCLR - UTP Transfer Request List CLear Register
+//
+#define UFS_HC_UTRLCLR UINT32
+
+#if 0
+//
+// UFSHCI 2.0 Spec Section 5.4.5 Offset 60h: UTRLRSR - UTP Transfer Request List Run Stop Register
+//
+typedef struct {
+ UINT32 UtrlRsr:1; // UTP Transfer Request List Run-Stop Register
+ UINT32 Rsvd1:31;
+} UFS_HC_UTRLRSR;
+#endif
+
+//
+// UFSHCI 2.0 Spec Section 5.5.1 Offset 70h: UTMRLBA - UTP Task Management Request List Base Address
+//
+typedef struct {
+ UINT32 Rsvd1:10;
+ UINT32 UtmrlBa:22; // UTP Task Management Request List Base Address
+} UFS_HC_UTMRLBA;
+
+//
+// UFSHCI 2.0 Spec Section 5.5.2 Offset 74h: UTMRLBAU - UTP Task Management Request List Base Address Upper 32-bits
+//
+#define UFS_HC_UTMRLBAU UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.5.3 Offset 78h: UTMRLDBR - UTP Task Management Request List Door Bell Register
+//
+typedef struct {
+ UINT32 UtmrlDbr:8; // UTP Task Management Request List Door bell Register
+ UINT32 Rsvd1:24;
+} UFS_HC_UTMRLDBR;
+
+//
+// UFSHCI 2.0 Spec Section 5.5.4 Offset 7Ch: UTMRLCLR - UTP Task Management Request List CLear Register
+//
+typedef struct {
+ UINT32 UtmrlClr:8; // UTP Task Management List Clear Register
+ UINT32 Rsvd1:24;
+} UFS_HC_UTMRLCLR;
+
+#if 0
+//
+// UFSHCI 2.0 Spec Section 5.5.5 Offset 80h: UTMRLRSR - UTP Task Management Request List Run Stop Register
+//
+typedef struct {
+ UINT32 UtmrlRsr:1; // UTP Task Management Request List Run-Stop Register
+ UINT32 Rsvd1:31;
+} UFS_HC_UTMRLRSR;
+#endif
+
+//
+// UFSHCI 2.0 Spec Section 5.6.1 Offset 90h: UICCMD - UIC Command
+//
+typedef struct {
+ UINT32 CmdOp:8; // Command Opcode
+ UINT32 Rsvd1:24;
+} UFS_HC_UICCMD;
+
+//
+// UFSHCI 2.0 Spec Section 5.6.2 Offset 94h: UICCMDARG1 - UIC Command Argument 1
+//
+#define UFS_HC_UICCMD_ARG1 UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.6.2 Offset 98h: UICCMDARG2 - UIC Command Argument 2
+//
+#define UFS_HC_UICCMD_ARG2 UINT32
+
+//
+// UFSHCI 2.0 Spec Section 5.6.2 Offset 9ch: UICCMDARG3 - UIC Command Argument 3
+//
+#define UFS_HC_UICCMD_ARG3 UINT32
+
+//
+// UIC command opcodes
+//
+typedef enum {
+ UfsUicDmeGet = 0x01,
+ UfsUicDmeSet = 0x02,
+ UfsUicDmePeerGet = 0x03,
+ UfsUicDmePeerSet = 0x04,
+ UfsUicDmePwrOn = 0x10,
+ UfsUicDmePwrOff = 0x11,
+ UfsUicDmeEnable = 0x12,
+ UfsUicDmeReset = 0x14,
+ UfsUicDmeEndpointReset = 0x15,
+ UfsUicDmeLinkStartup = 0x16,
+ UfsUicDmeHibernateEnter = 0x17,
+ UfsUicDmeHibernateExit = 0x18,
+ UfsUicDmeTestMode = 0x1A
+} UFS_UIC_OPCODE;
+
+//
+// UTP Transfer Request Descriptor
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT32 Rsvd1:24;
+ UINT32 Int:1; /* Interrupt */
+ UINT32 Dd:2; /* Data Direction */
+ UINT32 Rsvd2:1;
+ UINT32 Ct:4; /* Command Type */
+
+ //
+ // DW1
+ //
+ UINT32 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT32 Ocs:8; /* Overall Command Status */
+ UINT32 Rsvd4:24;
+
+ //
+ // DW3
+ //
+ UINT32 Rsvd5;
+
+ //
+ // DW4
+ //
+ UINT32 Rsvd6:7;
+ UINT32 UcdBa:25; /* UTP Command Descriptor Base Address */
+
+ //
+ // DW5
+ //
+ UINT32 UcdBaU; /* UTP Command Descriptor Base Address Upper 32-bits */
+
+ //
+ // DW6
+ //
+ UINT16 RuL; /* Response UPIU Length */
+ UINT16 RuO; /* Response UPIU Offset */
+
+ //
+ // DW7
+ //
+ UINT16 PrdtL; /* PRDT Length */
+ UINT16 PrdtO; /* PRDT Offset */
+} UTP_TRD;
+
+typedef struct {
+ //
+ // DW0
+ //
+ UINT32 Rsvd1:2;
+ UINT32 DbAddr:30; /* Data Base Address */
+
+ //
+ // DW1
+ //
+ UINT32 DbAddrU; /* Data Base Address Upper 32-bits */
+
+ //
+ // DW2
+ //
+ UINT32 Rsvd2;
+
+ //
+ // DW3
+ //
+ UINT32 DbCount:18; /* Data Byte Count */
+ UINT32 Rsvd3:14;
+} UTP_TR_PRD;
+
+//
+// UFS 2.0 Spec Section 10.5.3 - UTP Command UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x01*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 CmdSet:4; /* Command Set Type */
+ UINT8 Rsvd1:4;
+ UINT8 Rsvd2;
+ UINT8 Rsvd3;
+ UINT8 Rsvd4;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd5;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 ExpDataTranLen; /* Expected Data Transfer Length - Big Endian */
+
+ //
+ // DW4 - DW7
+ //
+ UINT8 Cdb[16];
+} UTP_COMMAND_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.4 - UTP Response UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x21*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 CmdSet:4; /* Command Set Type */
+ UINT8 Rsvd1:4;
+ UINT8 Rsvd2;
+ UINT8 Response; /* Response */
+ UINT8 Status; /* Status */
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3
+ //
+ UINT32 ResTranCount; /* Residual Transfer Count - Big Endian */
+
+ //
+ // DW4 - DW7
+ //
+ UINT8 Rsvd3[16];
+
+ //
+ // Data Segment - Sense Data
+ //
+ UINT16 SenseDataLen; /* Sense Data Length - Big Endian */
+ UINT8 SenseData[18]; /* Sense Data */
+} UTP_RESPONSE_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.5 - UTP Data-Out UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x02*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd2;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3
+ //
+ UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 DataTranCount; /* Data Transfer Count - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd3[12];
+
+ //
+ // Data Segment - Data to be sent out
+ //
+ //UINT8 Data[]; /* Data to be sent out, maximum is 65535 bytes */
+} UTP_DATA_OUT_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.6 - UTP Data-In UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x22*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd2;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3
+ //
+ UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 DataTranCount; /* Data Transfer Count - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd3[12];
+
+ //
+ // Data Segment - Data to be read
+ //
+ //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */
+} UTP_DATA_IN_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.7 - UTP Ready-To-Transfer UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x31*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd2;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 DataTranCount; /* Data Transfer Count - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd3[12];
+
+ //
+ // Data Segment - Data to be read
+ //
+ //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */
+} UTP_RDY_TO_TRAN_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.8 - UTP Task Management Request UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x04*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1;
+ UINT8 TskManFunc; /* Task Management Function */
+ UINT8 Rsvd2[2];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd3;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 InputParam1; /* Input Parameter 1 - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 InputParam2; /* Input Parameter 2 - Big Endian */
+
+ //
+ // DW5
+ //
+ UINT32 InputParam3; /* Input Parameter 3 - Big Endian */
+
+ //
+ // DW6 - DW7
+ //
+ UINT8 Rsvd4[8];
+} UTP_TM_REQ_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.9 - UTP Task Management Response UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x24*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[2];
+ UINT8 Resp; /* Response */
+ UINT8 Rsvd2;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd3;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT32 OutputParam1; /* Output Parameter 1 - Big Endian */
+
+ //
+ // DW4
+ //
+ UINT32 OutputParam2; /* Output Parameter 2 - Big Endian */
+
+ //
+ // DW5 - DW7
+ //
+ UINT8 Rsvd4[12];
+} UTP_TM_RESP_UPIU;
+
+//
+// UTP Task Management Request Descriptor
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT32 Rsvd1:24;
+ UINT32 Int:1; /* Interrupt */
+ UINT32 Rsvd2:7;
+
+ //
+ // DW1
+ //
+ UINT32 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT32 Ocs:8; /* Overall Command Status */
+ UINT32 Rsvd4:24;
+
+ //
+ // DW3
+ //
+ UINT32 Rsvd5;
+
+ //
+ // DW4 - DW11
+ //
+ UTP_TM_REQ_UPIU TmReq; /* Task Management Request UPIU */
+
+ //
+ // DW12 - DW19
+ //
+ UTP_TM_RESP_UPIU TmResp; /* Task Management Response UPIU */
+} UTP_TMRD;
+
+
+typedef struct {
+ UINT8 Opcode;
+ UINT8 DescId;
+ UINT8 Index;
+ UINT8 Selector;
+ UINT16 Rsvd1;
+ UINT16 Length;
+ UINT32 Value;
+ UINT32 Rsvd2;
+} UTP_UPIU_TSF;
+
+//
+// UFS 2.0 Spec Section 10.5.10 - UTP Query Request UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x16*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2;
+ UINT8 QueryFunc; /* Query Function */
+ UINT8 Rsvd3[2];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd4;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3 - 6
+ //
+ UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */
+
+ //
+ // DW7
+ //
+ UINT8 Rsvd5[4];
+
+ //
+ // Data Segment - Data to be transferred
+ //
+ //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */
+} UTP_QUERY_REQ_UPIU;
+
+#define QUERY_FUNC_STD_READ_REQ 0x01
+#define QUERY_FUNC_STD_WRITE_REQ 0x81
+
+typedef enum {
+ UtpQueryFuncOpcodeNop = 0x00,
+ UtpQueryFuncOpcodeRdDesc = 0x01,
+ UtpQueryFuncOpcodeWrDesc = 0x02,
+ UtpQueryFuncOpcodeRdAttr = 0x03,
+ UtpQueryFuncOpcodeWrAttr = 0x04,
+ UtpQueryFuncOpcodeRdFlag = 0x05,
+ UtpQueryFuncOpcodeSetFlag = 0x06,
+ UtpQueryFuncOpcodeClrFlag = 0x07,
+ UtpQueryFuncOpcodeTogFlag = 0x08
+} UTP_QUERY_FUNC_OPCODE;
+
+//
+// UFS 2.0 Spec Section 10.5.11 - UTP Query Response UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x36*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2;
+ UINT8 QueryFunc; /* Query Function */
+ UINT8 QueryResp; /* Query Response */
+ UINT8 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian */
+
+ //
+ // DW3 - 6
+ //
+ UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */
+
+ //
+ // DW7
+ //
+ UINT8 Rsvd4[4];
+
+ //
+ // Data Segment - Data to be transferred
+ //
+ //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */
+} UTP_QUERY_RESP_UPIU;
+
+typedef enum {
+ UfsUtpQueryResponseSuccess = 0x00,
+ UfsUtpQueryResponseParamNotReadable = 0xF6,
+ UfsUtpQueryResponseParamNotWriteable = 0xF7,
+ UfsUtpQueryResponseParamAlreadyWritten = 0xF8,
+ UfsUtpQueryResponseInvalidLen = 0xF9,
+ UfsUtpQueryResponseInvalidVal = 0xFA,
+ UfsUtpQueryResponseInvalidSelector = 0xFB,
+ UfsUtpQueryResponseInvalidIndex = 0xFC,
+ UfsUtpQueryResponseInvalidIdn = 0xFD,
+ UfsUtpQueryResponseInvalidOpc = 0xFE,
+ UfsUtpQueryResponseGeneralFailure = 0xFF
+} UTP_QUERY_RESP_CODE;
+
+//
+// UFS 2.0 Spec Section 10.5.12 - UTP Reject UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x3F*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Lun;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd1[2];
+ UINT8 Response; /* Response - 0x01 */
+ UINT8 Rsvd2;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information - 0x00 */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3
+ //
+ UINT8 HdrSts; /* Basic Header Status */
+ UINT8 Rsvd3;
+ UINT8 E2ESts; /* End-to-End Status */
+ UINT8 Rsvd4;
+
+ //
+ // DW4 - DW7
+ //
+ UINT8 Rsvd5[16];
+} UTP_REJ_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.13 - UTP NOP OUT UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x00*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2[4];
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 Rsvd3;
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3 - DW7
+ //
+ UINT8 Rsvd4[20];
+} UTP_NOP_OUT_UPIU;
+
+//
+// UFS 2.0 Spec Section 10.5.14 - UTP NOP IN UPIU
+//
+typedef struct {
+ //
+ // DW0
+ //
+ UINT8 TransCode:6; /* Transaction Type - 0x20*/
+ UINT8 Dd:1;
+ UINT8 Hd:1;
+ UINT8 Flags;
+ UINT8 Rsvd1;
+ UINT8 TaskTag; /* Task Tag */
+
+ //
+ // DW1
+ //
+ UINT8 Rsvd2[2];
+ UINT8 Resp; /* Response - 0x00 */
+ UINT8 Rsvd3;
+
+ //
+ // DW2
+ //
+ UINT8 EhsLen; /* Total EHS Length - 0x00 */
+ UINT8 DevInfo; /* Device Information - 0x00 */
+ UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */
+
+ //
+ // DW3 - DW7
+ //
+ UINT8 Rsvd4[20];
+} UTP_NOP_IN_UPIU;
+
+//
+// UFS Descriptors
+//
+typedef enum {
+ UfsDeviceDesc = 0x00,
+ UfsConfigDesc = 0x01,
+ UfsUnitDesc = 0x02,
+ UfsInterConnDesc = 0x04,
+ UfsStringDesc = 0x05,
+ UfsGeometryDesc = 0x07,
+ UfsPowerDesc = 0x08
+} UFS_DESC_IDN;
+
+//
+// UFS 2.0 Spec Section 14.1.6.2 - Device Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 Device;
+ UINT8 DevClass;
+ UINT8 DevSubClass;
+ UINT8 Protocol;
+ UINT8 NumLun;
+ UINT8 NumWLun;
+ UINT8 BootEn;
+ UINT8 DescAccessEn;
+ UINT8 InitPowerMode;
+ UINT8 HighPriorityLun;
+ UINT8 SecureRemovalType;
+ UINT8 SecurityLun;
+ UINT8 BgOpsTermLat;
+ UINT8 InitActiveIccLevel;
+ UINT16 SpecVersion;
+ UINT16 ManufactureDate;
+ UINT8 ManufacturerName;
+ UINT8 ProductName;
+ UINT8 SerialName;
+ UINT8 OemId;
+ UINT16 ManufacturerId;
+ UINT8 Ud0BaseOffset;
+ UINT8 Ud0ConfParamLen;
+ UINT8 DevRttCap;
+ UINT16 PeriodicRtcUpdate;
+ UINT8 Rsvd1[17];
+ UINT8 Rsvd2[16];
+} UFS_DEV_DESC;
+
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 Rsvd1;
+ UINT8 BootEn;
+ UINT8 DescAccessEn;
+ UINT8 InitPowerMode;
+ UINT8 HighPriorityLun;
+ UINT8 SecureRemovalType;
+ UINT8 InitActiveIccLevel;
+ UINT16 PeriodicRtcUpdate;
+ UINT8 Rsvd2[5];
+} UFS_CONFIG_DESC_GEN_HEADER;
+
+typedef struct {
+ UINT8 LunEn;
+ UINT8 BootLunId;
+ UINT8 LunWriteProt;
+ UINT8 MemType;
+ UINT32 NumAllocUnits;
+ UINT8 DataReliability;
+ UINT8 LogicBlkSize;
+ UINT8 ProvisionType;
+ UINT16 CtxCap;
+ UINT8 Rsvd1[3];
+} UFS_UNIT_DESC_CONFIG_PARAMS;
+
+//
+// UFS 2.0 Spec Section 14.1.6.3 - Configuration Descriptor
+//
+typedef struct {
+ UFS_CONFIG_DESC_GEN_HEADER Header;
+ UFS_UNIT_DESC_CONFIG_PARAMS UnitDescConfParams[8];
+} UFS_CONFIG_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.4 - Geometry Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 MediaTech;
+ UINT8 Rsvd1;
+ UINT64 TotalRawDevCapacity;
+ UINT8 Rsvd2;
+ UINT32 SegSize;
+ UINT8 AllocUnitSize;
+ UINT8 MinAddrBlkSize;
+ UINT8 OptReadBlkSize;
+ UINT8 OptWriteBlkSize;
+ UINT8 MaxInBufSize;
+ UINT8 MaxOutBufSize;
+ UINT8 RpmbRwSize;
+ UINT8 Rsvd3;
+ UINT8 DataOrder;
+ UINT8 MaxCtxIdNum;
+ UINT8 SysDataTagUnitSize;
+ UINT8 SysDataResUnitSize;
+ UINT8 SupSecRemovalTypes;
+ UINT16 SupMemTypes;
+ UINT32 SysCodeMaxNumAllocUnits;
+ UINT16 SupCodeCapAdjFac;
+ UINT32 NonPersMaxNumAllocUnits;
+ UINT16 NonPersCapAdjFac;
+ UINT32 Enhance1MaxNumAllocUnits;
+ UINT16 Enhance1CapAdjFac;
+ UINT32 Enhance2MaxNumAllocUnits;
+ UINT16 Enhance2CapAdjFac;
+ UINT32 Enhance3MaxNumAllocUnits;
+ UINT16 Enhance3CapAdjFac;
+ UINT32 Enhance4MaxNumAllocUnits;
+ UINT16 Enhance4CapAdjFac;
+} UFS_GEOMETRY_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.5 - Unit Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 UnitIdx;
+ UINT8 LunEn;
+ UINT8 BootLunId;
+ UINT8 LunWriteProt;
+ UINT8 LunQueueDep;
+ UINT8 Rsvd1;
+ UINT8 MemType;
+ UINT8 DataReliability;
+ UINT8 LogicBlkSize;
+ UINT64 LogicBlkCount;
+ UINT32 EraseBlkSize;
+ UINT8 ProvisionType;
+ UINT64 PhyMemResCount;
+ UINT16 CtxCap;
+ UINT8 LargeUnitGranularity;
+} UFS_UNIT_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.6 - RPMB Unit Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 UnitIdx;
+ UINT8 LunEn;
+ UINT8 BootLunId;
+ UINT8 LunWriteProt;
+ UINT8 LunQueueDep;
+ UINT8 Rsvd1;
+ UINT8 MemType;
+ UINT8 Rsvd2;
+ UINT8 LogicBlkSize;
+ UINT64 LogicBlkCount;
+ UINT32 EraseBlkSize;
+ UINT8 ProvisionType;
+ UINT64 PhyMemResCount;
+ UINT8 Rsvd3[3];
+} UFS_RPMB_UNIT_DESC;
+
+typedef struct {
+ UINT16 Value:10;
+ UINT16 Rsvd1:4;
+ UINT16 Unit:2;
+} UFS_POWER_PARAM_ELEMENT;
+
+//
+// UFS 2.0 Spec Section 14.1.6.7 - Power Parameter Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UFS_POWER_PARAM_ELEMENT ActiveIccLevelVcc[16];
+ UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ[16];
+ UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ2[16];
+} UFS_POWER_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.8 - InterConnect Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT16 UniProVer;
+ UINT16 MphyVer;
+} UFS_INTER_CONNECT_DESC;
+
+//
+// UFS 2.0 Spec Section 14.1.6.9 - 14.1.6.12 - String Descriptor
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ CHAR16 Unicode[126];
+} UFS_STRING_DESC;
+
+//
+// UFS 2.0 Spec Section 14.2 - Flags
+//
+typedef enum {
+ UfsFlagDevInit = 0x01,
+ UfsFlagPermWpEn = 0x02,
+ UfsFlagPowerOnWpEn = 0x03,
+ UfsFlagBgOpsEn = 0x04,
+ UfsFlagPurgeEn = 0x06,
+ UfsFlagPhyResRemoval = 0x08,
+ UfsFlagBusyRtc = 0x09,
+ UfsFlagPermDisFwUpdate = 0x0B
+} UFS_FLAGS_IDN;
+
+//
+// UFS 2.0 Spec Section 14.2 - Attributes
+//
+typedef enum {
+ UfsAttrBootLunEn = 0x00,
+ UfsAttrCurPowerMode = 0x02,
+ UfsAttrActiveIccLevel = 0x03,
+ UfsAttrOutOfOrderDataEn = 0x04,
+ UfsAttrBgOpStatus = 0x05,
+ UfsAttrPurgeStatus = 0x06,
+ UfsAttrMaxDataInSize = 0x07,
+ UfsAttrMaxDataOutSize = 0x08,
+ UfsAttrDynCapNeeded = 0x09,
+ UfsAttrRefClkFreq = 0x0a,
+ UfsAttrConfigDescLock = 0x0b,
+ UfsAttrMaxNumOfRtt = 0x0c,
+ UfsAttrExceptionEvtCtrl = 0x0d,
+ UfsAttrExceptionEvtSts = 0x0e,
+ UfsAttrSecondsPassed = 0x0f,
+ UfsAttrContextConf = 0x10,
+ UfsAttrCorrPrgBlkNum = 0x11
+} UFS_ATTR_IDN;
+
+typedef enum {
+ UfsNoData = 0,
+ UfsDataOut = 1,
+ UfsDataIn = 2,
+ UfsDdReserved
+} UFS_DATA_DIRECTION;
+
+
+#pragma pack()
+
+#endif
+
diff --git a/MdeModulePkg/Include/Ppi/UfsHostController.h b/MdeModulePkg/Include/Ppi/UfsHostController.h
new file mode 100644
index 0000000000..129636580e
--- /dev/null
+++ b/MdeModulePkg/Include/Ppi/UfsHostController.h
@@ -0,0 +1,60 @@
+/** @file
+
+Copyright (c) 2014, Intel Corporation. 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 _EDKII_PEI_UFS_HOST_CONTROLLER_PPI_H_
+#define _EDKII_PEI_UFS_HOST_CONTROLLER_PPI_H_
+
+///
+/// Global ID for the EDKII_UFS_HOST_CONTROLLER_PPI.
+///
+#define EDKII_UFS_HOST_CONTROLLER_PPI_GUID \
+ { \
+ 0xdc54b283, 0x1a77, 0x4cd6, { 0x83, 0xbb, 0xfd, 0xda, 0x46, 0x9a, 0x2e, 0xc6 } \
+ }
+
+///
+/// Forward declaration for the UFS_HOST_CONTROLLER_PPI.
+///
+typedef struct _EDKII_UFS_HOST_CONTROLLER_PPI EDKII_UFS_HOST_CONTROLLER_PPI;
+
+/**
+ Get the MMIO base address of UFS host controller.
+
+ @param[in] This The protocol instance pointer.
+ @param[in] ControllerId The ID of the UFS host controller.
+ @param[out] MmioBar Pointer to the UFS host controller MMIO base address.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_GET_MMIO_BAR)(
+ IN EDKII_UFS_HOST_CONTROLLER_PPI *This,
+ IN UINT8 ControllerId,
+ OUT UINTN *MmioBar
+ );
+
+///
+/// This PPI contains a set of services to interact with the UFS host controller.
+///
+struct _EDKII_UFS_HOST_CONTROLLER_PPI {
+ EDKII_UFS_HC_GET_MMIO_BAR GetUfsHcMmioBar;
+};
+
+extern EFI_GUID gEdkiiPeiUfsHostControllerPpiGuid;
+
+#endif
diff --git a/MdeModulePkg/Include/Protocol/UfsHostController.h b/MdeModulePkg/Include/Protocol/UfsHostController.h
new file mode 100644
index 0000000000..83db1a7bbd
--- /dev/null
+++ b/MdeModulePkg/Include/Protocol/UfsHostController.h
@@ -0,0 +1,205 @@
+/** @file
+
+ EDKII Universal Flash Storage Host Controller Protocol.
+
+Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under
+the terms and conditions of the BSD License that 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 __EDKII_UFS_HC_PROTOCOL_H__
+#define __EDKII_UFS_HC_PROTOCOL_H__
+
+//
+// UFS Host Controller Protocol GUID value
+//
+#define EDKII_UFS_HOST_CONTROLLER_PROTOCOL_GUID \
+ { \
+ 0xebc01af5, 0x7a9, 0x489e, { 0xb7, 0xce, 0xdc, 0x8, 0x9e, 0x45, 0x9b, 0x2f } \
+ }
+
+//
+// Forward reference for pure ANSI compatability
+//
+typedef struct _EDKII_UFS_HOST_CONTROLLER_PROTOCOL EDKII_UFS_HOST_CONTROLLER_PROTOCOL;
+
+
+/**
+ Get the MMIO base address of UFS host controller.
+
+ @param This The protocol instance pointer.
+ @param MmioBar Pointer to the UFS host controller MMIO base address.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_GET_MMIO_BAR)(
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ OUT UINTN *MmioBar
+ );
+
+///
+/// *******************************************************
+/// EFI_UFS_HOST_CONTROLLER_OPERATION
+/// *******************************************************
+///
+typedef enum {
+ ///
+ /// A read operation from system memory by a bus master.
+ ///
+ EdkiiUfsHcOperationBusMasterRead,
+ ///
+ /// A write operation from system memory by a bus master.
+ ///
+ EdkiiUfsHcOperationBusMasterWrite,
+ ///
+ /// Provides both read and write access to system memory by both the processor and a
+ /// bus master. The buffer is coherent from both the processor's and the bus master's point of view.
+ ///
+ EdkiiUfsHcOperationBusMasterCommonBuffer,
+ EdkiiUfsHcOperationMaximum
+} EDKII_UFS_HOST_CONTROLLER_OPERATION;
+
+/**
+ Provides the UFS controller-specific addresses needed to access system memory.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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 UFS 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 UFS 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.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_MAP)(
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN EDKII_UFS_HOST_CONTROLLER_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_UNMAP)(
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN VOID *Mapping
+ );
+
+/**
+ Allocates pages that are suitable for an EfiUfsHcOperationBusMasterCommonBuffer
+ mapping.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_ALLOCATE_BUFFER)(
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ IN UINT64 Attributes
+ );
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_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 EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+ was not allocated with AllocateBuffer().
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_FREE_BUFFER)(
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ );
+
+/**
+ Flushes all posted write transactions from the UFS bus to attached UFS device.
+
+ @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The posted write transactions were flushed from the UFS bus
+ to attached UFS device.
+ @retval EFI_DEVICE_ERROR The posted write transactions were not flushed from the UFS
+ bus to attached UFS device due to a hardware error.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_UFS_HC_FLUSH)(
+ IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This
+ );
+
+///
+/// UFS Host Controller Protocol structure.
+///
+struct _EDKII_UFS_HOST_CONTROLLER_PROTOCOL {
+ EDKII_UFS_HC_GET_MMIO_BAR GetUfsHcMmioBar;
+ EDKII_UFS_HC_ALLOCATE_BUFFER AllocateBuffer;
+ EDKII_UFS_HC_FREE_BUFFER FreeBuffer;
+ EDKII_UFS_HC_MAP Map;
+ EDKII_UFS_HC_UNMAP Unmap;
+ EDKII_UFS_HC_FLUSH Flush;
+};
+
+///
+/// UFS Host Controller Protocol GUID variable.
+///
+extern EFI_GUID gEdkiiUfsHostControllerProtocolGuid;
+
+#endif
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 421c6977ab..5591b2f068 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -324,6 +324,9 @@
## Include/Ppi/SerialPortPei.h
gPeiSerialPortPpiGuid = { 0x490e9d85, 0x8aef, 0x4193, { 0x8e, 0x56, 0xf7, 0x34, 0xa9, 0xff, 0xac, 0x8b}}
+ ## Include/Ppi/UfsHostController.h
+ gEdkiiPeiUfsHostControllerPpiGuid = { 0xdc54b283, 0x1a77, 0x4cd6, { 0x83, 0xbb, 0xfd, 0xda, 0x46, 0x9a, 0x2e, 0xc6 }}
+
[Protocols]
## Load File protocol provides capability to load and unload EFI image into memory and execute it.
# Include/Protocol/LoadPe32Image.h
@@ -401,6 +404,9 @@
## Include/Protocol/FormBrowserEx2.h
gEdkiiFormBrowserEx2ProtocolGuid = { 0xa770c357, 0xb693, 0x4e6d, { 0xa6, 0xcf, 0xd2, 0x1c, 0x72, 0x8e, 0x55, 0xb } }
+ ## Include/Protocol/UfsHostController.h
+ gEdkiiUfsHostControllerProtocolGuid = { 0xebc01af5, 0x7a9, 0x489e, { 0xb7, 0xce, 0xdc, 0x8, 0x9e, 0x45, 0x9b, 0x2f } }
+
#
# [Error.gEfiMdeModulePkgTokenSpaceGuid]
# 0x80000001 | Invalid value provided.
@@ -1214,6 +1220,12 @@
# @Prompt Disk I/O - Number of Data Buffer block.
gEfiMdeModulePkgTokenSpaceGuid.PcdDiskIoDataBufferBlockNum|64|UINT32|0x30001039
+ ## This PCD specifies the PCI-based UFS host controller mmio base address.
+ # Define the mmio base address of the pci-based UFS host controller. If there are multiple UFS
+ # host controllers, their mmio base addresses are calculated one by one from this base address.
+ # @Prompt Mmio base address of pci-based UFS host controller.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUfsPciHostControllerMmioBase|0xd0000000|UINT32|0x10000061
+
[PcdsPatchableInModule]
## Specify memory size with page number for PEI code when
# Loading Module at Fixed Address feature is enabled.
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index b9df820d6d..0bf3cca871 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -203,6 +203,10 @@
MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf
MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
+ MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf
+ MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf
+ MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf
+ MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf
MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf
diff --git a/MdeModulePkg/MdeModulePkg.uni b/MdeModulePkg/MdeModulePkg.uni
index add9a51e56..1527453712 100644
--- a/MdeModulePkg/MdeModulePkg.uni
+++ b/MdeModulePkg/MdeModulePkg.uni
Binary files differ