summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/LynxPoint/S3Support/Pei/PchS3Peim.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/S3Support/Pei/PchS3Peim.c')
-rw-r--r--ReferenceCode/Chipset/LynxPoint/S3Support/Pei/PchS3Peim.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/S3Support/Pei/PchS3Peim.c b/ReferenceCode/Chipset/LynxPoint/S3Support/Pei/PchS3Peim.c
new file mode 100644
index 0000000..7e7149e
--- /dev/null
+++ b/ReferenceCode/Chipset/LynxPoint/S3Support/Pei/PchS3Peim.c
@@ -0,0 +1,563 @@
+/** @file
+ This is the PEIM that performs the S3 resume tasks.
+
+@copyright
+ Copyright (c) 2008 - 2015 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+**/
+#include "PchS3Peim.h"
+#include "S3SupportHob.h"
+
+//
+// GUID Definitions
+//
+EFI_GUID gS3SupportHobGuid = S3_SUPPORT_HOB_GUID;
+EFI_GUID gS3SupportSmramDataGuid = EFI_PCH_S3_SUPPORT_DATA_GUID;
+EFI_GUID gS3DataHobGuid = S3_DATA_HOB_GUID;
+
+static EFI_PEI_NOTIFY_DESCRIPTOR mSmmAccessCallbackList = {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gPeiSmmAccessPpiGuid,
+ CreateS3DataHob
+};
+
+
+
+/**
+ PCH S3 PEIM entry point. This Entry Point is entered for three separate reasons.
+ 1. It is entered via dispatcher in standard POST in order to create a HOB for
+ the DXE module to find it's EntryPoint.
+ 2. It is entered via dispatcher in S3 Resume in order to find the Dispatch Script
+ in SMRAM and copy it to a HOB in Boot Services memory.
+ 3. It is entered in response to an invocation from the Boot Script Dispatch Opcode.
+
+ In all three cases it is critical that this code is executed directly from Flash and
+ not from a location in memory.
+
+
+ @param[in] FfsHeader Header for FFS
+ @param[in] PeiServices PEI Services table pointer
+
+ @retval EFI_SUCCESS Successfully completed
+**/
+EFI_STATUS
+EFIAPI
+InitializePchS3Peim (
+ IN EFI_FFS_FILE_HEADER *FfsHeader,
+ IN EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+ UINT32 ParameterSize;
+ UINT32 TypeSize;
+ UINT32 Size;
+ EFI_PCH_S3_DISPATCH_ARRAY *DispatchArray;
+ EFI_PCH_S3_DISPATCH_ITEM *DispatchItem;
+ S3_SUPPORT_HOB *S3SupportHob;
+ S3_DATA_HOB *GuidHob;
+ PEI_SMM_ACCESS_PPI *SmmAccessPpi;
+
+ DEBUG ((EFI_D_INFO, "InitializePchS3Peim() Start\n"));
+
+ Status = EFI_SUCCESS;
+
+ GuidHob = (S3_DATA_HOB *)GetFirstGuidHob (&gS3DataHobGuid);
+
+ ///
+ /// Search for the S3 Data Hob
+ /// Not finding the Hob indicates that this is the initial pass through this code for any particular POST (S3, S4, S5 or other)
+ ///
+ if (GuidHob == NULL)
+ {
+ //
+ // If we are entering the entry point for the first time on a specific boot (regardless of mode),
+ // then we need to generate a HOB with the entry point information in order to pass the data to DXE
+ // for entry into the Boot Script.
+ //
+ DEBUG ((EFI_D_INFO, "PCH S3 Data HOB didn't exist\n"));
+ DEBUG ((EFI_D_INFO, "Attempt to create the PchS3Peim S3 Support Hob\n"));
+
+ Status = (*PeiServices)->CreateHob (
+ PeiServices,
+ EFI_HOB_TYPE_GUID_EXTENSION,
+ sizeof (S3_SUPPORT_HOB),
+ &S3SupportHob
+ );
+ if (!EFI_ERROR (Status)) {
+ S3SupportHob->Header.Name = gS3SupportHobGuid;
+ S3SupportHob->PchS3PeimEntryPoint = (UINTN) _ModuleEntryPoint;
+ DEBUG ((EFI_D_INFO, "PCH S3 Hob Created - %g\n", &gS3SupportHobGuid));
+ }
+
+ DEBUG ((EFI_D_INFO, "Collect data from SMRAM for PCH S3 Data Script\n"));
+
+ ///
+ /// Creation of the S3 Data Hob is only needed on S3. However, the boot mode can't be trusted
+ /// until after Memory Initialization has been completed because of RapidStart (FFS).
+ /// As a result, we will check boot mode inside of the CreateS3DataHob function after the SmmAccessPpi
+ /// is available.
+ ///
+ Status = PeiServicesLocatePpi (
+ &gPeiSmmAccessPpiGuid,
+ 0,
+ NULL,
+ (VOID **)&SmmAccessPpi
+ );
+ if (Status == EFI_SUCCESS)
+ {
+
+ ///
+ /// Then this is our first call on S3 resume
+ /// Find the PCH S3 Boot Script within SMRAM and create the HOB used for executing the Script
+ ///
+ Status = CreateS3DataHob (PeiServices, NULL, SmmAccessPpi);
+ DEBUG ((EFI_D_INFO, "CreateS3DataHob, result=%r\n", Status));
+
+ }
+ else
+ {
+ ///
+ /// Register for notify if SMM_ACCESS isn't yet available.
+ /// We can't have a module dependency on SMM_ACCESS because the module
+ /// must be called on both Normal Boot and S3 resume. SMM_ACCESS
+ /// PPI isn't published on Normal Boot, however.
+ ///
+ DEBUG ((EFI_D_INFO, "SMM Access protocol not yet available -> Register for notification later.\n"));
+
+ //
+ // Register notify to set default variable once variable service is ready.
+ //
+ Status = (**PeiServices).NotifyPpi (PeiServices, &mSmmAccessCallbackList);
+ ASSERT_PEI_ERROR (PeiServices, Status);
+
+ }
+ return Status;
+ }
+ else
+ {
+ DEBUG ((EFI_D_INFO, "PchS3Peim S3 Data Hob located. Proceed with Dispatch.\n"));
+
+ ///
+ /// Setup the DispatchArray variable to point at the Hob
+ ///
+ DispatchArray = (EFI_PCH_S3_DISPATCH_ARRAY *)(VOID *)&GuidHob->S3DispatchDataArray;
+ }
+
+ DEBUG ((EFI_D_INFO, "Dispatch Array Located -> 0x%x\n", DispatchArray));
+ DEBUG ((EFI_D_INFO, "Dispatch Item Located (Current NextDispatchItem entry) -> 0x%x\n", DispatchArray->NextDispatchItem));
+
+ DispatchItem = (EFI_PCH_S3_DISPATCH_ITEM *)DispatchArray->NextDispatchItem;
+
+ DEBUG ((EFI_D_INFO, "Dispatch Item Type -> 0x%x\n", DispatchItem->ItemType.Value));
+
+ ///
+ /// Calculate the size required;
+ /// ** Always round up to be 8 byte aligned as the script is initially created from 64-bit code in DXE
+ ///
+ TypeSize = QWORD_ALIGNED_SIZE (EFI_PCH_S3_DISPATCH_ITEM_TYPE);
+
+ switch (DispatchItem->ItemType.Value) {
+ case PchS3ItemTypeSendCodecCommand:
+ ParameterSize = QWORD_ALIGNED_SIZE(EFI_PCH_S3_PARAMETER_SEND_CODEC_COMMAND);
+ Status = PchS3SendCodecCommand ((EFI_PCH_S3_PARAMETER_SEND_CODEC_COMMAND *)&DispatchItem->Parameter);
+ break;
+
+ case PchS3ItemTypeInitPcieRootPortDownstream:
+ ParameterSize = QWORD_ALIGNED_SIZE(EFI_PCH_S3_PARAMETER_INIT_PCIE_ROOT_PORT_DOWNSTREAM);
+ Status = PchS3InitPcieRootPortDownstream ((EFI_PCH_S3_PARAMETER_INIT_PCIE_ROOT_PORT_DOWNSTREAM *)&DispatchItem->Parameter);
+ if (Status == EFI_NOT_FOUND) {
+ ///
+ /// EFI_NOT_FOUND is not an error here
+ ///
+ Status = EFI_SUCCESS;
+ }
+ break;
+
+ case PchS3ItemTypePcieSetPm:
+ ParameterSize = QWORD_ALIGNED_SIZE(EFI_PCH_S3_PARAMETER_PCIE_SET_PM);
+ Status = PchS3PcieSetPm ((EFI_PCH_S3_PARAMETER_PCIE_SET_PM *)&DispatchItem->Parameter);
+ if (Status == EFI_NOT_FOUND) {
+ ///
+ /// EFI_NOT_FOUND is not an error here
+ ///
+ Status = EFI_SUCCESS;
+ }
+ break;
+
+ case PchS3ItemTypeProgramIobp:
+ ParameterSize = QWORD_ALIGNED_SIZE(EFI_PCH_S3_PARAMETER_PROG_IOBP);
+ Status = PchS3ProgramIobp ((EFI_PCH_S3_PARAMETER_PROG_IOBP *)&DispatchItem->Parameter);
+ break;
+
+ default:
+ ParameterSize = 0;
+ DEBUG ((EFI_D_INFO, "Parameter not found\n"));
+
+ ASSERT (FALSE);
+ break;
+ }
+
+ ///
+ /// Advance the Execution Position
+ ///
+ Size = ParameterSize + TypeSize;
+ DispatchArray->NextDispatchItem += Size;
+
+ if ((UINTN)DispatchArray->NextDispatchItem > (UINTN)((UINT8*)DispatchArray + DispatchArray->MaximumBufferSize)) {
+ ///
+ /// We are beyond end, wrap for the next S3 resume path
+ ///
+ DispatchArray->NextDispatchItem = (UINT8*)DispatchArray + QWORD_ALIGNED_SIZE(EFI_PCH_S3_DISPATCH_ARRAY);
+ }
+
+ DEBUG ((EFI_D_INFO, "InitializePchS3Peim() End\n"));
+
+ return Status;
+}
+
+/**
+This routine is used to search SMRAM and get SmramCpuData point.
+
+@param[in] PeiServices - PEI services global pointer
+@param[in] SmmAccessPpi - SmmAccess PPI instance
+
+@retval SmramCpuData - The pointer of CPU information in SMRAM.
+**/
+EFI_STATUS
+CreateS3DataHob (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Interface
+ )
+{
+ EFI_SMRAM_DESCRIPTOR *SmramRanges;
+ UINTN SmramRangeCount;
+ UINTN Size;
+ EFI_STATUS Status;
+ UINT32 Address;
+ EFI_PCH_S3_DISPATCH_ARRAY *S3DispatchArray;
+ PEI_SMM_ACCESS_PPI *SmmAccessPpi;
+ BOOLEAN Found;
+ UINTN Index;
+ S3_DATA_HOB *GuidHob;
+ EFI_BOOT_MODE BootMode;
+
+ DEBUG ((EFI_D_INFO, "CreateS3DataHob() Start\n"));
+
+ ///
+ /// Now that we know we have a valid Boot Mode.
+ /// Check it to see if we need to process the S3 Data Hob Request
+ ///
+ Status = (*PeiServices)->GetBootMode (PeiServices, &BootMode);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO, "PchS3Peim S3 Boot Mode not available.\n"));
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "PchS3Peim S3 Boot Mode Check: %x\n", BootMode));
+
+ if (BootMode != BOOT_ON_S3_RESUME) {
+ //
+ // Only Create the S3 Data Hob on S3 resume
+ //
+ return Status;
+ }
+
+ SmmAccessPpi = (PEI_SMM_ACCESS_PPI *)Interface;
+
+ Found = FALSE;
+ S3DispatchArray = NULL;
+
+ ///
+ /// Open all SMM regions
+ ///
+ Index = 0;
+ do {
+ Status = SmmAccessPpi->Open (PeiServices, SmmAccessPpi, Index);
+ Index++;
+ } while (!EFI_ERROR (Status));
+
+ ///
+ /// Get all SMRAM range
+ ///
+ Size = 0;
+ Status = SmmAccessPpi->GetCapabilities (PeiServices, SmmAccessPpi, &Size, NULL);
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+
+ Status = PeiServicesAllocatePool (
+ Size,
+ (VOID **)&SmramRanges
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = SmmAccessPpi->GetCapabilities (PeiServices, SmmAccessPpi, &Size, SmramRanges);
+ ASSERT_EFI_ERROR (Status);
+
+ Size /= sizeof (*SmramRanges);
+ SmramRangeCount = Size;
+
+ ///
+ /// Assume TSEG is the last range of SMRAM in SmramRanges
+ ///
+ SmramRanges += SmramRangeCount - 1;
+
+ ///
+ /// Search SMRAM on page alignment for the SMM PCH S3 SMRAM Data signature
+ ///
+ for (Address = (UINT32)(SmramRanges->CpuStart + SmramRanges->PhysicalSize - EFI_PAGE_SIZE);
+ Address >= (UINT32)SmramRanges->CpuStart;
+ Address -= EFI_PAGE_SIZE
+ ) {
+ S3DispatchArray = (EFI_PCH_S3_DISPATCH_ARRAY *)(UINTN)Address;
+ if (CompareGuid (&S3DispatchArray->PchS3CustomScriptGuid, &gS3SupportSmramDataGuid)) {
+ ///
+ /// Find it
+ ///
+ Found = TRUE;
+ break;
+ }
+ }
+
+ ///
+ /// Close all SMM regions
+ ///
+ Index = 0;
+ do {
+ Status = SmmAccessPpi->Close (PeiServices, SmmAccessPpi, Index);
+ Index++;
+ } while (!EFI_ERROR (Status));
+
+
+ if (!Found)
+ {
+ DEBUG ((EFI_D_INFO, "S3 Dispatch Data Not Found!\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ ///
+ /// Generate Hob from SMRAM Data
+ ///
+ Status = (*PeiServices)->CreateHob (
+ PeiServices,
+ EFI_HOB_TYPE_GUID_EXTENSION,
+ EFI_PAGE_SIZE,
+ &GuidHob
+ );
+
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO, "PchS3Peim S3 Data Hob created: Address - 0x%x\n", (UINTN)GuidHob));
+
+ GuidHob->Header.Name = gS3DataHobGuid;
+ CopyMem (&GuidHob->S3DispatchDataArray, S3DispatchArray, S3DispatchArray->MaximumBufferSize);
+
+ ///
+ /// Reset the NextDispatchItem to the beginning of the buffer for playback.
+ ///
+ S3DispatchArray = (EFI_PCH_S3_DISPATCH_ARRAY *)(UINTN)&GuidHob->S3DispatchDataArray;
+ S3DispatchArray->NextDispatchItem = (UINT8 *)S3DispatchArray + QWORD_ALIGNED_SIZE(EFI_PCH_S3_DISPATCH_ARRAY);
+
+ }
+
+ DEBUG ((EFI_D_INFO, "CreateS3DataHob() End\n"));
+
+ return EFI_SUCCESS;
+}
+
+
+#define AZALIA_MAX_LOOP_TIME 10
+#define AZALIA_WAIT_PERIOD 100
+
+/**
+ Polling the Status bit
+
+ @param[in] StatusReg The regsiter address to read the status
+ @param[in] PollingBitMap The bit mapping for polling
+ @param[in] PollingData The Data for polling
+
+ @retval EFI_SUCCESS The function completed successfully
+ @retval EFI_TIMEOUT Polling the bit map time out
+**/
+static
+EFI_STATUS
+CodecStatusPolling (
+ IN UINT32 StatusReg,
+ IN UINT16 PollingBitMap,
+ IN UINT16 PollingData
+ )
+{
+ UINT32 LoopTime;
+
+ for (LoopTime = 0; LoopTime < AZALIA_MAX_LOOP_TIME; LoopTime++) {
+ if ((MmioRead16 (StatusReg) & PollingBitMap) == PollingData) {
+ break;
+ } else {
+ PchPmTimerStall (AZALIA_WAIT_PERIOD);
+ }
+ }
+
+ if (LoopTime >= AZALIA_MAX_LOOP_TIME) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Send Codec command on S3 resume
+
+ @param[in] Parameter Parameters passed in from DXE
+
+ @retval EFI_DEVICE_ERROR Device status error, operation failed
+ @retval EFI_SUCCESS The function completed successfully
+**/
+EFI_STATUS
+PchS3SendCodecCommand (
+ EFI_PCH_S3_PARAMETER_SEND_CODEC_COMMAND *Parameter
+ )
+{
+ UINT32 HdaBar;
+ UINT32 *CodecCommandData;
+ EFI_STATUS Status;
+
+ HdaBar = Parameter->HdaBar;
+ CodecCommandData = &Parameter->CodecCmdData;
+
+ DEBUG ((EFI_D_INFO, "Going to SendCodecCommand: %08x! \n", *CodecCommandData));
+ Status = CodecStatusPolling (HdaBar + R_HDA_IRS, (UINT16) B_HDA_IRS_ICB, (UINT16) 0);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "ICB bit is not zero before SendCodecCommand! \n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ MmioWrite32 (HdaBar + R_HDA_IC, *CodecCommandData);
+ MmioOr16 ((UINTN) (HdaBar + R_HDA_IRS), (UINT16) ((B_HDA_IRS_IRV | B_HDA_IRS_ICB)));
+
+ Status = CodecStatusPolling (HdaBar + R_HDA_IRS, (UINT16) B_HDA_IRS_ICB, (UINT16) 0);
+ if (EFI_ERROR (Status)) {
+ MmioAnd16 ((UINTN) (HdaBar + R_HDA_IRS), (UINT16)~(B_HDA_IRS_ICB));
+ DEBUG ((EFI_D_ERROR, "SendCodecCommand: SendCodecCommand:%08x fail! \n"));
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Perform Init Root Port Downstream devices on S3 resume
+
+ @param[in] Parameter Parameters passed in from DXE
+
+ @retval EFI_STATUS
+**/
+EFI_STATUS
+PchS3InitPcieRootPortDownstream (
+ EFI_PCH_S3_PARAMETER_INIT_PCIE_ROOT_PORT_DOWNSTREAM *Parameter
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PchPcieInitRootPortDownstreamDevices (
+ Parameter->RootPortBus,
+ Parameter->RootPortDevice,
+ Parameter->RootPortFunc,
+ Parameter->TempBusNumberMin,
+ Parameter->TempBusNumberMax,
+ NULL
+ );
+ ///
+ /// Not checking the error status here - downstream device not present does not
+ /// mean an error of this root port. Our return status of EFI_SUCCESS means this
+ /// port is enabled and outer function depends on this return status to do
+ /// subsequent initializations.
+ ///
+ return Status;
+}
+
+/**
+ Perform Root Port Downstream devices PCIE ASPM and LTR override on S3 resume
+
+ @param[in] Parameter Parameters passed in from DXE
+
+ @retval EFI_STATUS
+**/
+EFI_STATUS
+PchS3PcieSetPm (
+ EFI_PCH_S3_PARAMETER_PCIE_SET_PM *Parameter
+ )
+{
+ EFI_STATUS Status;
+ PCH_PCIE_DEVICE_ASPM_OVERRIDE *DevAspmOverride;
+ PCH_PCIE_DEVICE_LTR_OVERRIDE *DevLtrOverride;
+ PCH_PCIE_PWR_OPT *S3PchPwrOptPcie;
+ BOOLEAN L1SubstatesSupported;
+ BOOLEAN LtrSupported;
+
+ DevAspmOverride = (PCH_PCIE_DEVICE_ASPM_OVERRIDE *) (UINTN) Parameter->DevAspmOverrideAddr;
+ DevLtrOverride = (PCH_PCIE_DEVICE_LTR_OVERRIDE *) (UINTN) Parameter->DevLtrOverrideAddr;
+ S3PchPwrOptPcie = (PCH_PCIE_PWR_OPT *) (UINTN) Parameter->PchPwrOptPcie;
+ Status = PcieSetPm (
+ Parameter->RootPortBus,
+ Parameter->RootPortDevice,
+ Parameter->RootPortFunc,
+ Parameter->RootPortAspm,
+ Parameter->NumOfDevAspmOverride,
+ DevAspmOverride,
+ Parameter->TempBusNumberMin,
+ Parameter->TempBusNumberMax,
+ Parameter->NumOfDevLtrOverride,
+ DevLtrOverride,
+ S3PchPwrOptPcie,
+ &L1SubstatesSupported,
+ Parameter->L1SubstatesConfig,
+ Parameter->PolicyRevision,
+ Parameter->FirstRPToSetPm,
+ Parameter->L1SupportedInAllEnabledPorts,
+ Parameter->ClkreqSupportedInAllEnabledPorts,
+ &LtrSupported
+ );
+ ///
+ /// Not checking the error status here - downstream device not present does not
+ /// mean an error of this root port. Our return status of EFI_SUCCESS means this
+ /// port is enabled and outer function depends on this return status to do
+ /// subsequent initializations.
+ ///
+ return Status;
+}
+
+/**
+ Perform PCH IOBP programming on S3 resume
+
+ @param[in] Parameter Parameters passed in from DXE
+
+ @retval EFI_STATUS
+**/
+EFI_STATUS
+PchS3ProgramIobp (
+ EFI_PCH_S3_PARAMETER_PROG_IOBP *Parameter
+ )
+{
+ EFI_STATUS Status;
+
+ Status = ProgramIobp (
+ Parameter->RootComplexBar,
+ Parameter->Address,
+ Parameter->AndMask,
+ Parameter->OrMask
+ );
+
+ return Status;
+}