diff mbox

[edk2,v3,1/5] ArmPlatformPkg: Add Lan91x Dxe driver

Message ID 1422696566-21807-2-git-send-email-fu.wei@linaro.org
State New
Headers show

Commit Message

Fu Wei Fu Jan. 31, 2015, 9:29 a.m. UTC
From: "Reece R. Pollack" <reece.pollack@linaro.org>

Added a driver for the SMSC Lan91x Ethernet controllers, such as
the 91C111 emulated in the ARM RTSM development simulators.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Reece R. Pollack <reece.pollack@linaro.org>
[submitting patch upstream]
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
 EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c   | 2233 +++++++++++++++++++++++++++
 EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf |   58 +
 EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h |  278 ++++
 EmbeddedPkg/EmbeddedPkg.dec                 |    3 +
 4 files changed, 2572 insertions(+)
diff mbox

Patch

diff --git a/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c
new file mode 100644
index 0000000..56dee7a
--- /dev/null
+++ b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.c
@@ -0,0 +1,2233 @@ 
+/** @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/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
new file mode 100644
index 0000000..73a5727
--- /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 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]
+  EmbeddedPkg/EmbeddedPkg.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]
+  gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress
+
+[Depex]
+  TRUE
diff --git a/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h b/EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h
new file mode 100644
index 0000000..1b05c54
--- /dev/null
+++ b/EmbeddedPkg/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/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
index 600d0e5..94a19c2 100644
--- a/EmbeddedPkg/EmbeddedPkg.dec
+++ b/EmbeddedPkg/EmbeddedPkg.dec
@@ -137,6 +137,9 @@ 
   gEmbeddedTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x0|UINT32|0x00000025
   gEmbeddedTokenSpaceGuid.PcdLan9118DefaultMacAddress|0x0|UINT64|0x00000026
 
+  # LAN91x Ethernet Driver PCDs
+  gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x0|UINT32|0x000000FE
+
   #
   # Android FastBoot
   #