]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg/AtaAtapiPassThru: Check IS to check for command completion
authorAlbecki, Mateusz <mateusz.albecki@intel.com>
Thu, 5 Nov 2020 12:48:44 +0000 (20:48 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Wed, 11 Nov 2020 02:27:59 +0000 (02:27 +0000)
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3024

AHCI driver used to poll D2H register type to determine whether the FIS
has been received. This caused a problem of long timeouts when the link
got a CRC error and the FIS never arrives. To fix this this change
switches AHCI driver to poll the IS register which will signal both the
reception of FIS and the occurrence of error.

Signed-off-by: Mateusz Albecki <mateusz.albecki@intel.com>
Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h

index 7e2fade400e30c31b970113c30265f17bc1ced30..180a60b5aac16f7172ddc39f687f9b228640a21a 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   The file for AHCI mode of ATA host controller.\r
 \r
-  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>\r
   (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
@@ -248,32 +248,23 @@ AhciWaitMemSet (
 /**\r
   Check the memory status to the test value.\r
 \r
-  @param[in]       Address           The memory address to test.\r
-  @param[in]       MaskValue         The mask value of memory.\r
-  @param[in]       TestValue         The test value of memory.\r
-  @param[in, out]  Task              Optional. Pointer to the ATA_NONBLOCK_TASK used by\r
-                                     non-blocking mode. If NULL, then just try once.\r
+  @param[in] Address    The memory address to test.\r
+  @param[in] MaskValue  The mask value of memory.\r
+  @param[in] TestValue  The test value of memory.\r
 \r
-  @retval EFI_NOTREADY      The memory is not set.\r
-  @retval EFI_TIMEOUT       The memory setting retry times out.\r
+  @retval EFI_NOT_READY     The memory is not set.\r
   @retval EFI_SUCCESS       The memory is correct set.\r
-\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
 AhciCheckMemSet (\r
   IN     UINTN                     Address,\r
   IN     UINT32                    MaskValue,\r
-  IN     UINT32                    TestValue,\r
-  IN OUT ATA_NONBLOCK_TASK         *Task\r
+  IN     UINT32                    TestValue\r
   )\r
 {\r
   UINT32     Value;\r
 \r
-  if (Task != NULL) {\r
-    Task->RetryTimes--;\r
-  }\r
-\r
   Value  = *(volatile UINT32 *) Address;\r
   Value &= MaskValue;\r
 \r
@@ -281,11 +272,7 @@ AhciCheckMemSet (
     return EFI_SUCCESS;\r
   }\r
 \r
-  if ((Task != NULL) && !Task->InfiniteWait && (Task->RetryTimes == 0)) {\r
-    return EFI_TIMEOUT;\r
-  } else {\r
-    return EFI_NOT_READY;\r
-  }\r
+  return EFI_NOT_READY;\r
 }\r
 \r
 \r
@@ -357,7 +344,7 @@ AhciDumpPortStatus (
     FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
     Offset      = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
 \r
-    Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
+    Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H);\r
     if (!EFI_ERROR (Status)) {\r
       //\r
       // If D2H FIS is received, update StatusBlock with its content.\r
@@ -621,6 +608,102 @@ AhciBuildCommandFis (
   CmdFis->AhciCFisDevHead     = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
 }\r
 \r
+/**\r
+  Checks if specified FIS has been received.\r
+\r
+  @param[in] PciIo    Pointer to AHCI controller PciIo.\r
+  @param[in] Port     SATA port index on which to check.\r
+  @param[in] FisType  FIS type for which to check.\r
+\r
+  @retval EFI_SUCCESS       FIS received.\r
+  @retval EFI_NOT_READY     FIS not received yet.\r
+  @retval EFI_DEVICE_ERROR  AHCI controller reported an error on port.\r
+**/\r
+EFI_STATUS\r
+AhciCheckFisReceived (\r
+  IN EFI_PCI_IO_PROTOCOL  *PciIo,\r
+  IN UINT8                Port,\r
+  IN SATA_FIS_TYPE        FisType\r
+  )\r
+{\r
+  UINT32      Offset;\r
+  UINT32      PortInterrupt;\r
+  UINT32      PortTfd;\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;\r
+  PortInterrupt = AhciReadReg (PciIo, Offset);\r
+  if ((PortInterrupt & EFI_AHCI_PORT_IS_ERROR_MASK) != 0) {\r
+    DEBUG ((DEBUG_ERROR, "AHCI: Error interrupt reported PxIS: %X\n", PortInterrupt));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // For PIO setup FIS - According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
+  // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from device\r
+  // after the transaction is finished successfully.\r
+  // To get better device compatibilities, we further check if the PxTFD's ERR bit is set.\r
+  // By this way, we can know if there is a real error happened.\r
+  //\r
+  if (((FisType == SataFisD2H) && ((PortInterrupt & EFI_AHCI_PORT_IS_DHRS) != 0)) ||\r
+      ((FisType == SataFisPioSetup) && (PortInterrupt & (EFI_AHCI_PORT_IS_PSS | EFI_AHCI_PORT_IS_DHRS)) != 0) ||\r
+      ((FisType == SataFisDmaSetup) && (PortInterrupt & (EFI_AHCI_PORT_IS_DSS | EFI_AHCI_PORT_IS_DHRS)) != 0)) {\r
+    Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+    PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
+    if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
+      return EFI_DEVICE_ERROR;\r
+    } else {\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+  Waits until specified FIS has been received.\r
+\r
+  @param[in] PciIo    Pointer to AHCI controller PciIo.\r
+  @param[in] Port     SATA port index on which to check.\r
+  @param[in] Timeout  Time after which function should stop polling.\r
+  @param[in] FisType  FIS type for which to check.\r
+\r
+  @retval EFI_SUCCESS       FIS received.\r
+  @retval EFI_TIMEOUT       FIS failed to arrive within a specified time period.\r
+  @retval EFI_DEVICE_ERROR  AHCI controller reported an error on port.\r
+**/\r
+EFI_STATUS\r
+AhciWaitUntilFisReceived (\r
+  IN EFI_PCI_IO_PROTOCOL  *PciIo,\r
+  IN UINT8                Port,\r
+  IN UINT64               Timeout,\r
+  IN SATA_FIS_TYPE        FisType\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  BOOLEAN     InfiniteWait;\r
+  UINT64      Delay;\r
+\r
+  Delay =  DivU64x32 (Timeout, 1000) + 1;\r
+  if (Timeout == 0) {\r
+    InfiniteWait = TRUE;\r
+  } else {\r
+    InfiniteWait = FALSE;\r
+  }\r
+\r
+  do {\r
+    Status = AhciCheckFisReceived (PciIo, Port, FisType);\r
+    if (Status != EFI_NOT_READY) {\r
+      return Status;\r
+    }\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+    Delay--;\r
+  } while (InfiniteWait || (Delay > 0));\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
 /**\r
   Start a PIO data transfer on specific port.\r
 \r
@@ -665,26 +748,13 @@ AhciPioTransfer (
   )\r
 {\r
   EFI_STATUS                    Status;\r
-  UINTN                         FisBaseAddr;\r
-  UINTN                         Offset;\r
   EFI_PHYSICAL_ADDRESS          PhyAddr;\r
   VOID                          *Map;\r
   UINTN                         MapLength;\r
   EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
-  UINT64                        Delay;\r
   EFI_AHCI_COMMAND_FIS          CFis;\r
   EFI_AHCI_COMMAND_LIST         CmdList;\r
-  UINT32                        PortTfd;\r
   UINT32                        PrdCount;\r
-  BOOLEAN                       InfiniteWait;\r
-  BOOLEAN                       PioFisReceived;\r
-  BOOLEAN                       D2hFisReceived;\r
-\r
-  if (Timeout == 0) {\r
-    InfiniteWait = TRUE;\r
-  } else {\r
-    InfiniteWait = FALSE;\r
-  }\r
 \r
   if (Read) {\r
     Flag = EfiPciIoOperationBusMasterWrite;\r
@@ -743,87 +813,18 @@ AhciPioTransfer (
     goto Exit;\r
   }\r
 \r
-  //\r
-  // Check the status and wait the driver sending data\r
-  //\r
-  FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
-\r
   if (Read && (AtapiCommand == 0)) {\r
-    //\r
-    // Wait device sends the PIO setup fis before data transfer\r
-    //\r
-    Status = EFI_TIMEOUT;\r
-    Delay  = DivU64x32 (Timeout, 1000) + 1;\r
-    do {\r
-      PioFisReceived = FALSE;\r
-      D2hFisReceived = FALSE;\r
-      Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
-      Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, NULL);\r
-      if (!EFI_ERROR (Status)) {\r
-        PioFisReceived = TRUE;\r
-      }\r
-      //\r
-      // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
-      // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from device\r
-      // after the transaction is finished successfully.\r
-      // To get better device compatibilities, we further check if the PxTFD's ERR bit is set.\r
-      // By this way, we can know if there is a real error happened.\r
-      //\r
-      Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
-      Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
-      if (!EFI_ERROR (Status)) {\r
-        D2hFisReceived = TRUE;\r
-      }\r
-\r
-      if (PioFisReceived || D2hFisReceived) {\r
-        Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
-        PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
-        //\r
-        // PxTFD will be updated if there is a D2H or SetupFIS received.\r
-        //\r
-        if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
-          Status = EFI_DEVICE_ERROR;\r
-          break;\r
-        }\r
-\r
-        PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
-        if (PrdCount == DataCount) {\r
-          Status = EFI_SUCCESS;\r
-          break;\r
-        }\r
-      }\r
-\r
-      //\r
-      // Stall for 100 microseconds.\r
-      //\r
-      MicroSecondDelay(100);\r
-\r
-      Delay--;\r
-      if (Delay == 0) {\r
-        Status = EFI_TIMEOUT;\r
+    Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisPioSetup);\r
+    if (Status == EFI_SUCCESS) {\r
+      PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
+      if (PrdCount == DataCount) {\r
+        Status = EFI_SUCCESS;\r
+      } else {\r
+        Status = EFI_DEVICE_ERROR;\r
       }\r
-    } while (InfiniteWait || (Delay > 0));\r
-  } else {\r
-    //\r
-    // Wait for D2H Fis is received\r
-    //\r
-    Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
-    Status = AhciWaitMemSet (\r
-               Offset,\r
-               EFI_AHCI_FIS_TYPE_MASK,\r
-               EFI_AHCI_FIS_REGISTER_D2H,\r
-               Timeout\r
-               );\r
-\r
-    if (EFI_ERROR (Status)) {\r
-      goto Exit;\r
-    }\r
-\r
-    Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
-    PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
-    if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
-      Status = EFI_DEVICE_ERROR;\r
     }\r
+  } else {\r
+    Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
   }\r
 \r
 Exit:\r
@@ -893,15 +894,12 @@ AhciDmaTransfer (
   )\r
 {\r
   EFI_STATUS                    Status;\r
-  UINTN                         Offset;\r
   EFI_PHYSICAL_ADDRESS          PhyAddr;\r
   VOID                          *Map;\r
   UINTN                         MapLength;\r
   EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
   EFI_AHCI_COMMAND_FIS          CFis;\r
   EFI_AHCI_COMMAND_LIST         CmdList;\r
-  UINTN                         FisBaseAddr;\r
-  UINT32                        PortTfd;\r
 \r
   EFI_PCI_IO_PROTOCOL           *PciIo;\r
   EFI_TPL                       OldTpl;\r
@@ -996,38 +994,17 @@ AhciDmaTransfer (
     }\r
   }\r
 \r
-  //\r
-  // Wait for command complete\r
-  //\r
-  FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
-  Offset      = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
   if (Task != NULL) {\r
-    //\r
-    // For Non-blocking\r
-    //\r
-    Status = AhciCheckMemSet (\r
-               Offset,\r
-               EFI_AHCI_FIS_TYPE_MASK,\r
-               EFI_AHCI_FIS_REGISTER_D2H,\r
-               Task\r
-               );\r
+    Status = AhciCheckFisReceived (PciIo, Port, SataFisD2H);\r
+    if (Status == EFI_NOT_READY) {\r
+      if (!Task->InfiniteWait && Task->RetryTimes == 0) {\r
+        Status = EFI_TIMEOUT;\r
+      } else {\r
+        Task->RetryTimes--;\r
+      }\r
+    }\r
   } else {\r
-    Status = AhciWaitMemSet (\r
-               Offset,\r
-               EFI_AHCI_FIS_TYPE_MASK,\r
-               EFI_AHCI_FIS_REGISTER_D2H,\r
-               Timeout\r
-               );\r
-  }\r
-\r
-  if (EFI_ERROR (Status)) {\r
-    goto Exit;\r
-  }\r
-\r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
-  PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
-  if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
-    Status = EFI_DEVICE_ERROR;\r
+    Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
   }\r
 \r
 Exit:\r
@@ -1105,9 +1082,6 @@ AhciNonDataTransfer (
   )\r
 {\r
   EFI_STATUS                   Status;\r
-  UINTN                        FisBaseAddr;\r
-  UINTN                        Offset;\r
-  UINT32                       PortTfd;\r
   EFI_AHCI_COMMAND_FIS         CFis;\r
   EFI_AHCI_COMMAND_LIST        CmdList;\r
 \r
@@ -1144,27 +1118,7 @@ AhciNonDataTransfer (
     goto Exit;\r
   }\r
 \r
-  //\r
-  // Wait device sends the Response Fis\r
-  //\r
-  FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
-  Offset      = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
-  Status      = AhciWaitMemSet (\r
-                  Offset,\r
-                  EFI_AHCI_FIS_TYPE_MASK,\r
-                  EFI_AHCI_FIS_REGISTER_D2H,\r
-                  Timeout\r
-                  );\r
-\r
-  if (EFI_ERROR (Status)) {\r
-    goto Exit;\r
-  }\r
-\r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
-  PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
-  if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
-    Status = EFI_DEVICE_ERROR;\r
-  }\r
+  Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
 \r
 Exit:\r
   AhciStopCommand (\r
index 786413930ae97ee03fd127d0b8f50614a90751f0..6bcff1bb7b550950b15cf84da87805e408460cbb 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Header file for AHCI mode of ATA host controller.\r
 \r
-  Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>\r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -96,7 +96,7 @@ typedef union {
 #define EFI_AHCI_PORT_IS                       0x0010\r
 #define   EFI_AHCI_PORT_IS_DHRS                BIT0\r
 #define   EFI_AHCI_PORT_IS_PSS                 BIT1\r
-#define   EFI_AHCI_PORT_IS_SSS                 BIT2\r
+#define   EFI_AHCI_PORT_IS_DSS                 BIT2\r
 #define   EFI_AHCI_PORT_IS_SDBS                BIT3\r
 #define   EFI_AHCI_PORT_IS_UFS                 BIT4\r
 #define   EFI_AHCI_PORT_IS_DPS                 BIT5\r
@@ -113,6 +113,7 @@ typedef union {
 #define   EFI_AHCI_PORT_IS_CPDS                BIT31\r
 #define   EFI_AHCI_PORT_IS_CLEAR               0xFFFFFFFF\r
 #define   EFI_AHCI_PORT_IS_FIS_CLEAR           0x0000000F\r
+#define   EFI_AHCI_PORT_IS_ERROR_MASK          (EFI_AHCI_PORT_IS_INFS | EFI_AHCI_PORT_IS_IFS | EFI_AHCI_PORT_IS_HBDS | EFI_AHCI_PORT_IS_HBFS | EFI_AHCI_PORT_IS_TFES)\r
 \r
 #define EFI_AHCI_PORT_IE                       0x0014\r
 #define EFI_AHCI_PORT_CMD                      0x0018\r
@@ -240,6 +241,12 @@ typedef struct {
   UINT8    AhciCFisRsvd5[44];\r
 } EFI_AHCI_COMMAND_FIS;\r
 \r
+typedef enum {\r
+  SataFisD2H = 0,\r
+  SataFisPioSetup,\r
+  SataFisDmaSetup\r
+} SATA_FIS_TYPE;\r
+\r
 //\r
 // ACMD: ATAPI command (12 or 16 bytes)\r
 //\r