/** @file
This is the driver that initializes the Intel PCH.
Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include "PchInit.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//
// Module variables
//
GLOBAL_REMOVE_IF_UNREFERENCED PCH_NVS_AREA_PROTOCOL mPchNvsAreaProtocol;
/**
Retrieve interrupt information about a PCH device from policy
@param[in] UartNumber Uart number
@retval PCH_DEVICE_INTERRUPT_CONFIG structure with device's interrupt information
**/
PCH_DEVICE_INTERRUPT_CONFIG
GetUartInterrupt (
IN UINT8 UartNumber
)
{
PCH_DEVICE_INTERRUPT_CONFIG EmptyRecord;
UINT8 DevNum;
UINT8 FuncNum;
UINT8 Index;
ZeroMem (&EmptyRecord, sizeof (PCH_DEVICE_INTERRUPT_CONFIG));
DevNum = SerialIoUartDevNumber (UartNumber);
FuncNum = SerialIoUartFuncNumber (UartNumber);
for (Index = 0; Index < mPchConfigHob->Interrupt.NumOfDevIntConfig; Index++) {
if ((mPchConfigHob->Interrupt.DevIntConfig[Index].Device == DevNum) &&
(mPchConfigHob->Interrupt.DevIntConfig[Index].Function == FuncNum)) {
return mPchConfigHob->Interrupt.DevIntConfig[Index];
}
}
return EmptyRecord;
}
/**
Update ASL definitions for SerialIo devices.
**/
VOID
UpdateSerialIoAcpiData (
VOID
)
{
UINT8 Index;
for (Index = 0; Index < GetPchMaxSerialIoSpiControllersNum (); Index++) {
mPchNvsAreaProtocol.Area->SM0[Index] = mPchConfigHob->SerialIo.SpiDeviceConfig[Index].Mode;
mPchNvsAreaProtocol.Area->SC0[Index] = GetSerialIoSpiPciCfg (Index);
}
for (Index = 0; Index < GetPchMaxSerialIoI2cControllersNum (); Index++) {
mPchNvsAreaProtocol.Area->IM0[Index] = mPchConfigHob->SerialIo.I2cDeviceConfig[Index].Mode;
mPchNvsAreaProtocol.Area->IC0[Index] = GetSerialIoI2cPciCfg (Index);
}
for (Index = 0; Index < GetPchMaxSerialIoUartControllersNum (); Index++) {
mPchNvsAreaProtocol.Area->UM0[Index] = mPchConfigHob->SerialIo.UartDeviceConfig[Index].Mode;
mPchNvsAreaProtocol.Area->UC0[Index] = GetSerialIoUartPciCfg (Index);
mPchNvsAreaProtocol.Area->UD0[Index] = mPchConfigHob->SerialIo.UartDeviceConfig[Index].DmaEnable;
mPchNvsAreaProtocol.Area->UP0[Index] = mPchConfigHob->SerialIo.UartDeviceConfig[Index].PowerGating;
mPchNvsAreaProtocol.Area->UI0[Index] = (GetUartInterrupt (Index)).Irq;
}
}
#if FixedPcdGet8(PcdEmbeddedEnable) == 0x1
/**
Update NVS Area for Timed GPIO devices.
**/
VOID
UpdateTimedGpioSetup (
VOID
)
{
mPchNvsAreaProtocol.Area->EnableTimedGpio0 = (UINT8)mPchConfigHob->Pm.EnableTimedGpio0;
mPchNvsAreaProtocol.Area->EnableTimedGpio1 = (UINT8)mPchConfigHob->Pm.EnableTimedGpio1;
}
#endif
/**
Update NVS Area after RST PCIe Storage Remapping and before Boot
@retval EFI_SUCCESS The function completed successfully
@retval EFI_NOT_FOUND Couldn't fetch RstHob
**/
EFI_STATUS
PchUpdateNvsAreaAfterRemapping (
VOID
)
{
UINTN Index;
VOID *Hob;
PCH_RST_HOB *RstHob;
Hob = GetFirstGuidHob (&gPchRstHobGuid);
if (Hob == NULL) {
return EFI_NOT_FOUND;
}
RstHob = (PCH_RST_HOB *) GET_GUID_HOB_DATA (Hob);
for (Index = 0; Index < PCH_MAX_RST_PCIE_STORAGE_CR; Index++) {
mPchNvsAreaProtocol.Area->RstPcieStorageInterfaceType[Index] = RstHob->RstCrConfiguration[Index].DeviceInterface;
mPchNvsAreaProtocol.Area->RstPcieStoragePmCapPtr[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].PmCapPtr;
mPchNvsAreaProtocol.Area->RstPcieStoragePcieCapPtr[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].PcieCapPtr;
mPchNvsAreaProtocol.Area->RstPcieStorageL1ssCapPtr[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].L1ssCapPtr;
mPchNvsAreaProtocol.Area->RstPcieStorageEpL1ssControl2[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].EndpointL1ssControl2;
mPchNvsAreaProtocol.Area->RstPcieStorageEpL1ssControl1[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].EndpointL1ssControl1;
mPchNvsAreaProtocol.Area->RstPcieStorageLtrCapPtr[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].LtrCapPtr;
mPchNvsAreaProtocol.Area->RstPcieStorageEpLtrData[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].EndpointLtrData;
mPchNvsAreaProtocol.Area->RstPcieStorageEpLctlData16[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].EndpointLctlData16;
mPchNvsAreaProtocol.Area->RstPcieStorageEpDctlData16[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].EndpointDctlData16;
mPchNvsAreaProtocol.Area->RstPcieStorageEpDctl2Data16[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].EndpointDctl2Data16;
mPchNvsAreaProtocol.Area->RstPcieStorageRpDctl2Data16[Index] = RstHob->SavedRemapedDeviceConfigSpace[Index].RootPortDctl2Data16;
mPchNvsAreaProtocol.Area->RstPcieStorageUniqueTableBar[Index] = RstHob->RstCrConfiguration[Index].EndPointUniqueMsixTableBar;
mPchNvsAreaProtocol.Area->RstPcieStorageUniqueTableBarValue[Index] = RstHob->RstCrConfiguration[Index].EndPointUniqueMsixTableBarValue;
mPchNvsAreaProtocol.Area->RstPcieStorageUniquePbaBar[Index] = RstHob->RstCrConfiguration[Index].EndPointUniqueMsixPbaBar;
mPchNvsAreaProtocol.Area->RstPcieStorageUniquePbaBarValue[Index] = RstHob->RstCrConfiguration[Index].EndPointUniqueMsixPbaBarValue;
mPchNvsAreaProtocol.Area->RstPcieStorageRootPortNum[Index] = RstHob->RstCrConfiguration[Index].RootPortNum;
}
return EFI_SUCCESS;
}
/**
Update the Hybrid storage location NVS Area if Hybrid Storage device is present
**/
EFI_STATUS
UpdateHybridStorageLocation (
VOID
)
{
VOID *Hob;
PCH_HYBRIDSTORAGE_HOB *HybridStorageHob;
Hob = GetFirstGuidHob (&gHybridStorageHobGuid);
if (Hob == NULL) {
return EFI_NOT_FOUND;
}
HybridStorageHob = (PCH_HYBRIDSTORAGE_HOB *) GET_GUID_HOB_DATA (Hob);
mPchNvsAreaProtocol.Area->HybridStorageLocation = HybridStorageHob->HybridStorageLocation;
return EFI_SUCCESS;
}
/**
PCH ACPI initialization before Boot Sript Table is closed
It update ACPI table and ACPI NVS area.
@param[in] Event A pointer to the Event that triggered the callback.
@param[in] Context A pointer to private data registered with the callback function.
**/
VOID
EFIAPI
PchAcpiOnEndOfDxe (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DEBUG ((DEBUG_INFO, "PchAcpiOnEndOfDxe() Start\n"));
///
/// Closed the event to avoid call twice when launch shell
///
gBS->CloseEvent (Event);
//
// Init HDA Audio ACPI tables
//
PchHdAudioAcpiInit ();
//
// Update ASL definitions for SerialIo devices.
//
UpdateSerialIoAcpiData ();
UpdateCnviAcpiData ();
#if FixedPcdGet8(PcdEmbeddedEnable) == 0x1
UpdateTimedGpioSetup();
#endif
//
// Update Pch Nvs Area
//
PchUpdateNvsArea ();
DEBUG ((DEBUG_INFO, "PchAcpiOnEndOfDxe() End\n"));
return;
}
/**
Initialize Pch acpi
@param[in] ImageHandle Handle for the image of this driver
@retval EFI_SUCCESS The function completed successfully
@retval EFI_OUT_OF_RESOURCES Do not have enough resources to initialize the driver
**/
EFI_STATUS
EFIAPI
PchAcpiInit (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_EVENT EndOfDxeEvent;
DEBUG ((DEBUG_INFO, "Install PCH NVS protocol\n"));
Status = (gBS->AllocatePool) (EfiACPIMemoryNVS, sizeof (PCH_NVS_AREA), (VOID **) &mPchNvsAreaProtocol.Area);
ASSERT_EFI_ERROR (Status);
ZeroMem ((VOID *) mPchNvsAreaProtocol.Area, sizeof (PCH_NVS_AREA));
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gPchNvsAreaProtocolGuid,
&mPchNvsAreaProtocol,
NULL
);
ASSERT_EFI_ERROR (Status);
///
/// Update the NVS Area after RST PCIe Storage Remapping
///
PchUpdateNvsAreaAfterRemapping ();
UpdateHybridStorageLocation ();
//
// Register an end of DXE event for PCH ACPI to do tasks before invoking any UEFI drivers,
// applications, or connecting consoles,...
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PchAcpiOnEndOfDxe,
NULL,
&gEfiEndOfDxeEventGroupGuid,
&EndOfDxeEvent
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
Update NVS area for PCIe root ports.
**/
STATIC
VOID
PcieRpUpdateNvsArea (
VOID
)
{
UINT32 Index;
for (Index = 0; Index < PCH_MAX_PCIE_CLOCKS; Index++) {
mPchNvsAreaProtocol.Area->ClockToRootPortMap[Index] = mPchConfigHob->PcieRp.PcieClock[Index].Usage;
}
}
/**
Update ASL object before Boot
@retval EFI_STATUS
@retval EFI_NOT_READY The Acpi protocols are not ready.
**/
EFI_STATUS
PchUpdateNvsArea (
VOID
)
{
EFI_STATUS Status;
UINTN Index;
UINT32 HpetBaseAdress;
GPIO_GROUP GroupToGpeDwX[3];
UINT32 GroupDw[3];
UINTN RpDev;
UINTN RpFun;
UINT32 Data32;
PCH_POLICY_PROTOCOL *PchPolicy;
GPIO_DXE_CONFIG *GpioDxeConfig;
UINT64 XdciPciBase;
UINT64 XdciBar;
UINT16 PciMemConfig;
UINT16 TcoBase;
UINT8 ClearXdciBar = FALSE;
///
/// Get PCH Policy Protocol
///
Status = gBS->LocateProtocol (&gPchPolicyProtocolGuid, NULL, (VOID **)&PchPolicy);
ASSERT_EFI_ERROR (Status);
///
/// Get GPIO DXE Config Block
///
Status = GetConfigBlock ((VOID *)PchPolicy, &gGpioDxeConfigGuid, (VOID *)&GpioDxeConfig);
ASSERT_EFI_ERROR (Status);
//
// Update ASL PCIE port address according to root port device and function
//
for (Index = 0; Index < GetPchMaxPciePortNum (); Index++) {
RpDev = PchPcieRpDevNumber (Index);
RpFun = PchPcieRpFuncNumber (Index);
Data32 = ((UINT8) RpDev << 16) | (UINT8) RpFun;
mPchNvsAreaProtocol.Area->RpAddress[Index] = Data32;
//
// Update Maximum Snoop Latency and Maximum No-Snoop Latency values for PCIE
//
mPchNvsAreaProtocol.Area->PcieLtrMaxSnoopLatency[Index] = mPchConfigHob->PcieRp.RootPort[Index].PcieRpCommonConfig.PcieRpLtrConfig.LtrMaxSnoopLatency;
mPchNvsAreaProtocol.Area->PcieLtrMaxNoSnoopLatency[Index] = mPchConfigHob->PcieRp.RootPort[Index].PcieRpCommonConfig.PcieRpLtrConfig.LtrMaxNoSnoopLatency;
}
//
// Update PCHS.
//
mPchNvsAreaProtocol.Area->PchSeries = PchSeries ();
//
// Update PCHG.
//
mPchNvsAreaProtocol.Area->PchGeneration = (UINT16) PchGeneration ();
//
// Update PSTP.
//
mPchNvsAreaProtocol.Area->PchStepping = (UINT16) PchStepping ();
//
// Update HPET base address.
//
PchHpetBaseGet (&HpetBaseAdress);
mPchNvsAreaProtocol.Area->HPTE = TRUE; // @todo remove the NVS, since it's always enabled.
mPchNvsAreaProtocol.Area->HPTB = HpetBaseAdress;
//
// Update SBREG_BAR.
//
mPchNvsAreaProtocol.Area->SBRG = PCH_PCR_BASE_ADDRESS;
//
// Update base address
//
mPchNvsAreaProtocol.Area->PMBS = PmcGetAcpiBase ();
mPchNvsAreaProtocol.Area->PWRM = PmcGetPwrmBase ();
PchTcoBaseGet (&TcoBase);
mPchNvsAreaProtocol.Area->TcoBase = TcoBase;
//
// Update PCH PID info
//
mPchNvsAreaProtocol.Area->IclkPid = PchPcrGetPid (PchIpIclk);
//
// Update GPIO device ACPI variables
//
mPchNvsAreaProtocol.Area->SGIR = mPchConfigHob->Interrupt.GpioIrqRoute;
mPchNvsAreaProtocol.Area->GPHD = (UINT8)GpioDxeConfig->HideGpioAcpiDevice;
//
// Update GPP_X to GPE_DWX mapping.
//
GpioGetGroupDwToGpeDwX (
&GroupToGpeDwX[0], &GroupDw[0],
&GroupToGpeDwX[1], &GroupDw[1],
&GroupToGpeDwX[2], &GroupDw[2]
);
//
// GEI0/1/2 and GED0/1/2 are objects for informing how GPIO groups are mapped to GPE0.
// If Group is mapped to 1-Tier GPE information is also stored on what Group DW
// is mapped to GPE_DWx. Because GPE_DWx register is 32 bits large if groups have more than
// 32 pads only part of it can be mapped.
//
// GEIx - GroupIndex mapped to GPE0_DWx
// GEDx - DoubleWorld part of Group: 0 - pins 31-0, 1 - pins 63-32, ...
//
mPchNvsAreaProtocol.Area->GEI0 = (UINT8) GpioGetGroupIndexFromGroup (GroupToGpeDwX[0]);
mPchNvsAreaProtocol.Area->GEI1 = (UINT8) GpioGetGroupIndexFromGroup (GroupToGpeDwX[1]);
mPchNvsAreaProtocol.Area->GEI2 = (UINT8) GpioGetGroupIndexFromGroup (GroupToGpeDwX[2]);
mPchNvsAreaProtocol.Area->GED0 = (UINT8) GroupDw[0];
mPchNvsAreaProtocol.Area->GED1 = (UINT8) GroupDw[1];
mPchNvsAreaProtocol.Area->GED2 = (UINT8) GroupDw[2];
//
// SCS Configuration
//
PcieRpUpdateNvsArea ();
//
// SATA configuration.
//
mPchNvsAreaProtocol.Area->SataPortPresence = GetSataPortPresentStatus (0);
DEBUG ((DEBUG_INFO, "SataPortPresence: 0x%x\n", mPchNvsAreaProtocol.Area->SataPortPresence));
//
// CPU SKU
//
mPchNvsAreaProtocol.Area->CpuSku = 0;
mPchNvsAreaProtocol.Area->PsOnEnable = (UINT8)mPchConfigHob->Pm.PsOnEnable;
for (Index = 0; Index < GetPchMaxPciePortNum (); Index++) {
mPchNvsAreaProtocol.Area->LtrEnable[Index] = (UINT8)mPchConfigHob->PcieRp.RootPort[Index].PcieRpCommonConfig.LtrEnable;
}
mPchNvsAreaProtocol.Area->GBES = IsGbePresent ();
//
// Update PCH Trace Hub Mode
//
mPchNvsAreaProtocol.Area->PchTraceHubMode = (UINT8) mPchConfigHob->PchTraceHub.PchTraceHubMode;
//
// Saving GCTL value into PCH NVS area
//
XdciPciBase = PchXdciPciCfgBase ();
//
// Determine Base address for Base address register (Offset 0x10)
//
if (PciSegmentRead32 (XdciPciBase) != 0xFFFFFFFF) {
XdciBar = PciSegmentRead32 (XdciPciBase + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFFFF0;
if ((PciSegmentRead32 (XdciPciBase + PCI_BASE_ADDRESSREG_OFFSET) & B_PCI_BAR_MEMORY_TYPE_MASK) == B_PCI_BAR_MEMORY_TYPE_64) {
XdciBar += (UINT64)PciSegmentRead32 (XdciPciBase + (PCI_BASE_ADDRESSREG_OFFSET + 4)) << 32;
}
if (XdciBar == 0x0) {
ClearXdciBar = TRUE;
PciSegmentWrite32 ((XdciPciBase + PCI_BASE_ADDRESSREG_OFFSET), PcdGet32 (PcdSiliconInitTempMemBaseAddr));
XdciBar = PciSegmentRead32 (XdciPciBase + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFFFF0;
if ((PciSegmentRead32 (XdciPciBase + PCI_BASE_ADDRESSREG_OFFSET) & B_PCI_BAR_MEMORY_TYPE_MASK) == B_PCI_BAR_MEMORY_TYPE_64) {
XdciBar += (UINT64)PciSegmentRead32 (XdciPciBase + (PCI_BASE_ADDRESSREG_OFFSET + 4)) << 32;
}
}
//
// Enable Pci Memconfig to read the correct value for GCTL register
//
PciMemConfig = PciSegmentRead16 (XdciPciBase + PCI_COMMAND_OFFSET);
PciSegmentWrite16 (XdciPciBase + PCI_COMMAND_OFFSET, PciMemConfig | (EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE));
mPchNvsAreaProtocol.Area->PchxDCIPwrDnScale = MmioRead32(XdciBar + R_XDCI_MEM_GCTL);
DEBUG ((DEBUG_INFO, "PchxDCIPwrDnScale 0x%x\n", (UINT64)mPchNvsAreaProtocol.Area->PchxDCIPwrDnScale));
//
// Disable Pci Memconfig & clear Base address
//
PciSegmentWrite16(XdciPciBase + PCI_COMMAND_OFFSET, PciMemConfig);
if (ClearXdciBar == TRUE) {
PciSegmentWrite32 ((XdciPciBase + PCI_BASE_ADDRESSREG_OFFSET), 0x0);
PciSegmentWrite32 ((XdciPciBase + (PCI_BASE_ADDRESSREG_OFFSET + 4)), 0x0);
}
}
return Status;
}