/** @file
|
Gbe MDI Library.
|
All function in this library is available for PEI, DXE, and SMM,
|
But do not support UEFI RUNTIME environment call.
|
|
Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
**/
|
|
#include <Base.h>
|
#include <Uefi/UefiBaseType.h>
|
#include <Library/TimerLib.h>
|
#include <Library/IoLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/BaseLib.h>
|
#include <Library/GbeMdiLib.h>
|
#include <Register/GbeRegs.h>
|
|
|
/**
|
Validates both Phy Address and Regster.
|
|
@param [in] PhyAddress
|
@param [in] PhyRegister
|
|
@retval BOOLEAN TRUE Validation passed
|
FALSE If the data is not within its range
|
|
**/
|
BOOLEAN
|
IsPhyAddressRegisterValid (
|
IN UINT32 PhyAddress,
|
IN UINT32 PhyRegister
|
)
|
{
|
if (((PhyAddress & (~B_PHY_MDI_PHY_ADDRESS_MASK)) != 0) || ((PhyRegister & (~B_PHY_MDI_PHY_REGISTER_MASK)) != 0)) {
|
DEBUG ((DEBUG_ERROR, "IsPhyAddressRegisterValid validation failed! PhyAddress: 0x%08X PhyRegister: 0x%08X \n", PhyAddress, PhyRegister));
|
return FALSE;
|
}
|
return TRUE;
|
}
|
|
/**
|
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.
|
|
@param [in] GbeBar GbE MMIO space
|
|
**/
|
VOID
|
GbeMdiForceMacToSmb (
|
IN UINT32 GbeBar
|
)
|
{
|
MmioOr32 (GbeBar + R_GBE_MEM_CSR_CTRL_EXT, B_GBE_MEM_CSR_CTRL_EXT_FORCE_SMB);
|
}
|
|
/**
|
Test for MDIO operation complete.
|
|
@param [in] GbeBar GbE MMIO space
|
|
@retval EFI_SUCCESS
|
@retval EFI_TIMEOUT
|
**/
|
EFI_STATUS
|
GbeMdiWaitReady (
|
IN UINT32 GbeBar
|
)
|
{
|
UINT32 Count;
|
|
for (Count = 0; Count < GBE_MAX_LOOP_TIME; ++Count) {
|
if (MmioRead32 (GbeBar + R_GBE_MEM_CSR_MDIC) & B_GBE_MEM_CSR_MDIC_RB) {
|
return EFI_SUCCESS;
|
}
|
MicroSecondDelay (GBE_ACQUIRE_MDIO_DELAY);
|
}
|
DEBUG ((DEBUG_ERROR, "GbeMdiWaitReady Timeout reached. MDIO operation failed to complete in %d micro seconds\n", GBE_MAX_LOOP_TIME * GBE_ACQUIRE_MDIO_DELAY));
|
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
|
GbeMdiAcquireMdio (
|
IN UINT32 GbeBar
|
)
|
{
|
UINT32 ExtCnfCtrl;
|
UINT32 Count;
|
|
MmioOr32 (GbeBar + R_GBE_MEM_CSR_EXTCNF_CTRL, B_GBE_MEM_CSR_EXTCNF_CTRL_SWFLAG);
|
for (Count = 0; Count < GBE_MAX_LOOP_TIME; ++Count) {
|
ExtCnfCtrl = MmioRead32 (GbeBar + R_GBE_MEM_CSR_EXTCNF_CTRL);
|
if (ExtCnfCtrl & B_GBE_MEM_CSR_EXTCNF_CTRL_SWFLAG) {
|
return EFI_SUCCESS;
|
}
|
MicroSecondDelay (GBE_ACQUIRE_MDIO_DELAY);
|
}
|
DEBUG ((DEBUG_ERROR, "GbeMdiAcquireMdio Timeout. Unable to acquire MDIO Semaphore in %d micro seconds\n", GBE_MAX_LOOP_TIME * GBE_ACQUIRE_MDIO_DELAY));
|
return EFI_TIMEOUT;
|
}
|
|
/**
|
Release MDIO software semaphore by clearing MBARA offset F00h [5]
|
|
@param [in] GbeBar GbE MMIO space
|
**/
|
VOID
|
GbeMdiReleaseMdio (
|
IN UINT32 GbeBar
|
)
|
{
|
ASSERT (MmioRead32 (GbeBar + R_GBE_MEM_CSR_EXTCNF_CTRL) & B_GBE_MEM_CSR_EXTCNF_CTRL_SWFLAG);
|
MmioAnd32 (GbeBar + R_GBE_MEM_CSR_EXTCNF_CTRL, (UINT32) ~B_GBE_MEM_CSR_EXTCNF_CTRL_SWFLAG);
|
ASSERT ((MmioRead32 (GbeBar + R_GBE_MEM_CSR_EXTCNF_CTRL) & B_GBE_MEM_CSR_EXTCNF_CTRL_SWFLAG) == 0);
|
}
|
|
/**
|
Sets page on MDI
|
Page setting is attempted twice.
|
If first attempt failes MAC and the Phy are force to be on SMBus.
|
|
Waits 4 mSec after page setting
|
|
@param [in] GbeBar GbE MMIO space
|
@param [in] Data Value to write in lower 16bits.
|
|
@retval EFI_SUCCESS Page setting was successfull
|
@retval EFI_DEVICE_ERROR Returned if both attermps of setting page failed
|
**/
|
EFI_STATUS
|
GbeMdiSetPage (
|
IN UINT32 GbeBar,
|
IN UINT32 Page
|
)
|
{
|
EFI_STATUS Status;
|
|
MmioWrite32 (GbeBar + R_GBE_MEM_CSR_MDIC, (~B_PHY_MDI_READY) & (B_PHY_MDI_WRITE | B_PHY_MDI_PHY_ADDRESS_01 | R_PHY_MDI_PHY_REG_SET_PAGE | ((Page * 32) & 0xFFFF)));
|
|
Status = GbeMdiWaitReady (GbeBar);
|
|
if (Status == EFI_TIMEOUT) {
|
DEBUG ((DEBUG_INFO, "GbeMdiSetPage Timeout reached. Forcing the interface between the MAC and the Phy to be on SMBus\n"));
|
GbeMdiForceMacToSmb (GbeBar);
|
//
|
// Retry page setting
|
//
|
MmioWrite32 (GbeBar + R_GBE_MEM_CSR_MDIC, (~B_PHY_MDI_READY) & (B_PHY_MDI_WRITE | B_PHY_MDI_PHY_ADDRESS_01 | R_PHY_MDI_PHY_REG_SET_PAGE | ((Page * 32) & 0xFFFF)));
|
Status = GbeMdiWaitReady (GbeBar);
|
if (Status == EFI_TIMEOUT) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiSetPage retry page setting failed!\n"));
|
return EFI_DEVICE_ERROR;
|
}
|
}
|
|
//
|
// Delay required for page to set properly
|
//
|
MicroSecondDelay (GBE_MDI_SET_PAGE_DELAY);
|
|
return Status;
|
}
|
|
/**
|
Sets Register in current page.
|
|
@param [in] GbeBar GbE MMIO space
|
@param [in] register Register number valid only in lower 16 Bits
|
|
@return EFI_STATUS
|
**/
|
EFI_STATUS
|
GbeMdiSetRegister (
|
IN UINT32 GbeBar,
|
IN UINT32 Register
|
)
|
{
|
MmioWrite32 (GbeBar + R_GBE_MEM_CSR_MDIC, (~B_PHY_MDI_READY) & (B_PHY_MDI_WRITE | B_PHY_MDI_PHY_ADDRESS_01 | R_PHY_MDI_PHY_REG_SET_ADDRESS | (Register & 0xFFFF)));
|
return GbeMdiWaitReady (GbeBar);
|
}
|
|
/**
|
Perform MDI write.
|
|
@param [in] GbeBar GbE MMIO space
|
@param [in] PhyAddress Phy Address General - 02 or Specific - 01
|
@param [in] PhyRegister Phy Register
|
@param [in] WriteData Value to write in lower 16bits.
|
|
@retval EFI_SUCCESS Based on response from GbeMdiWaitReady
|
@retval EFI_TIMEOUT Based on response from GbeMdiWaitReady
|
@retval EFI_INVALID_PARAMETER If Phy Address or Register validaton failed
|
**/
|
EFI_STATUS
|
GbeMdiWrite (
|
IN UINT32 GbeBar,
|
IN UINT32 PhyAddress,
|
IN UINT32 PhyRegister,
|
IN UINT32 WriteData
|
)
|
{
|
if(!IsPhyAddressRegisterValid (PhyAddress, PhyRegister)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiWrite PhyAddressRegister validaton failed!\n"));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
MmioWrite32 (GbeBar + R_GBE_MEM_CSR_MDIC, (~B_PHY_MDI_READY) & (B_PHY_MDI_WRITE | PhyAddress | PhyRegister | (WriteData & 0xFFFF)));
|
return GbeMdiWaitReady (GbeBar);
|
}
|
|
/**
|
Perform MDI read.
|
|
@param [in] GbeBar GbE MMIO space
|
@param [in] PhyAddress Phy Address General - 02 or Specific - 01
|
@param [in] PhyRegister Phy Register
|
@param [out] ReadData Return Value
|
|
@retval EFI_SUCCESS Based on response from GbeMdiWaitReady
|
@retval EFI_TIMEOUT Based on response from GbeMdiWaitReady
|
@retval EFI_INVALID_PARAMETER If Phy Address or Register validaton failed
|
**/
|
EFI_STATUS
|
GbeMdiRead (
|
IN UINT32 GbeBar,
|
IN UINT32 PhyAddress,
|
IN UINT32 PhyRegister,
|
OUT UINT16 *ReadData
|
)
|
{
|
EFI_STATUS Status;
|
|
if(!IsPhyAddressRegisterValid (PhyAddress, PhyRegister)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiRead PhyAddressRegister validaton failed!\n"));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
MmioWrite32 (GbeBar + R_GBE_MEM_CSR_MDIC, (~B_PHY_MDI_READY) & (B_PHY_MDI_READ | PhyAddress | PhyRegister));
|
Status = GbeMdiWaitReady (GbeBar);
|
if (EFI_SUCCESS == Status) {
|
*ReadData = (UINT16) MmioRead32 (GbeBar + R_GBE_MEM_CSR_MDIC);
|
}
|
return Status;
|
}
|
|
/**
|
Gets Phy Revision and Model Number
|
from PHY IDENTIFIER register 2 (offset 3)
|
|
@param [in] GbeBar GbE MMIO space
|
@param [out] LanPhyRevision Return Value
|
|
@return EFI_STATUS
|
@return EFI_INVALID_PARAMETER When GbeBar is incorrect
|
When Phy register or address is out of bounds
|
**/
|
EFI_STATUS
|
GbeMdiGetLanPhyRevision (
|
IN UINT32 GbeBar,
|
OUT UINT16 *LanPhyRevision
|
)
|
{
|
EFI_STATUS Status;
|
UINT8 LpcdLoop;
|
UINT8 Delay;
|
|
if (!((GbeBar & 0xFFFFF000) > 0)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision GbeBar validation failed! Bar: 0x%08X \n", GbeBar));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
Status = GbeMdiAcquireMdio (GbeBar);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision failed to aquire MDIO semaphore. Status: %r\n", Status));
|
return Status;
|
}
|
|
Status = GbeMdiSetPage (GbeBar, PHY_MDI_PAGE_769_PORT_CONTROL_REGISTERS);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision failed to Set Page 769. Status: %r\n", Status));
|
GbeMdiReleaseMdio (GbeBar);
|
return Status;
|
}
|
|
//
|
// Set register to: Custom Mode Control
|
// Reduced MDIO frequency access (slow mdio)
|
// BIT 10 set to 1
|
//
|
Status = GbeMdiWrite (GbeBar, B_PHY_MDI_PHY_ADDRESS_01, MDI_REG_SHIFT (R_PHY_MDI_PAGE_769_REGISETER_16_CMC), BIT13 | B_PHY_MDI_PAGE_769_REGISETER_16_CMC_MDIO_FREQ_ACCESS | BIT8 | BIT7);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision failed to enable slow MDIO mode. Status: %r\n", Status));
|
GbeMdiReleaseMdio (GbeBar);
|
return Status;
|
}
|
|
//
|
// Read register PHY Version from PHY IDENTIFIER 2 (offset 0x3)
|
// Bits [9:4] - Device Model Number
|
// Bits [3:0] - Device Revision Number
|
//
|
Status = GbeMdiRead (GbeBar, B_PHY_MDI_PHY_ADDRESS_02, R_PHY_MDI_GENEREAL_REGISTER_03_PHY_IDENTIFIER_2, LanPhyRevision);
|
|
//
|
// Failed to obtain PHY REV
|
//
|
if (*LanPhyRevision == 0x0) {
|
if ((MmioRead32 (GbeBar + R_GBE_MEM_CSR_CTRL) & (B_GBE_MEM_CSR_CTRL_LANPHYPC_OVERRIDE | B_GBE_MEM_CSR_CTRL_LANPHYPC_VAL))) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision failed to read Phy Revision. Other component tried to initialize GbE and failed.\n"));
|
Status = EFI_DEVICE_ERROR;
|
goto phy_exit;
|
}
|
DEBUG ((DEBUG_INFO, "GbeMdiGetLanPhyRevision failed to read Revision. Overriding LANPHYPC\n", Status));
|
//
|
// Taking over LANPHYPC
|
// 1. SW signal override - 1st cycle.
|
// 2. Turn LCD on - 2nd cycle.
|
//
|
MmioOr32 (GbeBar + R_GBE_MEM_CSR_CTRL, B_GBE_MEM_CSR_CTRL_LANPHYPC_OVERRIDE);
|
MmioOr32 (GbeBar + R_GBE_MEM_CSR_CTRL, B_GBE_MEM_CSR_CTRL_LANPHYPC_VAL);
|
|
//
|
// Poll on LPCD for 100mSec
|
//
|
LpcdLoop = 101;
|
while (LpcdLoop > 0) {
|
if (MmioRead32 (GbeBar + R_GBE_MEM_CSR_CTRL_EXT) & B_GBE_MEM_CSR_CTRL_EXT_LPCD) {
|
break;
|
} else {
|
LpcdLoop--;
|
MicroSecondDelay (1000);
|
}
|
}
|
|
if (LpcdLoop > 0) {
|
Delay = 100;
|
Status = GbeMdiRead (GbeBar, B_PHY_MDI_PHY_ADDRESS_02, R_PHY_MDI_GENEREAL_REGISTER_03_PHY_IDENTIFIER_2, LanPhyRevision);
|
while (*LanPhyRevision == 0 && Delay > 0) {
|
Status = GbeMdiRead (GbeBar, B_PHY_MDI_PHY_ADDRESS_02, R_PHY_MDI_GENEREAL_REGISTER_03_PHY_IDENTIFIER_2, LanPhyRevision);
|
if (EFI_ERROR(Status)) {
|
break;
|
}
|
MicroSecondDelay (1000);
|
Delay --;
|
}
|
}
|
//
|
// Restore LANPHYPC
|
// 1. Turn LCD off - 1st cycle.
|
// 2. Remove SW signal override - 2nd cycle.
|
//
|
MmioAnd32 (GbeBar + R_GBE_MEM_CSR_CTRL, (UINT32) ~B_GBE_MEM_CSR_CTRL_LANPHYPC_VAL);
|
MmioAnd32 (GbeBar + R_GBE_MEM_CSR_CTRL, (UINT32) ~B_GBE_MEM_CSR_CTRL_LANPHYPC_OVERRIDE);
|
}
|
|
phy_exit:
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision failed to read Revision and Model Number from PHY Identifier 2. Status: %r\n", Status));
|
GbeMdiReleaseMdio (GbeBar);
|
return Status;
|
}
|
|
//
|
// Switch back to normal MDIO frequency access
|
//
|
Status = GbeMdiWrite (GbeBar, B_PHY_MDI_PHY_ADDRESS_01, MDI_REG_SHIFT (R_PHY_MDI_PAGE_769_REGISETER_16_CMC), (~B_PHY_MDI_PAGE_769_REGISETER_16_CMC_MDIO_FREQ_ACCESS) & (BIT13 | BIT8 | BIT7));
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "GbeMdiGetLanPhyRevision failed to disable slow MDIO mode. Status: %r\n", Status));
|
}
|
|
GbeMdiReleaseMdio (GbeBar);
|
|
return Status;
|
}
|
|