]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
MdeModulePkg UfsPassThruDxe: Add Non-blocking I/O Support
[mirror_edk2.git] / MdeModulePkg / Bus / Ufs / UfsPassThruDxe / UfsPassThruHci.c
index 9f9abab40fded3ea72fc10e08669bcdfcd04ea38..4fbe199390f404ff1cd80e134750a9923073a2c1 100644 (file)
@@ -729,6 +729,7 @@ UfsCreateNopCommandDesc (
   @param[out] Slot          The available slot.\r
 \r
   @retval EFI_SUCCESS       The available slot was found successfully.\r
   @param[out] Slot          The available slot.\r
 \r
   @retval EFI_SUCCESS       The available slot was found successfully.\r
+  @retval EFI_NOT_READY     No slot is available at this moment.\r
 \r
 **/\r
 EFI_STATUS\r
 \r
 **/\r
 EFI_STATUS\r
@@ -737,15 +738,28 @@ UfsFindAvailableSlotInTrl (
      OUT UINT8                        *Slot\r
   )\r
 {\r
      OUT UINT8                        *Slot\r
   )\r
 {\r
+  UINT8            Nutrs;\r
+  UINT8            Index;\r
+  UINT32           Data;\r
+  EFI_STATUS       Status;\r
+\r
   ASSERT ((Private != NULL) && (Slot != NULL));\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
+  Status  = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Data);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
 \r
-  return EFI_SUCCESS;\r
+  Nutrs   = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1);\r
+\r
+  for (Index = 0; Index < Nutrs; Index++) {\r
+    if ((Data & (BIT0 << Index)) == 0) {\r
+      *Slot = Index;\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_READY;\r
 }\r
 \r
 /**\r
 }\r
 \r
 /**\r
@@ -1382,6 +1396,11 @@ Exit:
   @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
   @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
+  @param[in]      Event         If nonblocking I/O is not supported then Event is ignored, and blocking\r
+                                I/O is performed. If Event is NULL, then blocking I/O is performed. If\r
+                                Event is not NULL and non blocking I/O is supported, then\r
+                                nonblocking I/O is performed, and Event will be signaled when the\r
+                                SCSI Request Packet completes.\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
 \r
   @retval EFI_SUCCESS           The SCSI Request Packet was sent by the host. For bi-directional\r
                                 commands, InTransferLength bytes were transferred from\r
@@ -1398,19 +1417,14 @@ EFI_STATUS
 UfsExecScsiCmds (\r
   IN     UFS_PASS_THRU_PRIVATE_DATA                  *Private,\r
   IN     UINT8                                       Lun,\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
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet,\r
+  IN     EFI_EVENT                                   Event    OPTIONAL\r
   )\r
 {\r
   EFI_STATUS                           Status;\r
   )\r
 {\r
   EFI_STATUS                           Status;\r
-  UINT8                                Slot;\r
-  UTP_TRD                              *Trd;\r
-  UINT32                               CmdDescSize;\r
   UTP_RESPONSE_UPIU                    *Response;\r
   UINT16                               SenseDataLen;\r
   UINT32                               ResTranCount;\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
   VOID                                 *DataBuf;\r
   EFI_PHYSICAL_ADDRESS                 DataBufPhyAddr;\r
   UINT32                               DataLen;\r
@@ -1419,32 +1433,44 @@ UfsExecScsiCmds (
   EDKII_UFS_HOST_CONTROLLER_OPERATION  Flag;\r
   UFS_DATA_DIRECTION                   DataDirection;\r
   UTP_TR_PRD                           *PrdtBase;\r
   EDKII_UFS_HOST_CONTROLLER_OPERATION  Flag;\r
   UFS_DATA_DIRECTION                   DataDirection;\r
   UTP_TR_PRD                           *PrdtBase;\r
+  EFI_TPL                              OldTpl;\r
+  UFS_PASS_THRU_TRANS_REQ              *TransReq;\r
 \r
 \r
-  Trd            = NULL;\r
-  CmdDescHost    = NULL;\r
-  CmdDescMapping = NULL;\r
-  DataBufMapping = NULL;\r
+  TransReq       = AllocateZeroPool (sizeof (UFS_PASS_THRU_TRANS_REQ));\r
+  if (TransReq == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TransReq->Signature     = UFS_PASS_THRU_TRANS_REQ_SIG;\r
+  TransReq->TimeoutRemain = Packet->Timeout;\r
   DataBufPhyAddr = 0;\r
   UfsHc          = Private->UfsHostController;\r
   //\r
   // Find out which slot of transfer request list is available.\r
   //\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
+  Status = UfsFindAvailableSlotInTrl (Private, &TransReq->Slot);\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+  TransReq->Trd = ((UTP_TRD*)Private->UtpTrlBase) + TransReq->Slot;\r
 \r
   //\r
   // Fill transfer request descriptor to this slot.\r
   //\r
 \r
   //\r
   // Fill transfer request descriptor to this slot.\r
   //\r
-  Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd, &CmdDescHost, &CmdDescMapping);\r
+  Status = UfsCreateScsiCommandDesc (\r
+             Private,\r
+             Lun,\r
+             Packet,\r
+             TransReq->Trd,\r
+             &TransReq->CmdDescHost,\r
+             &TransReq->CmdDescMapping\r
+             );\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD);\r
+  TransReq->CmdDescSize = TransReq->Trd->PrdtO * sizeof (UINT32) + TransReq->Trd->PrdtL * sizeof (UTP_TR_PRD);\r
 \r
   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
     DataBuf       = Packet->InDataBuffer;\r
 \r
   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
     DataBuf       = Packet->InDataBuffer;\r
@@ -1468,7 +1494,7 @@ UfsExecScsiCmds (
                          DataBuf,\r
                          &MapLength,\r
                          &DataBufPhyAddr,\r
                          DataBuf,\r
                          &MapLength,\r
                          &DataBufPhyAddr,\r
-                         &DataBufMapping\r
+                         &TransReq->DataBufMapping\r
                          );\r
 \r
     if (EFI_ERROR (Status) || (DataLen != MapLength)) {\r
                          );\r
 \r
     if (EFI_ERROR (Status) || (DataLen != MapLength)) {\r
@@ -1478,14 +1504,32 @@ UfsExecScsiCmds (
   //\r
   // Fill PRDT table of Command UPIU for executed SCSI cmd.\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
+  PrdtBase = (UTP_TR_PRD*)((UINT8*)TransReq->CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));\r
   ASSERT (PrdtBase != NULL);\r
   UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);\r
 \r
   ASSERT (PrdtBase != NULL);\r
   UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);\r
 \r
+  //\r
+  // Insert the async SCSI cmd to the Async I/O list\r
+  //\r
+  if (Event != NULL) {\r
+    OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+    TransReq->Packet      = Packet;\r
+    TransReq->CallerEvent = Event;\r
+    InsertTailList (&Private->Queue, &TransReq->TransferList);\r
+    gBS->RestoreTPL (OldTpl);\r
+  }\r
+\r
   //\r
   // Start to execute the transfer request.\r
   //\r
   //\r
   // Start to execute the transfer request.\r
   //\r
-  UfsStartExecCmd (Private, Slot);\r
+  UfsStartExecCmd (Private, TransReq->Slot);\r
+\r
+  //\r
+  // Immediately return for async I/O.\r
+  //\r
+  if (Event != NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
 \r
   //\r
   // Wait for the completion of the transfer request.\r
 \r
   //\r
   // Wait for the completion of the transfer request.\r
@@ -1498,7 +1542,7 @@ UfsExecScsiCmds (
   //\r
   // Get sense data if exists\r
   //\r
   //\r
   // Get sense data if exists\r
   //\r
-  Response     = (UTP_RESPONSE_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+  Response     = (UTP_RESPONSE_UPIU*)((UINT8*)TransReq->CmdDescHost + TransReq->Trd->RuO * sizeof (UINT32));\r
   ASSERT (Response != NULL);\r
   SenseDataLen = Response->SenseDataLen;\r
   SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));\r
   ASSERT (Response != NULL);\r
   SenseDataLen = Response->SenseDataLen;\r
   SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));\r
@@ -1518,7 +1562,7 @@ UfsExecScsiCmds (
     goto Exit;\r
   }\r
 \r
     goto Exit;\r
   }\r
 \r
-  if (Trd->Ocs == 0) {\r
+  if (TransReq->Trd->Ocs == 0) {\r
     if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
       if ((Response->Flags & BIT5) == BIT5) {\r
         ResTranCount = Response->ResTranCount;\r
     if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
       if ((Response->Flags & BIT5) == BIT5) {\r
         ResTranCount = Response->ResTranCount;\r
@@ -1539,18 +1583,21 @@ UfsExecScsiCmds (
 Exit:\r
   UfsHc->Flush (UfsHc);\r
 \r
 Exit:\r
   UfsHc->Flush (UfsHc);\r
 \r
-  UfsStopExecCmd (Private, Slot);\r
+  UfsStopExecCmd (Private, TransReq->Slot);\r
 \r
 \r
-  if (DataBufMapping != NULL) {\r
-    UfsHc->Unmap (UfsHc, DataBufMapping);\r
+  if (TransReq->DataBufMapping != NULL) {\r
+    UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);\r
   }\r
 \r
 Exit1:\r
   }\r
 \r
 Exit1:\r
-  if (CmdDescMapping != NULL) {\r
-    UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+  if (TransReq->CmdDescMapping != NULL) {\r
+    UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);\r
   }\r
   }\r
-  if (CmdDescHost != NULL) {\r
-    UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+  if (TransReq->CmdDescHost != NULL) {\r
+    UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (TransReq->CmdDescSize), TransReq->CmdDescHost);\r
+  }\r
+  if (TransReq != NULL) {\r
+    FreePool (TransReq);\r
   }\r
   return Status;\r
 }\r
   }\r
   return Status;\r
 }\r
@@ -2123,3 +2170,178 @@ UfsControllerStop (
   return EFI_SUCCESS;\r
 }\r
 \r
   return EFI_SUCCESS;\r
 }\r
 \r
+\r
+/**\r
+  Internal helper function which will signal the caller event and clean up\r
+  resources.\r
+\r
+  @param[in] Private   The pointer to the UFS_PASS_THRU_PRIVATE_DATA data\r
+                       structure.\r
+  @param[in] TransReq  The pointer to the UFS_PASS_THRU_TRANS_REQ data\r
+                       structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SignalCallerEvent (\r
+  IN UFS_PASS_THRU_PRIVATE_DATA      *Private,\r
+  IN UFS_PASS_THRU_TRANS_REQ         *TransReq\r
+  )\r
+{\r
+  EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+  EFI_EVENT                          CallerEvent;\r
+\r
+  UfsHc        = Private->UfsHostController;\r
+  CallerEvent  = TransReq->CallerEvent;\r
+\r
+  RemoveEntryList (&TransReq->TransferList);\r
+\r
+  UfsHc->Flush (UfsHc);\r
+\r
+  UfsStopExecCmd (Private, TransReq->Slot);\r
+\r
+  if (TransReq->DataBufMapping != NULL) {\r
+    UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);\r
+  }\r
+\r
+  if (TransReq->CmdDescMapping != NULL) {\r
+    UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);\r
+  }\r
+  if (TransReq->CmdDescHost != NULL) {\r
+    UfsHc->FreeBuffer (\r
+             UfsHc,\r
+             EFI_SIZE_TO_PAGES (TransReq->CmdDescSize),\r
+             TransReq->CmdDescHost\r
+             );\r
+  }\r
+  if (TransReq != NULL) {\r
+    FreePool (TransReq);\r
+  }\r
+\r
+  gBS->SignalEvent (CallerEvent);\r
+  return;\r
+}\r
+\r
+/**\r
+  Call back function when the timer event is signaled.\r
+\r
+  @param[in]  Event     The Event this notify function registered to.\r
+  @param[in]  Context   Pointer to the context data registered to the Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ProcessAsyncTaskList (\r
+  IN EFI_EVENT          Event,\r
+  IN VOID               *Context\r
+  )\r
+{\r
+  UFS_PASS_THRU_PRIVATE_DATA                    *Private;\r
+  LIST_ENTRY                                    *Entry;\r
+  LIST_ENTRY                                    *NextEntry;\r
+  UFS_PASS_THRU_TRANS_REQ                       *TransReq;\r
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet;\r
+  UTP_RESPONSE_UPIU                             *Response;\r
+  UINT16                                        SenseDataLen;\r
+  UINT32                                        ResTranCount;\r
+  UINT32                                        SlotsMap;\r
+  UINT32                                        Value;\r
+  EFI_STATUS                                    Status;\r
+\r
+  Private   = (UFS_PASS_THRU_PRIVATE_DATA*) Context;\r
+  SlotsMap  = 0;\r
+\r
+  //\r
+  // Check the entries in the async I/O queue are done or not.\r
+  //\r
+  if (!IsListEmpty(&Private->Queue)) {\r
+    EFI_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->Queue) {\r
+      TransReq  = UFS_PASS_THRU_TRANS_REQ_FROM_THIS (Entry);\r
+      Packet    = TransReq->Packet;\r
+\r
+      if ((SlotsMap & (BIT0 << TransReq->Slot)) != 0) {\r
+        return;\r
+      }\r
+      SlotsMap |= BIT0 << TransReq->Slot;\r
+\r
+      Status = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Value);\r
+      if (EFI_ERROR (Status)) {\r
+        //\r
+        // TODO: Should find/add a proper host adapter return status for this\r
+        // case.\r
+        //\r
+        Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;\r
+        DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p UfsMmioRead32() Error.\n", TransReq->CallerEvent));\r
+        SignalCallerEvent (Private, TransReq);\r
+        continue;\r
+      }\r
+\r
+      if ((Value & (BIT0 << TransReq->Slot)) != 0) {\r
+        //\r
+        // Scsi cmd not finished yet.\r
+        //\r
+        if (TransReq->TimeoutRemain > UFS_HC_ASYNC_TIMER) {\r
+          TransReq->TimeoutRemain -= UFS_HC_ASYNC_TIMER;\r
+          continue;\r
+        } else {\r
+          //\r
+          // Timeout occurs.\r
+          //\r
+          Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;\r
+          DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT.\n", TransReq->CallerEvent));\r
+          SignalCallerEvent (Private, TransReq);\r
+          continue;\r
+        }\r
+      } else {\r
+        //\r
+        // Scsi cmd finished.\r
+        //\r
+        // Get sense data if exists\r
+        //\r
+        Response = (UTP_RESPONSE_UPIU*)((UINT8*)TransReq->CmdDescHost + TransReq->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_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Target Failure.\n", TransReq->CallerEvent));\r
+          SignalCallerEvent (Private, TransReq);\r
+          continue;\r
+        }\r
+\r
+        if (TransReq->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
+          DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Target Device Error.\n", TransReq->CallerEvent));\r
+          SignalCallerEvent (Private, TransReq);\r
+          continue;\r
+        }\r
+\r
+        DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Success.\n", TransReq->CallerEvent));\r
+        SignalCallerEvent (Private, TransReq);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r