X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FBus%2FAta%2FAtaAtapiPassThru%2FAhciMode.c;h=7e2fade400e30c31b970113c30265f17bc1ced30;hp=14578c0f948a6a0da44ce5c6a159fdd3b8012411;hb=8c39253dff7b0f5722f44cf42bf1e440a38f95bb;hpb=8d3c4b552f81ff7aafbe50e555032a83aef1b0d8
diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
index 14578c0f94..7e2fade400 100644
--- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
+++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
@@ -3,13 +3,7 @@
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
- This program and the accompanying materials
- are licensed and made available under the terms and conditions of the BSD License
- which accompanies this distribution. The full text of the license may be found at
- http://opensource.org/licenses/bsd-license.php
-
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@@ -225,7 +219,7 @@ AhciWaitMemSet (
do {
//
- // Access sytem memory to see if the value is the tested one.
+ // Access system memory to see if the value is the tested one.
//
// The system memory pointed by Address will be updated by the
// SATA Host Controller, "volatile" is introduced to prevent
@@ -294,41 +288,6 @@ AhciCheckMemSet (
}
}
-/**
- Check if the device is still on port. It also checks if the AHCI controller
- supports the address and data count will be transferred.
-
- @param PciIo The PCI IO protocol instance.
- @param Port The number of port.
-
- @retval EFI_SUCCESS The device is attached to port and the transfer data is
- supported by AHCI controller.
- @retval EFI_UNSUPPORTED The transfer address and count is not supported by AHCI
- controller.
- @retval EFI_NOT_READY The physical communication between AHCI controller and device
- is not ready.
-
-**/
-EFI_STATUS
-EFIAPI
-AhciCheckDeviceStatus (
- IN EFI_PCI_IO_PROTOCOL *PciIo,
- IN UINT8 Port
- )
-{
- UINT32 Data;
- UINT32 Offset;
-
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;
-
- Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK;
-
- if (Data == EFI_AHCI_PORT_SSTS_DET_PCE) {
- return EFI_SUCCESS;
- }
-
- return EFI_NOT_READY;
-}
/**
@@ -622,7 +581,7 @@ AhciBuildCommand (
}
/**
- Buid a command FIS.
+ Build a command FIS.
@param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data structure.
@param AtaCommandBlock A pointer to the AhciBuildCommandFis data structure.
@@ -820,7 +779,7 @@ AhciPioTransfer (
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
PortTfd = AhciReadReg (PciIo, (UINT32) Offset);
//
- // PxTFD will be updated if there is a D2H or SetupFIS received.
+ // PxTFD will be updated if there is a D2H or SetupFIS received.
//
if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
Status = EFI_DEVICE_ERROR;
@@ -1038,7 +997,7 @@ AhciDmaTransfer (
}
//
- // Wait for command compelte
+ // Wait for command complete
//
FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
@@ -1361,75 +1320,6 @@ AhciStartCommand (
return EFI_SUCCESS;
}
-/**
- Do AHCI port reset.
-
- @param PciIo The PCI IO protocol instance.
- @param Port The number of port.
- @param Timeout The timeout value of reset, uses 100ns as a unit.
-
- @retval EFI_DEVICE_ERROR The port reset unsuccessfully
- @retval EFI_TIMEOUT The reset operation is time out.
- @retval EFI_SUCCESS The port reset successfully.
-
-**/
-EFI_STATUS
-EFIAPI
-AhciPortReset (
- IN EFI_PCI_IO_PROTOCOL *PciIo,
- IN UINT8 Port,
- IN UINT64 Timeout
- )
-{
- EFI_STATUS Status;
- UINT32 Offset;
-
- AhciClearPortStatus (PciIo, Port);
-
- AhciStopCommand (PciIo, Port, Timeout);
-
- AhciDisableFisReceive (PciIo, Port, Timeout);
-
- AhciEnableFisReceive (PciIo, Port, Timeout);
-
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
-
- AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_DET_INIT);
-
- //
- // wait 5 millisecond before de-assert DET
- //
- MicroSecondDelay (5000);
-
- AhciAndReg (PciIo, Offset, (UINT32)EFI_AHCI_PORT_SCTL_MASK);
-
- //
- // wait 5 millisecond before de-assert DET
- //
- MicroSecondDelay (5000);
-
- //
- // Wait for communication to be re-established
- //
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;
- Status = AhciWaitMmioSet (
- PciIo,
- Offset,
- EFI_AHCI_PORT_SSTS_DET_MASK,
- EFI_AHCI_PORT_SSTS_DET_PCE,
- Timeout
- );
-
- if (EFI_ERROR (Status)) {
- DEBUG ((EFI_D_ERROR, "Port %d COMRESET failed: %r\n", Port, Status));
- return Status;
- }
-
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
- AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_ERR_CLEAR);
-
- return EFI_SUCCESS;
-}
/**
Do AHCI HBA reset.
@@ -2010,10 +1900,10 @@ AhciCreateTransferDescriptor (
//
MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);
Support64Bit = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE);
-
+
PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);
//
- // Get the highest bit of implemented ports which decides how many bytes are allocated for recived FIS.
+ // Get the highest bit of implemented ports which decides how many bytes are allocated for received FIS.
//
MaxPortNumber = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);
if (MaxPortNumber == 0) {
@@ -2068,7 +1958,7 @@ AhciCreateTransferDescriptor (
//
// Allocate memory for command list
- // Note that the implemenation is a single task model which only use a command list for all ports.
+ // Note that the implementation is a single task model which only use a command list for all ports.
//
Buffer = NULL;
MaxCommandListSize = MaxCommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST);
@@ -2218,6 +2108,213 @@ Error6:
return Status;
}
+/**
+ Read logs from SATA device.
+
+ @param PciIo The PCI IO protocol instance.
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The multiplier of port.
+ @param Buffer The data buffer to store SATA logs.
+ @param LogNumber The address of the log.
+ @param PageNumber The page number of the log.
+
+ @retval EFI_INVALID_PARAMETER PciIo, AhciRegisters or Buffer is NULL.
+ @retval others Return status of AhciPioTransfer().
+**/
+EFI_STATUS
+AhciReadLogExt (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN OUT UINT8 *Buffer,
+ IN UINT8 LogNumber,
+ IN UINT8 PageNumber
+ )
+{
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
+ EFI_ATA_STATUS_BLOCK AtaStatusBlock;
+
+ if (PciIo == NULL || AhciRegisters == NULL || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ///
+ /// Read log from device
+ ///
+ ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
+ ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
+ ZeroMem (Buffer, 512);
+
+ AtaCommandBlock.AtaCommand = ATA_CMD_READ_LOG_EXT;
+ AtaCommandBlock.AtaSectorCount = 1;
+ AtaCommandBlock.AtaSectorNumber = LogNumber;
+ AtaCommandBlock.AtaCylinderLow = PageNumber;
+
+ return AhciPioTransfer (
+ PciIo,
+ AhciRegisters,
+ Port,
+ PortMultiplier,
+ NULL,
+ 0,
+ TRUE,
+ &AtaCommandBlock,
+ &AtaStatusBlock,
+ Buffer,
+ 512,
+ ATA_ATAPI_TIMEOUT,
+ NULL
+ );
+}
+
+/**
+ Enable DEVSLP of the disk if supported.
+
+ @param PciIo The PCI IO protocol instance.
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The multiplier of port.
+ @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data.
+
+ @retval EFI_SUCCESS The DEVSLP is enabled per policy successfully.
+ @retval EFI_UNSUPPORTED The DEVSLP isn't supported by the controller/device and policy requires to enable it.
+**/
+EFI_STATUS
+AhciEnableDevSlp (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN EFI_IDENTIFY_DATA *IdentifyData
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Offset;
+ UINT32 Capability2;
+ UINT8 LogData[512];
+ DEVSLP_TIMING_VARIABLES DevSlpTiming;
+ UINT32 PortCmd;
+ UINT32 PortDevSlp;
+
+ if (mAtaAtapiPolicy->DeviceSleepEnable != 1) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Do not enable DevSlp if DevSlp is not supported.
+ //
+ Capability2 = AhciReadReg (PciIo, AHCI_CAPABILITY2_OFFSET);
+ DEBUG ((DEBUG_INFO, "AHCI CAPABILITY2 = %08x\n", Capability2));
+ if ((Capability2 & AHCI_CAP2_SDS) == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Do not enable DevSlp if DevSlp is not present
+ // Do not enable DevSlp if Hot Plug or Mechanical Presence Switch is supported
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH;
+ PortCmd = AhciReadReg (PciIo, Offset + EFI_AHCI_PORT_CMD);
+ PortDevSlp = AhciReadReg (PciIo, Offset + AHCI_PORT_DEVSLP);
+ DEBUG ((DEBUG_INFO, "Port CMD/DEVSLP = %08x / %08x\n", PortCmd, PortDevSlp));
+ if (((PortDevSlp & AHCI_PORT_DEVSLP_DSP) == 0) ||
+ ((PortCmd & (EFI_AHCI_PORT_CMD_HPCP | EFI_AHCI_PORT_CMD_MPSP)) != 0)
+ ) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Do not enable DevSlp if the device doesn't support DevSlp
+ //
+ DEBUG ((DEBUG_INFO, "IDENTIFY DEVICE: [77] = %04x, [78] = %04x, [79] = %04x\n",
+ IdentifyData->AtaData.reserved_77,
+ IdentifyData->AtaData.serial_ata_features_supported, IdentifyData->AtaData.serial_ata_features_enabled));
+ if ((IdentifyData->AtaData.serial_ata_features_supported & BIT8) == 0) {
+ DEBUG ((DEBUG_INFO, "DevSlp feature is not supported for device at port [%d] PortMultiplier [%d]!\n",
+ Port, PortMultiplier));
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Enable DevSlp when it is not enabled.
+ //
+ if ((IdentifyData->AtaData.serial_ata_features_enabled & BIT8) != 0) {
+ Status = AhciDeviceSetFeature (
+ PciIo, AhciRegisters, Port, 0, ATA_SUB_CMD_ENABLE_SATA_FEATURE, 0x09, ATA_ATAPI_TIMEOUT
+ );
+ DEBUG ((DEBUG_INFO, "DevSlp set feature for device at port [%d] PortMultiplier [%d] - %r\n",
+ Port, PortMultiplier, Status));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ Status = AhciReadLogExt(PciIo, AhciRegisters, Port, PortMultiplier, LogData, 0x30, 0x08);
+
+ //
+ // Clear PxCMD.ST and PxDEVSLP.ADSE before updating PxDEVSLP.DITO and PxDEVSLP.MDAT.
+ //
+ AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd & ~EFI_AHCI_PORT_CMD_ST);
+ PortDevSlp &= ~AHCI_PORT_DEVSLP_ADSE;
+ AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
+
+ //
+ // Set PxDEVSLP.DETO and PxDEVSLP.MDAT to 0.
+ //
+ PortDevSlp &= ~AHCI_PORT_DEVSLP_DETO_MASK;
+ PortDevSlp &= ~AHCI_PORT_DEVSLP_MDAT_MASK;
+ AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
+ DEBUG ((DEBUG_INFO, "Read Log Ext at port [%d] PortMultiplier [%d] - %r\n", Port, PortMultiplier, Status));
+ if (EFI_ERROR (Status)) {
+ //
+ // Assume DEVSLP TIMING VARIABLES is not supported if the Identify Device Data log (30h, 8) fails
+ //
+ ZeroMem (&DevSlpTiming, sizeof (DevSlpTiming));
+ } else {
+ CopyMem (&DevSlpTiming, &LogData[48], sizeof (DevSlpTiming));
+ DEBUG ((DEBUG_INFO, "DevSlpTiming: Supported(%d), Deto(%d), Madt(%d)\n",
+ DevSlpTiming.Supported, DevSlpTiming.Deto, DevSlpTiming.Madt));
+ }
+
+ //
+ // Use 20ms as default DETO when DEVSLP TIMING VARIABLES is not supported or the DETO is 0.
+ //
+ if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Deto == 0)) {
+ DevSlpTiming.Deto = 20;
+ }
+
+ //
+ // Use 10ms as default MADT when DEVSLP TIMING VARIABLES is not supported or the MADT is 0.
+ //
+ if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Madt == 0)) {
+ DevSlpTiming.Madt = 10;
+ }
+
+ PortDevSlp |= DevSlpTiming.Deto << 2;
+ PortDevSlp |= DevSlpTiming.Madt << 10;
+ AhciOrReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
+
+ if (mAtaAtapiPolicy->AggressiveDeviceSleepEnable == 1) {
+ if ((Capability2 & AHCI_CAP2_SADM) != 0) {
+ PortDevSlp &= ~AHCI_PORT_DEVSLP_DITO_MASK;
+ PortDevSlp |= (625 << 15);
+ AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
+
+ PortDevSlp |= AHCI_PORT_DEVSLP_ADSE;
+ AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
+ }
+ }
+
+
+ AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd);
+
+ DEBUG ((DEBUG_INFO, "Enabled DevSlp feature at port [%d] PortMultiplier [%d], Port CMD/DEVSLP = %08x / %08x\n",
+ Port, PortMultiplier, PortCmd, PortDevSlp));
+
+ return EFI_SUCCESS;
+}
/**
Spin-up disk if IDD was incomplete or PUIS feature is enabled
@@ -2316,6 +2413,38 @@ AhciSpinUpDisk (
return EFI_SUCCESS;
}
+/**
+ Enable/disable/skip PUIS of the disk according to policy.
+
+ @param PciIo The PCI IO protocol instance.
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The multiplier of port.
+
+**/
+EFI_STATUS
+AhciPuisEnable (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ if (mAtaAtapiPolicy->PuisEnable == 0) {
+ Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_DISABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);
+ } else if (mAtaAtapiPolicy->PuisEnable == 1) {
+ Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_ENABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);
+ }
+ DEBUG ((DEBUG_INFO, "%a PUIS feature at port [%d] PortMultiplier [%d] - %r!\n",
+ (mAtaAtapiPolicy->PuisEnable == 0) ? "Disable" : (
+ (mAtaAtapiPolicy->PuisEnable == 1) ? "Enable" : "Skip"
+ ), Port, PortMultiplier, Status));
+ return Status;
+}
+
/**
Initialize ATA host controller at AHCI mode.
@@ -2584,7 +2713,7 @@ AhciModeInitialization (
} else {
continue;
}
- DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n",
+ DEBUG ((DEBUG_INFO, "port [%d] port multitplier [%d] has a [%a]\n",
Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"));
//
@@ -2632,7 +2761,7 @@ AhciModeInitialization (
TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode);
//
- // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't
+ // Set supported DMA mode on this IDE device. Note that UDMA & MDMA can't
// be set together. Only one DMA mode can be set to a device. If setting
// DMA mode operation fails, we can continue moving on because we only use
// PIO mode at boot time. DMA modes are used by certain kind of OS booting
@@ -2657,6 +2786,29 @@ AhciModeInitialization (
CreateNewDeviceInfo (Instance, Port, 0xFFFF, DeviceType, &Buffer);
if (DeviceType == EfiIdeHarddisk) {
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));
+ AhciEnableDevSlp (
+ PciIo,
+ AhciRegisters,
+ Port,
+ 0,
+ &Buffer
+ );
+ }
+
+ //
+ // Enable/disable PUIS according to policy setting if PUIS is capable (Word[83].BIT5 is set).
+ //
+ if ((Buffer.AtaData.command_set_supported_83 & BIT5) != 0) {
+ Status = AhciPuisEnable (
+ PciIo,
+ AhciRegisters,
+ Port,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "PUIS enable/disable failed, Status = %r\n", Status));
+ continue;
+ }
}
}
}