]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AtaAtapiPassThru / AhciMode.c
index 5a6a3d7d0f862d5f7bbdb4a87e8b5ef67afcacfb..4d01c1dd7fca2c70d97250e9bace7840135e4d09 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   The file for AHCI mode of ATA host controller.\r
 \r
-  Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2015 Hewlett Packard Enterprise Development LP<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
@@ -156,9 +157,16 @@ AhciWaitMmioSet (
   )\r
 {\r
   UINT32     Value;\r
-  UINT32     Delay;\r
+  UINT64     Delay;\r
+  BOOLEAN    InfiniteWait;\r
 \r
-  Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
+  if (Timeout == 0) {\r
+    InfiniteWait = TRUE;\r
+  } else {\r
+    InfiniteWait = FALSE;\r
+  }\r
+\r
+  Delay = DivU64x32 (Timeout, 1000) + 1;\r
 \r
   do {\r
     //\r
@@ -177,7 +185,7 @@ AhciWaitMmioSet (
 \r
     Delay--;\r
 \r
-  } while (Delay > 0);\r
+  } while (InfiniteWait || (Delay > 0));\r
 \r
   return EFI_TIMEOUT;\r
 }\r
@@ -204,9 +212,16 @@ AhciWaitMemSet (
   )\r
 {\r
   UINT32     Value;\r
-  UINT32     Delay;\r
+  UINT64     Delay;\r
+  BOOLEAN    InfiniteWait;\r
+\r
+  if (Timeout == 0) {\r
+    InfiniteWait = TRUE;\r
+  } else {\r
+    InfiniteWait = FALSE;\r
+  }\r
 \r
-  Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
+  Delay =  DivU64x32 (Timeout, 1000) + 1;\r
 \r
   do {\r
     //\r
@@ -231,7 +246,7 @@ AhciWaitMemSet (
 \r
     Delay--;\r
 \r
-  } while (Delay > 0);\r
+  } while (InfiniteWait || (Delay > 0));\r
 \r
   return EFI_TIMEOUT;\r
 }\r
@@ -242,7 +257,8 @@ AhciWaitMemSet (
   @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]  RetryTimes        The retry times value for waitting memory set. If 0, then just try once.\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
 \r
   @retval EFI_NOTREADY      The memory is not set.\r
   @retval EFI_TIMEOUT       The memory setting retry times out.\r
@@ -255,13 +271,13 @@ AhciCheckMemSet (
   IN     UINTN                     Address,\r
   IN     UINT32                    MaskValue,\r
   IN     UINT32                    TestValue,\r
-  IN OUT UINTN                     *RetryTimes OPTIONAL\r
+  IN OUT ATA_NONBLOCK_TASK         *Task\r
   )\r
 {\r
   UINT32     Value;\r
 \r
-  if (RetryTimes != NULL) {\r
-    (*RetryTimes)--;\r
+  if (Task != NULL) {\r
+    Task->RetryTimes--;\r
   }\r
 \r
   Value  = *(volatile UINT32 *) Address;\r
@@ -271,7 +287,7 @@ AhciCheckMemSet (
     return EFI_SUCCESS;\r
   }\r
 \r
-  if ((RetryTimes != NULL) && (*RetryTimes == 0)) {\r
+  if ((Task != NULL) && !Task->InfiniteWait && (Task->RetryTimes == 0)) {\r
     return EFI_TIMEOUT;\r
   } else {\r
     return EFI_NOT_READY;\r
@@ -355,6 +371,7 @@ AhciClearPortStatus (
   in the Status Register, the Error Register's value is also be dumped.\r
 \r
   @param  PciIo            The PCI IO protocol instance.\r
+  @param  AhciRegisters    The pointer to the EFI_AHCI_REGISTERS.\r
   @param  Port             The number of port.\r
   @param  AtaStatusBlock   A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
 \r
@@ -363,24 +380,42 @@ VOID
 EFIAPI\r
 AhciDumpPortStatus (\r
   IN     EFI_PCI_IO_PROTOCOL        *PciIo,\r
+  IN     EFI_AHCI_REGISTERS         *AhciRegisters,\r
   IN     UINT8                      Port,\r
   IN OUT EFI_ATA_STATUS_BLOCK       *AtaStatusBlock\r
   )\r
 {\r
-  UINT32               Offset;\r
+  UINT               Offset;\r
   UINT32               Data;\r
+  UINTN                FisBaseAddr;\r
+  EFI_STATUS           Status;\r
 \r
   ASSERT (PciIo != NULL);\r
 \r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
-  Data   = AhciReadReg (PciIo, Offset);\r
-\r
   if (AtaStatusBlock != NULL) {\r
     ZeroMem (AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
 \r
-    AtaStatusBlock->AtaStatus  = (UINT8)Data;\r
-    if ((AtaStatusBlock->AtaStatus & BIT0) != 0) {\r
-      AtaStatusBlock->AtaError = (UINT8)(Data >> 8);\r
+    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
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // If D2H FIS is received, update StatusBlock with its content.\r
+      //\r
+      CopyMem (AtaStatusBlock, (UINT8 *)Offset, sizeof (EFI_ATA_STATUS_BLOCK));\r
+    } else {\r
+      //\r
+      // If D2H FIS is not received, only update Status & Error field through PxTFD\r
+      // as there is no other way to get the content of the Shadow Register Block.\r
+      //\r
+      Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+      Data   = AhciReadReg (PciIo, (UINT32)Offset);\r
+\r
+      AtaStatusBlock->AtaStatus  = (UINT8)Data;\r
+      if ((AtaStatusBlock->AtaStatus & BIT0) != 0) {\r
+        AtaStatusBlock->AtaError = (UINT8)(Data >> 8);\r
+      }\r
     }\r
   }\r
 }\r
@@ -411,13 +446,7 @@ AhciEnableFisReceive (
   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
   AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);\r
 \r
-  return AhciWaitMmioSet (\r
-           PciIo,\r
-           Offset,\r
-           EFI_AHCI_PORT_CMD_FR,\r
-           EFI_AHCI_PORT_CMD_FR,\r
-           Timeout\r
-           );\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -517,7 +546,7 @@ AhciBuildCommand (
   //\r
   // Filling the PRDT\r
   //\r
-  PrdtNumber = (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1) / EFI_AHCI_MAX_DATA_PER_PRDT;\r
+  PrdtNumber = (UINT32)DivU64x32 (((UINT64)DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1), EFI_AHCI_MAX_DATA_PER_PRDT);\r
 \r
   //\r
   // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.\r
@@ -548,7 +577,6 @@ AhciBuildCommand (
 \r
     CommandList->AhciCmdA = 1;\r
     CommandList->AhciCmdP = 1;\r
-    CommandList->AhciCmdC = (DataLength == 0) ? 1 : 0;\r
 \r
     AhciOrReg (PciIo, Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
   } else {\r
@@ -684,11 +712,20 @@ AhciPioTransfer (
   VOID                          *Map;\r
   UINTN                         MapLength;\r
   EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
-  UINT32                        Delay;\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
@@ -757,39 +794,56 @@ AhciPioTransfer (
     // Wait device sends the PIO setup fis before data transfer\r
     //\r
     Status = EFI_TIMEOUT;\r
-    Delay  = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
+    Delay  = DivU64x32 (Timeout, 1000) + 1;\r
     do {\r
-      Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
-      PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
-\r
-      if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
-        Status = EFI_DEVICE_ERROR;\r
-        break;\r
-      }\r
+      PioFisReceived = FALSE;\r
+      D2hFisReceived = FALSE;\r
       Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
-\r
-      Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);\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
-      Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
-      Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);\r
-      if (!EFI_ERROR (Status)) {\r
-        Status = EFI_DEVICE_ERROR;\r
-        break;\r
-      }\r
-\r
       //\r
       // Stall for 100 microseconds.\r
       //\r
       MicroSecondDelay(100);\r
 \r
       Delay--;\r
-    } while (Delay > 0);\r
+      if (Delay == 0) {\r
+        Status = EFI_TIMEOUT;\r
+      }\r
+    } while (InfiniteWait || (Delay > 0));\r
   } else {\r
     //\r
     // Wait for D2H Fis is received\r
@@ -831,7 +885,7 @@ Exit:
     Map\r
     );\r
 \r
-  AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
+  AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
 \r
   return Status;\r
 }\r
@@ -921,7 +975,6 @@ AhciDmaTransfer (
     //\r
     if (Task != NULL) {\r
       Task->IsStart      = TRUE;\r
-      Task->RetryTimes   = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
     }\r
     if (Read) {\r
       Flag = EfiPciIoOperationBusMasterWrite;\r
@@ -997,7 +1050,7 @@ AhciDmaTransfer (
                Offset,\r
                EFI_AHCI_FIS_TYPE_MASK,\r
                EFI_AHCI_FIS_REGISTER_D2H,\r
-               (UINTN *) (&Task->RetryTimes)\r
+               Task\r
                );\r
   } else {\r
     Status = AhciWaitMemSet (\r
@@ -1051,7 +1104,7 @@ Exit:
     }\r
   }\r
 \r
-  AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
+  AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
   return Status;\r
 }\r
 \r
@@ -1167,7 +1220,7 @@ Exit:
     Timeout\r
     );\r
 \r
-  AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
+  AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
 \r
   return Status;\r
 }\r
@@ -1301,10 +1354,6 @@ AhciStartCommand (
   //\r
   // Setting the command\r
   //\r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;\r
-  AhciAndReg (PciIo, Offset, 0);\r
-  AhciOrReg (PciIo, Offset, CmdSlotBit);\r
-\r
   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;\r
   AhciAndReg (PciIo, Offset, 0);\r
   AhciOrReg (PciIo, Offset, CmdSlotBit);\r
@@ -1372,6 +1421,7 @@ AhciPortReset (
              );\r
 \r
   if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Port %d COMRESET failed: %r\n", Port, Status));\r
     return Status;\r
   }\r
 \r
@@ -1399,14 +1449,21 @@ AhciReset (
   IN  UINT64                    Timeout\r
   )\r
 {\r
-  UINT32                 Delay;\r
+  UINT64                 Delay;\r
   UINT32                 Value;\r
 \r
-  AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
+  //\r
+  // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
+  //\r
+  Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
+\r
+  if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {\r
+    AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
+  }\r
 \r
   AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);\r
 \r
-  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+  Delay = DivU64x32(Timeout, 1000) + 1;\r
 \r
   do {\r
     Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
@@ -1436,7 +1493,7 @@ AhciReset (
   @param  PciIo               The PCI IO protocol instance.\r
   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
   @param  Port                The number of port.\r
-  @param  PortMultiplier      The timeout value of stop.\r
+  @param  PortMultiplier      The port multiplier port number.\r
   @param  AtaStatusBlock      A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
 \r
   @retval EFI_SUCCESS     Successfully get the return status of S.M.A.R.T command execution.\r
@@ -1484,9 +1541,18 @@ AhciAtaSmartReturnStatusCheck (
              );\r
 \r
   if (EFI_ERROR (Status)) {\r
+    REPORT_STATUS_CODE (\r
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+      (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)\r
+      );\r
     return EFI_DEVICE_ERROR;\r
   }\r
 \r
+  REPORT_STATUS_CODE (\r
+    EFI_PROGRESS_CODE,\r
+    (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)\r
+    );\r
+\r
   FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
 \r
   Value = *(UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET);\r
@@ -1500,12 +1566,19 @@ AhciAtaSmartReturnStatusCheck (
       // The threshold exceeded condition is not detected by the device\r
       //\r
       DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n"));\r
-\r
+      REPORT_STATUS_CODE (\r
+            EFI_PROGRESS_CODE,\r
+            (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)\r
+            );\r
     } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {\r
       //\r
       // The threshold exceeded condition is detected by the device\r
       //\r
       DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n"));\r
+      REPORT_STATUS_CODE (\r
+           EFI_PROGRESS_CODE,\r
+           (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)\r
+           );\r
     }\r
   }\r
 \r
@@ -1518,7 +1591,7 @@ AhciAtaSmartReturnStatusCheck (
   @param  PciIo               The PCI IO protocol instance.\r
   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
   @param  Port                The number of port.\r
-  @param  PortMultiplier      The timeout value of stop.\r
+  @param  PortMultiplier      The port multiplier port number.\r
   @param  IdentifyData        A pointer to data buffer which is used to contain IDENTIFY data.\r
   @param  AtaStatusBlock      A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
 \r
@@ -1546,11 +1619,21 @@ AhciAtaSmartSupport (
     //\r
     DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",\r
             Port, PortMultiplier));\r
+    REPORT_STATUS_CODE (\r
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+      (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)\r
+      );\r
   } else {\r
     //\r
     // Check if the feature is enabled. If not, then enable S.M.A.R.T.\r
     //\r
     if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) {\r
+\r
+      REPORT_STATUS_CODE (\r
+        EFI_PROGRESS_CODE,\r
+        (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE)\r
+        );\r
+\r
       ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
 \r
       AtaCommandBlock.AtaCommand      = ATA_CMD_SMART;\r
@@ -1624,7 +1707,7 @@ AhciAtaSmartSupport (
   @param  PciIo               The PCI IO protocol instance.\r
   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
   @param  Port                The number of port.\r
-  @param  PortMultiplier      The timeout value of stop.\r
+  @param  PortMultiplier      The port multiplier port number.\r
   @param  Buffer              The data buffer to store IDENTIFY PACKET data.\r
 \r
   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.\r
@@ -1682,7 +1765,7 @@ AhciIdentify (
   @param  PciIo               The PCI IO protocol instance.\r
   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
   @param  Port                The number of port.\r
-  @param  PortMultiplier      The timeout value of stop.\r
+  @param  PortMultiplier      The port multiplier port number.\r
   @param  Buffer              The data buffer to store IDENTIFY PACKET data.\r
 \r
   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.\r
@@ -1740,7 +1823,7 @@ AhciIdentifyPacket (
   @param  PciIo               The PCI IO protocol instance.\r
   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
   @param  Port                The number of port.\r
-  @param  PortMultiplier      The timeout value of stop.\r
+  @param  PortMultiplier      The port multiplier port number.\r
   @param  Feature             The data to send Feature register.\r
   @param  FeatureSpecificData The specific data for SET FEATURE cmd.\r
 \r
@@ -1904,6 +1987,7 @@ AhciCreateTransferDescriptor (
   VOID                  *Buffer;\r
 \r
   UINT32                Capability;\r
+  UINT32                PortImplementBitMap;\r
   UINT8                 MaxPortNumber;\r
   UINT8                 MaxCommandSlotNumber;\r
   BOOLEAN               Support64Bit;\r
@@ -1919,12 +2003,20 @@ AhciCreateTransferDescriptor (
   // Collect AHCI controller information\r
   //\r
   Capability           = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
-  MaxPortNumber        = (UINT8) ((Capability & 0x1F) + 1);\r
   //\r
   // Get the number of command slots per port supported by this HBA.\r
   //\r
   MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
   Support64Bit         = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE);\r
+  \r
+  PortImplementBitMap  = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);\r
+  //\r
+  // Get the highest bit of implemented ports which decides how many bytes are allocated for recived FIS.\r
+  //\r
+  MaxPortNumber        = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
+  if (MaxPortNumber == 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
 \r
   MaxReceiveFisSize    = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
   Status = PciIo->AllocateBuffer (\r
@@ -2156,6 +2248,7 @@ AhciModeInitialization (
   EFI_ATA_COLLECTIVE_MODE          *SupportedModes;\r
   EFI_ATA_TRANSFER_MODE            TransferMode;\r
   UINT32                           PhyDetectDelay;\r
+  UINT32                           Value;\r
 \r
   if (Instance == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -2171,14 +2264,36 @@ AhciModeInitialization (
   }\r
 \r
   //\r
-  // Enable AE before accessing any AHCI registers\r
+  // Collect AHCI controller information\r
+  //\r
+  Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
+\r
+  //\r
+  // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
   //\r
-  AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
+  Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
+\r
+  if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {\r
+    AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
+  }\r
 \r
   //\r
-  // Collect AHCI controller information\r
+  // Enable 64-bit DMA support in the PCI layer if this controller\r
+  // supports it.\r
   //\r
-  Capability           = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
+  if ((Capability & EFI_AHCI_CAP_S64A) != 0) {\r
+    Status = PciIo->Attributes (\r
+                      PciIo,\r
+                      EfiPciIoAttributeOperationEnable,\r
+                      EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,\r
+                      NULL\r
+                      );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_WARN,\r
+        "AhciModeInitialization: failed to enable 64-bit DMA on 64-bit capable controller (%r)\n",\r
+        Status));\r
+    }\r
+  }\r
 \r
   //\r
   // Get the number of command slots per port supported by this HBA.\r
@@ -2198,8 +2313,19 @@ AhciModeInitialization (
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  for (Port = 0; Port < MaxPortNumber; Port ++) {\r
+  for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port ++) {\r
     if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
+      //\r
+      // According to AHCI spec, MaxPortNumber should be equal or greater than the number of implemented ports.\r
+      //\r
+      if ((MaxPortNumber--) == 0) {\r
+        //\r
+        // Should never be here.\r
+        //\r
+        ASSERT (FALSE);\r
+        return EFI_SUCCESS;\r
+      }\r
+\r
       IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, Port);\r
 \r
       //\r
@@ -2248,16 +2374,6 @@ AhciModeInitialization (
       //\r
       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
       AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);\r
-      Status = AhciWaitMmioSet (\r
-                 PciIo,\r
-                 Offset,\r
-                 EFI_AHCI_PORT_CMD_FR,\r
-                 EFI_AHCI_PORT_CMD_FR,\r
-                 EFI_AHCI_PORT_CMD_FR_CLEAR_TIMEOUT\r
-                 );\r
-      if (EFI_ERROR (Status)) {\r
-        continue;\r
-      }\r
 \r
       //\r
       // Wait no longer than 10 ms to wait the Phy to detect the presence of a device.\r
@@ -2278,7 +2394,10 @@ AhciModeInitialization (
       if (PhyDetectDelay == 0) {\r
         //\r
         // No device detected at this port.\r
+        // Clear PxCMD.SUD for those ports at which there are no device present.\r
         //\r
+        Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+        AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_SUD));\r
         continue;\r
       }\r
 \r
@@ -2304,6 +2423,7 @@ AhciModeInitialization (
       } while (PhyDetectDelay > 0);\r
 \r
       if (PhyDetectDelay == 0) {\r
+        DEBUG ((EFI_D_ERROR, "Port %d Device presence detected but phy not ready (TFD=0x%X)\n", Port, Data));\r
         continue;\r
       }\r
 \r
@@ -2349,7 +2469,7 @@ AhciModeInitialization (
       //\r
       // If the device is a hard disk, then try to enable S.M.A.R.T feature\r
       //\r
-      if (DeviceType == EfiIdeHarddisk) {\r
+      if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) {\r
         AhciAtaSmartSupport (\r
           PciIo,\r
           AhciRegisters,\r
@@ -2413,7 +2533,7 @@ AhciModeInitialization (
       //\r
       // Found a ATA or ATAPI device, add it into the device list.\r
       //\r
-      CreateNewDeviceInfo (Instance, Port, 0, DeviceType, &Buffer);\r
+      CreateNewDeviceInfo (Instance, Port, 0xFFFF, DeviceType, &Buffer);\r
       if (DeviceType == EfiIdeHarddisk) {\r
         REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));\r
       }\r