/** @file PCH LAN Sx handler implementation. 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 "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 (); } }