From: Leif Lindholm Date: Wed, 19 Apr 2017 11:01:55 +0000 (+0100) Subject: EmbeddedPkg: import Lan91x Ethernet controller driver X-Git-Tag: edk2-stable201903~4144 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=4395f82e7f81f5215e52f3dba523ca1ccf280823 EmbeddedPkg: import Lan91x Ethernet controller driver OpenPlatformPkg (https://git.linaro.org/uefi/OpenPlatformPkg.git) holds a driver for the SMSC LAN91x, used (among other places) in several ARM Ltd. software system models. Import it to EDK2 EmbeddedPkg in preparation for migrating those model platforms to edk2-platforms. On the way, update the files to pass PatchCheck.py without warnings (EFI_D_ -> DEBUG_ and purging tab characters). Also update .inf file to current version (and sort entries within sections). And update copyright dates to reflect this. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Leif Lindholm Reviewed-by: Ard Biesheuvel --- diff --git a/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c new file mode 100644 index 0000000000..7dbbbae55d --- /dev/null +++ b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c @@ -0,0 +1,2235 @@ +/** @file +* SMSC LAN91x series Network Controller Driver. +* +* Copyright (c) 2013-2017 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 +#include +#include + +// Protocols used by this driver +#include +#include +#include +#include + +// Libraries used by this driver +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 + + LIST_ENTRY TransmitQueueHead; + + // 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 structures ------------------- */ + +typedef struct { + VOID *Buf; + UINTN Length; +} MSK_SYSTEM_BUF; + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + MSK_SYSTEM_BUF SystemBuf; +} MSK_LINKED_SYSTEM_BUF; + +#define TX_MBUF_SIGNATURE SIGNATURE_32 ('t','x','m','b') + +/* ------------------ 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 ((DEBUG_ERROR, "\nLAN91x I/O Register Dump:\n")); + + // Print currrent bank select register + Value = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET); + DEBUG ((DEBUG_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 ((DEBUG_ERROR, " %02x:", Offset)); + for (Bank = 0; Bank <= 3; ++Bank) { + DEBUG ((DEBUG_ERROR, " %04x", ReadIoReg16 (LanDriver, MakeRegister (Bank, Offset)))); + } + DEBUG ((DEBUG_ERROR, "\n")); + } +} + +STATIC +VOID +PrintPhyRegisters ( + IN LAN91X_DRIVER *LanDriver + ) +{ + UINTN RegNum; + + DEBUG ((DEBUG_ERROR, "\nLAN91x Phy %d Register Dump:\n", LanDriver->PhyAd)); + + // Print all Phy registers + for (RegNum = 0; RegNum <= 5; ++RegNum) { + DEBUG ((DEBUG_ERROR, " %2d: %04x\n", + RegNum, + ReadPhyReg16 (LanDriver, RegNum) + )); + } + for (RegNum = 16; RegNum <= 20; ++RegNum) { + DEBUG ((DEBUG_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 ((DEBUG_ERROR, " Dst: %02x-%02x-%02x", + Ptr[0], Ptr[1], Ptr[2])); + DEBUG ((DEBUG_ERROR, "-%02x-%02x-%02x", + Ptr[3], Ptr[4], Ptr[5])); + + Ptr = SrcMac; + DEBUG ((DEBUG_ERROR, " Src: %02x-%02x-%02x", + Ptr[0], Ptr[1], Ptr[2])); + DEBUG ((DEBUG_ERROR, "-%02x-%02x-%02x", + Ptr[3], Ptr[4], Ptr[5])); + + Ptr = Proto; + DEBUG ((DEBUG_ERROR, " Proto: %02x%02x\n", + Ptr[0], Ptr[1])); + + Ptr = IpDgram; + switch (Ptr[9]) { + case EFI_IP_PROTO_ICMP: + DEBUG ((DEBUG_ERROR, " ICMP")); + break; + case EFI_IP_PROTO_TCP: + DEBUG ((DEBUG_ERROR, " TCP")); + break; + case EFI_IP_PROTO_UDP: + DEBUG ((DEBUG_ERROR, " UDP")); + break; + default: + DEBUG ((DEBUG_ERROR, " IpProto %d\n", Ptr[9])); + return; + } + + DEBUG ((DEBUG_ERROR, " SrcIp: %d.%d.%d.%d", + Ptr[12], Ptr[13], Ptr[14], Ptr[15])); + DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "LAN91x: PHY type LAN83C183 (LAN91C111 Internal)\n")); + } else if ((PhyId1 == 0x0282) && ((PhyId2 & 0xfff0) == 0x1c50)) { + DEBUG ((DEBUG_ERROR, "LAN91x: PHY type LAN83C180\n")); + } else { + DEBUG ((DEBUG_ERROR, "LAN91x: PHY id %04x:%04x\n", PhyId1, PhyId2)); + } + return EFI_SUCCESS; + } + } + + DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "LAN91x: signature error: expecting 33x3, read %04x\n", Bank)); + return EFI_DEVICE_ERROR; + } + + // Validate the revision register + if ((Val16 & 0xff00) != 0x3300) { + DEBUG ((DEBUG_ERROR, "LAN91x: revision error: expecting 33xx, read %04x\n", Val16)); + return EFI_DEVICE_ERROR; + } + + ChipId = ChipIds[(Val16 >> 4) & 0x0f]; + if (ChipId == NULL) { + DEBUG ((DEBUG_ERROR, "LAN91x: unrecognized revision: %04x\n", Val16)); + return EFI_DEVICE_ERROR; + } + DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, //DEBUG_NET | DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver already started\n")); + ReturnUnlock (EFI_ALREADY_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver already initialized\n")); + ReturnUnlock (EFI_SUCCESS); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Soft reset failed\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Initiate a PHY reset + if (PhySoftReset (LanDriver) < 0) { + Snp->Mode->State = EfiSimpleNetworkStopped; + DEBUG ((DEBUG_WARN, "LAN91x: PHY soft reset timeout\n")); + ReturnUnlock (EFI_NOT_STARTED); + } + + // Do auto-negotiation + Status = PhyAutoNegotiate (LanDriver); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Soft reset failed\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Initiate a PHY reset + if (EFI_ERROR (PhySoftReset (LanDriver))) { + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver in stopped state\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_ERROR, "LAN91x:SnpReceiveFilters()\n")); + DEBUG ((DEBUG_ERROR, " Enable = %08x\n", Enable)); + DEBUG ((DEBUG_ERROR, " Disable = %08x\n", Disable)); + DEBUG ((DEBUG_ERROR, " Reset = %d\n", Reset)); + DEBUG ((DEBUG_ERROR, " NumMfilter = %d\n", NumMfilter)); + for (i = 0; i < NumMfilter; ++i) { + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_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; + MSK_LINKED_SYSTEM_BUF *LinkedTXRecycleBuff; + + // 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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 + // The transmit buffer status is not read when TxBuf is NULL + if (TxBuff != NULL) { + *((UINT8 **) TxBuff) = (UINT8 *) 0; + if( !IsListEmpty (&LanDriver->TransmitQueueHead)) + { + LinkedTXRecycleBuff = CR (GetFirstNode (&LanDriver->TransmitQueueHead), MSK_LINKED_SYSTEM_BUF, Link, TX_MBUF_SIGNATURE); + if(LinkedTXRecycleBuff != NULL) { + *TxBuff = LinkedTXRecycleBuff->SystemBuf.Buf; + RemoveEntryList (&LinkedTXRecycleBuff->Link); + FreePool (LinkedTXRecycleBuff); + } + } + } + + // Update the media status + MediaPresent = CheckLinkStatus (LanDriver); + if (MediaPresent != Snp->Mode->MediaPresent) { + DEBUG ((DEBUG_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; + MSK_LINKED_SYSTEM_BUF *LinkedTXRecycleBuff; + + + // Check preliminaries + if ((Snp == NULL) || (BufAddr == NULL)) { + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_ERROR, "LAN91x: SnpTransmit(): Invalid HdrSize %d\n", HdrSize)); + ReturnUnlock (EFI_INVALID_PARAMETER); + } + + if ((DstAddr == NULL) || (Protocol == NULL)) { + DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "LAN91x: Tx buffer allocation failure: %02x\n", PktNum)); + ReturnUnlock (EFI_NOT_READY); + } + PktNum &= ARR_PACKET; + + // Check for the nature of the frame + // If no destination address, it's ARP broadcast + if(DstAddr != NULL) + { + 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; + } + } else { + LanDriver->Stats.TxBroadcastFrames += 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 ((DEBUG_ERROR, "LAN91x: Tx buffer release failure: %d\n", Status)); + ReturnUnlock (EFI_DEVICE_ERROR); + } + + // Update the Tx statistics + LanDriver->Stats.TxTotalBytes += BufSize; + LanDriver->Stats.TxGoodFrames += 1; + + // Update the Tx Buffer cache + LinkedTXRecycleBuff = AllocateZeroPool (sizeof (MSK_LINKED_SYSTEM_BUF)); + if (LinkedTXRecycleBuff == NULL) { + return EFI_OUT_OF_RESOURCES; + } + LinkedTXRecycleBuff->Signature = TX_MBUF_SIGNATURE; + // + // Add the passed Buffer to the transmit queue. Don't copy. + // + LinkedTXRecycleBuff->SystemBuf.Buf = BufAddr; + LinkedTXRecycleBuff->SystemBuf.Length = BufSize; + InsertTailList (&LanDriver->TransmitQueueHead, &LinkedTXRecycleBuff->Link); + + Status = EFI_SUCCESS; + + // Dump the packet header +#if LAN91X_PRINT_PACKET_HEADERS + Ptr = BufAddr; + DEBUG ((DEBUG_ERROR, "LAN91X:SnpTransmit()\n")); + DEBUG ((DEBUG_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 ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n")); + ReturnUnlock (EFI_DEVICE_ERROR); + case EfiSimpleNetworkStopped: + DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n")); + ReturnUnlock (EFI_NOT_STARTED); + default: + DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "LAN91X:SnpReceive()\n")); + DEBUG ((DEBUG_ERROR, " HdrSize: %p, SrcAddr: %p, DstAddr: %p, Protocol: %p\n", + HdrSize, SrcAddr, DstAddr, Protocol)); + DEBUG ((DEBUG_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 ((DEBUG_ERROR, "LAN91x:Lan91xDxeEntry(): Probe failed with status %d\n", Status)); + return Status; + } + +#ifdef LAN91X_PRINT_REGISTERS + PrintIoRegisters (LanDriver); + PrintPhyRegisters (LanDriver); +#endif + + // Initialize transmit queue + InitializeListHead (&LanDriver->TransmitQueueHead); + + // 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/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf new file mode 100644 index 0000000000..1a9c568c99 --- /dev/null +++ b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf @@ -0,0 +1,58 @@ +#/** @file +# INF file for the SMSC LAN91x series Network Controller Driver. +# +# Copyright (c) 2013-2017 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 = 0x00010019 + 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] + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + ArmLib + BaseLib + BaseMemoryLib + DevicePathLib + IoLib + NetLib + TimerLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiSimpleNetworkProtocolGuid + gEfiMetronomeArchProtocolGuid + gEfiPxeBaseCodeProtocolGuid + gEfiDevicePathProtocolGuid + +[FixedPcd] + gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress + +[Depex] + TRUE diff --git a/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h new file mode 100644 index 0000000000..dc1295571d --- /dev/null +++ b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h @@ -0,0 +1,278 @@ +/** @file +* SMSC LAN91x series Network Controller Driver. +* +* Copyright (c) 2013-2017 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 + +#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/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec index a7e1270852..e90f039c28 100644 --- a/EmbeddedPkg/EmbeddedPkg.dec +++ b/EmbeddedPkg/EmbeddedPkg.dec @@ -4,6 +4,7 @@ # This Package provides headers and libraries that conform to EFI/PI Industry standards. # Copyright (c) 2007, Intel Corporation. All rights reserved.
# Copyright (c) 2012-2015, ARM Ltd. All rights reserved.
+# Copyright (c) 2017, Linaro Ltd. 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. @@ -160,6 +161,9 @@ # duplex operation is suspected to be broken in the driver. gEmbeddedTokenSpaceGuid.PcdLan9118NegotiationFeatureMask|0xFFFFFEBF|UINT32|0x00000028 + # LAN91x Ethernet Driver PCDs + gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x0|UINT32|0x00000029 + # # Android FastBoot #