From 646b243c0e3ef49b98071ca2c3fec15299b4d72f Mon Sep 17 00:00:00 2001 From: Jiewen Yao Date: Mon, 19 Jun 2017 10:55:06 +0800 Subject: Add KabylakeSiliconPkg reviewed-by: Jiewen Yao reviewed-by: Michael A Kubacki reviewed-by: Amy Chan reviewed-by: Rangasai V Chaganty reviewed-by: Chasel Chiu Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiewen Yao Signed-off-by: Chasel Chiu --- .../KabylakeSiliconPkg/Pch/PchInit/Dxe/PchAcpi.c | 465 +++++ .../Pch/PchInit/Dxe/PchCio2Acpi.c | 57 + .../Pch/PchInit/Dxe/PchHdaAcpi.c | 247 +++ .../KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.c | 504 ++++++ .../KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.h | 320 ++++ .../Pch/PchInit/Dxe/PchInitDxe.c | 699 ++++++++ .../Pch/PchInit/Dxe/PchInitDxe.inf | 101 ++ .../Pch/PchInit/Dxe/PchInitDxeFsp.inf | 80 + .../Pch/PchInit/Dxe/PchInitFsp.c | 169 ++ .../Pch/PchInit/Dxe/PchRstPcieStorage.c | 286 +++ .../KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSata.c | 125 ++ .../KabylakeSiliconPkg/Pch/PchInit/Dxe/PchScs.c | 1869 ++++++++++++++++++++ .../Pch/PchInit/Dxe/PchSerialIo.c | 49 + .../Pch/PchInit/Dxe/PchSerialIoDxe.c | 112 ++ 14 files changed, 5083 insertions(+) create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchAcpi.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchCio2Acpi.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchHdaAcpi.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.h create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.inf create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxeFsp.inf create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitFsp.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchRstPcieStorage.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSata.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchScs.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIo.c create mode 100644 Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIoDxe.c (limited to 'Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe') diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchAcpi.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchAcpi.c new file mode 100644 index 0000000000..789b61170d --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchAcpi.c @@ -0,0 +1,465 @@ +/** @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; +} + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchCio2Acpi.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchCio2Acpi.c new file mode 100644 index 0000000000..f0abea1323 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchCio2Acpi.c @@ -0,0 +1,57 @@ +/** @file + Initializes PCH CIO2 device ACPI data. + +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 + +/** + Update ASL definitions for CIO2 device. + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +UpdateCio2AcpiData ( + VOID + ) +{ + UINT32 Index; + PCH_STEPPING PchStep; + + DEBUG ((DEBUG_INFO, "UpdateCio2AcpiData() Start\n")); + PchStep = PchStepping (); + + //if CIO2 is enabled as ACPI device then update its ACPI data + if (PchStep >= PchLpC0) { + mPchNvsAreaProtocol.Area->Cio2EnabledAsAcpiDevice = 0; + DEBUG ((DEBUG_INFO, "UpdateCio2AcpiData() Cio2 has not been enabled as ACPI device\n")); + } else { + mPchNvsAreaProtocol.Area->Cio2EnabledAsAcpiDevice = 0; + if (mPchConfigHob->Cio2.DeviceEnable == 1) { + mPchNvsAreaProtocol.Area->Cio2EnabledAsAcpiDevice = 1; + for (Index = 0; Index < mPchConfigHob->Interrupt.NumOfDevIntConfig; Index++) { + if ((mPchConfigHob->Interrupt.DevIntConfig[Index].Device == PCI_DEVICE_NUMBER_PCH_CIO2) && + (mPchConfigHob->Interrupt.DevIntConfig[Index].Function == PCI_FUNCTION_NUMBER_PCH_CIO2)) { + mPchNvsAreaProtocol.Area->Cio2IrqNumber = mPchConfigHob->Interrupt.DevIntConfig[Index].Irq; + DEBUG ((DEBUG_INFO, "UpdateCio2AcpiData() Cio2 has been enabled as ACPI device. Irq number = 0x%x\n", mPchConfigHob->Interrupt.DevIntConfig[Index].Irq)); + break; + } + } + } + } + + DEBUG ((DEBUG_INFO, "UpdateCio2AcpiData() End\n")); + + return EFI_SUCCESS; +} + + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchHdaAcpi.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchHdaAcpi.c new file mode 100644 index 0000000000..732dc8905d --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchHdaAcpi.c @@ -0,0 +1,247 @@ +/** @file + Initializes the PCH HD Audio ACPI Tables. + +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 +#include +#include +#include + +PCH_HDA_NHLT_ENDPOINTS mPchHdaNhltEndpoints[HdaEndpointMax] = +{ + {HdaDmicX1, B_HDA_DMIC_1CH_48KHZ_16BIT_FORMAT, FALSE}, + {HdaDmicX2, (B_HDA_DMIC_2CH_48KHZ_16BIT_FORMAT | B_HDA_DMIC_2CH_48KHZ_32BIT_FORMAT), FALSE}, + {HdaDmicX4, (B_HDA_DMIC_4CH_48KHZ_16BIT_FORMAT | B_HDA_DMIC_4CH_48KHZ_32BIT_FORMAT), FALSE}, + {HdaBtRender, (B_HDA_BT_NARROWBAND_FORMAT | B_HDA_BT_WIDEBAND_FORMAT | B_HDA_BT_A2DP_FORMAT), FALSE}, + {HdaBtCapture, (B_HDA_BT_NARROWBAND_FORMAT | B_HDA_BT_WIDEBAND_FORMAT), FALSE}, + {HdaI2sRender1, (B_HDA_I2S_RTK298_RENDER_4CH_48KHZ_24BIT_FORMAT), FALSE}, + {HdaI2sRender2, (B_HDA_I2S_RTK298_RENDER_4CH_48KHZ_24BIT_FORMAT), FALSE}, + {HdaI2sCapture, (B_HDA_I2S_RTK298_CAPTURE_4CH_48KHZ_24BIT_FORMAT), FALSE} +}; + +/** + Retrieves address of NHLT table from XSDT/RSDT. + + @retval NHLT_ACPI_TABLE* Pointer to NHLT table if found + @retval NULL NHLT could not be found +**/ +NHLT_ACPI_TABLE * +LocateNhltAcpiTable ( + VOID + ) +{ + EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp; + EFI_ACPI_DESCRIPTION_HEADER *Xsdt; + NHLT_ACPI_TABLE *Nhlt; + UINTN Index; + UINT64 Data64; + EFI_STATUS Status; + Rsdp = NULL; + Xsdt = NULL; + Nhlt = NULL; + + /// + /// Find the AcpiSupport protocol returns RSDP (or RSD PTR) address. + /// + DEBUG ((DEBUG_INFO, "LocateNhltAcpiTable() Start\n")); + + Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID *) &Rsdp); + if (EFI_ERROR (Status) || (Rsdp == NULL)) { + DEBUG ((DEBUG_ERROR, "EFI_ERROR or Rsdp == NULL\n")); + return NULL; + } + + Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->XsdtAddress; + if (Xsdt == NULL || Xsdt->Signature != EFI_ACPI_5_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { + // If XSDT has not been found, check RSDT + Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress; + if (Xsdt == NULL || Xsdt->Signature != EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { + DEBUG ((DEBUG_ERROR, "XSDT/RSDT == NULL or wrong signature\n")); + return NULL; + } + } + + for (Index = sizeof (EFI_ACPI_DESCRIPTION_HEADER); Index < Xsdt->Length; Index = Index + sizeof (UINT64)) { + Data64 = *(UINT64 *) ((UINT8 *) Xsdt + Index); + Nhlt = (NHLT_ACPI_TABLE *) (UINTN) Data64; + if (Nhlt->Header.Signature == NHLT_ACPI_TABLE_SIGNATURE) { + break; + } + } + + if (Nhlt == NULL || Nhlt->Header.Signature != NHLT_ACPI_TABLE_SIGNATURE) { + DEBUG ((DEBUG_ERROR, "Nhlt == NULL or wrong signature\n")); + return NULL; + } + + DEBUG ((DEBUG_INFO, "Found NhltTable, Address = 0x%016x\n", Nhlt)); + + return Nhlt; +} + +/** + Constructs and installs NHLT table. + + @retval EFI_SUCCESS ACPI Table installed successfully + @retval EFI_UNSUPPORTED ACPI Table protocol not found +**/ +EFI_STATUS +PublishNhltAcpiTable ( + VOID + ) +{ + UINTN AcpiTableKey; + EFI_ACPI_TABLE_PROTOCOL *AcpiTable; + NHLT_ACPI_TABLE *NhltTable; + UINT32 TableLength; + EFI_STATUS Status; + + AcpiTable = NULL; + NhltTable = NULL; + AcpiTableKey = 0; + + DEBUG ((DEBUG_INFO, "PublishNhltAcpiTable() Start\n")); + + // + // Locate ACPI support protocol + // + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable); + if ( EFI_ERROR (Status) || AcpiTable == NULL) { + return EFI_UNSUPPORTED; + } + + NhltConstructor (mPchHdaNhltEndpoints, &NhltTable, &TableLength); + NhltAcpiHeaderConstructor (NhltTable, TableLength); + + Status = AcpiTable->InstallAcpiTable (AcpiTable, NhltTable, NhltTable->Header.Length, &AcpiTableKey); + + DEBUG ((DEBUG_INFO, "PublishNhltAcpiTable() End\n")); + return Status; +} + +/** + Sets NVS ACPI variables for _DSM accordingly to policy. + + @param[in] NhltAcpiTableAddress + @param[in] NhltAcpiTableLength + @param[in] DspFeatureMask +**/ +VOID +UpdateHdaAcpiData ( + IN UINT64 NhltAcpiTableAddress, + IN UINT32 NhltAcpiTableLength, + IN UINT32 DspFeatureMask + ) +{ + DEBUG ((DEBUG_INFO, "UpdateHdaAcpiData():\n NHLT Address = 0x%016x, Length = 0x%08x\n", NhltAcpiTableAddress, NhltAcpiTableLength)); + DEBUG ((DEBUG_INFO, " FeatureMask = 0x%08x\n", DspFeatureMask)); + + mPchNvsAreaProtocol.Area->NHLA = NhltAcpiTableAddress; + mPchNvsAreaProtocol.Area->NHLL = NhltAcpiTableLength; + mPchNvsAreaProtocol.Area->ADFM = DspFeatureMask; +} + +/** + Initialize and publish NHLT (Non-HDA Link Table), update NVS variables. + + @param[in] *HdaConfig + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +SetHdaAcpiTable ( + IN CONST HDAUDIO_HOB *HdaConfig + ) +{ + NHLT_ACPI_TABLE *NhltTable; + EFI_STATUS Status; + NhltTable = NULL; + + + switch (HdaConfig->DspEndpointDmic) { + case PchHdaDmic1chArray: + mPchHdaNhltEndpoints[HdaDmicX1].Enable = TRUE; + break; + case PchHdaDmic2chArray: + mPchHdaNhltEndpoints[HdaDmicX2].Enable = TRUE; + break; + case PchHdaDmic4chArray: + mPchHdaNhltEndpoints[HdaDmicX4].Enable = TRUE; + break; + case PchHdaDmicDisabled: + default: + mPchHdaNhltEndpoints[HdaDmicX2].Enable = FALSE; + mPchHdaNhltEndpoints[HdaDmicX4].Enable = FALSE; + } + + if (HdaConfig->DspEndpointBluetooth) { + mPchHdaNhltEndpoints[HdaBtRender].Enable = TRUE; + mPchHdaNhltEndpoints[HdaBtCapture].Enable = TRUE; + } + + if (HdaConfig->DspEndpointI2s) { + mPchHdaNhltEndpoints[HdaI2sRender1].Enable = TRUE; + mPchHdaNhltEndpoints[HdaI2sRender2].Enable = TRUE; + mPchHdaNhltEndpoints[HdaI2sCapture].Enable = TRUE; + } + + Status = PublishNhltAcpiTable (); + NhltTable = LocateNhltAcpiTable (); + if (NhltTable == NULL) { + return EFI_LOAD_ERROR; + } + + UpdateHdaAcpiData ((UINT64) (UINTN) NhltTable, (UINT32) (NhltTable->Header.Length), HdaConfig->DspFeatureMask); + + DEBUG_CODE ( NhltAcpiTableDump (NhltTable); ); + return Status; +} + +/** + Initialize Intel High Definition Audio ACPI Tables + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_LOAD_ERROR ACPI table cannot be installed + @retval EFI_UNSUPPORTED ACPI table not set because DSP is disabled +**/ +EFI_STATUS +PchHdAudioAcpiInit ( + VOID + ) +{ + EFI_STATUS Status; + CONST HDAUDIO_HOB *HdaConfig; + UINTN HdaPciBase; + + DEBUG ((DEBUG_INFO, "PchHdAudioAcpiInit() Start\n")); + + HdaConfig = &mPchConfigHob->HdAudio; + + HdaPciBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_HDA, + PCI_FUNCTION_NUMBER_PCH_HDA + ); + + if ((MmioRead16 (HdaPciBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) || (HdaConfig->DspEnable == FALSE)) { + // Do not set ACPI tables if HDAudio is Function disabled or DSP is disabled + DEBUG ((DEBUG_INFO, "AudioDSP: Non-HDAudio ACPI Table (NHLT) not set!\n")); + return EFI_UNSUPPORTED; + } + + Status = SetHdaAcpiTable (HdaConfig); + + DEBUG ((DEBUG_INFO, "PchHdAudioAcpiInit() End - Status = %r\n", Status)); + return Status; +} + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.c new file mode 100644 index 0000000000..0428479b50 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.c @@ -0,0 +1,504 @@ +/** @file + This is the Common 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" + +// +// Module variables +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_CONFIG_HOB *mPchConfigHob; +GLOBAL_REMOVE_IF_UNREFERENCED SI_CONFIG_HOB *mSiConfigHob; + +/** + 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); + + // + // Get Silicon Config data HOB + // + HobPtr.Guid = GetFirstGuidHob (&gSiConfigHobGuid); + ASSERT (HobPtr.Guid != NULL); + mSiConfigHob = (SI_CONFIG_HOB *) GET_GUID_HOB_DATA (HobPtr.Guid); + + DEBUG ((DEBUG_INFO, "PchInitEntryPointCommon() End\n")); + + return; +} + +/** + Lock USB registers before boot + + @param[in] PchConfigHob The PCH Config HOB +**/ +VOID +LockXhciConfiguration ( + IN CONST PCH_CONFIG_HOB *PchConfigHob + ) +{ + UINTN XhciPciMmBase; + UINT32 XhccCfg; + + DEBUG ((DEBUG_INFO, "LockXhciConfiguration () - Start\n")); + + XhciPciMmBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_XHCI, + PCI_FUNCTION_NUMBER_PCH_XHCI + ); + + /// + /// PCH BIOS Spec Section 13.2.4 Locking xHCI Register Settings + /// PCH BIOS Spec Locking xHCI Register settings + /// After xHCI is initialized, BIOS should lock the xHCI configuration registers to RO. + /// This prevent any unintended changes. There is also a lockdown feature for OverCurrent + /// registers. BIOS should set these bits to lock down the settings prior to end of POST. + /// 1. Set Access Control bit at XHCI PCI offset 40h[31] to 1b to lock xHCI register settings. + /// 2. Set OC Configuration Done bit at XHCI PCI offset 44h[31] to lock overcurrent mappings from + /// further changes. + /// + MmioOr32 (XhciPciMmBase + R_PCH_XHCI_XHCC2, (UINT32) (BIT31)); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (XhciPciMmBase + R_PCH_XHCI_XHCC2), + 1, + (VOID *) (UINTN) (XhciPciMmBase + R_PCH_XHCI_XHCC2) + ); + + /// + /// PCH BIOS Spec xHCI controller setup + /// Note: + /// XHCI PCI offset 40h is write once register. + /// Unsupported Request Detected bit is write clear + /// + XhccCfg = MmioRead32 (XhciPciMmBase + R_PCH_XHCI_XHCC1); + XhccCfg &= (UINT32) ~(B_PCH_XHCI_XHCC1_URD); + XhccCfg |= (UINT32) (B_PCH_XHCI_XHCC1_ACCTRL); + MmioWrite32 (XhciPciMmBase + R_PCH_XHCI_XHCC1, XhccCfg); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (XhciPciMmBase + R_PCH_XHCI_XHCC1), + 1, + (VOID *) (UINTN) (XhciPciMmBase + R_PCH_XHCI_XHCC1) + ); + + DEBUG ((DEBUG_INFO, "LockXhciConfiguration () - End\n")); +} + +/** + Process all the lock downs +**/ +VOID +ProcessAllLocks ( + VOID + ) +{ + UINTN Index; + UINT8 Data8; + UINT16 Data16And; + UINT16 Data16Or; + UINT32 Data32; + UINT32 Data32And; + UINT32 Data32Or; + UINT32 DlockValue; + UINTN PciLpcRegBase; + UINTN PciSpiRegBase; + UINTN PciPmcRegBase; + UINT16 TcoBase; + UINT32 PchPwrmBase; + UINTN PchSpiBar0; + + PciLpcRegBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC + ); + PciPmcRegBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_PMC, + PCI_FUNCTION_NUMBER_PCH_PMC + ); + PciSpiRegBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_SPI, + PCI_FUNCTION_NUMBER_PCH_SPI + ); + PchSpiBar0 = MmioRead32 (PciSpiRegBase + R_PCH_SPI_BAR0) &~(B_PCH_SPI_BAR0_MASK); + + PchTcoBaseGet (&TcoBase); + + // + // Lock XHCI configuration + // + LockXhciConfiguration (mPchConfigHob); + + /// + /// Set PWRMBASE + 620h [31] to lock the ST and NST PG register fields. + /// + ConfigurePmcStaticFunctionDisableLock (); + + /// + /// SKL PCH BWG 7.2.4 Additional PCH DMI and OP-DMI Programming Steps + /// Step 9.2 + /// Set PCR[DMI] + 2234h [31] = 1b. + /// Leave this in DXE since setting it in PEI would break the ActiveBIOS module. + /// + Data32And = 0xFFFFFFFF; + Data32Or = B_PCH_PCR_DMI_DMIC_SRL; + PchPcrAndThenOr32 ( + PID_DMI, R_PCH_PCR_DMI_DMIC, + Data32And, + Data32Or + ); + PCH_PCR_BOOT_SCRIPT_READ_WRITE ( + S3BootScriptWidthUint32, + PID_DMI, R_PCH_PCR_DMI_DMIC, + &Data32Or, + &Data32And + ); + + + /// + /// Program the Flash Protection Range Register based on policy + /// + DlockValue = MmioRead32 (PchSpiBar0 + R_PCH_SPI_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_PCH_SPI_PRX_WPE : 0; + Data32 |= (mPchConfigHob->ProtectRange[Index].ReadProtectionEnable == TRUE) ? B_PCH_SPI_PRX_RPE : 0; + Data32 |= ((UINT32) mPchConfigHob->ProtectRange[Index].ProtectedRangeLimit << N_PCH_SPI_PRX_PRL) & B_PCH_SPI_PRX_PRL_MASK; + Data32 |= ((UINT32) mPchConfigHob->ProtectRange[Index].ProtectedRangeBase << N_PCH_SPI_PRX_PRB) & B_PCH_SPI_PRX_PRB_MASK; + DEBUG ((DEBUG_INFO, "Protected range %d: 0x%08x \n", Index, Data32)); + + DlockValue |= (UINT32) (B_PCH_SPI_DLOCK_PR0LOCKDN << Index); + MmioWrite32 ((UINTN) (PchSpiBar0 + (R_PCH_SPI_PR0 + (Index * S_PCH_SPI_PRX))), Data32); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PchSpiBar0 + (R_PCH_SPI_PR0 + (Index * S_PCH_SPI_PRX))), + 1, + (VOID *) (UINTN) (PchSpiBar0 + (R_PCH_SPI_PR0 + (Index * S_PCH_SPI_PRX))) + ); + } + // + // Program DLOCK register + // + MmioWrite32 ((UINTN) (PchSpiBar0 + R_PCH_SPI_DLOCK), DlockValue); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PchSpiBar0 + R_PCH_SPI_DLOCK), + 1, + (VOID *) (UINTN) (PchSpiBar0 + R_PCH_SPI_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. + /// + MmioOr32 ((UINTN) (PchSpiBar0 + R_PCH_SPI_HSFSC), (UINT32) (B_PCH_SPI_HSFSC_FLOCKDN)); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PchSpiBar0 + R_PCH_SPI_HSFSC), + 1, + (VOID *) (UINTN) (PchSpiBar0 + R_PCH_SPI_HSFSC) + ); + + /// + /// 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_PCH_SPI_SFDP0_VSCC0), B_PCH_SPI_SFDP0_VSCC0_VCL); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PchSpiBar0 + R_PCH_SPI_SFDP0_VSCC0), + 1, + (VOID *) (UINTN) (PchSpiBar0 + R_PCH_SPI_SFDP0_VSCC0) + ); + + /// + /// Additional Power Management Programming + /// Step 3 + /// Set GEN_PMCON_LOCK register, PMC PCI offset A6h = 06h, after stretch and ACPI base programming completed. + /// + MmioOr8 ( + (UINTN) (PciPmcRegBase + R_PCH_PMC_GEN_PMCON_B + 2), + (UINT8) ((B_PCH_PMC_GEN_PMCON_B_SLPSX_STR_POL_LOCK | B_PCH_PMC_GEN_PMCON_B_ACPI_BASE_LOCK) >> 16) + ); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint8, + (UINTN) (PciPmcRegBase + R_PCH_PMC_GEN_PMCON_B + 2), + 1, + (VOID *) (UINTN) (PciPmcRegBase + R_PCH_PMC_GEN_PMCON_B + 2) + ); + + /// + /// PCH BIOS Spec Section 3.6 Flash Security Recommendation + /// BIOS needs to enable the BIOS Lock Enable (BLE) feature of the PCH by setting + /// SPI/eSPI/LPC PCI offset DCh[1] = 1b. + /// When this bit is set, attempts to write the BIOS Write Enable (BIOSWE) bit + /// in PCH will cause a SMI which will allow the BIOS to verify that the write is + /// from a valid source. + /// Remember that BIOS needs to set SPI/LPC/eSPI PCI Offset DC [0] = 0b to enable + /// BIOS region protection before exiting the SMI handler. + /// Also, TCO_EN bit needs to be set (SMI_EN Register, ABASE + 30h[13] = 1b) to keep + /// BLE feature enabled after booting to the OS. + /// Intel requires that BIOS enables the Lock Enable (LE) feature of the PCH to + /// ensure SMM protection of flash. + /// Left to platform code to register a callback function to handle BiosWp SMI + /// + if (mPchConfigHob->LockDown.BiosLock == TRUE) { + // + // eSPI/LPC + // + if (! (MmioRead8 (PciLpcRegBase + R_PCH_LPC_BC) & B_PCH_LPC_BC_LE)) { + DEBUG ((DEBUG_INFO, "Set LPC bios lock\n")); + MmioOr8 ((UINTN) (PciLpcRegBase + R_PCH_LPC_BC), B_PCH_LPC_BC_LE); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint8, + (UINTN) (PciLpcRegBase + R_PCH_LPC_BC), + 1, + (VOID *) (UINTN) (PciLpcRegBase + R_PCH_LPC_BC) + ); + } + // + // SPI + // + if (! (MmioRead8 (PciSpiRegBase + R_PCH_SPI_BC) & B_PCH_SPI_BC_LE)) { + DEBUG ((DEBUG_INFO, "Set SPI bios lock\n")); + MmioOr8 ((UINTN) (PciSpiRegBase + R_PCH_SPI_BC), (UINT8) B_PCH_SPI_BC_LE); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint8, + (UINTN) (PciSpiRegBase + R_PCH_SPI_BC), + 1, + (VOID *) (UINTN) (PciSpiRegBase + R_PCH_SPI_BC) + ); + } + } + + /// + /// PCH BIOS Spec Section 3.6 Flash Security Recommendation + /// BIOS also needs to set the BIOS Interface Lock Down bit in multiple locations + /// (PCR[DMI] + 274Ch[0], LPC/eSPI PCI offset DCh[7] and SPI PCI offset DCh[7]). + /// Setting these bits will prevent writes to the Top Swap bit (under their respective locations) + /// and the Boot BIOS Straps. Enabling this bit will mitigate malicious software + /// attempts to replace the system BIOS option ROM with its own code. + /// + if (mPchConfigHob->LockDown.BiosInterface == TRUE) { + /// + /// LPC + /// + MmioOr8 ((UINTN) (PciLpcRegBase + R_PCH_LPC_BC), (UINT32) B_PCH_LPC_BC_BILD); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint8, + (UINTN) (PciLpcRegBase + R_PCH_LPC_BC), + 1, + (VOID *) (UINTN) (PciLpcRegBase + R_PCH_LPC_BC) + ); + + /// + /// Reads back for posted write to take effect + /// + Data8 = MmioRead8 ((UINTN) (PciLpcRegBase + R_PCH_LPC_BC)); + S3BootScriptSaveMemPoll ( + S3BootScriptWidthUint8, + (UINTN) (PciLpcRegBase + R_PCH_LPC_BC), + &Data8, // BitMask + &Data8, // BitValue + 1, // Duration + 1 // LoopTimes + ); + + /// + /// SPI + /// + MmioOr8 ((UINTN) (PciSpiRegBase + R_PCH_SPI_BC), (UINT32) B_PCH_SPI_BC_BILD); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint8, + (UINTN) (PciSpiRegBase + R_PCH_SPI_BC), + 1, + (VOID *) (UINTN) (PciSpiRegBase + R_PCH_SPI_BC) + ); + + /// + /// Reads back for posted write to take effect + /// + Data8 = MmioRead8 ((UINTN) (PciSpiRegBase + R_PCH_SPI_BC)); + S3BootScriptSaveMemPoll ( + S3BootScriptWidthUint8, + (UINTN) (PciSpiRegBase + R_PCH_SPI_BC), + &Data8, // BitMask + &Data8, // BitValue + 1, // Duration + 1 // LoopTimes + ); + + /// + /// Set PCR[DMI] + 274C[0] = 1b + /// + Data32And = 0xFFFFFFFF; + Data32Or = B_PCH_PCR_DMI_BILD; + PchPcrAndThenOr32 (PID_DMI, R_PCH_PCR_DMI_GCS, Data32And, Data32Or); + PCH_PCR_BOOT_SCRIPT_READ_WRITE ( + S3BootScriptWidthUint32, + PID_DMI, R_PCH_PCR_DMI_GCS, + &Data32Or, + &Data32And + ); + } + + /// + /// PCH BIOS Spec Section 5.13 BIOS guide on using RTC RAM + /// For Data integrity protection, set RTC Memory locks (Upper 128 Byte Lock and + /// Lower 128 Byte Lock) at SBCR[RTC] + 3400h[4] and SBCR[RTC] + 3400h[3]. Note once locked + /// bytes 0x38 - 0x3F in each of the Upper and Lower Byte blocks, respectively, + /// cannot be unlocked until next reset. + /// + if (mPchConfigHob->LockDown.RtcLock == TRUE) { + Data32And = 0xFFFFFFFF; + Data32Or = (B_PCH_PCR_RTC_CONF_UCMOS_LOCK | B_PCH_PCR_RTC_CONF_LCMOS_LOCK | B_PCH_PCR_RTC_CONF_RESERVED); + PchPcrAndThenOr32 ( + PID_RTC, R_PCH_PCR_RTC_CONF, + Data32And, + Data32Or + ); + PCH_PCR_BOOT_SCRIPT_READ_WRITE ( + S3BootScriptWidthUint32, + PID_RTC, R_PCH_PCR_RTC_CONF, + &Data32Or, + &Data32And + ); + } + + /// + /// Lock Down TCO + /// + Data16And = 0xFFFF; + Data16Or = B_PCH_TCO_CNT_LOCK; + IoOr16 (TcoBase + R_PCH_TCO1_CNT, Data16Or); + S3BootScriptSaveIoReadWrite ( + S3BootScriptWidthUint16, + (UINTN) (TcoBase + R_PCH_TCO1_CNT), + &Data16Or, // Data to be ORed + &Data16And // Data to be ANDed + ); + + /// + /// PCH BIOS Spec Section 5.15.1 Additional Chipset Initialization + /// Step 1 + /// Set SPIBAR0 + F0h [0] to 1b + /// + MmioOr8 ((UINTN) (PchSpiBar0 + R_PCH_SPI_SSML), B_PCH_SPI_SSML_SSL); + + // + // Lock Down PMC + // Set PWRM + 0x18 [27, 22] prior to OS. + // + PchPwrmBaseGet (&PchPwrmBase); + Data32Or = BIT27; + if (mPchConfigHob->Pm.PmcReadDisable) { + Data32Or |= BIT22; + } + MmioOr32 (PchPwrmBase + R_PCH_PWRM_CFG, Data32Or); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PchPwrmBase + R_PCH_PWRM_CFG), + 1, + (VOID *) (UINTN) (PchPwrmBase + R_PCH_PWRM_CFG) + ); +} + +/** + Set eSPI BME bit +**/ +VOID +ConfigureEspiBme ( + VOID + ) +{ +} + +/** + Common PCH initialization before Boot Sript Table is closed + +**/ +VOID +PchOnPciEnumCompleteCommon ( + VOID + ) +{ + UINT32 Data32Or; + UINT32 Data32And; + + DEBUG ((DEBUG_INFO, "PchOnPciEnumCompleteCommon() Start\n")); + + ProcessAllLocks (); + + /// + /// Perform remaining configuration for PCH SATA on End of DXE + /// + ConfigurePchSataOnEndOfDxe (); + + /// + /// PSTHCTL (0xD00h[2]) = 1, PSTH IOSF Primary Trunk Clock Gating Enable (PSTHIOSFPTCGE) + /// + Data32And = 0xFFFFFFFF; + Data32Or = B_PCH_PCR_PSTH_PSTHIOSFPTCGE; + PchPcrAndThenOr32 (PID_PSTH, R_PCH_PCR_PSTH_PSTHCTL, Data32And, Data32Or); + PCH_PCR_BOOT_SCRIPT_READ_WRITE ( + S3BootScriptWidthUint32, + PID_PSTH, R_PCH_PCR_PSTH_PSTHCTL, + &Data32Or, + &Data32And + ); + + // + // Set eSPI BME after PCI enumeration + // + ConfigureEspiBme (); + + // + // This function process P2SB configuration + // This MUST be executed in very end of End Of Dxe + // All PSF access after this function will not acceptable + // + ConfigureP2sbSbiLock ((BOOLEAN) mPchConfigHob->P2sb.SbiUnlock); + + DEBUG ((DEBUG_INFO, "PchOnPciEnumCompleteCommon() End\n")); +} diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.h b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.h new file mode 100644 index 0000000000..a05697d337 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInit.h @@ -0,0 +1,320 @@ +/** @file + Header file for PCH Initialization Driver. + +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. + +**/ +#ifndef _PCH_INITIALIZATION_DRIVER_H_ +#define _PCH_INITIALIZATION_DRIVER_H_ + +typedef UINT16 STRING_REF; +#ifdef FSP_FLAG +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef FSP_FLAG +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + PCH_INFO_PROTOCOL PchInfo; +} PCH_INSTANCE_PRIVATE_DATA; + +// +// This struct is used to record the fields that is required to be saved and restored during RST PCIe Storage Remapping Configuration +// +typedef struct { + UINT8 PmCapPtr; + UINT8 PcieCapPtr; + UINT16 L1ssCapPtr; + UINT8 EndpointL1ssControl2; + UINT32 EndpointL1ssControl1; + UINT16 LtrCapPtr; + UINT32 EndpointLtrData; + UINT16 EndpointLctlData16; + UINT16 EndpointDctlData16; + UINT16 EndpointDctl2Data16; + UINT16 RootPortDctl2Data16; +} PCH_RST_PCIE_STORAGE_SAVE_RESTORE; + +// +// This struct is used to record the result of RST PCIe Storage detection for each RST PCIe Storage Cycle Router supported on the platform +// +typedef struct { + BOOLEAN SupportRstPcieStoragRemapping; // Indicates if RST PCIe Storage Remapping is supported and PCIe storage device is found under a Cycle Router + UINT8 RootPortNum; // Indicates the root port number with RST PCIe Storage Remapping remapping supported and PCIe storage device plugged on, numbering is 0-based + UINT8 RootPortLane; // Indicates the root port lanes occupied by the PCIe storage device with 4-bit mask + UINT8 DeviceInterface; // Indicates the interface of the PCIe storage device (AHCI or NVMe) + UINT8 IsMsixSupported; // Indicates if the PCIe storage device support MSI-X cap + UINT16 MsixStartingVector; // Records the starting vector of PCIe storage device's MSI-X (if supported) + UINT16 MsixEndingVector; // Records the ending vector of PCIe storage device's MSI-X (if supported) + UINT32 EndPointBarSize; // Records the PCIe storage device's BAR size + UINT32 EndPointUniqueMsixTableBar; // Records the PCIe storage device's MSI-X Table BAR if it supports unique MSI-X Table BAR + UINT32 EndPointUniqueMsixTableBarValue; // Records the PCIe storage device's MSI-X Table BAR value if it supports unique MSI-X Table BAR + UINT32 EndPointUniqueMsixPbaBar; // Records the PCIe storage device's MSI-X PBA BAR if it supports unique MSI-X PBA BAR + UINT32 EndPointUniqueMsixPbaBarValue; // Records the PCIe storage device's MSI-X PBA BAR value if it supports unique MSI-X PBA BAR + UINT8 EndPointBcc; // Records the PCIe storage device's Base Class Code + UINT8 EndPointScc; // Records the PCIe storage device's Sub Class Code + UINT8 EndPointPi; // Records the PCIe storage device's Programming Interface + PCH_RST_PCIE_STORAGE_SAVE_RESTORE PchRstPcieStorageSaveRestore; // Records the fields that is required to be saved and restored +} PCH_RST_PCIE_STORAGE_DETECTION; + +// +// Data definitions +// +extern EFI_HANDLE mImageHandle; + +// +// Pch NVS area definition +// +extern PCH_NVS_AREA_PROTOCOL mPchNvsAreaProtocol; + +extern PCH_CONFIG_HOB *mPchConfigHob; +extern SI_CONFIG_HOB *mSiConfigHob; + +// +// Function Prototype +// + +// +// Local function prototypes +// +/** + Initialize the PCH device according to the PCH Policy HOB + and install PCH info instance. + +**/ +VOID +InitializePchDevice ( + VOID + ); + +/** + Common PchInit Module Entry Point +**/ +VOID +PchInitEntryPointCommon ( + VOID + ); + +/** + Common PCH initialization on PCI enumeration complete. +**/ +VOID +PchOnPciEnumCompleteCommon ( + VOID + ); + +/** + Configures Serial IO Controllers + +**/ +EFI_STATUS +ConfigureSerialIoAtBoot ( + VOID + ); + +/** + Creates device handles for SerialIo devices in ACPI mode + +**/ +VOID +CreateSerialIoHandles ( + VOID + ); + +/** + Mark memory used by SerialIo devices in ACPI mode as allocated + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +AllocateSerialIoMemory ( + VOID + ); + +/** + Update ASL definitions for SerialIo devices. + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +UpdateSerialIoAcpiData ( + VOID + ); + +/** + Initialize PCIE SRC clocks in ICC subsystem + + @param[in] GbePortNumber Number of PCIE rootport assigned to GbE adapter + +**/ +VOID +ConfigurePchPcieClocks ( + IN UINTN GbePortNumber + ); + +/** + Initialize Intel High Definition Audio ACPI Tables + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_LOAD_ERROR ACPI table cannot be installed + @retval EFI_UNSUPPORTED ACPI table not set because DSP is disabled +**/ +EFI_STATUS +PchHdAudioAcpiInit ( + VOID + ); + +/** + Configure eMMC in HS400 Mode + + @param[in] This A pointer to PCH_EMMC_TUNING_PROTOCOL structure + @param[in] Revision Revision parameter used to verify the layout of EMMC_INFO and TUNINGDATA. + @param[in] EmmcInfo A pointer to EMMC_INFO structure + @param[out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_NOT_FOUND The item was not found + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_DEVICE_ERROR Hardware Error + @retval EFI_NO_MEDIA No media + @retval EFI_MEDIA_CHANGED Media Change + @retval EFI_BAD_BUFFER_SIZE Buffer size is bad + @retval EFI_CRC_ERROR Command or Data CRC Error +**/ +EFI_STATUS +EFIAPI +ConfigureEmmcHs400Mode ( + IN PCH_EMMC_TUNING_PROTOCOL *This, + IN UINT8 Revision, + IN EMMC_INFO *EmmcInfo, + OUT EMMC_TUNING_DATA *EmmcTuningData + ); + +/** + Install PCH EMMC TUNING PROTOCOL + +**/ +VOID +InstallPchEmmcTuningProtocol ( + VOID + ); + +/** + Perform the remaining configuration on PCH SATA to perform device detection, + then set the SATA SPD and PxE corresponding, and set the Register Lock on PCH SATA + + @retval None +**/ +VOID +ConfigurePchSataOnEndOfDxe ( + VOID + ); + +/** + Update ASL data for CIO2 Device. + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +UpdateCio2AcpiData ( + VOID + ); + +/** + 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 + ); + +/** + Update ASL object before Boot + + @retval EFI_STATUS + @retval EFI_NOT_READY The Acpi protocols are not ready. +**/ +EFI_STATUS +PchUpdateNvsArea ( + VOID + ); + +/** + 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 + ); + +/** + 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 + ); +#endif // _PCH_INITIALIZATION_DRIVER_H_ diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.c new file mode 100644 index 0000000000..1857b7f3c5 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.c @@ -0,0 +1,699 @@ +/** @file + This is the Uefi 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" + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_HANDLE mImageHandle; +GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mPcieIoTrapAddress; + + +VOID +EFIAPI +PchOnBootToOs ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +VOID +EFIAPI +PchOnExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +VOID +EFIAPI +PchOnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + BIOS W/A for Si bug on PCH stepping LpA0, LpA1, LpA2, LpB0: + allocates 1MB memory space for ISH BAR, + overrides 4KB memory allocation done by standard PCI enumarator. + + @retval EFI_STATUS + @retval EFI_SUCCESS Allocation successful or stepping other that A0/A1/A2 (W/A not required) + @retval EFI_OUT_OF_RESOURCES Memory could not be allocated. +**/ +EFI_STATUS +AllocateIshMemorySpace ( + VOID + ) +{ + EFI_STATUS Status; + UINTN PciIshRegBase; + EFI_PHYSICAL_ADDRESS IshMemBaseAddress; + + PciIshRegBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_ISH, + PCI_FUNCTION_NUMBER_PCH_ISH + ); + + if ((MmioRead16 (PciIshRegBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) || + (GetPchSeries () == PchH) || + (PchStepping () > PchLpB1)) { + return EFI_SUCCESS; + } + + if ((PcdGet8 (PcdEfiGcdAllocateType) == EfiGcdAllocateMaxAddressSearchBottomUp) || (PcdGet8 (PcdEfiGcdAllocateType) == EfiGcdAllocateMaxAddressSearchTopDown)) { + IshMemBaseAddress = 0xffffffff; + } + Status = gDS->AllocateMemorySpace ( + PcdGet8 (PcdEfiGcdAllocateType), + EfiGcdMemoryTypeMemoryMappedIo, + N_PCH_ISH_BAR0_ALIGNMENT, + V_PCH_ISH_BAR0_SIZE, + &IshMemBaseAddress, + mImageHandle, + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + MmioAnd16 (PciIshRegBase + PCI_COMMAND_OFFSET, (UINT16) ~EFI_PCI_COMMAND_MEMORY_SPACE); + MmioWrite32 (PciIshRegBase + R_PCH_ISH_BAR0_LOW, (UINT32) IshMemBaseAddress); + MmioWrite32 (PciIshRegBase + R_PCH_ISH_BAR0_HIGH, 0); + MmioOr16 (PciIshRegBase + PCI_COMMAND_OFFSET, (UINT16) EFI_PCI_COMMAND_MEMORY_SPACE); + + DEBUG ((DEBUG_INFO, "ISH W/A: Allocated 1MB MMIO, BAR0 = 0x%08x\n", (UINT32) IshMemBaseAddress)); + return Status; +} + + +/** + Process all the lock downs +**/ +VOID +ProcessSmiLocks ( + VOID + ) +{ + UINT32 Data32And; + UINT32 Data32Or; + UINT16 ABase; + UINTN PciPmcRegBase; + + PciPmcRegBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_PMC, + PCI_FUNCTION_NUMBER_PCH_PMC + ); + + /// + /// PCH BIOS Spec Section 3.6 Flash Security Recommendation + /// BIOS needs to enables SMI_LOCK (PMC PCI offset A0h[4] = 1b) which prevent writes + /// to the Global SMI Enable bit (GLB_SMI_EN ABASE + 30h[0]). Enabling this bit will + /// mitigate malicious software attempts to gain system management mode privileges. + /// + if (mPchConfigHob->LockDown.GlobalSmi == TRUE) { + PchAcpiBaseGet (&ABase); + /// + /// Save Global SMI Enable bit setting before BIOS enables SMI_LOCK during S3 resume + /// + Data32Or = IoRead32 ((UINTN) (ABase + R_PCH_SMI_EN)); + if ((Data32Or & B_PCH_SMI_EN_GBL_SMI) != 0) { + Data32And = 0xFFFFFFFF; + Data32Or |= B_PCH_SMI_EN_GBL_SMI; + S3BootScriptSaveIoReadWrite ( + S3BootScriptWidthUint32, + (UINTN) (ABase + R_PCH_SMI_EN), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + } + MmioOr8 ((UINTN) (PciPmcRegBase + R_PCH_PMC_GEN_PMCON_A), B_PCH_PMC_GEN_PMCON_A_SMI_LOCK); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint8, + (UINTN) (PciPmcRegBase + R_PCH_PMC_GEN_PMCON_A), + 1, + (VOID *) (UINTN) (PciPmcRegBase + R_PCH_PMC_GEN_PMCON_A) + ); + } +} + +/** + Do PCIE power management while resume from S3 +**/ +VOID +ReconfigurePciePowerManagementForS3 ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 Data32; + PCH_PCIE_IOTRAP_PROTOCOL *PchPcieIoTrapProtocol; + + Status = gBS->LocateProtocol (&gPchPcieIoTrapProtocolGuid, NULL, (VOID **) &PchPcieIoTrapProtocol); + ASSERT_EFI_ERROR (Status); + mPcieIoTrapAddress = PchPcieIoTrapProtocol->PcieTrapAddress; + DEBUG ((DEBUG_INFO, "PcieIoTrapAddress: %0x\n", mPcieIoTrapAddress)); + + if (mPcieIoTrapAddress != 0) { + // + // Save PCH PCIE IoTrap address to re-config PCIE power management setting after resume from S3 + // + Data32 = PciePmTrap; + S3BootScriptSaveIoWrite ( + S3BootScriptWidthUint32, + (UINTN) (mPcieIoTrapAddress), + 1, + &Data32 + ); + } else { + ASSERT (FALSE); + } +} + +/** + This is the callback function for PCI ENUMERATION COMPLETE. +**/ +VOID +EFIAPI +PchOnPciEnumComplete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *ProtocolPointer; + UINTN ThermalPciBase; + UINT32 PchPwrmBase; + + /// + /// Check if this is first time called by EfiCreateProtocolNotifyEvent() or not, + /// if it is, we will skip it until real event is triggered + /// + Status = gBS->LocateProtocol (&gEfiPciEnumerationCompleteProtocolGuid, NULL, (VOID **) &ProtocolPointer); + if (EFI_SUCCESS != Status) { + return; + } + gBS->CloseEvent (Event); + + + PchPwrmBaseGet (&PchPwrmBase); + + // + // Allocate BAR for ISH - workaround for Si bug on stepping LpA0/A1/A2 + // + AllocateIshMemorySpace (); + + // + // Enable Thermal MSE + // + ThermalPciBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_THERMAL, + PCI_FUNCTION_NUMBER_PCH_THERMAL + ); + if (MmioRead16 (ThermalPciBase + PCI_VENDOR_ID_OFFSET) != 0xFFFF) { + if (((MmioRead32 (ThermalPciBase + R_PCH_THERMAL_TBAR) & B_PCH_THERMAL_TBAR_MASK) != 0) || + ((MmioRead32 (ThermalPciBase + R_PCH_THERMAL_TBARH) != 0))) { + MmioOr8 (ThermalPciBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE); + } + } + + ReconfigurePciePowerManagementForS3 (); + ProcessSmiLocks (); +#ifndef FSP_WRAPPER_FLAG + PchOnPciEnumCompleteCommon (); +#endif +} + +/** + Register callback functions for PCH DXE. +**/ +VOID +PchRegisterNotifications ( + VOID + ) +{ + EFI_STATUS Status; + EFI_EVENT ReadyToBoot; + EFI_EVENT LegacyBootEvent; + EFI_EVENT ExitBootServicesEvent; + VOID *Registration; + + /// + /// Create PCI Enumeration Completed callback for PCH + /// + EfiCreateProtocolNotifyEvent ( + &gEfiPciEnumerationCompleteProtocolGuid, + TPL_CALLBACK, + PchOnPciEnumComplete, + NULL, + &Registration + ); + + // + // Register a Ready to boot event to config PCIE power management setting after OPROM executed + // + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + PchOnReadyToBoot, + NULL, + &ReadyToBoot + ); + ASSERT_EFI_ERROR (Status); + + // + // Create events for PCH to do the task before ExitBootServices/LegacyBoot. + // It is guaranteed that only one of two events below will be signalled + // + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, + PchOnExitBootServices, + NULL, + &ExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiCreateEventLegacyBootEx ( + TPL_CALLBACK, + PchOnBootToOs, + NULL, + &LegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Updates PCIe configuration in PCH Info Protocol. + + @param[in,out] PchInfo Pointer to PCH info protocol instance to be updated +**/ +VOID +UpdatePcieConfigInfo ( + IN OUT PCH_INFO_PROTOCOL *PchInfo + ) +{ + EFI_STATUS Status; + PCH_SERIES PchSeries; + UINT32 StrapFuseCfg1; + UINT32 StrapFuseCfg5; + UINT32 StrapFuseCfg9; + UINT32 StrapFuseCfg13; + UINT32 StrapFuseCfg17; + UINT32 StrapFuseCfg21; + + PchSeries = GetPchSeries (); + + Status = PchSbiRpPciRead32 ( + 0, + R_PCH_PCIE_STRPFUSECFG, + &StrapFuseCfg1 + ); + ASSERT_EFI_ERROR (Status); + + Status = PchSbiRpPciRead32 ( + 4, + R_PCH_PCIE_STRPFUSECFG, + &StrapFuseCfg5 + ); + ASSERT_EFI_ERROR (Status); + + Status = PchSbiRpPciRead32 ( + 8, + R_PCH_PCIE_STRPFUSECFG, + &StrapFuseCfg9 + ); + ASSERT_EFI_ERROR (Status); + + PchInfo->PcieControllerCfg1 = (UINT8) ((StrapFuseCfg1 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC); + PchInfo->PcieControllerCfg2 = (UINT8) ((StrapFuseCfg5 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC); + PchInfo->PcieControllerCfg3 = (UINT8) ((StrapFuseCfg9 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC); + + if (PchSeries == PchH) { + Status = PchSbiRpPciRead32 ( + 12, + R_PCH_PCIE_STRPFUSECFG, + &StrapFuseCfg13 + ); + ASSERT_EFI_ERROR (Status); + Status = PchSbiRpPciRead32 ( + 16, + R_PCH_PCIE_STRPFUSECFG, + &StrapFuseCfg17 + ); + ASSERT_EFI_ERROR (Status); + + PchInfo->PcieControllerCfg4 = (UINT8) ((StrapFuseCfg13 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC); + PchInfo->PcieControllerCfg5 = (UINT8) ((StrapFuseCfg17 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC); + + if (GetPchGeneration () == KblPch) { + Status = PchSbiRpPciRead32 ( + 20, + R_PCH_PCIE_STRPFUSECFG, + &StrapFuseCfg21 + ); + ASSERT_EFI_ERROR (Status); + + PchInfo->PcieControllerCfg6 = (UINT8) ((StrapFuseCfg21 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC); + } + + } +} + +/** + Stores PCIE Gen3 software equalization settings in bootscript to accelerate S3 resume +**/ +VOID +SavePcieGen3Eq ( + VOID + ) +{ + UINT8 RpIndex; + UINTN RpBase; + UINT8 EqMethod; + UINT32 Data32; + UINT32 Data32Or; + UINT32 Data32And; + + for (RpIndex = 0; RpIndex < GetPchMaxPciePortNum (); ++RpIndex) { + RpBase = PchPcieBase (RpIndex); + // + // Before trying to save it, make sure software gen3 equalization did actually happen + // + if (MmioRead32 (RpBase) == 0xFFFFFFFF) { + continue; + } + if (GetMaxLinkSpeed (RpBase) < 3) { + continue; + } + EqMethod = mPchConfigHob->PcieRp.RootPort[RpIndex].Gen3EqPh3Method; + if (EqMethod != PchPcieEqDefault && EqMethod != PchPcieEqSoftware) { + continue; + } + // + // Can't check current link speed due to some nonconformant devices + // Instead we verify B_PCH_PCIE_EQCFG1_RTLEPCEB bit which together with policy check above + // guarantees that software gen3 eq was performed + // + if (!(MmioRead32 (RpBase + R_PCH_PCIE_EQCFG1) & B_PCH_PCIE_EQCFG1_RTLEPCEB)) { + continue; + } + + // + // Restore PCIE Gen3 equalization coefficients + // + Data32 = MmioRead32 (RpBase + R_PCH_PCIE_RTPCL1); + S3BootScriptSaveMemWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_RTPCL1, 1, &Data32); + Data32 = MmioRead32 (RpBase + R_PCH_PCIE_RTPCL2); + S3BootScriptSaveMemWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_RTPCL2, 1, &Data32); + Data32And = 0xFFFFFFFF; + Data32Or = B_PCH_PCIE_EQCFG1_RTLEPCEB | B_PCH_PCIE_EQCFG1_RTPCOE; + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_EQCFG1, &Data32Or, &Data32And); + + // + // Perform link retrain. + // + Data32And = (UINT32) (~B_PCIE_LCTL2_TLS); + Data32Or = V_PCIE_LCTL2_TLS_GEN3; + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_LCTL2, &Data32Or, &Data32And); + Data32And = 0xFFFFFFFF; + Data32Or = B_PCIE_EX_LCTL3_PE; + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_EX_LCTL3, &Data32Or, &Data32And); + Data32Or = B_PCIE_LCTL_RL; + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_LCTL, &Data32Or, &Data32And); + /// PCH BIOS Spec Section 8.15 Additional PCI Express* Programming Steps + /// Set "Link Speed Training Policy", Dxx:Fn + D4h[6] to 1. + /// Make sure this is after mod-PHY related programming is completed. + Data32Or = B_PCH_PCIE_MPC2_LSTP; + DEBUG ((DEBUG_WARN, "Gen3Eq S3MmioOr32 0x%08x\n", RpBase + R_PCH_PCIE_MPC2)); + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, RpBase + R_PCH_PCIE_MPC2, &Data32Or, &Data32And); + } +} + +/** + Initialize the PCH device according to the PCH Policy HOB + and install PCH info instance. +**/ +VOID +InitializePchDevice ( + VOID + ) +{ + EFI_HANDLE Handle; + PCH_INFO_PROTOCOL *PchInfo; + EFI_STATUS Status; + UINT32 PchPwrmBase; + + Handle = NULL; + + DEBUG ((DEBUG_INFO, "InitializePchDevice() Start\n")); + + PchPwrmBaseGet (&PchPwrmBase); + + // + // Create the PCH Info protocol instance + // + PchInfo = AllocateZeroPool (sizeof (PCH_INFO_PROTOCOL)); + if (PchInfo == NULL) { + ASSERT (FALSE); + return; + } + + PchInfo->Revision = PCH_INFO_PROTOCOL_REVISION; + + UpdatePcieConfigInfo (PchInfo); + + // + // Store PCIE Gen3 software equalization settings in bootscripts to save time on S3 resume + // This must happen before some rootports get hidden by RST remapping + // + SavePcieGen3Eq (); + + + // + // Configure root port function number mapping + // For UEFI bios, execute RPFN mapping after RST configuraion and before PCI enumeration. + // + Status = PchConfigureRpfnMapping (); + ASSERT_EFI_ERROR (Status); + + // + // Install Pch Emmc Tuning Protocol + // + if ((mPchConfigHob->Scs.ScsEmmcEnabled == TRUE) && (mPchConfigHob->Scs.ScsEmmcHs400Enabled == TRUE)) { + InstallPchEmmcTuningProtocol (); + } + + // + // Install PCH info protocol on new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gPchInfoProtocolGuid, + PchInfo, + NULL + ); + ASSERT_EFI_ERROR (Status); + DEBUG ((DEBUG_INFO, "InitializePchDevice() End\n")); +} +/** + PchInit DXE Module Entry Point\n + - Introduction\n + The PchInit module is a DXE driver that initializes the Intel Platform Controller Hub + following the PCH BIOS specification and EDS requirements and recommendations. It consumes + the PCH_POLICY_HOB SI_POLICY_HOB for expected configurations per policy. + This is the standard EFI driver point that detects whether there is an supported PCH in + the system and if so, initializes the chipset. + + - Details\n + This module is required for initializing the Intel Platform Controller Hub to + follow the PCH BIOS specification and EDS. + This includes some initialization sequences, enabling and disabling PCH devices, + configuring clock gating, RST PCIe Storage Remapping, SATA controller, ASPM of PCIE devices. Right before end of DXE, + it's responsible to lock down registers for security requirement. + + - @pre + - PCH PCR base address configured + - EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL + - This is to ensure that PCI MMIO and IO resource has been prepared and available for this driver to allocate. + + - @result + - Publishes the @link _PCH_INFO_PROTOCOL PCH_INFO_PROTOCOL @endlink + - Publishes the @link _PCH_EMMC_TUNING_PROTOCOL PCH_EMMC_TUNING_PROTOCOL @endlink + + - References\n + - @link _PCH_POLICY PCH_POLICY_HOB @endlink. + - @link _SI_POLICY_STRUCT SI_POLICY_HOB @endlink. + + - Integration Checklists\n + - Verify prerequisites are met. Porting Recommendations. + - No modification of this module should be necessary + - Any modification of this module should follow the PCH BIOS Specification and EDS + + @param[in] ImageHandle Handle for the image of this driver + @param[in] SystemTable Pointer to the EFI System Table + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Do not have enough resources to initialize the driver +**/ +EFI_STATUS +EFIAPI +PchInitEntryPointDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "PchInitEntryPointDxe() Start\n")); + + mImageHandle = ImageHandle; + + PchInitEntryPointCommon (); + + InitializePchDevice (); + + Status = PchAcpiInit (ImageHandle); + + CreateSerialIoHandles (); + + PchRegisterNotifications (); + + DEBUG ((DEBUG_INFO, "PchInitEntryPointDxe() End\n")); + + return Status; +} + +/** + PCH initialization before ExitBootServices / LegacyBoot events + Useful for operations which must happen later than at EndOfPost event + + @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 +PchOnBootToOs ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + /// + /// Closed the event to avoid call twice + /// + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + ConfigureSerialIoAtBoot (); + + return; +} + +/** + PCH initialization on ExitBootService. This event is used if only ExitBootService is used + and not in legacy boot + + @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. + + @retval None +**/ +VOID +EFIAPI +PchOnExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PchOnBootToOs (NULL, NULL); + + PchUpdateNvsOnExitBootServices (); + + return; +} + +/** + PCH initialization before boot to OS + + @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 +PchOnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +#ifndef FSP_WRAPPER_FLAG + UINTN XhciMmioBase; + UINTN XhciPciMmBase; + UINT8 XhciPciCommand; + BOOLEAN XhciPdoRestartNeeded; +#endif // FSP_WRAPPER_FLAG + + DEBUG ((DEBUG_INFO, "Uefi PchOnReadyToBoot() Start\n")); + + if (Event != NULL) { + gBS->CloseEvent (Event); + } + +#ifndef FSP_WRAPPER_FLAG + if (mPchConfigHob->Usb.DelayPdoProgramming == TRUE) { + XhciPciMmBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_XHCI, + PCI_FUNCTION_NUMBER_PCH_XHCI + ); + + // Check if xHCI has memory access + XhciPciCommand = MmioRead8 (XhciPciMmBase + PCI_COMMAND_OFFSET); + + if ((XhciPciCommand & EFI_PCI_COMMAND_MEMORY_SPACE) == 0x0) { + DEBUG ((DEBUG_ERROR, "xHCI Controller isn't configured properly for PDO setting\n")); + } else { + // Getting base MMIO address for xHCI + XhciMmioBase = ( + MmioRead32 (XhciPciMmBase + R_PCH_XHCI_MEM_BASE) | + ((UINTN) MmioRead32 (XhciPciMmBase + R_PCH_XHCI_MEM_BASE_2) << N_PCH_XHCI_MEM_SHIFT) + ) & ((UINTN)~(B_PCH_XHCI_MEM_ALIGN_MASK)); + + XhciPdoRestartNeeded = UsbPdoProgramming ( + XhciMmioBase, + mPchConfigHob->Usb.Usb2DisabledPorts, + mPchConfigHob->Usb.Usb3DisabledPorts + ); + // If PDO registers are locked, reset platform to unlock them + if (XhciPdoRestartNeeded) { + gST->RuntimeServices->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } + } else { + DEBUG ((DEBUG_INFO, "PDO register already programmed\n")); + } +#endif // FSP_WRAPPER_FLAG + + // + // Trigger an Iotrap SMI to config PCIE power management setting after PCI enumrate is done + // + if (mPcieIoTrapAddress != 0) { + IoWrite32 ((UINTN) mPcieIoTrapAddress, PciePmTrap); + } else { + ASSERT (FALSE); + } + + DEBUG ((DEBUG_INFO, "Uefi PchOnReadyToBoot() End\n")); +} + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.inf b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.inf new file mode 100644 index 0000000000..0242dcbe9a --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxe.inf @@ -0,0 +1,101 @@ +## @file +# Component description file for Pch Initialization driver +# +# 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 which 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. +# +## + + +[Defines] +INF_VERSION = 0x00010017 +BASE_NAME = PchInitDxe +FILE_GUID = DE23ACEE-CF55-4fb6-AA77-984AB53DE823 +VERSION_STRING = 1.0 +MODULE_TYPE = DXE_DRIVER +ENTRY_POINT = PchInitEntryPointDxe + + +[LibraryClasses] +S3BootScriptLib +PchCycleDecodingLib +PchPcieRpLib +PchP2sbLib +PchPcrLib +PchInfoLib +PchPciExpressHelpersLib +UefiBootServicesTableLib +DebugLib +IoLib +TimerLib +HobLib +BaseMemoryLib +MemoryAllocationLib +UefiLib +DxeServicesTableLib +UefiDriverEntryPoint +UefiRuntimeServicesTableLib +AslUpdateLib +CpuPlatformLib +GpioLib +PchSerialIoLib +PchHdaLib +PchInitCommonLib +ConfigBlockLib +PchPsfLib +PchPsfPrivateLib +RstPrivateLib + +[Packages] +MdePkg/MdePkg.dec +KabylakeSiliconPkg/SiPkg.dec +KabylakeSiliconPkg/KabylakeSiliconPrivate.dec + + +[Pcd] +gSiPkgTokenSpaceGuid.PcdEfiGcdAllocateType + + +[Sources] +PchInitDxe.c +PchInit.h +PchInit.c +PchSata.c +PchRstPcieStorage.c +PchSerialIo.c +PchSerialIoDxe.c +PchHdaAcpi.c +PchScs.c +PchCio2Acpi.c +PchAcpi.c + + +[Protocols] +gPchInfoProtocolGuid ## PRODUCES +gPchNvsAreaProtocolGuid ## PRODUCES +gPchEmmcTuningProtocolGuid ## PRODUCES +gEfiPciIoProtocolGuid ## CONSUMES +gEfiAcpiTableProtocolGuid ## CONSUMES +gEfiBlockIoProtocolGuid ## CONSUMES +gEfiPciEnumerationCompleteProtocolGuid ## CONSUMES +gPchPcieIoTrapProtocolGuid ## CONSUMES + + +[Guids] +gEfiEndOfDxeEventGroupGuid +gEfiAcpiTableGuid +gSiConfigHobGuid ## CONSUMES +gPchConfigHobGuid ## CONSUMES +gPchPcieStorageDetectHobGuid ## CONSUMES + + +[Depex] +gEfiPciHostBridgeResourceAllocationProtocolGuid ## This is to ensure that PCI MMIO and IO resource has been prepared and available for this driver to allocate. + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxeFsp.inf b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxeFsp.inf new file mode 100644 index 0000000000..18feffd894 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitDxeFsp.inf @@ -0,0 +1,80 @@ +## @file +# Component description file for Pch Initialization driver for FSP package +# +# 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 which 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. +# +## + + +[Defines] +INF_VERSION = 0x00010005 +BASE_NAME = PchInitDxe +FILE_GUID = 5AA5031E-4CB6-43D4-B219-FE50FF5D116C +MODULE_TYPE = PEIM +VERSION_STRING = 1.0 +ENTRY_POINT = PchInitEntryPointFsp + + +[LibraryClasses] +PeimEntryPoint +PchCycleDecodingLib +PchPcieRpLib +PchP2sbLib +PchPcrLib +PchInfoLib +PchEspiLib +PchPciExpressHelpersLib +DebugLib +IoLib +TimerLib +HobLib +BaseMemoryLib +MemoryAllocationLib +CpuPlatformLib +GpioLib +PchSerialIoLib +PchInitCommonLib +S3BootScriptLib # NULL library +ConfigBlockLib +PchPsfLib +PchPsfPrivateLib + +[Packages] +MdePkg/MdePkg.dec +KabylakeSiliconPkg/SiPkg.dec +KabylakeSiliconPkg/KabylakeSiliconPrivate.dec + + +[Pcd] +gSiPkgTokenSpaceGuid.PcdEfiGcdAllocateType + + +[Sources] +PchInitFsp.c +PchInit.h +PchInit.c +PchSata.c +PchSerialIo.c + + +[Protocols] +gEfiPciEnumerationCompleteProtocolGuid ## CONSUMES + + +[Guids] +gEfiEventReadyToBootGuid +gSiConfigHobGuid ## CONSUMES +gPchConfigHobGuid ## CONSUMES + + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitFsp.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitFsp.c new file mode 100644 index 0000000000..9307b8fc4c --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchInitFsp.c @@ -0,0 +1,169 @@ +/** @file + This is the FSP 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" + +EFI_STATUS +EFIAPI +PchOnPciEnumCompleteFsp ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +STATIC +EFI_PEI_NOTIFY_DESCRIPTOR mPchOnPciEnumCompleteNotifyList[] = { + { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPciEnumerationCompleteProtocolGuid, + PchOnPciEnumCompleteFsp + } +}; + +EFI_STATUS +EFIAPI +PchReadyToBootEventFsp ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +STATIC EFI_PEI_NOTIFY_DESCRIPTOR mReadyToBootNotifyList[] = { + { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiEventReadyToBootGuid, + PchReadyToBootEventFsp + } +}; + + +/** + FSP PchInit Module Entry Point for FSP\n + + @param[in] FileHandle PEIM's file handle + @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Do not have enough resources to initialize the driver +**/ +EFI_STATUS +EFIAPI +PchInitEntryPointFsp ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "PchInitEntryPointFsp() Start\n")); + + PchInitEntryPointCommon (); + + Status = PeiServicesNotifyPpi (mPchOnPciEnumCompleteNotifyList); + ASSERT_EFI_ERROR (Status); + + Status = PeiServicesNotifyPpi (mReadyToBootNotifyList); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "PchInitEntryPointFsp() End\n")); + + return Status; +} + +/** + Fsp PCH initialization on PCI enumeration complete + + @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation + @param[in] NotifyDescriptor Address of the notification descriptor data structure. + @param[in] Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Do not have enough resources to initialize the driver +**/ +EFI_STATUS +EFIAPI +PchOnPciEnumCompleteFsp ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + DEBUG ((DEBUG_INFO, "PchOnPciEnumCompleteFsp() Start\n")); + + PchOnPciEnumCompleteCommon (); + + DEBUG ((DEBUG_INFO, "PchOnPciEnumCompleteFsp() End\n")); + + return EFI_SUCCESS; +} + +/** + FSP PCH Ready to boot event handler + + @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation + @param[in] NotifyDescriptor Address of the notification descriptor data structure. + @param[in] Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Do not have enough resources to initialize the driver +**/ +EFI_STATUS +EFIAPI +PchReadyToBootEventFsp ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + UINTN XhciMmioBase; + UINTN XhciPciMmBase; + UINT16 XhciPciCommand; + BOOLEAN XhciPdoRestartNeeded; + + DEBUG ((DEBUG_INFO, "PchReadyToBootEventFsp() Start\n")); + + // Check if USB PDO programming was skipped during PEI phase + if (mPchConfigHob->Usb.DelayPdoProgramming == TRUE) { + XhciPciMmBase = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_XHCI, + PCI_FUNCTION_NUMBER_PCH_XHCI + ); + + // Check if xHCI has memory access + XhciPciCommand = MmioRead16 (XhciPciMmBase + PCI_COMMAND_OFFSET); + if ((XhciPciCommand & EFI_PCI_COMMAND_MEMORY_SPACE) == 0x0) { + DEBUG ((DEBUG_ERROR, "xHCI Controller isn't configured properly\n")); + } else { + // Getting base MMIO address for xHCI + XhciMmioBase = MmioRead32 (XhciPciMmBase + R_PCH_XHCI_MEM_BASE) & (B_PCH_XHCI_MEM_ALIGN_MASK << N_PCH_XHCI_MEM_ALIGN); + + XhciPdoRestartNeeded = UsbPdoProgramming ( + XhciMmioBase, + mPchConfigHob->Usb.Usb2DisabledPorts, + mPchConfigHob->Usb.Usb3DisabledPorts + ); + // If PDO registers are locked, reset platform to unlock them + if (XhciPdoRestartNeeded) { + (*PeiServices)->ResetSystem2 (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } + } else { + DEBUG ((DEBUG_INFO, "PDO register already programmed\n")); + } + + DEBUG ((DEBUG_INFO, "PchReadyToBootEventFsp() End\n")); + + return EFI_SUCCESS; +} + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchRstPcieStorage.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchRstPcieStorage.c new file mode 100644 index 0000000000..801abf2602 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchRstPcieStorage.c @@ -0,0 +1,286 @@ +/** @file + Configures the remapping for PCH Intel RST PCie Storage + In order to use this feature, Intel RST Driver is required + +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 +#include + +// +// Variables below indicate the memory range to be allocated to the PCIe storage device BAR usage when it is HIDDEN, +// thus it does not overlapped with the SGREG_BAR from the host perspective +// +#define RST_PCIE_STORAGE_MEMORY_START_RANGE PCH_PCR_BASE_ADDRESS +#define RST_PCIE_STORAGE_MEMORY_END_RANGE (PCH_PCR_BASE_ADDRESS + PCH_PCR_MMIO_SIZE - 1) + +#define PCI_CARD_PM_CAP_ID 0x01 +#define PCI_CARD_MSIX_CAP_ID 0x11 +#define PCI_CARD_SATA_CAP_ID 0x12 +#define PCI_CARD_BAR_TOTAL 6 +#define PCI_CARD_LINK_SPEED_GEN1_GEN2 0 +#define PCI_CARD_LINK_SPEED_GEN3 1 +#define PCI_CARD_LINK_WIDTH_1 0 +#define PCI_CARD_LINK_WIDTH_2 1 +#define PCI_CARD_LINK_WIDTH_4 2 +#define PCI_CARD_REVISION 0x08 +#define PCI_CARD_BASE_ADDR0 0x10 +#define PCI_CARD_BASE_ADDR1 0x14 +#define PCI_CARD_BASE_ADDR5 0x24 + +GLOBAL_REMOVE_IF_UNREFERENCED PCH_RST_PCIE_STORAGE_DETECTION mRstPcieStorageDetection [PCH_MAX_RST_PCIE_STORAGE_CR]; + +UINT32 PchRstPcieStorageCurrentMemoryRange = RST_PCIE_STORAGE_MEMORY_START_RANGE; + + +/** + Function to perform dump for PCH_RST_PCIE_STORAGE_DETECTION + + @param[in] Index Index for RST PCIe Storage Cycle Router Instance + + @retval None +**/ +VOID +PchRstPcieStorageRemappingDump ( + IN UINTN Index + ) +{ + DEBUG_CODE_BEGIN (); + DEBUG ((DEBUG_INFO, "PchRstPcieStorageRemappingDump() Started\n")); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].SupportRstPcieStoragRemapping = %x\n", Index, mRstPcieStorageDetection[Index].SupportRstPcieStoragRemapping)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].RootPortNum = %x\n", Index, mRstPcieStorageDetection[Index].RootPortNum)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].RootPortLane = %x\n", Index, mRstPcieStorageDetection[Index].RootPortLane)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].DeviceInterface = %x\n", Index, mRstPcieStorageDetection[Index].DeviceInterface)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].IsMsixSupported = %x\n", Index, mRstPcieStorageDetection[Index].IsMsixSupported)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].MsixStartingVector = %x\n", Index, mRstPcieStorageDetection[Index].MsixStartingVector)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].MsixEndingVector = %x\n", Index, mRstPcieStorageDetection[Index].MsixEndingVector)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointBarSize = %x\n", Index, mRstPcieStorageDetection[Index].EndPointBarSize)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointUniqueMsixTableBar = %x\n", Index, mRstPcieStorageDetection[Index].EndPointUniqueMsixTableBar)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointUniqueMsixTableBarValue = %x\n", Index, mRstPcieStorageDetection[Index].EndPointUniqueMsixTableBarValue)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointUniqueMsixPbaBar = %x\n", Index, mRstPcieStorageDetection[Index].EndPointUniqueMsixPbaBar)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointUniqueMsixPbaBarValue = %x\n", Index, mRstPcieStorageDetection[Index].EndPointUniqueMsixPbaBarValue)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointBcc = %x\n", Index, mRstPcieStorageDetection[Index].EndPointBcc)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointScc = %x\n", Index, mRstPcieStorageDetection[Index].EndPointScc)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].EndPointPi = %x\n", Index, mRstPcieStorageDetection[Index].EndPointPi)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.PmCapPtr = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.PmCapPtr)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.PcieCapPtr = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.PcieCapPtr)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.L1ssCapPtr = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.L1ssCapPtr)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.EndpointL1ssControl2 = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointL1ssControl2)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.EndpointL1ssControl1 = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointL1ssControl1)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.LtrCapPtr = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.LtrCapPtr)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.EndpointLtrData = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointLtrData)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.EndpointLctlData16 = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointLctlData16)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.EndpointDctlData16 = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointDctlData16)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.EndpointDctl2Data16 = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.EndpointDctl2Data16)); + DEBUG ((DEBUG_INFO, "mRstPcieStorageDetection[%d].PchRstPcieStorageSaveRestore.RootPortDctl2Data16 = %x\n", Index, mRstPcieStorageDetection[Index].PchRstPcieStorageSaveRestore.RootPortDctl2Data16)); + DEBUG ((DEBUG_INFO, "PchRstPcieStorageRemappingDump() Ended\n")); + DEBUG_CODE_END (); +} + +/** + Perform memory space allocation or free memory pool for AHCI BAR + + @param[in] Operation True: Perform memory space allocation for AHCI BAR + False: Perform memory pool free for AHCI BAR + @param[in,out] MemBaseAddress The base memory address used by AHCI BAR +**/ +VOID +AbarMemorySpaceOperation ( + IN BOOLEAN Operation, + IN OUT EFI_PHYSICAL_ADDRESS *MemBaseAddress + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINTN AhciBarAlignment; + UINTN AhciBarLength; + + // + // Assign a 512k size memory space to the ABAR when NVM Remapping is enabled + // + AhciBarAlignment = N_PCH_SATA_AHCI_BAR_ALIGNMENT_512K; // 2^19: 512K Alignment + AhciBarLength = V_PCH_SATA_AHCI_BAR_LENGTH_512K; // 512K Length + + + if (Operation == TRUE) { + /// + /// If MemBaseAddress is not allocated yet, allocate the AHCI BAR + /// + if ((PcdGet8 (PcdEfiGcdAllocateType) == EfiGcdAllocateMaxAddressSearchBottomUp) || (PcdGet8 (PcdEfiGcdAllocateType) == EfiGcdAllocateMaxAddressSearchTopDown)) { + BaseAddress = 0x0ffffffff; + } + Status = gDS->AllocateMemorySpace ( + PcdGet8 (PcdEfiGcdAllocateType), + EfiGcdMemoryTypeMemoryMappedIo, + AhciBarAlignment, + AhciBarLength, + &BaseAddress, + mImageHandle, + NULL + ); + ASSERT_EFI_ERROR (Status); + *MemBaseAddress = BaseAddress; + } else { + /// + /// Else, free the GCD pool + /// + gDS->FreeMemorySpace ( + *MemBaseAddress, + AhciBarLength + ); + } +} + +/** + Function to perform memory allocation for PCIe storage device that support unique BAR + + @param[in] BarSize The BAR size required for memory allocation + @param[in,out] AllocAddr The Address that been allocated by this function + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Memory or storage is not enough +**/ +EFI_STATUS +PchRstPcieStorageMemAllocation ( + IN UINT32 BarSize, + IN OUT UINT32 *AllocAddr + ) +{ + if ((PchRstPcieStorageCurrentMemoryRange + BarSize) > RST_PCIE_STORAGE_MEMORY_END_RANGE) { + return EFI_OUT_OF_RESOURCES; + } + + if ((PchRstPcieStorageCurrentMemoryRange & (BarSize - 1)) != 0) { + *AllocAddr = (PchRstPcieStorageCurrentMemoryRange + BarSize) & ~(BarSize-1); + } else { + *AllocAddr = PchRstPcieStorageCurrentMemoryRange; + } + + PchRstPcieStorageCurrentMemoryRange = *AllocAddr + BarSize; + return EFI_SUCCESS; +} + +/** + Check and detect PCIe storage device per policies, and if existed, indicate if it should be subjected for remapping + Populate the mRstPcieStorageDetection structure along with it for later usages + + @param[in, out] RemappingRequired Boolean value with TRUE indicates that RST PCIe Storage Remapping is required + @param[in] TempPciBusMin The temporary minimum Bus number for root port initialization + @param[in] SataRegBase SATA Register Base + + @retval None +**/ +VOID +DetectPcieStorageDevices ( + IN OUT BOOLEAN *RemappingRequired, + IN UINT8 TempPciBusMin, + IN UINTN SataRegBase + ) +{ + EFI_STATUS Status; + PCH_SERIES PchSeries; + UINT8 DeviceInterface; + UINT32 PortNum; + UINTN PciRootPortRegBase; + UINTN RpDev; + UINTN RpFunc; + UINT8 RootPortLane; + PCIE_STORAGE_INFO_HOB *PcieStorageInfoHob; + VOID *Hob; + UINT32 CycleRouterNum; + + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: DetectPcieStorageDevices Started\n")); + + Hob = NULL; + Hob = GetFirstGuidHob (&gPchPcieStorageDetectHobGuid); + if (Hob != NULL) { + PcieStorageInfoHob = (PCIE_STORAGE_INFO_HOB *) GET_GUID_HOB_DATA (Hob); + } else { + return; + } + + PchSeries = GetPchSeries (); + + for (PortNum = 0; PortNum < GetPchMaxPciePortNum (); PortNum++) { + + // + // Check Info hob for Cycle Router avaliable + // skip to next root port if Cr is not avaliable + // + CycleRouterNum = PcieStorageInfoHob->RstCycleRouterMap[PortNum / 4]; + if (CycleRouterNum == RST_PCIE_STORAGE_CR_INVALID) { + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: cycle router not avaliable on Rp = %d\n", PortNum)); + continue; + } + + Status = GetPchPcieRpDevFun (PortNum, &RpDev, &RpFunc); + ASSERT_EFI_ERROR (Status); + + PciRootPortRegBase = MmPciBase (DEFAULT_PCI_BUS_NUMBER_PCH, (UINT32) RpDev, (UINT32) RpFunc); + RootPortLane = RstGetRpLaneOccupyMask (PortNum); + + + if (RootPortLane == 0) { + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: root port lane is not occupied\n")); + continue; + } + + /// + /// Check if the RST PCIe Storage Cycle Router is enabled, continue to the next root port in the next x4 lane if it is disabled + /// + if (mPchConfigHob->Sata.RstPcieStorageRemap[CycleRouterNum].Enable == 0) { + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: RST PCIe Storage Cycle Router %d is disabled\n", CycleRouterNum)); + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: Proceed to root port in the next x4 lane (@ port %d)\n", PortNum + 1)); + PortNum += 3 - (PortNum % 4); + continue; + } + + /// + /// Check if the current root port is matching selected root port from policy, continue to next port if they are not matched + /// + if ((mPchConfigHob->Sata.RstPcieStorageRemap[CycleRouterNum].RstPcieStoragePort != 0) && + (mPchConfigHob->Sata.RstPcieStorageRemap[CycleRouterNum].RstPcieStoragePort != PortNum + 1)) { + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: root port %d not matched with selected root port, proceed to next port\n", PortNum + 1)); + continue; + } + + + // Read device programming interface from info hob + // if no device is present continue + if (PcieStorageInfoHob->PcieStorageLinkWidth[PortNum]) { + DeviceInterface = PcieStorageInfoHob->PcieStorageProgrammingInterface[PortNum]; + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: Device present on RP# %d, PiInterface = %d\n", PortNum + 1, DeviceInterface)); + } else { + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: RP# %d no device present\n", PortNum + 1)); + continue; + } + + + /// + /// Update the remapping detail for detected PCIe storage device, and move to the root port in the next x4 lane + /// + if ((DeviceInterface != RST_PCIE_STORAGE_INTERFACE_NONE)) { + mRstPcieStorageDetection[CycleRouterNum].SupportRstPcieStoragRemapping = TRUE; + mRstPcieStorageDetection[CycleRouterNum].RootPortNum = (UINT8) PortNum; + mRstPcieStorageDetection[CycleRouterNum].RootPortLane = RootPortLane; + mRstPcieStorageDetection[CycleRouterNum].DeviceInterface = DeviceInterface; + PortNum += 3 - (PortNum % 4); + *RemappingRequired = TRUE; + } else { + continue; + } + } + + DEBUG ((DEBUG_INFO, "DetectPcieStorageDevices: DetectPcieStorageDevices() Ended\n")); +} + + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSata.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSata.c new file mode 100644 index 0000000000..d1f2948de6 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSata.c @@ -0,0 +1,125 @@ +/** @file + Perform related functions for PCH Sata in DXE phase + +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 + +/** + Perform the remaining configuration on PCH SATA to perform device detection, + then set the SATA SPD and PxE corresponding, and set the Register Lock on PCH SATA + + @retval None +**/ +VOID +ConfigurePchSataOnEndOfDxe ( + VOID + ) +{ + UINTN PciSataRegBase; + UINT16 SataPortsEnabled; + PCH_SERIES PchSeries; + UINT16 WordReg; + UINT32 DwordReg; + UINTN Index; + + /// + /// SATA PCS: Enable the port in any of below condition: + /// i.) Hot plug is enabled + /// ii.) A device is attached + /// iii.) Test mode is enabled + /// iv.) Configured as eSATA port + /// + PciSataRegBase = MmPciBase (DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SATA, PCI_FUNCTION_NUMBER_PCH_SATA); + PchSeries = GetPchSeries (); + SataPortsEnabled = 0; + + if (MmioRead16 (PciSataRegBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) { + return; + } + if (PchSeries == PchLp) { + WordReg = MmioRead16 (PciSataRegBase + R_PCH_LP_SATA_PCS); + for (Index = 0; Index < GetPchMaxSataPortNum (); Index++) { + if ((mPchConfigHob->Sata.PortSettings[Index].HotPlug == TRUE) || + (WordReg & (B_PCH_LP_SATA_PCS_P0P << Index)) || + (mPchConfigHob->Sata.TestMode == TRUE) || + (mPchConfigHob->Sata.PortSettings[Index].External == TRUE)) { + SataPortsEnabled |= (mPchConfigHob->Sata.PortSettings[Index].Enable << Index); + } + } + + /// + /// Set MAP."Sata PortX Disable", SATA PCI offset 90h[10:8] to 1b if SATA Port 0/1/2 is disabled + /// + MmioOr16 (PciSataRegBase + R_PCH_LP_SATA_MAP, ((~SataPortsEnabled << N_PCH_LP_SATA_MAP_SPD) & (UINT16) B_PCH_LP_SATA_MAP_SPD)); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint16, + (UINTN) (PciSataRegBase + R_PCH_LP_SATA_MAP), + 1, + (VOID *) (UINTN) (PciSataRegBase + R_PCH_LP_SATA_MAP) + ); + + /// + /// Program PCS "Port X Enabled", SATA PCI offset 92h[2:0] = Port 0~2 Enabled bit as per SataPortsEnabled value. + /// + MmioOr16 (PciSataRegBase + R_PCH_LP_SATA_PCS, SataPortsEnabled); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint16, + (UINTN) (PciSataRegBase + R_PCH_LP_SATA_PCS), + 1, + (VOID *) (UINTN) (PciSataRegBase + R_PCH_LP_SATA_PCS) + ); + } else { + DwordReg = MmioRead32 (PciSataRegBase + R_PCH_H_SATA_PCS); + for (Index = 0; Index < GetPchMaxSataPortNum (); Index++) { + if ((mPchConfigHob->Sata.PortSettings[Index].HotPlug == TRUE) || + (DwordReg & (B_PCH_H_SATA_PCS_P0P << Index)) || + (mPchConfigHob->Sata.TestMode == TRUE) || + (mPchConfigHob->Sata.PortSettings[Index].External == TRUE)) { + SataPortsEnabled |= (mPchConfigHob->Sata.PortSettings[Index].Enable << Index); + } + } + + /// + /// Set MAP."Sata PortX Disable", SATA PCI offset 90h[23:16] to 1b if SATA Port 0/1/2/3/4/5/6/7 is disabled + /// + MmioOr32 (PciSataRegBase + R_PCH_H_SATA_MAP, ((~SataPortsEnabled << N_PCH_H_SATA_MAP_SPD) & (UINT32) B_PCH_H_SATA_MAP_SPD)); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PciSataRegBase + R_PCH_H_SATA_MAP), + 1, + (VOID *) (UINTN) (PciSataRegBase + R_PCH_H_SATA_MAP) + ); + + /// + /// Program PCS "Port X Enabled", SATA PCI offset 94h[7:0] = Port 0~7 Enabled bit as per SataPortsEnabled value. + /// + MmioOr16 (PciSataRegBase + R_PCH_H_SATA_PCS, SataPortsEnabled); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint16, + (UINTN) (PciSataRegBase + R_PCH_H_SATA_PCS), + 1, + (VOID *) (UINTN) (PciSataRegBase + R_PCH_H_SATA_PCS) + ); + } + + /// + /// Step 14 + /// Program SATA PCI offset 9Ch [31] to 1b + /// + MmioOr32 ((UINTN) (PciSataRegBase + R_PCH_SATA_SATAGC), BIT31); + S3BootScriptSaveMemWrite ( + S3BootScriptWidthUint32, + (UINTN) (PciSataRegBase + R_PCH_SATA_SATAGC), + 1, + (VOID *) (UINTN) (PciSataRegBase + R_PCH_SATA_SATAGC) + ); +} diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchScs.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchScs.c new file mode 100644 index 0000000000..34227826b4 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchScs.c @@ -0,0 +1,1869 @@ +/** @file + Initializes PCH Storage and Communications Subsystem Controllers. + +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 + +// +// HS400 Tuning Definitions +// +#define RX_STROBE_DLL1_TAP_MAX_RANGE 39 +#define RX_STROBE_DLL1_TAP_MIN_RANGE 0 +#define RX_STROBE_DLL1_TAP_MIN_MEPT 5 +#define RX_STROBE_DLL1_TAP_MAX_MEPT 16 +#define TX_DATA_DLL_TAP_MAX_RANGE 79 +#define TX_DATA_DLL_TAP_MIN_RANGE 0 +#define TX_DATA_DLL_TAP_MIN_MEPT 4 +#define TX_DATA_DLL_TAP_MAX_MEPT 22 + +// +// Command Definitions +// +#define CMD6 6 +#define CMD8 8 +#define CMD13 13 +#define CMD31 31 +#define SWITCH CMD6 +#define SEND_EXT_CSD CMD8 +#define SEND_STATUS CMD13 +#define SEND_WRITE_PROT_TYPE CMD31 +#define WRITE_BYTE_MODE 3 +#define BLOCK_LENGTH 512 +#define TIMEOUT_COMMAND 100 +#define TIMEOUT_DATA 5000 +#define HS_TIMING_INDEX 185 +#define BUS_WIDTH_INDEX 183 + +// +// Card Status Definitions +// +#define SWITCH_ERROR BIT7 +#define ERASE_RESET BIT13 +#define WP_ERASE_SKIP BIT15 +#define CID_CSD_OVERWRITE BIT16 +#define ERROR BIT19 +#define CC_ERROR BIT20 +#define CARD_ECC_FAILED BIT21 +#define ILLEGAL_COMMAND BIT22 +#define COM_CRC_ERROR BIT23 +#define LOCK_UNLOCK_FAILED BIT24 +#define CARD_IS_LOCKED BIT25 +#define WP_VIOLATION BIT26 +#define ERASE_PARAM BIT27 +#define ERASE_SEQ_ERROR BIT28 +#define BLOCK_LEN_ERROR BIT29 +#define ADDRESS_MISALIGN BIT30 +#define ADDRESS_OUT_OF_RANGE BIT31 + +typedef enum { + Hs200 = 0, + Hs400, + DDR50, + SDR25 +} EMMC_MODE; + +typedef enum { + RxDll1 = 0, + RxDll2 +} RX_STROBE_DLL_REG; + +typedef enum { + NotAvailable = 0, + Passed, + Failed +} BLOCK_READ_WRITE_STATUS; + +typedef enum { + ResponseNo = 0, + ResponseR1, + ResponseR1b, + ResponseR2, + ResponseR3, + ResponseR4, + ResponseR5, + ResponseR5b, + ResponseR6, + ResponseR7 +} RESPONSE_TYPE; + +typedef enum { + NoData = 0, + InData, + OutData +} TRANSFER_TYPE; + +typedef struct { + UINT32 CmdSet: 3; + UINT32 Reserved0: 5; + UINT32 Value: 8; + UINT32 Index: 8; + UINT32 Access: 2; + UINT32 Reserved1: 6; +} SWITCH_ARGUMENT; + +// +// PCH_EMMC_TUNING PROTOCOL Global Variable +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_EMMC_TUNING_PROTOCOL PchEmmcTuningProtocol = { + ConfigureEmmcHs400Mode +}; + +#define EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER 5 + +GLOBAL_REMOVE_IF_UNREFERENCED +CONST UINT32 EmmcWorstCasePattern[] = +{ +0x60FEFE00, 0xEDFF10FE, 0x60FEFE00, 0xEDFF10FE, 0xDE08216A, 0xE25F20DF, 0xDE08216A, 0xE25F20DF, // spt_a0_sandisk_50ohm +0xFF00CFFF, 0x1014B5CF, 0xFF00CFFF, 0x1014B5CF, 0x225F25DF, 0x20DFDE61, 0x225F25DF, 0x20DFDE61, // spt_a0_sandisk_40ohm +0xEFE770BD, 0x4AFF0000, 0xEFE770BD, 0x4AFF0000, 0x7B84FF5B, 0x5B655B0E, 0x7B84FF5B, 0x5B655B0E, // spt_a0_hynix_50ohm +0x7B93D620, 0xDFFF00E8, 0x7B93D620, 0xDFFF00E8, 0x3DC2FF3D, 0x38EA7DF3, 0x3DC2FF3D, 0x38EA7DF3, // spt_a0_hynix_40ohm +0x50127FAC, 0x4400FD00, 0x50127FAC, 0x4400FD00, 0x7887FF78, 0x109A394F, 0x7887FF78, 0x109A394F, // spt_a0_hynix_33ohm +0x50F20DBF, 0x3832CDFD, 0x50F20DBF, 0x3832CDFD, 0xFB04D720, 0xE69DC704, 0xFB04D720, 0xE69DC704, // spt_a0_samsung19nm_33ohm +0x40EFFF00, 0x40FFFF00, 0x40EFFF00, 0x40FFFF00, 0xE85F20DF, 0x20DF003F, 0xE85F20DF, 0x20DF003F, // spt_a0_toshiba_50ohm +0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0x20DFA0FF, 0x58DB20DF, 0x20DFA0FF, 0x58DB20DF, // spt_a0_toshiba_40ohm +0x50FF0008, 0x7E10FFFF, 0x50FF0008, 0x7E10FFFF, 0x3B8F7A85, 0xFF7A5AFF, 0x3B8F7A85, 0xFF7A5AFF, // spt_b0_hynix_40ohm +0x8A7575FF, 0x9ACA7F54, 0x8A7575FF, 0x9ACA7F54, 0xDF649B92, 0x7B24AFA0, 0xDF649B92, 0x7B24AFA0, // spt_b0_samsung16nm_50ohm +0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xDAD6EF50, 0x6B1C6B25, 0xDAD6EF50, 0x6B1C6B25, // spt_b0_samsung16nm_40ohm +0x7f52ff02, 0xff02ff02, 0x7f52ff02, 0xff02ff02, 0x72bc63a5, 0x679afa9d, 0x72bc63a5, 0x679afa9d, // spt_b0_samsung16nm_33ohm + +0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, // baseline_pattern_8 +0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, +0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, +0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00 +}; + +GLOBAL_REMOVE_IF_UNREFERENCED +CONST UINT32 EmmcWorstCasePatternSize = sizeof (EmmcWorstCasePattern); + + + +/** + Set max clock frequency of the host, the actual frequency may not be the same + as MaxFrequency. It depends on the max frequency the host can support, divider, + and host speed mode. + + @param[in] This Pointer to EFI_SD_HOST_IO_PROTOCOL + @param[in] MaxFrequency Max frequency in HZ + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_TIMEOUT The timeout time expired. +**/ +EFI_STATUS +EFIAPI +SetClockFrequency ( + IN UINTN EmmcBaseAddress, + IN UINT32 MaxFrequency + ) +{ + UINT16 Data16; + UINT32 Frequency; + UINT32 Divider = 0; + UINT32 TimeOutCount; + UINT32 CurrentClockInKHz; + + DEBUG ((DEBUG_INFO, "SetClockFrequency: BaseClockInMHz = %d \n", 200)); + + Frequency = (200 * 1000 * 1000) / MaxFrequency; + DEBUG ((DEBUG_INFO, "SetClockFrequency: FrequencyInHz = %d \n", Frequency)); + + if ((200 * 1000 * 1000 % MaxFrequency) != 0) { + Frequency += 1; + } + + Divider = 1; + while (Frequency > Divider) { + Divider = Divider * 2; + } + if (Divider >= 0x400) { + Divider = 0x200; + } + Divider = Divider >> 1; + + DEBUG ((DEBUG_INFO, "SetClockFrequency: after shift: Base Clock Divider = 0x%x \n", Divider)); + + CurrentClockInKHz = (200 * 1000); + if (Divider != 0) { + CurrentClockInKHz = CurrentClockInKHz / (Divider * 2); + } + // + //Set frequency + // Bit[15:8] SDCLK Frequency Select at offset 2Ch + // 80h - base clock divided by 256 + // 40h - base clock divided by 128 + // 20h - base clock divided by 64 + // 10h - base clock divided by 32 + // 08h - base clock divided by 16 + // 04h - base clock divided by 8 + // 02h - base clock divided by 4 + // 01h - base clock divided by 2 + // 00h - Highest Frequency the target support (10MHz-63MHz) + // + // Bit [07:06] are assigned to bit 09-08 of clock divider in SDCLK Frequency Select on SD controller 3.0 + // + + Data16 = (UINT16) ((Divider & 0xFF) << 8 | (((Divider & 0xFF00) >> 8) << 6)); + + DEBUG ((DEBUG_INFO, + "SetClockFrequency: base=%dMHz, clkctl=0x%04x, f=%dKHz\n", + 200, + Data16, + CurrentClockInKHz + )); + DEBUG ((DEBUG_INFO, "SetClockFrequency: set MMIO_CLKCTL value = 0x%x \n", Data16)); + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, Data16); + Data16 |= BIT0; + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, Data16); + TimeOutCount = 1000; + do { + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL); + MicroSecondDelay (100); + TimeOutCount--; + if (TimeOutCount == 0) { + DEBUG ((DEBUG_INFO, "SetClockFrequency: Timeout\n")); + return EFI_TIMEOUT; + } + } while ((Data16 & BIT1) != BIT1); + + Data16 |= BIT2; + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, Data16); + return EFI_SUCCESS; +} + +/** + Get Error Reason from Host + + @param[in] CommandIndex Command Index which return error + @param[in] ErrorCode Command Error Code + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_TIMEOUT Command Timeout Error + @retval EFI_TIMEOUT Data Timeout Error + @retval EFI_CRC_ERROR Command or Data CRC Error + @retval EFI_DEVICE_ERROR Command End Bit Error + Command Index Error + Data End Bit Error + Current Limit Error + Auto CMD12 Error + ADMA Error +**/ +STATIC +EFI_STATUS +EmmcGetErrorReason ( + IN UINT16 CommandIndex, + IN UINT16 ErrorCode + ) +{ + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_ERROR, "[%2d] -- ", CommandIndex)); + + if (ErrorCode & BIT0) { + Status = EFI_TIMEOUT; + DEBUG ((DEBUG_ERROR, "Command Timeout Error\n")); + } + + if (ErrorCode & BIT1) { + Status = EFI_CRC_ERROR; + DEBUG ((DEBUG_ERROR, "Command CRC Error\n")); + } + + if (ErrorCode & BIT2) { + DEBUG ((DEBUG_ERROR, "Command End Bit Error\n")); + } + + if (ErrorCode & BIT3) { + DEBUG ((DEBUG_ERROR, "Command Index Error\n")); + } + + return Status; +} + +/** + Reset the host CMD and DATA Line + + @param[in] EmmcBaseAddress Base address of MMIO register + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_TIMEOUT The timeout time expired. +**/ +EFI_STATUS +EmmcReset ( + IN UINTN EmmcBaseAddress + ) +{ + UINT8 Data8; + UINT8 ResetType; + UINT16 SaveClkCtl; + UINT32 Data; + UINT32 TimeOutCount; + + ResetType = (B_PCH_SCS_DEV_MEM_SWRST_CMDLINE | B_PCH_SCS_DEV_MEM_SWRST_DATALINE); + + // + // To improve eMMC stability, we zero the R_PCH_SCS_DEV_MEM_CLKCTL register and + // stall for 50 microsecond before reseting the controller. We + // restore the register setting following the reset operation. + // + SaveClkCtl = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL); + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, 0); + MicroSecondDelay (50); + // + // Reset the SD host controller + // + MmioWrite8 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_SWRST, ResetType); + + Data = 0; + TimeOutCount = 1000; // 1 second timeout + do { + MicroSecondDelay (1 * 1000); + + TimeOutCount --; + + Data8 = MmioRead8 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_SWRST); + if ((Data8 & ResetType) == 0) { + break; + } + } while (TimeOutCount > 0); + + // + // We now restore the R_PCH_SCS_DEV_MEM_CLKCTL register which we set to 0 above. + // + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, SaveClkCtl); + + if (TimeOutCount == 0) { + DEBUG ((DEBUG_ERROR, "EmmcReset: Time out \n")); + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + Check card status, print the DEBUG info and check the error + + @param[in] CardStatus Status got from card status register + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_DEVICE_ERROR Device failed during operation +**/ +EFI_STATUS +EmmcCheckCardStatus ( + IN UINT32 CardStatus + ) +{ + + EFI_STATUS Status; + Status = EFI_SUCCESS; + DEBUG ((DEBUG_ERROR, "CardStatus:")); + + if (CardStatus & ADDRESS_OUT_OF_RANGE) { + DEBUG ((DEBUG_ERROR, " ADDRESS_OUT_OF_RANGE")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & ADDRESS_MISALIGN) { + DEBUG ((DEBUG_ERROR, " ADDRESS_MISALIGN")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & BLOCK_LEN_ERROR) { + DEBUG ((DEBUG_ERROR, " BLOCK_LEN_ERROR")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & ERASE_SEQ_ERROR) { + DEBUG ((DEBUG_ERROR, " ERASE_SEQ_ERROR")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & ERASE_PARAM) { + DEBUG ((DEBUG_ERROR, " ERASE_PARAM")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & WP_VIOLATION) { + DEBUG ((DEBUG_ERROR, " WP_VIOLATION")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & CARD_IS_LOCKED) { + DEBUG ((DEBUG_ERROR, " CARD_IS_LOCKED")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & LOCK_UNLOCK_FAILED) { + DEBUG ((DEBUG_ERROR, " LOCK_UNLOCK_FAILED")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & COM_CRC_ERROR) { + DEBUG ((DEBUG_ERROR, " COM_CRC_ERROR")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & ILLEGAL_COMMAND) { + DEBUG ((DEBUG_ERROR, " ILLEGAL_COMMAND")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & CARD_ECC_FAILED) { + DEBUG ((DEBUG_ERROR, " CARD_ECC_FAILED")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & CC_ERROR) { + DEBUG ((DEBUG_ERROR, " CC_ERROR")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & ERROR) { + DEBUG ((DEBUG_ERROR, " ERROR")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & CID_CSD_OVERWRITE) { + DEBUG ((DEBUG_ERROR, " CID_CSD_OVERWRITE")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & WP_ERASE_SKIP) { + DEBUG ((DEBUG_ERROR, " WP_ERASE_SKIP")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & ERASE_RESET) { + DEBUG ((DEBUG_ERROR, " ERASE_RESET")); + Status = EFI_DEVICE_ERROR; + } + + if (CardStatus & SWITCH_ERROR) { + DEBUG ((DEBUG_ERROR, " SWITCH_ERROR")); + Status = EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_ERROR, " \n")); + + return Status; +} +/** + The main function used to send the command to the card inserted into the SD/MMC host + slot. It will assemble the arguments to set the command register and wait for the command + and transfer completed until timeout. Then it will read the response register to fill + the ResponseData. + + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] CommandIndex The command index to set the command index field of command register + @param[in] Argument Command argument to set the argument field of command register + @param[in] DataType TRANSFER_TYPE, indicates no data, data in or data out + @param[in] Buffer Contains the data read from / write to the device + @param[in] BufferSize The size of the buffer + @param[in] ResponseType RESPONSE_TYPE + @param[in] TimeOut Time out value in 1 ms unit + @param[out] ResponseData Depending on the ResponseType, such as CSD or card status + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_OUT_OF_RESOURCES A resource has run out. + @retval EFI_TIMEOUT The timeout time expired. + @retval EFI_DEVICE_ERROR The physical device reported an error while attempting the operation +**/ +EFI_STATUS +SendCommand ( + IN UINTN EmmcBaseAddress, + IN UINT16 CommandIndex, + IN UINT32 Argument, + IN TRANSFER_TYPE DataType, + IN UINT8 *Buffer, OPTIONAL + IN UINT32 BufferSize, + IN RESPONSE_TYPE ResponseType, + IN UINT32 TimeOut, + OUT UINT32 *ResponseData + ) +{ + EFI_STATUS Status; + UINT32 ResponseDataCount; + UINT16 Data16; + UINT32 Data32; + UINT64 Data64; + UINT32 BlockLength; + UINT32 Index; + BOOLEAN CommandCompleted; + BOOLEAN BufferReadReady; + INT32 Timeout; + + Status = EFI_SUCCESS; + BlockLength = BLOCK_LENGTH; + + if (Buffer != NULL && DataType == NoData) { + Status = EFI_INVALID_PARAMETER; + return Status; + } + + // + // Check CMD INHIBIT and DATA INHIBIT before send command + // + Timeout = 1000; + do { + Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_PSTATE); + MicroSecondDelay (100); + } while ((Timeout-- > 0) && (Data32 & BIT0)); + + Timeout = 1000; + do { + Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_PSTATE); + MicroSecondDelay (100); + } while ((Timeout-- > 0) && (Data32 & BIT1)); + + + // + // Enable Interrupts + // + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTEN, B_PCH_SCS_DEV_MEM_NINTEN_MASK); + + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTEN, B_PCH_SCS_DEV_MEM_ERINTEN_MASK); + + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSIGNEN, B_PCH_SCS_DEV_MEM_NINTSIGNEN_MASK); + + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTSIGNEN, B_PCH_SCS_DEV_MEM_ERINTSIGNEN_MASK); + + // + // Clear status bits + // + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_CLEAR_MASK); + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTSTS, B_PCH_SCS_DEV_MEM_ERINTSTS_CLEAR_MASK); + + if (Buffer != NULL) { + Data16 = 0; + if (BufferSize <= BlockLength) { + Data16 |= BufferSize; + } else { + Data16 |= BlockLength; + } + } else { + Data16 = 0; + } + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_BLKSZ, Data16); + + if (Buffer != NULL) { + if (BufferSize <= BlockLength) { + Data16 = 1; + } else { + Data16 = (UINT16) (BufferSize / BlockLength); + } + } else { + Data16 = 0; + } + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_BLKCNT, Data16); + + // + // Argument + // + MmioWrite32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CMDARG, Argument); + + // + // Transfer Mode + // + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_XFRMODE); + + // + // Data Transfer Direction Select + // + Data16 = 0; + if (DataType == InData) { + Data16 |= B_PCH_SCS_DEV_MEM_XFRMODE_DATA_TRANS_DIR; + } + + if (CommandIndex == SEND_EXT_CSD) { + Data16 |= B_PCH_SCS_DEV_MEM_XFRMODE_BLKCNT_EN; + } + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_XFRMODE, Data16); + // + //Command + // + //ResponseTypeSelect IndexCheck CRCCheck ResponseType + // 00 0 0 NoResponse + // 01 0 1 R2 + // 10 0 0 R3, R4 + // 10 1 1 R1, R5, R6, R7 + // 11 1 1 R1b, R5b + // + switch (ResponseType) { + case ResponseNo: + Data16 = (CommandIndex << 8); + ResponseDataCount = 0; + break; + + case ResponseR1: + case ResponseR5: + case ResponseR6: + case ResponseR7: + Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP48 | + B_PCH_SCS_DEV_MEM_SDCMD_CMD_INDEX_CHECK_EN | B_PCH_SCS_DEV_MEM_SDCMD_CMD_CRC_CHECK_EN; + ResponseDataCount = 1; + break; + + case ResponseR1b: + case ResponseR5b: + Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP48_CHK | + B_PCH_SCS_DEV_MEM_SDCMD_CMD_INDEX_CHECK_EN | B_PCH_SCS_DEV_MEM_SDCMD_CMD_CRC_CHECK_EN; + ResponseDataCount = 1; + break; + + case ResponseR2: + Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP136 | + B_PCH_SCS_DEV_MEM_SDCMD_CMD_CRC_CHECK_EN; + ResponseDataCount = 4; + break; + + case ResponseR3: + case ResponseR4: + Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP48; + ResponseDataCount = 1; + break; + + default: + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + return Status; + } + + if (DataType != NoData) { + Data16 |= B_PCH_SCS_DEV_MEM_SDCMD_DATA_PRESENT_SEL; + } + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_SDCMD, Data16); + CommandCompleted = FALSE; + BufferReadReady = FALSE; + TimeOut = 1000; + do { + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTSTS); + if ((Data16 & B_PCH_SCS_DEV_MEM_ERINTSTS_MASK) != 0) { + Status = EmmcGetErrorReason (CommandIndex, Data16); + return Status; + } + + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS) & 0x1ff; + if (Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_CMD_COMPLETE) { + // + // Command completed + // + CommandCompleted = TRUE; + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_CMD_COMPLETE); + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS); + if ((DataType == NoData) & (ResponseType != ResponseR1b)) { + break; + } + } + + if ((CommandCompleted) && (ResponseType == ResponseR1b)) { + Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_PSTATE); + if (Data32 & B_PCH_SCS_DEV_MEM_PSTATE_DAT0) { + break; + } + } + + if ((CommandCompleted) && (Buffer!= NULL)) { + if (!(Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_CMD_COMPLETE)) { + + if (Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_BUF_READ_READY_INTR) { + BufferReadReady = TRUE; + MmioOr16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_BUF_READ_READY_INTR); + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS); + } + + if (BufferReadReady) { + if (!(Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_BUF_READ_READY_INTR)) { + for (Index = 0; Index < BufferSize; Index = Index + 4) { + Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_BUFDATAPORT); + if ((Index + 4) < BufferSize) { + CopyMem ((Buffer + Index), &Data32, 4); + } else { + CopyMem ((Buffer + Index), &Data32, (BufferSize - Index)); + } + } + } + Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS); + } + + if (Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_TRANSFER_COMPLETE) { + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_TRANSFER_COMPLETE); + break; + } + } + } + MicroSecondDelay (1*1000); + + TimeOut --; + + } while (TimeOut > 0); + + if (TimeOut == 0) { + Status = EFI_TIMEOUT; + return Status; + } + + if (ResponseData != NULL) { + UINT32 *ResDataPtr = NULL; + + ResDataPtr = ResponseData; + for (Index = 0; Index < ResponseDataCount; Index++) { + *ResDataPtr = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RESP + Index * 4); + ResDataPtr++; + } + + if (ResponseType == ResponseR2) { + // + // Adjustment for R2 response + // + Data32 = 1; + for (Index = 0; Index < ResponseDataCount; Index++) { + Data64 = LShiftU64 (*ResponseData, 8); + *ResponseData = (UINT32) ((Data64 & 0xFFFFFFFF) | Data32); + Data32 = (UINT32) RShiftU64 (Data64, 32); + ResponseData++; + } + } + } + return Status; +} + +/** + This main function is to send command to Emmc + + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] CommandIndex The command index to set the command index field of command register + @param[in] Argument Command argument to set the argument field of command register + @param[in] DataType TRANSFER_TYPE, indicates no data, data in or data out + @param[in] Buffer Contains the data read from / write to the device + @param[in] BufferSize The size of the buffer + @param[in] ResponseType RESPONSE_TYPE + @param[in] TimeOut Time out value in 1 ms unit + @param[out] ResponseData Depending on the ResponseType, such as CSD or card status + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_OUT_OF_RESOURCES A resource has run out. + @retval EFI_TIMEOUT The timeout time expired. + @retval EFI_DEVICE_ERROR The physical device reported an error while attempting the operation +**/ +EFI_STATUS +EmmcSendCommand ( + IN UINTN EmmcBaseAddress, + IN UINT16 CommandIndex, + IN UINT32 Argument, + IN TRANSFER_TYPE DataType, + IN UINT8 *Buffer, OPTIONAL + IN UINT32 BufferSize, + IN RESPONSE_TYPE ResponseType, + IN UINT32 TimeOut, + OUT UINT32 *ResponseData + ) +{ + EFI_STATUS Status; + + Status = SendCommand ( + EmmcBaseAddress, + CommandIndex, + Argument, + DataType, + Buffer, + BufferSize, + ResponseType, + TimeOut, + ResponseData + ); + + if (!EFI_ERROR (Status)) { + if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) { + ASSERT (ResponseData != NULL); + Status = EmmcCheckCardStatus (*ResponseData); + if (EFI_ERROR (Status)) { + EmmcReset (EmmcBaseAddress); + } + } + } else { + // + // Reset Host Controller CMD and DATA + // + EmmcReset (EmmcBaseAddress); + } + + return Status; +} +/** + Set Tx Data Delay Control 1 + + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] Value Value (0 - 79) + + @retval VOID +**/ +VOID +EmmcSetTxDllCtrl1 ( + IN UINTN EmmcBaseAddress, + IN UINT8 Value + ) +{ + MmioAndThenOr8 (EmmcBaseAddress + (R_PCH_SCS_DEV_MEM_TX_DATA_DLL_CNTL1 + 1), 0, Value); +} + +/** + Set Rx Strobe Delay Control DLL1 for HS400 + + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] RxDll To program RxDll1 or RxDll2 register + @param[in] Value Value (0 - 39) + + @retval VOID +**/ +VOID +EmmcSetRxDllCtrl ( + IN UINTN EmmcBaseAddress, + IN UINT8 RxDll, + IN UINT8 Value + ) +{ + if (RxDll == RxDll1) { + MmioAndThenOr8 (EmmcBaseAddress + (R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL + 1), 0, Value); + } else { + MmioAndThenOr8 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL, 0, Value); + } +} + +/** + Disable eMMC Host HS400 Support + + @param[in] EmmcBaseAddress Base address of MMIO register + + @retval N/A +**/ +VOID +EmmcHostHs400Disabled ( + IN UINTN EmmcBaseAddress + ) +{ + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode()::EmmcHostHs400Disabled()\n")); + MmioAnd32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CAP_BYPASS_REG1, (UINT32) ~B_PCH_SCS_DEV_MEM_CAP_BYPASS_REG1_HS400); +} +/** + Set Host Mode + + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] Mode Set Host Mode, 0: HS200, 1: HS400 + + @retval N/A +**/ +VOID +EmmcSetHostMode ( + IN UINTN EmmcBaseAddress, + IN UINT32 Mode + ) +{ + UINT16 ModeSet; + UINT32 HostCapabilities; + + ModeSet = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_HOST_CTL2); + HostCapabilities = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CAP2); + + ModeSet &= ~B_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_MASK; + if ((Mode == Hs200) && (HostCapabilities & B_PCH_SCS_DEV_MEM_CAP2_SDR104_SUPPORT)) { + ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_SDR104; + } else if ((Mode == Hs400) && (HostCapabilities & B_PCH_SCS_DEV_MEM_CAP2_HS400_SUPPORT)) { + ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_HS400; + } else if (Mode == DDR50) { + ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_DDR50; + } else if (Mode == SDR25) { + ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_SDR25; + } + + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode(): SetHostDdrMode = 0x%x\n", ModeSet)); + + MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_HOST_CTL2, ModeSet); +} + +/** + To select eMMC card operating mode HS200/HS400 + + @param[in] EmmcInfo A pointer to EMMC_INFO structure + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] EmmcMode To select HS200 or HS400 mode + + @retval EFI_SUCCESS Emmc Mode Select successful. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. +**/ +EFI_STATUS +EmmcModeSelection ( + IN EMMC_INFO *EmmcInfo, + IN UINTN EmmcBaseAddress, + IN EMMC_MODE EmmcMode + ) +{ + SWITCH_ARGUMENT SwitchArgument; + UINT32 CardStatus; + UINT32 HsTimingValue; + UINT32 BusWidthValue; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode()::EmmcModeSelection() Start\n")); + + if (EmmcMode == Hs200) { + HsTimingValue = 0x02; + BusWidthValue = EmmcInfo->HS200BusWidth; + DEBUG ((DEBUG_INFO, "HS200 Driver Strength: 50 Ohm\n")); + } else { + DEBUG ((DEBUG_INFO, "HS400 Driver Strength: ")); + switch (mPchConfigHob->Scs.ScsEmmcHs400DriverStrength) { + case DriverStrength33Ohm: + HsTimingValue = 0x13; + DEBUG ((DEBUG_INFO, "33 Ohm\n")); + break; + case DriverStrength40Ohm: + HsTimingValue = 0x43; + DEBUG ((DEBUG_INFO, "40 Ohm\n")); + break; + case DriverStrength50Ohm: + HsTimingValue = 0x03; + DEBUG ((DEBUG_INFO, "50 Ohm\n")); + break; + default: + HsTimingValue = 0x13; + DEBUG ((DEBUG_INFO, "default - 33 Ohm\n")); + } + + BusWidthValue = 6; + } + SetClockFrequency (EmmcBaseAddress, (50 * 1000 * 1000)); // Set 50MHz + // + // 1. Set HS_TIMING to 0x01 for High Speed interface timing. This is required prior DDR 8 bit bus width setting (CMD6) + // + ZeroMem (&SwitchArgument, sizeof (SWITCH_ARGUMENT)); + SwitchArgument.CmdSet = 0; + SwitchArgument.Value = 1; + SwitchArgument.Index = HS_TIMING_INDEX; + SwitchArgument.Access = WRITE_BYTE_MODE; + Status = EmmcSendCommand ( + EmmcBaseAddress, + SWITCH, + *(UINT32*)&SwitchArgument, + NoData, + NULL, + 0, + ResponseR1b, + TIMEOUT_COMMAND, + &CardStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + EmmcSetHostMode (EmmcBaseAddress, SDR25); + + // + // 2. Set desired bus width to DDR 8 bit CMD6 + // + ZeroMem (&SwitchArgument, sizeof (SWITCH_ARGUMENT)); + SwitchArgument.CmdSet = 0; + SwitchArgument.Value = BusWidthValue; + SwitchArgument.Index = BUS_WIDTH_INDEX; + SwitchArgument.Access = WRITE_BYTE_MODE; + Status = EmmcSendCommand ( + EmmcBaseAddress, + SWITCH, + *(UINT32*)&SwitchArgument, + NoData, + NULL, + 0, + ResponseR1b, + TIMEOUT_COMMAND, + &CardStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + EmmcSetHostMode (EmmcBaseAddress, DDR50); + + // + // 3. Set HS400 bit and Driver Strentgh value in HS_TIMING (CMD6) + // + ZeroMem (&SwitchArgument, sizeof (SWITCH_ARGUMENT)); + SwitchArgument.CmdSet = 0; + SwitchArgument.Value = HsTimingValue; + SwitchArgument.Index = HS_TIMING_INDEX; + SwitchArgument.Access = WRITE_BYTE_MODE; + Status = EmmcSendCommand ( + EmmcBaseAddress, + SWITCH, + *(UINT32*)&SwitchArgument, + NoData, + NULL, + 0, + ResponseR1b, + TIMEOUT_COMMAND, + &CardStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (EmmcMode == Hs200) { + DEBUG ((DEBUG_INFO, "Set eMMC Mode: HS200\n")); + EmmcSetHostMode (EmmcBaseAddress, Hs200); + } else { + DEBUG ((DEBUG_INFO, "Set eMMC Mode: HS400\n")); + EmmcSetHostMode (EmmcBaseAddress, Hs400); + } + SetClockFrequency (EmmcBaseAddress, (200 * 1000 * 1000)); // Set 200MHz + + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode()::EmmcModeSelection() End\n")); + return EFI_SUCCESS; +} + +/** + Prints worst case pattern used for HS400 taining + + @param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure + @param[in] TuningPattern Data buffer containg pattern for training + @param[in] TuningPatternSize Size of the buffer with pattern + + @retval N/A +**/ +VOID +EmmcPrintHs400TuningPattern ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN UINT8 *TuningPattern, + IN UINT32 TuningPatternSize + ) +{ + DEBUG_CODE_BEGIN (); + UINT32 Index; + + DEBUG ((DEBUG_VERBOSE, "--------------- TUNING PATTERN ----------------\n")); + DEBUG ((DEBUG_VERBOSE, "BlockIo->Media->MediaId = 0x%08x\n", BlockIo->Media->MediaId)); + DEBUG ((DEBUG_VERBOSE, "BlockIo->Media->BlockSize = 0x%08x\n", BlockIo->Media->BlockSize)); + DEBUG ((DEBUG_VERBOSE, "Tuning Pattern Size = 0x%08x\n", TuningPatternSize)); + for (Index = 0; Index < TuningPatternSize; Index++) { + if (Index % 16 == 0) { + DEBUG ((DEBUG_VERBOSE, "\n")); + } + if (Index % (BlockIo->Media->BlockSize) == 0) { + DEBUG ((DEBUG_VERBOSE, "-------------------- BLOCK --------------------\n")); + } + DEBUG ((DEBUG_VERBOSE, "%02x ", TuningPattern[Index])); + } + DEBUG ((DEBUG_VERBOSE, "\n-----------------------------------------------\n")); + DEBUG_CODE_END (); +} + +/** + Writes worst case pattern used for HS400 taining + + @param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure + @param[in] TuningPattern Data buffer containg pattern for training + @param[in] TuningPatternSize Size of the buffer with pattern + + @retval EFI_SUCCESS HS400 Rx Data Path Training is successful. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_DEVICE_ERROR Hardware Error + @retval EFI_NO_MEDIA No media + @retval EFI_MEDIA_CHANGED Media Change + @retval EFI_BAD_BUFFER_SIZE Buffer size is bad +**/ +EFI_STATUS +EmmcWriteHs400TuningPattern ( + IN EMMC_INFO *EmmcInfo, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN UINTN EmmcBaseAddress, + IN UINT8 *TuningPattern + ) +{ + UINT8 *Buffer; + UINT32 Index; + UINT32 TuningPatternSize; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + TuningPatternSize = BlockIo->Media->BlockSize * EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER; + + DEBUG ((DEBUG_INFO, "EmmcWriteHs400TuningPattern() Start\n")); + + Buffer = (VOID *) AllocateZeroPool (TuningPatternSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + for (Index = 0; Index < EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER; Index++){ + CopyMem (Buffer + (Index * BlockIo->Media->BlockSize), TuningPattern, BlockIo->Media->BlockSize); + } + + Status = BlockIo->WriteBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + TuningPatternSize, + Buffer + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcWriteHs400TuningPattern: Multiple Blocks Write at HS200 Mode Failed!\n")); + goto Exit; + } + +Exit: + FreePool (Buffer); + DEBUG ((DEBUG_INFO, "EmmcWriteHs400TuningPattern() End, Status = %r\n", Status)); + return Status; +} + +/** + To perform HS400 Rx Data Path Training + + @param[in] EmmcInfo A pointer to EMMC_INFO structure + @param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in/out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure + + @retval EFI_SUCCESS HS400 Rx Data Path Training is successful. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_DEVICE_ERROR Hardware Error + @retval EFI_NO_MEDIA No media + @retval EFI_MEDIA_CHANGED Media Change + @retval EFI_BAD_BUFFER_SIZE Buffer size is bad + @retval EFI_CRC_ERROR Command or Data CRC Error +**/ +EFI_STATUS +EmmcRxHs400Tuning ( + IN EMMC_INFO *EmmcInfo, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN UINTN EmmcBaseAddress, + IN OUT EMMC_TUNING_DATA *EmmcTuningData + ) +{ + UINT8 *Buffer; + UINT8 DllCount; + UINT8 DllMax; + UINT8 DllMin; + UINT8 Smin; + UINT8 Smax; + UINT8 Sopt; + EFI_STATUS Status; + EFI_STATUS ModeStatus; + BLOCK_READ_WRITE_STATUS FirstRead; + UINT32 TuningPatternSize; + + DEBUG ((DEBUG_INFO, "EmmcRxHs400Tuning() Start\n")); + + Status = EFI_SUCCESS; + ModeStatus = EFI_SUCCESS; + + Smin = RX_STROBE_DLL1_TAP_MIN_MEPT; + Smax = RX_STROBE_DLL1_TAP_MAX_MEPT; + TuningPatternSize = BlockIo->Media->BlockSize * EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER; + + Buffer = (VOID *) AllocateZeroPool (TuningPatternSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // 1. Read Tuning Block + // + Status = BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + TuningPatternSize, + Buffer + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcRxHs400Tuning: Tuning Blocks Read at HS200 Mode Failed!\n")); + goto Exit; + } + + DEBUG_CODE ( EmmcPrintHs400TuningPattern (BlockIo, Buffer, TuningPatternSize); ); + + // + // 2. Move to HS400 Mode + // + Status = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs400); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcRxHs400Tuning: eMMC HS400 Mode Selection Failed!\n")); + goto Exit; + } + // + // 3. Set Rx Strobe DLL1 to the Minimal Expected Passing Tap (Smin) + // Offset 830h: Rx Strobe Delay DLL 1(HS400 Mode), bits [14:8] + // Set Rx Data Strobe DLL2 to middle point 0x13 + // Offset 830h: Rx Strobe Delay DLL 2(HS400 Mode), bits [6:0] + DEBUG ((DEBUG_INFO, "EmmcRxHs400Tuning: Set Rx Data DLL1 to the Minimal Expected Passing Tap (Smin = 0x%x)\n", Smin)); + DllCount = RX_STROBE_DLL1_TAP_MIN_MEPT; + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount); + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount); + + // + // 4. Read the block that was stored + // 5. If CRC fails on first read, increase the DLL Step and repeat block read until passed + // Else if CRC passed on first read, decrease the DLL Step and repeat block read until failed + // 6. Store the Rx Path min DLL passing step number + // + FirstRead = NotAvailable; + DllMax = RX_STROBE_DLL1_TAP_MAX_RANGE; + DllMin = RX_STROBE_DLL1_TAP_MIN_RANGE; + + while ((DllCount <= DllMax) && (DllCount >= DllMin)) { + DEBUG ((DEBUG_INFO, "[ EmmcRxHs400Tuning: Rx Min DLL1 (DllCount) = 0x%x ]\n", DllCount)); + Status = BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + TuningPatternSize, + Buffer + ); + if (Status == EFI_SUCCESS) { + if (FirstRead == NotAvailable) { + FirstRead = Passed; + } else if (FirstRead == Failed) { + Smin = DllCount; + break; + } + if (DllCount == RX_STROBE_DLL1_TAP_MIN_RANGE) { + Smin = RX_STROBE_DLL1_TAP_MIN_RANGE; + break; + } + } else if (Status == EFI_CRC_ERROR) { // Rely on the driver to return ReadBlocks status on CRC error + if (FirstRead == NotAvailable) { + FirstRead = Failed; + } else if (FirstRead == Passed) { + Smin = DllCount + 1; + break; + } + if (DllCount == RX_STROBE_DLL1_TAP_MAX_RANGE) { + Status = EFI_CRC_ERROR; + DEBUG ((DEBUG_ERROR, "[Rx DLL Tuning Failed] DllCount == TAP_MAX_RANGE\n")); + goto Exit; + } + } else { + DEBUG ((DEBUG_ERROR, "[Rx DLL Tuning Failed] Smin - Tuning Blocks Read Error!\n")); + goto Exit; + } + + if (FirstRead == Failed) { + DllCount++; + } else { + DllCount--; + } + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount); + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount); + } + + DEBUG ((DEBUG_INFO, "[Rx DLL Tuning] Found Minimal Passing Tap = 0x%x\n", Smin)); + + // + // 7. Set the Rx Strobe DLL1 to the Maximal Expected Passing Tap (Smax) + // Offset 830h: Rx Strobe Delay DLL 1(HS400 Mode), bits [14:8] + // 8. Read the block that was stored + // 9. If CRC fails on first read, decrease the DLL Step and repeat step 8 until pass + // Else if CRC passed on first read, increase the DLL Step and repeat step 8 until failed + // + DEBUG ((DEBUG_INFO, "EmmcRxHs400Tuning: Set Rx Data DLL1 to the Maximal Expected Passing Tap (Smax = 0x%x)\n", Smax)); + DllCount = RX_STROBE_DLL1_TAP_MAX_MEPT; + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount); + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount); + + FirstRead = NotAvailable; + DllMax = RX_STROBE_DLL1_TAP_MAX_RANGE; + DllMin = RX_STROBE_DLL1_TAP_MIN_RANGE; + while ((DllCount <= DllMax) && (DllCount >= DllMin)) { + DEBUG ((DEBUG_INFO, "[ EmmcRxHs400Tuning: Rx Max DLL1 (DllCount) = 0x%x ]\n", DllCount)); + Status = BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + TuningPatternSize, + Buffer + ); + if (Status == EFI_SUCCESS) { + if (FirstRead == NotAvailable) { + FirstRead = Passed; + } else if (FirstRead == Failed) { + Smax = DllCount; + break; + } + if (DllCount == RX_STROBE_DLL1_TAP_MAX_RANGE) { + Smax = DllCount; + break; + } + } else if (Status == EFI_CRC_ERROR) { // Rely on the driver to return ReadBlocks status on CRC error + if (FirstRead == NotAvailable) { + FirstRead = Failed; + } else if (FirstRead == Passed) { + Smax = DllCount - 1; + break; + } + if (DllCount == RX_STROBE_DLL1_TAP_MIN_RANGE) { + Status = EFI_CRC_ERROR; + goto Exit; + } + } else { + DEBUG ((DEBUG_ERROR, "[Rx DLL Tuning Failed] Smax - Tuning Blocks Read Error!\n")); + goto Exit; + } + if (FirstRead == Failed) { + DllCount--; + } else { + DllCount++; + } + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount); + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount); + } + // + // 10. Store the Rx Path max DLL Passing Step number + // + + // + // 11. Compute the Rx DLL Optimal Point (Sopt) = (Smax - Smin)/2 + Smin + // + Sopt = (Smax - Smin) / 2 + Smin; + DEBUG ((DEBUG_INFO, "[Rx DLL Tuning] Optimal Point (Sopt = (Smax[0x%x] - Smin[0x%x]) / 2 + Smin[0x%x]) = 0x%x\n", Smax, Smin, Smin, Sopt)); + // + // 12. Store the Rx DLL optimal value (Sopt) + // + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, Sopt); + EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, Sopt); + + DEBUG ((DEBUG_INFO, "[Rx HS400 Tuning Success] Rx Strobe Delay Control (830h) = 0x%08x\n", + MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL))); + + Status = EFI_SUCCESS; + EmmcTuningData->Hs400RxStrobe1Dll = Sopt; + +Exit: + FreePool (Buffer); + DEBUG ((DEBUG_INFO, "EmmcRxHs400Tuning() End, Status = %r\n", Status)); + return Status; +} + +/** + To perform HS400 Tx Data Path Training + + @param[in] EmmcInfo A pointer to EMMC_INFO structure + @param[in] EmmcBaseAddress Base address of MMIO register + @param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure + @param[in/out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure + + @retval EFI_SUCCESS HS400 Rx Data Path Training is successful. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_DEVICE_ERROR Hardware Error + @retval EFI_NO_MEDIA No media + @retval EFI_MEDIA_CHANGED Media Change + @retval EFI_BAD_BUFFER_SIZE Buffer size is bad + @retval EFI_CRC_ERROR Command or Data CRC Error +**/ +EFI_STATUS +EmmcTxHs400Tuning ( + IN EMMC_INFO *EmmcInfo, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN UINTN EmmcBaseAddress, + IN OUT EMMC_TUNING_DATA *EmmcTuningData + ) +{ + UINT8 *Buffer; + UINT8 DllCount; + UINT8 DllMax; + UINT8 DllMin; + UINT8 Smin; + UINT8 Smax; + UINT8 Sopt; + UINT8 N; + EFI_STATUS Status; + EFI_STATUS ModeStatus; + BLOCK_READ_WRITE_STATUS FirstWrite; + + DEBUG ((DEBUG_INFO, "EmmcTxHs400Tuning() Start\n")); + Status = EFI_SUCCESS; + ModeStatus = EFI_SUCCESS; + + Smin = TX_DATA_DLL_TAP_MIN_MEPT; + Smax = TX_DATA_DLL_TAP_MAX_MEPT; + N = 0; + + Buffer = (VOID *) AllocateZeroPool (BlockIo->Media->BlockSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + // + // 1. Read Tuning Block that used at Rx HS400 Tuning + // + Status = BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + BlockIo->Media->BlockSize, + Buffer + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: Tuning Block Read at HS400 Mode Failed!\n")); + goto Exit; + } + DEBUG_CODE ( EmmcPrintHs400TuningPattern (BlockIo, Buffer, BlockIo->Media->BlockSize); ); + + // + // 2. Set Tx Data DLL1 to the Minimal Expected Passing Tap (Smin) + // Offset 824h: Tx Data Delay Control 1 + // Tx Data Delay (HS400 Mode), BIT[14:8] + // + DEBUG ((DEBUG_INFO, "EmmcTxHs400Tuning: Set Tx Data DLL to the Minimal Expected Passing Tap (Smin = 0x%x)\n", Smin)); + + DllCount = TX_DATA_DLL_TAP_MIN_MEPT; + EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount); + + // + // 2. Write Single Block + // + FirstWrite = NotAvailable; + DllMax = TX_DATA_DLL_TAP_MAX_RANGE; + DllMin = TX_DATA_DLL_TAP_MIN_RANGE; + + while ((DllCount <= DllMax) && (DllCount >= DllMin)) { + DEBUG ((DEBUG_INFO, "[ EmmcTxHs400Tuning: Tx Min DLL (DllCount) = 0x%x ]\n", DllCount)); + Status = BlockIo->WriteBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + BlockIo->Media->BlockSize, + Buffer + ); + if (Status == EFI_SUCCESS) { + if (FirstWrite == NotAvailable) { + FirstWrite = Passed; + } else if (FirstWrite == Failed) { + Smin = DllCount; + break; + } + if (DllCount == TX_DATA_DLL_TAP_MIN_RANGE) { + Smin = TX_DATA_DLL_TAP_MIN_RANGE; + break; + } + // + // 3. If CRC fails increment DLL Step and repeat step 2 + // + } else if (Status == EFI_CRC_ERROR) { // Rely on the driver to return ReadBlocks status on CRC error + if (FirstWrite == NotAvailable) { + FirstWrite = Failed; + } else if (FirstWrite == Passed) { + Smin = DllCount + 1; + break; + } + if (DllCount == TX_DATA_DLL_TAP_MAX_RANGE) { + DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] DllCount == TAP_MAX_RANGE\n")); + goto Exit; + } + } else { + DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] Smin - Tuning Block Write Error!\n")); + goto Exit; + } + if (FirstWrite == Failed) { + DllCount++; + } else { + DllCount--; + } + EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount); + } + // + // 4. Store the Tx Path min DLL passing step number + // + DEBUG ((DEBUG_INFO, "[Tx DLL Tuning] Found Minimal Passing Tap = 0x%x\n", Smin)); + + // + // 5. Set the DLL to max expected passing step (Smax) + // + DEBUG ((DEBUG_INFO, "EmmcTxHs400Tuning: Set Tx Data DLL to the Maximal Expected Passing Tap (Smax = 0x%x)\n", Smax)); + DllCount = TX_DATA_DLL_TAP_MAX_MEPT; + EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount); + // + // 6. Write Single Block + // + FirstWrite = NotAvailable; + DllMax = TX_DATA_DLL_TAP_MAX_RANGE; + DllMin = TX_DATA_DLL_TAP_MIN_RANGE; + + while ((DllCount <= DllMax) && (DllCount >= DllMin)) { + DEBUG ((DEBUG_INFO, "[ EmmcTxHs400Tuning: Tx Max DLL1 (DllCount) = 0x%x ]\n", DllCount)); + Status = BlockIo->WriteBlocks ( + BlockIo, + BlockIo->Media->MediaId, + EmmcInfo->Lba, + BlockIo->Media->BlockSize, + Buffer + ); + + if (Status == EFI_SUCCESS) { + if (FirstWrite == NotAvailable) { + FirstWrite = Passed; + } else if (FirstWrite == Failed) { + Smax = DllCount; + break; + } + if (DllCount == TX_DATA_DLL_TAP_MAX_RANGE) { + Smax = TX_DATA_DLL_TAP_MAX_RANGE; + break; + } + // + // 7. If CRC fails decrement DLL Step and repeat step 6 + // + } else if (Status == EFI_CRC_ERROR) { // Rely on the driver to return ReadBlocks status on CRC error + if (FirstWrite == NotAvailable) { + FirstWrite = Failed; + } else if (FirstWrite == Passed) { + Smax = DllCount - 1; + break; + } + if (DllCount == TX_DATA_DLL_TAP_MIN_RANGE) { + DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] DllCount == TAP_MIN_RANGE\n")); + goto Exit; + } + } else { + DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] Smax - Tuning Block Write Error!\n")); + goto Exit; + } + if (FirstWrite == Failed) { + DllCount--; + } else { + DllCount++; + } + EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount); + } + // + // 8. Store the DLL passing step number (Smax) + // + DEBUG ((DEBUG_INFO, "[Tx DLL Tuning] Found Maximal Passing Tap = 0x%x\n", Smax)); + + N = Smax - Smin; + if (N <= 0) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // 9. Compute the Tx DLL Optimal point (Sopt) = (Smax - Smin) / 2 + Smin + // + Sopt = (Smax - Smin) / 2 + Smin; + DEBUG ((DEBUG_INFO, "[Tx DLL Tuning] Optimal Point (Sopt = (Smax[0x%x] - Smin[0x%x]) / 2 + Smin[0x%x]) = 0x%x\n", Smax, Smin, Smin, Sopt)); + + // + // 10. Store the Tx Strobe DLL Optimal point value + // + EmmcSetTxDllCtrl1 (EmmcBaseAddress, Sopt); + + DEBUG ((DEBUG_INFO, "[Tx HS400 Tuning Success] Tx Data Delay Control 1 (824h) = 0x%08x\n", + MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_TX_DATA_DLL_CNTL1))); + + Status = EFI_SUCCESS; + EmmcTuningData->Hs400TxDataDll = Sopt; + +Exit: + FreePool (Buffer); + DEBUG ((DEBUG_INFO, "EmmcTxHs400Tuning() End, Status = %r\n", Status)); + return Status; +} + +/** + To perform write protection checking on the address to write + + @param[in] EmmcInfo A pointer to EMMC_INFO structure + @param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure + @param[in] EmmcBaseAddress Base address of MMIO register + + @retval EFI_SUCCESS HS400 Rx Data Path Training is successful. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_DEVICE_ERROR Hardware Error + @retval EFI_NO_MEDIA No media + @retval EFI_MEDIA_CHANGED Media Change + @retval EFI_BAD_BUFFER_SIZE Buffer size is bad + @retval EFI_CRC_ERROR Command or Data CRC Error +**/ +EFI_STATUS +EmmcWriteProtectCheck ( + IN EMMC_INFO *EmmcInfo, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN UINTN EmmcBaseAddress + ) +{ + EFI_STATUS Status; + UINT32 DataSize; + UINT32 CardStatus; + UINT32 Address; + UINT32 Timeout; + UINT16 CommandIndex; + UINT8 *Buffer; + + Status = EFI_SUCCESS; + DataSize = 8; + CommandIndex = SEND_WRITE_PROT_TYPE; + Timeout = TIMEOUT_DATA; + + Buffer = (VOID *) AllocateZeroPool (DataSize); + Address = (UINT32) DivU64x32 (MultU64x32 (EmmcInfo->Lba, BlockIo->Media->BlockSize), 512); + + DEBUG ((DEBUG_INFO, "EmmcWriteProtectCheck() Start\n")); + + if (BlockIo->Media->ReadOnly == TRUE) { + Status = EFI_WRITE_PROTECTED; + goto Exit; + } + + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + Status = EmmcSendCommand ( + EmmcBaseAddress, + CommandIndex, + Address, + InData, + Buffer, + DataSize, + ResponseR1, + Timeout, + &CardStatus + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + if ((Buffer[7] & (BIT0 | BIT1)) !=0) { + Status = EFI_WRITE_PROTECTED; + goto Exit; + } + +Exit: + FreePool (Buffer); + DEBUG ((DEBUG_INFO, "EmmcWriteProtectCheck End, Status = %r\n", Status)); + return Status; +} + +/** + Configure eMMC in HS400 Mode + + @param[in] This A pointer to PCH_EMMC_TUNING_PROTOCOL structure + @param[in] Revision Revision parameter used to verify the layout of EMMC_INFO and TUNINGDATA. + @param[in] EmmcInfo A pointer to EMMC_INFO structure + @param[out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_NOT_FOUND The item was not found + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER A parameter was incorrect. + @retval EFI_DEVICE_ERROR Hardware Error + @retval EFI_NO_MEDIA No media + @retval EFI_MEDIA_CHANGED Media Change + @retval EFI_BAD_BUFFER_SIZE Buffer size is bad + @retval EFI_CRC_ERROR Command or Data CRC Error +**/ +EFI_STATUS +EFIAPI +ConfigureEmmcHs400Mode ( + IN PCH_EMMC_TUNING_PROTOCOL *This, + IN UINT8 Revision, + IN EMMC_INFO *EmmcInfo, + OUT EMMC_TUNING_DATA *EmmcTuningData + ) +{ + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_STATUS Status; + EFI_STATUS ModeStatus; + UINTN EmmcBaseAddress; + UINTN EmmcPciBaseAddress; + ModeStatus = EFI_SUCCESS; + + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode() Start\n")); + EmmcTuningData->Hs400DataValid = FALSE; + // + // Check PCH_EMMC_TUNING_PROTOCOL_REVISION + // + if (Revision != PCH_EMMC_TUNING_PROTOCOL_REVISION) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: PCH eMMC Tuning Protocol Revision Not Match! Tuning Aborted\n")); + return EFI_UNSUPPORTED; + } + // + // Get eMMC Host Controller Mmio base register + // + EmmcPciBaseAddress = MmPciBase ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_SCS_EMMC, + PCI_FUNCTION_NUMBER_PCH_SCS_EMMC + ); + + if (MmioRead16 (EmmcPciBaseAddress + PCI_VENDOR_ID_OFFSET) == 0xFFFF) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: eMMC Host Controller Unavailable!\n")); + return EFI_UNSUPPORTED; + } + + // + // Assume BAR is ready since it's executed after blockio ready, handling 64bit BAR in DXE is not required + // + EmmcBaseAddress = MmioRead32 (EmmcPciBaseAddress + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000; + // + // Enable Memory Decode + // + if ((MmioRead8 (EmmcPciBaseAddress + PCI_COMMAND_OFFSET) & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) { + MmioOr8 (EmmcPciBaseAddress + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE); + } + + DEBUG ((DEBUG_INFO, "Initial HS400 DLL values (set in PEI):\n")); + DEBUG ((DEBUG_INFO, "Tx Data Delay Control 1 (824h) = 0x%08x\n", MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_TX_DATA_DLL_CNTL1))); + DEBUG ((DEBUG_INFO, "Rx Strobe Delay Control (830h) = 0x%08x\n", MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL))); + + if (mPchConfigHob->Scs.ScsEmmcHs400TuningRequired == FALSE) { + if (mPchConfigHob->Scs.ScsEmmcHs400DllDataValid == TRUE) { + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode: SCS eMMC 5.0 HS400 Tuning Not Required, set device to HS400 mode\n")); + Status = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs400); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: eMMC HS400 Mode Selection Failed!\n")); + } + return Status; + } else { + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode: SCS eMMC 5.0 HS400 Mode Selection Not Required.\n")); + return EFI_ABORTED; + } + } + // + // Handle Platform Emmc Info Protocol for Efi Block Io Protocol + // + Status = gBS->HandleProtocol ( + EmmcInfo->PartitionHandle, + &gEfiBlockIoProtocolGuid, + (VOID**) &BlockIo + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: BlockIo: Platform Emmc Info Protocol Handle Not Found!\n")); + EmmcHostHs400Disabled (EmmcBaseAddress); + return Status; + } + + // + // Write Protection checking on the region + // + Status = EmmcWriteProtectCheck (EmmcInfo, BlockIo, EmmcBaseAddress); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Region is write protected! HS400 Tuning Abort!\n")); + EmmcHostHs400Disabled (EmmcBaseAddress); + return Status; + } + + Status = EmmcWriteHs400TuningPattern (EmmcInfo, BlockIo, EmmcBaseAddress, (UINT8*)EmmcWorstCasePattern); + + // + // Rx HS400 Auto Tuning + // + Status = EmmcRxHs400Tuning (EmmcInfo, BlockIo, EmmcBaseAddress, EmmcTuningData); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Rx HS400 Auto Tuning Failed!\n")); + ModeStatus = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs200); + if (EFI_ERROR (ModeStatus)) { + DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: eMMC HS200 Mode Selection Failed!\n")); + } + EmmcTuningData->Hs400DataValid = FALSE; + return Status; + } + + // + // Tx HS400 Auto Tuning + // + Status = EmmcTxHs400Tuning (EmmcInfo, BlockIo, EmmcBaseAddress, EmmcTuningData); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Tx HS400 Auto Tuning Failed!\n")); + ModeStatus = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs200); + if (EFI_ERROR (ModeStatus)) { + DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: eMMC HS200 Mode Selection Failed!\n")); + } + EmmcTuningData->Hs400DataValid = FALSE; + return Status; + } + + // + // Set current HS400 Driver Strength in tuning data structre + // Set HS400 Data Valid Tuning Bit to TRUE + // + EmmcTuningData->Hs400DriverStrength = (UINT8) mPchConfigHob->Scs.ScsEmmcHs400DriverStrength; + EmmcTuningData->Hs400DataValid = TRUE; + + DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode() End\n")); + return Status; +} + +/** + Install PCH EMMC TUNING PROTOCOL + +**/ +VOID +InstallPchEmmcTuningProtocol ( + VOID + ) +{ + + EFI_HANDLE Handle; + EFI_STATUS Status; + + Handle = NULL; + + /// + /// For normal boot flow + /// 1. If ScsEmmcEnabled and ScsEmmcHs400Enabled policy set, + /// a) Set ScsEmmcHs400TuningRequired policy to state tuning required in PEI, + /// - if RTC_PWR_STS bit is set which indicates a new coin-cell battery insertion, a battery failure or CMOS clear.(Boot with default settings) + /// - if non-volatile variable 'Hs400TuningData' does not exist + /// b) RC installed Pch Emmc Tuning Protocol regardless of ScsEmmcHs400TuningRequired policy setting.in DXE + /// c) If boot with default settings after CMOS cleared, platform delete variable 'Hs400TuningData' in DXE + /// 2. Once RC successfully installed Pch Emmc Tuning Protocol, it will be used to perform EmmcTune for Hs400. + /// 3. Then, platform must set the variable with returned EmmcTuningData no matter tuning pass of fail + /// 4. Platform shall set variable 'Hs400TuningData' for one time only or after CMOS clear + /// + /// For fast boot flow + /// 1. If ScsEmmcEnabled and ScsEmmcHs400Enabled policy set, + /// a) Set ScsEmmcHs400TuningRequired policy to state tuning not required, if non-volatile variable 'Hs400TuningData' exist + /// b) RC installed Pch Emmc Tuning Protocol regardless of ScsEmmcHs400TuningRequired policy setting in DXE + /// 2. Once RC successfully installed Pch Emmc Tuning Protocol, it will be used to perform EmmcTune + /// 3. Since ScsEmmcHs400TuningRequired state tuning not required, RC will not perform Emmc Hs400 Tuning but just set the device to operate in HS400 mode if data is valid + /// 4. Platform shall not set variable 'Hs400TuningData' + /// + // + // Install PchEmmcTuningProtocol Protocol + // + Status = gBS->InstallProtocolInterface ( + &Handle, + &gPchEmmcTuningProtocolGuid, + EFI_NATIVE_INTERFACE, + &PchEmmcTuningProtocol + ); + ASSERT_EFI_ERROR (Status); +} diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIo.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIo.c new file mode 100644 index 0000000000..013c729ddf --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIo.c @@ -0,0 +1,49 @@ +/** @file + Initializes Serial IO Controllers. + +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 +#include + + +/** + Puts all SerialIo controllers (except UARTs in debug mode) in D3 + Clears MemoryEnable for all PCI-mode controllers +**/ +EFI_STATUS +ConfigureSerialIoAtBoot ( + VOID + ) +{ + PCH_SERIAL_IO_CONTROLLER Index; + UINTN PciCfgBase; + + for (Index = 0; Index < PchSerialIoIndexMax; Index++) { + if ((mPchConfigHob->SerialIo.DevMode[Index] == PchSerialIoDisabled) || + (mPchConfigHob->SerialIo.DevMode[Index] == PchSerialIoSkipInit) ) { + continue; + } + if ((Index >= PchSerialIoIndexUart0) && + (mPchConfigHob->SerialIo.EnableDebugUartAfterPost) && + (mPchConfigHob->SerialIo.DebugUartNumber == (UINT32) (Index - PchSerialIoIndexUart0))) { + continue; + } + PciCfgBase = FindSerialIoBar (Index,1); + MmioOr32 (PciCfgBase + R_PCH_SERIAL_IO_PME_CTRL_STS, B_PCH_SERIAL_IO_PME_CTRL_STS_PWR_ST); + MmioRead32 (PciCfgBase + R_PCH_SERIAL_IO_PME_CTRL_STS); + if (mPchConfigHob->SerialIo.DevMode[Index] == PchSerialIoPci) { + MmioAnd32 (PciCfgBase + PCI_COMMAND_OFFSET, (UINT32)~(EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER) ); + } + } + return EFI_SUCCESS; +} + diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIoDxe.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIoDxe.c new file mode 100644 index 0000000000..3b951092b5 --- /dev/null +++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Dxe/PchSerialIoDxe.c @@ -0,0 +1,112 @@ +/** @file + Initializes Serial IO Controllers. + +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 +#include +#include + +typedef struct { + ACPI_HID_DEVICE_PATH RootPort; + ACPI_EXTENDED_HID_DEVICE_PATH AcpiDev; + CHAR8 HidString[8]; + CHAR8 UidString; + CHAR8 CidString; + EFI_DEVICE_PATH_PROTOCOL End; +} SERIALIO_DEVICE_PATH; + +#define gPciRootBridge {{ACPI_DEVICE_PATH, ACPI_DP, {(UINT8)(sizeof(ACPI_HID_DEVICE_PATH)), 0}}, EISA_PNP_ID (0x0A03), 0} +#define gAcpiDev {{ACPI_DEVICE_PATH,ACPI_EXTENDED_DP,{(UINT8)(sizeof(ACPI_EXTENDED_HID_DEVICE_PATH)+SERIALIO_TOTAL_ID_LENGTH),0}},0,0,0} +#define gEndEntire {END_DEVICE_PATH_TYPE,END_ENTIRE_DEVICE_PATH_SUBTYPE,{END_DEVICE_PATH_LENGTH,0}} + +GLOBAL_REMOVE_IF_UNREFERENCED SERIALIO_DEVICE_PATH gSerialIoPath = { + gPciRootBridge, + gAcpiDev, + "\0\0\0\0\0\0\0", + '\0', + '\0', + gEndEntire +}; + +/** +Mark memory used by SerialIo devices in ACPI mode as allocated + +@retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +AllocateSerialIoMemory ( + VOID + ) +{ + PCH_SERIAL_IO_CONTROLLER i; + UINT8 BarNumber; + UINTN Bar; + EFI_STATUS Status; + + for (i=0; iSerialIo.DevMode[i] == PchSerialIoAcpiHidden || + mPchConfigHob->SerialIo.DevMode[i] == PchSerialIoLegacyUart || + mPchConfigHob->SerialIo.DevMode[i] == PchSerialIoAcpi) { + for (BarNumber = 0; BarNumber<=1; BarNumber++) { + Bar = FindSerialIoBar (i,BarNumber); + Status = gDS->AddMemorySpace ( + EfiGcdMemoryTypeReserved, + Bar, + V_PCH_SERIAL_IO_BAR_SIZE, + 0 + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gDS->AllocateMemorySpace ( + EfiGcdAllocateAddress, + EfiGcdMemoryTypeReserved, + N_PCH_SERIAL_IO_BAR_ALIGNMENT, + V_PCH_SERIAL_IO_BAR_SIZE, + &Bar, + mImageHandle, + NULL + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + return EFI_SUCCESS; +} + +VOID +CreateSerialIoHandles ( + VOID + ) +{ + EFI_HANDLE NewHandle; + EFI_DEVICE_PATH_PROTOCOL *NewPath; + EFI_STATUS Status; + UINT32 Controller; + + for (Controller = 0; Controller < PchSerialIoIndexMax; Controller++) { + if (mPchConfigHob->SerialIo.DevMode[Controller] == PchSerialIoAcpi) { + NewHandle = NULL; + CopyMem (gSerialIoPath.HidString, GetSerialIoAcpiHID (Controller), SERIALIO_HID_LENGTH); + NewPath = DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*)&gSerialIoPath); + Status = gBS->InstallMultipleProtocolInterfaces ( + &NewHandle, + &gEfiDevicePathProtocolGuid, + NewPath, + NULL ); + } + } +} -- cgit v1.2.3