/** @file
|
This is the Common driver that initializes the Intel PCH.
|
|
Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
**/
|
#include <Library/DebugLib.h>
|
#include <Library/IoLib.h>
|
#include <Library/PciSegmentLib.h>
|
#include <Library/TimerLib.h>
|
#include <Library/S3BootScriptLib.h>
|
#include <Library/HobLib.h>
|
|
#include "PchInit.h"
|
#include <PchPolicyCommon.h>
|
#include <Library/SpiCommonLib.h>
|
#include <Library/PmcPrivateLib.h>
|
#include <Library/PchDmiLib.h>
|
#include <Library/SiScheduleResetLib.h>
|
#include <Library/PchCycleDecodingLib.h>
|
#include <Library/PchPcrLib.h>
|
#include <IndustryStandard/Pci30.h>
|
#include <Library/SpiAccessPrivateLib.h>
|
#include <Register/PchRegs.h>
|
#include <Register/PchPcrRegs.h>
|
#include <Register/PchRegsLpc.h>
|
#include <Register/SpiRegs.h>
|
#include <Register/PchRegsPsth.h>
|
#include <Register/PmcRegs.h>
|
#include <Register/HdaRegs.h>
|
#include <Library/GpioCheckConflictLib.h>
|
#include <Library/PchPciBdfLib.h>
|
#include <Library/PchInfoLib.h>
|
|
//
|
// Module variables
|
//
|
GLOBAL_REMOVE_IF_UNREFERENCED PCH_CONFIG_HOB *mPchConfigHob;
|
|
//
|
// EFI_EVENT
|
//
|
GLOBAL_REMOVE_IF_UNREFERENCED EFI_EVENT mHeciEvent;
|
|
/**
|
Common PchInit Module Entry Point
|
**/
|
VOID
|
PchInitEntryPointCommon (
|
VOID
|
)
|
{
|
EFI_PEI_HOB_POINTERS HobPtr;
|
|
DEBUG ((DEBUG_INFO, "PchInitEntryPointCommon() Start\n"));
|
|
//
|
// Get PCH Config HOB.
|
//
|
HobPtr.Guid = GetFirstGuidHob (&gPchConfigHobGuid);
|
ASSERT (HobPtr.Guid != NULL);
|
mPchConfigHob = (PCH_CONFIG_HOB *) GET_GUID_HOB_DATA (HobPtr.Guid);
|
|
DEBUG ((DEBUG_INFO, "PchInitEntryPointCommon() End\n"));
|
|
return;
|
}
|
|
/**
|
Lock SPI register before boot
|
**/
|
VOID
|
LockSpiConfiguration (
|
VOID
|
)
|
{
|
UINTN Index;
|
UINT16 Data16;
|
UINT16 Data16And;
|
UINT16 Data16Or;
|
UINT32 Data32;
|
UINT32 DlockValue;
|
UINT64 PciSpiRegBase;
|
UINT32 PchSpiBar0;
|
UINT32 Timer;
|
|
PciSpiRegBase = SpiPciCfgBase ();
|
|
//
|
// Check for SPI controller presence before programming
|
//
|
if (PciSegmentRead16 (PciSpiRegBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
return;
|
}
|
|
//
|
// Make sure SPI BAR0 has fixed address before writing to boot script.
|
// The same base address is set in PEI and will be used during resume.
|
//
|
PchSpiBar0 = PCH_SPI_BASE_ADDRESS;
|
|
PciSegmentAnd8 (PciSpiRegBase + PCI_COMMAND_OFFSET, (UINT8) ~EFI_PCI_COMMAND_MEMORY_SPACE);
|
PciSegmentWrite32 (PciSpiRegBase + R_SPI_CFG_BAR0, PchSpiBar0);
|
PciSegmentOr8 (PciSpiRegBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
|
|
//
|
// Locking for security reasons only if Extended BIOS Range Decode is supported
|
//
|
if (IsExtendedBiosRangeDecodeSupported ()) {
|
//
|
// Before setting FLOCKDN lock Extended BIOS Range configuration
|
// All configuration of this feature shall be done already at this moment
|
//
|
PciSegmentOr32 (PciSpiRegBase + R_SPI_CFG_BC, BIT28);
|
S3BootScriptSavePciCfgWrite (
|
S3BootScriptWidthUint32,
|
(UINTN) PciSpiRegBase + R_SPI_CFG_BC,
|
1,
|
(VOID *) (UINTN) (PciSpiRegBase + R_SPI_CFG_BC)
|
);
|
}
|
|
//
|
// Program the Flash Protection Range Register based on policy
|
//
|
DlockValue = MmioRead32 (PchSpiBar0 + R_SPI_MEM_DLOCK);
|
for (Index = 0; Index < PCH_FLASH_PROTECTED_RANGES; ++Index) {
|
if ((mPchConfigHob->ProtectRange[Index].WriteProtectionEnable ||
|
mPchConfigHob->ProtectRange[Index].ReadProtectionEnable) != TRUE) {
|
continue;
|
}
|
|
//
|
// Proceed to program the register after ensure it is enabled
|
//
|
Data32 = 0;
|
Data32 |= (mPchConfigHob->ProtectRange[Index].WriteProtectionEnable == TRUE) ? B_SPI_MEM_PRX_WPE : 0;
|
Data32 |= (mPchConfigHob->ProtectRange[Index].ReadProtectionEnable == TRUE) ? B_SPI_MEM_PRX_RPE : 0;
|
Data32 |= ((UINT32) mPchConfigHob->ProtectRange[Index].ProtectedRangeLimit << N_SPI_MEM_PRX_PRL) & B_SPI_MEM_PRX_PRL_MASK;
|
Data32 |= ((UINT32) mPchConfigHob->ProtectRange[Index].ProtectedRangeBase << N_SPI_MEM_PRX_PRB) & B_SPI_MEM_PRX_PRB_MASK;
|
DEBUG ((DEBUG_INFO, "Protected range %d: 0x%08x \n", Index, Data32));
|
|
DlockValue |= (UINT32) (B_SPI_MEM_DLOCK_PR0LOCKDN << Index);
|
MmioWrite32 ((UINTN) (PchSpiBar0 + (R_SPI_MEM_PR0 + (Index * S_SPI_MEM_PRX))), Data32);
|
S3BootScriptSaveMemWrite (
|
S3BootScriptWidthUint32,
|
(UINTN) (PchSpiBar0 + (R_SPI_MEM_PR0 + (Index * S_SPI_MEM_PRX))),
|
1,
|
(VOID *) (UINTN) (PchSpiBar0 + (R_SPI_MEM_PR0 + (Index * S_SPI_MEM_PRX)))
|
);
|
}
|
//
|
// Program DLOCK register
|
//
|
MmioWrite32 ((UINTN) (PchSpiBar0 + R_SPI_MEM_DLOCK), DlockValue);
|
S3BootScriptSaveMemWrite (
|
S3BootScriptWidthUint32,
|
(UINTN) (PchSpiBar0 + R_SPI_MEM_DLOCK),
|
1,
|
(VOID *) (UINTN) (PchSpiBar0 + R_SPI_MEM_DLOCK)
|
);
|
|
///
|
/// PCH BIOS Spec Section 3.6 Flash Security Recommendation
|
/// In PCH SPI controller the BIOS should set the Flash Configuration Lock-Down bit
|
/// (SPI_BAR0 + 04[15]) at end of post. When set to 1, those Flash Program Registers
|
/// that are locked down by this FLOCKDN bit cannot be written.
|
/// Please refer to the EDS for which program registers are impacted.
|
/// Additionally BIOS must program SPI_BAR0 + 0x04 BIT11 (WRSDIS) to disable Write Status in HW sequencing
|
///
|
|
//
|
// Ensure there is no pending SPI trasaction before setting lock bits
|
//
|
Timer = 0;
|
while (MmioRead16 (PchSpiBar0 + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_SCIP) {
|
if (Timer > SPI_WAIT_TIME) {
|
//
|
// SPI transaction is pending too long at this point, exit with error.
|
//
|
DEBUG ((DEBUG_ERROR, "SPI Cycle timeout\n"));
|
ASSERT (FALSE);
|
break;
|
}
|
MicroSecondDelay (SPI_WAIT_PERIOD);
|
Timer += SPI_WAIT_PERIOD;
|
}
|
|
Data16And = B_SPI_MEM_HSFSC_SCIP;
|
Data16 = 0;
|
S3BootScriptSaveMemPoll (
|
S3BootScriptWidthUint16,
|
PchSpiBar0 + R_SPI_MEM_HSFSC,
|
&Data16And,
|
&Data16,
|
SPI_WAIT_PERIOD,
|
SPI_WAIT_TIME / SPI_WAIT_PERIOD
|
);
|
|
//
|
// Clear any outstanding status
|
//
|
Data16Or = B_SPI_MEM_HSFSC_SAF_DLE
|
| B_SPI_MEM_HSFSC_SAF_ERROR
|
| B_SPI_MEM_HSFSC_AEL
|
| B_SPI_MEM_HSFSC_FCERR
|
| B_SPI_MEM_HSFSC_FDONE;
|
Data16And = 0xFFFF;
|
MmioAndThenOr16 (PchSpiBar0 + R_SPI_MEM_HSFSC, Data16And, Data16Or);
|
S3BootScriptSaveMemReadWrite (
|
S3BootScriptWidthUint16,
|
PchSpiBar0 + R_SPI_MEM_HSFSC,
|
&Data16Or,
|
&Data16And
|
);
|
|
//
|
// Set WRSDIS
|
//
|
Data16Or = B_SPI_MEM_HSFSC_WRSDIS;
|
Data16And = 0xFFFF;
|
MmioAndThenOr16 (PchSpiBar0 + R_SPI_MEM_HSFSC, Data16And, Data16Or);
|
S3BootScriptSaveMemReadWrite (
|
S3BootScriptWidthUint16,
|
PchSpiBar0 + R_SPI_MEM_HSFSC,
|
&Data16Or,
|
&Data16And
|
);
|
|
//
|
// Set FLOCKDN
|
//
|
Data16Or = B_SPI_MEM_HSFSC_FLOCKDN;
|
Data16And = 0xFFFF;
|
MmioAndThenOr16 (PchSpiBar0 + R_SPI_MEM_HSFSC, Data16And, Data16Or);
|
S3BootScriptSaveMemReadWrite (
|
S3BootScriptWidthUint16,
|
PchSpiBar0 + R_SPI_MEM_HSFSC,
|
&Data16Or,
|
&Data16And
|
);
|
|
///
|
/// SPI Flash Programming Guide Section 5.5.2 Vendor Component Lock
|
/// It is strongly recommended that BIOS sets the Vendor Component Lock (VCL) bits. VCL applies
|
/// the lock to both VSCC0 and VSCC1 even if VSCC0 is not used. Without the VCL bits set, it is
|
/// possible to make Host/GbE VSCC register(s) changes in that can cause undesired host and
|
/// integrated GbE Serial Flash functionality.
|
///
|
MmioOr32 ((UINTN) (PchSpiBar0 + R_SPI_MEM_SFDP0_VSCC0), B_SPI_MEM_SFDP0_VSCC0_VCL);
|
S3BootScriptSaveMemWrite (
|
S3BootScriptWidthUint32,
|
(UINTN) (PchSpiBar0 + R_SPI_MEM_SFDP0_VSCC0),
|
1,
|
(VOID *) (UINTN) (PchSpiBar0 + R_SPI_MEM_SFDP0_VSCC0)
|
);
|
}
|
|
/**
|
Set HD Audio PME bit
|
**/
|
VOID
|
ConfigureHdAudioPme (
|
VOID
|
)
|
{
|
UINT64 HdaPciBase;
|
|
HdaPciBase = HdaPciCfgBase ();
|
|
if (PciSegmentRead16 (HdaPciBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
return;
|
}
|
|
///
|
/// PME Enable for Audio controller
|
///
|
if (mPchConfigHob->HdAudio.Pme == TRUE) {
|
PciSegmentOr32 (HdaPciBase + R_HDA_CFG_PCS, (UINT32) B_HDA_CFG_PCS_PMEE);
|
}
|
}
|
|
/**
|
Set eSPI BME bit
|
**/
|
VOID
|
ConfigureEspiBme (
|
VOID
|
)
|
{
|
UINT64 EspiPciBase;
|
|
EspiPciBase = EspiPciCfgBase ();
|
|
if (PciSegmentRead16 (EspiPciBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
return;
|
}
|
if ((PciSegmentRead32 (EspiPciBase + R_ESPI_CFG_PCBC) & B_ESPI_CFG_PCBC_ESPI_EN) == 0) {
|
return;
|
}
|
|
//
|
// Refer to PCH BWG.
|
// To enable eSPI bus mastering BIOS must enable BME in eSPI controller
|
// and also set BME bit in the respective slave devices through Configuration
|
// and Capabilities register of each slave using Get_Configuration and Set_Configuration functionality.
|
//
|
// NOTE: The setting is also done in PEI, but might be cleared by PCI bus during PCI enumeration.
|
// Therefore, reeable it after PCI enumeration done.
|
//
|
if (mPchConfigHob->Espi.BmeMasterSlaveEnabled == TRUE) {
|
PciSegmentOr8 (EspiPciBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_BUS_MASTER);
|
}
|
}
|