diff options
author | Steven Kinney <steven.kinney@amd.com> | 2014-04-07 14:59:57 -0500 |
---|---|---|
committer | Steven Kinney <steven.kinney@amd.com> | 2014-04-07 14:59:57 -0500 |
commit | 9e49d010945d4e8e5f380b05853831f636415af8 (patch) | |
tree | 64fb186456a0fa91a29d913b4b9e437b32cb08cf | |
parent | 368ab0d62af7cc55dff43bc566e39759143d0e82 (diff) | |
parent | 26b937d34f2ae1a016280ff3290032c4a887acc7 (diff) |
Merge remote-tracking branch 'rmt-1/linaro-topic-pxeboot' into release-prep
-rw-r--r-- | ArmPlatformPkg/ArmPlatformPkg.dec | 11 | ||||
-rw-r--r-- | ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc | 2 | ||||
-rw-r--r-- | ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.c | 2589 | ||||
-rw-r--r-- | ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.inf | 55 | ||||
-rw-r--r-- | ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118DxeHw.h | 325 | ||||
-rw-r--r-- | ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.c | 2234 | ||||
-rw-r--r-- | ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.inf | 58 | ||||
-rw-r--r-- | ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxeHw.h | 278 | ||||
-rwxr-xr-x[-rw-r--r--] | MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c | 33 | ||||
-rw-r--r-- | MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c | 6 |
10 files changed, 5572 insertions, 19 deletions
diff --git a/ArmPlatformPkg/ArmPlatformPkg.dec b/ArmPlatformPkg/ArmPlatformPkg.dec index 4509c76b4..d4e4af780 100644 --- a/ArmPlatformPkg/ArmPlatformPkg.dec +++ b/ArmPlatformPkg/ArmPlatformPkg.dec @@ -148,6 +148,16 @@ gArmPlatformTokenSpaceGuid.PcdDefaultConInPaths|L""|VOID*|0x0000001B
gArmPlatformTokenSpaceGuid.PcdDefaultConOutPaths|L""|VOID*|0x0000001C
+ # + # LAN9118 Ethernet Driver PCDs + # + gArmPlatformTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x0|UINT32|0x000000FF + + # + # LAN91x Ethernet Driver PCDs + # + gArmPlatformTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x0|UINT32|0x000000FE + [PcdsFixedAtBuild.ARM]
# Stack for CPU Cores in Secure Monitor Mode
gArmPlatformTokenSpaceGuid.PcdCPUCoresSecMonStackBase|0|UINT32|0x00000007
@@ -159,4 +169,3 @@ # and PcdCPUCoreSecSecondaryStackSize
gArmPlatformTokenSpaceGuid.PcdCPUCoresSecMonStackBase|0|UINT32|0x00000007
gArmPlatformTokenSpaceGuid.PcdCPUCoreSecMonStackSize|0x0|UINT32|0x00000008
-
diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc index c399c553c..a3708b521 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc @@ -388,3 +388,5 @@ EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsbDxe.inf
ArmPlatformPkg/ArmVExpressPkg/ArmVExpressFastBootDxe/ArmVExpressFastBootDxe.inf
+ ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.inf
+ ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.inf
diff --git a/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.c b/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.c new file mode 100644 index 000000000..636496214 --- /dev/null +++ b/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.c @@ -0,0 +1,2589 @@ +/** @file +* +* Copyright (c) 2012-2013, 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 <Uefi.h> +#include <Uefi/UefiSpec.h> +#include <Base.h> + +// Protocols used by this driver +#include <Protocol/SimpleNetwork.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/PxeBaseCode.h> +#include <Protocol/DevicePath.h> + +// Libraries used by this driver +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/IoLib.h> +#include <Library/PcdLib.h> +#include <Library/NetLib.h> +#include <Library/DevicePathLib.h> +#include <Library/PrintLib.h> + +#include "LAN9118DxeHw.h" + +#define LAN9118_TPL TPL_CALLBACK + +// Most common CRC32 Polynomial for little endian machines +#define CRC_POLYNOMIAL 0xEDB88320 + +/* ------------------ Debug Functions ------------------ */ + +// Flags for printing register values +#define PRINT_REGISTERS_MAC BIT0 +#define PRINT_REGISTERS_PHY BIT1 +#define PRINT_REGISTERS_ALL (BIT0 | BIT1) +#define PRINT_REGISTERS_SEL_MAC BIT12 +#define PRINT_REGISTERS_SEL_PHY BIT13 + + +/* ------------------ MAC CSR Access ------------------- */ + + +// Flags for software reset +#define SOFT_RESET_CHECK_MAC_ADDR_LOAD BIT0 +#define SOFT_RESET_CLEAR_INT BIT1 +#define SOFT_RESET_SELF_TEST BIT2 + +// Flags for PHY reset +#define PHY_RESET_PMT BIT0 +#define PHY_RESET_BCR BIT1 +#define PHY_RESET_CHECK_LINK BIT2 +#define PHY_SOFT_RESET_CLEAR_INT BIT3 + +// Flags for Hardware configuration +#define HW_CONF_USE_LEDS BIT0 + +// Stop transmitter flags +#define STOP_TX_MAC BIT0 +#define STOP_TX_CFG BIT1 +#define STOP_TX_CLEAR BIT2 + +// Stop receiver flags +#define STOP_RX_CLEAR BIT0 + +// Start transmitter flags +#define START_TX_MAC BIT0 +#define START_TX_CFG BIT1 +#define START_TX_CLEAR BIT2 + +// Stop receiver flags +#define START_RX_CLEAR BIT0 + +// Flags for FIFO allocation +#define ALLOC_USE_DEFAULT 0x00000001 +#define ALLOC_USE_FIFOS 0x00000002 +#define ALLOC_USE_DMA 0x00000004 + +// FIFO min and max sizes +#define TX_FIFO_MIN_SIZE 0x00000600 +#define TX_FIFO_MAX_SIZE 0x00003600 +//#define RX_FIFO_MIN_SIZE +//#define RX_FIFO_MAX_SIZE +#define LAN9118_STALL 2 + +#define LAN9118_DEFAULT_MAC_ADDRL 0x00F70200 +#define LAN9118_DEFAULT_MAC_ADDRH 0x00009040 + +/*--------------------------------------------------------------------------------------------------------------------- + + LAN9118 Information Structure + +---------------------------------------------------------------------------------------------------------------------*/ + +#define LAN9118_TX_CACHE_DEPTH 16 + +// TxCache management structure +typedef struct { + VOID *buffadr; + UINTN refcount; +} LAN9118_TX_CACHE; + +typedef struct { + // Driver signature + UINT32 Signature; + EFI_HANDLE ControllerHandle; + + // EFI SNP protocol instances + EFI_SIMPLE_NETWORK_PROTOCOL Snp; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + // EFI Snp statistics instance + EFI_NETWORK_STATISTICS Stats; + + // Transmit Completion Cache + VOID *TxCache[LAN9118_TX_CACHE_DEPTH]; +} LAN9118_DRIVER; + +#define LAN9118_SIGNATURE SIGNATURE_32('l', 'a', 'n', '9') +#define INSTANCE_FROM_SNP_THIS(a) CR(a, LAN9118_DRIVER, Snp, LAN9118_SIGNATURE) + + +typedef struct { + MAC_ADDR_DEVICE_PATH Lan9118; + EFI_DEVICE_PATH_PROTOCOL End; +} LAN9118_DEVICE_PATH; + +LAN9118_DEVICE_PATH Lan9118PathTemplate = { + { + { + MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP, + { (UINT8) (sizeof(MAC_ADDR_DEVICE_PATH)), (UINT8) ((sizeof(MAC_ADDR_DEVICE_PATH)) >> 8) } + }, + { 0 }, + 0 + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + sizeof(EFI_DEVICE_PATH_PROTOCOL), + 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) + return ReverseBits (Remainder); +} + +#ifndef MDEPKG_NDEBUG +STATIC CONST CHAR16 *Mac2Str (EFI_MAC_ADDRESS *Mac) +{ + static CHAR16 MacStr[18]; + + if (Mac == NULL) { + return L"<null>"; + } + + UnicodeSPrintAsciiFormat (MacStr, sizeof(MacStr), + "%02x:%02x:%02x:%02x:%02x:%02x", + Mac->Addr[0], Mac->Addr[1], Mac->Addr[2], + Mac->Addr[3], Mac->Addr[4], Mac->Addr[5]); + return MacStr; +} +#endif + +// 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; +} + + +// DEBUG: Print all register values +UINT32 +PrintRegisters ( + IN UINT32 Flags, + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + UINT32 Count; + UINT32 RegValue; + + // Select MAC register if defined (5 LSBs select register) + if (Flags & PRINT_REGISTERS_SEL_MAC) { + RegValue = IndirectMACRead32 (Flags & 0x1FF); + DEBUG((EFI_D_ERROR, "MAC Register %02x:\thex: 0x%08x\n",Flags & 0x1FF, RegValue)); + return 0; + } + + // Select PHY register if defined (5 LSBs select register) + if (Flags & PRINT_REGISTERS_SEL_PHY) { + RegValue = IndirectPHYRead32 (Flags & 0x1FF); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n",Flags & 0x1FF, RegValue)); + return 0; + } + + // Loop through all MAC registers + if (Flags & PRINT_REGISTERS_MAC) { + for (Count = 1;Count <= 0xC;Count++) { + RegValue = IndirectMACRead32 (Count); + DEBUG((EFI_D_ERROR, "MAC Register %02x:\thex: 0x%08x\n", Count, RegValue)); + } + } + + // Print PHY registers + if (Flags & PRINT_REGISTERS_PHY) { + for (Count = 0;Count <= 6;Count ++) { + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + } + + Count = 17; + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + + Count = 18; + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + + Count = 27; + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + + Count = 29; + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + + Count = 30; + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + + Count = 31; + RegValue = IndirectPHYRead32 (Count); + DEBUG((EFI_D_ERROR, "PHY Register %d:\thex: 0x%08x\n", Count, RegValue)); + } + + return 0; +} + + +/* ---------------- EEPROM Operations ------------------ */ + + +// Function to read from EEPROM memory +UINT32 +IndirectEEPROMRead32 ( + UINT32 Index +) +{ + // Eeprom command + UINT32 EepromCmd = 0;//= MmioRead32 (LAN9118_E2P_CMD); + + // Set the busy bit to ensure read will occur + EepromCmd |= ((UINT32)1 << 31); + + // Set the EEPROM command to read(0b000) + EepromCmd &= ~(0x70000000); // Clear the command first + EepromCmd |= (0 << 28); // Not necessary, but here for clarity + + // Set the index to access desired EEPROM memory location + EepromCmd |= (Index & 0xF); + + // 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 &= ~(0x70000000); // 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 ----------------- */ + + +// 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; +} + +// 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; + + // 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 |= HWCFG_SRST | 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 + ResetTime = 1000; + while (MmioRead32 (LAN9118_HW_CFG) & HWCFG_SRST) { + + gBS->Stall (LAN9118_STALL); + + // If time taken exceeds 1000us, then there was an error condition + if (--ResetTime == 0) { + Snp->Mode->State = EfiSimpleNetworkStopped; + return EFI_TIMEOUT; + } + } + + // Check that MAC Address loaded successfully (if required) + if (Flags & SOFT_RESET_CHECK_MAC_ADDR_LOAD) { + 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" + " Using hard-coded MAC Address.\n")); + + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL, LAN9118_DEFAULT_MAC_ADDRL); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH, LAN9118_DEFAULT_MAC_ADDRH); + } + } + + // Check that EEPROM isn't active + while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); + + // 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; +} + + +// Check the Link Status and take appropriate action +BOOLEAN +CheckLinkStatus ( + EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + UINT32 PhyBStatus; + + // Get the PHY Status + PhyBStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS); + + return (PhyBStatus & PHYSTS_LINK_STS) != 0; +} + +// 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 (!CheckLinkStatus(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 ( + EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + UINTN Retries; + UINT32 PhyControl; + UINT32 PhyStatus; + UINT32 PhyAdvert; + + // First check that auto-negotiation is supported + PhyStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS); + if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) { + return EFI_SUCCESS; + } + + // Translate capabilities to advertise + PhyAdvert = PHYANA_CSMA; + + if ((PhyStatus & PHYSTS_10BASET_HDPLX) != 0) { + PhyAdvert |= PHYANA_10BASET; + } + if ((PhyStatus & PHYSTS_10BASET_FDPLX) != 0) { + PhyAdvert |= PHYANA_10BASETFD; + } + if ((PhyStatus & PHYSTS_100BASETX_HDPLX) != 0) { + PhyAdvert |= PHYANA_100BASETX; + } + if ((PhyStatus & PHYSTS_100BASETX_FDPLX) != 0) { + PhyAdvert |= PHYANA_100BASETXFD; + } + if ((PhyStatus & PHYSTS_100BASE_T4) != 0) { + PhyAdvert |= PHYANA_100BASET4; + } + + // Write the features + IndirectPHYWrite32 (PHY_INDEX_AUTO_NEG_ADVERT, PhyAdvert); + + // Restart Auto-Negotiation + PhyControl = IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL); + PhyControl &= ~(PHYCR_SPEED_SEL | PHYCR_DUPLEX_MODE); + PhyControl |= PHYCR_AUTO_EN | PHYCR_RST_AUTO; + IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PhyControl); + + // Wait up to 2 seconds for the process to complete + Retries = 2000000; + while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0) { + if (--Retries == 0) { + DEBUG((EFI_D_ERROR, "LAN9118: PHY auto-negotiation timed-out\n")); + return EFI_TIMEOUT; + } + gBS->Stall (100); + } + + return EFI_SUCCESS; +} + +// Start the transmitter +EFI_STATUS +StartTx ( + EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + LAN9118_DRIVER *LanDriver; + UINT32 MacCsr; + UINT32 TxCfg; + + // Clear the transmitter + TxCfg = MmioRead32 (LAN9118_TX_CFG); + TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP; + MmioWrite32 (LAN9118_TX_CFG, TxCfg); + gBS->Stall (LAN9118_STALL); + + // Clear the TxCache + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + ZeroMem (LanDriver->TxCache, sizeof(LanDriver->TxCache)); + + // Start the 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); + } + + // Enable the transmitter + 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 ( + 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 ( + 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; +} + +// 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; +} + +/*--------------------------------------------------------------------------------------------------------------------- + + Utility functions + +---------------------------------------------------------------------------------------------------------------------*/ + +EFI_MAC_ADDRESS +GetCurrentMacAddress ( + VOID + ) +{ + UINT32 MacAddrHighValue; + UINT32 MacAddrLowValue; + EFI_MAC_ADDRESS MacAddress; + + // 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; + + DEBUG((DEBUG_NET, "GetCurrentMacAddress() = %s\n", Mac2Str (&MacAddress))); + return MacAddress; +} + +#define ReturnUnlock(s) do { Status = (s); goto exit_unlock; } while(0) + +/* +** UEFI Start() function +** +** Parameters: +** +** @param pobj: A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance. +** +** Description: +** +** This function starts a network interface. If the network interface successfully starts, then +** EFI_SUCCESS will be returned. +*/ +EFI_STATUS +EFIAPI +SnpStart ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp + ) +{ + EFI_MAC_ADDRESS *Mac; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // Check state + if ((Snp->Mode->State == EfiSimpleNetworkStarted) || (Snp->Mode->State == EfiSimpleNetworkInitialized)) { + ReturnUnlock (EFI_ALREADY_STARTED); + } else if (Snp->Mode->State == EfiSimpleNetworkMaxState) { + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // 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 + while ((MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_READY) == 0); + + // Check that EEPROM isn't active + while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); + + Mac = &Snp->Mode->CurrentAddress; + DEBUG((DEBUG_NET, "Using current address %s\n", Mac2Str(Mac))); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL, + (Mac->Addr[3] << 24) | + (Mac->Addr[2] << 16) | + (Mac->Addr[1] << 8) | + Mac->Addr[0] + ); + + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH, + (Mac->Addr[5] << 8) | + Mac->Addr[4] + ); + + // Clear and acknowledge interrupts + MmioWrite32 (LAN9118_INT_EN, 0); + MmioWrite32 (LAN9118_IRQ_CFG, 0); + MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF); + + // Change state + Snp->Mode->State = EfiSimpleNetworkStarted; + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Stop() function +** +*/ +EFI_STATUS +EFIAPI +SnpStop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // Check state of the driver + if ((Snp->Mode->State == EfiSimpleNetworkStopped) || (Snp->Mode->State == EfiSimpleNetworkMaxState)) { + ReturnUnlock (EFI_NOT_STARTED); + } + + // Stop the Tx and Rx + StopTx (STOP_TX_CFG | STOP_TX_MAC, Snp); + StopRx (0, Snp); + + // Change the state + switch (Snp->Mode->State) { + case EfiSimpleNetworkStarted: + case EfiSimpleNetworkInitialized: + Snp->Mode->State = EfiSimpleNetworkStopped; + break; + default: + ReturnUnlock (EFI_DEVICE_ERROR); + } + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +// Allocated receive and transmit buffers +STATIC UINT32 gTxBuffer = 0; + +// Buffer sizes +STATIC UINT32 gTxDataSize = 0; +STATIC UINT32 gTxStatusSize = 0; +STATIC UINT32 gRxDataSize = 0; +STATIC UINT32 gRxStatusSize = 0; + +/* +** UEFI Initialize() function +** +*/ +EFI_STATUS +EFIAPI +SnpInitialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN UINTN RxBufferSize OPTIONAL, + IN UINTN TxBufferSize OPTIONAL + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINT32 PmConf; + INT32 AllocResult; + + // Initialize variables + // Global variables to hold tx and rx FIFO allocation + gTxBuffer = 0; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // First check that driver has not already been initialized + if (Snp->Mode->State == EfiSimpleNetworkInitialized) { + DEBUG((EFI_D_WARN, "LAN9118 Driver already initialized\n")); + ReturnUnlock (EFI_SUCCESS); + } else + if (Snp->Mode->State == EfiSimpleNetworkStopped) { + DEBUG((EFI_D_WARN, "LAN9118 Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Initiate a PHY reset + if (PhySoftReset (PHY_RESET_PMT | PHY_RESET_CHECK_LINK, Snp) < 0) { + Snp->Mode->State = EfiSimpleNetworkStopped; + DEBUG((EFI_D_WARN, "Warning: Link not ready after TimeOut. Check ethernet cable\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Initiate a software reset + Status = SoftReset (0, Snp); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_WARN, "Soft Reset Failed: Hardware Error\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Read the PM register + PmConf = MmioRead32 (LAN9118_PMT_CTRL); + + // MPTCTRL_WOL_EN: Allow Wake-On-Lan to detect wake up frames or magic packets + // MPTCTRL_ED_EN: Allow energy detection to allow lowest power consumption mode + // MPTCTRL_PME_EN: Allow Power Management Events + PmConf = 0; + PmConf |= (MPTCTRL_WOL_EN | MPTCTRL_ED_EN | MPTCTRL_PME_EN); + + // Write the current configuration to the register + MmioWrite32 (LAN9118_PMT_CTRL, PmConf); + gBS->Stall (LAN9118_STALL); + gBS->Stall (LAN9118_STALL); + + // Configure GPIO and HW + Status = ConfigureHardware (HW_CONF_USE_LEDS, Snp); + if (EFI_ERROR(Status)) { + ReturnUnlock (Status); + } + + // Assign the transmitter buffer size (default values) + gTxDataSize = 4608; + gTxStatusSize = 512; // this never changes + gRxDataSize = 10560; + gRxStatusSize = 704; + + // Check that a buff size was specified + if (TxBufferSize) { + AllocResult = ChangeFifoAllocation ( + ALLOC_USE_FIFOS, + &TxBufferSize, + &RxBufferSize, + &gTxStatusSize, + &gRxStatusSize, + Snp + ); + + if (AllocResult < 0) { + return EFI_OUT_OF_RESOURCES; + } + + gTxDataSize = gTxDataSize; + gTxStatusSize = gTxStatusSize; + gTxDataSize = TxBufferSize; + gRxDataSize = RxBufferSize; + } + + + // Do auto-negotiation if supported + Status = AutoNegotiate (Snp); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_WARN, "Lan9118: Auto Negotiation not supported.\n")); + } + + // Configure flow control depending on speed capabilities + Status = ConfigureFlow (0, 0, 0, 0, Snp); + if (EFI_ERROR(Status)) { + ReturnUnlock (Status); + } + + // Enable the receiver and transmitter + Status = StartRx (0, Snp); + if (EFI_ERROR(Status)) { + ReturnUnlock (Status); + } + + Status = StartTx (Snp); + if (EFI_ERROR(Status)) { + ReturnUnlock (Status); + } + + // Now acknowledge all interrupts + MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF); + + // Declare the driver as initialized + Snp->Mode->State = EfiSimpleNetworkInitialized; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Reset () function +** +*/ +EFI_STATUS +EFIAPI +SnpReset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN BOOLEAN Verification + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINT32 PmConf; + UINT32 HwConf; + UINT32 ResetFlags; + + PmConf = 0; + HwConf = 0; + ResetFlags = 0; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // First check that driver has not already been initialized + if (Snp->Mode->State == EfiSimpleNetworkStarted) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } else if (Snp->Mode->State == EfiSimpleNetworkStopped) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Initiate a PHY reset + if (PhySoftReset (PHY_RESET_PMT | PHY_RESET_CHECK_LINK, Snp) < 0) { + Snp->Mode->State = EfiSimpleNetworkStopped; + ReturnUnlock (EFI_NOT_STARTED); + } + + // Initiate a software reset + ResetFlags |= SOFT_RESET_CHECK_MAC_ADDR_LOAD | SOFT_RESET_CLEAR_INT; + + if (Verification) { + ResetFlags |= SOFT_RESET_SELF_TEST; + } + + if (SoftReset (ResetFlags, Snp) < 0) { + DEBUG((EFI_D_WARN, "Warning: Soft Reset Failed: Hardware Error\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Read the PM register + PmConf = MmioRead32 (LAN9118_PMT_CTRL); + + // MPTCTRL_WOL_EN: Allow Wake-On-Lan to detect wake up frames or magic packets + // MPTCTRL_ED_EN: Allow energy detection to allow lowest power consumption mode + // MPTCTRL_PME_EN: Allow Power Management Events + PmConf |= (MPTCTRL_WOL_EN | MPTCTRL_ED_EN | MPTCTRL_PME_EN); + + // Write the current configuration to the register + MmioWrite32 (LAN9118_PMT_CTRL, PmConf); + gBS->Stall (LAN9118_STALL); + + // Check that a buffer size was specified in SnpInitialize + if (gTxBuffer != 0) { + HwConf = MmioRead32 (LAN9118_HW_CFG); // Read the HW register + HwConf &= ~((UINT32)0xF0000); // Clear buffer bits first + HwConf |= (gTxBuffer << 16); // assign size chosen in SnpInitialize + + MmioWrite32 (LAN9118_HW_CFG, HwConf); // Write the conf + gBS->Stall (LAN9118_STALL); + } + + // Enable the receiver and transmitter and clear their contents + StartRx (START_RX_CLEAR, Snp); + StartTx (Snp); + + // Now acknowledge all interrupts + MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF); + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Shutdown () function +** +*/ +EFI_STATUS +EFIAPI +SnpShutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // First check that driver has not already been initialized + if (Snp->Mode->State == EfiSimpleNetworkStarted) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } else if (Snp->Mode->State == EfiSimpleNetworkStopped) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Initiate a PHY reset + PhySoftReset (PHY_RESET_PMT, Snp); + + // Initiate a software reset + if (SoftReset (0, Snp) < 0) { + DEBUG((EFI_D_WARN, "Warning: Soft Reset Failed: Hardware Error\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/* +** UEFI ReceiveFilters() function +** +*/ +EFI_STATUS +EFIAPI +SnpReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN Reset, + IN UINTN NumMfilter OPTIONAL, + IN EFI_MAC_ADDRESS *Mfilter OPTIONAL + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINT32 MacCSRValue; + UINT32 MultHashTableHigh; + UINT32 MultHashTableLow; + UINT32 Crc; + UINT8 BitToSelect; + UINT32 Count; + + MacCSRValue = 0; + MultHashTableHigh = 0; + MultHashTableLow = 0; + Crc = 0xFFFFFFFF; + BitToSelect = 0; + Count = 0; + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // Check that driver was started and initialised + if (Snp->Mode->State == EfiSimpleNetworkStarted) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } else if (Snp->Mode->State == EfiSimpleNetworkStopped) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // If reset then clear the filter registers + if (Reset) { + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; + IndirectMACWrite32 (INDIRECT_MAC_INDEX_HASHL, 0x00000000); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_HASHH, 0x00000000); + } + + // Set the hash tables + if ((NumMfilter > 0) && (!Reset)) { + + // Read the Multicast High Hash Table + MultHashTableHigh = IndirectMACRead32 (INDIRECT_MAC_INDEX_HASHH); + + // Read the Multicast Low Hash Table + MultHashTableLow = IndirectMACRead32 (INDIRECT_MAC_INDEX_HASHL); + + // Go through each filter address and set appropriate bits on hash table + for (Count = 0; Count < NumMfilter; Count++) { + + // Generate a 32-bit CRC for Ethernet + Crc = GenEtherCrc32 (&Mfilter[Count],6); + //gBS->CalculateCrc32 ((VOID*)&Mfilter[Count],6,&Crc); <-- doesn't work as desired + + // Get the most significant 6 bits to index hash registers + BitToSelect = (Crc >> 26) & 0x3F; + + // Select hashlow register if MSB is not set + if ((BitToSelect & 0x20) == 0) { + MultHashTableLow |= (1 << BitToSelect); + } else { + MultHashTableHigh |= (1 << (BitToSelect & 0x1F)); + } + } + + // Write the desired hash + IndirectMACWrite32 (INDIRECT_MAC_INDEX_HASHL, MultHashTableLow); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_HASHH, MultHashTableHigh); + } + + // Read MAC controller + MacCSRValue = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR); + + // Set the options for the MAC_CSR + if (Enable & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) { + StartRx (0, Snp); + DEBUG((DEBUG_NET, "Allowing Unicast Frame Reception\n")); + } + + if (Disable & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) { + StopRx (0, Snp); + DEBUG((DEBUG_NET, "Disabling Unicast Frame Reception\n")); + } + + if (Enable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) { + MacCSRValue |= MACCR_HPFILT; + DEBUG((DEBUG_NET, "Allowing Multicast Frame Reception\n")); + } + + if (Disable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) { + MacCSRValue &= ~MACCR_HPFILT; + DEBUG((DEBUG_NET, "Disabling Multicast Frame Reception\n")); + } + + if (Enable & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) { + MacCSRValue &= ~(MACCR_BCAST); + DEBUG((DEBUG_NET, "Allowing Broadcast Frame Reception\n")); + } + + if (Disable & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) { + MacCSRValue |= MACCR_BCAST; + DEBUG((DEBUG_NET, "Disabling Broadcast Frame Reception\n")); + } + + if (Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) { + MacCSRValue |= MACCR_PRMS; + DEBUG((DEBUG_NET, "Enabling Promiscuous Mode\n")); + } + + if (Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) { + MacCSRValue &= ~MACCR_PRMS; + DEBUG((DEBUG_NET, "Disabling Promiscuous Mode\n")); + } + + if (Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) { + MacCSRValue |= (MACCR_HPFILT | MACCR_PRMS); + DEBUG((DEBUG_NET, "Enabling Promiscuous Multicast Mode\n")); + } + + if (Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) { + MacCSRValue &= ~(MACCR_HPFILT | MACCR_PRMS); + DEBUG((DEBUG_NET, "Disabling Promiscuous Multicast Mode\n")); + } + + // Write the options to the MAC_CSR + IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCSRValue); + gBS->Stall (LAN9118_STALL); + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI StationAddress() function +** +*/ +EFI_STATUS +EFIAPI +SnpStationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS *NewMac +) +{ + DEBUG((DEBUG_NET, "SnpStationAddress(%d, %s)\n", Reset, Mac2Str(NewMac))); + + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINT32 Count; + UINT8 PermAddr[6]; + + Count = 0; + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // Check that driver was started and initialised + if (Snp->Mode->State == EfiSimpleNetworkStarted) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } else if (Snp->Mode->State == EfiSimpleNetworkStopped) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Get the Permanent MAC address if need reset + if (Reset) { + // Try using EEPROM first + if ((IndirectEEPROMRead32 (0) & 0xFF) == 0xA5) { + for (Count = 1; Count < 7; Count++) { + PermAddr[Count - 1] = IndirectEEPROMRead32 (Count); + } + + // Write address + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL, (UINT32)((UINT32)PermAddr[0]) | ((UINT32)PermAddr[1] << 8) | ((UINT32)PermAddr[2] << 16) | ((UINT32)PermAddr[3] << 24)); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH, (UINT32)((((UINT32)PermAddr[4]) | ((UINT32)PermAddr[5] << 8)) & 0xFFFF)); + } else { + // Otherwise make our own + DEBUG((EFI_D_WARN, "Warning: No valid EEPROM detected. Using a hard coded address.\n")); + + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL, LAN9118_DEFAULT_MAC_ADDRL); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH, LAN9118_DEFAULT_MAC_ADDRH); + } + + // Otherwise use the specified new MAC address + } else { + if (NewMac == NULL) { + ReturnUnlock (EFI_INVALID_PARAMETER); + } + + // Write address + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL, (UINT32)((UINT32)NewMac->Addr[0]) | ((UINT32)NewMac->Addr[1] << 8) | ((UINT32)NewMac->Addr[2] << 16) | ((UINT32)NewMac->Addr[3] << 24)); + IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH, (UINT32)((((UINT32)NewMac->Addr[4]) | ((UINT32)NewMac->Addr[5] << 8)) & 0xFFFF)); + } + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Statistics() function +** +*/ +EFI_STATUS +EFIAPI +SnpStatistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN BOOLEAN Reset, + IN OUT UINTN *StatSize, + OUT EFI_NETWORK_STATISTICS *Statistics + ) +{ + LAN9118_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + DEBUG((DEBUG_NET, "SnpStatistics()\n")); + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Check pointless condition + if ((!Reset) && (StatSize == NULL) && (Statistics == NULL)) { + return EFI_SUCCESS; + } + + // Check the parameters + if ((StatSize == NULL) && (Statistics != NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Check that driver was started and initialised + if (Snp->Mode->State == EfiSimpleNetworkStarted) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } else if (Snp->Mode->State == EfiSimpleNetworkStopped) { + DEBUG((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Do a reset if required + if (Reset) { + ZeroMem (&LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS)); + } + + // Check buffer size + if (*StatSize < sizeof(EFI_NETWORK_STATISTICS)) { + *StatSize = sizeof(EFI_NETWORK_STATISTICS); + ReturnUnlock (EFI_BUFFER_TOO_SMALL); + } + + // Fill in the statistics + CopyMem(&Statistics, &LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS)); + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI MCastIPtoMAC() function +** +*/ +EFI_STATUS +EFIAPI +SnpMcastIptoMac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN BOOLEAN IsIpv6, + IN EFI_IP_ADDRESS *Ip, + OUT EFI_MAC_ADDRESS *McastMac + ) +{ + DEBUG((DEBUG_NET, "SnpMcastIptoMac()\n")); + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Check parameters + if ((McastMac == NULL) || (Ip == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Make sure MAC address is empty + ZeroMem (McastMac, sizeof(EFI_MAC_ADDRESS)); + + // If we need ipv4 address + if (!IsIpv6) { + // Most significant 25 bits of a multicast HW address are set + McastMac->Addr[0] = 0x01; + McastMac->Addr[1] = 0x00; + McastMac->Addr[2] = 0x5E; + + // Lower 23 bits from ipv4 address + McastMac->Addr[3] = (Ip->v4.Addr[1] & 0x7F); // Clear the ms bit (25th bit of MAC must be 0) + McastMac->Addr[4] = Ip->v4.Addr[2]; + McastMac->Addr[5] = Ip->v4.Addr[3]; + } else { + // Most significant 16 bits of multicast v6 HW address are set + McastMac->Addr[0] = 0x33; + McastMac->Addr[1] = 0x33; + + // lower four octets are taken from ipv6 address + McastMac->Addr[2] = Ip->v6.Addr[8]; + McastMac->Addr[3] = Ip->v6.Addr[9]; + McastMac->Addr[4] = Ip->v6.Addr[10]; + McastMac->Addr[5] = Ip->v6.Addr[11]; + } + + return EFI_SUCCESS; +} + +/* +** UEFI NvData() function +** +*/ +EFI_STATUS +EFIAPI +SnpNvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* pobj, + IN BOOLEAN read_write, + IN UINTN offset, + IN UINTN buff_size, + IN OUT VOID *data + ) +{ + DEBUG((DEBUG_NET, "SnpNvData()\n")); + + return EFI_UNSUPPORTED; +} + + +/* +** UEFI GetStatus () function +** +*/ +EFI_STATUS +EFIAPI +SnpGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + OUT UINT32 *IrqStat OPTIONAL, + OUT VOID **TxBuff OPTIONAL + ) +{ + LAN9118_DRIVER *LanDriver; + EFI_STATUS Status; + EFI_TPL SavedTpl; + UINT32 FifoInt; + UINT32 IntStatus; + BOOLEAN MediaPresent; + UINT32 TxStatus; + INTN TxCacheIndex; + + // Check preliminaries + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + if (Snp->Mode->State != EfiSimpleNetworkInitialized) { + ReturnUnlock (EFI_NOT_STARTED); + } + + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Set the transmit status available level + FifoInt = MmioRead32 (LAN9118_FIFO_INT); + + if ((FifoInt & 0x00FF0000) == 0) { + FifoInt |= (1 << 16); + MmioWrite32 (LAN9118_FIFO_INT, FifoInt); + } + + IntStatus = MmioRead32 (LAN9118_INT_STS); + if ((IntStatus & (INSTS_TXSTOP_INT | INSTS_RXSTOP_INT | INSTS_TXSO | INSTS_RWT | + INSTS_RXE | INSTS_TXE | INSTS_TDFO | INSTS_TDFA | INSTS_TSFF | + INSTS_TSFL | INSTS_RXDF_INT | INSTS_RSFF)) != 0) { + DEBUG((EFI_D_WARN, "IntStatus: %08x\n", IntStatus)); + } + + // Report interrupt status if IrqStat is not NULL + if (IrqStat != NULL) { + *IrqStat = 0; + + // Check for receive interrupt + if (IntStatus & INSTS_RSFL) { // Data moved from rx FIFO + *IrqStat |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + MmioWrite32 (LAN9118_INT_STS,INSTS_RSFL); + } + + // Check for transmit interrupt + if (IntStatus & INSTS_TSFL) { + *IrqStat |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + MmioWrite32 (LAN9118_INT_STS,INSTS_TSFL); + } + + // Check for software interrupt + if (IntStatus & INSTS_SW_INT) { + *IrqStat |= EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT; + MmioWrite32 (LAN9118_INT_STS,INSTS_SW_INT); + } + } + + // Return the recycled transmit address + if ((TxBuff != NULL) && (TxStatusUsedSpace (Snp) > 0)) { + TxStatus = MmioRead32 (LAN9118_TX_STATUS); + + TxCacheIndex = ((INTN)(TxStatus >> 16) & 0xffff) - 1; + if ((0 <= TxCacheIndex) && (TxCacheIndex < LAN9118_TX_CACHE_DEPTH)) { + if (LanDriver->TxCache[TxCacheIndex] != NULL) { + *TxBuff = LanDriver->TxCache[TxCacheIndex]; + LanDriver->TxCache[TxCacheIndex] = NULL; + } else { + *TxBuff = NULL; + } + } else { + *TxBuff = NULL; + } + + // Check Tx Status (we ignore TXSTATUS_NO_CA has it might happen in Full Duplex) + if (((TxStatus & TXSTATUS_ES) != 0) && ((TxStatus & TXSTATUS_NO_CA) == 0)) { + DEBUG((EFI_D_WARN, "Warning: There was an error transmitting TxStatus=0x%X:\n", TxStatus)); + if (TxStatus & TXSTATUS_DEF) { + DEBUG((EFI_D_WARN, "- Packet tx was deferred\n")); + } + if (TxStatus & TXSTATUS_EDEF) { + DEBUG((EFI_D_WARN, "- Tx ended because of excessive deferral\n")); + } + if (TxStatus & TXSTATUS_ECOLL) { + DEBUG((EFI_D_WARN, "- Tx ended because of Excessive Collisions\n")); + } + if (TxStatus & TXSTATUS_LCOLL) { + DEBUG((EFI_D_WARN, "- Packet Tx aborted after coll window of 64 bytes\n")); + } + if (TxStatus & TXSTATUS_LOST_CA) { + DEBUG((EFI_D_WARN, "- Lost carrier during Tx\n")); + } + } + } + + // Update the media status + MediaPresent = CheckLinkStatus (Snp); + if (MediaPresent != Snp->Mode->MediaPresent) { + DEBUG((EFI_D_WARN, "LAN9118: Link %s\n", MediaPresent ? L"up" : L"down")); + } + Snp->Mode->MediaPresent = MediaPresent; + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/* +** UEFI Transmit() function +** +*/ +EFI_STATUS +EFIAPI +SnpTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN UINTN HdrSize, + IN UINTN BuffSize, + IN VOID *Data, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DstAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ) +{ + LAN9118_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINT32 TxFreeSpace; + UINT32 TxStatusSpace; + INT32 Count; + UINT32 CommandA; + UINT32 CommandB; + UINT16 LocalProtocol; + UINT32 *LocalData; + INTN TxCacheIndex; + + // Check preliminaries + if ((Snp == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + TxCacheIndex = (-1); + + if (Snp->Mode->State != EfiSimpleNetworkInitialized) { + ReturnUnlock (EFI_NOT_STARTED); + } + + // Ensure header is correct size if non-zero + if (HdrSize) { + if (HdrSize != Snp->Mode->MediaHeaderSize) { + ReturnUnlock (EFI_INVALID_PARAMETER); + } + + if ((DstAddr == NULL) || (Protocol == NULL)) { + ReturnUnlock (EFI_INVALID_PARAMETER); + } + } + + // Before transmitting check the link status + if (!Snp->Mode->MediaPresent) { + ReturnUnlock (EFI_NOT_READY); + } + + // Find a free entry in the TxCache array + for (TxCacheIndex = LAN9118_TX_CACHE_DEPTH - 1; TxCacheIndex >= 0; --TxCacheIndex) { + if (LanDriver->TxCache[TxCacheIndex] == NULL) { + break; + } + } + if (TxCacheIndex < 0) { + ReturnUnlock (EFI_NOT_READY); + } + LanDriver->TxCache[TxCacheIndex] = Data; + + // Get DATA FIFO free space in bytes + TxFreeSpace = TxDataFreeSpace (Snp); + if (TxFreeSpace < BuffSize) { + ReturnUnlock (EFI_NOT_READY); + } + + // Get STATUS FIFO used space in DWORDS + TxStatusSpace = TxStatusUsedSpace (Snp); + if (TxStatusSpace > 125) { + ReturnUnlock (EFI_NOT_READY); + } + + // Check for the nature of the frame + if ((DstAddr->Addr[0] & 0x1) == 1) { + LanDriver->Stats.TxMulticastFrames += 1; + } else { + LanDriver->Stats.TxUnicastFrames += 1; + } + + // Check if broadcast + if (DstAddr->Addr[0] == 0xFF) { + LanDriver->Stats.TxBroadcastFrames += 1; + } + + if (HdrSize) { + // Format pointer + LocalData = (UINT32*) Data; + LocalProtocol = *Protocol; + + // Create first buffer to pass to controller (for the header) + CommandA = TX_CMD_A_FIRST_SEGMENT | TX_CMD_A_BUFF_SIZE(HdrSize); + CommandB = TX_CMD_B_PACKET_TAG(TxCacheIndex + 1) | TX_CMD_B_PACKET_LENGTH(BuffSize); + + // Write the commands first + MmioWrite32 (LAN9118_TX_DATA, CommandA); + MmioWrite32 (LAN9118_TX_DATA, CommandB); + + // Write the destination address + MmioWrite32 (LAN9118_TX_DATA, + (DstAddr->Addr[0]) | + (DstAddr->Addr[1] << 8) | + (DstAddr->Addr[2] << 16) | + (DstAddr->Addr[3] << 24) + ); + + MmioWrite32 (LAN9118_TX_DATA, + (DstAddr->Addr[4]) | + (DstAddr->Addr[5] << 8) | + + // Write the Source Address + (SrcAddr->Addr[0] << 16) | + (SrcAddr->Addr[1] << 24) + ); + + MmioWrite32 (LAN9118_TX_DATA, + (SrcAddr->Addr[2]) | + (SrcAddr->Addr[3] << 8) | + (SrcAddr->Addr[4] << 16) | + (SrcAddr->Addr[5] << 24) + ); + + // Write the Protocol + MmioWrite32 (LAN9118_TX_DATA, (UINT32)(HTONS(LocalProtocol))); + + // Next buffer is the payload + CommandA = TX_CMD_A_COMPLETION_INT | TX_CMD_A_START_OFFSET(2) | + TX_CMD_A_LAST_SEGMENT | TX_CMD_A_BUFF_SIZE(BuffSize - HdrSize); + + // Write the commands + MmioWrite32 (LAN9118_TX_DATA, CommandA); + MmioWrite32 (LAN9118_TX_DATA, CommandB); + + // Write the payload + for (Count = 0; Count < ((BuffSize + 3) >> 2) - 3; Count++) { + MmioWrite32 (LAN9118_TX_DATA,LocalData[Count + 3]); + } + + // Get STATUS FIFO used space in bytes + /*TxStatusSpace = TxStatusUsedSpace (0, Snp); + DEBUG((EFI_D_ERROR, "Status FIFO Size after write: %d\n",TxStatusSpace)); + + // Data written debug + DEBUG((EFI_D_ERROR, "Payload written: %d bytes",BuffSize - HdrSize)); + for (Count = 0; Count < ((BuffSize + 3) >> 2) - 3; Count++) { + if ((Count % 6) == 0) + DEBUG((EFI_D_ERROR, "\n")); + DEBUG((DEBUG_NET, "0x%08X", NTOHL(LocalData[Count + 3]))); + } + DEBUG((EFI_D_ERROR, "\n")); + */ + + } else { + + // Format pointer + LocalData = (UINT32*) Data; + + // Create a buffer to pass to controller + CommandA = TX_CMD_A_COMPLETION_INT | TX_CMD_A_FIRST_SEGMENT | + TX_CMD_A_LAST_SEGMENT | TX_CMD_A_BUFF_SIZE(BuffSize); + CommandB = TX_CMD_B_PACKET_TAG(TxCacheIndex + 1) | TX_CMD_B_PACKET_LENGTH(BuffSize); + + // Write the commands first + MmioWrite32 (LAN9118_TX_DATA, CommandA); + MmioWrite32 (LAN9118_TX_DATA, CommandB); + + // Write all the data + for (Count = 0; Count < ((BuffSize + 3) >> 2); Count++) { + MmioWrite32 (LAN9118_TX_DATA,LocalData[Count]); + } + + // Data written debug + /*DEBUG((EFI_D_ERROR, "Packet written:")); + for (Count = 0; Count < ((BuffSize + 3) >> 2); Count++) { + if ((Count % 6) == 0) + DEBUG((EFI_D_ERROR, "\n")); + DEBUG((DEBUG_NET, "0x%08X",LocalData[Count])); + }*/ + } + + LanDriver->Stats.TxTotalFrames += 1; + LanDriver->Stats.TxGoodFrames += 1; + TxCacheIndex = (-1); + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + if (TxCacheIndex >= 0) { + LanDriver->TxCache[TxCacheIndex] = NULL; + } + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/* +** UEFI Receive() function +** +*/ +EFI_STATUS +EFIAPI +SnpReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + OUT UINTN *HdrSize OPTIONAL, + IN OUT UINTN *BuffSize, + OUT VOID *Data, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DstAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + LAN9118_DRIVER *LanDriver; + UINT32 RxFifoStatus; + UINT32 NumPackets; + UINT32 RxDataUsed; + UINT32 RxStatusUsed; + UINT32 RxCfgValue; + UINT32 PLength; // Packet length + UINT32 ReadLimit; + UINT32 Count; + UINT32 Padding; + UINT32 *RawData; + EFI_MAC_ADDRESS Dst; + EFI_MAC_ADDRESS Src; + + // Check preliminaries + if ((Snp == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN9118_TPL); + + // Check state + if (Snp->Mode->State != EfiSimpleNetworkInitialized) { + ReturnUnlock (EFI_NOT_STARTED); + } + + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Before receiving check the link status + /*if (CheckLinkStatus (Snp) < 0) { + ReturnUnlock (EFI_NOT_READY); + }*/ + + // Get the used space in rx fifo + RxDataUsed = RxDataUsedSpace (0, Snp); + if (RxDataUsed > gRxDataSize - 4) { + LanDriver->Stats.RxDroppedFrames += 1; + ReturnUnlock (EFI_NOT_READY); + } + + // Get the used status space + RxStatusUsed = RxStatusUsedSpace (0, Snp); + if (RxStatusUsed > gRxStatusSize - 4) { + LanDriver->Stats.RxDroppedFrames += 1; + ReturnUnlock (EFI_NOT_READY); + } + + // Get the number of packets to read + NumPackets = RxStatusUsed >> 2; + if (!NumPackets) { + ReturnUnlock (EFI_NOT_READY); + } + + // Read Rx Status (only if not empty) + RxFifoStatus = MmioRead32 (LAN9118_RX_STATUS); + LanDriver->Stats.RxTotalFrames += 1; + + // First check for errors + if ((RxFifoStatus & RXSTATUS_MII_ERROR) || + (RxFifoStatus & RXSTATUS_RXW_TO) || + (RxFifoStatus & RXSTATUS_FTL) || + (RxFifoStatus & RXSTATUS_LCOLL) || + (RxFifoStatus & RXSTATUS_LE) || + (RxFifoStatus & RXSTATUS_DB)) + { + DEBUG((EFI_D_WARN, "Warning: There was an error on frame reception.\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Check if we got a CRC error + if (RxFifoStatus & RXSTATUS_CRC_ERROR) { + DEBUG((EFI_D_WARN, "Warning: Crc Error\n")); + LanDriver->Stats.RxCrcErrorFrames += 1; + LanDriver->Stats.RxDroppedFrames += 1; + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Check if we got a runt frame + if (RxFifoStatus & RXSTATUS_RUNT) { + DEBUG((EFI_D_WARN, "Warning: Runt Frame\n")); + LanDriver->Stats.RxUndersizeFrames += 1; + LanDriver->Stats.RxDroppedFrames += 1; + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Check filtering status for this packet + if (RxFifoStatus & RXSTATUS_FILT_FAIL) { + DEBUG((EFI_D_WARN, "Warning: Frame Failed Filtering\n")); + // fast forward? + } + + // Check if we got a broadcast frame + if (RxFifoStatus & RXSTATUS_BCF) { + LanDriver->Stats.RxBroadcastFrames += 1; + } + + // Check if we got a multicast frame + if (RxFifoStatus & RXSTATUS_MCF) { + LanDriver->Stats.RxMulticastFrames += 1; + } + + // Check if we got a unicast frame + if ((RxFifoStatus & RXSTATUS_BCF) && ((RxFifoStatus & RXSTATUS_MCF) == 0)) { + LanDriver->Stats.RxUnicastFrames += 1; + } + + // Get the received packet length + PLength = (RxFifoStatus & RXSTATUS_PL_MASK) >> 16; + LanDriver->Stats.RxTotalBytes += (PLength - 4); + + // Check buffer size + if (*BuffSize < PLength) { + *BuffSize = PLength; + ReturnUnlock (EFI_BUFFER_TOO_SMALL); + } + + // If padding is applied, read more DWORDs + if (PLength % 4) { + Padding = 4 - (PLength % 4); + ReadLimit = (PLength + Padding)/4; + } else { + ReadLimit = PLength/4; + Padding = 0; + } + + // Set the amount of data to be transfered out of FIFO for THIS packet + // This can be used to trigger an interrupt, and status can be checked + RxCfgValue = MmioRead32 (LAN9118_RX_CFG); + RxCfgValue &= ~(RXCFG_RX_DMA_CNT_MASK); + RxCfgValue |= (ReadLimit & 0xFFFF) << 16; + + // Set end alignment to 4-bytes + RxCfgValue &= ~(RXCFG_RX_END_ALIGN_MASK); + MmioWrite32 (LAN9118_RX_CFG, RxCfgValue); + + // Update buffer size + *BuffSize = PLength; // -4 bytes may be needed: Received in buffer as + // 4 bytes longer than packet actually is, unless + // packet is < 64 bytes + + if (HdrSize != NULL) + *HdrSize = Snp->Mode->MediaHeaderSize; + + // Format the pointer + RawData = (UINT32*)Data; + + // Read Rx Packet + for (Count = 0; Count < ReadLimit; Count++) { + RawData[Count] = MmioRead32 (LAN9118_RX_DATA); + } + + // Check for Rx errors (worst possible error) + if (MmioRead32 (LAN9118_INT_STS) & INSTS_RXE) { + DEBUG((EFI_D_WARN, "Warning: Receiver Error. Restarting...\n")); + + // Initiate a software reset + if (SoftReset (0, Snp) < 0) { + DEBUG((EFI_D_ERROR, "Error: Soft Reset Failed: Hardware Error.\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Acknowledge the RXE + MmioWrite32 (LAN9118_INT_STS, INSTS_RXE); + gBS->Stall (LAN9118_STALL); + + // Restart the rx (and do not clear FIFO) + StartRx (0, Snp); + + // Say that command could not be sent + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Get the destination address + if (DstAddr != NULL) { + Dst.Addr[0] = (RawData[0] & 0xFF); + Dst.Addr[1] = (RawData[0] & 0xFF00) >> 8; + Dst.Addr[2] = (RawData[0] & 0xFF0000) >> 16; + Dst.Addr[3] = (RawData[0] & 0xFF000000) >> 24; + Dst.Addr[4] = (RawData[1] & 0xFF); + Dst.Addr[5] = (RawData[1] & 0xFF00) >> 8; + CopyMem (DstAddr, &Dst, NET_ETHER_ADDR_LEN); + } + + // Get the source address + if (SrcAddr != NULL) { + Src.Addr[0] = (RawData[1] & 0xFF0000) >> 16; + Src.Addr[1] = (RawData[1] & 0xFF000000) >> 24; + Src.Addr[2] = (RawData[2] & 0xFF); + Src.Addr[3] = (RawData[2] & 0xFF00) >> 8; + Src.Addr[4] = (RawData[2] & 0xFF0000) >> 16; + Src.Addr[5] = (RawData[2] & 0xFF000000) >> 24; + CopyMem (SrcAddr,&Src, NET_ETHER_ADDR_LEN); + } + + // Get the protocol + if (Protocol != NULL) { + *Protocol = NTOHS (RawData[3] & 0xFFFF); + } + + LanDriver->Stats.RxGoodFrames += 1; + Status = EFI_SUCCESS; + +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + + +/* +** Entry point for the LAN9118 driver +** +*/ +EFI_STATUS +Lan9118DxeEntry ( + IN EFI_HANDLE Handle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + LAN9118_DRIVER *LanDriver; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + LAN9118_DEVICE_PATH *Lan9118Path; + EFI_HANDLE ControllerHandle; + + // The PcdLan9118DxeBaseAddress PCD must be defined + ASSERT(PcdGet32 (PcdLan9118DxeBaseAddress) != 0); + + // Allocate Resources + LanDriver = AllocateZeroPool (sizeof(LAN9118_DRIVER)); + Lan9118Path = (LAN9118_DEVICE_PATH*)AllocateCopyPool(sizeof(LAN9118_DEVICE_PATH), &Lan9118PathTemplate); + + // Initialize pointers + Snp = &(LanDriver->Snp); + SnpMode = &(LanDriver->SnpMode); + Snp->Mode = SnpMode; + + // Set the signature of the LAN Driver structure + LanDriver->Signature = LAN9118_SIGNATURE; + + // Assign fields and func pointers + Snp->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + Snp->WaitForPacket = NULL; + Snp->Initialize = SnpInitialize; + Snp->Start = SnpStart; + Snp->Stop = SnpStop; + Snp->Reset = SnpReset; + Snp->Shutdown = SnpShutdown; + Snp->ReceiveFilters = SnpReceiveFilters; + Snp->StationAddress = SnpStationAddress; + Snp->Statistics = SnpStatistics; + Snp->MCastIpToMac = SnpMcastIptoMac; + Snp->NvData = SnpNvData; + Snp->GetStatus = SnpGetStatus; + Snp->Transmit = SnpTransmit; + Snp->Receive = SnpReceive; + + // Start completing simple network mode structure + SnpMode->State = EfiSimpleNetworkStopped; + SnpMode->HwAddressSize = NET_ETHER_ADDR_LEN; // HW address is 6 bytes + SnpMode->MediaHeaderSize = sizeof(ETHER_HEAD); // Not sure of this + SnpMode->MaxPacketSize = EFI_PAGE_SIZE; // Preamble + SOF + Ether Frame (with VLAN tag +4bytes) + SnpMode->NvRamSize = 0; // No NVRAM with this device + SnpMode->NvRamAccessSize = 0; // No NVRAM with this device + + // Update network mode information + SnpMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;/* | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;*/ + // Current allowed settings + SnpMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + + // LAN9118 has 64bit multicast hash table + SnpMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT; + SnpMode->MCastFilterCount = 0; + ZeroMem (&SnpMode->MCastFilter, MAX_MCAST_FILTER_CNT * sizeof(EFI_MAC_ADDRESS)); + + // Set the interface type (1: Ethernet or 6: IEEE 802 Networks) + SnpMode->IfType = NET_IFTYPE_ETHERNET; + + // Mac address is changeable as it is loaded from erasable memory + SnpMode->MacAddressChangeable = TRUE; + + // Can only transmit one packet at a time + SnpMode->MultipleTxSupported = FALSE; + + // MediaPresent checks for cable connection and partner link + SnpMode->MediaPresentSupported = TRUE; + SnpMode->MediaPresent = FALSE; + + // Set broadcast address + SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF); + SnpMode->PermanentAddress = GetCurrentMacAddress (); + SnpMode->CurrentAddress = SnpMode->PermanentAddress; + + // Assign fields for device path + Lan9118Path->Lan9118.MacAddress = SnpMode->PermanentAddress; + Lan9118Path->Lan9118.IfType = Snp->Mode->IfType; + + // Initialise the protocol + ControllerHandle = 0; + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, Snp, + &gEfiDevicePathProtocolGuid, Lan9118Path, + NULL + ); + // Say what the status of loading the protocol structure is + if (EFI_ERROR(Status)) { + FreePool (LanDriver); + } else { + LanDriver->ControllerHandle = ControllerHandle; + } + + return Status; +} diff --git a/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.inf b/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.inf new file mode 100644 index 000000000..232f502cc --- /dev/null +++ b/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118Dxe.inf @@ -0,0 +1,55 @@ +#/** @file +# INF file for the LAN9118 Network Controller Driver. +# +# Copyright (c) 2012-2013, 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. +# +#**/ + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = LAN9118Dxe + FILE_GUID = 4356b162-d0b2-11e1-8952-4437e6a60ea5 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 0.1 + ENTRY_POINT = Lan9118DxeEntry + +[Sources.common] + LAN9118Dxe.c + LAN9118DxeHw.h + +[Packages] + ArmPlatformPkg/ArmPlatformPkg.dec + NetworkPkg/NetworkPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + UefiLib + NetLib + UefiDriverEntryPoint + BaseMemoryLib + ArmLib + IoLib + TimerLib + DevicePathLib + +[Protocols] + gEfiSimpleNetworkProtocolGuid + gEfiMetronomeArchProtocolGuid + gEfiPxeBaseCodeProtocolGuid + gEfiDevicePathProtocolGuid + +[FixedPcd] + gArmPlatformTokenSpaceGuid.PcdLan9118DxeBaseAddress + +[Depex] + TRUE diff --git a/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118DxeHw.h b/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118DxeHw.h new file mode 100644 index 000000000..f622d0b54 --- /dev/null +++ b/ArmPlatformPkg/Drivers/LAN9118Dxe/LAN9118DxeHw.h @@ -0,0 +1,325 @@ +/** @file +* +* Copyright (c) 2012-2013, 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. +* +**/ + +#ifndef __LAN9118_DXE_HW_H__ +#define __LAN9118_DXE_HW_H__ + +/*--------------------------------------------------------------------------------------------------------------------- + + LAN9118 SMCS Registers + +---------------------------------------------------------------------------------------------------------------------*/ + +// Base address as on the VE board +#define LAN9118_BA ((UINT32) PcdGet32(PcdLan9118DxeBaseAddress)) + +/* ------------------------------ Tx and Rx Data and Status Memory Locations -------------------------------- */ +#define LAN9118_RX_DATA (0x00000000 + LAN9118_BA) +#define LAN9118_RX_STATUS (0x00000040 + LAN9118_BA) +#define LAN9118_RX_STATUS_PEEK (0x00000044 + LAN9118_BA) +#define LAN9118_TX_DATA (0x00000020 + LAN9118_BA) +#define LAN9118_TX_STATUS (0x00000048 + LAN9118_BA) +#define LAN9118_TX_STATUS_PEEK (0x0000004C + LAN9118_BA) + +/* ---------------------------------- System Control and Status Registers ----------------------------------- */ +#define LAN9118_ID_REV (0x00000050 + LAN9118_BA) // Chip ID and Revision +#define LAN9118_IRQ_CFG (0x00000054 + LAN9118_BA) // Interrupt Configuration +#define LAN9118_INT_STS (0x00000058 + LAN9118_BA) // Interrupt Status +#define LAN9118_INT_EN (0x0000005C + LAN9118_BA) // Interrupt Enable +//#define LAN9118_RESERVED (0x00000060) +#define LAN9118_BYTE_TEST (0x00000064 + LAN9118_BA) // Byte Order Test +#define LAN9118_FIFO_INT (0x00000068 + LAN9118_BA) // FIFO Level Interrupts +#define LAN9118_RX_CFG (0x0000006C + LAN9118_BA) // Receive Configuration +#define LAN9118_TX_CFG (0x00000070 + LAN9118_BA) // Transmit Configuration +#define LAN9118_HW_CFG (0x00000074 + LAN9118_BA) // Hardware Configuration +#define LAN9118_RX_DP_CTL (0x00000078 + LAN9118_BA) // Receive Data-Path Configuration +#define LAN9118_RX_FIFO_INF (0x0000007C + LAN9118_BA) // Receive FIFO Information +#define LAN9118_TX_FIFO_INF (0x00000080 + LAN9118_BA) // Transmit FIFO Information +#define LAN9118_PMT_CTRL (0x00000084 + LAN9118_BA) // Power Management Control +#define LAN9118_GPIO_CFG (0x00000088 + LAN9118_BA) // General Purpose IO Configuration +#define LAN9118_GPT_CFG (0x0000008C + LAN9118_BA) // General Purpose Timer Configuration +#define LAN9118_GPT_CNT (0x00000090 + LAN9118_BA) // General Purpose Timer Current Count +//#define LAN9118_RESERVED (0x00000094) +#define LAN9118_WORD_SWAP (0x00000098 + LAN9118_BA) // Word Swap Control +#define LAN9118_FREE_RUN (0x0000009C + LAN9118_BA) // Free-Run 25MHz Counter +#define LAN9118_RX_DROP (0x000000A0 + LAN9118_BA) // Receiver Dropped Frames Counter +#define LAN9118_MAC_CSR_CMD (0x000000A4 + LAN9118_BA) // MAC CSR Synchronizer Command +#define LAN9118_MAC_CSR_DATA (0x000000A8 + LAN9118_BA) // MAC CSR Synchronizer Data +#define LAN9118_AFC_CFG (0x000000AC + LAN9118_BA) // Automatic Flow Control Configuration +#define LAN9118_E2P_CMD (0x000000B0 + LAN9118_BA) // EEPROM Command +#define LAN9118_E2P_DATA (0x000000B4 + LAN9118_BA) // EEPROM Data +//#define LAN9118_RESERVED (0x000000B8 - 0x000000FC) + + +// Receiver Status bits +#define RXSTATUS_CRC_ERROR BIT1 // Cyclic Redundancy Check Error +#define RXSTATUS_DB BIT2 // Dribbling bit: Frame had non-integer multiple of 8bits +#define RXSTATUS_MII_ERROR BIT3 // Receive error during interception +#define RXSTATUS_RXW_TO BIT4 // Incomming frame larger than 2kb +#define RXSTATUS_FT BIT5 // 1: Ether type / 0: 802.3 type frame +#define RXSTATUS_LCOLL BIT6 // Late collision detected +#define RXSTATUS_FTL BIT7 // Frame longer than Ether type +#define RXSTATUS_MCF BIT10 // Frame has Multicast Address +#define RXSTATUS_RUNT BIT11 // Bad frame +#define RXSTATUS_LE BIT12 // Actual length of frame different than it claims +#define RXSTATUS_BCF BIT13 // Frame has Broadcast Address +#define RXSTATUS_ES BIT15 // Reports any error from bits 1,6,7 and 11 +#define RXSTATUS_PL_MASK (0x3FFF0000) // Packet length bit mask +#define RXSTATUS_FILT_FAIL BIT30 // The frame failed filtering test + +// Transmitter Status bits +#define TXSTATUS_DEF BIT0 // Packet tx was deferred +#define TXSTATUS_EDEF BIT2 // Tx ended because of excessive deferral (> 24288 bit times) +#define TXSTATUS_CC_MASK (0x00000078) // Collision Count (before Tx) bit mask +#define TXSTATUS_ECOLL BIT8 // Tx ended because of Excessive Collisions (makes CC_MASK invalid after 16 collisions) +#define TXSTATUS_LCOLL BIT9 // Packet Tx aborted after coll window of 64 bytes +#define TXSTATUS_NO_CA BIT10 // Carrier signal not present during Tx (bad?) +#define TXSTATUS_LOST_CA BIT11 // Lost carrier during Tx +#define TXSTATUS_ES BIT15 // Reports any errors from bits 1,2,8,9,10 and 11 +#define TXSTATUS_PTAG_MASK (0xFFFF0000) // Mask for Unique ID of packets (So we know who the packets are for) + +// ID_REV register bits +#define IDREV_ID ((MmioRead32(LAN9118_ID_REV) & 0xFFFF0000) >> 16) +#define IDREV_REV (MmioRead32(LAN9118_ID_REV) & 0x0000FFFF) + +// Interrupt Config Register bits +#define IRQCFG_IRQ_TYPE BIT0 // IRQ Buffer type +#define IRQCFG_IRQ_POL BIT4 // IRQ Polarity +#define IRQCFG_IRQ_EN BIT8 // Enable external interrupt +#define IRQCFG_IRQ_INT BIT12 // State of internal interrupts line +#define IRQCFG_INT_DEAS_STS BIT13 // State of deassertion interval +#define IRQCFG_INT_DEAS_CLR BIT14 // Clear the deassertion counter +#define IRQCFG_INT_DEAS_MASK (0xFF000000) // Interrupt deassertion interval value mask + +// Interrupt Status Register bits +#define INSTS_GPIO_MASK (0x7) // GPIO interrupts mask +#define INSTS_RSFL BIT3 // Rx Status FIFO Level reached +#define INSTS_RSFF BIT4 // Rx Status FIFO full +#define INSTS_RXDF_INT BIT6 // Rx Frame dropped +#define INSTS_TSFL BIT7 // Tx Status FIFO Level reached +#define INSTS_TSFF BIT8 // Tx Status FIFO full +#define INSTS_TDFA BIT9 // Tx Data FIFO Level exceeded +#define INSTS_TDFO BIT10 // Tx Data FIFO full +#define INSTS_TXE BIT13 // Transmitter Error +#define INSTS_RXE BIT14 // Receiver Error +#define INSTS_RWT BIT15 // Packet > 2048 bytes received +#define INSTS_TXSO BIT16 // Tx Status FIFO Overflow +#define INSTS_PME_INT BIT17 // PME Signal detected +#define INSTS_PHY_INT BIT18 // Indicates PHY Interrupt +#define INSTS_GPT_INT BIT19 // GP Timer wrapped past 0xFFFF +#define INSTS_RXD_INT BIT20 // Indicates that amount of data written to RX_CFG was cleared +#define INSTS_TX_IOC BIT21 // Finished loading IOC flagged buffer to Tx FIFO +#define INSTS_RXDFH_INT BIT23 // Rx Dropped frames went past 0x7FFFFFFF +#define INSTS_RXSTOP_INT BIT24 // Rx was stopped +#define INSTS_TXSTOP_INT BIT25 // Tx was stopped +#define INSTS_SW_INT BIT31 // Software Interrupt occurred + +// Interrupt Enable Register bits + + +// Hardware Config Register bits +#define HWCFG_SRST BIT0 // Software Reset bit (SC) +#define HWCFG_SRST_TO (0x2) // Software Reset Timeout bit (RO) +#define HWCFG_BMODE (0x4) // 32/16 bit Mode bit (RO) +#define HWCFG_TX_FIFO_SIZE_MASK (~ (UINT32)0xF0000) // Mask to Clear FIFO Size +#define HWCFG_MBO BIT20 // Must Be One bit + +// Power Management Control Register +#define MPTCTRL_READY BIT0 // Device ready indicator +#define MPTCTRL_PME_EN BIT1 // Enable external PME signals +#define MPTCTRL_PME_POL BIT2 // Set polarity of PME signals +#define MPTCTRL_PME_IND BIT3 // Signal type of PME (refer to Spec) +#define MPTCTRL_WUPS_MASK (0x18) // Wake up status indicator mask +#define MPTCTRL_PME_TYPE BIT6 // PME Buffer type (Open Drain or Push-Pull) +#define MPTCTRL_ED_EN BIT8 // Energy-detect enable +#define MPTCTRL_WOL_EN BIT9 // Enable wake-on-lan +#define MPTCTRL_PHY_RST BIT10 // Reset the PHY +#define MPTCTRL_PM_MODE_MASK (BIT12 | BIT13) // Set the power mode + +// PHY control register bits +#define PHYCR_COLL_TEST BIT7 // Collision test enable +#define PHYCR_DUPLEX_MODE BIT8 // Set Duplex Mode +#define PHYCR_RST_AUTO BIT9 // Restart Auto-Negotiation of Link abilities +#define PHYCR_PD BIT11 // Power-Down switch +#define PHYCR_AUTO_EN BIT12 // Auto-Negotiation Enable +#define PHYCR_SPEED_SEL BIT13 // Link Speed Selection +#define PHYCR_LOOPBK BIT14 // Set loopback mode +#define PHYCR_RESET BIT15 // Do a PHY reset + +// PHY status register bits +#define PHYSTS_EXT_CAP BIT0 // Extended Capabilities Register capability +#define PHYSTS_JABBER BIT1 // Jabber condition detected +#define PHYSTS_LINK_STS BIT2 // Link Status +#define PHYSTS_AUTO_CAP BIT3 // Auto-Negotiation Capability +#define PHYSTS_REMOTE_FAULT BIT4 // Remote fault detected +#define PHYSTS_AUTO_COMP BIT5 // Auto-Negotiation Completed +#define PHYSTS_10BASET_HDPLX BIT11 // 10Mbps Half-Duplex ability +#define PHYSTS_10BASET_FDPLX BIT12 // 10Mbps Full-Duplex ability +#define PHYSTS_100BASETX_HDPLX BIT13 // 100Mbps Half-Duplex ability +#define PHYSTS_100BASETX_FDPLX BIT14 // 100Mbps Full-Duplex ability +#define PHYSTS_100BASE_T4 BIT15 // Base T4 ability + +// PHY Auto-Negotiation advertisement +#define PHYANA_SEL_MASK ((UINT32)0x1F) // Link type selector +#define PHYANA_CSMA BIT0 // Advertise CSMA capability +#define PHYANA_10BASET BIT5 // Advertise 10BASET capability +#define PHYANA_10BASETFD BIT6 // Advertise 10BASET Full duplex capability +#define PHYANA_100BASETX BIT7 // Advertise 100BASETX capability +#define PHYANA_100BASETXFD BIT8 // Advertise 100 BASETX Full duplex capability +#define PHYANA_100BASET4 BIT9 // Advertise 100 BASETX Full duplex capability +#define PHYANA_PAUSE_OP_MASK (3 << 10) // Advertise PAUSE frame capability +#define PHYANA_REMOTE_FAULT BIT13 // Remote fault detected + + +// PHY Auto-Negotiation Link Partner Ability + +// PHY Auto-Negotiation Expansion + +// PHY Mode control/status + +// PHY Special Modes + +// PHY Special control/status + +// PHY Interrupt Source Flags + +// PHY Interrupt Mask + +// PHY Super Special control/status +#define PHYSSCS_HCDSPEED_MASK (7 << 2) // Speed indication +#define PHYSSCS_AUTODONE BIT12 // Auto-Negotiation Done + + +// MAC control register bits +#define MACCR_RX_EN BIT2 // Enable Receiver bit +#define MACCR_TX_EN BIT3 // Enable Transmitter bit +#define MACCR_DFCHK BIT5 // Deferral Check bit +#define MACCR_PADSTR BIT8 // Automatic Pad Stripping bit +#define MACCR_BOLMT_MASK (0xC0) // Back-Off limit mask +#define MACCR_DISRTY BIT10 // Disable Transmit Retry bit +#define MACCR_BCAST BIT11 // Disable Broadcast Frames bit +#define MACCR_LCOLL BIT12 // Late Collision Control bit +#define MACCR_HPFILT BIT13 // Hash/Perfect Filtering Mode bit +#define MACCR_HO BIT15 // Hash Only Filtering Mode +#define MACCR_PASSBAD BIT16 // Receive all frames that passed filter bit +#define MACCR_INVFILT BIT17 // Enable Inverse Filtering bit +#define MACCR_PRMS BIT18 // Promiscuous Mode bit +#define MACCR_MCPAS BIT19 // Pass all Multicast packets bit +#define MACCR_FDPX BIT20 // Full Duplex Mode bit +#define MACCR_LOOPBK BIT21 // Loopback operation mode bit +#define MACCR_RCVOWN BIT23 // Disable Receive Own frames bit +#define MACCR_RX_ALL BIT31 // Receive all Packets and route to Filter + +// Wake-Up Control and Status Register +#define WUCSR_MPEN BIT1 // Magic Packet enable (allow wake from Magic P) +#define WUCSR_WUEN BIT2 // Allow remote wake up using Wake-Up Frames +#define WUCSR_MPR_MASK (0x10) // Received Magic Packet +#define WUCSR_WUFR_MASK (0x20) // Received Wake-Up Frame +#define WUCSR_GUE BIT9 // Enable wake on global unicast frames + +// RX Configuration Register bits +#define RXCFG_RXDOFF_MASK (0x1F00) // Rx Data Offset in Bytes +#define RXCFG_RX_DUMP BIT15 // Clear Rx data and status FIFOs +#define RXCFG_RX_DMA_CNT_MASK (0x0FFF0000) // Amount of data to be read from Rx FIFO +#define RXCFG_RX_END_ALIGN_MASK (0xC0000000) // Alignment to preserve + +// TX Configuration Register bits +#define TXCFG_STOP_TX BIT0 // Stop the transmitter +#define TXCFG_TX_ON BIT1 // Start the transmitter +#define TXCFG_TXSAO BIT2 // Tx Status FIFO full +#define TXCFG_TXD_DUMP BIT14 // Clear Tx Data FIFO +#define TXCFG_TXS_DUMP BIT15 // Clear Tx Status FIFO + +// Rx FIFO Information Register bits +#define RXFIFOINF_RXDUSED_MASK (0xFFFF) // Rx Data FIFO Used Space +#define RXFIFOINF_RXSUSED_MASK (0xFF0000) // Rx Status FIFO Used Space + +// Tx FIFO Information Register bits +#define TXFIFOINF_TDFREE_MASK (0xFFFF) // Tx Data FIFO Free Space +#define TXFIFOINF_TXSUSED_MASK (0xFF0000) // Tx Status FIFO Used Space + +// E2P Register +#define E2P_EPC_BUSY BIT31 +#define E2P_EPC_TIMEOUT BIT9 +#define E2P_EPC_MAC_ADDRESS_LOADED BIT8 + +// GPIO Configuration register +#define GPIO_GPIO0_PUSH_PULL BIT16 +#define GPIO_GPIO1_PUSH_PULL BIT17 +#define GPIO_GPIO2_PUSH_PULL BIT18 +#define GPIO_LED1_ENABLE BIT28 +#define GPIO_LED2_ENABLE BIT29 +#define GPIO_LED3_ENABLE BIT30 + +// MII_ACC bits +#define MII_ACC_MII_BUSY BIT0 +#define MII_ACC_MII_WRITE BIT1 +#define MII_ACC_MII_READ 0 + +#define MII_ACC_PHY_VALUE BIT11 +#define MII_ACC_MII_REG_INDEX(index) (((index) & 0x1F) << 6) + +// +// PHY Control Indexes +// +#define PHY_INDEX_BASIC_CTRL 0 +#define PHY_INDEX_BASIC_STATUS 1 +#define PHY_INDEX_ID1 2 +#define PHY_INDEX_ID2 3 +#define PHY_INDEX_AUTO_NEG_ADVERT 4 +#define PHY_INDEX_AUTO_NEG_LINK_ABILITY 5 +#define PHY_INDEX_AUTO_NEG_EXP 6 +#define PHY_INDEX_MODE 17 +#define PHY_INDEX_SPECIAL_MODES 18 +#define PHY_INDEX_SPECIAL_CTLR 27 +#define PHY_INDEX_INT_SRC 29 +#define PHY_INDEX_INT_MASK 30 +#define PHY_INDEX_SPECIAL_PHY_CTLR 31 + +// Indirect MAC Indexes +#define INDIRECT_MAC_INDEX_CR 1 +#define INDIRECT_MAC_INDEX_ADDRH 2 +#define INDIRECT_MAC_INDEX_ADDRL 3 +#define INDIRECT_MAC_INDEX_HASHH 4 +#define INDIRECT_MAC_INDEX_HASHL 5 +#define INDIRECT_MAC_INDEX_MII_ACC 6 +#define INDIRECT_MAC_INDEX_MII_DATA 7 + +// +// MAC CSR Synchronizer Command register +// +#define MAC_CSR_BUSY BIT31 +#define MAC_CSR_READ BIT30 +#define MAC_CSR_WRITE 0 +#define MAC_CSR_ADDR(Addr) ((Addr) & 0xFF) + +// +// TX Packet Format +// +#define TX_CMD_A_COMPLETION_INT BIT31 +#define TX_CMD_A_START_OFFSET(off) (((off) & 0x1f) << 16) +#define TX_CMD_A_FIRST_SEGMENT BIT13 +#define TX_CMD_A_LAST_SEGMENT BIT12 +#define TX_CMD_A_BUFF_SIZE(size) ((size) & 0x000003FF) +#define TX_CMD_B_PACKET_TAG(tag) ((tag) << 16) +#define TX_CMD_B_PACKET_LENGTH(size) ((size) & 0x000003FF) + +// +// Conditional compilation flags +// +//#define USE_TX_BUFFER +//#define EVAL_PERFORMANCE + + +#endif /* __LAN9118_DXE_HDR_H__ */ diff --git a/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.c b/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.c new file mode 100644 index 000000000..90c3288ba --- /dev/null +++ b/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.c @@ -0,0 +1,2234 @@ +/** @file +* SMSC LAN91x series Network Controller Driver. +* +* Copyright (c) 2013 Linaro.org +* +* Derived from the LAN9118 driver. Original sources +* Copyright (c) 2012-2013, 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 <Uefi.h> +#include <Uefi/UefiSpec.h> +#include <Base.h> + +// Protocols used by this driver +#include <Protocol/SimpleNetwork.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/PxeBaseCode.h> +#include <Protocol/DevicePath.h> + +// Libraries used by this driver +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/IoLib.h> +#include <Library/PcdLib.h> +#include <Library/NetLib.h> +#include <Library/DevicePathLib.h> + +// Hardware register definitions +#include "LAN91xDxeHw.h" + +// Debugging output options +//#define LAN91X_PRINT_REGISTERS 1 +//#define LAN91X_PRINT_PACKET_HEADERS 1 +//#define LAN91X_PRINT_RECEIVE_FILTERS 1 + +// Chip power-down option -- UNTESTED +//#define LAN91X_POWER_DOWN 1 + +/*--------------------------------------------------------------------------------------------------------------------- + + LAN91x Information Structure + +---------------------------------------------------------------------------------------------------------------------*/ +typedef struct _LAN91X_DRIVER { + // Driver signature + UINT32 Signature; + EFI_HANDLE ControllerHandle; + + // EFI SNP protocol instances + EFI_SIMPLE_NETWORK_PROTOCOL Snp; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + // EFI Snp statistics instance + EFI_NETWORK_STATISTICS Stats; + + // Transmit Buffer recycle queue +#define TX_QUEUE_DEPTH 16 + VOID *TxQueue[TX_QUEUE_DEPTH]; + UINTN TxQueHead; + UINTN TxQueTail; + + // Register access variables + UINTN IoBase; // I/O Base Address + UINT8 Revision; // Chip Revision Number + INT8 PhyAd; // Phy Address + UINT8 BankSel; // Currently selected register bank + +} LAN91X_DRIVER; + +#define LAN91X_NO_PHY (-1) // PhyAd value if PHY not detected + +#define LAN91X_SIGNATURE SIGNATURE_32('S', 'M', '9', '1') +#define INSTANCE_FROM_SNP_THIS(a) CR(a, LAN91X_DRIVER, Snp, LAN91X_SIGNATURE) + +#define LAN91X_STALL 2 +#define LAN91X_MEMORY_ALLOC_POLLS 100 // Max times to poll for memory allocation +#define LAN91X_PKT_OVERHEAD 6 // Overhead bytes in packet buffer + +// Synchronization TPLs +#define LAN91X_TPL TPL_CALLBACK + +// Most common CRC32 Polynomial for little endian machines +#define CRC_POLYNOMIAL 0xEDB88320 + + +typedef struct { + MAC_ADDR_DEVICE_PATH Lan91x; + EFI_DEVICE_PATH_PROTOCOL End; +} LAN91X_DEVICE_PATH; + +LAN91X_DEVICE_PATH Lan91xPathTemplate = { + { + { + MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP, + { (UINT8) (sizeof(MAC_ADDR_DEVICE_PATH)), (UINT8) ((sizeof(MAC_ADDR_DEVICE_PATH)) >> 8) } + }, + { 0 }, + 0 + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + sizeof(EFI_DEVICE_PATH_PROTOCOL), + 0 + } +}; + +// Chip ID numbers and name strings +#define CHIP_9192 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 6 +#define CHIP_91100 7 +#define CHIP_91100FD 8 +#define CHIP_91111FD 9 + +STATIC CHAR16 CONST * CONST ChipIds[ 16 ] = { + NULL, NULL, NULL, + /* 3 */ L"SMC91C90/91C92", + /* 4 */ L"SMC91C94", + /* 5 */ L"SMC91C95", + /* 6 */ L"SMC91C96", + /* 7 */ L"SMC91C100", + /* 8 */ L"SMC91C100FD", + /* 9 */ L"SMC91C11xFD", + NULL, NULL, NULL, + NULL, NULL, NULL +}; + + +/* ------------------ TxBuffer Queue functions ------------------- */ + +#define TxQueNext(off) ((((off) + 1) >= TX_QUEUE_DEPTH) ? 0 : ((off) + 1)) + +STATIC +BOOLEAN +TxQueInsert ( + IN LAN91X_DRIVER *LanDriver, + IN VOID *Buffer + ) +{ + + if (TxQueNext (LanDriver->TxQueTail) == LanDriver->TxQueHead) { + return FALSE; + } + + LanDriver->TxQueue[LanDriver->TxQueTail] = Buffer; + LanDriver->TxQueTail = TxQueNext (LanDriver->TxQueTail); + + return TRUE; +} + +STATIC +VOID +*TxQueRemove ( + IN LAN91X_DRIVER *LanDriver + ) +{ + VOID *Buffer; + + if (LanDriver->TxQueTail == LanDriver->TxQueHead) { + return NULL; + } + + Buffer = LanDriver->TxQueue[LanDriver->TxQueHead]; + LanDriver->TxQueue[LanDriver->TxQueHead] = NULL; + LanDriver->TxQueHead = TxQueNext (LanDriver->TxQueHead); + + return Buffer; +} + +/* ------------------ MAC Address Hash Calculations ------------------- */ + +/* +** Generate a hash value from a multicast address +** +** This uses the Ethernet standard CRC32 algorithm +** +** 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 +*/ +STATIC +UINT32 +MulticastHash ( + IN EFI_MAC_ADDRESS *Mac, + IN UINT32 AddrLen + ) +{ + UINT32 Iter; + UINT32 Remainder; + UINT32 Crc32; + UINT8 *Addr; + + // 0xFFFFFFFF is standard seed for Ethernet + Remainder = 0xFFFFFFFF; + + // Generate the remainder byte-by-byte (LSB first) + Addr = &Mac->Addr[0]; + while (AddrLen-- > 0) { + Remainder ^= *Addr++; + for (Iter = 0; Iter < 8; ++Iter) { + // Check if exponent is set + if ((Remainder & 1) != 0) { + Remainder = (Remainder >> 1) ^ CRC_POLYNOMIAL; + } else { + Remainder = (Remainder >> 1) ^ 0; + } + } + } + + // Reverse the bits of the remainder + Crc32 = 0; + for (Iter = 0; Iter < 32; ++Iter) { + Crc32 <<= 1; + Crc32 |= Remainder & 1; + Remainder >>= 1; + } + return Crc32; +} + + +/* ---------------- Banked Register Operations ------------------ */ + +// Select the proper I/O bank +STATIC +VOID +SelectIoBank ( + LAN91X_DRIVER *LanDriver, + UINTN Register + ) +{ + UINT8 Bank; + + Bank = RegisterToBank (Register); + + // Select the proper I/O bank + if (LanDriver->BankSel != Bank) { + MmioWrite16 (LanDriver->IoBase + LAN91X_BANK_OFFSET, Bank); + LanDriver->BankSel = Bank; + } +} + +// Read a 16-bit I/O-space register +STATIC +UINT16 +ReadIoReg16 ( + LAN91X_DRIVER *LanDriver, + UINTN Register + ) +{ + UINT8 Offset; + + // Select the proper I/O bank + SelectIoBank (LanDriver, Register); + + // Read the requested register + Offset = RegisterToOffset (Register); + return MmioRead16 (LanDriver->IoBase + Offset); +} + +// Write a 16-bit I/O-space register +STATIC +UINT16 +WriteIoReg16 ( + LAN91X_DRIVER *LanDriver, + UINTN Register, + UINT16 Value + ) +{ + UINT8 Offset; + + // Select the proper I/O bank + SelectIoBank (LanDriver, Register); + + // Write the requested register + Offset = RegisterToOffset (Register); + return MmioWrite16 (LanDriver->IoBase + Offset, Value); +} + +// Read an 8-bit I/O-space register +STATIC +UINT8 +ReadIoReg8 ( + LAN91X_DRIVER *LanDriver, + UINTN Register + ) +{ + UINT8 Offset; + + // Select the proper I/O bank + SelectIoBank (LanDriver, Register); + + // Read the requested register + Offset = RegisterToOffset (Register); + return MmioRead8 (LanDriver->IoBase + Offset); +} + +// Write an 8-bit I/O-space register +STATIC +UINT8 +WriteIoReg8 ( + LAN91X_DRIVER *LanDriver, + UINTN Register, + UINT8 Value + ) +{ + UINT8 Offset; + + // Select the proper I/O bank + SelectIoBank (LanDriver, Register); + + // Write the requested register + Offset = RegisterToOffset (Register); + return MmioWrite8 (LanDriver->IoBase + Offset, Value); +} + + +/* ---------------- MII/PHY Access Operations ------------------ */ + +#define LAN91X_MDIO_STALL 1 + +STATIC +VOID +MdioOutput ( + LAN91X_DRIVER *LanDriver, + UINTN Bits, + UINT32 Value + ) +{ + UINT16 MgmtReg; + UINT32 Mask; + + MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT); + MgmtReg &= ~MGMT_MCLK; + MgmtReg |= MGMT_MDOE; + + for (Mask = (1 << (Bits - 1)); Mask != 0; Mask >>= 1) { + if ((Value & Mask) != 0) { + MgmtReg |= MGMT_MDO; + } else { + MgmtReg &= ~MGMT_MDO; + } + + WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg); + gBS->Stall (LAN91X_MDIO_STALL); + WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg | MGMT_MCLK); + gBS->Stall (LAN91X_MDIO_STALL); + } +} +#define PHY_OUTPUT_TIME (2 * LAN91X_MDIO_STALL) + +STATIC +UINT32 +MdioInput ( + LAN91X_DRIVER *LanDriver, + UINTN Bits + ) +{ + UINT16 MgmtReg; + UINT32 Mask; + UINT32 Value; + + MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT); + MgmtReg &= ~(MGMT_MDOE | MGMT_MCLK | MGMT_MDO); + WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg); + + Value = 0; + for (Mask = (1 << (Bits - 1)); Mask != 0; Mask >>= 1) { + if ((ReadIoReg16 (LanDriver, LAN91X_MGMT) & MGMT_MDI) != 0) { + Value |= Mask; + } + + WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg); + gBS->Stall (LAN91X_MDIO_STALL); + WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg | MGMT_MCLK); + gBS->Stall (LAN91X_MDIO_STALL); + } + + return Value; +} +#define PHY_INPUT_TIME (2 * LAN91X_MDIO_STALL) + +STATIC +VOID +MdioIdle ( + LAN91X_DRIVER *LanDriver + ) +{ + UINT16 MgmtReg; + + MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT); + MgmtReg &= ~(MGMT_MDOE | MGMT_MCLK | MGMT_MDO); + WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg); +} + +// Write to a PHY register +STATIC +VOID +WritePhyReg16 ( + LAN91X_DRIVER *LanDriver, + UINTN RegAd, + UINT16 Value + ) +{ + // Bit-bang the MII Serial Frame write operation + MdioOutput (LanDriver, 32, 0xffffffff); // Send 32 Ones as a preamble + MdioOutput (LanDriver, 2, 0x01); // Send Start (01) + MdioOutput (LanDriver, 2, 0x01); // Send Write (01) + MdioOutput (LanDriver, 5, LanDriver->PhyAd); // Send PHYAD[4:0] + MdioOutput (LanDriver, 5, RegAd); // Send REGAD[4:0] + MdioOutput (LanDriver, 2, 0x02); // Send TurnAround (10) + MdioOutput (LanDriver, 16, Value); // Write 16 data bits + + // Idle the MDIO bus + MdioIdle (LanDriver); +} +// Calculate approximate time to write a PHY register in microseconds +#define PHY_WRITE_TIME ((32 + 2 + 2 + 5 + 5 + 2 + 16) * PHY_OUTPUT_TIME) + +// Read from a PHY register +STATIC +UINT16 +ReadPhyReg16 ( + LAN91X_DRIVER *LanDriver, + UINTN RegAd + ) +{ + UINT32 Value; + + // Bit-bang the MII Serial Frame read operation + MdioOutput (LanDriver, 32, 0xffffffff); // Send 32 Ones as a preamble + MdioOutput (LanDriver, 2, 0x01); // Send Start (01) + MdioOutput (LanDriver, 2, 0x02); // Send Read (10) + MdioOutput (LanDriver, 5, LanDriver->PhyAd); // Send PHYAD[4:0] + MdioOutput (LanDriver, 5, RegAd); // Send REGAD[4:0] + + (VOID) MdioInput (LanDriver, 2); // Discard TurnAround bits + Value = MdioInput (LanDriver, 16); // Read 16 data bits + + // Idle the MDIO bus + MdioIdle (LanDriver); + + return (Value & 0xffff); +} +// Calculate approximate time to read a PHY register in microseconds +#define PHY_READ_TIME (((32 + 2 + 2 + 5 + 5) * PHY_OUTPUT_TIME) + \ + ((2 + 16) * PHY_INPUT_TIME)) + + +/* ---------------- Debug Functions ------------------ */ + +#ifdef LAN91X_PRINT_REGISTERS +STATIC +VOID +PrintIoRegisters ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINTN Bank; + UINTN Offset; + UINT16 Value; + + DEBUG((EFI_D_ERROR, "\nLAN91x I/O Register Dump:\n")); + + // Print currrent bank select register + Value = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET); + DEBUG((EFI_D_ERROR, " BankSel: %d Bank Register %04x (%d)\n", + LanDriver->BankSel, Value, Value & 0x0007)); + + // Print all I/O registers + for (Offset = 0; Offset < 0x0e; Offset += 2) { + DEBUG((EFI_D_ERROR, " %02x:", Offset)); + for (Bank = 0; Bank <= 3; ++Bank) { + DEBUG((EFI_D_ERROR, " %04x", ReadIoReg16 (LanDriver, MakeRegister (Bank, Offset)))); + } + DEBUG((EFI_D_ERROR, "\n")); + } +} + +STATIC +VOID +PrintPhyRegisters ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINTN RegNum; + + DEBUG((EFI_D_ERROR, "\nLAN91x Phy %d Register Dump:\n", LanDriver->PhyAd)); + + // Print all Phy registers + for (RegNum = 0; RegNum <= 5; ++RegNum) { + DEBUG((EFI_D_ERROR, " %2d: %04x\n", + RegNum, + ReadPhyReg16 (LanDriver, RegNum) + )); + } + for (RegNum = 16; RegNum <= 20; ++RegNum) { + DEBUG((EFI_D_ERROR, " %2d: %04x\n", + RegNum, + ReadPhyReg16 (LanDriver, RegNum) + )); + } +} +#endif + +#if LAN91X_PRINT_PACKET_HEADERS +STATIC +VOID +PrintIpDgram ( + IN CONST VOID *DstMac, + IN CONST VOID *SrcMac, + IN CONST VOID *Proto, + IN CONST VOID *IpDgram + ) +{ + CONST UINT8 *Ptr; + UINT16 SrcPort; + UINT16 DstPort; + + Ptr = DstMac; + DEBUG((EFI_D_ERROR, " Dst: %02x-%02x-%02x", + Ptr[0], Ptr[1], Ptr[2])); + DEBUG((EFI_D_ERROR, "-%02x-%02x-%02x", + Ptr[3], Ptr[4], Ptr[5])); + + Ptr = SrcMac; + DEBUG((EFI_D_ERROR, " Src: %02x-%02x-%02x", + Ptr[0], Ptr[1], Ptr[2])); + DEBUG((EFI_D_ERROR, "-%02x-%02x-%02x", + Ptr[3], Ptr[4], Ptr[5])); + + Ptr = Proto; + DEBUG((EFI_D_ERROR, " Proto: %02x%02x\n", + Ptr[0], Ptr[1])); + + Ptr = IpDgram; + switch (Ptr[9]) { + case EFI_IP_PROTO_ICMP: + DEBUG((EFI_D_ERROR, " ICMP")); + break; + case EFI_IP_PROTO_TCP: + DEBUG((EFI_D_ERROR, " TCP")); + break; + case EFI_IP_PROTO_UDP: + DEBUG((EFI_D_ERROR, " UDP")); + break; + default: + DEBUG((EFI_D_ERROR, " IpProto %d\n", Ptr[9])); + return; + } + + DEBUG((EFI_D_ERROR, " SrcIp: %d.%d.%d.%d", + Ptr[12], Ptr[13], Ptr[14], Ptr[15])); + DEBUG((EFI_D_ERROR, " DstIp: %d.%d.%d.%d", + Ptr[16], Ptr[17], Ptr[18], Ptr[19])); + + SrcPort = (Ptr[20] << 8) | Ptr[21]; + DstPort = (Ptr[22] << 8) | Ptr[23]; + DEBUG((EFI_D_ERROR, " SrcPort: %d DstPort: %d\n", SrcPort, DstPort)); +} +#endif + + +/* ---------------- PHY Management Operations ----------------- */ + +STATIC +EFI_STATUS +PhyDetect ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINT16 PhyId1; + UINT16 PhyId2; + + for (LanDriver->PhyAd = 0x1f; LanDriver->PhyAd >= 0 ; --LanDriver->PhyAd) { + PhyId1 = ReadPhyReg16 (LanDriver, PHY_INDEX_ID1); + PhyId2 = ReadPhyReg16 (LanDriver, PHY_INDEX_ID2); + + if ((PhyId1 != 0x0000) && (PhyId1 != 0xffff) && + (PhyId2 != 0x0000) && (PhyId2 != 0xffff)) { + if ((PhyId1 == 0x0016) && ((PhyId2 & 0xfff0) == 0xf840)) { + DEBUG((EFI_D_ERROR, "LAN91x: PHY type LAN83C183 (LAN91C111 Internal)\n")); + } else if ((PhyId1 == 0x0282) && ((PhyId2 & 0xfff0) == 0x1c50)) { + DEBUG((EFI_D_ERROR, "LAN91x: PHY type LAN83C180\n")); + } else { + DEBUG((EFI_D_ERROR, "LAN91x: PHY id %04x:%04x\n", PhyId1, PhyId2)); + } + return EFI_SUCCESS; + } + } + + DEBUG((EFI_D_ERROR, "LAN91x: PHY detection failed\n")); + return EFI_NO_MEDIA; +} + + +// Check the Link Status and take appropriate action +STATIC +BOOLEAN +CheckLinkStatus ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINT16 PhyStatus; + + // Get the PHY Status + PhyStatus = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS); + + return (PhyStatus & PHYSTS_LINK_STS) != 0; +} + + +// Do auto-negotiation +STATIC +EFI_STATUS +PhyAutoNegotiate ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINTN Retries; + UINT16 PhyControl; + UINT16 PhyStatus; + UINT16 PhyAdvert; + + // If there isn't a PHY, don't try to reset it + if (LanDriver->PhyAd == LAN91X_NO_PHY) { + return EFI_SUCCESS; + } + + // Next check that auto-negotiation is supported + PhyStatus = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS); + if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) { + return EFI_SUCCESS; + } + + // Translate capabilities to advertise + PhyAdvert = PHYANA_CSMA; + + if ((PhyStatus & PHYSTS_10BASET_HDPLX) != 0) { + PhyAdvert |= PHYANA_10BASET; + } + if ((PhyStatus & PHYSTS_10BASET_FDPLX) != 0) { + PhyAdvert |= PHYANA_10BASETFD; + } + if ((PhyStatus & PHYSTS_100BASETX_HDPLX) != 0) { + PhyAdvert |= PHYANA_100BASETX; + } + if ((PhyStatus & PHYSTS_100BASETX_FDPLX) != 0) { + PhyAdvert |= PHYANA_100BASETXFD; + } + if ((PhyStatus & PHYSTS_100BASE_T4) != 0) { + PhyAdvert |= PHYANA_100BASET4; + } + + // Set the capabilities to advertise + WritePhyReg16 (LanDriver, PHY_INDEX_AUTO_NEG_ADVERT, PhyAdvert); + (VOID) ReadPhyReg16 (LanDriver, PHY_INDEX_AUTO_NEG_ADVERT); + + // Restart Auto-Negotiation + PhyControl = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL); + PhyControl &= ~(PHYCR_SPEED_SEL | PHYCR_DUPLEX_MODE); + PhyControl |= PHYCR_AUTO_EN | PHYCR_RST_AUTO; + WritePhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL, PhyControl); + + // Wait up to 2 seconds for the process to complete + Retries = 2000000 / (PHY_READ_TIME + 100); + while ((ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0) { + if (--Retries == 0) { + DEBUG((EFI_D_ERROR, "LAN91x: PHY auto-negotiation timed-out\n")); + return EFI_TIMEOUT; + } + gBS->Stall (100); + } + + return EFI_SUCCESS; +} + + +// Perform PHY software reset +STATIC +EFI_STATUS +PhySoftReset ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINTN Retries; + + // If there isn't a PHY, don't try to reset it + if (LanDriver->PhyAd == LAN91X_NO_PHY) { + return EFI_SUCCESS; + } + + // Request a PHY reset + WritePhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL, PHYCR_RESET); + + // The internal PHY will reset within 50ms. Allow 100ms. + Retries = 100000 / (PHY_READ_TIME + 100); + while (ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) { + if (--Retries == 0) { + DEBUG((EFI_D_ERROR, "LAN91x: PHY reset timed-out\n")); + return EFI_TIMEOUT; + } + gBS->Stall (100); + } + + return EFI_SUCCESS; +} + + +/* ---------------- General Operations ----------------- */ + +STATIC +EFI_MAC_ADDRESS +GetCurrentMacAddress ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINTN RegNum; + UINT8 *Addr; + EFI_MAC_ADDRESS MacAddress; + + SetMem (&MacAddress, sizeof(MacAddress), 0); + + Addr = &MacAddress.Addr[0]; + for (RegNum = LAN91X_IAR0; RegNum <= LAN91X_IAR5; ++RegNum) { + *Addr = ReadIoReg8 (LanDriver, RegNum); + ++Addr; + } + + return MacAddress; +} + +STATIC +EFI_STATUS +SetCurrentMacAddress ( + IN LAN91X_DRIVER *LanDriver, + IN EFI_MAC_ADDRESS *MacAddress + ) +{ + UINTN RegNum; + UINT8 *Addr; + + Addr = &MacAddress->Addr[0]; + for (RegNum = LAN91X_IAR0; RegNum <= LAN91X_IAR5; ++RegNum) { + WriteIoReg8 (LanDriver, RegNum, *Addr); + ++Addr; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MmuOperation ( + IN LAN91X_DRIVER *LanDriver, + IN UINTN MmuOp + ) +{ + UINTN Polls; + + WriteIoReg16 (LanDriver, LAN91X_MMUCR, MmuOp); + Polls = 100; + while ((ReadIoReg16 (LanDriver, LAN91X_MMUCR) & MMUCR_BUSY) != 0) { + if (--Polls == 0) { + DEBUG((EFI_D_ERROR, "LAN91x: MMU operation %04x timed-out\n", MmuOp)); + return EFI_TIMEOUT; + } + gBS->Stall (LAN91X_STALL); + } + + return EFI_SUCCESS; +} + +// Read bytes from the DATA register +STATIC +EFI_STATUS +ReadIoData ( + IN LAN91X_DRIVER *LanDriver, + IN VOID *Buffer, + IN UINTN BufLen + ) +{ + UINT8 *Ptr; + + Ptr = Buffer; + for (; BufLen > 0; --BufLen) { + *Ptr = ReadIoReg8 (LanDriver, LAN91X_DATA0); + ++Ptr; + } + + return EFI_SUCCESS; +} + +// Write bytes to the DATA register +STATIC +EFI_STATUS +WriteIoData ( + IN LAN91X_DRIVER *LanDriver, + IN VOID *Buffer, + IN UINTN BufLen + ) +{ + UINT8 *Ptr; + + Ptr = Buffer; + for (; BufLen > 0; --BufLen) { + WriteIoReg8 (LanDriver, LAN91X_DATA0, *Ptr); + ++Ptr; + } + + return EFI_SUCCESS; +} + +// Disable the interface +STATIC +EFI_STATUS +ChipDisable ( + IN LAN91X_DRIVER *LanDriver + ) +{ +#ifdef LAN91X_POWER_DOWN + UINT16 Val16; +#endif + + // Stop Rx and Tx operations + WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR); + WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_CLEAR); + +#ifdef LAN91X_POWER_DOWN + // Power-down the chip + Val16 = ReadIoReg16 (LanDriver, LAN91X_CR); + Val16 &= ~CR_EPH_POWER_EN; + WriteIoReg16 (LanDriver, LAN91X_CR, Val16); +#endif + + return EFI_SUCCESS; +} + +// Enable the interface +STATIC +EFI_STATUS +ChipEnable ( + IN LAN91X_DRIVER *LanDriver + ) +{ +#ifdef LAN91X_POWER_DOWN + UINT16 Val16; + + // Power-up the chip + Val16 = ReadIoReg16 (LanDriver, LAN91X_CR); + Val16 |= CR_EPH_POWER_EN; + WriteIoReg16 (LanDriver, LAN91X_CR, Val16); + gBS->Stall (LAN91X_STALL); +#endif + + // Start Rx and Tx operations + WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_DEFAULT); + WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_DEFAULT); + + return EFI_SUCCESS; +} + + +// Perform software reset on the LAN91x +STATIC +EFI_STATUS +SoftReset ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINT16 Val16; + + // Issue the reset + WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_SOFT_RST); + gBS->Stall (LAN91X_STALL); + WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR); + + // Set the configuration register + WriteIoReg16 (LanDriver, LAN91X_CR, CR_DEFAULT); + gBS->Stall (LAN91X_STALL); + + // Stop Rx and Tx + WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR); + WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_CLEAR); + + // Initialize the Control Register + Val16 = ReadIoReg16 (LanDriver, LAN91X_CTR); + Val16 |= CTR_AUTO_REL; + WriteIoReg16 (LanDriver, LAN91X_CTR, Val16); + + // Reset the MMU + MmuOperation (LanDriver, MMUCR_OP_RESET_MMU); + + return EFI_SUCCESS; +} + +/* +** Probe() +** +** Validate that there is a LAN91x device. +** +*/ +STATIC +EFI_STATUS +Probe ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINT16 Bank; + UINT16 Val16; + CHAR16 CONST *ChipId; + UINTN ResetTime; + + // First check that the Bank Select register is valid + Bank = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET); + if ((Bank & 0xff00) != 0x3300) { + DEBUG((EFI_D_ERROR, "LAN91x: signature error: expecting 33xx, read %04x\n", Bank)); + return EFI_DEVICE_ERROR; + } + + // Try reading the revision register next + LanDriver->BankSel = 0xff; + Val16 = ReadIoReg16 (LanDriver, LAN91X_REV); + + Bank = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET); + if ((Bank & 0xff03) != 0x3303) { + DEBUG((EFI_D_ERROR, "LAN91x: signature error: expecting 33x3, read %04x\n", Bank)); + return EFI_DEVICE_ERROR; + } + + // Validate the revision register + if ((Val16 & 0xff00) != 0x3300) { + DEBUG((EFI_D_ERROR, "LAN91x: revision error: expecting 33xx, read %04x\n", Val16)); + return EFI_DEVICE_ERROR; + } + + ChipId = ChipIds[(Val16 >> 4) & 0x0f]; + if (ChipId == NULL) { + DEBUG((EFI_D_ERROR, "LAN91x: unrecognized revision: %04x\n", Val16)); + return EFI_DEVICE_ERROR; + } + DEBUG((EFI_D_ERROR, "LAN91x: detected chip %s rev %d\n", ChipId, Val16 & 0xf)); + LanDriver->Revision = Val16 & 0xff; + + // Reload from EEPROM to get the hardware MAC address + WriteIoReg16 (LanDriver, LAN91X_CTR, CTR_RESERVED | CTR_RELOAD); + ResetTime = 1000; + while ((ReadIoReg16 (LanDriver, LAN91X_CTR) & CTR_RELOAD) != 0) { + if (--ResetTime == 0) { + DEBUG((EFI_D_ERROR, "LAN91x: reload from EEPROM timed-out\n")); + WriteIoReg16 (LanDriver, LAN91X_CTR, CTR_RESERVED); + return EFI_DEVICE_ERROR; + } + gBS->Stall (LAN91X_STALL); + } + + // Read and save the Permanent MAC Address + LanDriver->SnpMode.PermanentAddress = GetCurrentMacAddress (LanDriver); + LanDriver->SnpMode.CurrentAddress = LanDriver->SnpMode.PermanentAddress; + DEBUG((EFI_D_ERROR, //EFI_D_NET | EFI_D_INFO, + "LAN91x: HW MAC Address: %02x-%02x-%02x-%02x-%02x-%02x\n", + LanDriver->SnpMode.PermanentAddress.Addr[0], + LanDriver->SnpMode.PermanentAddress.Addr[1], + LanDriver->SnpMode.PermanentAddress.Addr[2], + LanDriver->SnpMode.PermanentAddress.Addr[3], + LanDriver->SnpMode.PermanentAddress.Addr[4], + LanDriver->SnpMode.PermanentAddress.Addr[5] + )); + + // Reset the device + SoftReset (LanDriver); + + // Try to detect a PHY + if (LanDriver->Revision > (CHIP_91100 << 4)) { + PhyDetect (LanDriver); + } else { + LanDriver->PhyAd = LAN91X_NO_PHY; + } + + return EFI_SUCCESS; +} + + + + +/*------------------ Simple Network Driver entry point functions ------------------*/ + +// Refer to the Simple Network Protocol section (21.1) +// in the UEFI 2.3.1 Specification for documentation. + +#define ReturnUnlock(s) do { Status = (s); goto exit_unlock; } while(0) + + +/* +** UEFI Start() function +** +*/ +EFI_STATUS +EFIAPI +SnpStart ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp + ) +{ + EFI_SIMPLE_NETWORK_MODE *Mode; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + Mode = Snp->Mode; + + // Check state of the driver + switch (Mode->State) { + case EfiSimpleNetworkStopped: + break; + case EfiSimpleNetworkStarted: + case EfiSimpleNetworkInitialized: + DEBUG((EFI_D_WARN, "LAN91x: Driver already started\n")); + ReturnUnlock (EFI_ALREADY_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + + // Change state + Mode->State = EfiSimpleNetworkStarted; + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Stop() function +** +*/ +EFI_STATUS +EFIAPI +SnpStop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check state of the driver + switch (Snp->Mode->State) { + case EfiSimpleNetworkStarted: + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Stop the Tx and Rx + ChipDisable (LanDriver); + + // Change the state + Snp->Mode->State = EfiSimpleNetworkStopped; + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Initialize() function +** +*/ +EFI_STATUS +EFIAPI +SnpInitialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN UINTN RxBufferSize OPTIONAL, + IN UINTN TxBufferSize OPTIONAL + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started but not initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkStarted: + break; + case EfiSimpleNetworkInitialized: + DEBUG((EFI_D_WARN, "LAN91x: Driver already initialized\n")); + ReturnUnlock (EFI_SUCCESS); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Initiate a software reset + Status = SoftReset (LanDriver); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_WARN, "LAN91x: Soft reset failed\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Initiate a PHY reset + if (PhySoftReset (LanDriver) < 0) { + Snp->Mode->State = EfiSimpleNetworkStopped; + DEBUG((EFI_D_WARN, "LAN91x: PHY soft reset timeout\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Do auto-negotiation + Status = PhyAutoNegotiate (LanDriver); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_WARN, "LAN91x: PHY auto-negotiation failed\n")); + } + + // Enable the receiver and transmitter + ChipEnable (LanDriver); + + // Now acknowledge all interrupts + WriteIoReg8 (LanDriver, LAN91X_IST, 0xFF); + + // Declare the driver as initialized + Snp->Mode->State = EfiSimpleNetworkInitialized; + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Reset () function +** +*/ +EFI_STATUS +EFIAPI +SnpReset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN BOOLEAN Verification + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started and initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Initiate a software reset + if (EFI_ERROR (SoftReset (LanDriver))) { + DEBUG((EFI_D_WARN, "LAN91x: Soft reset failed\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Initiate a PHY reset + if (EFI_ERROR (PhySoftReset (LanDriver))) { + DEBUG((EFI_D_WARN, "LAN91x: PHY soft reset failed\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Enable the receiver and transmitter + Status = ChipEnable (LanDriver); + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Shutdown () function +** +*/ +EFI_STATUS +EFIAPI +SnpShutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // First check that driver has already been initialized + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver in stopped state\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Disable the interface + Status = ChipDisable (LanDriver); + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/* +** UEFI ReceiveFilters() function +** +*/ +EFI_STATUS +EFIAPI +SnpReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN Reset, + IN UINTN NumMfilter OPTIONAL, + IN EFI_MAC_ADDRESS *Mfilter OPTIONAL + ) +{ +#define MCAST_HASH_BYTES 8 + + LAN91X_DRIVER *LanDriver; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINTN i; + UINT32 Crc; + UINT16 RcvCtrl; + UINT8 McastHash[MCAST_HASH_BYTES]; + + // Check Snp Instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // First check that driver has already been initialized + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + SnpMode = Snp->Mode; + +#ifdef LAN91X_PRINT_RECEIVE_FILTERS + DEBUG((EFI_D_ERROR, "LAN91x:SnpReceiveFilters()\n")); + DEBUG((EFI_D_ERROR, " Enable = %08x\n", Enable)); + DEBUG((EFI_D_ERROR, " Disable = %08x\n", Disable)); + DEBUG((EFI_D_ERROR, " Reset = %d\n", Reset)); + DEBUG((EFI_D_ERROR, " NumMfilter = %d\n", NumMfilter)); + for (i = 0; i < NumMfilter; ++i) { + DEBUG((EFI_D_ERROR, + " [%2d] = %02x-%02x-%02x-%02x-%02x-%02x\n", + i, + Mfilter[i].Addr[0], + Mfilter[i].Addr[1], + Mfilter[i].Addr[2], + Mfilter[i].Addr[3], + Mfilter[i].Addr[4], + Mfilter[i].Addr[5])); + } +#endif + + // Update the Multicast Hash registers + if (Reset) { + // Clear the hash table + SetMem (McastHash, MCAST_HASH_BYTES, 0); + SnpMode->MCastFilterCount = 0; + } else { + // Read the current hash table + for (i = 0; i < MCAST_HASH_BYTES; ++i) { + McastHash[i] = ReadIoReg8 (LanDriver, LAN91X_MT0 + i); + } + // Set the new additions + for (i = 0; i < NumMfilter; ++i) { + Crc = MulticastHash (&Mfilter[i], NET_ETHER_ADDR_LEN); + McastHash[(Crc >> 29) & 0x3] |= 1 << ((Crc >> 26) & 0x3); + } + SnpMode->MCastFilterCount = NumMfilter; + } + // If the hash registers need updating, write them + if (Reset || NumMfilter > 0) { + for (i = 0; i < MCAST_HASH_BYTES; ++i) { + WriteIoReg8 (LanDriver, LAN91X_MT0 + i, McastHash[i]); + } + } + + RcvCtrl = ReadIoReg16 (LanDriver, LAN91X_RCR); + if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) { + RcvCtrl |= RCR_PRMS; + SnpMode->ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } + if ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) { + RcvCtrl &= ~RCR_PRMS; + SnpMode->ReceiveFilterSetting &= ~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } + + if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) { + RcvCtrl |= RCR_ALMUL; + SnpMode->ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } + if ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) { + RcvCtrl &= ~RCR_ALMUL; + SnpMode->ReceiveFilterSetting &= ~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } + WriteIoReg16 (LanDriver, LAN91X_RCR, RcvCtrl); + + Status = SetCurrentMacAddress (LanDriver, &SnpMode->CurrentAddress); + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI StationAddress() function +** +*/ +EFI_STATUS +EFIAPI +SnpStationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS *NewMac +) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started and initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + if (Reset) { + Snp->Mode->CurrentAddress = Snp->Mode->PermanentAddress; + } else { + if (NewMac == NULL) { + ReturnUnlock (EFI_INVALID_PARAMETER); + } + Snp->Mode->CurrentAddress = *NewMac; + } + + Status = SetCurrentMacAddress (LanDriver, &Snp->Mode->CurrentAddress); + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI Statistics() function +** +*/ +EFI_STATUS +EFIAPI +SnpStatistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN BOOLEAN Reset, + IN OUT UINTN *StatSize, + OUT EFI_NETWORK_STATISTICS *Statistics + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Check pointless condition + if ((!Reset) && (StatSize == NULL) && (Statistics == NULL)) { + return EFI_SUCCESS; + } + + // Check the parameters + if ((StatSize == NULL) && (Statistics != NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started and initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Do a reset if required + if (Reset) { + ZeroMem (&LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS)); + } + + // Check buffer size + if (*StatSize < sizeof(EFI_NETWORK_STATISTICS)) { + *StatSize = sizeof(EFI_NETWORK_STATISTICS); + ReturnUnlock (EFI_BUFFER_TOO_SMALL); + goto exit_unlock; + } + + // Fill in the statistics + CopyMem(&Statistics, &LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS)); + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + +/* +** UEFI MCastIPtoMAC() function +** +*/ +EFI_STATUS +EFIAPI +SnpMcastIptoMac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp, + IN BOOLEAN IsIpv6, + IN EFI_IP_ADDRESS *Ip, + OUT EFI_MAC_ADDRESS *McastMac + ) +{ + // Check Snp instance + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Check parameters + if ((McastMac == NULL) || (Ip == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Make sure MAC address is empty + ZeroMem (McastMac, sizeof(EFI_MAC_ADDRESS)); + + // If we need ipv4 address + if (!IsIpv6) { + // Most significant 25 bits of a multicast HW address are set + McastMac->Addr[0] = 0x01; + McastMac->Addr[1] = 0x00; + McastMac->Addr[2] = 0x5E; + + // Lower 23 bits from ipv4 address + McastMac->Addr[3] = (Ip->v4.Addr[1] & 0x7F); // Clear the ms bit (25th bit of MAC must be 0) + McastMac->Addr[4] = Ip->v4.Addr[2]; + McastMac->Addr[5] = Ip->v4.Addr[3]; + } else { + // Most significant 16 bits of multicast v6 HW address are set + McastMac->Addr[0] = 0x33; + McastMac->Addr[1] = 0x33; + + // lower four octets are taken from ipv6 address + McastMac->Addr[2] = Ip->v6.Addr[8]; + McastMac->Addr[3] = Ip->v6.Addr[9]; + McastMac->Addr[4] = Ip->v6.Addr[10]; + McastMac->Addr[5] = Ip->v6.Addr[11]; + } + + return EFI_SUCCESS; +} + +/* +** UEFI NvData() function +** +*/ +EFI_STATUS +EFIAPI +SnpNvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL* pobj, + IN BOOLEAN read_write, + IN UINTN offset, + IN UINTN buff_size, + IN OUT VOID *data + ) +{ + DEBUG((EFI_D_ERROR, "LAN91x: Non-volatile storage not supported\n")); + + return EFI_UNSUPPORTED; +} + + +/* +** UEFI GetStatus () function +** +*/ +EFI_STATUS +EFIAPI +SnpGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp, + OUT UINT32 *IrqStat OPTIONAL, + OUT VOID **TxBuff OPTIONAL + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + BOOLEAN MediaPresent; + UINT8 IstReg; + + // Check preliminaries + if (Snp == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started and initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Arbitrarily set the interrupt status to 0 + if (IrqStat != NULL) { + *IrqStat = 0; + IstReg = ReadIoReg8 (LanDriver, LAN91X_IST); + if ((IstReg & IST_RCV) != 0) { + *IrqStat |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + } + if ((IstReg & IST_TX) != 0) { + *IrqStat |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + } + } + + // Pass back the completed buffer address + if (TxBuff != NULL) { + *TxBuff = TxQueRemove (LanDriver); + } + + // Update the media status + MediaPresent = CheckLinkStatus (LanDriver); + if (MediaPresent != Snp->Mode->MediaPresent) { + DEBUG((EFI_D_WARN, "LAN91x: Link %s\n", MediaPresent ? L"up" : L"down")); + } + Snp->Mode->MediaPresent = MediaPresent; + Status = EFI_SUCCESS; + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/* +** UEFI Transmit() function +** +*/ +EFI_STATUS +EFIAPI +SnpTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp, + IN UINTN HdrSize, + IN UINTN BufSize, + IN VOID *BufAddr, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DstAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ) +{ + LAN91X_DRIVER *LanDriver; + EFI_TPL SavedTpl; + EFI_STATUS Status; + UINT8 *Ptr; + UINTN Len; + UINTN MmuPages; + UINTN Retries; + UINT16 Proto; + UINT8 PktNum; + + // Check preliminaries + if ((Snp == NULL) || (BufAddr == NULL)) { + DEBUG((EFI_D_ERROR, "LAN91x: SnpTransmit(): NULL Snp (%p) or BufAddr (%p)\n", + Snp, BufAddr)); + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started and initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Ensure header is correct size if non-zero + if (HdrSize != 0) { + if (HdrSize != Snp->Mode->MediaHeaderSize) { + DEBUG((EFI_D_ERROR, "LAN91x: SnpTransmit(): Invalid HdrSize %d\n", HdrSize)); + ReturnUnlock (EFI_INVALID_PARAMETER); + } + + if ((DstAddr == NULL) || (Protocol == NULL)) { + DEBUG((EFI_D_ERROR, "LAN91x: SnpTransmit(): NULL DstAddr %p or Protocol %p\n", + DstAddr, Protocol)); + ReturnUnlock (EFI_INVALID_PARAMETER); + } + } + + // Before transmitting check the link status + if (!Snp->Mode->MediaPresent) { + DEBUG((EFI_D_WARN, "LAN91x: SnpTransmit(): Link not ready\n")); + ReturnUnlock (EFI_NOT_READY); + } + + // Calculate the request size in 256-byte "pages" minus 1 + // The 91C111 ignores this, but some older devices need it. + MmuPages = ((BufSize & ~1) + LAN91X_PKT_OVERHEAD - 1) >> 8; + if (MmuPages > 7) { + DEBUG((EFI_D_WARN, "LAN91x: Tx buffer too large (%d bytes)\n", BufSize)); + LanDriver->Stats.TxOversizeFrames += 1; + LanDriver->Stats.TxDroppedFrames += 1; + ReturnUnlock (EFI_BAD_BUFFER_SIZE); + } + + // Request allocation of a transmit buffer + Status = MmuOperation (LanDriver, MMUCR_OP_TX_ALLOC | MmuPages); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "LAN91x: Tx buffer request failure: %d\n", Status)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Wait for allocation request completion + Retries = LAN91X_MEMORY_ALLOC_POLLS; + while ((ReadIoReg8 (LanDriver, LAN91X_IST) & IST_ALLOC) == 0) { + if (--Retries == 0) { + DEBUG((EFI_D_ERROR, "LAN91x: Tx buffer allocation timeout\n")); + ReturnUnlock (EFI_TIMEOUT); + } + } + + // Check for successful allocation + PktNum = ReadIoReg8 (LanDriver, LAN91X_ARR); + if ((PktNum & ARR_FAILED) != 0) { + DEBUG((EFI_D_ERROR, "LAN91x: Tx buffer allocation failure: %02x\n", PktNum)); + ReturnUnlock (EFI_NOT_READY); + } + PktNum &= ARR_PACKET; + + // Check for the nature of the frame + if (DstAddr->Addr[0] == 0xFF) { + LanDriver->Stats.TxBroadcastFrames += 1; + } else if ((DstAddr->Addr[0] & 0x1) == 1) { + LanDriver->Stats.TxMulticastFrames += 1; + } else { + LanDriver->Stats.TxUnicastFrames += 1; + } + + // Set the Packet Number and Pointer registers + WriteIoReg8 (LanDriver, LAN91X_PNR, PktNum); + WriteIoReg16 (LanDriver, LAN91X_PTR, PTR_AUTO_INCR); + + // Set up mutable buffer information variables + Ptr = BufAddr; + Len = BufSize; + + // Write Status and Byte Count first + WriteIoReg16 (LanDriver, LAN91X_DATA0, 0); + WriteIoReg16 (LanDriver, LAN91X_DATA0, (Len + LAN91X_PKT_OVERHEAD) & BCW_COUNT); + + // This packet may come with a preconfigured Ethernet header. + // If not, we need to construct one from optional parameters. + if (HdrSize) { + + // Write the destination address + WriteIoData (LanDriver, DstAddr, NET_ETHER_ADDR_LEN); + + // Write the Source Address + if (SrcAddr != NULL) { + WriteIoData (LanDriver, SrcAddr, NET_ETHER_ADDR_LEN); + } else { + WriteIoData (LanDriver, &LanDriver->SnpMode.CurrentAddress, NET_ETHER_ADDR_LEN); + } + + // Write the Protocol word + Proto = HTONS (*Protocol); + WriteIoReg16 (LanDriver, LAN91X_DATA0, Proto); + + // Adjust the data start and length + Ptr += sizeof(ETHER_HEAD); + Len -= sizeof(ETHER_HEAD); + } + + // Copy the remainder data buffer, except the odd byte + WriteIoData (LanDriver, Ptr, Len & ~1); + Ptr += Len & ~1; + Len &= 1; + + // Write the Packet Control Word and odd byte + WriteIoReg16 (LanDriver, LAN91X_DATA0, + (Len != 0) ? (PCW_ODD | PCW_CRC | *Ptr) : PCW_CRC); + + // Release the packet for transmission + Status = MmuOperation (LanDriver, MMUCR_OP_TX_PUSH); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "LAN91x: Tx buffer release failure: %d\n", Status)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Update the Rx statistics + LanDriver->Stats.TxTotalBytes += BufSize; + LanDriver->Stats.TxGoodFrames += 1; + + // Update the Tx Buffer cache + if (!TxQueInsert (LanDriver, BufAddr)) { + DEBUG((EFI_D_WARN, "LAN91x: SnpTransmit(): TxQueue insert failure.\n")); + } + Status = EFI_SUCCESS; + + // Dump the packet header +#if LAN91X_PRINT_PACKET_HEADERS + Ptr = BufAddr; + DEBUG((EFI_D_ERROR, "LAN91X:SnpTransmit()\n")); + DEBUG((EFI_D_ERROR, " HdrSize: %d, SrcAddr: %p, Length: %d, Last byte: %02x\n", + HdrSize, SrcAddr, BufSize, Ptr[BufSize - 1])); + PrintIpDgram ( + (HdrSize == 0) ? (EFI_MAC_ADDRESS *)&Ptr[0] : DstAddr, + (HdrSize == 0) ? (EFI_MAC_ADDRESS *)&Ptr[6] : (SrcAddr != NULL) ? SrcAddr : &LanDriver->SnpMode.CurrentAddress, + (HdrSize == 0) ? (UINT16 *)&Ptr[12] : &Proto, + &Ptr[14] + ); +#endif + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/* +** UEFI Receive() function +** +*/ +EFI_STATUS +EFIAPI +SnpReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp, + OUT UINTN *HdrSize OPTIONAL, + IN OUT UINTN *BuffSize, + OUT VOID *Data, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DstAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ) +{ + EFI_TPL SavedTpl; + EFI_STATUS Status; + LAN91X_DRIVER *LanDriver; + UINT8 *DataPtr; + UINT16 PktStatus; + UINT16 PktLength; + UINT16 PktControl; + UINT8 IstReg; + + // Check preliminaries + if ((Snp == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Serialize access to data and registers + SavedTpl = gBS->RaiseTPL (LAN91X_TPL); + + // Check that driver was started and initialised + switch (Snp->Mode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStarted: + DEBUG((EFI_D_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG((EFI_D_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG((EFI_D_ERROR, "LAN91x: Driver in an invalid state: %u\n", + (UINTN)Snp->Mode->State)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Find the LanDriver structure + LanDriver = INSTANCE_FROM_SNP_THIS(Snp); + + // Check for Rx Overrun + IstReg = ReadIoReg8 (LanDriver, LAN91X_IST); + if ((IstReg & IST_RX_OVRN) != 0) { + LanDriver->Stats.RxTotalFrames += 1; + LanDriver->Stats.RxDroppedFrames += 1; + WriteIoReg8 (LanDriver, LAN91X_IST, IST_RX_OVRN); + DEBUG((EFI_D_WARN, "LAN91x: Receiver overrun\n")); + } + + // Check for Rx data available + if ((IstReg & IST_RCV) == 0) { + ReturnUnlock (EFI_NOT_READY); + } + + // Configure the PTR register for reading + WriteIoReg16 (LanDriver, LAN91X_PTR, PTR_RCV | PTR_AUTO_INCR | PTR_READ); + + // Read the Packet Status and Packet Length words + PktStatus = ReadIoReg16 (LanDriver, LAN91X_DATA0); + PktLength = ReadIoReg16 (LanDriver, LAN91X_DATA0) & BCW_COUNT; + + // Check for valid received packet + if ((PktStatus == 0) && (PktLength == 0)) { + DEBUG((EFI_D_WARN, "LAN91x: Received zero-length packet. IST=%04x\n", IstReg)); + ReturnUnlock (EFI_NOT_READY); + } + LanDriver->Stats.RxTotalFrames += 1; + + // Check if we got a CRC error + if ((PktStatus & RX_BAD_CRC) != 0) { + DEBUG((EFI_D_WARN, "LAN91x: Received frame CRC error\n")); + LanDriver->Stats.RxCrcErrorFrames += 1; + LanDriver->Stats.RxDroppedFrames += 1; + Status = EFI_DEVICE_ERROR; + goto exit_release; + } + + // Check if we got a too-short frame + if ((PktStatus & RX_TOO_SHORT) != 0) { + DEBUG((EFI_D_WARN, "LAN91x: Received frame too short (%d bytes)\n", PktLength)); + LanDriver->Stats.RxUndersizeFrames += 1; + LanDriver->Stats.RxDroppedFrames += 1; + Status = EFI_DEVICE_ERROR; + goto exit_release; + } + + // Check if we got a too-long frame + if ((PktStatus & RX_TOO_LONG) != 0) { + DEBUG((EFI_D_WARN, "LAN91x: Received frame too long (%d bytes)\n", PktLength)); + LanDriver->Stats.RxOversizeFrames += 1; + LanDriver->Stats.RxDroppedFrames += 1; + Status = EFI_DEVICE_ERROR; + goto exit_release; + } + + // Check if we got an alignment error + if ((PktStatus & RX_ALGN_ERR) != 0) { + DEBUG((EFI_D_WARN, "LAN91x: Received frame alignment error\n")); + // Don't seem to keep track of these specifically + LanDriver->Stats.RxDroppedFrames += 1; + Status = EFI_DEVICE_ERROR; + goto exit_release; + } + + // Classify the received fram + if ((PktStatus & RX_MULTICAST) != 0) { + LanDriver->Stats.RxMulticastFrames += 1; + } else if ((PktStatus & RX_BROADCAST) != 0) { + LanDriver->Stats.RxBroadcastFrames += 1; + } else { + LanDriver->Stats.RxUnicastFrames += 1; + } + + // Calculate the received packet data length + PktLength -= LAN91X_PKT_OVERHEAD; + if ((PktStatus & RX_ODD_FRAME) != 0) { + PktLength += 1; + } + + // Check buffer size + if (*BuffSize < PktLength) { + DEBUG((EFI_D_WARN, "LAN91x: Receive buffer too small for packet (%d < %d)\n", + *BuffSize, PktLength)); + *BuffSize = PktLength; + Status = EFI_BUFFER_TOO_SMALL; + goto exit_release; + } + + // Transfer the data bytes + DataPtr = Data; + ReadIoData (LanDriver, DataPtr, PktLength & ~0x0001); + + // Read the PktControl and Odd Byte from the FIFO + PktControl = ReadIoReg16 (LanDriver, LAN91X_DATA0); + if ((PktControl & PCW_ODD) != 0) { + DataPtr[PktLength - 1] = PktControl & PCW_ODD_BYTE; + } + + // Update buffer size + *BuffSize = PktLength; + + if (HdrSize != NULL) { + *HdrSize = LanDriver->SnpMode.MediaHeaderSize; + } + + // Extract the destination address + if (DstAddr != NULL) { + CopyMem (DstAddr, &DataPtr[0], NET_ETHER_ADDR_LEN); + } + + // Get the source address + if (SrcAddr != NULL) { + CopyMem (SrcAddr, &DataPtr[6], NET_ETHER_ADDR_LEN); + } + + // Get the protocol + if (Protocol != NULL) { + *Protocol = NTOHS (*(UINT16*)(&DataPtr[12])); + } + + // Update the Rx statistics + LanDriver->Stats.RxTotalBytes += PktLength; + LanDriver->Stats.RxGoodFrames += 1; + Status = EFI_SUCCESS; + +#if LAN91X_PRINT_PACKET_HEADERS + // Dump the packet header + DEBUG((EFI_D_ERROR, "LAN91X:SnpReceive()\n")); + DEBUG((EFI_D_ERROR, " HdrSize: %p, SrcAddr: %p, DstAddr: %p, Protocol: %p\n", + HdrSize, SrcAddr, DstAddr, Protocol)); + DEBUG((EFI_D_ERROR, " Length: %d, Last byte: %02x\n", PktLength, DataPtr[PktLength - 1])); + PrintIpDgram (&DataPtr[0], &DataPtr[6], &DataPtr[12], &DataPtr[14]); +#endif + + // Release the FIFO buffer +exit_release: + MmuOperation (LanDriver, MMUCR_OP_RX_POP_REL); + + // Restore TPL and return +exit_unlock: + gBS->RestoreTPL (SavedTpl); + return Status; +} + + +/*------------------ Driver Execution Environment main entry point ------------------*/ + +/* +** Entry point for the LAN91x driver +** +*/ +EFI_STATUS +Lan91xDxeEntry ( + IN EFI_HANDLE Handle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + LAN91X_DRIVER *LanDriver; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + LAN91X_DEVICE_PATH *Lan91xPath; + + // The PcdLan91xDxeBaseAddress PCD must be defined + ASSERT(PcdGet32 (PcdLan91xDxeBaseAddress) != 0); + + // Allocate Resources + LanDriver = AllocateZeroPool (sizeof(LAN91X_DRIVER)); + Lan91xPath = AllocateCopyPool (sizeof(LAN91X_DEVICE_PATH), &Lan91xPathTemplate); + + // Initialize I/O Space access info + LanDriver->IoBase = PcdGet32 (PcdLan91xDxeBaseAddress); + LanDriver->PhyAd = LAN91X_NO_PHY; + LanDriver->BankSel = 0xff; + + // Initialize pointers + Snp = &(LanDriver->Snp); + SnpMode = &(LanDriver->SnpMode); + Snp->Mode = SnpMode; + + // Set the signature of the LAN Driver structure + LanDriver->Signature = LAN91X_SIGNATURE; + + // Probe the device + Status = Probe (LanDriver); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "LAN91x:Lan91xDxeEntry(): Probe failed with status %d\n", Status)); + return Status; + } + +#ifdef LAN91X_PRINT_REGISTERS + PrintIoRegisters (LanDriver); + PrintPhyRegisters (LanDriver); +#endif + + // Assign fields and func pointers + Snp->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + Snp->WaitForPacket = NULL; + Snp->Initialize = SnpInitialize; + Snp->Start = SnpStart; + Snp->Stop = SnpStop; + Snp->Reset = SnpReset; + Snp->Shutdown = SnpShutdown; + Snp->ReceiveFilters = SnpReceiveFilters; + Snp->StationAddress = SnpStationAddress; + Snp->Statistics = SnpStatistics; + Snp->MCastIpToMac = SnpMcastIptoMac; + Snp->NvData = SnpNvData; + Snp->GetStatus = SnpGetStatus; + Snp->Transmit = SnpTransmit; + Snp->Receive = SnpReceive; + + // Fill in simple network mode structure + SnpMode->State = EfiSimpleNetworkStopped; + SnpMode->HwAddressSize = NET_ETHER_ADDR_LEN; // HW address is 6 bytes + SnpMode->MediaHeaderSize = sizeof(ETHER_HEAD); // Size of an Ethernet header + SnpMode->MaxPacketSize = EFI_PAGE_SIZE; // Ethernet Frame (with VLAN tag +4 bytes) + + // Supported receive filters + SnpMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + + // Initially-enabled receive filters + SnpMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + + // LAN91x has 64bit hash table. We can filter an infinite MACs, but + // higher-level software must filter out any hash collisions. + SnpMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT; + SnpMode->MCastFilterCount = 0; + ZeroMem (&SnpMode->MCastFilter, MAX_MCAST_FILTER_CNT * sizeof(EFI_MAC_ADDRESS)); + + // Set the interface type (1: Ethernet or 6: IEEE 802 Networks) + SnpMode->IfType = NET_IFTYPE_ETHERNET; + + // Mac address is changeable + SnpMode->MacAddressChangeable = TRUE; + + // We can only transmit one packet at a time + SnpMode->MultipleTxSupported = FALSE; + + // MediaPresent checks for cable connection and partner link + SnpMode->MediaPresentSupported = TRUE; + SnpMode->MediaPresent = FALSE; + + // Set broadcast address + SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF); + + // Assign fields for device path + Lan91xPath->Lan91x.MacAddress = SnpMode->PermanentAddress; + Lan91xPath->Lan91x.IfType = SnpMode->IfType; + + // Initialise the protocol + Status = gBS->InstallMultipleProtocolInterfaces ( + &LanDriver->ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, Snp, + &gEfiDevicePathProtocolGuid, Lan91xPath, + NULL + ); + + // Say what the status of loading the protocol structure is + if (EFI_ERROR(Status)) { + FreePool (LanDriver); + } + + return Status; +} diff --git a/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.inf b/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.inf new file mode 100644 index 000000000..c7ef43ba2 --- /dev/null +++ b/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxe.inf @@ -0,0 +1,58 @@ +#/** @file +# INF file for the SMSC LAN91x series Network Controller Driver. +# +# Copyright (c) 2013 Linaro.org +# +# Derived from the LAN9118 driver. Original sources +# Copyright (c) 2012-2013, 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. +# +#**/ + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = LAN91xDxe + FILE_GUID = 5c12ea2f-9897-48af-8138-25f4ce6ff8d6 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 0.1 + ENTRY_POINT = Lan91xDxeEntry + +[Sources.common] + LAN91xDxe.c + LAN91xDxeHw.h + +[Packages] + ArmPlatformPkg/ArmPlatformPkg.dec + NetworkPkg/NetworkPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + UefiLib + NetLib + UefiDriverEntryPoint + BaseMemoryLib + ArmLib + IoLib + TimerLib + DevicePathLib + +[Protocols] + gEfiSimpleNetworkProtocolGuid + gEfiMetronomeArchProtocolGuid + gEfiPxeBaseCodeProtocolGuid + gEfiDevicePathProtocolGuid + +[FixedPcd] + gArmPlatformTokenSpaceGuid.PcdLan91xDxeBaseAddress + +[Depex] + TRUE diff --git a/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxeHw.h b/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxeHw.h new file mode 100644 index 000000000..9274ba072 --- /dev/null +++ b/ArmPlatformPkg/Drivers/LAN91xDxe/LAN91xDxeHw.h @@ -0,0 +1,278 @@ +/** @file +* SMSC LAN91x series Network Controller Driver. +* +* Copyright (c) 2013 Linaro.org +* +* 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. +* +**/ + +#ifndef __LAN91XDXEHW_H__ +#define __LAN91XDXEHW_H__ + +#include <Base.h> + +#define MakeRegister(Bank, Offset) (((Bank) << 8) | (Offset)) +#define RegisterToBank(Register) (((Register) >> 8) & 0x07) +#define RegisterToOffset(Register) ((Register) & 0x0f) + +/*--------------------------------------------------------------------------------------------------------------------- + + SMSC LAN91x Registers + +---------------------------------------------------------------------------------------------------------------------*/ +#define LAN91X_BANK_OFFSET 0xe // Bank Select Register (all banks) + +#define LAN91X_TCR MakeRegister (0, 0x0) // Transmit Control Register +#define LAN91X_EPHSR MakeRegister (0, 0x2) // EPH Status Register +#define LAN91X_RCR MakeRegister (0, 0x4) // Receive Control Register +#define LAN91X_ECR MakeRegister (0, 0x6) // Counter Register +#define LAN91X_MIR MakeRegister (0, 0x8) // Memory Information Register +#define LAN91X_RPCR MakeRegister (0, 0xa) // Receive/Phy Control Register + +#define LAN91X_CR MakeRegister (1, 0x0) // Configuration Register +#define LAN91X_BAR MakeRegister (1, 0x2) // Base Address Register +#define LAN91X_IAR0 MakeRegister (1, 0x4) // Individual Address Register 0 +#define LAN91X_IAR1 MakeRegister (1, 0x5) // Individual Address Register 1 +#define LAN91X_IAR2 MakeRegister (1, 0x6) // Individual Address Register 2 +#define LAN91X_IAR3 MakeRegister (1, 0x7) // Individual Address Register 3 +#define LAN91X_IAR4 MakeRegister (1, 0x8) // Individual Address Register 4 +#define LAN91X_IAR5 MakeRegister (1, 0x9) // Individual Address Register 5 +#define LAN91X_GPR MakeRegister (1, 0xa) // General Purpose Register +#define LAN91X_CTR MakeRegister (1, 0xc) // Control Register + +#define LAN91X_MMUCR MakeRegister (2, 0x0) // MMU Command Register +#define LAN91X_PNR MakeRegister (2, 0x2) // Packet Number Register +#define LAN91X_ARR MakeRegister (2, 0x3) // Allocation Result Register +#define LAN91X_FIFO MakeRegister (2, 0x4) // FIFO Ports Register +#define LAN91X_PTR MakeRegister (2, 0x6) // Pointer Register +#define LAN91X_DATA0 MakeRegister (2, 0x8) // Data Register 0 +#define LAN91X_DATA1 MakeRegister (2, 0x9) // Data Register 1 +#define LAN91X_DATA2 MakeRegister (2, 0xa) // Data Register 2 +#define LAN91X_DATA3 MakeRegister (2, 0xb) // Data Register 3 +#define LAN91X_IST MakeRegister (2, 0xc) // Interrupt Status Register +#define LAN91X_MSK MakeRegister (2, 0xd) // Interrupt Mask Register + +#define LAN91X_MT0 MakeRegister (3, 0x0) // Multicast Table Register 0 +#define LAN91X_MT1 MakeRegister (3, 0x1) // Multicast Table Register 1 +#define LAN91X_MT2 MakeRegister (3, 0x2) // Multicast Table Register 2 +#define LAN91X_MT3 MakeRegister (3, 0x3) // Multicast Table Register 3 +#define LAN91X_MT4 MakeRegister (3, 0x4) // Multicast Table Register 4 +#define LAN91X_MT5 MakeRegister (3, 0x5) // Multicast Table Register 5 +#define LAN91X_MT6 MakeRegister (3, 0x6) // Multicast Table Register 6 +#define LAN91X_MT7 MakeRegister (3, 0x7) // Multicast Table Register 7 +#define LAN91X_MGMT MakeRegister (3, 0x8) // Management Interface Register +#define LAN91X_REV MakeRegister (3, 0xa) // Revision Register +#define LAN91X_RCV MakeRegister (3, 0xc) // RCV Register + +// Transmit Control Register Bits +#define TCR_TXENA BIT0 +#define TCR_LOOP BIT1 +#define TCR_FORCOL BIT2 +#define TCR_PAD_EN BIT7 +#define TCR_NOCRC BIT8 +#define TCR_MON_CSN BIT10 +#define TCR_FDUPLX BIT11 +#define TCR_STP_SQET BIT12 +#define TCR_EPH_LOOP BIT13 +#define TCR_SWFDUP BIT15 + +#define TCR_DEFAULT (TCR_TXENA | TCR_PAD_EN) +#define TCR_CLEAR 0x0 + +// EPH Status Register Bits +#define EPHSR_TX_SUC BIT0 +#define EPHSR_SNGLCOL BIT1 +#define EPHSR_MULCOL BIT2 +#define EPHSR_LTX_MULT BIT3 +#define EPHSR_16COL BIT4 +#define EPHSR_SQET BIT5 +#define EPHSR_LTX_BRD BIT6 +#define EPHSR_TX_DEFR BIT7 +#define EPHSR_LATCOL BIT9 +#define EPHSR_LOST_CARR BIT10 +#define EPHSR_EXC_DEF BIT11 +#define EPHSR_CTR_ROL BIT12 +#define EPHSR_LINK_OK BIT14 + +// Receive Control Register Bits +#define RCR_RX_ABORT BIT0 +#define RCR_PRMS BIT1 +#define RCR_ALMUL BIT2 +#define RCR_RXEN BIT8 +#define RCR_STRIP_CRC BIT9 +#define RCR_ABORT_ENB BIT13 +#define RCR_FILT_CAR BIT14 +#define RCR_SOFT_RST BIT15 + +#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) +#define RCR_CLEAR 0x0 + +// Receive/Phy Control Register Bits +#define RPCR_LS0B BIT2 +#define RPCR_LS1B BIT3 +#define RPCR_LS2B BIT4 +#define RPCR_LS0A BIT5 +#define RPCR_LS1A BIT6 +#define RPCR_LS2A BIT7 +#define RPCR_ANEG BIT11 +#define RPCR_DPLX BIT12 +#define RPCR_SPEED BIT13 + +// Configuration Register Bits +#define CR_EXT_PHY BIT9 +#define CR_GPCNTRL BIT10 +#define CR_NO_WAIT BIT12 +#define CR_EPH_POWER_EN BIT15 + +#define CR_DEFAULT (CR_EPH_POWER_EN | CR_NO_WAIT) + +// Control Register Bits +#define CTR_STORE BIT0 +#define CTR_RELOAD BIT1 +#define CTR_EEPROM_SEL BIT2 +#define CTR_TE_ENABLE BIT5 +#define CTR_CR_ENABLE BIT6 +#define CTR_LE_ENABLE BIT7 +#define CTR_AUTO_REL BIT11 +#define CTR_RCV_BAD BIT14 + +#define CTR_RESERVED (BIT12 | BIT9 | BIT4) +#define CTR_DEFAULT (CTR_RESERVED | CTR_AUTO_REL) + +// MMU Command Register Bits +#define MMUCR_BUSY BIT0 + +// MMU Command Register Operaction Codes +#define MMUCR_OP_NOOP (0 << 5) // No operation +#define MMUCR_OP_TX_ALLOC (1 << 5) // Allocate memory for TX +#define MMUCR_OP_RESET_MMU (2 << 5) // Reset MMU to initial state +#define MMUCR_OP_RX_POP (3 << 5) // Remove frame from top of RX FIFO +#define MMUCR_OP_RX_POP_REL (4 << 5) // Remove and release frame from top of RX FIFO +#define MMUCR_OP_RX_REL (5 << 5) // Release specific RX frame +#define MMUCR_OP_TX_PUSH (6 << 5) // Enqueue packet number into TX FIFO +#define MMUCR_OP_TX_RESET (7 << 5) // Reset TX FIFOs + +// Packet Number Register Bits +#define PNR_PACKET (0x3f) + +// Allocation Result Register Bits +#define ARR_PACKET (0x3f) +#define ARR_FAILED BIT7 + +// FIFO Ports Register Bits +#define FIFO_TX_PACKET (0x003f) +#define FIFO_TEMPTY BIT7 +#define FIFO_RX_PACKET (0x3f00) +#define FIFO_REMPTY BIT15 + +// Pointer Register Bits +#define PTR_POINTER (0x07ff) +#define PTR_NOT_EMPTY BIT11 +#define PTR_READ BIT13 +#define PTR_AUTO_INCR BIT14 +#define PTR_RCV BIT15 + +// Interupt Status and Mask Register Bits +#define IST_RCV BIT0 +#define IST_TX BIT1 +#define IST_TX_EMPTY BIT2 +#define IST_ALLOC BIT3 +#define IST_RX_OVRN BIT4 +#define IST_EPH BIT5 +#define IST_MD BIT7 + +// Management Interface +#define MGMT_MDO BIT0 +#define MGMT_MDI BIT1 +#define MGMT_MCLK BIT2 +#define MGMT_MDOE BIT3 +#define MGMT_MSK_CRS100 BIT14 + +// RCV Register +#define RCV_MBO (0x1f) +#define RCV_RCV_DISCRD BIT7 + +// Packet RX Status word bits +#define RX_MULTICAST BIT0 +#define RX_HASH (0x7e) +#define RX_TOO_SHORT BIT10 +#define RX_TOO_LONG BIT11 +#define RX_ODD_FRAME BIT12 +#define RX_BAD_CRC BIT13 +#define RX_BROADCAST BIT14 +#define RX_ALGN_ERR BIT15 + +// Packet Byte Count word bits +#define BCW_COUNT (0x7fe) + +// Packet Control Word bits +#define PCW_ODD_BYTE (0x00ff) +#define PCW_CRC BIT12 +#define PCW_ODD BIT13 + +/*--------------------------------------------------------------------------------------------------------------------- + + SMSC PHY Registers + + Most of these should be common, as there is + documented STANDARD for PHY registers! + +---------------------------------------------------------------------------------------------------------------------*/ +// +// PHY Register Numbers +// +#define PHY_INDEX_BASIC_CTRL 0 +#define PHY_INDEX_BASIC_STATUS 1 +#define PHY_INDEX_ID1 2 +#define PHY_INDEX_ID2 3 +#define PHY_INDEX_AUTO_NEG_ADVERT 4 +#define PHY_INDEX_AUTO_NEG_LINK_ABILITY 5 + +#define PHY_INDEX_CONFIG1 16 +#define PHY_INDEX_CONFIG2 17 +#define PHY_INDEX_STATUS_OUTPUT 18 +#define PHY_INDEX_MASK 19 + + +// PHY control register bits +#define PHYCR_COLL_TEST BIT7 // Collision test enable +#define PHYCR_DUPLEX_MODE BIT8 // Set Duplex Mode +#define PHYCR_RST_AUTO BIT9 // Restart Auto-Negotiation of Link abilities +#define PHYCR_PD BIT11 // Power-Down switch +#define PHYCR_AUTO_EN BIT12 // Auto-Negotiation Enable +#define PHYCR_SPEED_SEL BIT13 // Link Speed Selection +#define PHYCR_LOOPBK BIT14 // Set loopback mode +#define PHYCR_RESET BIT15 // Do a PHY reset + +// PHY status register bits +#define PHYSTS_EXT_CAP BIT0 // Extended Capabilities Register capability +#define PHYSTS_JABBER BIT1 // Jabber condition detected +#define PHYSTS_LINK_STS BIT2 // Link Status +#define PHYSTS_AUTO_CAP BIT3 // Auto-Negotiation Capability +#define PHYSTS_REMOTE_FAULT BIT4 // Remote fault detected +#define PHYSTS_AUTO_COMP BIT5 // Auto-Negotiation Completed +#define PHYSTS_10BASET_HDPLX BIT11 // 10Mbps Half-Duplex ability +#define PHYSTS_10BASET_FDPLX BIT12 // 10Mbps Full-Duplex ability +#define PHYSTS_100BASETX_HDPLX BIT13 // 100Mbps Half-Duplex ability +#define PHYSTS_100BASETX_FDPLX BIT14 // 100Mbps Full-Duplex ability +#define PHYSTS_100BASE_T4 BIT15 // Base T4 ability + +// PHY Auto-Negotiation advertisement +#define PHYANA_SEL_MASK ((UINT32)0x1F) // Link type selector +#define PHYANA_CSMA BIT0 // Advertise CSMA capability +#define PHYANA_10BASET BIT5 // Advertise 10BASET capability +#define PHYANA_10BASETFD BIT6 // Advertise 10BASET Full duplex capability +#define PHYANA_100BASETX BIT7 // Advertise 100BASETX capability +#define PHYANA_100BASETXFD BIT8 // Advertise 100 BASETX Full duplex capability +#define PHYANA_100BASET4 BIT9 // Advertise 100 BASETX Full duplex capability +#define PHYANA_PAUSE_OP_MASK (3 << 10) // Advertise PAUSE frame capability +#define PHYANA_REMOTE_FAULT BIT13 // Remote fault detected + +#endif /* __LAN91XDXEHW_H__ */ diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c index 81ddd6263..5cf717f94 100644..100755 --- a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c @@ -103,22 +103,6 @@ ArpCreateService ( }
//
- // Get the underlayer Snp mode data.
- //
- Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode);
- if ((Status != EFI_NOT_STARTED) && EFI_ERROR (Status)) {
- goto ERROR_EXIT;
- }
-
- if (ArpService->SnpMode.IfType != NET_IFTYPE_ETHERNET) {
- //
- // Only support the ethernet.
- //
- Status = EFI_UNSUPPORTED;
- goto ERROR_EXIT;
- }
-
- //
// Set the Mnp config parameters.
//
ArpService->MnpConfigData.ReceivedQueueTimeoutValue = 0;
@@ -141,6 +125,23 @@ ArpCreateService ( }
//
+ // Get the underlayer Snp mode data. Must do this after MNP configuration else some parameters + // (e.g. current address) may not be set + // + Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode); + if ((Status != EFI_NOT_STARTED) && EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + if (ArpService->SnpMode.IfType != NET_IFTYPE_ETHERNET) { + // + // Only support the ethernet. + // + Status = EFI_UNSUPPORTED; + goto ERROR_EXIT; + } + + // // Create the event used in the RxToken.
//
Status = gBS->CreateEvent (
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c index 4c0f3ddd9..e163a7890 100644 --- a/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c @@ -79,8 +79,10 @@ MnpGetModeData ( // Upon successful return of GetStatus(), the Snp->Mode->MediaPresent
// will be updated to reflect any change of media status
//
- Snp->GetStatus (Snp, &InterruptStatus, NULL);
- CopyMem (SnpModeData, Snp->Mode, sizeof (*SnpModeData));
+ Status = Snp->GetStatus (Snp, &InterruptStatus, NULL); + if (!EFI_ERROR (Status) || (Status == EFI_NOT_STARTED)) { + CopyMem (SnpModeData, Snp->Mode, sizeof (*SnpModeData)); + } }
if (!Instance->Configured) {
|