summaryrefslogtreecommitdiff
path: root/ReferenceCode/Haswell/PowerManagement/Dxe/IdleStates.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Haswell/PowerManagement/Dxe/IdleStates.c')
-rw-r--r--ReferenceCode/Haswell/PowerManagement/Dxe/IdleStates.c918
1 files changed, 918 insertions, 0 deletions
diff --git a/ReferenceCode/Haswell/PowerManagement/Dxe/IdleStates.c b/ReferenceCode/Haswell/PowerManagement/Dxe/IdleStates.c
new file mode 100644
index 0000000..a450b5d
--- /dev/null
+++ b/ReferenceCode/Haswell/PowerManagement/Dxe/IdleStates.c
@@ -0,0 +1,918 @@
+/** @file
+ This file contains power management C State configuration functions for
+ Haswell processors.
+
+ Acronyms:
+ PPM - Processor Power Management
+ TM - Thermal Monitor
+ IST - Intel(R) Speedstep technology
+ HT - Hyper-Threading Technology
+
+@copyright
+ Copyright (c) 2012 - 2013 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 "PowerMgmtCommon.h"
+
+/**
+ Initializes C States Power management features
+
+ @param[in] CpuPmConfig Pointer to policy protocol instance
+**/
+VOID
+InitializeCState (
+ IN POWER_MGMT_CONFIG *CpuPmConfig
+ )
+{
+ EFI_STATUS Status;
+ UINT16 mAcpiBaseAddr;
+ ///
+ /// Get the ACPI Base Address
+ ///
+ mAcpiBaseAddr = PchLpcPciCfg16 (R_PCH_LPC_ACPI_BASE) & 0xFFFE;
+
+ ///
+ /// Initialize C states, some are general, some are processor specific.
+ /// Dynamic loading of CST SSDT tables occurs at PpmPostInit.
+ ///
+ EnableCStates (mAcpiBaseAddr + PM_CST_LVL2);
+ ///
+ /// Calibrate C State 24MHz BCLK
+ ///
+//@todo: Need to finalize on whether or not re-calibration option should be provided
+ Status = CalibrateBclkForCStates ();
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_INFO, "24MHz BCLK calibration Failed \n"));
+//@todo: Need to finalize on how to handle failure of 24 MHz calibration
+ }
+
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C_STATES) {
+ ///
+ /// Update Fadt table for C State support.
+ ///
+ ConfigureFadtCStates ();
+ }
+ InitCstatePreWake (mCpuPmConfig);
+
+}
+
+/**
+ Disable/Enable the CState Pre-Wake Feature
+
+ @param[in] CpuPmConfig Pointer to policy protocol instance
+**/
+VOID
+InitCstatePreWake (
+ IN POWER_MGMT_CONFIG *CpuPmConfig
+ )
+{
+ MSR_REGISTER TempMsr;
+
+ TempMsr.Qword = AsmReadMsr64 (MSR_POWER_CTL);
+ TempMsr.Dwords.Low &= ~(B_MSR_POWER_CTL_CSTATE_PRE_WAKE_DISABLE);
+ if (CpuPmConfig->pFunctionEnables->CStatePreWake == PPM_DISABLE) {
+ TempMsr.Dwords.Low |= B_MSR_POWER_CTL_CSTATE_PRE_WAKE_DISABLE;
+ }
+ AsmWriteMsr64 (MSR_POWER_CTL, TempMsr.Qword);
+
+ return;
+}
+
+/**
+ Enables C-State support as specified by the input flags on all logical
+ processors and sets associated timing requirements in the chipset.
+
+ @param[in] C3IoAddress IO address to generate C3 states (PM base + 014 usually)
+**/
+VOID
+EnableCStates (
+ IN UINT16 C3IoAddress
+ )
+{
+ MSR_REGISTER PowerCtl;
+ MSR_REGISTER TempMsr;
+ UINT32 LCR0Latency;
+ UINT32 LCR1Latency;
+ UINT32 LCR2Latency;
+ UINT32 LCR3Latency;
+ UINT32 LCR4Latency;
+ UINT32 LCR5Latency;
+ UINT16 EnableCStateParameters;
+ CPU_FAMILY mCpuFamilyId;
+
+ mCpuFamilyId = mPpmGlobalNvsAreaProtocol->Area->Cpuid & CPUID_FULL_FAMILY_MODEL;
+ ///
+ /// Load the C-State parameters to pass to the core function.
+ ///
+ EnableCStateParameters = C3IoAddress;
+ ///
+ /// Enable C-States on all logical processors.
+ ///
+ RunOnAllLogicalProcessors (ApSafeEnableCStates, &EnableCStateParameters);
+ ///
+ /// If C-states are disabled or not supported, Disable C1e and retrun
+ ///
+ if ((mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C_STATES) == 0) {
+ PowerCtl.Qword = AsmReadMsr64 (MSR_POWER_CTL);
+ PowerCtl.Dwords.Low &= ~B_MSR_POWER_CTL_C1E;
+ AsmWriteMsr64 (MSR_POWER_CTL, PowerCtl.Qword);
+ DEBUG (
+ (EFI_D_INFO,
+ "Setup C state disabled.Disable C1e. MSR(1FC) : %X %X\n",
+ PowerCtl.Dwords.High,
+ PowerCtl.Dwords.Low)
+ );
+ return;
+ }
+ ///
+ /// Configure supported enhanced C-states
+ ///
+ /// Read Power Ctl MSR
+ ///
+ PowerCtl.Qword = AsmReadMsr64 (MSR_POWER_CTL);
+ DEBUG ((EFI_D_INFO, "MSR(1FC) before configuring C1E: %X %X\n", PowerCtl.Dwords.High, PowerCtl.Dwords.Low));
+ ///
+ /// Enable supported states
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C1E) {
+ PowerCtl.Dwords.Low |= B_MSR_POWER_CTL_C1E;
+ } else {
+ PowerCtl.Dwords.Low &= ~B_MSR_POWER_CTL_C1E;
+ }
+ ///
+ /// Update Power Control MSR
+ ///
+ AsmWriteMsr64 (MSR_POWER_CTL, PowerCtl.Qword);
+ DEBUG ((EFI_D_INFO, "MSR(1FC) after configuring C1E: %X %X\n", PowerCtl.Dwords.High, PowerCtl.Dwords.Low));
+ ///
+ /// Program Interrupt response time limits used by processor to decided when to get into
+ /// package C3, C6 and C7
+ ///
+ DEBUG ((EFI_D_INFO, "Programming the 0xC3/C6/C7 (MSR 0x60A, 0x60B ,0X60C Latencies \n"));
+ //
+ // Package C3 Interrupt response time
+ //
+ TempMsr.Qword = AsmReadMsr64 (MSR_C_STATE_LATENCY_CONTROL_0);
+ DEBUG ((EFI_D_INFO, "MSR(60A) before configuring Latency: %X %X\n", TempMsr.Dwords.High, TempMsr.Dwords.Low));
+ TempMsr.Dwords.Low &= ~(B_INTERRUPT_RESPONSE_TIME_LIMIT_MASK | B_TIME_UNIT_MASK | B_PKG_IRTL_VALID);
+ ///
+ /// Program Interrupt Response Time Unit and Latency for MSR 0x60A
+ ///
+ TempMsr.Dwords.Low |= mCpuPmConfig->CstateLatencyControl0Irtl;
+ TempMsr.Dwords.Low |= LShiftU64 (mCpuPmConfig->CstateLatencyControl0TimeUnit, N_TIME_UNIT_OFFSET);
+ TempMsr.Dwords.Low |= B_PKG_IRTL_VALID;
+ AsmWriteMsr64 (MSR_C_STATE_LATENCY_CONTROL_0, TempMsr.Qword);
+ //
+ // Package C6/C7 short Interrupt response time
+ //
+ TempMsr.Qword = AsmReadMsr64 (MSR_C_STATE_LATENCY_CONTROL_1);
+ DEBUG ((EFI_D_INFO, "MSR(60B) before configuring Latency: %X %X\n", TempMsr.Dwords.High, TempMsr.Dwords.Low));
+ TempMsr.Dwords.Low &= ~(B_INTERRUPT_RESPONSE_TIME_LIMIT_MASK | B_TIME_UNIT_MASK | B_PKG_IRTL_VALID);
+ ///
+ /// Program Interrupt Response Time Unit and Latency for MSR 0x60B
+ ///
+ TempMsr.Dwords.Low |= mCpuPmConfig->CstateLatencyControl1Irtl;
+ TempMsr.Dwords.Low |= LShiftU64 (mCpuPmConfig->CstateLatencyControl1TimeUnit, N_TIME_UNIT_OFFSET);
+ TempMsr.Dwords.Low |= B_PKG_IRTL_VALID;
+ AsmWriteMsr64 (MSR_C_STATE_LATENCY_CONTROL_1, TempMsr.Qword);
+ //
+ // Package C6/C7 long Interrupt response time
+ //
+ TempMsr.Qword = AsmReadMsr64 (MSR_C_STATE_LATENCY_CONTROL_2);
+ DEBUG ((EFI_D_INFO, "MSR(60C) before configuring Latency: %X %X\n", TempMsr.Dwords.High, TempMsr.Dwords.Low));
+ TempMsr.Dwords.Low &= ~(B_INTERRUPT_RESPONSE_TIME_LIMIT_MASK | B_TIME_UNIT_MASK | B_PKG_IRTL_VALID);
+ ///
+ /// Program Interrupt Response Time Unit and Latency for MSR 0x60C
+ ///
+ TempMsr.Dwords.Low |= mCpuPmConfig->CstateLatencyControl2Irtl;
+ TempMsr.Dwords.Low |= LShiftU64 (mCpuPmConfig->CstateLatencyControl2TimeUnit, N_TIME_UNIT_OFFSET);
+ TempMsr.Dwords.Low |= B_PKG_IRTL_VALID;
+ AsmWriteMsr64 (MSR_C_STATE_LATENCY_CONTROL_2, TempMsr.Qword);
+ if (mCpuFamilyId == EnumCpuHswUlt) {
+ //
+ // Package C8 Interrupt response time
+ //
+ TempMsr.Qword = AsmReadMsr64 (MSR_C_STATE_LATENCY_CONTROL_3);
+ DEBUG ((EFI_D_INFO, "MSR(633) before configuring Latency: %X %X\n", TempMsr.Dwords.High, TempMsr.Dwords.Low));
+ TempMsr.Dwords.Low &= ~(B_INTERRUPT_RESPONSE_TIME_LIMIT_MASK | B_TIME_UNIT_MASK | B_PKG_IRTL_VALID);
+ ///
+ /// Program Interrupt Response Time Unit and Latency for MSR 0x633
+ ///
+ TempMsr.Dwords.Low |= mCpuPmConfig->CstateLatencyControl3Irtl;
+ TempMsr.Dwords.Low |= LShiftU64 (mCpuPmConfig->CstateLatencyControl3TimeUnit, N_TIME_UNIT_OFFSET);
+ TempMsr.Dwords.Low |= B_PKG_IRTL_VALID;
+ AsmWriteMsr64 (MSR_C_STATE_LATENCY_CONTROL_3, TempMsr.Qword);
+ //
+ // Package C9 Interrupt response time
+ //
+ TempMsr.Qword = AsmReadMsr64 (MSR_C_STATE_LATENCY_CONTROL_4);
+ DEBUG ((EFI_D_INFO, "MSR(634) before configuring Latency: %X %X\n", TempMsr.Dwords.High, TempMsr.Dwords.Low));
+ TempMsr.Dwords.Low &= ~(B_INTERRUPT_RESPONSE_TIME_LIMIT_MASK | B_TIME_UNIT_MASK | B_PKG_IRTL_VALID);
+ ///
+ /// Program Interrupt Response Time Unit and Latency for MSR 0x634
+ ///
+ TempMsr.Dwords.Low |= mCpuPmConfig->CstateLatencyControl4Irtl;
+ TempMsr.Dwords.Low |= LShiftU64 (mCpuPmConfig->CstateLatencyControl4TimeUnit, N_TIME_UNIT_OFFSET);
+ TempMsr.Dwords.Low |= B_PKG_IRTL_VALID;
+ AsmWriteMsr64 (MSR_C_STATE_LATENCY_CONTROL_4, TempMsr.Qword);
+ //
+ // Package C10 Interrupt response time
+ //
+ TempMsr.Qword = AsmReadMsr64 (MSR_C_STATE_LATENCY_CONTROL_5);
+ DEBUG ((EFI_D_INFO, "MSR(635) before configuring Latency: %X %X\n", TempMsr.Dwords.High, TempMsr.Dwords.Low));
+ TempMsr.Dwords.Low &= ~(B_INTERRUPT_RESPONSE_TIME_LIMIT_MASK | B_TIME_UNIT_MASK | B_PKG_IRTL_VALID);
+ ///
+ /// Program Interrupt Response Time Unit and Latency for MSR 0x635
+ ///
+ TempMsr.Dwords.Low |= mCpuPmConfig->CstateLatencyControl5Irtl;
+ TempMsr.Dwords.Low |= LShiftU64 (mCpuPmConfig->CstateLatencyControl5TimeUnit, N_TIME_UNIT_OFFSET);
+ TempMsr.Dwords.Low |= B_PKG_IRTL_VALID;
+ AsmWriteMsr64 (MSR_C_STATE_LATENCY_CONTROL_5, TempMsr.Qword);
+ }
+ ///
+ /// Update the PPM Global NVS Area
+ ///
+ LCR0Latency = (1 << (mCpuPmConfig->CstateLatencyControl0TimeUnit * 5));
+ LCR0Latency = (LCR0Latency * mCpuPmConfig->CstateLatencyControl0Irtl) / 1000;
+ //
+ // _CST Latency: WordConst, so limit the latency value to max 0xFFFF
+ //
+ if (LCR0Latency > 0xFFFF) {
+ LCR0Latency = 0xFFFF;
+ }
+ LCR1Latency = (1 << (mCpuPmConfig->CstateLatencyControl1TimeUnit * 5));
+ LCR1Latency = (LCR1Latency * mCpuPmConfig->CstateLatencyControl1Irtl) / 1000;
+ if (LCR1Latency > 0xFFFF) {
+ LCR1Latency = 0xFFFF;
+ }
+ LCR2Latency = (1 << (mCpuPmConfig->CstateLatencyControl2TimeUnit * 5));
+ LCR2Latency = (LCR2Latency * mCpuPmConfig->CstateLatencyControl2Irtl) / 1000;
+ if (LCR2Latency > 0xFFFF) {
+ LCR2Latency = 0xFFFF;
+ }
+
+ LCR3Latency = (1 << (mCpuPmConfig->CstateLatencyControl3TimeUnit * 5));
+ LCR3Latency = (LCR3Latency * mCpuPmConfig->CstateLatencyControl3Irtl) / 1000;
+ if (LCR3Latency > 0xFFFF) {
+ LCR3Latency = 0xFFFF;
+ }
+
+ LCR4Latency = (1 << (mCpuPmConfig->CstateLatencyControl4TimeUnit * 5));
+ LCR4Latency = (LCR4Latency * mCpuPmConfig->CstateLatencyControl4Irtl) / 1000;
+ if (LCR4Latency > 0xFFFF) {
+ LCR4Latency = 0xFFFF;
+ }
+
+ LCR5Latency = (1 << (mCpuPmConfig->CstateLatencyControl5TimeUnit * 5));
+ LCR5Latency = (LCR5Latency * mCpuPmConfig->CstateLatencyControl5Irtl) / 1000;
+ if (LCR5Latency > 0xFFFF) {
+ LCR5Latency = 0xFFFF;
+ }
+
+ ///
+ /// Update the PPM Global NVS Area.
+ /// Update the PPM NVRAM values for C3
+ ///
+ mPpmGlobalNvsAreaProtocol->Area->C3MwaitValue = 0x10;
+ mPpmGlobalNvsAreaProtocol->Area->C3Latency = (UINT16) LCR0Latency;
+ ///
+ /// Update PPM NVRAM Values for C6
+ ///
+ if ((mCpuPmConfig->pFunctionEnables->LongLatencyC6) &&( mPpmGlobalNvsAreaProtocol->Area->PpmFlags & C6_LONG_LATENCY_ENABLE)) {
+ mPpmGlobalNvsAreaProtocol->Area->C6MwaitValue = 0x21;
+ mPpmGlobalNvsAreaProtocol->Area->C6Latency = (UINT16) LCR2Latency;
+ } else {
+ mPpmGlobalNvsAreaProtocol->Area->C6MwaitValue = 0x20;
+ mPpmGlobalNvsAreaProtocol->Area->C6Latency = (UINT16) LCR1Latency;
+ }
+ ///
+ /// Update PPM NVRAM Values for C7 - select the C-state supported among- C7 / C7S
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C7) { // Is C7 supported ?
+ if ((mCpuPmConfig->pFunctionEnables->LongLatencyC7) && (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & C7_LONG_LATENCY_ENABLE)) {
+ mPpmGlobalNvsAreaProtocol->Area->C7MwaitValue = 0x31;
+ mPpmGlobalNvsAreaProtocol->Area->C7Latency = (UINT16) LCR2Latency;
+ } else {
+ mPpmGlobalNvsAreaProtocol->Area->C7MwaitValue = 0x30;
+ mPpmGlobalNvsAreaProtocol->Area->C7Latency = (UINT16) LCR1Latency;
+ }
+ }
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C7S) { // Is C7S supported ?
+ if ((mCpuPmConfig->pFunctionEnables->LongLatencyC7) && (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & C7s_LONG_LATENCY_ENABLE)) {
+ mPpmGlobalNvsAreaProtocol->Area->C7MwaitValue = 0x33;
+ mPpmGlobalNvsAreaProtocol->Area->C7Latency = (UINT16) LCR2Latency;
+ } else {
+ mPpmGlobalNvsAreaProtocol->Area->C7MwaitValue = 0x32;
+ mPpmGlobalNvsAreaProtocol->Area->C7Latency = (UINT16) LCR1Latency;
+ }
+ }
+ ///
+ /// Update PPM NVRAM Values for CD - select the deepest C-state supported among- C8 / C9 / C10
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C10) { // C10 supported
+ mPpmGlobalNvsAreaProtocol->Area->CDIOLevel = PCH_ACPI_LV7;
+ mPpmGlobalNvsAreaProtocol->Area->CDPowerValue = C10_POWER;
+ mPpmGlobalNvsAreaProtocol->Area->CDMwaitValue = 0x60;
+ mPpmGlobalNvsAreaProtocol->Area->CDLatency = (UINT16) LCR5Latency;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C9) { // C9 supported
+ mPpmGlobalNvsAreaProtocol->Area->CDIOLevel = PCH_ACPI_LV6;
+ mPpmGlobalNvsAreaProtocol->Area->CDPowerValue = C9_POWER;
+ mPpmGlobalNvsAreaProtocol->Area->CDMwaitValue = 0x50;
+ mPpmGlobalNvsAreaProtocol->Area->CDLatency = (UINT16) LCR4Latency;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C8) { // C8 supported
+ mPpmGlobalNvsAreaProtocol->Area->CDIOLevel = PCH_ACPI_LV5;
+ mPpmGlobalNvsAreaProtocol->Area->CDPowerValue = C8_POWER;
+ mPpmGlobalNvsAreaProtocol->Area->CDMwaitValue = 0x40;
+ mPpmGlobalNvsAreaProtocol->Area->CDLatency = (UINT16) LCR3Latency;
+ }
+
+ return;
+}
+
+/**
+ BootScript for PCode Mailbox function for mailbox write commands.
+ This function will poll the mailbox interface for control, issue the command
+ during s3 resume
+
+ @param[IN] MailboxCommand,
+ @param[IN] MailboxData,
+**/
+VOID
+MailboxS3Write (
+ IN UINT32 MailboxCommand,
+ IN UINT32 MailboxData
+ )
+{
+#ifdef ULT_FLAG
+ UINT32 Data32Mask;
+ UINT32 Data32Value;
+ UINT16 StallCount;
+ UINT32 MchBar;
+
+ StallCount = 0;
+
+ ///
+ /// Poll the run/busy to ensure the interface is available
+ ///
+ Data32Mask = BIT31;
+ Data32Value = 0;
+ MchBar = (MmioRead32 (MmPciAddress (0, 0, 0, 0, MCHBAR_OFFSET)) &~BIT0);
+ SCRIPT_MEM_POLL (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (MchBar + PCODE_MAILBOX_INTERFACE_OFFSET),
+ &Data32Mask,
+ &Data32Value,
+ MAILBOX_WAIT_STALL,
+ MAILBOX_WAIT_TIMEOUT
+ );
+
+ ///
+ /// Write the PCODE mailbox DATA field
+ ///
+ MchBar = (MmioRead32 (MmPciAddress (0, 0, 0, 0, MCHBAR_OFFSET)) &~BIT0);
+ SCRIPT_MEM_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) ((MchBar + PCODE_MAILBOX_DATA_OFFSET)),
+ 1,
+ &(MailboxData)
+ );
+ ///
+ /// Write the PCODE mailbox Command field
+ ///
+ SCRIPT_MEM_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (MchBar + PCODE_MAILBOX_INTERFACE_OFFSET),
+ 1,
+ &(MailboxCommand)
+ );
+#endif // ULT_FLAG
+ return;
+}
+
+/**
+ Calibrate 24MHz BCLK support to reduce the power consumption in idle states.
+
+ @retval EFI_UNSUPPORTED Unrecognized 24MHz BCLK Calibration Type.
+ @retval EFI_SUCCESS Processor C-State 24MHz BCLK support calibrated successfully.
+**/
+EFI_STATUS
+CalibrateBclkForCStates (
+ VOID
+ )
+{
+ /*****************************************************************************************************************
+ - BIOS can choose to configure the conversion factor or allow PCODE to calibrate itself or have NO calibration at all.
+ - If NO Calibration then the below steps are needed
+ o BIOS should bypass all calibration process and write a constant value via "WRITE convert ratio"
+ - If BIOS chooses PCODE calibration then the below steps are needed
+ o BIOS writes FSM interval: A value of all Fs is recommended for this
+ o Read PCODE calibration factor
+ o Store and use if needed on next Power up for BIOS calibration
+ - If BIOS chooses to calibrate itself then the below steps are needed
+ o Send command for calibration to prevent BCLK shut off
+ o Read TSC counter values (send command for atomic sampling of TSC100 and TSC24, then read the values)
+ o Delay (what is the maximum tolerable delay?)
+ o Read counters again
+ o Divide 100 MHz ticks by 24 MHz ticks to get the calibration factor
+ o Store the calibration factor value
+ o Write calibration factor using mailbox command during subsequent power-ups
+
+ Command: Command Name: Description:
+ 0x80000009 WriteTsc24_100Cmd Sample 24 MHz and 100 MHz TSC simultaneously
+ 0x80000109 ReadTsc24LowerCmd Read lower 32 bits of 24 MHz TSC
+ 0x80000209 ReadTsc24UpperCmd Read upper 32 bits of 24 MHz TSC
+ 0x80000309 ReadTsc100LowerCmd Read lower 32 bits of 100 MHz TSC
+ 0x80000409 ReadTsc100UpperCmd Read upper 32 bits of 100 MHz TSC
+ 0x80000509 ReadPcodeCalibratedCmd Read PCODE calibrated conversion factor
+ 0x80000609 WriteConversionRatioCmd Command for writing the conversion ratio
+ 0x80000709 WritePreventBclkOffCmd Command for calibration prevents BCLK from shutting off and prevents package
+ from entering deep C states
+ 0x80000809 WRITE_MEASURE_INTERVAL_CMD Measurement interval for pCode calibration of TSC24-to-TSC100 conversion factor
+ 0x80000909 WriteFsmMeasureIntvlCmd Write FSM measure interval
+ 0x85000000 StartCalValue Constant value to start calibration
+ 0xFFFFFFFF PcodeIntervalValue PCODE flow calibration time value
+
+ ***************************************************************************************************************/
+
+ EFI_STATUS Status = EFI_SUCCESS;
+
+#ifdef ULT_FLAG
+ CPU_FAMILY mCpuFamilyId;
+ mCpuFamilyId = mPpmGlobalNvsAreaProtocol->Area->Cpuid & CPUID_FULL_FAMILY_MODEL;
+ if (mCpuFamilyId == EnumCpuHswUlt) {
+ UINT32 PcodeIntervalValue;
+ UINT32 BiosMeasureIntervalValue;
+ UINT32 PcalFactor_Lower;
+ UINT32 PcalFactor_Upper;
+ UINT32 LibStatus;
+ UINT64 Tsc24_64;
+ UINT64 Tsc100_64;
+ UINT64 Temp24_64;
+ UINT64 Temp100_64;
+ UINT64 PcalFactor;
+ UINT64 SafeCalibrationValue;
+ UINT64 TscRemainder;
+
+ PCODE_BCLK_CALIBRATION_MAILBOX ReturnCalVal;
+
+ PcodeIntervalValue = 0xFFFFFFFF;
+ BiosMeasureIntervalValue = 0x7270E00;
+ PcalFactor_Lower = 0x0;
+ PcalFactor_Upper = 0x0;
+ Tsc24_64 = 0x0;
+ Tsc100_64 = 0x0;
+ Temp24_64 = 0x0;
+ Temp100_64 = 0x0;
+ PcalFactor = 0x0;
+ SafeCalibrationValue = 0x0;
+ TscRemainder = 0x0;
+ LibStatus = 0x0;
+ DEBUG ((EFI_D_INFO, "Initializing 24MHz BCLK calibration \n"));
+
+ switch (mCpuPmConfig->PcodeCalibration) {
+ case NO_CALIBRATE:
+ ///
+ /// Bypass all calibration process
+ ///
+ DEBUG ((EFI_D_INFO, "Bypass the 24MHz BCLK calibration \n"));
+ ///
+ /// Write a constant value
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, WRITE_CONVERTION_RATIO_CMD, START_CAL_VALUE, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed to write a constant value \n"));
+ break;
+ }
+ MailboxS3Write(WRITE_CONVERTION_RATIO_CMD, START_CAL_VALUE);
+ break;
+
+ case PCODE_CALIBRATE:
+ ///
+ /// Initiate the PCODE calibration
+ ///
+ DEBUG ((EFI_D_INFO, "Initiate PCODE 24MHz BCLK calibration \n"));
+ ///
+ /// BIOS writes FSM interval: A value of all F's is recommended for this
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, WRITE_FSM_MEASURE_INTVL_CMD, PcodeIntervalValue, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed BIOS writes FSM interval \n"));
+ break;
+ }
+ MailboxS3Write(WRITE_FSM_MEASURE_INTVL_CMD, PcodeIntervalValue);
+ break;
+
+ case BIOS_CALIBRATE:
+ ///
+ /// Initiate Bios calibration
+ ///
+ DEBUG ((EFI_D_INFO, "Initiate BIOS 24MHz BCLK calibration \n"));
+ ///
+ /// Send command for calibration to prevent BCLK shut off
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, WRITE_PREVENT_BCLKOFF_CMD, 1, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Send command for calibration to prevent BCLK shut off \n"));
+ break;
+ }
+ ///
+ /// Read TSC counter values (send command for atomic sampling of TSC100 and TSC24)
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, SAMPLE_TSC_24AND100_CMD, 0, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed First Write TSC counter values (send command for atomic sampling of TSC100 and TSC24) \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC24_LOWER_CMD, &ReturnCalVal.TSC24_L1, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed First read TSC-24 Lower 32 bits \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC24_UPPER_CMD, &ReturnCalVal.TSC24_U1, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed First read TSC-24 Upper 32 bits \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC100_LOWER_CMD, &ReturnCalVal.TSC100_L1, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed First read TSC-100 Lower 32 bits \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC100_UPPER_CMD, &ReturnCalVal.TSC100_U1, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed First read TSC-100 Upper 32 bits \n"));
+ break;
+ }
+ ///
+ /// Store initial clock values
+ ///
+ Tsc24_64 = ((UINT64)ReturnCalVal.TSC24_U1 << 32);
+ Temp24_64 = (Tsc24_64 |= (ReturnCalVal.TSC24_L1));
+ Tsc100_64 = ((UINT64)ReturnCalVal.TSC100_U1 << 32);
+ Temp100_64 = (Tsc100_64 |= (ReturnCalVal.TSC100_L1));
+
+ ///
+ /// Delay for 22 ms
+ ///
+ gBS->Stall (PCODE_BCLK_CALIBRATION_TIMEOUT * STALL_ONE_MILLI_SECOND);
+
+ ///
+ /// Read TSC 24 and TSC 100 counters again, and calculate calibration factor
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, SAMPLE_TSC_24AND100_CMD, 0, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Second Write TSC counter values (send command for atomic sampling of TSC100 and TSC24) \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC24_LOWER_CMD, &ReturnCalVal.TSC24_L2, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Second read TSC-24 Lower 32 bits \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC24_UPPER_CMD, &ReturnCalVal.TSC24_U2, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Second read TSC-24 Upper 32 bits \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC100_LOWER_CMD, &ReturnCalVal.TSC100_L2, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Second read TSC-100 Lower 32 bits \n"));
+ break;
+ }
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_TSC100_UPPER_CMD, &ReturnCalVal.TSC100_U2, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Second read TSC-100 Upper 32 bits \n"));
+ break;
+ }
+ ///
+ /// Store current clock values, and calculate difference
+ ///
+ Tsc24_64 = ((UINT64)ReturnCalVal.TSC24_U2 << 32);
+ Tsc24_64 = ((Tsc24_64 |= (ReturnCalVal.TSC24_L2)) - Temp24_64);
+ Tsc100_64 = ((UINT64)ReturnCalVal.TSC100_U2 << 32);
+ Tsc100_64 = ((Tsc100_64 |= (ReturnCalVal.TSC100_L2)) - Temp100_64);
+
+ ///
+ /// Calculate updated conversion factor in fixed point format (U32.3.29)
+ ///
+ Tsc100_64 = (Tsc100_64 << 29);
+ if (Tsc24_64 !=0) {
+ PcalFactor = InternalMathDivRemS64x64 (Tsc100_64, Tsc24_64, (INT64 *) &TscRemainder);
+ }
+
+ ///
+ /// Read the TSC24-to-TSC100 conversion factor currently in use by pCode
+ ///
+ Status = MailboxRead(MAILBOX_TYPE_PCODE, READ_PCODE_CALIBRATED_CMD, &ReturnCalVal.PCalFactor, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed read TSC24-to-TSC100 PCalFactor from pCode currently\n"));
+ break;
+ }
+
+ if (PcalFactor <= 0) {
+ ///
+ /// Set Safe Calibration Value as ReturnCalVal.PCalFactor which is TSC24-to-TSC100 PCalFactor read from pCode by READ_PCODE_CALIBRATED_CMD
+ ///
+ PcalFactor = ReturnCalVal.PCalFactor;
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Warning - SafeCalibrationValue used \n"));
+ }
+ PcalFactor_Lower |= PcalFactor;
+ PcalFactor_Upper |= (PcalFactor >> 32);
+ ///
+ /// Calibrate 24MHz BCLK using the calculated calibration factor value
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, WRITE_CONVERTION_RATIO_CMD, PcalFactor_Lower, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Calibrate 24MHz BCLK using the calculated lower calibration factor value \n"));
+ break;
+ }
+ MailboxS3Write(WRITE_CONVERTION_RATIO_CMD, PcalFactor_Lower);
+
+ ///
+ /// Send command for calibration to turn BCLK on
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, WRITE_PREVENT_BCLKOFF_CMD, 0, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Send command for calibration to turn BCLK on \n"));
+ break;
+ }
+ MailboxS3Write(WRITE_PREVENT_BCLKOFF_CMD, 0);
+
+ ///
+ /// Write measurement interval for pCode calibration of TSC24-to-TSC100 conversion factor
+ ///
+ Status = MailboxWrite(MAILBOX_TYPE_PCODE, WRITE_MEASURE_INTERVAL_CMD, BiosMeasureIntervalValue, &LibStatus);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "24 MHz BCLK Calibration: Failed Send command for calibration to meature interval for pCode calibration \n"));
+ break;
+ }
+ MailboxS3Write(WRITE_MEASURE_INTERVAL_CMD, BiosMeasureIntervalValue);
+ break;
+
+ default:
+ DEBUG ((EFI_D_ERROR, "Unrecognized 24MHz BCLK Calibration Type \n"));
+ Status = EFI_UNSUPPORTED;
+ break;
+ }
+
+ DEBUG ((EFI_D_INFO, "24MHz BCLK calibration completed \n"));
+
+ }
+#endif // ULT_FLAG
+
+ return Status;
+}
+
+/**
+ Enable C-State support as specified by the input flags on a logical processor.
+ Configure BIOS C1 Coordination (SMI coordination)
+ Enable IO redirection coordination
+ Choose proper coordination method
+ Configure extended C-States
+
+ This function must be MP safe.
+
+ @param[in] Buffer Pointer to a ENABLE_CSTATE_PARAMS containing the necessary
+ information to enable C-States
+
+ @retval EFI_SUCCESS Processor C-State support configured successfully.
+**/
+VOID
+EFIAPI
+ApSafeEnableCStates (
+ IN OUT VOID *Buffer
+ )
+{
+ MSR_REGISTER Ia32MiscEnable;
+ MSR_REGISTER PmCfgCtrl;
+ MSR_REGISTER IoCaptAddr;
+ UINT16 C3IoAddress;
+ CPU_STEPPING mCpuSteppingId;
+ CPU_FAMILY mCpuFamilyId;
+ mCpuFamilyId = mPpmGlobalNvsAreaProtocol->Area->Cpuid & CPUID_FULL_FAMILY_MODEL;
+ mCpuSteppingId = mPpmGlobalNvsAreaProtocol->Area->Cpuid & CPUID_FULL_STEPPING;
+ ///
+ /// Extract parameters from the buffer
+ ///
+ C3IoAddress = *((UINT16 *) Buffer);
+ ///
+ /// If C-states are disabled in setup, disable C-states
+ ///
+ if (!(mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C_STATES)) {
+ PmCfgCtrl.Qword = AsmReadMsr64 (MSR_PMG_CST_CONFIG);
+ PmCfgCtrl.Dwords.Low &= ~B_PACKAGE_C_STATE_LIMIT;
+ AsmWriteMsr64 (MSR_PMG_CST_CONFIG, PmCfgCtrl.Qword);
+ return;
+ }
+ ///
+ /// Set C-state package limit to the highest C-state enabled
+ ///
+ PmCfgCtrl.Qword = AsmReadMsr64 (MSR_PMG_CST_CONFIG);
+ if (mCpuPmConfig->PkgCStateLimit != PkgCpuDefault) {
+ PmCfgCtrl.Dwords.Low &= ~B_PACKAGE_C_STATE_LIMIT;
+
+ if ((mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C10) && (mCpuPmConfig->PkgCStateLimit == PkgAuto)) {
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C10;
+ } else if ((mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C9) && (mCpuPmConfig->PkgCStateLimit == PkgAuto)) {
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C9;
+ } else if ((mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C8) && (mCpuPmConfig->PkgCStateLimit == PkgAuto)) {
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C8;
+ } else if ((mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C7S) &&
+ !((mCpuFamilyId == EnumCpuHsw) && (mCpuSteppingId == EnumHswA0) && (mCpuPmConfig->PkgCStateLimit == PkgAuto))) { // When user selects Auto - Disable Package C7s state on Haswell A-step processors
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C7S;
+ } else if ((mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C7)&&
+ !((mCpuFamilyId == EnumCpuHsw) && (mCpuSteppingId == EnumHswA0) && (mCpuPmConfig->PkgCStateLimit == PkgAuto))) { // When user selects Auto - Disable Package C7 state on Haswell A-step processors
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C7;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C6) {
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C6;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C3) {
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C3;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C1) {
+ PmCfgCtrl.Dwords.Low |= V_CSTATE_LIMIT_C1;
+ }
+ if (mCpuPmConfig->PkgCStateLimit < PkgCMax) {
+ PmCfgCtrl.Dwords.Low &= ~B_PACKAGE_C_STATE_LIMIT;
+ PmCfgCtrl.Dwords.Low |= (mCpuPmConfig->PkgCStateLimit & B_PACKAGE_C_STATE_LIMIT);
+ }
+ }
+ ///
+ /// Enable C State IO redirection by default
+ ///
+ PmCfgCtrl.Dwords.Low |= B_IO_MWAIT_REDIRECTION_ENABLE;
+ //
+ // Enable TimedMwait
+ //
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_TIMED_MWAIT) {
+ PmCfgCtrl.Dwords.Low &= (~B_TIMED_MWAIT_ENABLE);
+ PmCfgCtrl.Dwords.Low |= B_TIMED_MWAIT_ENABLE;
+ }
+ ///
+ /// Configure C-state auto-demotion
+ ///
+ PmCfgCtrl.Dwords.Low &= ~(B_C1_AUTO_DEMOTION_ENABLE | B_C3_AUTO_DEMOTION_ENABLE);
+ if (mCpuPmConfig->pFunctionEnables->C3AutoDemotion) {
+ ///
+ /// Enable C6/C7 Auto-demotion to C3
+ ///
+ PmCfgCtrl.Dwords.Low |= B_C3_AUTO_DEMOTION_ENABLE;
+ }
+ if (mCpuPmConfig->pFunctionEnables->C1AutoDemotion) {
+ ///
+ /// Enable C3/C6/C7 Auto-demotion to C1
+ ///
+ PmCfgCtrl.Dwords.Low |= B_C1_AUTO_DEMOTION_ENABLE;
+ }
+ ///
+ /// Configure C-state un-demotion
+ ///
+ PmCfgCtrl.Dwords.Low &= ~(B_C1_AUTO_UNDEMOTION_ENABLE | B_C3_AUTO_UNDEMOTION_ENABLE);
+ if (mCpuPmConfig->pFunctionEnables->C3UnDemotion) {
+ ///
+ /// Enable un-demotion from demoted C3
+ ///
+ PmCfgCtrl.Dwords.Low |= B_C3_AUTO_UNDEMOTION_ENABLE;
+ }
+ if (mCpuPmConfig->pFunctionEnables->C1UnDemotion) {
+ ///
+ /// Enable un-demotion from demoted C1
+ ///
+ PmCfgCtrl.Dwords.Low |= B_C1_AUTO_UNDEMOTION_ENABLE;
+ }
+ ///
+ /// Configure Package C-state Demotion / un-demotion - Supported only on HSW B0 and Above
+ ///
+ if (mCpuSteppingId > EnumHswA0) {
+ PmCfgCtrl.Dwords.Low &= ~(B_PKG_CSTATE_DEMOTION_ENABLE | B_PKG_CSTATE_UNDEMOTION_ENABLE);
+ if (mCpuPmConfig->pFunctionEnables->PkgCStateDemotion) {
+ ///
+ /// Enable Package C-state Demotion
+ ///
+ PmCfgCtrl.Dwords.Low |= B_PKG_CSTATE_DEMOTION_ENABLE;
+ }
+ if (mCpuPmConfig->pFunctionEnables->PkgCStateUnDemotion) {
+ ///
+ /// Enable Package C-state un-demotion
+ ///
+ PmCfgCtrl.Dwords.Low |= B_PKG_CSTATE_UNDEMOTION_ENABLE;
+ }
+ }
+ AsmWriteMsr64 (MSR_PMG_CST_CONFIG, PmCfgCtrl.Qword);
+ ///
+ /// Enable MONITOR/MWAIT support
+ /// (already done on BSP, but must be done on all components.)
+ ///
+ Ia32MiscEnable.Qword = AsmReadMsr64 (MSR_IA32_MISC_ENABLE);
+ Ia32MiscEnable.Qword |= B_MSR_IA32_MISC_ENABLE_MONITOR;
+ AsmWriteMsr64 (MSR_IA32_MISC_ENABLE, Ia32MiscEnable.Qword);
+ ///
+ /// Haswell specific configuration of I/O capture and I/O coordination SMI MSR.
+ /// Configure the base port and range in the MSR to match LVL_X settings in ACPI tables
+ /// Set I/O capture base port and range
+ ///
+ IoCaptAddr.Qword = AsmReadMsr64 (MSR_PMG_IO_CAPTURE_BASE);
+ ///
+ /// Mask off CST range and set the CST range
+ ///
+ IoCaptAddr.Dwords.Low &= ~B_MSR_PMG_CST_RANGE;
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C10) {
+ IoCaptAddr.Dwords.Low |= V_IO_CAPT_LVL7;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C9) {
+ IoCaptAddr.Dwords.Low |= V_IO_CAPT_LVL6;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C8) {
+ IoCaptAddr.Dwords.Low |= V_IO_CAPT_LVL5;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C7) {
+ IoCaptAddr.Dwords.Low |= V_IO_CAPT_LVL4;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C6) {
+ IoCaptAddr.Dwords.Low |= V_IO_CAPT_LVL3;
+ } else if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C3) {
+ IoCaptAddr.Dwords.Low |= V_IO_CAPT_LVL2;
+ }
+ ///
+ /// Set the base CST address
+ ///
+ IoCaptAddr.Dwords.Low &= ~(V_IO_CAPT_LVL2_BASE_ADDR_MASK);
+ IoCaptAddr.Dwords.Low |= C3IoAddress;
+ AsmWriteMsr64 (MSR_PMG_IO_CAPTURE_BASE, IoCaptAddr.Qword);
+ return;
+}
+
+
+//
+// Update ACPI IdleStates tables
+//
+
+/**
+ Configure the FACP for C state support
+**/
+VOID
+ConfigureFadtCStates (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_DESCRIPTION_HEADER *Table;
+ EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer;
+ INTN Index;
+ UINTN Handle;
+ EFI_ACPI_TABLE_VERSION Version;
+
+ ///
+ /// Locate table with matching ID
+ ///
+ Index = 0;
+ do {
+ Status = mAcpiSupport->GetAcpiTable (mAcpiSupport, Index, (VOID **) &Table, &Version, &Handle);
+ if (Status == EFI_NOT_FOUND) {
+ break;
+ }
+ ASSERT_EFI_ERROR (Status);
+ Index++;
+ } while (Table->Signature != EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
+ //
+ // Can't have ACPI without FADT, so safe to assert
+ //
+ ASSERT (Table->Signature == EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
+ FadtPointer = (EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *) Table;
+ //
+ // Verify expected state. Should be initialized to off during build.
+ //
+ ASSERT (FadtPointer->PLvl3Lat >= FADT_C3_LATENCY_DISABLED);
+ ///
+ /// Configure C states
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C3) {
+ ///
+ /// Enable C3 in FADT.
+ ///
+ FadtPointer->PLvl3Lat = FADT_C3_LATENCY;
+ }
+ ///
+ /// Update the table
+ ///
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ Table,
+ Table->Length,
+ &Handle
+ );
+ FreePool (Table);
+
+ return;
+} \ No newline at end of file