]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
MdeModulePkg/Bus/Ata: Fix various typos
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AtaAtapiPassThru / AhciMode.c
index 14578c0f948a6a0da44ce5c6a159fdd3b8012411..7e2fade400e30c31b970113c30265f17bc1ced30 100644 (file)
@@ -3,13 +3,7 @@
 \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
-  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
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -225,7 +219,7 @@ AhciWaitMemSet (
 \r
   do {\r
     //\r
-    // Access sytem memory to see if the value is the tested one.\r
+    // Access system memory to see if the value is the tested one.\r
     //\r
     // The system memory pointed by Address will be updated by the\r
     // SATA Host Controller, "volatile" is introduced to prevent\r
@@ -294,41 +288,6 @@ AhciCheckMemSet (
   }\r
 }\r
 \r
-/**\r
-  Check if the device is still on port. It also checks if the AHCI controller\r
-  supports the address and data count will be transferred.\r
-\r
-  @param  PciIo            The PCI IO protocol instance.\r
-  @param  Port             The number of port.\r
-\r
-  @retval EFI_SUCCESS      The device is attached to port and the transfer data is\r
-                           supported by AHCI controller.\r
-  @retval EFI_UNSUPPORTED  The transfer address and count is not supported by AHCI\r
-                           controller.\r
-  @retval EFI_NOT_READY    The physical communication between AHCI controller and device\r
-                           is not ready.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-AhciCheckDeviceStatus (\r
-  IN  EFI_PCI_IO_PROTOCOL    *PciIo,\r
-  IN  UINT8                  Port\r
-  )\r
-{\r
-  UINT32      Data;\r
-  UINT32      Offset;\r
-\r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;\r
-\r
-  Data   = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK;\r
-\r
-  if (Data == EFI_AHCI_PORT_SSTS_DET_PCE) {\r
-    return EFI_SUCCESS;\r
-  }\r
-\r
-  return EFI_NOT_READY;\r
-}\r
 \r
 /**\r
 \r
@@ -622,7 +581,7 @@ AhciBuildCommand (
 }\r
 \r
 /**\r
-  Buid a command FIS.\r
+  Build a command FIS.\r
 \r
   @param  CmdFis            A pointer to the EFI_AHCI_COMMAND_FIS data structure.\r
   @param  AtaCommandBlock   A pointer to the AhciBuildCommandFis data structure.\r
@@ -820,7 +779,7 @@ AhciPioTransfer (
         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
+        // 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
@@ -1038,7 +997,7 @@ AhciDmaTransfer (
   }\r
 \r
   //\r
-  // Wait for command compelte\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
@@ -1361,75 +1320,6 @@ AhciStartCommand (
   return EFI_SUCCESS;\r
 }\r
 \r
-/**\r
-  Do AHCI port reset.\r
-\r
-  @param  PciIo              The PCI IO protocol instance.\r
-  @param  Port               The number of port.\r
-  @param  Timeout            The timeout value of reset, uses 100ns as a unit.\r
-\r
-  @retval EFI_DEVICE_ERROR   The port reset unsuccessfully\r
-  @retval EFI_TIMEOUT        The reset operation is time out.\r
-  @retval EFI_SUCCESS        The port reset successfully.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-AhciPortReset (\r
-  IN  EFI_PCI_IO_PROTOCOL       *PciIo,\r
-  IN  UINT8                     Port,\r
-  IN  UINT64                    Timeout\r
-  )\r
-{\r
-  EFI_STATUS      Status;\r
-  UINT32          Offset;\r
-\r
-  AhciClearPortStatus (PciIo, Port);\r
-\r
-  AhciStopCommand (PciIo, Port, Timeout);\r
-\r
-  AhciDisableFisReceive (PciIo, Port, Timeout);\r
-\r
-  AhciEnableFisReceive (PciIo, Port, Timeout);\r
-\r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;\r
-\r
-  AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_DET_INIT);\r
-\r
-  //\r
-  // wait 5 millisecond before de-assert DET\r
-  //\r
-  MicroSecondDelay (5000);\r
-\r
-  AhciAndReg (PciIo, Offset, (UINT32)EFI_AHCI_PORT_SCTL_MASK);\r
-\r
-  //\r
-  // wait 5 millisecond before de-assert DET\r
-  //\r
-  MicroSecondDelay (5000);\r
-\r
-  //\r
-  // Wait for communication to be re-established\r
-  //\r
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;\r
-  Status = AhciWaitMmioSet (\r
-             PciIo,\r
-             Offset,\r
-             EFI_AHCI_PORT_SSTS_DET_MASK,\r
-             EFI_AHCI_PORT_SSTS_DET_PCE,\r
-             Timeout\r
-             );\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
-  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
-  AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_ERR_CLEAR);\r
-\r
-  return EFI_SUCCESS;\r
-}\r
 \r
 /**\r
   Do AHCI HBA reset.\r
@@ -2010,10 +1900,10 @@ AhciCreateTransferDescriptor (
   //\r
   MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
   Support64Bit         = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE);\r
-  \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
+  // Get the highest bit of implemented ports which decides how many bytes are allocated for received FIS.\r
   //\r
   MaxPortNumber        = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
   if (MaxPortNumber == 0) {\r
@@ -2068,7 +1958,7 @@ AhciCreateTransferDescriptor (
 \r
   //\r
   // Allocate memory for command list\r
-  // Note that the implemenation is a single task model which only use a command list for all ports.\r
+  // Note that the implementation is a single task model which only use a command list for all ports.\r
   //\r
   Buffer = NULL;\r
   MaxCommandListSize = MaxCommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST);\r
@@ -2218,6 +2108,213 @@ 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
+    ZeroMem (&DevSlpTiming, sizeof (DevSlpTiming));\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
@@ -2316,6 +2413,38 @@ AhciSpinUpDisk (
   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
@@ -2584,7 +2713,7 @@ AhciModeInitialization (
       } else {\r
         continue;\r
       }\r
-      DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n",\r
+      DEBUG ((DEBUG_INFO, "port [%d] port multitplier [%d] has a [%a]\n",\r
               Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"));\r
 \r
       //\r
@@ -2632,7 +2761,7 @@ AhciModeInitialization (
       TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode);\r
 \r
       //\r
-      // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't\r
+      // Set supported DMA mode on this IDE device. Note that UDMA & MDMA can't\r
       // be set together. Only one DMA mode can be set to a device. If setting\r
       // DMA mode operation fails, we can continue moving on because we only use\r
       // PIO mode at boot time. DMA modes are used by certain kind of OS booting\r
@@ -2657,6 +2786,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