/** @file This is the driver that initializes the Intel PCH. Copyright (c) 2017, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php. THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "PchInit.h" #include // // Module variables // GLOBAL_REMOVE_IF_UNREFERENCED PCH_NVS_AREA_PROTOCOL mPchNvsAreaProtocol; extern PCH_RST_PCIE_STORAGE_DETECTION mRstPcieStorageDetection[]; /** Retrieve interrupt information about a PCH device from policy @param[in] Device PCI device number @retval PCH_DEVICE_INTERRUPT_CONFIG structure with device's interrupt information **/ PCH_DEVICE_INTERRUPT_CONFIG GetInterruptPolicy ( IN PCH_SERIAL_IO_CONTROLLER Device ) { PCH_DEVICE_INTERRUPT_CONFIG EmptyRecord; UINT8 DevNum; UINT8 FuncNum; UINT8 Index; ZeroMem (&EmptyRecord, sizeof (PCH_DEVICE_INTERRUPT_CONFIG)); DevNum = GetSerialIoDeviceNumber (Device); FuncNum = GetSerialIoFunctionNumber (Device); 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. @retval EFI_SUCCESS The function completed successfully **/ EFI_STATUS UpdateSerialIoAcpiData ( VOID ) { PCH_SERIAL_IO_CONTROLLER Index; for (Index = 0; Index < PCH_SERIALIO_MAX_CONTROLLERS; Index++) { mPchNvsAreaProtocol.Area->SMD[Index] = mPchConfigHob->SerialIo.DevMode[Index]; mPchNvsAreaProtocol.Area->SIR[Index] = (GetInterruptPolicy (Index)).Irq; mPchNvsAreaProtocol.Area->SB0[Index] = FindSerialIoBar (Index, 0); mPchNvsAreaProtocol.Area->SB1[Index] = FindSerialIoBar (Index, 1); } if (GetPchSeries () == PchH) { mPchNvsAreaProtocol.Area->SMD[PchSerialIoIndexI2C4] = PchSerialIoDisabled; mPchNvsAreaProtocol.Area->SMD[PchSerialIoIndexI2C5] = PchSerialIoDisabled; } // // Update GPIO device ACPI variables // mPchNvsAreaProtocol.Area->GPEN = mPchConfigHob->SerialIo.Gpio; mPchNvsAreaProtocol.Area->SGIR = mPchConfigHob->Interrupt.GpioIrqRoute; DEBUG ((DEBUG_INFO, "UpdateSerialIoAcpiData() End\n")); return EFI_SUCCESS; } /** Update NVS Area after RST PCIe Storage Remapping and before Boot @retval EFI_SUCCESS The function completed successfully **/ EFI_STATUS PchUpdateNvsAreaAfterRemapping ( VOID ) { UINTN Index; for (Index = 0; Index < PCH_MAX_RST_PCIE_STORAGE_CR; Index++) { mPchNvsAreaProtocol.Area->RstPcieStorageInterfaceType[Index] = mRstPcieStorageDetection[Index].DeviceInterface; mPchNvsAreaProtocol.Area->RstPcieStoragePmCapPtr[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.PmCapPtr; mPchNvsAreaProtocol.Area->RstPcieStoragePcieCapPtr[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.PcieCapPtr; mPchNvsAreaProtocol.Area->RstPcieStorageL1ssCapPtr[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.L1ssCapPtr; mPchNvsAreaProtocol.Area->RstPcieStorageEpL1ssControl2[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointL1ssControl2; mPchNvsAreaProtocol.Area->RstPcieStorageEpL1ssControl1[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointL1ssControl1; mPchNvsAreaProtocol.Area->RstPcieStorageLtrCapPtr[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.LtrCapPtr; mPchNvsAreaProtocol.Area->RstPcieStorageEpLtrData[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointLtrData; mPchNvsAreaProtocol.Area->RstPcieStorageEpLctlData16[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointLctlData16; mPchNvsAreaProtocol.Area->RstPcieStorageEpDctlData16[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointDctlData16; mPchNvsAreaProtocol.Area->RstPcieStorageEpDctl2Data16[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointDctl2Data16; mPchNvsAreaProtocol.Area->RstPcieStorageRpDctl2Data16[Index] = mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.RootPortDctl2Data16; mPchNvsAreaProtocol.Area->RstPcieStorageUniqueTableBar[Index] = mRstPcieStorageDetection[Index].EndPointUniqueMsixTableBar; mPchNvsAreaProtocol.Area->RstPcieStorageUniqueTableBarValue[Index] = mRstPcieStorageDetection[Index].EndPointUniqueMsixTableBarValue; mPchNvsAreaProtocol.Area->RstPcieStorageUniquePbaBar[Index] = mRstPcieStorageDetection[Index].EndPointUniqueMsixPbaBar; mPchNvsAreaProtocol.Area->RstPcieStorageUniquePbaBarValue[Index] = mRstPcieStorageDetection[Index].EndPointUniqueMsixPbaBarValue; mPchNvsAreaProtocol.Area->RstPcieStorageRootPortNum[Index] = mRstPcieStorageDetection[Index].RootPortNum; } 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 ) { PCH_SERIES PchSeries; DEBUG ((DEBUG_INFO, "PchAcpiOnEndOfDxe() Start\n")); PchSeries = GetPchSeries (); /// /// 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 (); // // Define and update ASL definitions for Cio2 device (only on PCH-LP). // if (PchSeries >= PchLp) { UpdateCio2AcpiData (); } // // Update Pch Nvs Area // PchUpdateNvsArea (); // // Patch PchNvsArea Address // PatchPchNvsAreaAddress (); 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 (); // // 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; } /** PCH Update NvsArea ExitBootServicesFlag on ExitBootService. This event is used if only ExitBootService is used and not in legacy boot @retval None **/ VOID EFIAPI PchUpdateNvsOnExitBootServices ( VOID ) { mPchNvsAreaProtocol.Area->ExitBootServicesFlag = 1; return; } /** 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; PCH_SERIES PchSeries; UINTN Index; UINT32 HpetBaseAdress; GPIO_GROUP GroupToGpeDw0; GPIO_GROUP GroupToGpeDw1; GPIO_GROUP GroupToGpeDw2; GPIO_GROUP Group; UINT32 PadNumber; GPIO_PAD GpioPad; GPIO_PAD_OWN PadOwnVal; GPIO_CONFIG GpioData; UINTN RpDev; UINTN RpFun; UINT32 Data32; UINT16 Data16; Status = EFI_SUCCESS; PchSeries = GetPchSeries (); // // Update ASL PCIE port address according to root port device and function // for (Index = 0; Index < GetPchMaxPciePortNum (); Index++) { Status = GetPchPcieRpDevFun (Index, &RpDev, &RpFun); ASSERT_EFI_ERROR (Status); 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].LtrMaxSnoopLatency; mPchNvsAreaProtocol.Area->PcieLtrMaxNoSnoopLatency[Index] = mPchConfigHob->PcieRp.RootPort[Index].LtrMaxNoSnoopLatency; } // // Update PCHS. // mPchNvsAreaProtocol.Area->PchSeries = (UINT16) PchSeries; // // Update PCHG. // mPchNvsAreaProtocol.Area->PchGeneration = (UINT16) GetPchGeneration (); // // 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 PMC ACPIBASE and PWRMBASE // PchAcpiBaseGet (&Data16); mPchNvsAreaProtocol.Area->PMBS = Data16; PchPwrmBaseGet (&Data32); mPchNvsAreaProtocol.Area->PWRM = Data32; // // Update GPP_X to GPE_DWX mapping. // GpioGetGroupToGpeDwX (&GroupToGpeDw0, &GroupToGpeDw1, &GroupToGpeDw2); // // GPEM is an object for informing how GPIO groups are mapped to GPE. // Mapping for GPP_x is evaluated from (GPEM >> (GroupNumber*2)) & 0x3 // Here GroupNumber does not match xxx_GPIO_GROUP type and is always // 0 based (GPP_A = 0, both for LP and H) // Possible values for each group: // 00b - 2-tier // 01b - 1-tier, GPE_DW0 // 10b - 1-tier, GPE_DW1 // 11b - 1-tier, GPE_DW2 // mPchNvsAreaProtocol.Area->GPEM = (0x1 << (GpioGetGroupIndexFromGroup (GroupToGpeDw0) * 2)) | (0x2 << (GpioGetGroupIndexFromGroup (GroupToGpeDw1) * 2)) | (0x3 << (GpioGetGroupIndexFromGroup (GroupToGpeDw2) * 2)); // // GP2T[GroupIndex] is an object for storing information about GPIO pads which are // enabled for 2-tier GPE event and their interrupt is configured for level // 0b - Gpio Pad which is not 2-tier event or hasn't got level interrupt // 1b - Gpio Pad enabled for 2-tier event and having level interrupt // for (Group = GpioGetLowestGroup (); Group <= GpioGetHighestGroup (); Group++) { Index = GpioGetGroupIndexFromGroup (Group); if (Index >= sizeof (mPchNvsAreaProtocol.Area->GP2T) / sizeof (mPchNvsAreaProtocol.Area->GP2T[0])) { DEBUG ((DEBUG_ERROR, "GpioGetGroupIndexFromGroup (%x) = %x out of GP2T table range\n", Group, GpioGetGroupIndexFromGroup (Group))); continue; } mPchNvsAreaProtocol.Area->GP2T[Index] = 0x0; if ((Group == GroupToGpeDw0) || (Group == GroupToGpeDw1) || (Group == GroupToGpeDw2)) { // // not 2-tier GPE // continue; } for (PadNumber = 0; PadNumber < GpioGetPadPerGroup (Group); PadNumber++) { GpioPad = GpioGetGpioPadFromGroupAndPadNumber (Group, PadNumber); GpioGetPadOwnership (GpioPad, &PadOwnVal); if (PadOwnVal != GpioPadOwnHost) { continue; } GpioGetPadConfig (GpioPad, &GpioData); if (((GpioData.InterruptConfig & B_GPIO_INT_CONFIG_INT_SOURCE_MASK) == GpioIntSci) && ((GpioData.InterruptConfig & B_GPIO_INT_CONFIG_INT_TYPE_MASK) == GpioIntLevel)) { // // This pad is enabled for GPE and has level interrupt // mPchNvsAreaProtocol.Area->GP2T[Index] |= 1 << PadNumber; } } } // // Thermal device in ACPI mode // mPchNvsAreaProtocol.Area->ThermalDeviceAcpiEnabled = mPchConfigHob->Thermal.ThermalDeviceEnable == 2 ? 1 : 0; // // Get Thermal Device interrupt line number // for (Index = 0; Index < mPchConfigHob->Interrupt.NumOfDevIntConfig; Index++) { if ((mPchConfigHob->Interrupt.DevIntConfig[Index].Device == PCI_DEVICE_NUMBER_PCH_THERMAL) && (mPchConfigHob->Interrupt.DevIntConfig[Index].Function == PCI_FUNCTION_NUMBER_PCH_THERMAL)) { mPchNvsAreaProtocol.Area->ThermalDeviceInterruptLine = mPchConfigHob->Interrupt.DevIntConfig[Index].Irq; } } // // SCS Configuration // // Update eMMC HS400 mode enablement // mPchNvsAreaProtocol.Area->EMH4 = (UINT8) mPchConfigHob->Scs.ScsEmmcHs400Enabled; // // Update eMMC Driver Strength // Per eMMC 5.01 JEDEC Specification (JESD84-B50.1, Table 186) // Nominal Impedance - Driver Type Values: // 50 Ohm 0x0 // 33 Ohm 0x1 // 40 Ohm 0x4 // switch (mPchConfigHob->Scs.ScsEmmcHs400DriverStrength) { case DriverStrength33Ohm: mPchNvsAreaProtocol.Area->EMDS = 0x1; break; case DriverStrength40Ohm: mPchNvsAreaProtocol.Area->EMDS = 0x4; break; case DriverStrength50Ohm: default: mPchNvsAreaProtocol.Area->EMDS = 0x0; } // // CPU SKU // mPchNvsAreaProtocol.Area->CpuSku = GetCpuSku (); return Status; } /** Initialize PCH Nvs Area opeartion region. @retval EFI_SUCCESS initialized successfully @retval EFI_NOT_FOUND Nvs Area operation region is not found **/ EFI_STATUS PatchPchNvsAreaAddress ( VOID ) { EFI_STATUS Status; UINT32 Address; UINT16 Length; Status = InitializeAslUpdateLib (); ASSERT_EFI_ERROR (Status); Address = (UINT32) (UINTN) mPchNvsAreaProtocol.Area; Length = (UINT16) sizeof (PCH_NVS_AREA); DEBUG ((DEBUG_INFO, "PatchPchNvsAreaAddress: PCH NVS Address %x Length %x\n", Address, Length)); Status = UpdateNameAslCode (SIGNATURE_32 ('P','N','V','B'), &Address, sizeof (Address)); ASSERT_EFI_ERROR (Status); Status = UpdateNameAslCode (SIGNATURE_32 ('P','N','V','L'), &Length, sizeof (Length)); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }