/** @file PCH Pcie SMM Driver Entry Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PchInitSmm.h" GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_DEVICE_OVERRIDE *mDevAspmOverride; GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mNumOfDevAspmOverride; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mPolicyRevision; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mPchBusNumber; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mTempRootPortBusNumMin; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mTempRootPortBusNumMax; GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_ROOT_PORT_CONFIG mPcieRootPortConfig[PCH_MAX_PCIE_ROOT_PORTS]; GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mLtrNonSupportRpBitMap; GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mPciePmTrapExecuted = FALSE; GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mAllowNoLtrIccPllShutdownPolicy = FALSE; extern EFI_GUID gPchDeviceTableHobGuid; /** Program Common Clock and ASPM of Downstream Devices @param[in] PortIndex Pcie Root Port Number @param[in] RpDevice Pcie Root Pci Device Number @param[in] RpFunction Pcie Root Pci Function Number @retval EFI_SUCCESS Root port complete successfully @retval EFI_UNSUPPORTED PMC has invalid vendor ID **/ EFI_STATUS PchPcieSmi ( IN UINT8 PortIndex, IN UINT8 RpDevice, IN UINT8 RpFunction ) { UINT8 SecBus; UINT8 SubBus; EFI_HANDLE Handle; UINT32 PwrmBase; UINTN RpBase; BOOLEAN LtrSupported; BOOLEAN DownstreamDevicePresent; Handle = NULL; LtrSupported = TRUE; RpBase = MmPciBase ( mPchBusNumber, (UINT32) RpDevice, (UINT32) RpFunction ); if (MmioRead16 (RpBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) { return EFI_SUCCESS; } // // Check for presence detect state // DownstreamDevicePresent = !!(MmioRead16 (RpBase + R_PCH_PCIE_SLSTS) & B_PCIE_SLSTS_PDS); mLtrNonSupportRpBitMap &= ~(1 << PortIndex); if (DownstreamDevicePresent) { SecBus = MmioRead8 (RpBase + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET); SubBus = MmioRead8 (RpBase + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET); ASSERT (SecBus != 0 && SubBus != 0); PchPcieInitRootPortDownstreamDevices ( DEFAULT_PCI_BUS_NUMBER_PCH, RpDevice, RpFunction, mTempRootPortBusNumMin, mTempRootPortBusNumMax, (BOOLEAN)mPcieRootPortConfig[PortIndex].EnableCpm ); if (!LtrSupported) { mLtrNonSupportRpBitMap |= 1 << PortIndex; } // // Perform Equalization // MmioOr32 (RpBase + R_PCH_PCIE_EX_LCTL3, B_PCIE_EX_LCTL3_PE); MmioOr32 (RpBase + R_PCH_PCIE_LCTL, B_PCIE_LCTL_RL); } // // If not all devices support LTR, set PWRMBASE + 0x3EC [24,16] = 0, 1 // This will disable ICC PLL shutdown // if (!mAllowNoLtrIccPllShutdownPolicy) { PchPwrmBaseGet (&PwrmBase); if (mLtrNonSupportRpBitMap != 0) { MmioAndThenOr32 (PwrmBase + R_PCH_PWRM_CS_SD_CTL2, (UINT32) ~BIT24, BIT16); } else { MmioAnd32 (PwrmBase + R_PCH_PWRM_CS_SD_CTL2, (UINT32) ~(BIT24 | BIT16)); } } return EFI_SUCCESS; } /** PCIE Hotplug SMI call back function for each Root port @param[in] DispatchHandle Handle of this dispatch function @param[in] RpContext Rootport context, which contains RootPort Index, and RootPort PCI BDF. **/ VOID EFIAPI PchPcieSmiRpHandlerFunction ( IN EFI_HANDLE DispatchHandle, IN PCH_PCIE_SMI_RP_CONTEXT *RpContext ) { PchPcieSmi (RpContext->RpIndex, RpContext->DevNum, RpContext->FuncNum); } /** PCIE Link Active State Change Hotplug SMI call back function for all Root ports @param[in] DispatchHandle Handle of this dispatch function @param[in] RpContext Rootport context, which contains RootPort Index, and RootPort PCI BDF. **/ VOID EFIAPI PchPcieLinkActiveStateChange ( IN EFI_HANDLE DispatchHandle, IN PCH_PCIE_SMI_RP_CONTEXT *RpContext ) { return; } /** PCIE Link Equalization Request SMI call back function for all Root ports @param[in] DispatchHandle Handle of this dispatch function @param[in] RpContext Rootport context, which contains RootPort Index, and RootPort PCI BDF. **/ VOID EFIAPI PchPcieLinkEqHandlerFunction ( IN EFI_HANDLE DispatchHandle, IN PCH_PCIE_SMI_RP_CONTEXT *RpContext ) { /// /// From PCI Express specification, the PCIe device can request for Link Equalization. When the /// Link Equalization is requested by the device, an SMI will be generated by PCIe RP when /// enabled and the SMI subroutine would invoke the Software Preset/Coefficient Search /// software to re-equalize the link. /// return; } /** An IoTrap callback to config PCIE power management settings **/ VOID PchPciePmIoTrapSmiCallback ( VOID ) { UINT32 PwrmBase; UINT32 PortIndex; UINTN RpBase; BOOLEAN L1SubstatesSupportedPerPort; UINT8 MaxPciePortNum; UINTN RpDevice; UINTN RpFunction; PCH_GENERATION PchGen; PCH_SERIES PchSeries; PCH_STEPPING PchStep; PchGen = GetPchGeneration (); PchSeries = GetPchSeries (); PchStep = PchStepping (); L1SubstatesSupportedPerPort = FALSE; MaxPciePortNum = GetPchMaxPciePortNum (); // // Step 4a // In case not all devices support LTR, disable ICC PLL shutdown // set PWRMBASE + 0x3EC [24,16] = 0, 1 // (Note this register should be saved and restored during S3 transitions) // if (!mAllowNoLtrIccPllShutdownPolicy) { PchPwrmBaseGet (&PwrmBase); MmioAndThenOr32 (PwrmBase + R_PCH_PWRM_CS_SD_CTL2, (UINT32) ~BIT24, BIT16); } for (PortIndex = 0; PortIndex < MaxPciePortNum; PortIndex++) { GetPchPcieRpDevFun (PortIndex, &RpDevice, &RpFunction); RpBase = MmPciBase (DEFAULT_PCI_BUS_NUMBER_PCH, (UINT32) RpDevice, (UINT32) RpFunction); } // // Step 4b // By this time, LTR support map is known. // If all devices support LTR, allow ICC PLL shutdown // (Note this register should be saved and restored during S3 transitions) // if (!mAllowNoLtrIccPllShutdownPolicy) { if (mLtrNonSupportRpBitMap == 0) { PchPwrmBaseGet (&PwrmBase); MmioAnd32 (PwrmBase + R_PCH_PWRM_CS_SD_CTL2, (UINT32) ~(BIT24 | BIT16)); } } // // Program the remapped root port's ASPM based on endpoint's ASPM // if ((PchGen == SklPch) && (((PchSeries == PchLp) && (PchStep < PchLpC0)) || ((PchSeries == PchH) && (PchStep < PchHD0)))) { return; } } /** An IoTrap callback to config PCIE power management settings @param[in] DispatchHandle - The handle of this callback, obtained when registering @param[in] DispatchContext - Pointer to the EFI_SMM_IO_TRAP_DISPATCH_CALLBACK_CONTEXT **/ VOID EFIAPI PchPcieIoTrapSmiCallback ( IN EFI_HANDLE DispatchHandle, IN EFI_SMM_IO_TRAP_CONTEXT *CallbackContext, IN OUT VOID *CommBuffer, IN OUT UINTN *CommBufferSize ) { if (CallbackContext->WriteData == PciePmTrap) { if (mPciePmTrapExecuted == FALSE) { PchPciePmIoTrapSmiCallback (); mPciePmTrapExecuted = TRUE; } } else { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); } } /** This function clear the Io trap executed flag before enter S3 @param[in] Handle Handle of the callback @param[in] Context The dispatch context @retval EFI_SUCCESS PCH register saved **/ EFI_STATUS EFIAPI PchPcieS3EntryCallBack ( IN EFI_HANDLE Handle, IN CONST VOID *Context OPTIONAL, IN OUT VOID *CommBuffer OPTIONAL, IN OUT UINTN *CommBufferSize OPTIONAL ) { mPciePmTrapExecuted = FALSE; return EFI_SUCCESS; } /** Register PCIE Hotplug SMI dispatch function to handle Hotplug enabling @param[in] ImageHandle The image handle of this module @param[in] SystemTable The EFI System Table @retval EFI_SUCCESS The function completes successfully **/ EFI_STATUS EFIAPI InitializePchPcieSmm ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; UINT8 PortIndex; UINT8 Data8; UINT32 Data32Or; UINT32 Data32And; UINTN RpBase; UINTN RpDevice; UINTN RpFunction; EFI_HANDLE PcieHandle; PCH_PCIE_SMI_DISPATCH_PROTOCOL *PchPcieSmiDispatchProtocol; EFI_HOB_GUID_TYPE* Hob; UINT32 DevTableSize; EFI_HANDLE PchIoTrapHandle; EFI_SMM_IO_TRAP_REGISTER_CONTEXT PchIoTrapContext; EFI_SMM_SX_REGISTER_CONTEXT SxDispatchContext; PCH_PCIE_IOTRAP_PROTOCOL *PchPcieIoTrapProtocol; EFI_HANDLE SxDispatchHandle; UINT8 MaxPciePortNum; DEBUG ((DEBUG_INFO, "InitializePchPcieSmm () Start\n")); MaxPciePortNum = GetPchMaxPciePortNum (); // // Locate Pch Pcie Smi Dispatch Protocol // Status = gSmst->SmmLocateProtocol (&gPchPcieSmiDispatchProtocolGuid, NULL, (VOID**)&PchPcieSmiDispatchProtocol); ASSERT_EFI_ERROR (Status); mPolicyRevision = mSiConfigHob->Header.Revision; mPchBusNumber = DEFAULT_PCI_BUS_NUMBER_PCH; mTempRootPortBusNumMin = mSiConfigHob->TempPciBusMin; mTempRootPortBusNumMax = mSiConfigHob->TempPciBusMax; mAllowNoLtrIccPllShutdownPolicy = (BOOLEAN) mPchConfigHob->PcieRp.AllowNoLtrIccPllShutdown; ASSERT (sizeof mPcieRootPortConfig == sizeof mPchConfigHob->PcieRp.RootPort); CopyMem ( mPcieRootPortConfig, &(mPchConfigHob->PcieRp.RootPort), sizeof (mPcieRootPortConfig) ); mDevAspmOverride = NULL; mNumOfDevAspmOverride = 0; mLtrNonSupportRpBitMap = 0; Hob = GetFirstGuidHob (&gPchDeviceTableHobGuid); if (Hob != NULL) { DevTableSize = GET_GUID_HOB_DATA_SIZE (Hob); ASSERT ((DevTableSize % sizeof (PCH_PCIE_DEVICE_OVERRIDE)) == 0); mNumOfDevAspmOverride = DevTableSize / sizeof (PCH_PCIE_DEVICE_OVERRIDE); DEBUG ((DEBUG_INFO, "Found PcieDeviceTable HOB (%d entries)\n", mNumOfDevAspmOverride)); Status = gSmst->SmmAllocatePool ( EfiRuntimeServicesData, DevTableSize, (VOID **) &mDevAspmOverride ); CopyMem (mDevAspmOverride, GET_GUID_HOB_DATA (Hob), DevTableSize); } // // Throught all PCIE root port function and register the SMI Handler for enabled ports. // for (PortIndex = 0; PortIndex < MaxPciePortNum; PortIndex++) { GetPchPcieRpDevFun (PortIndex, &RpDevice, &RpFunction); RpBase = MmPciBase (DEFAULT_PCI_BUS_NUMBER_PCH, (UINT32) RpDevice, (UINT32) RpFunction); // // Skip the root port function which is not enabled // if (MmioRead32 (RpBase) == 0xFFFFFFFF) { continue; } if (!mPcieRootPortConfig[PortIndex].EnableHotplugSmi) { continue; } // // Register SMI Handlers for Hot Plug and Link Active State Change // Data8 = MmioRead8 (RpBase + R_PCH_PCIE_SLCAP); if (Data8 & B_PCIE_SLCAP_HPC) { PcieHandle = NULL; Status = PchPcieSmiDispatchProtocol->HotPlugRegister ( PchPcieSmiDispatchProtocol, PchPcieSmiRpHandlerFunction, PortIndex, &PcieHandle ); ASSERT_EFI_ERROR (Status); Status = PchPcieSmiDispatchProtocol->LinkActiveRegister ( PchPcieSmiDispatchProtocol, PchPcieLinkActiveStateChange, PortIndex, &PcieHandle ); ASSERT_EFI_ERROR (Status); Data32Or = B_PCH_PCIE_MPC_HPME; Data32And = (UINT32) ~B_PCH_PCIE_MPC_HPME; S3BootScriptSaveMemReadWrite ( S3BootScriptWidthUint32, (UINTN) (RpBase + R_PCH_PCIE_MPC), &Data32Or, /// Data to be ORed &Data32And /// Data to be ANDed ); } // // Register SMI Handler for Link Equalization Request from Gen 3 Devices. // Data8 = MmioRead8 (RpBase + R_PCH_PCIE_LCAP); if ((Data8 & B_PCIE_LCAP_MLS) == V_PCIE_LCAP_MLS_GEN3) { Status = PchPcieSmiDispatchProtocol->LinkEqRegister ( PchPcieSmiDispatchProtocol, PchPcieLinkEqHandlerFunction, PortIndex, &PcieHandle ); ASSERT_EFI_ERROR (Status); } } ASSERT_EFI_ERROR (Status); PchIoTrapContext.Type = WriteTrap; PchIoTrapContext.Length = 4; PchIoTrapContext.Address = 0; Status = mPchIoTrap->Register ( mPchIoTrap, (EFI_SMM_HANDLER_ENTRY_POINT2) PchPcieIoTrapSmiCallback, &PchIoTrapContext, &PchIoTrapHandle ); ASSERT_EFI_ERROR (Status); // // Install the PCH Pcie IoTrap protocol // (gBS->AllocatePool) (EfiBootServicesData, sizeof (PCH_PCIE_IOTRAP_PROTOCOL), (VOID **)&PchPcieIoTrapProtocol); PchPcieIoTrapProtocol->PcieTrapAddress = PchIoTrapContext.Address; Status = gBS->InstallMultipleProtocolInterfaces ( &ImageHandle, &gPchPcieIoTrapProtocolGuid, PchPcieIoTrapProtocol, NULL ); // // Register the callback for S3 entry // SxDispatchContext.Type = SxS3; SxDispatchContext.Phase = SxEntry; Status = mSxDispatch->Register ( mSxDispatch, PchPcieS3EntryCallBack, &SxDispatchContext, &SxDispatchHandle ); ASSERT_EFI_ERROR (Status); DEBUG ((DEBUG_INFO, "InitializePchPcieSmm () End\n")); return EFI_SUCCESS; }