]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
MdeModulePkg/Ata/AtaAtapiPassThru: Enable/disable DEVSLP per policy
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AtaAtapiPassThru / AhciMode.c
index 3534d9fc2328872ae099d5c85c7c4143cb5d22ea..1bae1e8e0cfa09c7a11dfc0d12c59547b297d686 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   The file for AHCI mode of ATA host controller.\r
 \r
-  Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2010 - 2018, 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
@@ -371,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
@@ -379,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
@@ -427,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
@@ -872,7 +885,7 @@ Exit:
     Map\r
     );\r
 \r
-  AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
+  AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
 \r
   return Status;\r
 }\r
@@ -1091,7 +1104,7 @@ Exit:
     }\r
   }\r
 \r
-  AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
+  AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
   return Status;\r
 }\r
 \r
@@ -1207,7 +1220,7 @@ Exit:
     Timeout\r
     );\r
 \r
-  AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
+  AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
 \r
   return Status;\r
 }\r
@@ -1438,17 +1451,13 @@ AhciReset (
 {\r
   UINT64                 Delay;\r
   UINT32                 Value;\r
-  UINT32                 Capability;\r
 \r
   //\r
-  // Collect AHCI controller information\r
-  //\r
-  Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
-  \r
+  // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
   //\r
-  // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
-  //\r
-  if ((Capability & EFI_AHCI_CAP_SAM) == 0) {\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
@@ -1817,6 +1826,7 @@ AhciIdentifyPacket (
   @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
+  @param  Timeout             The timeout value of SET FEATURE cmd, uses 100ns as a unit.\r
 \r
   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.\r
   @retval EFI_TIMEOUT         The operation is time out.\r
@@ -1832,7 +1842,8 @@ AhciDeviceSetFeature (
   IN UINT8                  Port,\r
   IN UINT8                  PortMultiplier,\r
   IN UINT16                 Feature,\r
-  IN UINT32                 FeatureSpecificData\r
+  IN UINT32                 FeatureSpecificData,\r
+  IN UINT64                 Timeout\r
   )\r
 {\r
   EFI_STATUS               Status;\r
@@ -1859,7 +1870,7 @@ AhciDeviceSetFeature (
              0,\r
              &AtaCommandBlock,\r
              &AtaStatusBlock,\r
-             ATA_ATAPI_TIMEOUT,\r
+             Timeout,\r
              NULL\r
              );\r
 \r
@@ -2207,6 +2218,343 @@ Error6:
   return Status;\r
 }\r
 \r
+/**\r
+  Read logs from SATA device.\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  PortMultiplier      The multiplier of port.\r
+  @param  Buffer              The data buffer to store SATA logs.\r
+  @param  LogNumber           The address of the log.\r
+  @param  PageNumber          The page number of the log.\r
+\r
+  @retval EFI_INVALID_PARAMETER  PciIo, AhciRegisters or Buffer is NULL.\r
+  @retval others                 Return status of AhciPioTransfer().\r
+**/\r
+EFI_STATUS\r
+AhciReadLogExt (\r
+  IN EFI_PCI_IO_PROTOCOL       *PciIo,\r
+  IN EFI_AHCI_REGISTERS        *AhciRegisters,\r
+  IN UINT8                     Port,\r
+  IN UINT8                     PortMultiplier,\r
+  IN OUT UINT8                 *Buffer,\r
+  IN UINT8                     LogNumber,\r
+  IN UINT8                     PageNumber\r
+  )\r
+{\r
+  EFI_ATA_COMMAND_BLOCK        AtaCommandBlock;\r
+  EFI_ATA_STATUS_BLOCK         AtaStatusBlock;\r
+\r
+  if (PciIo == NULL || AhciRegisters == NULL || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ///\r
+  /// Read log from device\r
+  ///\r
+  ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+  ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
+  ZeroMem (Buffer, 512);\r
+\r
+  AtaCommandBlock.AtaCommand      = ATA_CMD_READ_LOG_EXT;\r
+  AtaCommandBlock.AtaSectorCount  = 1;\r
+  AtaCommandBlock.AtaSectorNumber = LogNumber;\r
+  AtaCommandBlock.AtaCylinderLow  = PageNumber;\r
+\r
+  return AhciPioTransfer (\r
+           PciIo,\r
+           AhciRegisters,\r
+           Port,\r
+           PortMultiplier,\r
+           NULL,\r
+           0,\r
+           TRUE,\r
+           &AtaCommandBlock,\r
+           &AtaStatusBlock,\r
+           Buffer,\r
+           512,\r
+           ATA_ATAPI_TIMEOUT,\r
+           NULL\r
+           );\r
+}\r
+\r
+/**\r
+  Enable DEVSLP of the disk if supported.\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  PortMultiplier      The multiplier of port.\r
+  @param  IdentifyData        A pointer to data buffer which is used to contain IDENTIFY data.\r
+\r
+  @retval EFI_SUCCESS         The DEVSLP is enabled per policy successfully.\r
+  @retval EFI_UNSUPPORTED     The DEVSLP isn't supported by the controller/device and policy requires to enable it.\r
+**/\r
+EFI_STATUS\r
+AhciEnableDevSlp (\r
+  IN EFI_PCI_IO_PROTOCOL           *PciIo,\r
+  IN EFI_AHCI_REGISTERS            *AhciRegisters,\r
+  IN UINT8                         Port,\r
+  IN UINT8                         PortMultiplier,\r
+  IN EFI_IDENTIFY_DATA             *IdentifyData\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  UINT32                   Offset;\r
+  UINT32                   Capability2;\r
+  UINT8                    LogData[512];\r
+  DEVSLP_TIMING_VARIABLES  DevSlpTiming;\r
+  UINT32                   PortCmd;\r
+  UINT32                   PortDevSlp;\r
+\r
+  if (mAtaAtapiPolicy->DeviceSleepEnable != 1) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Do not enable DevSlp if DevSlp is not supported.\r
+  //\r
+  Capability2 = AhciReadReg (PciIo, AHCI_CAPABILITY2_OFFSET);\r
+  DEBUG ((DEBUG_INFO, "AHCI CAPABILITY2 = %08x\n", Capability2));\r
+  if ((Capability2 & AHCI_CAP2_SDS) == 0) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Do not enable DevSlp if DevSlp is not present\r
+  // Do not enable DevSlp if Hot Plug or Mechanical Presence Switch is supported\r
+  //\r
+  Offset     = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH;\r
+  PortCmd    = AhciReadReg (PciIo, Offset + EFI_AHCI_PORT_CMD);\r
+  PortDevSlp = AhciReadReg (PciIo, Offset + AHCI_PORT_DEVSLP);\r
+  DEBUG ((DEBUG_INFO, "Port CMD/DEVSLP = %08x / %08x\n", PortCmd, PortDevSlp));\r
+  if (((PortDevSlp & AHCI_PORT_DEVSLP_DSP) == 0) ||\r
+      ((PortCmd & (EFI_AHCI_PORT_CMD_HPCP | EFI_AHCI_PORT_CMD_MPSP)) != 0)\r
+     ) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Do not enable DevSlp if the device doesn't support DevSlp\r
+  //\r
+  DEBUG ((DEBUG_INFO, "IDENTIFY DEVICE: [77] = %04x, [78] = %04x, [79] = %04x\n",\r
+          IdentifyData->AtaData.reserved_77,\r
+          IdentifyData->AtaData.serial_ata_features_supported, IdentifyData->AtaData.serial_ata_features_enabled));\r
+  if ((IdentifyData->AtaData.serial_ata_features_supported & BIT8) == 0) {\r
+    DEBUG ((DEBUG_INFO, "DevSlp feature is not supported for device at port [%d] PortMultiplier [%d]!\n",\r
+            Port, PortMultiplier));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Enable DevSlp when it is not enabled.\r
+  //\r
+  if ((IdentifyData->AtaData.serial_ata_features_enabled & BIT8) != 0) {\r
+    Status = AhciDeviceSetFeature (\r
+      PciIo, AhciRegisters, Port, 0, ATA_SUB_CMD_ENABLE_SATA_FEATURE, 0x09, ATA_ATAPI_TIMEOUT\r
+    );\r
+    DEBUG ((DEBUG_INFO, "DevSlp set feature for device at port [%d] PortMultiplier [%d] - %r\n",\r
+            Port, PortMultiplier, Status));\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  Status = AhciReadLogExt(PciIo, AhciRegisters, Port, PortMultiplier, LogData, 0x30, 0x08);\r
+\r
+  //\r
+  // Clear PxCMD.ST and PxDEVSLP.ADSE before updating PxDEVSLP.DITO and PxDEVSLP.MDAT.\r
+  //\r
+  AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd & ~EFI_AHCI_PORT_CMD_ST);\r
+  PortDevSlp &= ~AHCI_PORT_DEVSLP_ADSE;\r
+  AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
+\r
+  //\r
+  // Set PxDEVSLP.DETO and PxDEVSLP.MDAT to 0.\r
+  //\r
+  PortDevSlp &= ~AHCI_PORT_DEVSLP_DETO_MASK;\r
+  PortDevSlp &= ~AHCI_PORT_DEVSLP_MDAT_MASK;\r
+  AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
+  DEBUG ((DEBUG_INFO, "Read Log Ext at port [%d] PortMultiplier [%d] - %r\n", Port, PortMultiplier, Status));\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Assume DEVSLP TIMING VARIABLES is not supported if the Identify Device Data log (30h, 8) fails\r
+    //\r
+    DevSlpTiming.Supported = 0;\r
+  } else {\r
+    CopyMem (&DevSlpTiming, &LogData[48], sizeof (DevSlpTiming));\r
+    DEBUG ((DEBUG_INFO, "DevSlpTiming: Supported(%d), Deto(%d), Madt(%d)\n",\r
+            DevSlpTiming.Supported, DevSlpTiming.Deto, DevSlpTiming.Madt));\r
+  }\r
+\r
+  //\r
+  // Use 20ms as default DETO when DEVSLP TIMING VARIABLES is not supported or the DETO is 0.\r
+  //\r
+  if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Deto == 0)) {\r
+    DevSlpTiming.Deto = 20;\r
+  }\r
+\r
+  //\r
+  // Use 10ms as default MADT when DEVSLP TIMING VARIABLES is not supported or the MADT is 0.\r
+  //\r
+  if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Madt == 0)) {\r
+    DevSlpTiming.Madt = 10;\r
+  }\r
+\r
+  PortDevSlp |= DevSlpTiming.Deto << 2;\r
+  PortDevSlp |= DevSlpTiming.Madt << 10;\r
+  AhciOrReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
+\r
+  if (mAtaAtapiPolicy->AggressiveDeviceSleepEnable == 1) {\r
+    if ((Capability2 & AHCI_CAP2_SADM) != 0) {\r
+      PortDevSlp &= ~AHCI_PORT_DEVSLP_DITO_MASK;\r
+      PortDevSlp |= (625 << 15);\r
+      AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
+\r
+      PortDevSlp |= AHCI_PORT_DEVSLP_ADSE;\r
+      AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
+    }\r
+  }\r
+\r
+\r
+  AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd);\r
+\r
+  DEBUG ((DEBUG_INFO, "Enabled DevSlp feature at port [%d] PortMultiplier [%d], Port CMD/DEVSLP = %08x / %08x\n",\r
+          Port, PortMultiplier, PortCmd, PortDevSlp));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Spin-up disk if IDD was incomplete or PUIS feature is enabled\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  PortMultiplier      The multiplier of port.\r
+  @param  IdentifyData        A pointer to data buffer which is used to contain IDENTIFY data.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciSpinUpDisk (\r
+  IN EFI_PCI_IO_PROTOCOL           *PciIo,\r
+  IN EFI_AHCI_REGISTERS            *AhciRegisters,\r
+  IN UINT8                         Port,\r
+  IN UINT8                         PortMultiplier,\r
+  IN OUT EFI_IDENTIFY_DATA         *IdentifyData\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  EFI_ATA_COMMAND_BLOCK    AtaCommandBlock;\r
+  EFI_ATA_STATUS_BLOCK     AtaStatusBlock;\r
+  UINT8                    Buffer[512];\r
+\r
+  if (IdentifyData->AtaData.specific_config == ATA_SPINUP_CFG_REQUIRED_IDD_INCOMPLETE) {\r
+    //\r
+    // Use SET_FEATURE subcommand to spin up the device.\r
+    //\r
+    Status = AhciDeviceSetFeature (\r
+               PciIo, AhciRegisters, Port, PortMultiplier,\r
+               ATA_SUB_CMD_PUIS_SET_DEVICE_SPINUP, 0x00, ATA_SPINUP_TIMEOUT\r
+               );\r
+    DEBUG ((DEBUG_INFO, "CMD_PUIS_SET_DEVICE_SPINUP for device at port [%d] PortMultiplier [%d] - %r!\n",\r
+            Port, PortMultiplier, Status));\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    ASSERT (IdentifyData->AtaData.specific_config == ATA_SPINUP_CFG_NOT_REQUIRED_IDD_INCOMPLETE);\r
+\r
+    //\r
+    // Use READ_SECTORS to spin up the device if SpinUp SET FEATURE subcommand is not supported\r
+    //\r
+    ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+    ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
+    //\r
+    // Perform READ SECTORS PIO Data-In command to Read LBA 0\r
+    //\r
+    AtaCommandBlock.AtaCommand      = ATA_CMD_READ_SECTORS;\r
+    AtaCommandBlock.AtaSectorCount  = 0x1;\r
+\r
+    Status = AhciPioTransfer (\r
+               PciIo,\r
+               AhciRegisters,\r
+               Port,\r
+               PortMultiplier,\r
+               NULL,\r
+               0,\r
+               TRUE,\r
+               &AtaCommandBlock,\r
+               &AtaStatusBlock,\r
+               &Buffer,\r
+               sizeof (Buffer),\r
+               ATA_SPINUP_TIMEOUT,\r
+               NULL\r
+               );\r
+    DEBUG ((DEBUG_INFO, "Read LBA 0 for device at port [%d] PortMultiplier [%d] - %r!\n",\r
+            Port, PortMultiplier, Status));\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Read the complete IDENTIFY DEVICE data.\r
+  //\r
+  ZeroMem (IdentifyData, sizeof (*IdentifyData));\r
+  Status = AhciIdentify (PciIo, AhciRegisters, Port, PortMultiplier, IdentifyData);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "Read IDD failed for device at port [%d] PortMultiplier [%d] - %r!\n",\r
+            Port, PortMultiplier, Status));\r
+    return Status;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "IDENTIFY DEVICE: [0] = %016x, [2] = %016x, [83] = %016x, [86] = %016x\n",\r
+          IdentifyData->AtaData.config, IdentifyData->AtaData.specific_config,\r
+          IdentifyData->AtaData.command_set_supported_83, IdentifyData->AtaData.command_set_feature_enb_86));\r
+  //\r
+  // Check if IDD is incomplete\r
+  //\r
+  if ((IdentifyData->AtaData.config & BIT2) != 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Enable/disable/skip PUIS of the disk according to policy.\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  PortMultiplier      The multiplier of port.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciPuisEnable (\r
+  IN EFI_PCI_IO_PROTOCOL           *PciIo,\r
+  IN EFI_AHCI_REGISTERS            *AhciRegisters,\r
+  IN UINT8                         Port,\r
+  IN UINT8                         PortMultiplier\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+  if (mAtaAtapiPolicy->PuisEnable == 0) {\r
+    Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_DISABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);\r
+  } else if (mAtaAtapiPolicy->PuisEnable == 1) {\r
+    Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_ENABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);\r
+  }\r
+  DEBUG ((DEBUG_INFO, "%a PUIS feature at port [%d] PortMultiplier [%d] - %r!\n",\r
+    (mAtaAtapiPolicy->PuisEnable == 0) ? "Disable" : (\r
+    (mAtaAtapiPolicy->PuisEnable == 1) ? "Enable" : "Skip"\r
+      ), Port, PortMultiplier, Status));\r
+  return Status;\r
+}\r
+\r
 /**\r
   Initialize ATA host controller at AHCI mode.\r
 \r
@@ -2239,6 +2587,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
@@ -2257,14 +2606,34 @@ AhciModeInitialization (
   // Collect AHCI controller information\r
   //\r
   Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
-  \r
+\r
   //\r
-  // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
+  // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
   //\r
-  if ((Capability & EFI_AHCI_CAP_SAM) == 0) {\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
+  //\r
+  // Enable 64-bit DMA support in the PCI layer if this controller\r
+  // supports it.\r
+  //\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
   //\r
@@ -2284,7 +2653,7 @@ AhciModeInitialization (
   }\r
 \r
   for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port ++) {\r
-    if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
+    if ((PortImplementBitMap & (((UINT32)BIT0) << Port)) != 0) {\r
       //\r
       // According to AHCI spec, MaxPortNumber should be equal or greater than the number of implemented ports.\r
       //\r
@@ -2344,20 +2713,9 @@ 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
-      // It's the requirment from SATA1.0a spec section 5.2.\r
+      // Wait for the Phy to detect the presence of a device.\r
       //\r
       PhyDetectDelay = EFI_AHCI_BUS_PHY_DETECT_TIMEOUT;\r
       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;\r
@@ -2439,6 +2797,28 @@ AhciModeInitialization (
           continue;\r
         }\r
 \r
+        DEBUG ((\r
+          DEBUG_INFO, "IDENTIFY DEVICE: [0] = %016x, [2] = %016x, [83] = %016x, [86] = %016x\n",\r
+          Buffer.AtaData.config, Buffer.AtaData.specific_config,\r
+          Buffer.AtaData.command_set_supported_83, Buffer.AtaData.command_set_feature_enb_86\r
+          ));\r
+        if ((Buffer.AtaData.config & BIT2) != 0) {\r
+          //\r
+          // SpinUp disk if device reported incomplete IDENTIFY DEVICE.\r
+          //\r
+          Status = AhciSpinUpDisk (\r
+                     PciIo,\r
+                     AhciRegisters,\r
+                     Port,\r
+                     0,\r
+                     &Buffer\r
+                     );\r
+          if (EFI_ERROR (Status)) {\r
+            DEBUG ((DEBUG_ERROR, "Spin up standby device failed - %r\n", Status));\r
+            continue;\r
+          }\r
+        }\r
+\r
         DeviceType = EfiIdeHarddisk;\r
       } else {\r
         continue;\r
@@ -2504,7 +2884,7 @@ AhciModeInitialization (
         TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode;\r
       }\r
 \r
-      Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode));\r
+      Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode), ATA_ATAPI_TIMEOUT);\r
       if (EFI_ERROR (Status)) {\r
         DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status));\r
         continue;\r
@@ -2516,6 +2896,29 @@ AhciModeInitialization (
       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
+        AhciEnableDevSlp (\r
+          PciIo,\r
+          AhciRegisters,\r
+          Port,\r
+          0,\r
+          &Buffer\r
+          );\r
+      }\r
+\r
+      //\r
+      // Enable/disable PUIS according to policy setting if PUIS is capable (Word[83].BIT5 is set).\r
+      //\r
+      if ((Buffer.AtaData.command_set_supported_83 & BIT5) != 0) {\r
+        Status = AhciPuisEnable (\r
+                   PciIo,\r
+                   AhciRegisters,\r
+                   Port,\r
+                   0\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          DEBUG ((DEBUG_ERROR, "PUIS enable/disable failed, Status = %r\n", Status));\r
+          continue;\r
+        }\r
       }\r
     }\r
   }\r