summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c216
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsDevConfigProtocol.c190
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c1196
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h999
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni17
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf60
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c2454
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h1339
9 files changed, 6485 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c
new file mode 100644
index 00000000..11df7721
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c
@@ -0,0 +1,216 @@
+/** @file
+
+ Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#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/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsDevConfigProtocol.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsDevConfigProtocol.c
new file mode 100644
index 00000000..d8c5fb3a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsDevConfigProtocol.c
@@ -0,0 +1,190 @@
+/** @file
+ The implementation of the EFI UFS Device Config Protocol.
+
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "UfsPassThru.h"
+
+/**
+ Read or write specified device descriptor of a UFS device.
+
+ The function is used to read/write UFS device descriptors. The consumer of this API is
+ responsible for allocating the data buffer pointed by Descriptor.
+
+ @param[in] This The pointer to the EFI_UFS_DEVICE_CONFIG_PROTOCOL instance.
+ @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, out] DescSize The size of device descriptor buffer. On input, the size, in bytes,
+ of the data buffer specified by Descriptor. On output, the number
+ of bytes that were actually transferred.
+
+ @retval EFI_SUCCESS The device descriptor is read/written successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Descriptor is NULL or DescSize is NULL.
+ DescId, Index and Selector are invalid combination to point to a
+ type of UFS device descriptor.
+ @retval EFI_DEVICE_ERROR The device descriptor is not read/written successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsRwUfsDescriptor (
+ IN EFI_UFS_DEVICE_CONFIG_PROTOCOL *This,
+ IN BOOLEAN Read,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT UINT8 *Descriptor,
+ IN OUT UINT32 *DescSize
+ )
+{
+ EFI_STATUS Status;
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_DEV_CONFIG (This);
+
+ if ((This == NULL) || (Descriptor == NULL) || (DescSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = UfsRwDeviceDesc (
+ Private,
+ Read,
+ DescId,
+ Index,
+ Selector,
+ Descriptor,
+ DescSize
+ );
+ if (Status == EFI_TIMEOUT) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ return Status;
+}
+
+/**
+ Read or write specified flag of a UFS device.
+
+ The function is used to read/write UFS flag descriptors. The consumer of this API is responsible
+ for allocating the buffer pointed by Flag. The buffer size is 1 byte as UFS flag descriptor is
+ just a single Boolean value that represents a TRUE or FALSE, '0' or '1', ON or OFF type of value.
+
+ @param[in] This The pointer to the EFI_UFS_DEVICE_CONFIG_PROTOCOL instance.
+ @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] Flag The buffer to set or clear flag.
+
+ @retval EFI_SUCCESS The flag descriptor is set/clear successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Flag is NULL.
+ FlagId is an invalid UFS flag ID.
+ @retval EFI_DEVICE_ERROR The flag is not set/clear successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsRwUfsFlag (
+ IN EFI_UFS_DEVICE_CONFIG_PROTOCOL *This,
+ IN BOOLEAN Read,
+ IN UINT8 FlagId,
+ IN OUT UINT8 *Flag
+ )
+{
+ EFI_STATUS Status;
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_DEV_CONFIG (This);
+
+ if ((This == NULL) || (Flag == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = UfsRwFlags (Private, Read, FlagId, Flag);
+ if (Status == EFI_TIMEOUT) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ return Status;
+}
+
+/**
+ Read or write specified attribute of a UFS device.
+
+ The function is used to read/write UFS attributes. The consumer of this API is responsible for
+ allocating the data buffer pointed by Attribute.
+
+ @param[in] This The pointer to the EFI_UFS_DEVICE_CONFIG_PROTOCOL instance.
+ @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] Attribute The buffer of Attribute to be read or written.
+ @param[in, out] AttrSize The size of Attribute buffer. On input, the size, in bytes, of the
+ data buffer specified by Attribute. On output, the number of bytes
+ that were actually transferred.
+
+ @retval EFI_SUCCESS The attribute is read/written successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Attribute is NULL or AttrSize is NULL.
+ AttrId, Index and Selector are invalid combination to point to a
+ type of UFS attribute.
+ @retval EFI_DEVICE_ERROR The attribute is not read/written successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsRwUfsAttribute (
+ IN EFI_UFS_DEVICE_CONFIG_PROTOCOL *This,
+ IN BOOLEAN Read,
+ IN UINT8 AttrId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT UINT8 *Attribute,
+ IN OUT UINT32 *AttrSize
+ )
+{
+ EFI_STATUS Status;
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ UINT32 Attribute32;
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_DEV_CONFIG (This);
+ Attribute32 = 0;
+
+ if ((This == NULL) || (Attribute == NULL) || (AttrSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // According to UFS Version 2.1 Spec (JESD220C) Section 14.3, the size of a attribute will not
+ // exceed 32-bit.
+ //
+ if (*AttrSize > 4) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Read) {
+ CopyMem (&Attribute32, Attribute, *AttrSize);
+ }
+
+ Status = UfsRwAttributes (
+ Private,
+ Read,
+ AttrId,
+ Index,
+ Selector,
+ &Attribute32
+ );
+ if (!EFI_ERROR (Status)) {
+ if (Read) {
+ CopyMem (Attribute, &Attribute32, *AttrSize);
+ }
+ } else {
+ *AttrSize = 0;
+ if (Status == EFI_TIMEOUT) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c
new file mode 100644
index 00000000..d56dcc78
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c
@@ -0,0 +1,1196 @@
+/** @file
+
+ Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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,
+ EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO,
+ sizeof (UINTN)
+ },
+ { // ExtScsiPassThru
+ NULL,
+ UfsPassThruPassThru,
+ UfsPassThruGetNextTargetLun,
+ UfsPassThruBuildDevicePath,
+ UfsPassThruGetTargetLun,
+ UfsPassThruResetChannel,
+ UfsPassThruResetTargetLun,
+ UfsPassThruGetNextTarget
+ },
+ { // UfsDevConfig
+ UfsRwUfsDescriptor,
+ UfsRwUfsFlag,
+ UfsRwUfsAttribute
+ },
+ 0, // UfsHostController
+ 0, // UfsHcBase
+ {0, 0}, // UfsHcInfo
+ {NULL, NULL}, // UfsHcDriverInterface
+ 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
+ },
+ NULL, // TimerEvent
+ { // Queue
+ NULL,
+ NULL
+ }
+};
+
+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];
+
+GLOBAL_REMOVE_IF_UNREFERENCED EDKII_UFS_HC_PLATFORM_PROTOCOL *mUfsHcPlatform;
+
+/**
+ 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, Event);
+
+ 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 UFS_DEVICE_PATH
+ //
+ if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_UFS_DP) ||
+ (DevicePathNodeLength(DevicePath) != sizeof(UFS_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
+ )
+{
+ 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;
+}
+
+/**
+ Finishes device initialization by setting fDeviceInit flag and waiting untill device responds by
+ clearing it.
+
+ @param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+UfsFinishDeviceInitialization (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINT8 DeviceInitStatus;
+ UINT32 Timeout;
+
+ DeviceInitStatus = 0xFF;
+
+ //
+ // The host enables the device initialization completion by setting fDeviceInit flag.
+ //
+ Status = UfsSetFlag (Private, UfsFlagDevInit);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // There are cards that can take upto 600ms to clear fDeviceInit flag.
+ //
+ Timeout = UFS_INIT_COMPLETION_TIMEOUT;
+ do {
+ Status = UfsReadFlag (Private, UfsFlagDevInit, &DeviceInitStatus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ MicroSecondDelay (1);
+ Timeout--;
+ } while (DeviceInitStatus != 0 && Timeout != 0);
+
+ if (Timeout == 0) {
+ DEBUG ((DEBUG_ERROR, "UfsFinishDeviceInitialization DeviceInitStatus=%x EFI_TIMEOUT \n", DeviceInitStatus));
+ return EFI_TIMEOUT;
+ } else {
+ DEBUG ((DEBUG_INFO, "UfsFinishDeviceInitialization Timeout left=%x EFI_SUCCESS \n", Timeout));
+ 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_UNIT_DESC UnitDescriptor;
+ UFS_DEV_DESC DeviceDescriptor;
+ UINT32 UnitDescriptorSize;
+ UINT32 DeviceDescriptorSize;
+
+ Status = EFI_SUCCESS;
+ UfsHc = NULL;
+ Private = NULL;
+ UfsHcBase = 0;
+
+ DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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) {
+ DEBUG ((DEBUG_ERROR, "Unable to allocate Ufs Pass Thru private data\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ Private->ExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;
+ Private->UfsHostController = UfsHc;
+ Private->UfsHcBase = UfsHcBase;
+ Private->Handle = Controller;
+ Private->UfsHcDriverInterface.UfsHcProtocol = UfsHc;
+ Private->UfsHcDriverInterface.UfsExecUicCommand = UfsHcDriverInterfaceExecUicCommand;
+ InitializeListHead (&Private->Queue);
+
+ //
+ // This has to be done before initializing UfsHcInfo or calling the UfsControllerInit
+ //
+ if (mUfsHcPlatform == NULL) {
+ Status = gBS->LocateProtocol (&gEdkiiUfsHcPlatformProtocolGuid, NULL, (VOID**)&mUfsHcPlatform);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "No UfsHcPlatformProtocol present\n"));
+ }
+ }
+
+ Status = GetUfsHcInfo (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to initialize UfsHcInfo\n"));
+ goto Error;
+ }
+
+ //
+ // Initialize UFS Host Controller H/W.
+ //
+ Status = UfsControllerInit (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_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 ((DEBUG_ERROR, "Ufs Sending NOP IN command Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ Status = UfsFinishDeviceInitialization (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Device failed to finish initialization, Status = %r\n", Status));
+ goto Error;
+ }
+
+ //
+ // Check if 8 common luns are active and set corresponding bit mask.
+ //
+ UnitDescriptorSize = sizeof (UFS_UNIT_DESC);
+ for (Index = 0; Index < 8; Index++) {
+ Status = UfsRwDeviceDesc (Private, TRUE, UfsUnitDesc, (UINT8) Index, 0, &UnitDescriptor, &UnitDescriptorSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to read unit descriptor, index = %X, status = %r\n", Index, Status));
+ continue;
+ }
+ if (UnitDescriptor.LunEn == 0x1) {
+ DEBUG ((DEBUG_INFO, "UFS LUN %X is enabled\n", Index));
+ Private->Luns.BitMask |= (BIT0 << Index);
+ }
+ }
+
+ //
+ // Check if RPMB WLUN is supported and set corresponding bit mask.
+ //
+ DeviceDescriptorSize = sizeof (UFS_DEV_DESC);
+ Status = UfsRwDeviceDesc (Private, TRUE, UfsDeviceDesc, 0, 0, &DeviceDescriptor, &DeviceDescriptorSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to read device descriptor, status = %r\n", Status));
+ } else {
+ if (DeviceDescriptor.SecurityLun == 0x1) {
+ DEBUG ((DEBUG_INFO, "UFS WLUN RPMB is supported\n"));
+ Private->Luns.BitMask |= BIT11;
+ }
+ }
+
+ //
+ // Start the asynchronous interrupt monitor
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ ProcessAsyncTaskList,
+ Private,
+ &Private->TimerEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Ufs Create Async Tasks Event Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ Status = gBS->SetTimer (
+ Private->TimerEvent,
+ TimerPeriodic,
+ UFS_HC_ASYNC_TIMER
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Ufs Set Periodic Timer Error, Status = %r\n", Status));
+ goto Error;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &(Private->ExtScsiPassThru),
+ &gEfiUfsDeviceConfigProtocolGuid,
+ &(Private->UfsDevConfig),
+ NULL
+ );
+ 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);
+ }
+
+ if (Private->TimerEvent != NULL) {
+ gBS->CloseEvent (Private->TimerEvent);
+ }
+
+ 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;
+ UFS_PASS_THRU_TRANS_REQ *TransReq;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+
+ DEBUG ((DEBUG_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;
+
+ //
+ // Cleanup the resources of I/O requests in the async I/O queue
+ //
+ if (!IsListEmpty(&Private->Queue)) {
+ BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->Queue) {
+ TransReq = UFS_PASS_THRU_TRANS_REQ_FROM_THIS (Entry);
+
+ //
+ // TODO: Should find/add a proper host adapter return status for this
+ // case.
+ //
+ TransReq->Packet->HostAdapterStatus =
+ EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;
+
+ SignalCallerEvent (Private, TransReq);
+ }
+ }
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &(Private->ExtScsiPassThru),
+ &gEfiUfsDeviceConfigProtocolGuid,
+ &(Private->UfsDevConfig),
+ NULL
+ );
+
+ 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);
+ }
+
+ if (Private->TimerEvent != NULL) {
+ gBS->CloseEvent (Private->TimerEvent);
+ }
+
+ 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/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
new file mode 100644
index 00000000..b86ddabb
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
@@ -0,0 +1,999 @@
+/** @file
+
+ Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _UFS_PASS_THRU_H_
+#define _UFS_PASS_THRU_H_
+
+#include <Uefi.h>
+
+#include <Protocol/ScsiPassThruExt.h>
+#include <Protocol/UfsDeviceConfig.h>
+#include <Protocol/UfsHostController.h>
+#include <Protocol/UfsHostControllerPlatform.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/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
+#define UFS_INIT_COMPLETION_TIMEOUT 600000
+
+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;
+ EFI_UFS_DEVICE_CONFIG_PROTOCOL UfsDevConfig;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHostController;
+ UINTN UfsHcBase;
+ EDKII_UFS_HC_INFO UfsHcInfo;
+ EDKII_UFS_HC_DRIVER_INTERFACE UfsHcDriverInterface;
+
+ UINT8 TaskTag;
+
+ VOID *UtpTrlBase;
+ UINT8 Nutrs;
+ VOID *TrlMapping;
+ VOID *UtpTmrlBase;
+ UINT8 Nutmrs;
+ VOID *TmrlMapping;
+
+ UFS_EXPOSED_LUNS Luns;
+
+ //
+ // For Non-blocking operation.
+ //
+ EFI_EVENT TimerEvent;
+ LIST_ENTRY Queue;
+} UFS_PASS_THRU_PRIVATE_DATA;
+
+#define UFS_PASS_THRU_TRANS_REQ_SIG SIGNATURE_32 ('U', 'F', 'S', 'T')
+
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY TransferList;
+
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ UINT32 CmdDescSize;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ VOID *AlignedDataBuf;
+ UINTN AlignedDataBufSize;
+ VOID *DataBufMapping;
+
+ EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet;
+ UINT64 TimeoutRemain;
+ EFI_EVENT CallerEvent;
+} UFS_PASS_THRU_TRANS_REQ;
+
+#define UFS_PASS_THRU_TRANS_REQ_FROM_THIS(a) \
+ CR(a, UFS_PASS_THRU_TRANS_REQ, TransferList, UFS_PASS_THRU_TRANS_REQ_SIG)
+
+#define UFS_TIMEOUT EFI_TIMER_PERIOD_SECONDS(3)
+#define UFS_HC_ASYNC_TIMER EFI_TIMER_PERIOD_MILLISECONDS(1)
+
+#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 \
+ )
+
+#define UFS_PASS_THRU_PRIVATE_DATA_FROM_DEV_CONFIG(a) \
+ CR (a, \
+ UFS_PASS_THRU_PRIVATE_DATA, \
+ UfsDevConfig, \
+ UFS_PASS_THRU_SIG \
+ )
+
+#define UFS_PASS_THRU_PRIVATE_DATA_FROM_DRIVER_INTF(a) \
+ CR (a, \
+ UFS_PASS_THRU_PRIVATE_DATA, \
+ UfsHcDriverInterface, \
+ UFS_PASS_THRU_SIG \
+ )
+
+typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET {
+ UINT64 Timeout;
+ VOID *DataBuffer;
+ UINT8 Opcode;
+ UINT8 DescId;
+ UINT8 Index;
+ UINT8 Selector;
+ UINT32 TransferLength;
+ UINT8 DataDirection;
+} 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.
+ @param[in] 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_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,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+/**
+ 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 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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, out] DescSize The size of device descriptor buffer. On input, the size, in bytes,
+ of the data buffer specified by Descriptor. On output, the number
+ of bytes that were actually transferred.
+
+ @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 OUT UINT32 *DescSize
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ Call back function when the timer event is signaled.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the Event.
+
+**/
+VOID
+EFIAPI
+ProcessAsyncTaskList (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Internal helper function which will signal the caller event and clean up
+ resources.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data
+ structure.
+ @param[in] TransReq The pointer to the UFS_PASS_THRU_TRANS_REQ data
+ structure.
+
+**/
+VOID
+EFIAPI
+SignalCallerEvent (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UFS_PASS_THRU_TRANS_REQ *TransReq
+ );
+
+/**
+ Read or write specified device descriptor of a UFS device.
+
+ The function is used to read/write UFS device descriptors. The consumer of this API is
+ responsible for allocating the data buffer pointed by Descriptor.
+
+ @param[in] This The pointer to the EFI_UFS_DEVICE_CONFIG_PROTOCOL instance.
+ @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, out] DescSize The size of device descriptor buffer. On input, the size, in bytes,
+ of the data buffer specified by Descriptor. On output, the number
+ of bytes that were actually transferred.
+
+ @retval EFI_SUCCESS The device descriptor is read/written successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Descriptor is NULL or DescSize is NULL.
+ DescId, Index and Selector are invalid combination to point to a
+ type of UFS device descriptor.
+ @retval EFI_DEVICE_ERROR The device descriptor is not read/written successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsRwUfsDescriptor (
+ IN EFI_UFS_DEVICE_CONFIG_PROTOCOL *This,
+ IN BOOLEAN Read,
+ IN UINT8 DescId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT UINT8 *Descriptor,
+ IN OUT UINT32 *DescSize
+ );
+
+/**
+ Read or write specified flag of a UFS device.
+
+ The function is used to read/write UFS flag descriptors. The consumer of this API is responsible
+ for allocating the buffer pointed by Flag. The buffer size is 1 byte as UFS flag descriptor is
+ just a single Boolean value that represents a TRUE or FALSE, '0' or '1', ON or OFF type of value.
+
+ @param[in] This The pointer to the EFI_UFS_DEVICE_CONFIG_PROTOCOL instance.
+ @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] Flag The buffer to set or clear flag.
+
+ @retval EFI_SUCCESS The flag descriptor is set/clear successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Flag is NULL.
+ FlagId is an invalid UFS flag ID.
+ @retval EFI_DEVICE_ERROR The flag is not set/clear successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsRwUfsFlag (
+ IN EFI_UFS_DEVICE_CONFIG_PROTOCOL *This,
+ IN BOOLEAN Read,
+ IN UINT8 FlagId,
+ IN OUT UINT8 *Flag
+ );
+
+/**
+ Read or write specified attribute of a UFS device.
+
+ The function is used to read/write UFS attributes. The consumer of this API is responsible for
+ allocating the data buffer pointed by Attribute.
+
+ @param[in] This The pointer to the EFI_UFS_DEVICE_CONFIG_PROTOCOL instance.
+ @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] Attribute The buffer of Attribute to be read or written.
+ @param[in, out] AttrSize The size of Attribute buffer. On input, the size, in bytes, of the
+ data buffer specified by Attribute. On output, the number of bytes
+ that were actually transferred.
+
+ @retval EFI_SUCCESS The attribute is read/written successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Attribute is NULL or AttrSize is NULL.
+ AttrId, Index and Selector are invalid combination to point to a
+ type of UFS attribute.
+ @retval EFI_DEVICE_ERROR The attribute is not read/written successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+UfsRwUfsAttribute (
+ IN EFI_UFS_DEVICE_CONFIG_PROTOCOL *This,
+ IN BOOLEAN Read,
+ IN UINT8 AttrId,
+ IN UINT8 Index,
+ IN UINT8 Selector,
+ IN OUT UINT8 *Attribute,
+ IN OUT UINT32 *AttrSize
+ );
+
+/**
+ Execute UIC command.
+
+ @param[in] This Pointer to driver interface produced by the UFS controller.
+ @param[in, out] UicCommand Descriptor of the command that will be executed.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_INVALID_PARAMETER This or UicCommand is NULL.
+ @retval Others Command failed to execute.
+**/
+EFI_STATUS
+EFIAPI
+UfsHcDriverInterfaceExecUicCommand (
+ IN EDKII_UFS_HC_DRIVER_INTERFACE *This,
+ IN OUT EDKII_UIC_COMMAND *UicCommand
+ );
+
+/**
+ Initializes UfsHcInfo field in private data.
+
+ @param[in] Private Pointer to host controller private data.
+
+ @retval EFI_SUCCESS UfsHcInfo initialized successfully.
+ @retval Others Failed to initalize UfsHcInfo.
+**/
+EFI_STATUS
+GetUfsHcInfo (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ );
+
+extern EFI_COMPONENT_NAME_PROTOCOL gUfsPassThruComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2;
+extern EFI_DRIVER_BINDING_PROTOCOL gUfsPassThruDriverBinding;
+extern EDKII_UFS_HC_PLATFORM_PROTOCOL *mUfsHcPlatform;
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni
new file mode 100644
index 00000000..5aa4f587
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni
@@ -0,0 +1,17 @@
+// /** @file
+// The UfsPassThruDxe driver is used to provide support on accessing UFS device.
+//
+// It produces an EFI_EXT_SCSI_PASS_THRU_PROTOCOL interface for upper layer to send
+// SCSI cmd to UFS device.
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Used to provide support on accessing UFS device."
+
+#string STR_MODULE_DESCRIPTION #language en-US "It produces an EFI_EXT_SCSI_PASS_THRU_PROTOCOL interface for upper layer to send SCSI cmd to UFS device."
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf
new file mode 100644
index 00000000..c14e2fd9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf
@@ -0,0 +1,60 @@
+## @file
+# Description file for the Universal Flash Storage (UFS) Pass Thru driver.
+#
+# Copyright (c) 2014 - 2019, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[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 EBC
+#
+# DRIVER_BINDING = gUfsPassThruDriverBinding
+# COMPONENT_NAME = gUfsPassThruComponentName
+#
+
+[Sources]
+ ComponentName.c
+ UfsDevConfigProtocol.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
+ TimerLib
+
+[Protocols]
+ gEfiExtScsiPassThruProtocolGuid ## BY_START
+ gEfiUfsDeviceConfigProtocolGuid ## BY_START
+ gEdkiiUfsHostControllerProtocolGuid ## TO_START
+ gEdkiiUfsHcPlatformProtocolGuid ## SOMETIMES_CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ UfsPassThruExtra.uni
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni
new file mode 100644
index 00000000..dd6d3152
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// UfsPassThruDxe Localized Strings and Content
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UFS PassThru UEFI Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
new file mode 100644
index 00000000..958bf28e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
@@ -0,0 +1,2454 @@
+/** @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 - 2019, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "UfsPassThru.h"
+
+/**
+ Read 32bits data from specified UFS MMIO register.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Offset The offset within the UFS Host Controller MMIO space to start
+ the memory operation.
+ @param[out] Value The data buffer to store.
+
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+UfsMmioRead32 (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINTN Offset,
+ OUT UINT32 *Value
+ )
+{
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+ EFI_STATUS Status;
+
+ UfsHc = Private->UfsHostController;
+
+ Status = UfsHc->Read (UfsHc, EfiUfsHcWidthUint32, Offset, 1, Value);
+
+ return Status;
+}
+
+/**
+ Write 32bits data to specified UFS MMIO register.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Offset The offset within the UFS Host Controller MMIO space to start
+ the memory operation.
+ @param[in] Value The data to write.
+
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+UfsMmioWrite32 (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINTN Offset,
+ IN UINT32 Value
+ )
+{
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+ EFI_STATUS Status;
+
+ UfsHc = Private->UfsHostController;
+
+ Status = UfsHc->Write (UfsHc, EfiUfsHcWidthUint32, Offset, 1, &Value);
+
+ return Status;
+}
+
+/**
+ Wait for the value of the specified system memory set to the test value.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in] Offset The offset within the UFS Host Controller MMIO space to start
+ the memory operation.
+ @param[in] MaskValue The mask value of memory.
+ @param[in] TestValue The test value of memory.
+ @param[in] Timeout The time out value for wait memory set, uses 100ns as a unit.
+
+ @retval EFI_TIMEOUT The system memory setting is time out.
+ @retval EFI_SUCCESS The system memory is correct set.
+ @retval Others The operation fails.
+
+**/
+EFI_STATUS
+UfsWaitMemSet (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINTN Offset,
+ IN UINT32 MaskValue,
+ IN UINT32 TestValue,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Value;
+ UINT64 Delay;
+ BOOLEAN InfiniteWait;
+ EFI_STATUS Status;
+
+ 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.
+ //
+ Status = UfsMmioRead32 (Private, Offset, &Value);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Value &= 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 ((DEBUG_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x02:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n"));
+ break;
+ case 0x03:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x04:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x05:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - BAD_INDEX\n"));
+ break;
+ case 0x06:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n"));
+ break;
+ case 0x07:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n"));
+ break;
+ case 0x08:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n"));
+ break;
+ case 0x09:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - BUSY\n"));
+ break;
+ case 0x0A:
+ DEBUG ((DEBUG_VERBOSE, "UIC configuration command fails - DME_FAILURE\n"));
+ break;
+ default :
+ ASSERT (FALSE);
+ break;
+ }
+ } else {
+ switch (Result) {
+ case 0x00:
+ break;
+ case 0x01:
+ DEBUG ((DEBUG_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 ((DEBUG_VERBOSE, "Query Response with Parameter Not Readable\n"));
+ break;
+ case 0xF7:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Parameter Not Writeable\n"));
+ break;
+ case 0xF8:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Parameter Already Written\n"));
+ break;
+ case 0xF9:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Invalid Length\n"));
+ break;
+ case 0xFA:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Invalid Value\n"));
+ break;
+ case 0xFB:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Invalid Selector\n"));
+ break;
+ case 0xFC:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Invalid Index\n"));
+ break;
+ case 0xFD:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Invalid Idn\n"));
+ break;
+ case 0xFE:
+ DEBUG ((DEBUG_VERBOSE, "Query Response with Invalid Opcode\n"));
+ break;
+ case 0xFF:
+ DEBUG ((DEBUG_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;
+
+ ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
+ ASSERT ((BufferSize & (BIT1 | BIT0)) == 0);
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ 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);
+
+ SwapLittleEndianToBigEndian ((UINT8*)&DataSize, sizeof (UINT16));
+ QueryReq->DataSegLen = (UINT16)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->Ocs = UFS_HC_TRD_OCS_INIT_VALUE;
+ 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;
+ DataSize = Packet->TransferLength;
+ Data = Packet->DataBuffer;
+
+ if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) {
+ if (DataSize == 0 || Data == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ 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 = UFS_HC_TRD_OCS_INIT_VALUE;
+ 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->Ocs = UFS_HC_TRD_OCS_INIT_VALUE;
+ 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.
+ @retval EFI_NOT_READY No slot is available at this moment.
+
+**/
+EFI_STATUS
+UfsFindAvailableSlotInTrl (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ OUT UINT8 *Slot
+ )
+{
+ UINT8 Nutrs;
+ UINT8 Index;
+ UINT32 Data;
+ EFI_STATUS Status;
+
+ ASSERT ((Private != NULL) && (Slot != NULL));
+
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Nutrs = (UINT8)((Private->UfsHcInfo.Capabilities & UFS_HC_CAP_NUTRS) + 1);
+
+ for (Index = 0; Index < Nutrs; Index++) {
+ if ((Data & (BIT0 << Index)) == 0) {
+ *Slot = Index;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_READY;
+}
+
+
+/**
+ 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.
+
+**/
+EFI_STATUS
+UfsStartExecCmd (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Slot
+ )
+{
+ UINT32 Data;
+ EFI_STATUS Status;
+
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLRSR_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) {
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLRSR_OFFSET, UFS_HC_UTRLRSR);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLDBR_OFFSET, BIT0 << Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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.
+
+**/
+EFI_STATUS
+UfsStopExecCmd (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UINT8 Slot
+ )
+{
+ UINT32 Data;
+ EFI_STATUS Status;
+
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Data & (BIT0 << Slot)) != 0) {
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLCLR_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLCLR_OFFSET, Data & ~(BIT0 << Slot));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Extracts return data from query response upiu.
+
+ @param[in] Packet Pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET.
+ @param[in] QueryResp Pointer to the query response.
+
+ @retval EFI_INVALID_PARAMETER Packet or QueryResp are empty or opcode is invalid.
+ @retval EFI_DEVICE_ERROR Data returned from device is invalid.
+ @retval EFI_SUCCESS Data extracted.
+
+**/
+EFI_STATUS
+UfsGetReturnDataFromQueryResponse (
+ IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet,
+ IN UTP_QUERY_RESP_UPIU *QueryResp
+ )
+{
+ UINT16 ReturnDataSize;
+ UINT32 ReturnData;
+
+ if (Packet == NULL || QueryResp == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Packet->Opcode) {
+ case UtpQueryFuncOpcodeRdDesc:
+ ReturnDataSize = QueryResp->Tsf.Length;
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16));
+ //
+ // Make sure the hardware device does not return more data than expected.
+ //
+ if (ReturnDataSize > Packet->TransferLength) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (Packet->DataBuffer, (QueryResp + 1), ReturnDataSize);
+ Packet->TransferLength = ReturnDataSize;
+ break;
+ case UtpQueryFuncOpcodeWrDesc:
+ ReturnDataSize = QueryResp->Tsf.Length;
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16));
+ Packet->TransferLength = ReturnDataSize;
+ break;
+ case UtpQueryFuncOpcodeRdFlag:
+ case UtpQueryFuncOpcodeSetFlag:
+ case UtpQueryFuncOpcodeClrFlag:
+ case UtpQueryFuncOpcodeTogFlag:
+ //
+ // The 'FLAG VALUE' field is at byte offset 3 of QueryResp->Tsf.Value
+ //
+ *((UINT8*)(Packet->DataBuffer)) = *((UINT8*)&(QueryResp->Tsf.Value) + 3);
+ break;
+ case UtpQueryFuncOpcodeRdAttr:
+ case UtpQueryFuncOpcodeWrAttr:
+ ReturnData = QueryResp->Tsf.Value;
+ SwapLittleEndianToBigEndian ((UINT8*) &ReturnData, sizeof (UINT32));
+ CopyMem (Packet->DataBuffer, &ReturnData, sizeof (UINT32));
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Creates Transfer Request descriptor and sends Query Request to the device.
+
+ @param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA.
+ @param[in] Packet Pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET.
+
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.
+ @retval EFI_INVALID_PARAMETER The DescId, Index and Selector fields in Packet are invalid
+ combination to point to a type of UFS device descriptor.
+ @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
+UfsSendDmRequestRetry (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet
+ )
+{
+ UINT8 Slot;
+ UTP_TRD *Trd;
+ VOID *CmdDescHost;
+ VOID *CmdDescMapping;
+ UINT32 CmdDescSize;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+ UTP_QUERY_RESP_UPIU *QueryResp;
+ EFI_STATUS Status;
+
+ //
+ // 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)) {
+ DEBUG ((DEBUG_ERROR, "Failed to create DM command descriptor\n"));
+ return Status;
+ }
+
+ 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.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_UTRLDBR_OFFSET, BIT0, 0, Packet->Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (Trd->Ocs != 0 || QueryResp->QueryResp != UfsUtpQueryResponseSuccess) {
+ DEBUG ((DEBUG_ERROR, "Failed to send query request, OCS = %X, QueryResp = %X\n", Trd->Ocs, QueryResp->QueryResp));
+ DumpQueryResponseResult (QueryResp->QueryResp);
+
+ if ((QueryResp->QueryResp == UfsUtpQueryResponseInvalidSelector) ||
+ (QueryResp->QueryResp == UfsUtpQueryResponseInvalidIndex) ||
+ (QueryResp->QueryResp == UfsUtpQueryResponseInvalidIdn)) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+ goto Exit;
+ }
+
+ Status = UfsGetReturnDataFromQueryResponse (Packet, QueryResp);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to get return data from query response\n"));
+ goto Exit;
+ }
+
+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 Query Request to the device. Query is sent until device responds correctly or counter runs out.
+
+ @param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA.
+ @param[in] Packet Pointer to the UFS_DEVICE_MANAGEMENT_PACKET.
+
+ @retval EFI_SUCCESS The device responded correctly to the Query request.
+ @retval EFI_INVALID_PARAMETER The DescId, Index and Selector fields in Packet are invalid
+ combination to point to a type of UFS device descriptor.
+ @retval EFI_DEVICE_ERROR A device error occurred while waiting for the response from the device.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of the operation.
+
+**/
+EFI_STATUS
+UfsSendDmRequest (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Retry;
+
+ Status = EFI_SUCCESS;
+
+ for (Retry = 0; Retry < 5; Retry ++) {
+ Status = UfsSendDmRequestRetry (Private, Packet);
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+ }
+
+ DEBUG ((DEBUG_ERROR, "Failed to get response from the device after %d retries\n", Retry));
+ return Status;
+}
+
+/**
+ 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, out] DescSize The size of device descriptor buffer. On input, the size, in bytes,
+ of the data buffer specified by Descriptor. On output, the number
+ of bytes that were actually transferred.
+
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.
+ @retval EFI_INVALID_PARAMETER DescId, Index and Selector are invalid combination to point to a
+ type of UFS device descriptor.
+ @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 OUT UINT32 *DescSize
+ )
+{
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+ EFI_STATUS Status;
+
+ if (DescSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ Packet.DataDirection = UfsDataIn;
+ Packet.Opcode = UtpQueryFuncOpcodeRdDesc;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ Packet.Opcode = UtpQueryFuncOpcodeWrDesc;
+ }
+ Packet.DataBuffer = Descriptor;
+ Packet.TransferLength = *DescSize;
+ Packet.DescId = DescId;
+ Packet.Index = Index;
+ Packet.Selector = Selector;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ Status = UfsSendDmRequest (Private, &Packet);
+ if (EFI_ERROR (Status)) {
+ *DescSize = 0;
+ } else {
+ *DescSize = Packet.TransferLength;
+ }
+
+ 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_INVALID_PARAMETER AttrId, Index and Selector are invalid combination to point to a
+ type of UFS device descriptor.
+ @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
+ )
+{
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));
+
+ if (Read) {
+ Packet.DataDirection = UfsDataIn;
+ Packet.Opcode = UtpQueryFuncOpcodeRdAttr;
+ } else {
+ Packet.DataDirection = UfsDataOut;
+ Packet.Opcode = UtpQueryFuncOpcodeWrAttr;
+ }
+ Packet.DataBuffer = Attributes;
+ Packet.DescId = AttrId;
+ Packet.Index = Index;
+ Packet.Selector = Selector;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ return UfsSendDmRequest (Private, &Packet);
+}
+
+/**
+ 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_INVALID_PARAMETER FlagId is an invalid UFS flag ID.
+ @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
+ )
+{
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;
+
+ 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.DataBuffer = Value;
+ Packet.DescId = FlagId;
+ Packet.Index = 0;
+ Packet.Selector = 0;
+ Packet.Timeout = UFS_TIMEOUT;
+
+ return UfsSendDmRequest (Private, &Packet);
+}
+
+/**
+ 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;
+}
+
+/**
+ 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;
+ 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.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_UTRLDBR_OFFSET, BIT0 << Slot, 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;
+}
+
+/**
+ Cleanup data buffers after data transfer. This function
+ also takes care to copy all data to user memory pool for
+ unaligned data transfers.
+
+ @param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA
+ @param[in] TransReq Pointer to the transfer request
+**/
+VOID
+UfsReconcileDataTransferBuffer (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UFS_PASS_THRU_TRANS_REQ *TransReq
+ )
+{
+ if (TransReq->DataBufMapping != NULL) {
+ Private->UfsHostController->Unmap (
+ Private->UfsHostController,
+ TransReq->DataBufMapping
+ );
+ }
+
+ //
+ // Check if unaligned transfer was performed. If it was and we read
+ // data from device copy memory to user data buffers before cleanup.
+ // The assumption is if auxiliary aligned data buffer is not NULL then
+ // unaligned transfer has been performed.
+ //
+ if (TransReq->AlignedDataBuf != NULL) {
+ if (TransReq->Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+ CopyMem (TransReq->Packet->InDataBuffer, TransReq->AlignedDataBuf, TransReq->Packet->InTransferLength);
+ }
+ //
+ // Wipe out the transfer buffer in case it contains sensitive data.
+ //
+ ZeroMem (TransReq->AlignedDataBuf, TransReq->AlignedDataBufSize);
+ FreeAlignedPages (TransReq->AlignedDataBuf, EFI_SIZE_TO_PAGES (TransReq->AlignedDataBufSize));
+ TransReq->AlignedDataBuf = NULL;
+ }
+}
+
+/**
+ Prepare data buffer for transfer.
+
+ @param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA
+ @param[in, out] TransReq Pointer to the transfer request
+
+ @retval EFI_DEVICE_ERROR Failed to prepare buffer for transfer
+ @retval EFI_SUCCESS Buffer ready for transfer
+**/
+EFI_STATUS
+UfsPrepareDataTransferBuffer (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN OUT UFS_PASS_THRU_TRANS_REQ *TransReq
+ )
+{
+ EFI_STATUS Status;
+ VOID *DataBuf;
+ UINT32 DataLen;
+ UINTN MapLength;
+ EFI_PHYSICAL_ADDRESS DataBufPhyAddr;
+ EDKII_UFS_HOST_CONTROLLER_OPERATION Flag;
+ UTP_TR_PRD *PrdtBase;
+
+ DataBufPhyAddr = 0;
+ DataBuf = NULL;
+
+ //
+ // For unaligned data transfers we allocate auxiliary DWORD aligned memory pool.
+ // When command is finished auxiliary memory pool is copied into actual user memory.
+ // This is requiered to assure data transfer safety(DWORD alignment required by UFS spec.)
+ //
+ if (TransReq->Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+ if (((UINTN)TransReq->Packet->InDataBuffer % 4 != 0) || (TransReq->Packet->InTransferLength % 4 != 0)) {
+ DataLen = TransReq->Packet->InTransferLength + (4 - (TransReq->Packet->InTransferLength % 4));
+ DataBuf = AllocateAlignedPages (EFI_SIZE_TO_PAGES (DataLen), 4);
+ if (DataBuf == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ ZeroMem (DataBuf, DataLen);
+ TransReq->AlignedDataBuf = DataBuf;
+ TransReq->AlignedDataBufSize = DataLen;
+ } else {
+ DataLen = TransReq->Packet->InTransferLength;
+ DataBuf = TransReq->Packet->InDataBuffer;
+ }
+ Flag = EdkiiUfsHcOperationBusMasterWrite;
+ } else {
+ if (((UINTN)TransReq->Packet->OutDataBuffer % 4 != 0) || (TransReq->Packet->OutTransferLength % 4 != 0)) {
+ DataLen = TransReq->Packet->OutTransferLength + (4 - (TransReq->Packet->OutTransferLength % 4));
+ DataBuf = AllocateAlignedPages (EFI_SIZE_TO_PAGES (DataLen), 4);
+ if (DataBuf == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ CopyMem (DataBuf, TransReq->Packet->OutDataBuffer, TransReq->Packet->OutTransferLength);
+ TransReq->AlignedDataBuf = DataBuf;
+ TransReq->AlignedDataBufSize = DataLen;
+ } else {
+ DataLen = TransReq->Packet->OutTransferLength;
+ DataBuf = TransReq->Packet->OutDataBuffer;
+ }
+ Flag = EdkiiUfsHcOperationBusMasterRead;
+ }
+
+ if (DataLen != 0) {
+ MapLength = DataLen;
+ Status = Private->UfsHostController->Map (
+ Private->UfsHostController,
+ Flag,
+ DataBuf,
+ &MapLength,
+ &DataBufPhyAddr,
+ &TransReq->DataBufMapping
+ );
+
+ if (EFI_ERROR (Status) || (DataLen != MapLength)) {
+ if (TransReq->AlignedDataBuf != NULL) {
+ //
+ // Wipe out the transfer buffer in case it contains sensitive data.
+ //
+ ZeroMem (TransReq->AlignedDataBuf, TransReq->AlignedDataBufSize);
+ FreeAlignedPages (TransReq->AlignedDataBuf, EFI_SIZE_TO_PAGES (TransReq->AlignedDataBufSize));
+ TransReq->AlignedDataBuf = NULL;
+ }
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Fill PRDT table of Command UPIU for executed SCSI cmd.
+ //
+ PrdtBase = (UTP_TR_PRD*)((UINT8*)TransReq->CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));
+ ASSERT (PrdtBase != NULL);
+ UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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.
+ @param[in] 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_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,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UTP_RESPONSE_UPIU *Response;
+ UINT16 SenseDataLen;
+ UINT32 ResTranCount;
+ EFI_TPL OldTpl;
+ UFS_PASS_THRU_TRANS_REQ *TransReq;
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+
+ TransReq = AllocateZeroPool (sizeof (UFS_PASS_THRU_TRANS_REQ));
+ if (TransReq == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TransReq->Signature = UFS_PASS_THRU_TRANS_REQ_SIG;
+ TransReq->TimeoutRemain = Packet->Timeout;
+ TransReq->Packet = Packet;
+
+ UfsHc = Private->UfsHostController;
+ //
+ // Find out which slot of transfer request list is available.
+ //
+ Status = UfsFindAvailableSlotInTrl (Private, &TransReq->Slot);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ TransReq->Trd = ((UTP_TRD*)Private->UtpTrlBase) + TransReq->Slot;
+
+ //
+ // Fill transfer request descriptor to this slot.
+ //
+ Status = UfsCreateScsiCommandDesc (
+ Private,
+ Lun,
+ Packet,
+ TransReq->Trd,
+ &TransReq->CmdDescHost,
+ &TransReq->CmdDescMapping
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ TransReq->CmdDescSize = TransReq->Trd->PrdtO * sizeof (UINT32) + TransReq->Trd->PrdtL * sizeof (UTP_TR_PRD);
+
+ Status = UfsPrepareDataTransferBuffer (Private, TransReq);
+ if (EFI_ERROR (Status)) {
+ goto Exit1;
+ }
+
+ //
+ // Insert the async SCSI cmd to the Async I/O list
+ //
+ if (Event != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ TransReq->CallerEvent = Event;
+ InsertTailList (&Private->Queue, &TransReq->TransferList);
+ gBS->RestoreTPL (OldTpl);
+ }
+
+ //
+ // Start to execute the transfer request.
+ //
+ UfsStartExecCmd (Private, TransReq->Slot);
+
+ //
+ // Immediately return for async I/O.
+ //
+ if (Event != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Wait for the completion of the transfer request.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_UTRLDBR_OFFSET, BIT0 << TransReq->Slot, 0, Packet->Timeout);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get sense data if exists
+ //
+ Response = (UTP_RESPONSE_UPIU*)((UINT8*)TransReq->CmdDescHost + TransReq->Trd->RuO * sizeof (UINT32));
+ ASSERT (Response != NULL);
+ SenseDataLen = Response->SenseDataLen;
+ SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));
+
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) {
+ //
+ // Make sure the hardware device does not return more data than expected.
+ //
+ if (SenseDataLen <= Packet->SenseDataLength) {
+ CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen);
+ Packet->SenseDataLength = (UINT8)SenseDataLen;
+ } else {
+ Packet->SenseDataLength = 0;
+ }
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ Packet->TargetStatus = Response->Status;
+ if (Response->Response != 0) {
+ DEBUG ((DEBUG_ERROR, "UfsExecScsiCmds() fails with Target Failure\n"));
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ if (TransReq->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, TransReq->Slot);
+
+ UfsReconcileDataTransferBuffer (Private, TransReq);
+
+Exit1:
+ if (TransReq->CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);
+ }
+ if (TransReq->CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (TransReq->CmdDescSize), TransReq->CmdDescHost);
+ }
+ if (TransReq != NULL) {
+ FreePool (TransReq);
+ }
+ return Status;
+}
+
+/**
+ Send UIC command.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.
+ @param[in, out] UicCommand UIC command descriptor. On exit contains UIC command results.
+
+ @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.
+
+**/
+EFI_STATUS
+UfsExecUicCommands (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN OUT EDKII_UIC_COMMAND *UicCommand
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+
+ Status = UfsMmioRead32 (Private, UFS_HC_IS_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) {
+ //
+ // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_IS_OFFSET, Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // 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.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UCMD_ARG1_OFFSET, UicCommand->Arg1);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UCMD_ARG2_OFFSET, UicCommand->Arg2);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UCMD_ARG3_OFFSET, UicCommand->Arg3);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Host software shall only set the UICCMD if HCS.UCRDY is set to 1.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_STATUS_OFFSET, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UIC_CMD_OFFSET, UicCommand->Opcode);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // 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.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_IS_OFFSET, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (UicCommand->Opcode != UfsUicDmeReset) {
+ Status = UfsMmioRead32 (Private, UFS_HC_UCMD_ARG2_OFFSET, &UicCommand->Arg2);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = UfsMmioRead32 (Private, UFS_HC_UCMD_ARG3_OFFSET, &UicCommand->Arg3);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if ((UicCommand->Arg2 & 0xFF) != 0) {
+ DEBUG_CODE_BEGIN();
+ DumpUicCmdExecResult ((UINT8)UicCommand->Opcode, (UINT8)(UicCommand->Arg2 & 0xFF));
+ DEBUG_CODE_END();
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ 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->UfsHcInfo.Capabilities & UFS_HC_CAP_64ADDR) == UFS_HC_CAP_64ADDR) {
+ Is32BitAddr = FALSE;
+ } else {
+ Is32BitAddr = TRUE;
+ }
+
+ 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;
+ UINT32 Data;
+
+ if (mUfsHcPlatform != NULL && mUfsHcPlatform->Callback != NULL) {
+ Status = mUfsHcPlatform->Callback (Private->Handle, EdkiiUfsHcPreHce, &Private->UfsHcDriverInterface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failure from platform driver during EdkiiUfsHcPreHce, Status = %r\n", Status));
+ return Status;
+ }
+ }
+
+ //
+ // UFS 2.0 spec section 7.1.1 - Host Controller Initialization
+ //
+ // Reinitialize the UFS host controller if HCE bit of HC register is set.
+ //
+ Status = UfsMmioRead32 (Private, UFS_HC_ENABLE_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) {
+ //
+ // Write a 0 to the HCE register at first to disable the host controller.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_ENABLE_OFFSET, 0);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Wait until HCE is read as '0' before continuing.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_ENABLE_OFFSET, 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.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_ENABLE_OFFSET, UFS_HC_HCE_EN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Wait until HCE is read as '1' before continuing.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_ENABLE_OFFSET, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (mUfsHcPlatform != NULL && mUfsHcPlatform->Callback != NULL) {
+ Status = mUfsHcPlatform->Callback (Private->Handle, EdkiiUfsHcPostHce, &Private->UfsHcDriverInterface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failure from platform driver during EdkiiUfsHcPostHce, Status = %r\n", Status));
+ return Status;
+ }
+ }
+
+ 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;
+ UINT32 Data;
+ EDKII_UIC_COMMAND LinkStartupCommand;
+
+ if (mUfsHcPlatform != NULL && mUfsHcPlatform->Callback != NULL) {
+ Status = mUfsHcPlatform->Callback (Private->Handle, EdkiiUfsHcPreLinkStartup, &Private->UfsHcDriverInterface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failure from platform driver during EdkiiUfsHcPreLinkStartup, Status = %r\n", Status));
+ return Status;
+ }
+ }
+
+ //
+ // Start UFS device detection.
+ // Try up to 3 times for establishing data link with device.
+ //
+ for (Retry = 0; Retry < 3; Retry++) {
+ LinkStartupCommand.Opcode = UfsUicDmeLinkStartup;
+ LinkStartupCommand.Arg1 = 0;
+ LinkStartupCommand.Arg2 = 0;
+ LinkStartupCommand.Arg3 = 0;
+ Status = UfsExecUicCommands (Private, &LinkStartupCommand);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UfsMmioRead32 (Private, UFS_HC_STATUS_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((Data & UFS_HC_HCS_DP) == 0) {
+ Status = UfsWaitMemSet (Private, UFS_HC_IS_OFFSET, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ } else {
+ if (mUfsHcPlatform != NULL && mUfsHcPlatform->Callback != NULL) {
+ Status = mUfsHcPlatform->Callback (Private->Handle, EdkiiUfsHcPostLinkStartup, &Private->UfsHcDriverInterface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failure from platform driver during EdkiiUfsHcPostLinkStartup, Status = %r\n", Status));
+ return Status;
+ }
+ }
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ 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
+ )
+{
+ 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;
+
+ //
+ // Allocate and initialize UTP Task Management Request List.
+ //
+ Nutmrs = (UINT8) (RShiftU64 ((Private->UfsHcInfo.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.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTMRLBA_OFFSET, (UINT32)(UINTN)CmdDescPhyAddr);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTMRLBAU_OFFSET, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ 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'.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTMRLRSR_OFFSET, UFS_HC_UTMRLRSR);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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
+ )
+{
+ 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;
+
+ //
+ // Allocate and initialize UTP Transfer Request List.
+ //
+ Nutrs = (UINT8)((Private->UfsHcInfo.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.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLBA_OFFSET, (UINT32)(UINTN)CmdDescPhyAddr);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLBAU_OFFSET, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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'.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLRSR_OFFSET, UFS_HC_UTRLRSR);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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 ((DEBUG_ERROR, "UfsControllerInit: Enable Host Controller Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsDeviceDetection (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "UfsControllerInit: Device Detection Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsInitTaskManagementRequestList (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "UfsControllerInit: Task management list initialization Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = UfsInitTransferRequestList (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "UfsControllerInit: Transfer list initialization Fails, Status = %r\n", Status));
+ return Status;
+ }
+
+ DEBUG ((DEBUG_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;
+ UINT32 Data;
+
+ //
+ // Enable the UTP Task Management Request List by setting the UTP Task Management
+ // Request List RunStop Register (UTMRLRSR) to '1'.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTMRLRSR_OFFSET, 0);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List
+ // RunStop Register (UTRLRSR) to '1'.
+ //
+ Status = UfsMmioWrite32 (Private, UFS_HC_UTRLRSR_OFFSET, 0);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Write a 0 to the HCE register in order to disable the host controller.
+ //
+ Status = UfsMmioRead32 (Private, UFS_HC_ENABLE_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ASSERT ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN);
+
+ Status = UfsMmioWrite32 (Private, UFS_HC_ENABLE_OFFSET, 0);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Wait until HCE is read as '0' before continuing.
+ //
+ Status = UfsWaitMemSet (Private, UFS_HC_ENABLE_OFFSET, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((DEBUG_INFO, "UfsController is stopped\n"));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Internal helper function which will signal the caller event and clean up
+ resources.
+
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data
+ structure.
+ @param[in] TransReq The pointer to the UFS_PASS_THRU_TRANS_REQ data
+ structure.
+
+**/
+VOID
+EFIAPI
+SignalCallerEvent (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,
+ IN UFS_PASS_THRU_TRANS_REQ *TransReq
+ )
+{
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
+ EFI_EVENT CallerEvent;
+
+ ASSERT ((Private != NULL) && (TransReq != NULL));
+
+ UfsHc = Private->UfsHostController;
+ CallerEvent = TransReq->CallerEvent;
+
+ RemoveEntryList (&TransReq->TransferList);
+
+ UfsHc->Flush (UfsHc);
+
+ UfsStopExecCmd (Private, TransReq->Slot);
+
+ UfsReconcileDataTransferBuffer (Private, TransReq);
+
+ if (TransReq->CmdDescMapping != NULL) {
+ UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);
+ }
+ if (TransReq->CmdDescHost != NULL) {
+ UfsHc->FreeBuffer (
+ UfsHc,
+ EFI_SIZE_TO_PAGES (TransReq->CmdDescSize),
+ TransReq->CmdDescHost
+ );
+ }
+
+ FreePool (TransReq);
+
+ gBS->SignalEvent (CallerEvent);
+ return;
+}
+
+/**
+ Call back function when the timer event is signaled.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the Event.
+
+**/
+VOID
+EFIAPI
+ProcessAsyncTaskList (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ UFS_PASS_THRU_TRANS_REQ *TransReq;
+ EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet;
+ UTP_RESPONSE_UPIU *Response;
+ UINT16 SenseDataLen;
+ UINT32 ResTranCount;
+ UINT32 SlotsMap;
+ UINT32 Value;
+ EFI_STATUS Status;
+
+ Private = (UFS_PASS_THRU_PRIVATE_DATA*) Context;
+ SlotsMap = 0;
+
+ //
+ // Check the entries in the async I/O queue are done or not.
+ //
+ if (!IsListEmpty(&Private->Queue)) {
+ BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->Queue) {
+ TransReq = UFS_PASS_THRU_TRANS_REQ_FROM_THIS (Entry);
+ Packet = TransReq->Packet;
+
+ if ((SlotsMap & (BIT0 << TransReq->Slot)) != 0) {
+ return;
+ }
+ SlotsMap |= BIT0 << TransReq->Slot;
+
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Value);
+ if (EFI_ERROR (Status)) {
+ //
+ // TODO: Should find/add a proper host adapter return status for this
+ // case.
+ //
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;
+ DEBUG ((DEBUG_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p UfsMmioRead32() Error.\n", TransReq->CallerEvent));
+ SignalCallerEvent (Private, TransReq);
+ continue;
+ }
+
+ if ((Value & (BIT0 << TransReq->Slot)) != 0) {
+ //
+ // Scsi cmd not finished yet.
+ //
+ if (TransReq->TimeoutRemain > UFS_HC_ASYNC_TIMER) {
+ TransReq->TimeoutRemain -= UFS_HC_ASYNC_TIMER;
+ continue;
+ } else {
+ //
+ // Timeout occurs.
+ //
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;
+ DEBUG ((DEBUG_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT.\n", TransReq->CallerEvent));
+ SignalCallerEvent (Private, TransReq);
+ continue;
+ }
+ } else {
+ //
+ // Scsi cmd finished.
+ //
+ // Get sense data if exists
+ //
+ Response = (UTP_RESPONSE_UPIU*)((UINT8*)TransReq->CmdDescHost + TransReq->Trd->RuO * sizeof (UINT32));
+ ASSERT (Response != NULL);
+ SenseDataLen = Response->SenseDataLen;
+ SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));
+
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) {
+ //
+ // Make sure the hardware device does not return more data than expected.
+ //
+ if (SenseDataLen <= Packet->SenseDataLength) {
+ CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen);
+ Packet->SenseDataLength = (UINT8)SenseDataLen;
+ } else {
+ Packet->SenseDataLength = 0;
+ }
+ }
+
+ //
+ // Check the transfer request result.
+ //
+ Packet->TargetStatus = Response->Status;
+ if (Response->Response != 0) {
+ DEBUG ((DEBUG_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Target Failure.\n", TransReq->CallerEvent));
+ SignalCallerEvent (Private, TransReq);
+ continue;
+ }
+
+ if (TransReq->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 {
+ DEBUG ((DEBUG_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Target Device Error.\n", TransReq->CallerEvent));
+ SignalCallerEvent (Private, TransReq);
+ continue;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Success.\n", TransReq->CallerEvent));
+ SignalCallerEvent (Private, TransReq);
+ }
+ }
+ }
+}
+
+/**
+ Execute UIC command.
+
+ @param[in] This Pointer to driver interface produced by the UFS controller.
+ @param[in, out] UicCommand Descriptor of the command that will be executed.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_INVALID_PARAMETER This or UicCommand is NULL.
+ @retval Others Command failed to execute.
+**/
+EFI_STATUS
+EFIAPI
+UfsHcDriverInterfaceExecUicCommand (
+ IN EDKII_UFS_HC_DRIVER_INTERFACE *This,
+ IN OUT EDKII_UIC_COMMAND *UicCommand
+ )
+{
+ UFS_PASS_THRU_PRIVATE_DATA *Private;
+
+ if (This == NULL || UicCommand == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_DRIVER_INTF (This);
+
+ return UfsExecUicCommands (Private, UicCommand);
+}
+
+/**
+ Initializes UfsHcInfo field in private data.
+
+ @param[in] Private Pointer to host controller private data.
+
+ @retval EFI_SUCCESS UfsHcInfo initialized successfully.
+ @retval Others Failed to initalize UfsHcInfo.
+**/
+EFI_STATUS
+GetUfsHcInfo (
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private
+ )
+{
+ UINT32 Data;
+ EFI_STATUS Status;
+
+ Status = UfsMmioRead32 (Private, UFS_HC_VER_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private->UfsHcInfo.Version = Data;
+
+ Status = UfsMmioRead32 (Private, UFS_HC_CAP_OFFSET, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private->UfsHcInfo.Capabilities = Data;
+
+ if (mUfsHcPlatform != NULL && mUfsHcPlatform->OverrideHcInfo != NULL) {
+ Status = mUfsHcPlatform->OverrideHcInfo (Private->Handle, &Private->UfsHcInfo);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failure from platform on OverrideHcInfo, Status = %r\n", Status));
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h
new file mode 100644
index 00000000..b4d3be7b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/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 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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
+
+//
+// The initial value of the OCS field of UTP TRD or TMRD descriptor
+// defined in JEDEC JESD223 specification
+//
+#define UFS_HC_TRD_OCS_INIT_VALUE 0x0F
+
+//
+// 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
+