X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=EmbeddedPkg%2FDrivers%2FLan9118Dxe%2FLan9118DxeUtil.c;fp=EmbeddedPkg%2FDrivers%2FLan9118Dxe%2FLan9118DxeUtil.c;h=99c3ff0cec518207020fceec054162df052b235b;hp=0000000000000000000000000000000000000000;hb=46f2c53b544438c735708abba5dc66f83fd2dc4f;hpb=4d6e1e5a069329d785844962a4628ac4bb09833c diff --git a/EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c b/EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c new file mode 100644 index 0000000000..99c3ff0cec --- /dev/null +++ b/EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c @@ -0,0 +1,1021 @@ +/** @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) + ); + + CopyMem (&Snp->Mode->CurrentAddress, &Mac, NET_ETHER_ADDR_LEN); +} + +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); + } + } 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 +INT32 +PhySoftReset ( + UINT32 Flags, + EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + UINT32 PmtCtrl = 0; + UINT32 LinkTo = 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_PMT) { + IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PHYCR_RESET); + + // Wait for completion + while (IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) { + gBS->Stall (LAN9118_STALL); + } + } + + // Check the link status + if (Flags & PHY_RESET_CHECK_LINK) { + LinkTo = 100000; // 2 second (could be 50% more) + while (EFI_ERROR (CheckLinkStatus (0, Snp)) && (LinkTo > 0)) { + gBS->Stall (LAN9118_STALL); + LinkTo--; + } + + // Timed out + if (LinkTo <= 0) { + return -1; + } + } + + // 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 0; +} + + +// 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; +}