alder_lake_bios/Intel/AlderLake/AlderLakeChipsetPkg/SataDevSleepDxe/SataDevSleep.c

458 lines
16 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2018 - 2020, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include <SataDevSleep.h>
#include <Library/PchInfoLib.h>
#include <SetupVariable.h>
#include <Library/SataSocLib.h>
EFI_STATUS
EFIAPI
SataDevSleepEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_EVENT Event;
VOID *Registration;
Status = EFI_SUCCESS;
DEBUG((DEBUG_INFO | DEBUG_ERROR, "SATADevSleepEntryPoint Entry\n"));
// if (IsPchLp()) {
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SATADevSleepCallback,
NULL,
&Event
);
if (!EFI_ERROR (Status)) {
Status = gBS->RegisterProtocolNotify (
&gEfiAtaPassThruProtocolGuid,
Event,
&Registration
);
}
// } else {
// DEBUG ((DEBUG_ERROR, "Platform does not support DevSleep \n"));
// }
DEBUG((DEBUG_INFO | DEBUG_ERROR, "SATADevSleepEntryPoint End\n"));
return EFI_SUCCESS;
}
/**
Enable DEVSLP command of the disk if supported.
The following steps are according to PCH spec <DevSleep Enabling>
**/
VOID
EFIAPI
SATADevSleepCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN HandleCount;
UINT32 HandleIndex;
EFI_HANDLE *HandleBuffer;
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThruPtr;
EFI_DEVICE_PATH_PROTOCOL *AtaPassThruDevPath;
EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
BOOLEAN IsMatched;
UINT32 AhciBar;
UINT32 PciD31F2RegBase;
UINT32 Data32Or;
UINT16 PortIndex;
DEV_SATA_DVSLEEP_DATA PortData[PCH_MAX_SATA_PORTS] = {0};
PCH_SETUP *PchSetup;
UINTN PchSetupSize;
BOOLEAN UpdatePchSetup;
UINT8 MaxSataPorts;
BOOLEAN SetupPortDevSleepEnable;
UINT32 Capability2;
UINT32 Data32;
DEBUG((DEBUG_INFO | DEBUG_ERROR, "SATADevSleepCallback Start\n"));
Status = EFI_SUCCESS;
HandleCount = 0;
HandleBuffer = NULL;
AtaPassThruDevPath = NULL;
IsMatched = FALSE;
PortIndex = 0;
PchSetup = NULL;
UpdatePchSetup = FALSE;
MaxSataPorts = MaxSataPortNum (SATA_1_CONTROLLER_INDEX);
SetupPortDevSleepEnable = FALSE;
PciD31F2RegBase = (UINT32) MmPciAddress (0, 0, PCI_DEVICE_NUMBER_PCH_SATA, PCI_FUNCTION_NUMBER_PCH_SATA, 0);
AhciBar = Mmio32 (PciD31F2RegBase , R_SATA_CFG_AHCI_BAR);
Capability2 = Mmio32 (AhciBar, R_SATA_MEM_AHCI_CAP2);
PchSetupSize = sizeof (PCH_SETUP);
PchSetup = AllocateZeroPool (PchSetupSize);
if (PchSetup == NULL) {
return;
}
Status = gRT->GetVariable (
PCH_SETUP_VARIABLE_NAME,
&gPchSetupVariableGuid,
NULL,
&PchSetupSize,
PchSetup
);
if (EFI_ERROR (Status)) {
goto Error2;
}
//
// Check there has any port enable DevSlp function on platform or not.
//
for (PortIndex = 0; PortIndex < MaxSataPorts; PortIndex++) {
if (PchSetup->PxDevSlp[PortIndex] == TRUE) {
PortData[PortIndex].State.SetupDevsleepEnable = PchSetup->PxDevSlp[PortIndex];
SetupPortDevSleepEnable = TRUE;
}
}
if (!SetupPortDevSleepEnable) {
goto Error2;
}
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiAtaPassThruProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status) || (HandleCount == 0)) {
DEBUG((DEBUG_ERROR, "LocateHandleBuffer Error!, Status = %r\n", Status));
goto Error1;
}
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
Status = gBS->OpenProtocol (
HandleBuffer[HandleIndex],
&gEfiDevicePathProtocolGuid,
(VOID **) &AtaPassThruDevPath,
NULL,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
continue;
}
TmpDevicePath = AtaPassThruDevPath;
while (!IsDevicePathEnd (TmpDevicePath)) {
if((TmpDevicePath->Type == HARDWARE_DEVICE_PATH) && (TmpDevicePath->SubType == HW_PCI_DP)) {
if((((PCI_DEVICE_PATH*)TmpDevicePath)->Device == PCI_DEVICE_NUMBER_PCH_SATA) &&
(((PCI_DEVICE_PATH*)TmpDevicePath)->Function) == PCI_FUNCTION_NUMBER_PCH_SATA) {
//
// Controller matched
//
IsMatched = TRUE;
break;
}
}
TmpDevicePath = NextDevicePathNode (TmpDevicePath);
}
if(IsMatched) {
break;
}
}
Status = gBS->HandleProtocol (
HandleBuffer[HandleIndex],
&gEfiAtaPassThruProtocolGuid,
(VOID**)&AtaPassThruPtr
);
if (EFI_ERROR (Status)) {
DEBUG((DEBUG_ERROR, " Handle Protocolr for gEfiAtaPassThruProtocolGuid Error!, Status = %r\n", Status));
goto Error1;
}
//
// Do not enable DevSlp if DevSlp and Aggressive DevSlp Management is not supported
//
if ((Capability2 & (B_SATA_MEM_AHCI_CAP2_SDS | B_SATA_MEM_AHCI_CAP2_SADM)) == 0) {
DEBUG ((DEBUG_INFO, "ERROR: DevSlp and Aggressive DevSlp Management is not supported.\n"));
goto Error1;
}
//
// For any port, Check activitys before opening the feature.
//
for (PortIndex = 0; PortIndex < MaxSataPorts; PortIndex++) {
if (!PortData[PortIndex].State.SetupDevsleepEnable) {
continue;
}
//
// Do not enable DevSlp if DevSlp is not present
// Do not enable DevSlp if Hot Plug or Mechanical Presence Switch is supported
//
if ((((Mmio32(AhciBar, R_SATA_MEM_AHCI_P0DEVSLP + (0x80 * PortIndex))) & B_SATA_MEM_AHCI_PxDEVSLP_DSP) == 0) ||
((Mmio32(AhciBar, R_SATA_MEM_AHCI_P0CMD + (0x80 * PortIndex)) &
(B_SATA_MEM_AHCI_PxCMD_HPCP | B_SATA_MEM_AHCI_PxCMD_MPSP)) != 0)) {
DEBUG ((DEBUG_INFO, "ERROR: DevSlp is not present, Hot Plug or Mechanical Presence Switch is supported\n"));
continue;
}
//
// Detect whether the device supports DevSlp
//
GetSATAIdentifyDeviceData (AtaPassThruPtr, &PortIndex, &PortData[PortIndex]);
if (!PortData[PortIndex].State.DevsleepCap) {
//
// DevSlp is not supported by the device
//
DEBUG ((DEBUG_INFO, "DevSlp feature is not supported for device at port [%d]!\n", PortIndex));
continue;
}
if (!PortData[PortIndex].State.DevsleepPwr) {
//
// DevSlp to Reduced PwrState is not supported
//
DEBUG ((DEBUG_INFO, "DevSlp to Reduced PwrState is not supported for device at port [%d]\n", PortIndex));
continue;
}
//
// DevSlp feature wasn't enabled for device
//
if (!PortData[PortIndex].State.DevsleepEn) {
//
// Enable Device DevSlp feature via SET FEATURES command.
//
Status = EnableSATADevsleep (AtaPassThruPtr, &PortIndex);
if (EFI_ERROR (Status)) {
DEBUG((DEBUG_ERROR, "EnableSATADevsleep Error!, Status = %r\n", Status));
continue;
}
}
Status = GetSATADevsleepTimes (AtaPassThruPtr, &PortIndex, &PortData[PortIndex]);
if (EFI_ERROR (Status)) {
DEBUG((DEBUG_ERROR, "GetSATADevsleepTimes Error!, Status = %r\n", Status));
}
//
// Makesure PxCMD.ST and PxDEVSLP.ADSE are cleared to '0' before updating PxDEVSLP.DITO and PxDEVSLP.MDAT value.
//
Data32 = Mmio32 (AhciBar, R_SATA_MEM_AHCI_P0CMD + (0x80 * PortIndex));
Mmio32And (AhciBar, R_SATA_MEM_AHCI_P0CMD + (0x80 * PortIndex), (UINT32)~(B_SATA_MEM_AHCI_PxCMD_ST));
Mmio32And (AhciBar, R_SATA_MEM_AHCI_P0DEVSLP + (0x80 * PortIndex), (UINT32)~(B_SATA_MEM_AHCI_PxDEVSLP_ADSE));
Mmio32And (AhciBar, R_SATA_MEM_AHCI_P0DEVSLP + (0x80 * PortIndex), (UINT32)~((0x1f) << 10));
Mmio32And (AhciBar, R_SATA_MEM_AHCI_P0DEVSLP + (0x80 * PortIndex), (UINT32)~((0xff) << 2));
//
// Update SATA PxDEVSLP.DETO and PxDEVSLP.MDAT
//
Data32Or = (((PortData[PortIndex].DETO & 0x1FF) << 2) | ((PortData[PortIndex].MDAT & 0x1F) << 10));
Mmio32AndThenOr (AhciBar, R_SATA_MEM_AHCI_P0DEVSLP + (0x80 * PortIndex), 0xFFFF8003, Data32Or);
//
// Restore SATA PxCMD.ST and Update PxDEVSLP.ADSE
//
Mmio32Or (AhciBar, R_SATA_MEM_AHCI_P0CMD + (0x80 * PortIndex), (Data32 & B_SATA_MEM_AHCI_PxCMD_ST));
Mmio32Or (AhciBar, R_SATA_MEM_AHCI_P0DEVSLP + (0x80 * PortIndex), B_SATA_MEM_AHCI_PxDEVSLP_ADSE);
if (PortData[PortIndex].State.Valid) {
PchSetup->SataType[PortIndex] = PortData[PortIndex].State.SolidStateDrive;
UpdatePchSetup = TRUE;
}
}
DEBUG((DEBUG_INFO | DEBUG_ERROR, "SATADevSleepCallback End\n"));
if (UpdatePchSetup == TRUE) {
Status = gRT->SetVariable (
PCH_SETUP_VARIABLE_NAME,
&gPchSetupVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
PchSetupSize,
PchSetup
);
}
Error1:
FreePool (HandleBuffer);
Error2:
FreePool (PchSetup);
}
EFI_STATUS
EnableSATADevsleep (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThruPtr,
IN UINT16 *PortNumber
)
{
EFI_STATUS Status;
EFI_ATA_STATUS_BLOCK Asb;
EFI_ATA_COMMAND_BLOCK Acb;
EFI_ATA_PASS_THRU_COMMAND_PACKET AtaPassThruCmdPacket;
Status = EFI_SUCCESS;
ZeroMem (&AtaPassThruCmdPacket, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));
ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));
ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
AtaPassThruCmdPacket.Asb = &Asb;
AtaPassThruCmdPacket.Acb = &Acb;
AtaPassThruCmdPacket.Acb->AtaCommand = ATA_COMMAND_SET_FEATURES;
AtaPassThruCmdPacket.Acb->AtaFeatures = ATA_COMMAND_SET_FEATURES_ENABLE;
AtaPassThruCmdPacket.Acb->AtaSectorCount = ATA_COMMAND_FEATURES_DEVSLEEP;
AtaPassThruCmdPacket.Timeout = 100000000;
AtaPassThruCmdPacket.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;
AtaPassThruCmdPacket.Length = EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER;
Status = AtaPassThruPtr->PassThru (
AtaPassThruPtr,
*PortNumber,
0,
&AtaPassThruCmdPacket,
NULL
);
return Status;
}
EFI_STATUS
GetSATADevsleepTimes (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThruPtr,
IN UINT16 *PortNumber,
OUT DEV_SATA_DVSLEEP_DATA *PortData
)
{
EFI_STATUS Status;
EFI_ATA_STATUS_BLOCK Asb;
EFI_ATA_COMMAND_BLOCK Acb;
EFI_ATA_PASS_THRU_COMMAND_PACKET AtaPassThruCmdPacket;
EFI_READ_LOG_EXT_DATA *SataLog;
UINT32 DevSlpData;
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (EFI_READ_LOG_EXT_DATA),
(VOID **)&SataLog
);
ZeroMem (SataLog, sizeof (EFI_READ_LOG_EXT_DATA));
ZeroMem (&AtaPassThruCmdPacket, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));
ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));
ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
AtaPassThruCmdPacket.Asb = &Asb;
AtaPassThruCmdPacket.Acb = &Acb;
AtaPassThruCmdPacket.Acb->AtaCommand = ATA_COMMAND_READ_LOG_EXT;
AtaPassThruCmdPacket.Acb->AtaSectorNumber = 0x30;
AtaPassThruCmdPacket.Acb->AtaCylinderLow = 0x8;
AtaPassThruCmdPacket.Acb->AtaSectorCount = (UINT8) 0x1;
AtaPassThruCmdPacket.InTransferLength = sizeof (EFI_READ_LOG_EXT_DATA);
AtaPassThruCmdPacket.InDataBuffer = SataLog;
AtaPassThruCmdPacket.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN;
AtaPassThruCmdPacket.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;
AtaPassThruCmdPacket.Timeout = 100000000;
Status = AtaPassThruPtr->PassThru (
AtaPassThruPtr,
*PortNumber,
0,
&AtaPassThruCmdPacket,
NULL
);
//
// Set PxDEVSLP.DETO to 20ms and PxDEVSLP.MDAT to 10ms if the Identify Device Data log (30h, 8)
// reports 0 or if the command fails
//
DevSlpData = SataLog->Buffer[12];
if (EFI_ERROR (Status) || ((DevSlpData & 0xFF00) == 0)) {
PortData->DETO = 20;
} else {
PortData->DETO = ((DevSlpData & 0xFF00) >> 8);
}
if (EFI_ERROR (Status) || ((DevSlpData & 0x1F) == 0)) {
PortData->MDAT = 10;
} else {
PortData->MDAT = DevSlpData & 0x1F;
}
FreePool (SataLog);
return Status;
}
EFI_STATUS
GetSATAIdentifyDeviceData (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThruPtr,
IN UINT16 *PortNumber,
DEV_SATA_DVSLEEP_DATA *PortData
)
{
EFI_STATUS Status;
EFI_ATA_STATUS_BLOCK Asb;
EFI_ATA_COMMAND_BLOCK Acb;
EFI_ATA_PASS_THRU_COMMAND_PACKET AtaPassThruCmdPacket;
EFI_ATA8_IDENTIFY_DATA *IdentifyTable;
PortData->State.Valid = 0;
PortData->State.SolidStateDrive = 0;
PortData->State.DevsleepCap = 0;
PortData->State.DevsleepEn = 0;
Status = gBS->AllocatePool (
EfiBootServicesData,
0x200,
(VOID **)&IdentifyTable
);
ZeroMem (&AtaPassThruCmdPacket, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));
ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));
ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
AtaPassThruCmdPacket.Asb = &Asb;
AtaPassThruCmdPacket.Acb = &Acb;
AtaPassThruCmdPacket.Acb->AtaCommand = ATA_COMMAND_IDENTIFY_DEVICE;
AtaPassThruCmdPacket.InDataBuffer = (VOID*) IdentifyTable;
AtaPassThruCmdPacket.InTransferLength = ATA_LOG_SIZE;
AtaPassThruCmdPacket.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN;
AtaPassThruCmdPacket.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;
AtaPassThruCmdPacket.Timeout = 100000000;
Status = AtaPassThruPtr->PassThru (
AtaPassThruPtr,
*PortNumber,
0,
&AtaPassThruCmdPacket,
NULL
);
if (!EFI_ERROR (Status)) {
PortData->State.Valid = 1;
//
// Detect if the Device is Non-rotating media (e.g., SSD)
//
if (IdentifyTable->reserved_210_255[7] == 0x0001) {
PortData->State.SolidStateDrive = 1;
//
// Word 77, bit 7: DevSlp to Reduced PwrState
//
PortData->State.DevsleepPwr = ((IdentifyTable->reserved_69_79[8] & 0x80) >> 7);
//
// Word 78, bit 8: Support DevSlp
//
PortData->State.DevsleepCap = ((IdentifyTable->reserved_69_79[9] & 0x100) >> 8);
//
// Word 79, bit 8: Enable DevSlp
//
PortData->State.DevsleepEn = ((IdentifyTable->reserved_69_79[10] & 0x100) >> 8);
}
}
FreePool (IdentifyTable);
return EFI_SUCCESS;
}