+/**\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
+\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