The UEFI specification does not require the initialisation and reset interface to check if an Ethernet cable is connected or not, and provides the GetStatus() interface to do this. Furthermore, the 'Managed Network Protocol' take care of the cable connection check in edk2 network stack. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron <ronald.cron@arm.com> Reviewed-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16326 6f19259b-4bc3-4df7-8a09-765794883524
1006 lines
26 KiB
C
1006 lines
26 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2012-2014, ARM Limited. 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.
|
|
*
|
|
**/
|
|
|
|
#include "Lan9118Dxe.h"
|
|
|
|
STATIC EFI_MAC_ADDRESS mZeroMac = { { 0 } };
|
|
|
|
/**
|
|
This internal function reverses bits for 32bit data.
|
|
|
|
@param Value The data to be reversed.
|
|
|
|
@return Data reversed.
|
|
|
|
**/
|
|
UINT32
|
|
ReverseBits (
|
|
UINT32 Value
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT32 NewValue;
|
|
|
|
NewValue = 0;
|
|
for (Index = 0; Index < 32; Index++) {
|
|
if ((Value & (1 << Index)) != 0) {
|
|
NewValue = NewValue | (1 << (31 - Index));
|
|
}
|
|
}
|
|
|
|
return NewValue;
|
|
}
|
|
|
|
/*
|
|
** Create Ethernet CRC
|
|
**
|
|
** INFO USED:
|
|
** 1: http://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
|
**
|
|
** 2: http://www.erg.abdn.ac.uk/~gorry/eg3567/dl-pages/crc.html
|
|
**
|
|
** 3: http://en.wikipedia.org/wiki/Computation_of_CRC
|
|
*/
|
|
UINT32
|
|
GenEtherCrc32 (
|
|
IN EFI_MAC_ADDRESS *Mac,
|
|
IN UINT32 AddrLen
|
|
)
|
|
{
|
|
INT32 Iter;
|
|
UINT32 Remainder;
|
|
UINT8 *Ptr;
|
|
|
|
Iter = 0;
|
|
Remainder = 0xFFFFFFFF; // 0xFFFFFFFF is standard seed for Ethernet
|
|
|
|
// Convert Mac Address to array of bytes
|
|
Ptr = (UINT8*)Mac;
|
|
|
|
// Generate the Crc bit-by-bit (LSB first)
|
|
while (AddrLen--) {
|
|
Remainder ^= *Ptr++;
|
|
for (Iter = 0;Iter < 8;Iter++) {
|
|
// Check if exponent is set
|
|
if (Remainder & 1) {
|
|
Remainder = (Remainder >> 1) ^ CRC_POLYNOMIAL;
|
|
} else {
|
|
Remainder = (Remainder >> 1) ^ 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reverse the bits before returning (to Big Endian)
|
|
//TODO: Need to be reviewed. Do we want to do a bit reverse or a byte reverse (in this case use SwapBytes32())
|
|
return ReverseBits (Remainder);
|
|
}
|
|
|
|
// Function to read from MAC indirect registers
|
|
UINT32
|
|
IndirectMACRead32 (
|
|
UINT32 Index
|
|
)
|
|
{
|
|
UINT32 MacCSR;
|
|
|
|
// Check index is in the range
|
|
ASSERT(Index <= 12);
|
|
|
|
// Wait until CSR busy bit is cleared
|
|
while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
|
|
|
|
// Set CSR busy bit to ensure read will occur
|
|
// Set the R/W bit to indicate we are reading
|
|
// Set the index of CSR Address to access desired register
|
|
MacCSR = MAC_CSR_BUSY | MAC_CSR_READ | MAC_CSR_ADDR(Index);
|
|
|
|
// Write to the register
|
|
MmioWrite32 (LAN9118_MAC_CSR_CMD, MacCSR);
|
|
|
|
// Wait until CSR busy bit is cleared
|
|
while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
|
|
|
|
// Now read from data register to get read value
|
|
return MmioRead32 (LAN9118_MAC_CSR_DATA);
|
|
}
|
|
|
|
// Function to write to MAC indirect registers
|
|
UINT32
|
|
IndirectMACWrite32 (
|
|
UINT32 Index,
|
|
UINT32 Value
|
|
)
|
|
{
|
|
UINT32 ValueWritten;
|
|
UINT32 MacCSR;
|
|
|
|
// Check index is in the range
|
|
ASSERT(Index <= 12);
|
|
|
|
// Wait until CSR busy bit is cleared
|
|
while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
|
|
|
|
// Set CSR busy bit to ensure read will occur
|
|
// Set the R/W bit to indicate we are writing
|
|
// Set the index of CSR Address to access desired register
|
|
MacCSR = MAC_CSR_BUSY | MAC_CSR_WRITE | MAC_CSR_ADDR(Index);
|
|
|
|
// Now write the value to the register before issuing the write command
|
|
ValueWritten = MmioWrite32 (LAN9118_MAC_CSR_DATA, Value);
|
|
|
|
// Write the config to the register
|
|
MmioWrite32 (LAN9118_MAC_CSR_CMD, MacCSR);
|
|
|
|
// Wait until CSR busy bit is cleared
|
|
while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
|
|
|
|
return ValueWritten;
|
|
}
|
|
|
|
// Function to read from MII register (PHY Access)
|
|
UINT32
|
|
IndirectPHYRead32 (
|
|
UINT32 Index
|
|
)
|
|
{
|
|
UINT32 ValueRead;
|
|
UINT32 MiiAcc;
|
|
|
|
// Check it is a valid index
|
|
ASSERT(Index < 31);
|
|
|
|
// Wait for busy bit to clear
|
|
while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
|
|
|
|
// Clear the R/W bit to indicate we are reading
|
|
// Set the index of the MII register
|
|
// Set the PHY Address
|
|
// Set the MII busy bit to allow read
|
|
MiiAcc = MII_ACC_MII_READ | MII_ACC_MII_REG_INDEX(Index) | MII_ACC_PHY_VALUE | MII_ACC_MII_BUSY;
|
|
|
|
// Now write this config to register
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_ACC, MiiAcc & 0xFFFF);
|
|
|
|
// Wait for busy bit to clear
|
|
while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
|
|
|
|
// Now read the value of the register
|
|
ValueRead = (IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_DATA) & 0xFFFF); // only lower 16 bits are valid for any PHY register
|
|
|
|
return ValueRead;
|
|
}
|
|
|
|
|
|
// Function to write to the MII register (PHY Access)
|
|
UINT32
|
|
IndirectPHYWrite32 (
|
|
UINT32 Index,
|
|
UINT32 Value
|
|
)
|
|
{
|
|
UINT32 MiiAcc;
|
|
UINT32 ValueWritten;
|
|
|
|
// Check it is a valid index
|
|
ASSERT(Index < 31);
|
|
|
|
// Wait for busy bit to clear
|
|
while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
|
|
|
|
// Clear the R/W bit to indicate we are reading
|
|
// Set the index of the MII register
|
|
// Set the PHY Address
|
|
// Set the MII busy bit to allow read
|
|
MiiAcc = MII_ACC_MII_WRITE | MII_ACC_MII_REG_INDEX(Index) | MII_ACC_PHY_VALUE | MII_ACC_MII_BUSY;
|
|
|
|
// Write the desired value to the register first
|
|
ValueWritten = IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_DATA, (Value & 0xFFFF));
|
|
|
|
// Now write the config to register
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_ACC, MiiAcc & 0xFFFF);
|
|
|
|
// Wait for operation to terminate
|
|
while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
|
|
|
|
return ValueWritten;
|
|
}
|
|
|
|
|
|
/* ---------------- EEPROM Operations ------------------ */
|
|
|
|
|
|
// Function to read from EEPROM memory
|
|
UINT32
|
|
IndirectEEPROMRead32 (
|
|
UINT32 Index
|
|
)
|
|
{
|
|
UINT32 EepromCmd;
|
|
|
|
// Set the busy bit to ensure read will occur
|
|
EepromCmd = E2P_EPC_BUSY | E2P_EPC_CMD_READ;
|
|
|
|
// Set the index to access desired EEPROM memory location
|
|
EepromCmd |= E2P_EPC_ADDRESS(Index);
|
|
|
|
// Write to Eeprom command register
|
|
MmioWrite32 (LAN9118_E2P_CMD, EepromCmd);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
// Wait until operation has completed
|
|
while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
|
|
|
|
// Check that operation didn't time out
|
|
if (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_TIMEOUT) {
|
|
DEBUG ((EFI_D_ERROR, "EEPROM Operation Timed out: Read command on index %x\n",Index));
|
|
return 0;
|
|
}
|
|
|
|
// Wait until operation has completed
|
|
while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
|
|
|
|
// Finally read the value
|
|
return MmioRead32 (LAN9118_E2P_DATA);
|
|
}
|
|
|
|
// Function to write to EEPROM memory
|
|
UINT32
|
|
IndirectEEPROMWrite32 (
|
|
UINT32 Index,
|
|
UINT32 Value
|
|
)
|
|
{
|
|
UINT32 ValueWritten;
|
|
UINT32 EepromCmd;
|
|
|
|
ValueWritten = 0;
|
|
|
|
// Read the EEPROM Command register
|
|
EepromCmd = MmioRead32 (LAN9118_E2P_CMD);
|
|
|
|
// Set the busy bit to ensure read will occur
|
|
EepromCmd |= ((UINT32)1 << 31);
|
|
|
|
// Set the EEPROM command to write(0b011)
|
|
EepromCmd &= ~(7 << 28); // Clear the command first
|
|
EepromCmd |= (3 << 28); // Write 011
|
|
|
|
// Set the index to access desired EEPROM memory location
|
|
EepromCmd |= (Index & 0xF);
|
|
|
|
// Write the value to the data register first
|
|
ValueWritten = MmioWrite32 (LAN9118_E2P_DATA, Value);
|
|
|
|
// Write to Eeprom command register
|
|
MmioWrite32 (LAN9118_E2P_CMD, EepromCmd);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
// Wait until operation has completed
|
|
while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
|
|
|
|
// Check that operation didn't time out
|
|
if (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_TIMEOUT) {
|
|
DEBUG ((EFI_D_ERROR, "EEPROM Operation Timed out: Write command at memloc 0x%x, with value 0x%x\n",Index, Value));
|
|
return 0;
|
|
}
|
|
|
|
// Wait until operation has completed
|
|
while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
|
|
|
|
return ValueWritten;
|
|
}
|
|
|
|
/* ---------------- General Operations ----------------- */
|
|
|
|
VOID
|
|
Lan9118SetMacAddress (
|
|
EFI_MAC_ADDRESS *Mac,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL,
|
|
(Mac->Addr[0] & 0xFF) |
|
|
((Mac->Addr[1] & 0xFF) << 8) |
|
|
((Mac->Addr[2] & 0xFF) << 16) |
|
|
((Mac->Addr[3] & 0xFF) << 24)
|
|
);
|
|
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH,
|
|
(UINT32)(Mac->Addr[4] & 0xFF) |
|
|
((Mac->Addr[5] & 0xFF) << 8)
|
|
);
|
|
}
|
|
|
|
VOID
|
|
Lan9118ReadMacAddress (
|
|
OUT EFI_MAC_ADDRESS *MacAddress
|
|
)
|
|
{
|
|
UINT32 MacAddrHighValue;
|
|
UINT32 MacAddrLowValue;
|
|
|
|
// Read the Mac Addr high register
|
|
MacAddrHighValue = (IndirectMACRead32 (INDIRECT_MAC_INDEX_ADDRH) & 0xFFFF);
|
|
// Read the Mac Addr low register
|
|
MacAddrLowValue = IndirectMACRead32 (INDIRECT_MAC_INDEX_ADDRL);
|
|
|
|
SetMem (MacAddress, sizeof(*MacAddress), 0);
|
|
MacAddress->Addr[0] = (MacAddrLowValue & 0xFF);
|
|
MacAddress->Addr[1] = (MacAddrLowValue & 0xFF00) >> 8;
|
|
MacAddress->Addr[2] = (MacAddrLowValue & 0xFF0000) >> 16;
|
|
MacAddress->Addr[3] = (MacAddrLowValue & 0xFF000000) >> 24;
|
|
MacAddress->Addr[4] = (MacAddrHighValue & 0xFF);
|
|
MacAddress->Addr[5] = (MacAddrHighValue & 0xFF00) >> 8;
|
|
}
|
|
|
|
/*
|
|
* Power up the 9118 and find its MAC address.
|
|
*
|
|
* This operation can be carried out when the LAN9118 is in any power state
|
|
*
|
|
*/
|
|
EFI_STATUS
|
|
Lan9118Initialize (
|
|
IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINTN Timeout;
|
|
UINT64 DefaultMacAddress;
|
|
|
|
// Attempt to wake-up the device if it is in a lower power state
|
|
if (((MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_PM_MODE_MASK) >> 12) != 0) {
|
|
DEBUG ((DEBUG_NET, "Waking from reduced power state.\n"));
|
|
MmioWrite32 (LAN9118_BYTE_TEST, 0xFFFFFFFF);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
|
|
// Check that device is active
|
|
Timeout = 20;
|
|
while ((MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_READY) == 0 && --Timeout) {
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
if (!Timeout) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
// Check that EEPROM isn't active
|
|
Timeout = 20;
|
|
while ((MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY) && --Timeout){
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
if (!Timeout) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
// Check if a MAC address was loaded from EEPROM, and if it was, set it as the
|
|
// current address.
|
|
if ((MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_MAC_ADDRESS_LOADED) == 0) {
|
|
DEBUG ((EFI_D_ERROR, "Warning: There was an error detecting EEPROM or loading the MAC Address.\n"));
|
|
|
|
// If we had an address before (set by StationAddess), continue to use it
|
|
if (CompareMem (&Snp->Mode->CurrentAddress, &mZeroMac, NET_ETHER_ADDR_LEN)) {
|
|
Lan9118SetMacAddress (&Snp->Mode->CurrentAddress, Snp);
|
|
} else {
|
|
// If there are no cached addresses, then fall back to a default
|
|
DEBUG ((EFI_D_WARN, "Warning: using driver-default MAC address\n"));
|
|
DefaultMacAddress = FixedPcdGet64 (PcdLan9118DefaultMacAddress);
|
|
Lan9118SetMacAddress((EFI_MAC_ADDRESS *) &DefaultMacAddress, Snp);
|
|
CopyMem (&Snp->Mode->CurrentAddress, &DefaultMacAddress, NET_ETHER_ADDR_LEN);
|
|
}
|
|
} else {
|
|
// Store the MAC address that was loaded from EEPROM
|
|
Lan9118ReadMacAddress (&Snp->Mode->CurrentAddress);
|
|
CopyMem (&Snp->Mode->PermanentAddress, &Snp->Mode->CurrentAddress, NET_ETHER_ADDR_LEN);
|
|
}
|
|
|
|
// Clear and acknowledge interrupts
|
|
MmioWrite32 (LAN9118_INT_EN, 0);
|
|
MmioWrite32 (LAN9118_IRQ_CFG, 0);
|
|
MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF);
|
|
|
|
// Do self tests here?
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
// Perform software reset on the LAN9118
|
|
// Return 0 on success, -1 on error
|
|
EFI_STATUS
|
|
SoftReset (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 HwConf;
|
|
UINT32 ResetTime;
|
|
|
|
// Initialize variable
|
|
ResetTime = 0;
|
|
|
|
// Stop Rx and Tx
|
|
StopTx (STOP_TX_MAC | STOP_TX_CFG | STOP_TX_CLEAR, Snp);
|
|
StopRx (STOP_RX_CLEAR, Snp); // Clear receiver FIFO
|
|
|
|
// Issue the reset
|
|
HwConf = MmioRead32 (LAN9118_HW_CFG);
|
|
HwConf |= 1;
|
|
|
|
// Set the Must Be One (MBO) bit
|
|
if (((HwConf & HWCFG_MBO) >> 20) == 0) {
|
|
HwConf |= HWCFG_MBO;
|
|
}
|
|
|
|
// Check that EEPROM isn't active
|
|
while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
|
|
|
|
// Write the configuration
|
|
MmioWrite32 (LAN9118_HW_CFG, HwConf);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
// Wait for reset to complete
|
|
while (MmioRead32 (LAN9118_HW_CFG) & HWCFG_SRST) {
|
|
|
|
gBS->Stall (LAN9118_STALL);
|
|
ResetTime += 1;
|
|
|
|
// If time taken exceeds 100us, then there was an error condition
|
|
if (ResetTime > 1000) {
|
|
Snp->Mode->State = EfiSimpleNetworkStopped;
|
|
return EFI_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
// Check that EEPROM isn't active
|
|
while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
|
|
|
|
// TODO we probably need to re-set the mac address here.
|
|
|
|
// Clear and acknowledge all interrupts
|
|
if (Flags & SOFT_RESET_CLEAR_INT) {
|
|
MmioWrite32 (LAN9118_INT_EN, 0);
|
|
MmioWrite32 (LAN9118_IRQ_CFG, 0);
|
|
MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF);
|
|
}
|
|
|
|
// Do self tests here?
|
|
if (Flags & SOFT_RESET_SELF_TEST) {
|
|
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
// Perform PHY software reset
|
|
EFI_STATUS
|
|
PhySoftReset (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 PmtCtrl = 0;
|
|
|
|
// PMT PHY reset takes precedence over BCR
|
|
if (Flags & PHY_RESET_PMT) {
|
|
PmtCtrl = MmioRead32 (LAN9118_PMT_CTRL);
|
|
PmtCtrl |= MPTCTRL_PHY_RST;
|
|
MmioWrite32 (LAN9118_PMT_CTRL,PmtCtrl);
|
|
|
|
// Wait for completion
|
|
while (MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_PHY_RST) {
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
// PHY Basic Control Register reset
|
|
} else if (Flags & PHY_RESET_BCR) {
|
|
IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PHYCR_RESET);
|
|
|
|
// Wait for completion
|
|
while (IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) {
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
}
|
|
|
|
// Clear and acknowledge all interrupts
|
|
if (Flags & PHY_SOFT_RESET_CLEAR_INT) {
|
|
MmioWrite32 (LAN9118_INT_EN, 0);
|
|
MmioWrite32 (LAN9118_IRQ_CFG, 0);
|
|
MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
// Configure hardware for LAN9118
|
|
EFI_STATUS
|
|
ConfigureHardware (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 GpioConf;
|
|
|
|
// Check if we want to use LEDs on GPIO
|
|
if (Flags & HW_CONF_USE_LEDS) {
|
|
GpioConf = MmioRead32 (LAN9118_GPIO_CFG);
|
|
|
|
// Enable GPIO as LEDs and Config as Push-Pull driver
|
|
GpioConf |= GPIO_GPIO0_PUSH_PULL | GPIO_GPIO1_PUSH_PULL | GPIO_GPIO2_PUSH_PULL |
|
|
GPIO_LED1_ENABLE | GPIO_LED2_ENABLE | GPIO_LED3_ENABLE;
|
|
|
|
// Write the configuration
|
|
MmioWrite32 (LAN9118_GPIO_CFG, GpioConf);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Configure flow control
|
|
EFI_STATUS
|
|
ConfigureFlow (
|
|
UINT32 Flags,
|
|
UINT32 HighTrig,
|
|
UINT32 LowTrig,
|
|
UINT32 BPDuration,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Do auto-negotiation
|
|
EFI_STATUS
|
|
AutoNegotiate (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 PhyControl;
|
|
UINT32 PhyStatus;
|
|
UINT32 Features;
|
|
UINT32 TimeOut;
|
|
|
|
// First check that auto-negotiation is supported
|
|
PhyStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS);
|
|
if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) {
|
|
DEBUG ((EFI_D_ERROR, "Auto-negotiation not supported.\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
// Check that link is up first
|
|
if ((PhyStatus & PHYSTS_LINK_STS) == 0) {
|
|
// Wait until it is up or until Time Out
|
|
TimeOut = 2000;
|
|
while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_LINK_STS) == 0) {
|
|
gBS->Stall (LAN9118_STALL);
|
|
TimeOut--;
|
|
if (!TimeOut) {
|
|
DEBUG ((EFI_D_ERROR, "Link timeout in auto-negotiation.\n"));
|
|
return EFI_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Configure features to advertise
|
|
Features = IndirectPHYRead32 (PHY_INDEX_AUTO_NEG_ADVERT);
|
|
|
|
if ((Flags & AUTO_NEGOTIATE_ADVERTISE_ALL) > 0) {
|
|
// Link speed capabilities
|
|
Features |= (PHYANA_10BASET | PHYANA_10BASETFD | PHYANA_100BASETX | PHYANA_100BASETXFD);
|
|
|
|
// Pause frame capabilities
|
|
Features &= ~(PHYANA_PAUSE_OP_MASK);
|
|
Features |= 3 << 10;
|
|
}
|
|
|
|
// Write the features
|
|
IndirectPHYWrite32 (PHY_INDEX_AUTO_NEG_ADVERT, Features);
|
|
|
|
// Read control register
|
|
PhyControl = IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL);
|
|
|
|
// Enable Auto-Negotiation
|
|
if ((PhyControl & PHYCR_AUTO_EN) == 0) {
|
|
PhyControl |= PHYCR_AUTO_EN;
|
|
}
|
|
|
|
// Restart auto-negotiation
|
|
PhyControl |= PHYCR_RST_AUTO;
|
|
|
|
// Enable collision test if required to do so
|
|
if (Flags & AUTO_NEGOTIATE_COLLISION_TEST) {
|
|
PhyControl |= PHYCR_COLL_TEST;
|
|
} else {
|
|
PhyControl &= ~ PHYCR_COLL_TEST;
|
|
}
|
|
|
|
// Write this configuration
|
|
IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PhyControl);
|
|
|
|
// Wait until process has completed
|
|
while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Check the Link Status and take appropriate action
|
|
EFI_STATUS
|
|
CheckLinkStatus (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
// Get the PHY Status
|
|
UINT32 PhyBStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS);
|
|
|
|
if (PhyBStatus & PHYSTS_LINK_STS) {
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
// Stop the transmitter
|
|
EFI_STATUS
|
|
StopTx (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 MacCsr;
|
|
UINT32 TxCfg;
|
|
|
|
MacCsr = 0;
|
|
TxCfg = 0;
|
|
|
|
// Check if we want to clear tx
|
|
if (Flags & STOP_TX_CLEAR) {
|
|
TxCfg = MmioRead32 (LAN9118_TX_CFG);
|
|
TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP;
|
|
MmioWrite32 (LAN9118_TX_CFG, TxCfg);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
|
|
// Check if already stopped
|
|
if (Flags & STOP_TX_MAC) {
|
|
MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
|
|
|
|
if (MacCsr & MACCR_TX_EN) {
|
|
MacCsr &= ~MACCR_TX_EN;
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
|
|
}
|
|
}
|
|
|
|
if (Flags & STOP_TX_CFG) {
|
|
TxCfg = MmioRead32 (LAN9118_TX_CFG);
|
|
|
|
if (TxCfg & TXCFG_TX_ON) {
|
|
TxCfg |= TXCFG_STOP_TX;
|
|
MmioWrite32 (LAN9118_TX_CFG, TxCfg);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
// Wait for Tx to finish transmitting
|
|
while (MmioRead32 (LAN9118_TX_CFG) & TXCFG_STOP_TX);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Stop the receiver
|
|
EFI_STATUS
|
|
StopRx (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 MacCsr;
|
|
UINT32 RxCfg;
|
|
|
|
RxCfg = 0;
|
|
|
|
// Check if already stopped
|
|
MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
|
|
|
|
if (MacCsr & MACCR_RX_EN) {
|
|
MacCsr &= ~ MACCR_RX_EN;
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
|
|
}
|
|
|
|
// Check if we want to clear receiver FIFOs
|
|
if (Flags & STOP_RX_CLEAR) {
|
|
RxCfg = MmioRead32 (LAN9118_RX_CFG);
|
|
RxCfg |= RXCFG_RX_DUMP;
|
|
MmioWrite32 (LAN9118_RX_CFG, RxCfg);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
while (MmioRead32 (LAN9118_RX_CFG) & RXCFG_RX_DUMP);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Start the transmitter
|
|
EFI_STATUS
|
|
StartTx (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 MacCsr;
|
|
UINT32 TxCfg;
|
|
|
|
MacCsr = 0;
|
|
TxCfg = 0;
|
|
|
|
// Check if we want to clear tx
|
|
if (Flags & START_TX_CLEAR) {
|
|
TxCfg = MmioRead32 (LAN9118_TX_CFG);
|
|
TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP;
|
|
MmioWrite32 (LAN9118_TX_CFG, TxCfg);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
|
|
// Check if tx was started from MAC and enable if not
|
|
if (Flags & START_TX_MAC) {
|
|
MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
|
|
gBS->Stall (LAN9118_STALL);
|
|
if ((MacCsr & MACCR_TX_EN) == 0) {
|
|
MacCsr |= MACCR_TX_EN;
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
}
|
|
|
|
// Check if tx was started from TX_CFG and enable if not
|
|
if (Flags & START_TX_CFG) {
|
|
TxCfg = MmioRead32 (LAN9118_TX_CFG);
|
|
gBS->Stall (LAN9118_STALL);
|
|
if ((TxCfg & TXCFG_TX_ON) == 0) {
|
|
TxCfg |= TXCFG_TX_ON;
|
|
MmioWrite32 (LAN9118_TX_CFG, TxCfg);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
}
|
|
|
|
// Set the tx data trigger level
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Start the receiver
|
|
EFI_STATUS
|
|
StartRx (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 MacCsr;
|
|
UINT32 RxCfg;
|
|
|
|
RxCfg = 0;
|
|
|
|
// Check if already started
|
|
MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
|
|
|
|
if ((MacCsr & MACCR_RX_EN) == 0) {
|
|
// Check if we want to clear receiver FIFOs before starting
|
|
if (Flags & START_RX_CLEAR) {
|
|
RxCfg = MmioRead32 (LAN9118_RX_CFG);
|
|
RxCfg |= RXCFG_RX_DUMP;
|
|
MmioWrite32 (LAN9118_RX_CFG, RxCfg);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
while (MmioRead32 (LAN9118_RX_CFG) & RXCFG_RX_DUMP);
|
|
}
|
|
|
|
MacCsr |= MACCR_RX_EN;
|
|
IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
|
|
gBS->Stall (LAN9118_STALL);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Check Tx Data available space
|
|
UINT32
|
|
TxDataFreeSpace (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 TxInf;
|
|
UINT32 FreeSpace;
|
|
|
|
// Get the amount of free space from information register
|
|
TxInf = MmioRead32 (LAN9118_TX_FIFO_INF);
|
|
FreeSpace = (TxInf & TXFIFOINF_TDFREE_MASK);
|
|
|
|
return FreeSpace; // Value in bytes
|
|
}
|
|
|
|
// Check Tx Status used space
|
|
UINT32
|
|
TxStatusUsedSpace (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 TxInf;
|
|
UINT32 UsedSpace;
|
|
|
|
// Get the amount of used space from information register
|
|
TxInf = MmioRead32 (LAN9118_TX_FIFO_INF);
|
|
UsedSpace = (TxInf & TXFIFOINF_TXSUSED_MASK) >> 16;
|
|
|
|
return UsedSpace << 2; // Value in bytes
|
|
}
|
|
|
|
// Check Rx Data used space
|
|
UINT32
|
|
RxDataUsedSpace (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 RxInf;
|
|
UINT32 UsedSpace;
|
|
|
|
// Get the amount of used space from information register
|
|
RxInf = MmioRead32 (LAN9118_RX_FIFO_INF);
|
|
UsedSpace = (RxInf & RXFIFOINF_RXDUSED_MASK);
|
|
|
|
return UsedSpace; // Value in bytes (rounded up to nearest DWORD)
|
|
}
|
|
|
|
// Check Rx Status used space
|
|
UINT32
|
|
RxStatusUsedSpace (
|
|
UINT32 Flags,
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 RxInf;
|
|
UINT32 UsedSpace;
|
|
|
|
// Get the amount of used space from information register
|
|
RxInf = MmioRead32 (LAN9118_RX_FIFO_INF);
|
|
UsedSpace = (RxInf & RXFIFOINF_RXSUSED_MASK) >> 16;
|
|
|
|
return UsedSpace << 2; // Value in bytes
|
|
}
|
|
|
|
|
|
// Change the allocation of FIFOs
|
|
EFI_STATUS
|
|
ChangeFifoAllocation (
|
|
IN UINT32 Flags,
|
|
IN OUT UINTN *TxDataSize OPTIONAL,
|
|
IN OUT UINTN *RxDataSize OPTIONAL,
|
|
IN OUT UINT32 *TxStatusSize OPTIONAL,
|
|
IN OUT UINT32 *RxStatusSize OPTIONAL,
|
|
IN OUT EFI_SIMPLE_NETWORK_PROTOCOL *Snp
|
|
)
|
|
{
|
|
UINT32 HwConf;
|
|
UINT32 TxFifoOption;
|
|
|
|
// Check that desired sizes don't exceed limits
|
|
if (*TxDataSize > TX_FIFO_MAX_SIZE)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
#if defined(RX_FIFO_MIN_SIZE) && defined(RX_FIFO_MAX_SIZE)
|
|
if (*RxDataSize > RX_FIFO_MAX_SIZE) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
#endif
|
|
|
|
if (Flags & ALLOC_USE_DEFAULT) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// If we use the FIFOs (always use this first)
|
|
if (Flags & ALLOC_USE_FIFOS) {
|
|
// Read the current value of allocation
|
|
HwConf = MmioRead32 (LAN9118_HW_CFG);
|
|
TxFifoOption = (HwConf >> 16) & 0xF;
|
|
|
|
// Choose the correct size (always use larger than requested if possible)
|
|
if (*TxDataSize < TX_FIFO_MIN_SIZE) {
|
|
*TxDataSize = TX_FIFO_MIN_SIZE;
|
|
*RxDataSize = 13440;
|
|
*RxStatusSize = 896;
|
|
TxFifoOption = 2;
|
|
} else if ((*TxDataSize > TX_FIFO_MIN_SIZE) && (*TxDataSize <= 2560)) {
|
|
*TxDataSize = 2560;
|
|
*RxDataSize = 12480;
|
|
*RxStatusSize = 832;
|
|
TxFifoOption = 3;
|
|
} else if ((*TxDataSize > 2560) && (*TxDataSize <= 3584)) {
|
|
*TxDataSize = 3584;
|
|
*RxDataSize = 11520;
|
|
*RxStatusSize = 768;
|
|
TxFifoOption = 4;
|
|
} else if ((*TxDataSize > 3584) && (*TxDataSize <= 4608)) { // default option
|
|
*TxDataSize = 4608;
|
|
*RxDataSize = 10560;
|
|
*RxStatusSize = 704;
|
|
TxFifoOption = 5;
|
|
} else if ((*TxDataSize > 4608) && (*TxDataSize <= 5632)) {
|
|
*TxDataSize = 5632;
|
|
*RxDataSize = 9600;
|
|
*RxStatusSize = 640;
|
|
TxFifoOption = 6;
|
|
} else if ((*TxDataSize > 5632) && (*TxDataSize <= 6656)) {
|
|
*TxDataSize = 6656;
|
|
*RxDataSize = 8640;
|
|
*RxStatusSize = 576;
|
|
TxFifoOption = 7;
|
|
} else if ((*TxDataSize > 6656) && (*TxDataSize <= 7680)) {
|
|
*TxDataSize = 7680;
|
|
*RxDataSize = 7680;
|
|
*RxStatusSize = 512;
|
|
TxFifoOption = 8;
|
|
} else if ((*TxDataSize > 7680) && (*TxDataSize <= 8704)) {
|
|
*TxDataSize = 8704;
|
|
*RxDataSize = 6720;
|
|
*RxStatusSize = 448;
|
|
TxFifoOption = 9;
|
|
} else if ((*TxDataSize > 8704) && (*TxDataSize <= 9728)) {
|
|
*TxDataSize = 9728;
|
|
*RxDataSize = 5760;
|
|
*RxStatusSize = 384;
|
|
TxFifoOption = 10;
|
|
} else if ((*TxDataSize > 9728) && (*TxDataSize <= 10752)) {
|
|
*TxDataSize = 10752;
|
|
*RxDataSize = 4800;
|
|
*RxStatusSize = 320;
|
|
TxFifoOption = 11;
|
|
} else if ((*TxDataSize > 10752) && (*TxDataSize <= 11776)) {
|
|
*TxDataSize = 11776;
|
|
*RxDataSize = 3840;
|
|
*RxStatusSize = 256;
|
|
TxFifoOption = 12;
|
|
} else if ((*TxDataSize > 11776) && (*TxDataSize <= 12800)) {
|
|
*TxDataSize = 12800;
|
|
*RxDataSize = 2880;
|
|
*RxStatusSize = 192;
|
|
TxFifoOption = 13;
|
|
} else if ((*TxDataSize > 12800) && (*TxDataSize <= 13824)) {
|
|
*TxDataSize = 13824;
|
|
*RxDataSize = 1920;
|
|
*RxStatusSize = 128;
|
|
TxFifoOption = 14;
|
|
}
|
|
} else {
|
|
ASSERT(0); // Untested code path
|
|
HwConf = 0;
|
|
TxFifoOption = 0;
|
|
}
|
|
|
|
// Do we need DMA?
|
|
if (Flags & ALLOC_USE_DMA) {
|
|
return EFI_UNSUPPORTED; // Unsupported as of now
|
|
}
|
|
// Clear and assign the new size option
|
|
HwConf &= ~(0xF0000);
|
|
HwConf |= ((TxFifoOption & 0xF) << 16);
|
|
MmioWrite32 (LAN9118_HW_CFG, HwConf);
|
|
gBS->Stall (LAN9118_STALL);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|