summaryrefslogtreecommitdiff
path: root/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Smm/PchLanSxSmm.c
diff options
context:
space:
mode:
Diffstat (limited to 'Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Smm/PchLanSxSmm.c')
-rw-r--r--Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Smm/PchLanSxSmm.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Smm/PchLanSxSmm.c b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Smm/PchLanSxSmm.c
new file mode 100644
index 0000000000..d05cb61bbc
--- /dev/null
+++ b/Silicon/Intel/KabylakeSiliconPkg/Pch/PchInit/Smm/PchLanSxSmm.c
@@ -0,0 +1,415 @@
+/** @file
+ PCH LAN Sx handler implementation.
+
+Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+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 <Library/TimerLib.h>
+#include "PchInitSmm.h"
+
+//
+// Maximum loop time for GbE status check
+//
+#define GBE_MAX_LOOP_TIME 4000
+
+/**
+ Checks if Lan is Enabled or Disabled
+
+ @retval BOOLEAN TRUE if device is enabled, FALSE otherwise.
+**/
+BOOLEAN
+IsGbeEnabled (
+ VOID
+ )
+{
+ UINTN GbePciBase;
+
+ GbePciBase = MmPciBase (
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LAN,
+ PCI_FUNCTION_NUMBER_PCH_LAN
+ );
+
+ if (MmioRead32 (GbePciBase) != 0xFFFFFFFF) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+/**
+ Test for MDIO operation complete.
+
+ @param [in] GbeBar GbE MMIO space
+
+ @retval EFI_SUCCESS
+ @retval EFI_TIMEOUT
+**/
+EFI_STATUS
+GbeMdiWaitReady (
+ UINT32 GbeBar
+ )
+{
+ UINT32 Count;
+
+ for (Count = 0; Count < GBE_MAX_LOOP_TIME; ++Count) {
+ if (MmioRead32 (GbeBar + R_PCH_LAN_CSR_MDIC) & B_PCH_LAN_CSR_MDIC_RB) {
+ return EFI_SUCCESS;
+ }
+ MicroSecondDelay (50);
+ }
+ return EFI_TIMEOUT;
+}
+
+/**
+ Acquire MDIO software semaphore.
+
+ 1. Ensure that MBARA offset F00h [5] = 1b
+ 2. Poll MBARA offset F00h [5] up to 200ms
+
+ @param [in] GbeBar GbE MMIO space
+
+ @retval EFI_SUCCESS
+ @retval EFI_TIMEOUT
+**/
+EFI_STATUS
+GbeAcquireMdio (
+ IN UINT32 GbeBar
+ )
+{
+ UINT32 ExtCnfCtrl;
+ UINT32 Count;
+
+ MmioOr32 (GbeBar + R_PCH_LAN_CSR_EXTCNF_CTRL, B_PCH_LAN_CSR_EXTCNF_CTRL_SWFLAG);
+ for (Count = 0; Count < GBE_MAX_LOOP_TIME; ++Count) {
+ ExtCnfCtrl = MmioRead32 (GbeBar + R_PCH_LAN_CSR_EXTCNF_CTRL);
+ if (ExtCnfCtrl & B_PCH_LAN_CSR_EXTCNF_CTRL_SWFLAG) {
+ return EFI_SUCCESS;
+ }
+ MicroSecondDelay (50);
+ }
+ ASSERT (FALSE);
+ return EFI_TIMEOUT;
+}
+
+
+/**
+ Release MDIO software semaphore by clearing MBARA offset F00h [5]
+
+ @param [in] GbeBar GbE MMIO space
+**/
+VOID
+GbeReleaseMdio (
+ IN UINT32 GbeBar
+ )
+{
+ ASSERT (MmioRead32 (GbeBar + R_PCH_LAN_CSR_EXTCNF_CTRL) & B_PCH_LAN_CSR_EXTCNF_CTRL_SWFLAG);
+ MmioAnd32 (GbeBar + R_PCH_LAN_CSR_EXTCNF_CTRL, (UINT32) ~B_PCH_LAN_CSR_EXTCNF_CTRL_SWFLAG);
+ ASSERT ((MmioRead32 (GbeBar + R_PCH_LAN_CSR_EXTCNF_CTRL) & B_PCH_LAN_CSR_EXTCNF_CTRL_SWFLAG) == 0);
+}
+
+/**
+ Perform MDI write.
+
+ @param [in] GbeBar GbE MMIO space
+ @param [in] Data Value to write in lower 16bits, upper 16bit may contain opcode.
+
+ @return EFI_STATUS
+**/
+EFI_STATUS
+GbeMdiWrite (
+ IN UINT32 GbeBar,
+ IN UINT32 Data
+ )
+{
+ MmioWrite32 (GbeBar + R_PCH_LAN_CSR_MDIC, Data | 0x04000000);
+ return GbeMdiWaitReady (GbeBar);
+}
+
+/**
+ Perform MDI read.
+
+ @param [in] GbeBar GbE MMIO space
+ @param [in] Command MDI command
+ @param [out] Data Value read
+
+ @return EFI_STATUS
+**/
+EFI_STATUS
+GbeMdiRead (
+ IN UINT32 GbeBar,
+ IN UINT32 Command,
+ OUT UINT16 *Data
+ )
+{
+ EFI_STATUS Status;
+ MmioWrite32 (GbeBar + R_PCH_LAN_CSR_MDIC, Command | 0x08000000);
+ Status = GbeMdiWaitReady (GbeBar);
+ *Data = (UINT16) MmioRead32 (GbeBar + R_PCH_LAN_CSR_MDIC);
+ return Status;
+}
+
+/**
+ Configure WOL during Sx entry.
+
+ @param [in] GbeBar GbE MMIO space
+**/
+VOID
+GbeWolWorkaround (
+ IN UINT32 GbeBar
+ )
+{
+ UINT32 RAL0;
+ UINT32 RAH0;
+ EFI_STATUS Status;
+ UINT16 Data16;
+
+ //
+ // System BIOS performs read from MBARA offset 5400h [31:0] and MBARA offset 5404h [31:0]
+ //
+ RAL0 = MmioRead32 (GbeBar + R_PCH_LAN_CSR_RAL);
+ RAH0 = MmioRead32 (GbeBar + R_PCH_LAN_CSR_RAH);
+
+ //
+ // Set MBARA offset 20h = 0x043f6400
+ //
+ Status = GbeMdiWrite (GbeBar, 0x043f6400);
+
+ //
+ // If timeout is reached, force MAC to SMB and try again
+ // Assert if failed on second attempt
+ //
+ if (Status == EFI_TIMEOUT) {
+ //
+ // Change Extended Device Control Register BIT 11 to 1 which
+ // forces the interface between the MAC and the Phy to be on SMBus.
+ // Cleared on the assertion of PCI reset.
+ //
+ MmioOr32 (GbeBar + R_PCH_LAN_CSR_CTRL_EXT, B_PCH_LAN_CSR_CTRL_EXT_FORCE_SMB);
+ Status = GbeMdiWrite (GbeBar, 0x043f6400);
+ }
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Wait 4 mSec
+ //
+ MicroSecondDelay (4000);
+
+ //
+ // Set MBARA offset 20h = 0x4310010
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04310010);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4320000 or with
+ // the least significant word of MBARA offset 5400
+ //
+ Status = GbeMdiWrite (GbeBar, (0x04320000 | (RAL0 & 0xFFFF)));
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4310011
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04310011);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4320000 or with
+ // the most significant word of MBARA offset 5400
+ //
+ Status = GbeMdiWrite (GbeBar, (0x04320000 | (RAL0 >> 16)));
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4310012
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04310012);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4320000 or with
+ // the least significant word of MBARA offset 5404
+ //
+ Status = GbeMdiWrite (GbeBar, (0x04320000 | (RAH0 & B_PCH_LAN_CSR_RAH_RAH)));
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4310013
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04310013);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4328000
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04328000);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4310001
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04310001);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x8320000
+ //
+ Status = GbeMdiRead (GbeBar, 0x08320000, &Data16);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x4320000 OR TEMP[15:0] OR 1
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04320000 | Data16 | BIT0);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x43f6460
+ //
+ Status = GbeMdiWrite (GbeBar, 0x043f6460);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Wait 4 mSec
+ //
+ MicroSecondDelay (4000);
+
+ //
+ // Set MBARA offset 20h = 0x4310042
+ //
+ Status = GbeMdiWrite (GbeBar, 0x04310042);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA offset 20h = 0x43F6020
+ //
+ Status = GbeMdiWrite (GbeBar, 0x043F6020);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Wait 4 mSec
+ //
+ MicroSecondDelay (4000);
+
+ //
+ // Set MBARA offset 20h = 0x8310000
+ // TEMP[15:0] = MBARA + 20[15:0]
+ //
+ Status = GbeMdiRead (GbeBar, 0x08310000, &Data16);
+ if (EFI_ERROR (Status)) return;
+
+ //
+ // Set MBARA + 20h = 4310000h or with the TEMP[15:0] OR 10h
+ //
+ GbeMdiWrite (GbeBar, 0x04310000 | Data16 | BIT4);
+}
+
+/**
+ Additional Internal GbE Controller special cases WOL Support.
+
+ System BIOS is required perform additional steps upon S0 to S3,4,5 transition
+ when ME is off and GbE device in D0. This is needed to enable LAN wake
+ in particular when platform is shut-down from EFI.
+**/
+VOID
+GbeSxWorkaround (
+ VOID
+ )
+{
+ UINTN LanRegBase;
+ UINT32 GbeBar;
+ EFI_STATUS Status;
+
+ LanRegBase = MmPciBase (
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LAN,
+ PCI_FUNCTION_NUMBER_PCH_LAN
+ );
+
+ if (MmioRead16 (LanRegBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
+ return;
+ }
+
+ //
+ // Check if GbE device is in D0
+ //
+ if ((MmioRead16 (LanRegBase + R_PCH_LAN_PMCS) & B_PCH_LAN_PMCS_PS) != V_PCH_LAN_PMCS_PS0) {
+ return;
+ }
+
+ ASSERT (mResvMmioSize >= (1 << N_PCH_LAN_MBARA_ALIGN));
+ GbeBar = (UINT32) mResvMmioBaseAddr;
+ if (GbeBar == 0) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Enable MMIO decode using reserved range.
+ //
+ MmioAnd16 (LanRegBase + PCI_COMMAND_OFFSET, (UINT16) ~EFI_PCI_COMMAND_MEMORY_SPACE);
+ MmioWrite32 (LanRegBase + R_PCH_LAN_MBARA, GbeBar);
+ MmioOr16 (LanRegBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
+
+ //
+ // If MBARA offset 5800h [0] = 1b then proceed with the w/a
+ //
+ if (MmioRead32 (GbeBar + R_PCH_LAN_CSR_WUC) & B_PCH_LAN_CSR_WUC_APME) {
+ Status = GbeAcquireMdio (GbeBar);
+ ASSERT_EFI_ERROR (Status);
+ if (!EFI_ERROR (Status)) {
+ GbeWolWorkaround (GbeBar);
+ GbeReleaseMdio (GbeBar);
+ }
+ }
+
+ //
+ // Disable MMIO decode.
+ //
+ MmioAnd16 (LanRegBase + PCI_COMMAND_OFFSET, (UINT16) ~EFI_PCI_COMMAND_MEMORY_SPACE);
+ MmioWrite32 (LanRegBase + R_PCH_LAN_MBARA, 0);
+}
+
+/**
+ Enable platform wake from LAN when in DeepSx if platform supports it.
+ Called upon Sx entry.
+**/
+VOID
+GbeConfigureDeepSxWake (
+ VOID
+ )
+{
+ UINT32 PchPwrmBase;
+
+ PchPwrmBaseGet (&PchPwrmBase);
+
+ if ((MmioRead32 (PchPwrmBase + R_PCH_PWRM_DSX_CFG) & (UINT32) B_PCH_PWRM_DSX_CFG_LAN_WAKE_EN) != 0) {
+ IoOr32 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_GPE0_EN_127_96), (UINT32) B_PCH_ACPI_GPE0_EN_127_96_LAN_WAKE);
+ }
+}
+
+/**
+ GbE Sx entry handler
+**/
+VOID
+PchLanSxCallback (
+ VOID
+ )
+{
+ if (IsGbeEnabled ()) {
+ GbeSxWorkaround ();
+ GbeConfigureDeepSxWake ();
+
+ }
+}