+/** @file\r
+ UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface\r
+ for upper layer application to execute UFS-supported SCSI cmds.\r
+\r
+ Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "UfsPassThru.h"\r
+\r
+/**\r
+ Wait for the value of the specified system memory set to the test value.\r
+\r
+ @param Address The system memory address to test.\r
+ @param MaskValue The mask value of memory.\r
+ @param TestValue The test value of memory.\r
+ @param Timeout The time out value for wait memory set, uses 100ns as a unit.\r
+\r
+ @retval EFI_TIMEOUT The system memory setting is time out.\r
+ @retval EFI_SUCCESS The system memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+UfsWaitMemSet (\r
+ IN UINTN Address,\r
+ IN UINT32 MaskValue,\r
+ IN UINT32 TestValue,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Value;\r
+ UINT64 Delay;\r
+ BOOLEAN InfiniteWait;\r
+\r
+ if (Timeout == 0) {\r
+ InfiniteWait = TRUE;\r
+ } else {\r
+ InfiniteWait = FALSE;\r
+ }\r
+\r
+ Delay = DivU64x32 (Timeout, 10) + 1;\r
+\r
+ do {\r
+ //\r
+ // Access PCI MMIO space to see if the value is the tested one.\r
+ //\r
+ Value = MmioRead32 (Address) & MaskValue;\r
+\r
+ if (Value == TestValue) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Stall for 1 microseconds.\r
+ //\r
+ MicroSecondDelay (1);\r
+\r
+ Delay--;\r
+\r
+ } while (InfiniteWait || (Delay > 0));\r
+\r
+ return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+ Dump UIC command execution result for debugging.\r
+\r
+ @param[in] UicOpcode The executed UIC opcode.\r
+ @param[in] Result The result to be parsed.\r
+\r
+**/\r
+VOID\r
+DumpUicCmdExecResult (\r
+ IN UINT8 UicOpcode,\r
+ IN UINT8 Result\r
+ )\r
+{\r
+ if (UicOpcode <= UfsUicDmePeerSet) {\r
+ switch (Result) {\r
+ case 0x00:\r
+ break;\r
+ case 0x01:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n"));\r
+ break;\r
+ case 0x02:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n"));\r
+ break;\r
+ case 0x03:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n"));\r
+ break;\r
+ case 0x04:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n"));\r
+ break;\r
+ case 0x05:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_INDEX\n"));\r
+ break;\r
+ case 0x06:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n"));\r
+ break;\r
+ case 0x07:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n"));\r
+ break;\r
+ case 0x08:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n"));\r
+ break; \r
+ case 0x09:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BUSY\n"));\r
+ break;\r
+ case 0x0A:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - DME_FAILURE\n"));\r
+ break; \r
+ default :\r
+ ASSERT (FALSE);\r
+ break;\r
+ }\r
+ } else {\r
+ switch (Result) {\r
+ case 0x00:\r
+ break;\r
+ case 0x01:\r
+ DEBUG ((EFI_D_VERBOSE, "UIC control command fails - FAILURE\n"));\r
+ break; \r
+ default :\r
+ ASSERT (FALSE);\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Dump QUERY RESPONSE UPIU result for debugging.\r
+\r
+ @param[in] Result The result to be parsed.\r
+\r
+**/\r
+VOID\r
+DumpQueryResponseResult (\r
+ IN UINT8 Result\r
+ )\r
+{\r
+ switch (Result) {\r
+ case 0xF6:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Readable\n"));\r
+ break;\r
+ case 0xF7:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Writeable\n"));\r
+ break;\r
+ case 0xF8:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Already Written\n"));\r
+ break;\r
+ case 0xF9:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Length\n"));\r
+ break;\r
+ case 0xFA:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Value\n"));\r
+ break;\r
+ case 0xFB:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Selector\n"));\r
+ break;\r
+ case 0xFC:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Index\n"));\r
+ break;\r
+ case 0xFD:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Idn\n"));\r
+ break;\r
+ case 0xFE:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Opcode\n"));\r
+ break; \r
+ case 0xFF:\r
+ DEBUG ((EFI_D_VERBOSE, "Query Response with General Failure\n"));\r
+ break;\r
+ default :\r
+ ASSERT (FALSE);\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ Swap little endian to big endian.\r
+\r
+ @param[in, out] Buffer The data buffer. In input, it contains little endian data.\r
+ In output, it will become big endian.\r
+ @param[in] BufferSize The length of converted data.\r
+\r
+**/\r
+VOID\r
+SwapLittleEndianToBigEndian (\r
+ IN OUT UINT8 *Buffer,\r
+ IN UINT32 BufferSize\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT8 Temp;\r
+ UINT32 SwapCount;\r
+\r
+ SwapCount = BufferSize / 2;\r
+ for (Index = 0; Index < SwapCount; Index++) {\r
+ Temp = Buffer[Index];\r
+ Buffer[Index] = Buffer[BufferSize - 1 - Index];\r
+ Buffer[BufferSize - 1 - Index] = Temp;\r
+ }\r
+}\r
+\r
+/**\r
+ Fill TSF field of QUERY REQUEST UPIU.\r
+\r
+ @param[in, out] TsfBase The base address of TSF field of QUERY REQUEST UPIU.\r
+ @param[in] Opcode The opcode of request.\r
+ @param[in] DescId The descriptor ID of request.\r
+ @param[in] Index The index of request.\r
+ @param[in] Selector The selector of request.\r
+ @param[in] Length The length of transferred data. The maximum is 4.\r
+ @param[in] Value The value of transferred data.\r
+\r
+**/\r
+VOID\r
+UfsFillTsfOfQueryReqUpiu (\r
+ IN OUT UTP_UPIU_TSF *TsfBase,\r
+ IN UINT8 Opcode,\r
+ IN UINT8 DescId OPTIONAL,\r
+ IN UINT8 Index OPTIONAL,\r
+ IN UINT8 Selector OPTIONAL,\r
+ IN UINT16 Length OPTIONAL,\r
+ IN UINT32 Value OPTIONAL\r
+ )\r
+{\r
+ ASSERT (TsfBase != NULL);\r
+ ASSERT (Opcode <= UtpQueryFuncOpcodeTogFlag);\r
+\r
+ TsfBase->Opcode = Opcode;\r
+ if (Opcode != UtpQueryFuncOpcodeNop) {\r
+ TsfBase->DescId = DescId;\r
+ TsfBase->Index = Index;\r
+ TsfBase->Selector = Selector;\r
+\r
+ if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) {\r
+ SwapLittleEndianToBigEndian ((UINT8*)&Length, sizeof (Length));\r
+ TsfBase->Length = Length;\r
+ }\r
+ \r
+ if (Opcode == UtpQueryFuncOpcodeWrAttr) {\r
+ SwapLittleEndianToBigEndian ((UINT8*)&Value, sizeof (Value));\r
+ TsfBase->Value = Value;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Initialize COMMAND UPIU.\r
+\r
+ @param[in, out] Command The base address of COMMAND UPIU.\r
+ @param[in] Lun The Lun on which the SCSI command is executed.\r
+ @param[in] TaskTag The task tag of request.\r
+ @param[in] Cdb The cdb buffer containing SCSI command.\r
+ @param[in] CdbLength The cdb length.\r
+ @param[in] DataDirection The direction of data transfer.\r
+ @param[in] ExpDataTranLen The expected transfer data length.\r
+\r
+ @retval EFI_SUCCESS The initialization succeed.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsInitCommandUpiu (\r
+ IN OUT UTP_COMMAND_UPIU *Command,\r
+ IN UINT8 Lun,\r
+ IN UINT8 TaskTag,\r
+ IN UINT8 *Cdb,\r
+ IN UINT8 CdbLength,\r
+ IN UFS_DATA_DIRECTION DataDirection,\r
+ IN UINT32 ExpDataTranLen\r
+ )\r
+{\r
+ UINT8 Flags;\r
+\r
+ ASSERT ((Command != NULL) && (Cdb != NULL));\r
+\r
+ //\r
+ // Task attribute is hard-coded to Ordered.\r
+ //\r
+ if (DataDirection == UfsDataIn) {\r
+ Flags = BIT0 | BIT6;\r
+ } else if (DataDirection == UfsDataOut) {\r
+ Flags = BIT0 | BIT5;\r
+ } else {\r
+ Flags = BIT0;\r
+ }\r
+\r
+ //\r
+ // Fill UTP COMMAND UPIU associated fields.\r
+ //\r
+ Command->TransCode = 0x01;\r
+ Command->Flags = Flags;\r
+ Command->Lun = Lun;\r
+ Command->TaskTag = TaskTag;\r
+ Command->CmdSet = 0x00;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ExpDataTranLen, sizeof (ExpDataTranLen));\r
+ Command->ExpDataTranLen = ExpDataTranLen;\r
+\r
+ CopyMem (Command->Cdb, Cdb, CdbLength);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize UTP PRDT for data transfer.\r
+\r
+ @param[in] Prdt The base address of PRDT.\r
+ @param[in] Buffer The buffer to be read or written.\r
+ @param[in] BufferSize The data size to be read or written.\r
+\r
+ @retval EFI_SUCCESS The initialization succeed.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsInitUtpPrdt (\r
+ IN UTP_TR_PRD *Prdt,\r
+ IN VOID *Buffer,\r
+ IN UINT32 BufferSize\r
+ )\r
+{\r
+ UINT32 PrdtIndex;\r
+ UINT32 RemainingLen;\r
+ UINT8 *Remaining;\r
+ UINTN PrdtNumber;\r
+\r
+ if (BufferSize == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);\r
+\r
+ RemainingLen = BufferSize;\r
+ Remaining = Buffer;\r
+ PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);\r
+\r
+ for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
+ if (RemainingLen < UFS_MAX_DATA_LEN_PER_PRD) {\r
+ Prdt[PrdtIndex].DbCount = (UINT32)RemainingLen - 1;\r
+ } else {\r
+ Prdt[PrdtIndex].DbCount = UFS_MAX_DATA_LEN_PER_PRD - 1;\r
+ }\r
+\r
+ Prdt[PrdtIndex].DbAddr = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 2);\r
+ Prdt[PrdtIndex].DbAddrU = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 32);\r
+ RemainingLen -= UFS_MAX_DATA_LEN_PER_PRD;\r
+ Remaining += UFS_MAX_DATA_LEN_PER_PRD;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize QUERY REQUEST UPIU.\r
+\r
+ @param[in, out] QueryReq The base address of QUERY REQUEST UPIU.\r
+ @param[in] TaskTag The task tag of request.\r
+ @param[in] Opcode The opcode of request.\r
+ @param[in] DescId The descriptor ID of request.\r
+ @param[in] Index The index of request.\r
+ @param[in] Selector The selector of request.\r
+ @param[in] DataSize The data size to be read or written.\r
+ @param[in] Data The buffer to be read or written.\r
+\r
+ @retval EFI_SUCCESS The initialization succeed.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsInitQueryRequestUpiu (\r
+ IN OUT UTP_QUERY_REQ_UPIU *QueryReq,\r
+ IN UINT8 TaskTag,\r
+ IN UINT8 Opcode,\r
+ IN UINT8 DescId,\r
+ IN UINT8 Index,\r
+ IN UINT8 Selector,\r
+ IN UINTN DataSize OPTIONAL,\r
+ IN UINT8 *Data OPTIONAL\r
+ )\r
+{\r
+ ASSERT (QueryReq != NULL);\r
+\r
+ QueryReq->TransCode = 0x16;\r
+ QueryReq->TaskTag = TaskTag;\r
+ if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeRdFlag) || (Opcode == UtpQueryFuncOpcodeRdAttr)) {\r
+ QueryReq->QueryFunc = QUERY_FUNC_STD_READ_REQ;\r
+ } else {\r
+ QueryReq->QueryFunc = QUERY_FUNC_STD_WRITE_REQ;\r
+ }\r
+\r
+ if (Opcode == UtpQueryFuncOpcodeWrAttr) {\r
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, *(UINT32*)Data);\r
+ } else if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) {\r
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, (UINT16)DataSize, 0);\r
+ } else {\r
+ UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, 0);\r
+ }\r
+\r
+ if (Opcode == UtpQueryFuncOpcodeWrDesc) {\r
+ CopyMem (QueryReq + 1, Data, DataSize);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate COMMAND/RESPONSE UPIU for filling UTP TRD's command descriptor field.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Lun The Lun on which the SCSI command is executed.\r
+ @param[in] Packet The pointer to the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET data structure.\r
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.\r
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.\r
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The creation succeed.\r
+ @retval EFI_DEVICE_ERROR The creation failed.\r
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsCreateScsiCommandDesc (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 Lun,\r
+ IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
+ IN UTP_TRD *Trd,\r
+ OUT VOID **CmdDescHost,\r
+ OUT VOID **CmdDescMapping\r
+ )\r
+{\r
+ UINTN TotalLen;\r
+ UINTN PrdtNumber;\r
+ UTP_COMMAND_UPIU *CommandUpiu;\r
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;\r
+ EFI_STATUS Status;\r
+ UINT32 DataLen;\r
+ UFS_DATA_DIRECTION DataDirection;\r
+\r
+ ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL));\r
+\r
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
+ DataLen = Packet->InTransferLength;\r
+ DataDirection = UfsDataIn;\r
+ } else {\r
+ DataLen = Packet->OutTransferLength;\r
+ DataDirection = UfsDataOut;\r
+ }\r
+\r
+ if (DataLen == 0) {\r
+ DataDirection = UfsNoData;\r
+ }\r
+\r
+ PrdtNumber = (UINTN)DivU64x32 ((UINT64)DataLen + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);\r
+\r
+ TotalLen = ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)) + PrdtNumber * sizeof (UTP_TR_PRD);\r
+\r
+ Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ CommandUpiu = (UTP_COMMAND_UPIU*)*CmdDescHost;\r
+\r
+ UfsInitCommandUpiu (CommandUpiu, Lun, Private->TaskTag++, Packet->Cdb, Packet->CdbLength, DataDirection, DataLen);\r
+\r
+ //\r
+ // Fill UTP_TRD associated fields\r
+ // NOTE: Some UFS host controllers request the Response UPIU and the Physical Region Description Table\r
+ // *MUST* be located at a 64-bit aligned boundary.\r
+ //\r
+ Trd->Int = UFS_INTERRUPT_COMMAND;\r
+ Trd->Dd = DataDirection;\r
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;\r
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7);\r
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32);\r
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)), sizeof (UINT32));\r
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)), sizeof (UINT32));\r
+ Trd->PrdtL = (UINT16)PrdtNumber;\r
+ Trd->PrdtO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))), sizeof (UINT32));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate QUERY REQUEST/QUERY RESPONSE UPIU for filling UTP TRD's command descriptor field.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Packet The pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET data structure.\r
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.\r
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.\r
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The creation succeed.\r
+ @retval EFI_DEVICE_ERROR The creation failed.\r
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.\r
+ @retval EFI_INVALID_PARAMETER The parameter passed in is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsCreateDMCommandDesc (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet,\r
+ IN UTP_TRD *Trd,\r
+ OUT VOID **CmdDescHost,\r
+ OUT VOID **CmdDescMapping\r
+ )\r
+{\r
+ UINTN TotalLen;\r
+ UTP_QUERY_REQ_UPIU *QueryReqUpiu;\r
+ UINT8 Opcode;\r
+ UINT32 DataSize;\r
+ UINT8 *Data;\r
+ UINT8 DataDirection;\r
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL));\r
+\r
+ Opcode = Packet->Opcode;\r
+ if ((Opcode > UtpQueryFuncOpcodeTogFlag) || (Opcode == UtpQueryFuncOpcodeNop)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DataDirection = Packet->DataDirection;\r
+ if (DataDirection == UfsDataIn) {\r
+ DataSize = Packet->InTransferLength;\r
+ Data = Packet->InDataBuffer;\r
+ } else if (DataDirection == UfsDataOut) {\r
+ DataSize = Packet->OutTransferLength;\r
+ Data = Packet->OutDataBuffer;\r
+ } else {\r
+ DataSize = 0;\r
+ Data = NULL;\r
+ }\r
+\r
+ if (((Opcode != UtpQueryFuncOpcodeSetFlag) && (Opcode != UtpQueryFuncOpcodeClrFlag) && (Opcode != UtpQueryFuncOpcodeTogFlag))\r
+ && ((DataSize == 0) || (Data == NULL))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (((Opcode == UtpQueryFuncOpcodeSetFlag) || (Opcode == UtpQueryFuncOpcodeClrFlag) || (Opcode == UtpQueryFuncOpcodeTogFlag))\r
+ && ((DataSize != 0) || (Data != NULL))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((Opcode == UtpQueryFuncOpcodeWrAttr) && (DataSize != sizeof (UINT32))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) {\r
+ TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize);\r
+ } else {\r
+ TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU));\r
+ }\r
+\r
+ Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Initialize UTP QUERY REQUEST UPIU\r
+ //\r
+ QueryReqUpiu = (UTP_QUERY_REQ_UPIU*)*CmdDescHost;\r
+ ASSERT (QueryReqUpiu != NULL);\r
+ UfsInitQueryRequestUpiu (\r
+ QueryReqUpiu,\r
+ Private->TaskTag++,\r
+ Opcode,\r
+ Packet->DescId,\r
+ Packet->Index,\r
+ Packet->Selector,\r
+ DataSize,\r
+ Data\r
+ );\r
+\r
+ //\r
+ // Fill UTP_TRD associated fields\r
+ // NOTE: Some UFS host controllers request the Query Response UPIU *MUST* be located at a 64-bit aligned boundary.\r
+ //\r
+ Trd->Int = UFS_INTERRUPT_COMMAND;\r
+ Trd->Dd = DataDirection;\r
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;\r
+ Trd->Ocs = 0x0F;\r
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7);\r
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32);\r
+ if (Opcode == UtpQueryFuncOpcodeWrDesc) {\r
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)), sizeof (UINT32));\r
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32));\r
+ } else {\r
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32));\r
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)), sizeof (UINT32));\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate NOP IN and NOP OUT UPIU for filling UTP TRD's command descriptor field.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Trd The pointer to the UTP Transfer Request Descriptor.\r
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.\r
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The creation succeed.\r
+ @retval EFI_DEVICE_ERROR The creation failed.\r
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsCreateNopCommandDesc (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UTP_TRD *Trd,\r
+ OUT VOID **CmdDescHost,\r
+ OUT VOID **CmdDescMapping\r
+ )\r
+{\r
+ UINTN TotalLen;\r
+ UTP_NOP_OUT_UPIU *NopOutUpiu;\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;\r
+\r
+ ASSERT ((Private != NULL) && (Trd != NULL));\r
+\r
+ TotalLen = ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)) + ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU));\r
+ Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ NopOutUpiu = (UTP_NOP_OUT_UPIU*)*CmdDescHost;\r
+ ASSERT (NopOutUpiu != NULL);\r
+ NopOutUpiu->TaskTag = Private->TaskTag++;\r
+\r
+ //\r
+ // Fill UTP_TRD associated fields\r
+ // NOTE: Some UFS host controllers request the Nop Out UPIU *MUST* be located at a 64-bit aligned boundary.\r
+ //\r
+ Trd->Int = UFS_INTERRUPT_COMMAND;\r
+ Trd->Dd = 0x00;\r
+ Trd->Ct = UFS_STORAGE_COMMAND_TYPE;\r
+ Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7);\r
+ Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32);\r
+ Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)), sizeof (UINT32));\r
+ Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)), sizeof (UINT32));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Find out available slot in transfer list of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[out] Slot The available slot.\r
+\r
+ @retval EFI_SUCCESS The available slot was found successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsFindAvailableSlotInTrl (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ OUT UINT8 *Slot\r
+ )\r
+{\r
+ ASSERT ((Private != NULL) && (Slot != NULL));\r
+\r
+ //\r
+ // The simplest algo to always use slot 0.\r
+ // TODO: enhance it to support async transfer with multiple slot.\r
+ //\r
+ *Slot = 0;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Find out available slot in task management transfer list of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[out] Slot The available slot.\r
+\r
+ @retval EFI_SUCCESS The available slot was found successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsFindAvailableSlotInTmrl (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ OUT UINT8 *Slot\r
+ )\r
+{\r
+ ASSERT ((Private != NULL) && (Slot != NULL));\r
+\r
+ //\r
+ // The simplest algo to always use slot 0.\r
+ // TODO: enhance it to support async transfer with multiple slot.\r
+ //\r
+ *Slot = 0;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Start specified slot in transfer list of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Slot The slot to be started.\r
+\r
+**/\r
+VOID\r
+UfsStartExecCmd (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 Slot\r
+ ) \r
+{\r
+ UINTN UfsHcBase;\r
+ UINTN Address;\r
+ UINT32 Data;\r
+\r
+ UfsHcBase = Private->UfsHcBase;\r
+\r
+ Address = UfsHcBase + UFS_HC_UTRLRSR_OFFSET; \r
+ Data = MmioRead32 (Address);\r
+ if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) {\r
+ MmioWrite32 (Address, UFS_HC_UTRLRSR);\r
+ }\r
+\r
+ Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET;\r
+ MmioWrite32 (Address, BIT0 << Slot);\r
+}\r
+\r
+/**\r
+ Stop specified slot in transfer list of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Slot The slot to be stop.\r
+\r
+**/\r
+VOID\r
+UfsStopExecCmd (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 Slot\r
+ ) \r
+{\r
+ UINTN UfsHcBase;\r
+ UINTN Address;\r
+ UINT32 Data;\r
+\r
+ UfsHcBase = Private->UfsHcBase;\r
+\r
+ Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; \r
+ Data = MmioRead32 (Address);\r
+ if ((Data & (BIT0 << Slot)) != 0) {\r
+ Address = UfsHcBase + UFS_HC_UTRLCLR_OFFSET; \r
+ Data = MmioRead32 (Address);\r
+ MmioWrite32 (Address, (Data & ~(BIT0 << Slot)));\r
+ }\r
+}\r
+\r
+/**\r
+ Read or write specified device descriptor of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Read The boolean variable to show r/w direction.\r
+ @param[in] DescId The ID of device descriptor.\r
+ @param[in] Index The Index of device descriptor.\r
+ @param[in] Selector The Selector of device descriptor.\r
+ @param[in, out] Descriptor The buffer of device descriptor to be read or written.\r
+ @param[in] DescSize The size of device descriptor buffer.\r
+\r
+ @retval EFI_SUCCESS The device descriptor was read/written successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsRwDeviceDesc (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN BOOLEAN Read,\r
+ IN UINT8 DescId,\r
+ IN UINT8 Index,\r
+ IN UINT8 Selector,\r
+ IN OUT VOID *Descriptor,\r
+ IN UINT32 DescSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;\r
+ UINT8 Slot;\r
+ UTP_TRD *Trd;\r
+ UINTN Address;\r
+ UTP_QUERY_RESP_UPIU *QueryResp;\r
+ UINT32 CmdDescSize;\r
+ UINT16 ReturnDataSize;\r
+ VOID *CmdDescHost;\r
+ VOID *CmdDescMapping;\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+\r
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));\r
+\r
+ if (Read) {\r
+ Packet.DataDirection = UfsDataIn;\r
+ Packet.InDataBuffer = Descriptor;\r
+ Packet.InTransferLength = DescSize;\r
+ Packet.Opcode = UtpQueryFuncOpcodeRdDesc;\r
+ } else {\r
+ Packet.DataDirection = UfsDataOut;\r
+ Packet.OutDataBuffer = Descriptor;\r
+ Packet.OutTransferLength = DescSize;\r
+ Packet.Opcode = UtpQueryFuncOpcodeWrDesc;\r
+ }\r
+ Packet.DescId = DescId;\r
+ Packet.Index = Index;\r
+ Packet.Selector = Selector;\r
+ Packet.Timeout = UFS_TIMEOUT;\r
+\r
+ //\r
+ // Find out which slot of transfer request list is available.\r
+ //\r
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+ //\r
+ // Fill transfer request descriptor to this slot.\r
+ //\r
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the transfer request result.\r
+ //\r
+ UfsHc = Private->UfsHostController;\r
+ QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+ ASSERT (QueryResp != NULL);\r
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);\r
+\r
+ //\r
+ // Start to execute the transfer request.\r
+ //\r
+ UfsStartExecCmd (Private, Slot);\r
+\r
+ //\r
+ // Wait for the completion of the transfer request.\r
+ // \r
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; \r
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (QueryResp->QueryResp != 0) {\r
+ DumpQueryResponseResult (QueryResp->QueryResp);\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
+\r
+ if (Trd->Ocs == 0) {\r
+ ReturnDataSize = QueryResp->Tsf.Length;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16));\r
+\r
+ if (Read) {\r
+ CopyMem (Packet.InDataBuffer, (QueryResp + 1), ReturnDataSize);\r
+ Packet.InTransferLength = ReturnDataSize;\r
+ } else {\r
+ Packet.OutTransferLength = ReturnDataSize;\r
+ }\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+Exit:\r
+ UfsHc->Flush (UfsHc);\r
+\r
+ UfsStopExecCmd (Private, Slot);\r
+\r
+ if (CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+ }\r
+ if (CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Read or write specified attribute of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Read The boolean variable to show r/w direction.\r
+ @param[in] AttrId The ID of Attribute.\r
+ @param[in] Index The Index of Attribute.\r
+ @param[in] Selector The Selector of Attribute.\r
+ @param[in, out] Attributes The value of Attribute to be read or written.\r
+\r
+ @retval EFI_SUCCESS The Attribute was read/written successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the Attribute.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the Attribute.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsRwAttributes (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN BOOLEAN Read,\r
+ IN UINT8 AttrId,\r
+ IN UINT8 Index,\r
+ IN UINT8 Selector,\r
+ IN OUT UINT32 *Attributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;\r
+ UINT8 Slot;\r
+ UTP_TRD *Trd;\r
+ UINTN Address;\r
+ UTP_QUERY_RESP_UPIU *QueryResp;\r
+ UINT32 CmdDescSize;\r
+ UINT32 ReturnData;\r
+ VOID *CmdDescHost;\r
+ VOID *CmdDescMapping;\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+\r
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));\r
+\r
+ if (Read) {\r
+ Packet.DataDirection = UfsDataIn;\r
+ Packet.Opcode = UtpQueryFuncOpcodeRdAttr;\r
+ } else {\r
+ Packet.DataDirection = UfsDataOut;\r
+ Packet.Opcode = UtpQueryFuncOpcodeWrAttr;\r
+ }\r
+ Packet.DescId = AttrId;\r
+ Packet.Index = Index;\r
+ Packet.Selector = Selector;\r
+ Packet.Timeout = UFS_TIMEOUT;\r
+\r
+ //\r
+ // Find out which slot of transfer request list is available.\r
+ //\r
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+ //\r
+ // Fill transfer request descriptor to this slot.\r
+ //\r
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the transfer request result.\r
+ //\r
+ UfsHc = Private->UfsHostController;\r
+ QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+ ASSERT (QueryResp != NULL);\r
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);\r
+\r
+ //\r
+ // Start to execute the transfer request.\r
+ //\r
+ UfsStartExecCmd (Private, Slot);\r
+\r
+ //\r
+ // Wait for the completion of the transfer request.\r
+ // \r
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; \r
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (QueryResp->QueryResp != 0) {\r
+ DumpQueryResponseResult (QueryResp->QueryResp);\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
+\r
+ if (Trd->Ocs == 0) {\r
+ ReturnData = QueryResp->Tsf.Value;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ReturnData, sizeof (UINT32));\r
+ *Attributes = ReturnData;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+Exit:\r
+ UfsHc->Flush (UfsHc);\r
+\r
+ UfsStopExecCmd (Private, Slot);\r
+\r
+ if (CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+ }\r
+\r
+ if (CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Read or write specified flag of a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Read The boolean variable to show r/w direction.\r
+ @param[in] FlagId The ID of flag to be read or written.\r
+ @param[in, out] Value The value to set or clear flag.\r
+\r
+ @retval EFI_SUCCESS The flag was read/written successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the flag.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the flag.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsRwFlags (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN BOOLEAN Read,\r
+ IN UINT8 FlagId,\r
+ IN OUT UINT8 *Value\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet;\r
+ UINT8 Slot;\r
+ UTP_TRD *Trd;\r
+ UINTN Address;\r
+ UTP_QUERY_RESP_UPIU *QueryResp;\r
+ UINT32 CmdDescSize;\r
+ VOID *CmdDescHost;\r
+ VOID *CmdDescMapping;\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+\r
+ if (Value == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET));\r
+\r
+ if (Read) {\r
+ ASSERT (Value != NULL);\r
+ Packet.DataDirection = UfsDataIn;\r
+ Packet.Opcode = UtpQueryFuncOpcodeRdFlag;\r
+ } else {\r
+ Packet.DataDirection = UfsDataOut;\r
+ if (*Value == 1) {\r
+ Packet.Opcode = UtpQueryFuncOpcodeSetFlag;\r
+ } else if (*Value == 0) {\r
+ Packet.Opcode = UtpQueryFuncOpcodeClrFlag;\r
+ } else {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ Packet.DescId = FlagId;\r
+ Packet.Index = 0;\r
+ Packet.Selector = 0;\r
+ Packet.Timeout = UFS_TIMEOUT;\r
+\r
+ //\r
+ // Find out which slot of transfer request list is available.\r
+ //\r
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Fill transfer request descriptor to this slot.\r
+ //\r
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+ Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the transfer request result.\r
+ //\r
+ UfsHc = Private->UfsHostController;\r
+ QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+ ASSERT (QueryResp != NULL);\r
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);\r
+\r
+ //\r
+ // Start to execute the transfer request.\r
+ //\r
+ UfsStartExecCmd (Private, Slot);\r
+\r
+ //\r
+ // Wait for the completion of the transfer request.\r
+ // \r
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; \r
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (QueryResp->QueryResp != 0) {\r
+ DumpQueryResponseResult (QueryResp->QueryResp);\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
+\r
+ if (Trd->Ocs == 0) {\r
+ *Value = (UINT8)QueryResp->Tsf.Value;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+Exit:\r
+ UfsHc->Flush (UfsHc);\r
+\r
+ UfsStopExecCmd (Private, Slot);\r
+\r
+ if (CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+ }\r
+ if (CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Set specified flag to 1 on a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] FlagId The ID of flag to be set.\r
+\r
+ @retval EFI_SUCCESS The flag was set successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsSetFlag (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 FlagId\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Value;\r
+\r
+ Value = 1;\r
+ Status = UfsRwFlags (Private, FALSE, FlagId, &Value);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Clear specified flag to 0 on a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] FlagId The ID of flag to be cleared.\r
+\r
+ @retval EFI_SUCCESS The flag was cleared successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to clear the flag.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of clearing the flag.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsClearFlag (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 FlagId\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Value;\r
+\r
+ Value = 0;\r
+ Status = UfsRwFlags (Private, FALSE, FlagId, &Value);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Read specified flag from a UFS device.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] FlagId The ID of flag to be read.\r
+ @param[out] Value The flag's value.\r
+\r
+ @retval EFI_SUCCESS The flag was read successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to read the flag.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of reading the flag.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsReadFlag (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 FlagId,\r
+ OUT UINT8 *Value\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = UfsRwFlags (Private, TRUE, FlagId, Value);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Sends NOP IN cmd to a UFS device for initialization process request.\r
+ For more details, please refer to UFS 2.0 spec Figure 13.3.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was\r
+ received successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command.\r
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsExecNopCmds (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Slot;\r
+ UTP_TRD *Trd;\r
+ UTP_NOP_IN_UPIU *NopInUpiu;\r
+ UINT32 CmdDescSize;\r
+ UINTN Address;\r
+ VOID *CmdDescHost;\r
+ VOID *CmdDescMapping;\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+\r
+ //\r
+ // Find out which slot of transfer request list is available.\r
+ //\r
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+ Status = UfsCreateNopCommandDesc (Private, Trd, &CmdDescHost, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the transfer request result.\r
+ //\r
+ UfsHc = Private->UfsHostController;\r
+ NopInUpiu = (UTP_NOP_IN_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+ ASSERT (NopInUpiu != NULL);\r
+ CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32);\r
+\r
+ //\r
+ // Start to execute the transfer request.\r
+ //\r
+ UfsStartExecCmd (Private, Slot);\r
+\r
+ //\r
+ // Wait for the completion of the transfer request.\r
+ // \r
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; \r
+ Status = UfsWaitMemSet (Address, BIT0, 0, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (NopInUpiu->Resp != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ } else {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+Exit:\r
+ UfsHc->Flush (UfsHc);\r
+\r
+ UfsStopExecCmd (Private, Slot);\r
+\r
+ if (CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+ }\r
+ if (CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.\r
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the\r
+ UFS device.\r
+\r
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional\r
+ commands, InTransferLength bytes were transferred from\r
+ InDataBuffer. For write and bi-directional commands,\r
+ OutTransferLength bytes were transferred by\r
+ OutDataBuffer.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request\r
+ Packet.\r
+ @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsExecScsiCmds (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 Lun,\r
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Slot;\r
+ UTP_TRD *Trd;\r
+ UINTN Address;\r
+ UINT32 CmdDescSize;\r
+ UTP_RESPONSE_UPIU *Response;\r
+ UINT16 SenseDataLen;\r
+ UINT32 ResTranCount;\r
+ VOID *CmdDescHost;\r
+ VOID *CmdDescMapping;\r
+ VOID *DataBufMapping;\r
+ VOID *DataBuf;\r
+ EFI_PHYSICAL_ADDRESS DataBufPhyAddr;\r
+ UINT32 DataLen;\r
+ UINTN MapLength;\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+ EDKII_UFS_HOST_CONTROLLER_OPERATION Flag;\r
+ UFS_DATA_DIRECTION DataDirection;\r
+ UTP_TR_PRD *PrdtBase;\r
+\r
+ Trd = NULL;\r
+ CmdDescHost = NULL;\r
+ CmdDescMapping = NULL;\r
+ DataBufMapping = NULL;\r
+ DataBufPhyAddr = 0;\r
+ UfsHc = Private->UfsHostController;\r
+ //\r
+ // Find out which slot of transfer request list is available.\r
+ //\r
+ Status = UfsFindAvailableSlotInTrl (Private, &Slot);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+\r
+ //\r
+ // Fill transfer request descriptor to this slot.\r
+ //\r
+ Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd, &CmdDescHost, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD);\r
+\r
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
+ DataBuf = Packet->InDataBuffer;\r
+ DataLen = Packet->InTransferLength;\r
+ DataDirection = UfsDataIn;\r
+ Flag = EdkiiUfsHcOperationBusMasterWrite;\r
+ } else {\r
+ DataBuf = Packet->OutDataBuffer;\r
+ DataLen = Packet->OutTransferLength;\r
+ DataDirection = UfsDataOut;\r
+ Flag = EdkiiUfsHcOperationBusMasterRead;\r
+ }\r
+\r
+ if (DataLen == 0) {\r
+ DataDirection = UfsNoData;\r
+ } else {\r
+ MapLength = DataLen;\r
+ Status = UfsHc->Map (\r
+ UfsHc,\r
+ Flag,\r
+ DataBuf,\r
+ &MapLength,\r
+ &DataBufPhyAddr,\r
+ &DataBufMapping\r
+ );\r
+\r
+ if (EFI_ERROR (Status) || (DataLen != MapLength)) {\r
+ goto Exit1;\r
+ }\r
+ }\r
+ //\r
+ // Fill PRDT table of Command UPIU for executed SCSI cmd.\r
+ //\r
+ PrdtBase = (UTP_TR_PRD*)((UINT8*)CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));\r
+ ASSERT (PrdtBase != NULL);\r
+ UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);\r
+\r
+ //\r
+ // Start to execute the transfer request.\r
+ //\r
+ UfsStartExecCmd (Private, Slot);\r
+\r
+ //\r
+ // Wait for the completion of the transfer request.\r
+ // \r
+ Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; \r
+ Status = UfsWaitMemSet (Address, BIT0, 0, Packet->Timeout);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Get sense data if exists\r
+ //\r
+ Response = (UTP_RESPONSE_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+ ASSERT (Response != NULL);\r
+ SenseDataLen = Response->SenseDataLen;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));\r
+ \r
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) {\r
+ CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen);\r
+ Packet->SenseDataLength = (UINT8)SenseDataLen;\r
+ }\r
+\r
+ //\r
+ // Check the transfer request result.\r
+ //\r
+ Packet->TargetStatus = Response->Status;\r
+ if (Response->Response != 0) {\r
+ DEBUG ((EFI_D_ERROR, "UfsExecScsiCmds() fails with Target Failure\n"));\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
+\r
+ if (Trd->Ocs == 0) {\r
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
+ if ((Response->Flags & BIT5) == BIT5) {\r
+ ResTranCount = Response->ResTranCount;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));\r
+ Packet->InTransferLength -= ResTranCount;\r
+ }\r
+ } else {\r
+ if ((Response->Flags & BIT5) == BIT5) {\r
+ ResTranCount = Response->ResTranCount;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));\r
+ Packet->OutTransferLength -= ResTranCount;\r
+ }\r
+ }\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+Exit:\r
+ UfsHc->Flush (UfsHc);\r
+\r
+ UfsStopExecCmd (Private, Slot);\r
+\r
+ if (DataBufMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, DataBufMapping);\r
+ }\r
+\r
+Exit1:\r
+ if (CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+ }\r
+ if (CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Sent UIC DME_LINKSTARTUP command to start the link startup procedure.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] UicOpcode The opcode of the UIC command.\r
+ @param[in] Arg1 The value for 1st argument of the UIC command.\r
+ @param[in] Arg2 The value for 2nd argument of the UIC command.\r
+ @param[in] Arg3 The value for 3rd argument of the UIC command.\r
+\r
+ @return EFI_SUCCESS Successfully execute this UIC command and detect attached UFS device.\r
+ @return EFI_DEVICE_ERROR Fail to execute this UIC command and detect attached UFS device.\r
+ @return EFI_NOT_FOUND The presence of the UFS device isn't detected.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsExecUicCommands (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINT8 UicOpcode,\r
+ IN UINT32 Arg1,\r
+ IN UINT32 Arg2,\r
+ IN UINT32 Arg3\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Address;\r
+ UINT32 Data;\r
+ UINTN UfsHcBase;\r
+\r
+ UfsHcBase = Private->UfsHcBase;\r
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;\r
+ Data = MmioRead32 (Address);\r
+ if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) {\r
+ //\r
+ // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first.\r
+ //\r
+ MmioWrite32 (Address, Data);\r
+ }\r
+\r
+ //\r
+ // When programming UIC command registers, host software shall set the register UICCMD\r
+ // only after all the UIC command argument registers (UICCMDARG1, UICCMDARG2 and UICCMDARG3)\r
+ // are set.\r
+ //\r
+ Address = UfsHcBase + UFS_HC_UCMD_ARG1_OFFSET;\r
+ MmioWrite32 (Address, Arg1);\r
+\r
+ Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET;\r
+ MmioWrite32 (Address, Arg2);\r
+\r
+ Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET;\r
+ MmioWrite32 (Address, Arg3);\r
+\r
+ //\r
+ // Host software shall only set the UICCMD if HCS.UCRDY is set to 1.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_STATUS_OFFSET;\r
+ Status = UfsWaitMemSet (Address, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Address = UfsHcBase + UFS_HC_UIC_CMD_OFFSET;\r
+ MmioWrite32 (Address, (UINT32)UicOpcode);\r
+\r
+ //\r
+ // UFS 2.0 spec section 5.3.1 Offset:0x20 IS.Bit10 UIC Command Completion Status (UCCS)\r
+ // This bit is set to '1' by the host controller upon completion of a UIC command. \r
+ //\r
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;\r
+ Data = MmioRead32 (Address);\r
+ Status = UfsWaitMemSet (Address, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (UicOpcode != UfsUicDmeReset) {\r
+ Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET;\r
+ Data = MmioRead32 (Address);\r
+ if ((Data & 0xFF) != 0) {\r
+ DEBUG_CODE_BEGIN();\r
+ DumpUicCmdExecResult (UicOpcode, (UINT8)(Data & 0xFF));\r
+ DEBUG_CODE_END();\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check value of HCS.DP and make sure that there is a device attached to the Link.\r
+ //\r
+ Address = UfsHcBase + UFS_HC_STATUS_OFFSET; \r
+ Data = MmioRead32 (Address);\r
+ if ((Data & UFS_HC_HCS_DP) == 0) {\r
+ Address = UfsHcBase + UFS_HC_IS_OFFSET;\r
+ Status = UfsWaitMemSet (Address, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "UfsPassThruDxe: found a attached UFS device\n"));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate common buffer for host and UFS bus master access simultaneously.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+ @param[in] Size The length of buffer to be allocated.\r
+ @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range.\r
+ @param[out] CmdDescPhyAddr The resulting map address for the UFS bus master to use to access the hosts CmdDescHost.\r
+ @param[out] CmdDescMapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The common buffer was allocated successfully.\r
+ @retval EFI_DEVICE_ERROR The allocation fails.\r
+ @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsAllocateAlignCommonBuffer (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UINTN Size,\r
+ OUT VOID **CmdDescHost,\r
+ OUT EFI_PHYSICAL_ADDRESS *CmdDescPhyAddr,\r
+ OUT VOID **CmdDescMapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Bytes;\r
+ BOOLEAN Is32BitAddr;\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+\r
+ if ((Private->Capabilities & UFS_HC_CAP_64ADDR) == UFS_HC_CAP_64ADDR) {\r
+ Is32BitAddr = TRUE;\r
+ } else {\r
+ Is32BitAddr = FALSE;\r
+ }\r
+\r
+ UfsHc = Private->UfsHostController;\r
+ Status = UfsHc->AllocateBuffer (\r
+ UfsHc,\r
+ AllocateAnyPages,\r
+ EfiBootServicesData,\r
+ EFI_SIZE_TO_PAGES (Size),\r
+ CmdDescHost,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ *CmdDescMapping = NULL;\r
+ *CmdDescHost = NULL;\r
+ *CmdDescPhyAddr = 0;\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Bytes = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size));\r
+ Status = UfsHc->Map (\r
+ UfsHc,\r
+ EdkiiUfsHcOperationBusMasterCommonBuffer,\r
+ *CmdDescHost,\r
+ &Bytes,\r
+ CmdDescPhyAddr,\r
+ CmdDescMapping\r
+ );\r
+\r
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)))) {\r
+ UfsHc->FreeBuffer (\r
+ UfsHc,\r
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)),\r
+ *CmdDescHost\r
+ );\r
+ *CmdDescHost = NULL;\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (Is32BitAddr && ((*CmdDescPhyAddr) > 0x100000000ULL)) {\r
+ //\r
+ // The UFS host controller doesn't support 64bit addressing, so should not get a >4G UFS bus master address.\r
+ //\r
+ UfsHc->Unmap (\r
+ UfsHc,\r
+ *CmdDescMapping\r
+ );\r
+ UfsHc->FreeBuffer (\r
+ UfsHc,\r
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)),\r
+ *CmdDescHost\r
+ );\r
+ *CmdDescMapping = NULL;\r
+ *CmdDescHost = NULL;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ZeroMem (*CmdDescHost, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Enable the UFS host controller for accessing.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The UFS host controller enabling was executed successfully.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while enabling the UFS host controller.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsEnableHostController (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Address;\r
+ UINT32 Data;\r
+\r
+ //\r
+ // UFS 2.0 spec section 7.1.1 - Host Controller Initialization\r
+ //\r
+ // Reinitialize the UFS host controller if HCE bit of HC register is set.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET;\r
+ Data = MmioRead32 (Address);\r
+ if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) {\r
+ //\r
+ // Write a 0 to the HCE register at first to disable the host controller.\r
+ //\r
+ MmioWrite32 (Address, 0);\r
+ //\r
+ // Wait until HCE is read as '0' before continuing.\r
+ //\r
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Write a 1 to the HCE register to enable the UFS host controller.\r
+ //\r
+ MmioWrite32 (Address, UFS_HC_HCE_EN);\r
+ //\r
+ // Wait until HCE is read as '1' before continuing.\r
+ //\r
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Detect if a UFS device attached.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The UFS device detection was executed successfully.\r
+ @retval EFI_NOT_FOUND Not found a UFS device attached.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while detecting the UFS device.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsDeviceDetection (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ UINTN Retry;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Start UFS device detection.\r
+ // Try up to 3 times for establishing data link with device.\r
+ //\r
+ for (Retry = 0; Retry < 3; Retry++) {\r
+ Status = UfsExecUicCommands (Private, UfsUicDmeLinkStartup, 0, 0, 0);\r
+ if (!EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ if (Status == EFI_NOT_FOUND) {\r
+ continue;\r
+ }\r
+\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (Retry == 3) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize UFS task management request list related h/w context.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The UFS task management list was initialzed successfully.\r
+ @retval EFI_DEVICE_ERROR The initialization fails.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsInitTaskManagementRequestList (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ UINTN Address;\r
+ UINT32 Data;\r
+ UINT8 Nutmrs;\r
+ VOID *CmdDescHost;\r
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;\r
+ VOID *CmdDescMapping;\r
+ EFI_STATUS Status;\r
+ \r
+ //\r
+ // Initial h/w and s/w context for future operations.\r
+ //\r
+ CmdDescHost = NULL;\r
+ CmdDescMapping = NULL;\r
+ CmdDescPhyAddr = 0;\r
+ Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; \r
+ Data = MmioRead32 (Address);\r
+ Private->Capabilities = Data;\r
+\r
+ //\r
+ // Allocate and initialize UTP Task Management Request List.\r
+ //\r
+ Nutmrs = (UINT8) (RShiftU64 ((Private->Capabilities & UFS_HC_CAP_NUTMRS), 16) + 1);\r
+ Status = UfsAllocateAlignCommonBuffer (Private, Nutmrs * sizeof (UTP_TMRD), &CmdDescHost, &CmdDescPhyAddr, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Program the UTP Task Management Request List Base Address and UTP Task Management\r
+ // Request List Base Address with a 64-bit address allocated at step 6.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_UTMRLBA_OFFSET; \r
+ MmioWrite32 (Address, (UINT32)(UINTN)CmdDescPhyAddr);\r
+ Address = Private->UfsHcBase + UFS_HC_UTMRLBAU_OFFSET; \r
+ MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32));\r
+ Private->UtpTmrlBase = CmdDescHost;\r
+ Private->Nutmrs = Nutmrs;\r
+ Private->TmrlMapping = CmdDescMapping;\r
+\r
+ //\r
+ // Enable the UTP Task Management Request List by setting the UTP Task Management\r
+ // Request List RunStop Register (UTMRLRSR) to '1'.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; \r
+ MmioWrite32 (Address, UFS_HC_UTMRLRSR);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize UFS transfer request list related h/w context.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The UFS transfer list was initialzed successfully.\r
+ @retval EFI_DEVICE_ERROR The initialization fails.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsInitTransferRequestList (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ UINTN Address;\r
+ UINT32 Data;\r
+ UINT8 Nutrs;\r
+ VOID *CmdDescHost;\r
+ EFI_PHYSICAL_ADDRESS CmdDescPhyAddr;\r
+ VOID *CmdDescMapping; \r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Initial h/w and s/w context for future operations.\r
+ //\r
+ CmdDescHost = NULL;\r
+ CmdDescMapping = NULL;\r
+ CmdDescPhyAddr = 0;\r
+ Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; \r
+ Data = MmioRead32 (Address);\r
+ Private->Capabilities = Data;\r
+\r
+ //\r
+ // Allocate and initialize UTP Transfer Request List.\r
+ //\r
+ Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1);\r
+ Status = UfsAllocateAlignCommonBuffer (Private, Nutrs * sizeof (UTP_TRD), &CmdDescHost, &CmdDescPhyAddr, &CmdDescMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Program the UTP Transfer Request List Base Address and UTP Transfer Request List\r
+ // Base Address with a 64-bit address allocated at step 8.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_UTRLBA_OFFSET; \r
+ MmioWrite32 (Address, (UINT32)(UINTN)CmdDescPhyAddr);\r
+ Address = Private->UfsHcBase + UFS_HC_UTRLBAU_OFFSET; \r
+ MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32));\r
+ Private->UtpTrlBase = CmdDescHost;\r
+ Private->Nutrs = Nutrs; \r
+ Private->TrlMapping = CmdDescMapping;\r
+\r
+ //\r
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List\r
+ // RunStop Register (UTRLRSR) to '1'.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; \r
+ MmioWrite32 (Address, UFS_HC_UTRLRSR);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize the UFS host controller.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully.\r
+ @retval Others A device error occurred while initializing the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsControllerInit (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = UfsEnableHostController (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Enable Host Controller Fails, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Status = UfsDeviceDetection (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Device Detection Fails, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Status = UfsInitTaskManagementRequestList (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Task management list initialization Fails, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Status = UfsInitTransferRequestList (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "UfsControllerInit: Transfer list initialization Fails, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "UfsControllerInit Finished\n"));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Stop the UFS host controller.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully.\r
+ @retval Others A device error occurred while stopping the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+UfsControllerStop (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Address;\r
+ UINT32 Data;\r
+\r
+ //\r
+ // Enable the UTP Task Management Request List by setting the UTP Task Management\r
+ // Request List RunStop Register (UTMRLRSR) to '1'.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; \r
+ MmioWrite32 (Address, 0);\r
+\r
+ //\r
+ // Enable the UTP Transfer Request List by setting the UTP Transfer Request List\r
+ // RunStop Register (UTRLRSR) to '1'.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; \r
+ MmioWrite32 (Address, 0);\r
+\r
+ //\r
+ // Write a 0 to the HCE register in order to disable the host controller.\r
+ //\r
+ Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET;\r
+ Data = MmioRead32 (Address);\r
+ ASSERT ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN);\r
+ MmioWrite32 (Address, 0);\r
+\r
+ //\r
+ // Wait until HCE is read as '0' before continuing.\r
+ //\r
+ Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "UfsController is stopped\n"));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r