/** @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) 2020 Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Specification Reference:
**/
#include
#include
#include
#include
#include
#include
#include
#include
/**
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
EFIAPI
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
EFIAPI
GbeMdiForceMacToSmb (
IN UINT32 GbeBar
)
{
MmioOr32 (GbeBar + R_LAN_MEM_CSR_CTRL_EXT, B_LAN_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
EFIAPI
GbeMdiWaitReady (
IN UINT32 GbeBar
)
{
UINT32 Count;
for (Count = 0; Count < GBE_MAX_LOOP_TIME; ++Count) {
if (MmioRead32 (GbeBar + R_LAN_MEM_CSR_MDIC) & B_LAN_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
EFIAPI
GbeMdiAcquireMdio (
IN UINT32 GbeBar
)
{
UINT32 ExtCnfCtrl;
UINT32 Count;
MmioOr32 (GbeBar + R_LAN_MEM_CSR_EXTCNF_CTRL, B_LAN_MEM_CSR_EXTCNF_CTRL_SWFLAG);
for (Count = 0; Count < GBE_MAX_LOOP_TIME; ++Count) {
ExtCnfCtrl = MmioRead32 (GbeBar + R_LAN_MEM_CSR_EXTCNF_CTRL);
if (ExtCnfCtrl & B_LAN_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
EFIAPI
GbeMdiReleaseMdio (
IN UINT32 GbeBar
)
{
ASSERT (MmioRead32 (GbeBar + R_LAN_MEM_CSR_EXTCNF_CTRL) & B_LAN_MEM_CSR_EXTCNF_CTRL_SWFLAG);
MmioAnd32 (GbeBar + R_LAN_MEM_CSR_EXTCNF_CTRL, (UINT32) ~B_LAN_MEM_CSR_EXTCNF_CTRL_SWFLAG);
ASSERT ((MmioRead32 (GbeBar + R_LAN_MEM_CSR_EXTCNF_CTRL) & B_LAN_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
EFIAPI
GbeMdiSetPage (
IN UINT32 GbeBar,
IN UINT32 Page
)
{
EFI_STATUS Status;
MmioWrite32 (GbeBar + R_LAN_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_LAN_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
EFIAPI
GbeMdiSetRegister (
IN UINT32 GbeBar,
IN UINT32 Register
)
{
MmioWrite32 (GbeBar + R_LAN_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
EFIAPI
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_LAN_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
EFIAPI
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_LAN_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_LAN_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
EFIAPI
GbeMdiGetLanPhyRevision (
IN UINT32 GbeBar,
OUT UINT16 *LanPhyRevision
)
{
EFI_STATUS Status;
UINT8 LpcdLoop;
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_LAN_MEM_CSR_CTRL) & (B_LAN_MEM_CSR_CTRL_LANPHYPC_OVERRIDE | B_LAN_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_LAN_MEM_CSR_CTRL, B_LAN_MEM_CSR_CTRL_LANPHYPC_OVERRIDE);
MmioOr32 (GbeBar + R_LAN_MEM_CSR_CTRL, B_LAN_MEM_CSR_CTRL_LANPHYPC_VAL);
//
// Poll on LPCD for 100mSec
//
LpcdLoop = 101;
while (LpcdLoop > 0) {
if (MmioRead32 (GbeBar + R_LAN_MEM_CSR_CTRL_EXT) & B_LAN_MEM_CSR_CTRL_EXT_LPCD) {
break;
} else {
LpcdLoop--;
MicroSecondDelay (1000);
}
}
if (LpcdLoop > 0) {
Status = GbeMdiRead (GbeBar, B_PHY_MDI_PHY_ADDRESS_02, R_PHY_MDI_GENEREAL_REGISTER_03_PHY_IDENTIFIER_2, LanPhyRevision);
}
//
// Restore LANPHYPC
// 1. Turn LCD off - 1st cycle.
// 2. Remove SW signal override - 2nd cycle.
//
MmioAnd32 (GbeBar + R_LAN_MEM_CSR_CTRL, (UINT32) ~B_LAN_MEM_CSR_CTRL_LANPHYPC_VAL);
MmioAnd32 (GbeBar + R_LAN_MEM_CSR_CTRL, (UINT32) ~B_LAN_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;
}