public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Ard Biesheuvel" <ard.biesheuvel@arm.com>
To: devel@edk2.groups.io
Cc: leif@nuviainc.com, philmd@redhat.com,
	Ard Biesheuvel <ard.biesheuvel@arm.com>
Subject: [PATCH edk2-platforms v3 3/8] Silicon/Broadcom/BcmGenetDxe: Add GENET driver
Date: Tue, 12 May 2020 18:41:20 +0200	[thread overview]
Message-ID: <20200512164125.28139-4-ard.biesheuvel@arm.com> (raw)
In-Reply-To: <20200512164125.28139-1-ard.biesheuvel@arm.com>

Add support for the Broadcom GENET v5 ethernet controller
for the Raspberry Pi 4 (BCM2711)

Co-authored-by: Jared McNeill <jmcneill@invisible.ca>
Co-authored-by: Andrei Warkentin <awarkentin@vmware.com>
Co-authored-by: Samer El-Haj-Mahmoud <samer.el-haj-mahmoud@arm.com>
Co-authored-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf |  46 +-
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.h   | 363 +++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h    | 133 ++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c | 202 +++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/DriverBinding.c | 408 ++++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c    | 404 ++++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/Genet.c         | 114 ---
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c     | 814 +++++++++++++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c | 838 ++++++++++++++++++++
 9 files changed, 3195 insertions(+), 127 deletions(-)

diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf
index 9e9301608f24..28f3e66ebaf0 100644
--- a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf
@@ -1,40 +1,60 @@
 ## @file
+# Component description file for Broadcom GENET driver.
 #
+# Copyright (c) 2020, Jared McNeill All rights reserved.<BR>
 # Copyright (c) 2020, Jeremy Linton All rights reserved.<BR>
+# Copyright (c) 2020, ARM Limited. All rights reserved.<BR>
 #
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 ##
 
 [Defines]
-  INF_VERSION                    = 0x0001001A
+  INF_VERSION                    = 1.27
   BASE_NAME                      = BcmGenetDxe
   FILE_GUID                      = e2b1eaf3-50b7-4ae1-b79e-ec8020cb57ac
-  MODULE_TYPE                    = DXE_DRIVER
-  VERSION_STRING                 = 0.1
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
   ENTRY_POINT                    = GenetEntryPoint
+  UNLOAD_IMAGE                   = GenetUnload
 
 [Sources]
-  Genet.c
+  BcmGenetDxe.h
+  ComponentName.c
+  DriverBinding.c
+  GenericPhy.c
+  GenericPhy.h
+  GenetUtil.c
+  SimpleNetwork.c
 
 [Packages]
-  ArmPkg/ArmPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
+  NetworkPkg/NetworkPkg.dec
   Silicon/Broadcom/Drivers/Net/BcmNet.dec
 
 [LibraryClasses]
-  ArmLib
   BaseLib
+  BaseMemoryLib
+  DebugLib
+  DevicePathLib
+  DmaLib
   IoLib
+  MemoryAllocationLib
+  NetLib
+  UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
 
+[Protocols]
+  gBcmGenetPlatformDeviceProtocolGuid         ## TO_START
+  gEfiDevicePathProtocolGuid                  ## BY_START
+  gEfiSimpleNetworkProtocolGuid               ## BY_START
+
+[Guids]
+  gEfiEventExitBootServicesGuid
+
 [FixedPcd]
-  gBcmNetTokenSpaceGuid.PcdBcmGenetRegistersAddress
-
-[Pcd]
-  gBcmNetTokenSpaceGuid.PcdBcmGenetMacAddress
-
-[Depex]
-  TRUE
+  gEmbeddedTokenSpaceGuid.PcdDmaDeviceOffset
+  gEmbeddedTokenSpaceGuid.PcdDmaDeviceLimit
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.h b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.h
new file mode 100644
index 000000000000..0af9d5209cf2
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.h
@@ -0,0 +1,363 @@
+/** @file
+
+  Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca>
+  Copyright (c) 2020, ARM Limited. All rights reserved.
+  Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef BCM_GENET_DXE_H__
+#define BCM_GENET_DXE_H__
+
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Protocol/BcmGenetPlatformDevice.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/SimpleNetwork.h>
+
+#include "GenericPhy.h"
+
+#define LOWEST_SET_BIT(__mask)    ((((__mask) - 1) & (__mask)) ^ (__mask))
+#define SHIFTOUT(__x, __mask)     (((__x) & (__mask)) / LOWEST_SET_BIT (__mask))
+#define SHIFTIN(__x, __mask)      ((__x) * LOWEST_SET_BIT (__mask))
+
+/*
+ * Aux control shadow register, bits 0-2 select function (0x00 to
+ * 0x07).
+ */
+#define BRGPHY_MII_AUXCTL                0x18     /* AUX control */
+#define BRGPHY_AUXCTL_SHADOW_MISC        0x07
+#define BRGPHY_AUXCTL_MISC_DATA_MASK     0x7ff8
+#define BRGPHY_AUXCTL_MISC_READ_SHIFT    12
+#define BRGPHY_AUXCTL_MISC_WRITE_EN      0x8000
+#define BRGPHY_AUXCTL_MISC_RGMII_SKEW_EN 0x0200
+
+/*
+ * Shadow register 0x1C, bit 15 is write enable,
+ * bits 14-10 select function (0x00 to 0x1F).
+ */
+#define BRGPHY_MII_SHADOW_1C             0x1C
+#define BRGPHY_SHADOW_1C_WRITE_EN        0x8000
+#define BRGPHY_SHADOW_1C_SELECT_MASK     0x7C00
+#define BRGPHY_SHADOW_1C_DATA_MASK       0x03FF
+
+/* Shadow 0x1C Clock Alignment Control Register (select value 0x03) */
+#define BRGPHY_SHADOW_1C_CLK_CTRL        (0x03 << 10)
+#define BRGPHY_SHADOW_1C_GTXCLK_EN       0x0200
+
+#define MAX_ETHERNET_PKT_SIZE                   1500
+
+#define GENET_VERSION                           0x0a
+#define GENET_MAX_PACKET_SIZE                   1536
+
+#define GENET_SYS_REV_CTRL                      0x000
+#define  SYS_REV_MAJOR                          (BIT27|BIT26|BIT25|BIT24)
+#define  SYS_REV_MINOR                          (BIT19|BIT18|BIT17|BIT16)
+#define GENET_SYS_PORT_CTRL                     0x004
+#define  GENET_SYS_PORT_MODE_EXT_GPHY           3
+#define GENET_SYS_RBUF_FLUSH_CTRL               0x008
+#define  GENET_SYS_RBUF_FLUSH_RESET             BIT1
+#define GENET_SYS_TBUF_FLUSH_CTRL               0x00c
+#define GENET_EXT_RGMII_OOB_CTRL                0x08c
+#define  GENET_EXT_RGMII_OOB_ID_MODE_DISABLE    BIT16
+#define  GENET_EXT_RGMII_OOB_RGMII_MODE_EN      BIT6
+#define  GENET_EXT_RGMII_OOB_OOB_DISABLE        BIT5
+#define  GENET_EXT_RGMII_OOB_RGMII_LINK         BIT4
+#define GENET_INTRL2_CPU_STAT                   0x200
+#define GENET_INTRL2_CPU_CLEAR                  0x208
+#define GENET_INTRL2_CPU_STAT_MASK              0x20c
+#define GENET_INTRL2_CPU_SET_MASK               0x210
+#define GENET_INTRL2_CPU_CLEAR_MASK             0x214
+#define  GENET_IRQ_MDIO_ERROR                   BIT24
+#define  GENET_IRQ_MDIO_DONE                    BIT23
+#define  GENET_IRQ_TXDMA_DONE                   BIT16
+#define  GENET_IRQ_RXDMA_DONE                   BIT13
+#define GENET_RBUF_CTRL                         0x300
+#define  GENET_RBUF_BAD_DIS                     BIT2
+#define  GENET_RBUF_ALIGN_2B                    BIT1
+#define  GENET_RBUF_64B_EN                      BIT0
+#define GENET_RBUF_TBUF_SIZE_CTRL               0x3b4
+#define GENET_UMAC_CMD                          0x808
+#define  GENET_UMAC_CMD_LCL_LOOP_EN             BIT15
+#define  GENET_UMAC_CMD_SW_RESET                BIT13
+#define  GENET_UMAC_CMD_HD_EN                   BIT10
+#define  GENET_UMAC_CMD_PROMISC                 BIT4
+#define  GENET_UMAC_CMD_SPEED                   (BIT3|BIT2)
+#define   GENET_UMAC_CMD_SPEED_10               0
+#define   GENET_UMAC_CMD_SPEED_100              1
+#define   GENET_UMAC_CMD_SPEED_1000             2
+#define  GENET_UMAC_CMD_RXEN                    BIT1
+#define  GENET_UMAC_CMD_TXEN                    BIT0
+#define GENET_UMAC_MAC0                         0x80c
+#define GENET_UMAC_MAC1                         0x810
+#define GENET_UMAC_MAX_FRAME_LEN                0x814
+#define GENET_UMAC_TX_FLUSH                     0xb34
+#define GENET_UMAC_MIB_CTRL                     0xd80
+#define  GENET_UMAC_MIB_RESET_TX                BIT2
+#define  GENET_UMAC_MIB_RESET_RUNT              BIT1
+#define  GENET_UMAC_MIB_RESET_RX                BIT0
+#define GENET_MDIO_CMD                          0xe14
+#define  GENET_MDIO_START_BUSY                  BIT29
+#define  GENET_MDIO_READ                        BIT27
+#define  GENET_MDIO_WRITE                       BIT26
+#define  GENET_MDIO_PMD                         (BIT25|BIT24|BIT23|BIT22|BIT21)
+#define  GENET_MDIO_REG                         (BIT20|BIT19|BIT18|BIT17|BIT16)
+#define GENET_UMAC_MDF_CTRL                     0xe50
+#define GENET_UMAC_MDF_ADDR0(n)                 (0xe54 + (n) * 0x8)
+#define GENET_UMAC_MDF_ADDR1(n)                 (0xe58 + (n) * 0x8)
+#define GENET_MAX_MDF_FILTER                    17
+
+#define GENET_DMA_DESC_COUNT                    256
+#define GENET_DMA_DESC_SIZE                     12
+#define GENET_DMA_DEFAULT_QUEUE                 16
+
+#define GENET_DMA_RING_SIZE                     0x40
+#define GENET_DMA_RINGS_SIZE                    (GENET_DMA_RING_SIZE * (GENET_DMA_DEFAULT_QUEUE + 1))
+
+#define GENET_RX_BASE                           0x2000
+#define GENET_TX_BASE                           0x4000
+
+#define GENET_RX_DMA_RINGBASE(qid)              (GENET_RX_BASE + 0xc00 + GENET_DMA_RING_SIZE * (qid))
+#define GENET_RX_DMA_WRITE_PTR_LO(qid)          (GENET_RX_DMA_RINGBASE(qid) + 0x00)
+#define GENET_RX_DMA_WRITE_PTR_HI(qid)          (GENET_RX_DMA_RINGBASE(qid) + 0x04)
+#define GENET_RX_DMA_PROD_INDEX(qid)            (GENET_RX_DMA_RINGBASE(qid) + 0x08)
+#define GENET_RX_DMA_CONS_INDEX(qid)            (GENET_RX_DMA_RINGBASE(qid) + 0x0c)
+#define GENET_RX_DMA_RING_BUF_SIZE(qid)         (GENET_RX_DMA_RINGBASE(qid) + 0x10)
+#define  GENET_RX_DMA_RING_BUF_SIZE_DESC_COUNT  0xffff0000
+#define  GENET_RX_DMA_RING_BUF_SIZE_BUF_LENGTH  0x0000ffff
+#define GENET_RX_DMA_START_ADDR_LO(qid)         (GENET_RX_DMA_RINGBASE(qid) + 0x14)
+#define GENET_RX_DMA_START_ADDR_HI(qid)         (GENET_RX_DMA_RINGBASE(qid) + 0x18)
+#define GENET_RX_DMA_END_ADDR_LO(qid)           (GENET_RX_DMA_RINGBASE(qid) + 0x1c)
+#define GENET_RX_DMA_END_ADDR_HI(qid)           (GENET_RX_DMA_RINGBASE(qid) + 0x20)
+#define GENET_RX_DMA_XON_XOFF_THRES(qid)        (GENET_RX_DMA_RINGBASE(qid) + 0x28)
+#define  GENET_RX_DMA_XON_XOFF_THRES_LO         0xffff0000
+#define  GENET_RX_DMA_XON_XOFF_THRES_HI         0x0000ffff
+#define GENET_RX_DMA_READ_PTR_LO(qid)           (GENET_RX_DMA_RINGBASE(qid) + 0x2c)
+#define GENET_RX_DMA_READ_PTR_HI(qid)           (GENET_RX_DMA_RINGBASE(qid) + 0x30)
+
+#define GENET_TX_DMA_RINGBASE(qid)              (GENET_TX_BASE + 0xc00 + GENET_DMA_RING_SIZE * (qid))
+#define GENET_TX_DMA_READ_PTR_LO(qid)           (GENET_TX_DMA_RINGBASE(qid) + 0x00)
+#define GENET_TX_DMA_READ_PTR_HI(qid)           (GENET_TX_DMA_RINGBASE(qid) + 0x04)
+#define GENET_TX_DMA_CONS_INDEX(qid)            (GENET_TX_DMA_RINGBASE(qid) + 0x08)
+#define GENET_TX_DMA_PROD_INDEX(qid)            (GENET_TX_DMA_RINGBASE(qid) + 0x0c)
+#define GENET_TX_DMA_RING_BUF_SIZE(qid)         (GENET_TX_DMA_RINGBASE(qid) + 0x10)
+#define  GENET_TX_DMA_RING_BUF_SIZE_DESC_COUNT  0xffff0000
+#define  GENET_TX_DMA_RING_BUF_SIZE_BUF_LENGTH  0x0000ffff
+#define GENET_TX_DMA_START_ADDR_LO(qid)         (GENET_TX_DMA_RINGBASE(qid) + 0x14)
+#define GENET_TX_DMA_START_ADDR_HI(qid)         (GENET_TX_DMA_RINGBASE(qid) + 0x18)
+#define GENET_TX_DMA_END_ADDR_LO(qid)           (GENET_TX_DMA_RINGBASE(qid) + 0x1c)
+#define GENET_TX_DMA_END_ADDR_HI(qid)           (GENET_TX_DMA_RINGBASE(qid) + 0x20)
+#define GENET_TX_DMA_MBUF_DONE_THRES(qid)       (GENET_TX_DMA_RINGBASE(qid) + 0x24)
+#define GENET_TX_DMA_FLOW_PERIOD(qid)           (GENET_TX_DMA_RINGBASE(qid) + 0x28)
+#define GENET_TX_DMA_WRITE_PTR_LO(qid)          (GENET_TX_DMA_RINGBASE(qid) + 0x2c)
+#define GENET_TX_DMA_WRITE_PTR_HI(qid)          (GENET_TX_DMA_RINGBASE(qid) + 0x30)
+
+#define GENET_RX_DESC_STATUS(idx)               (GENET_RX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x00)
+#define  GENET_RX_DESC_STATUS_BUFLEN            (BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)
+#define  GENET_RX_DESC_STATUS_OWN               BIT15
+#define  GENET_RX_DESC_STATUS_EOP               BIT14
+#define  GENET_RX_DESC_STATUS_SOP               BIT13
+#define  GENET_RX_DESC_STATUS_RX_ERROR          BIT2
+#define GENET_RX_DESC_ADDRESS_LO(idx)           (GENET_RX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x04)
+#define GENET_RX_DESC_ADDRESS_HI(idx)           (GENET_RX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x08)
+
+#define GENET_TX_DESC_STATUS(idx)               (GENET_TX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x00)
+#define  GENET_TX_DESC_STATUS_BUFLEN            (BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)
+#define  GENET_TX_DESC_STATUS_OWN               BIT15
+#define  GENET_TX_DESC_STATUS_EOP               BIT14
+#define  GENET_TX_DESC_STATUS_SOP               BIT13
+#define  GENET_TX_DESC_STATUS_QTAG              (BIT12|BIT11|BIT10|BIT9|BIT8|BIT7)
+#define  GENET_TX_DESC_STATUS_CRC               BIT6
+#define GENET_TX_DESC_ADDRESS_LO(idx)           (GENET_TX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x04)
+#define GENET_TX_DESC_ADDRESS_HI(idx)           (GENET_TX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x08)
+
+#define GENET_RX_DMA_RING_CFG                   (GENET_RX_BASE + 0x1040 + 0x00)
+#define GENET_RX_DMA_CTRL                       (GENET_RX_BASE + 0x1040 + 0x04)
+#define  GENET_RX_DMA_CTRL_RBUF_EN(qid)         (BIT1 << (qid))
+#define  GENET_RX_DMA_CTRL_EN                   BIT0
+#define GENET_RX_SCB_BURST_SIZE                 (GENET_RX_BASE + 0x1040 + 0x0c)
+
+#define GENET_TX_DMA_RING_CFG                   (GENET_TX_BASE + 0x1040 + 0x00)
+#define GENET_TX_DMA_CTRL                       (GENET_TX_BASE + 0x1040 + 0x04)
+#define  GENET_TX_DMA_CTRL_RBUF_EN(qid)         (BIT1 << (qid))
+#define  GENET_TX_DMA_CTRL_EN                   BIT0
+#define GENET_TX_SCB_BURST_SIZE                 (GENET_TX_BASE + 0x1040 + 0x0c)
+
+typedef struct {
+  EFI_PHYSICAL_ADDRESS            PhysAddress;
+  VOID *                          Mapping;
+} GENET_MAP_INFO;
+
+typedef enum {
+  GENET_PHY_MODE_MII,
+  GENET_PHY_MODE_RGMII,
+  GENET_PHY_MODE_RGMII_RXID,
+  GENET_PHY_MODE_RGMII_TXID,
+  GENET_PHY_MODE_RGMII_ID,
+} GENET_PHY_MODE;
+
+typedef struct {
+  UINT32                              Signature;
+  EFI_HANDLE                          ControllerHandle;
+
+  EFI_LOCK                            Lock;
+  EFI_EVENT                           ExitBootServicesEvent;
+
+  EFI_SIMPLE_NETWORK_PROTOCOL         Snp;
+  EFI_SIMPLE_NETWORK_MODE             SnpMode;
+
+  BCM_GENET_PLATFORM_DEVICE_PROTOCOL  *Dev;
+
+  GENERIC_PHY_PRIVATE_DATA            Phy;
+
+  UINT8                               *TxBuffer[GENET_DMA_DESC_COUNT];
+  VOID                                *TxBufferMap[GENET_DMA_DESC_COUNT];
+  UINT8                               TxQueued;
+  UINT16                              TxNext;
+  UINT16                              TxConsIndex;
+  UINT16                              TxProdIndex;
+
+  EFI_PHYSICAL_ADDRESS                RxBuffer;
+  GENET_MAP_INFO                      RxBufferMap[GENET_DMA_DESC_COUNT];
+  UINT16                              RxConsIndex;
+  UINT16                              RxProdIndex;
+
+  GENET_PHY_MODE                      PhyMode;
+
+  UINTN                               RegBase;
+} GENET_PRIVATE_DATA;
+
+extern EFI_COMPONENT_NAME_PROTOCOL            gGenetComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL           gGenetComponentName2;
+
+extern CONST EFI_SIMPLE_NETWORK_PROTOCOL      gGenetSimpleNetworkTemplate;
+
+#define GENET_DRIVER_SIGNATURE                SIGNATURE_32('G', 'N', 'E', 'T')
+#define GENET_PRIVATE_DATA_FROM_SNP_THIS(a)   CR(a, GENET_PRIVATE_DATA, Snp, GENET_DRIVER_SIGNATURE)
+
+#define GENET_RX_BUFFER(g, idx)               ((UINT8 *)(UINTN)(g)->RxBuffer + GENET_MAX_PACKET_SIZE * (idx))
+
+EFI_STATUS
+EFIAPI
+GenetPhyRead (
+  IN  VOID   *Priv,
+  IN  UINT8  PhyAddr,
+  IN  UINT8  Reg,
+  OUT UINT16 *Data
+  );
+
+EFI_STATUS
+EFIAPI
+GenetPhyWrite (
+  IN VOID   *Priv,
+  IN UINT8  PhyAddr,
+  IN UINT8  Reg,
+  IN UINT16 Data
+  );
+
+EFI_STATUS
+EFIAPI
+GenetPhyResetAction (
+  IN VOID *Priv
+  );
+
+VOID
+EFIAPI
+GenetPhyConfigure (
+  IN VOID               *Priv,
+  IN GENERIC_PHY_SPEED  Speed,
+  IN GENERIC_PHY_DUPLEX Duplex
+  );
+
+VOID
+GenetReset (
+  IN GENET_PRIVATE_DATA *Genet
+  );
+
+VOID
+EFIAPI
+GenetSetMacAddress (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN EFI_MAC_ADDRESS    *MacAddr
+  );
+
+VOID
+GenetSetPhyMode (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN GENET_PHY_MODE     PhyMode
+  );
+
+VOID
+GenetEnableTxRx (
+  IN GENET_PRIVATE_DATA *Genet
+  );
+
+VOID
+GenetDisableTxRx (
+  IN GENET_PRIVATE_DATA *Genet
+  );
+
+VOID
+GenetSetPromisc (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN BOOLEAN            Enable
+  );
+
+VOID
+GenetEnableBroadcastFilter (
+  IN GENET_PRIVATE_DATA   *Genet,
+  IN BOOLEAN              Enable
+  );
+
+VOID
+GenetDmaInitRings (
+  IN GENET_PRIVATE_DATA *Genet
+  );
+
+EFI_STATUS
+GenetDmaAlloc (
+  IN GENET_PRIVATE_DATA *Genet
+  );
+
+VOID
+GenetDmaFree (
+  IN GENET_PRIVATE_DATA *Genet
+  );
+
+VOID
+GenetDmaTriggerTx (
+  IN GENET_PRIVATE_DATA   *Genet,
+  IN UINT8                DescIndex,
+  IN EFI_PHYSICAL_ADDRESS PhysAddr,
+  IN UINTN                NumberOfBytes
+  );
+
+EFI_STATUS
+GenetDmaMapRxDescriptor (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN UINT8              DescIndex
+  );
+
+VOID
+GenetDmaUnmapRxDescriptor (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN UINT8               DescIndex
+  );
+
+VOID
+GenetTxIntr (
+  IN GENET_PRIVATE_DATA *Genet,
+  OUT VOID              **TxBuf
+  );
+
+EFI_STATUS
+GenetRxIntr (
+  IN GENET_PRIVATE_DATA *Genet,
+  OUT UINT8             *DescIndex,
+  OUT UINTN             *FrameLength
+  );
+
+#endif /* GENET_UTIL_H__ */
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h
new file mode 100644
index 000000000000..a6c70aa97502
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h
@@ -0,0 +1,133 @@
+/** @file
+
+  Copyright (c) 2020 Jared McNeill. All rights reserved.
+  Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef GENERICPHY_H__
+#define GENERICPHY_H__
+
+//
+// Basic Control Register
+//
+#define GENERIC_PHY_BMCR                0x00
+#define  GENERIC_PHY_BMCR_RESET         BIT15
+#define  GENERIC_PHY_BMCR_ANE           BIT12
+#define  GENERIC_PHY_BMCR_RESTART_AN    BIT9
+
+//
+// Basic Status Register
+//
+#define GENERIC_PHY_BMSR                0x01
+#define  GENERIC_PHY_BMSR_ANEG_COMPLETE BIT5
+#define  GENERIC_PHY_BMSR_LINK_STATUS   BIT2
+
+//
+// PHY Identifier I & II
+//
+#define GENERIC_PHY_PHYIDR1             0x02
+#define GENERIC_PHY_PHYIDR2             0x03
+
+//
+// Auto-Negotiation Advertisement Register
+//
+#define GENERIC_PHY_ANAR                0x04
+#define  GENERIC_PHY_ANAR_100BASETX_FDX BIT8
+#define  GENERIC_PHY_ANAR_100BASETX     BIT7
+#define  GENERIC_PHY_ANAR_10BASET_FDX   BIT6
+#define  GENERIC_PHY_ANAR_10BASET       BIT5
+
+//
+// Auto-Negotiation Link Partner Ability Register
+//
+#define GENERIC_PHY_ANLPAR              0x05
+
+//
+// 1000Base-T Control Register
+//
+#define GENERIC_PHY_GBCR                0x09
+#define  GENERIC_PHY_GBCR_1000BASET_FDX BIT9
+#define  GENERIC_PHY_GBCR_1000BASET     BIT8
+
+//
+// 1000Base-T Status Register
+//
+#define GENERIC_PHY_GBSR                0x0A
+
+typedef enum {
+  PHY_SPEED_NONE  = 0,
+  PHY_SPEED_10    = 10,
+  PHY_SPEED_100   = 100,
+  PHY_SPEED_1000  = 1000
+} GENERIC_PHY_SPEED;
+
+typedef enum {
+  PHY_DUPLEX_HALF,
+  PHY_DUPLEX_FULL
+} GENERIC_PHY_DUPLEX;
+
+typedef
+EFI_STATUS
+(EFIAPI *GENERIC_PHY_READ) (
+  IN VOID                     *Priv,
+  IN UINT8                    PhyAddr,
+  IN UINT8                    Reg,
+  OUT UINT16 *                Data
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GENERIC_PHY_WRITE) (
+  IN VOID                     *Priv,
+  IN UINT8                    PhyAddr,
+  IN UINT8                    Reg,
+  IN UINT16                   Data
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GENERIC_PHY_RESET_ACTION) (
+  IN VOID                     *Priv
+  );
+
+typedef
+VOID
+(EFIAPI *GENERIC_PHY_CONFIGURE) (
+  IN VOID                     *Priv,
+  IN GENERIC_PHY_SPEED        Speed,
+  IN GENERIC_PHY_DUPLEX       Duplex
+  );
+
+typedef struct {
+  GENERIC_PHY_READ            Read;
+  GENERIC_PHY_WRITE           Write;
+  GENERIC_PHY_RESET_ACTION    ResetAction;
+  GENERIC_PHY_CONFIGURE       Configure;
+  VOID                        *PrivateData;
+
+  UINT8                       PhyAddr;
+  BOOLEAN                     LinkUp;
+} GENERIC_PHY_PRIVATE_DATA;
+
+EFI_STATUS
+EFIAPI
+GenericPhyInit (
+  IN GENERIC_PHY_PRIVATE_DATA *Phy
+  );
+
+EFI_STATUS
+EFIAPI
+GenericPhyUpdateConfig (
+  IN GENERIC_PHY_PRIVATE_DATA *Phy
+  );
+
+EFI_STATUS
+EFIAPI
+GenericPhyReset (
+  IN GENERIC_PHY_PRIVATE_DATA *Phy
+  );
+
+#endif // GENERICPHY_H__
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c
new file mode 100644
index 000000000000..860e30b4da6b
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c
@@ -0,0 +1,202 @@
+/** @file
+  UEFI Component Name(2) protocol implementation for GENET UEFI driver.
+
+  Copyright (c) 2020 Jared McNeill. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Protocol/ComponentName2.h>
+
+#include "BcmGenetDxe.h"
+
+STATIC EFI_UNICODE_STRING_TABLE mGenetDriverNameTable[] = {
+  {
+    "eng;en",
+    L"Broadcom GENET Ethernet Driver"
+  },
+  {
+     NULL,
+     NULL
+  }
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mGenetDeviceNameTable[] = {
+  {
+    "eng;en",
+    L"Broadcom GENET Ethernet"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **DriverName
+  )
+{
+  return LookupUnicodeString2 (Language,
+                               This->SupportedLanguages,
+                               mGenetDriverNameTable,
+                               DriverName,
+                               (BOOLEAN)(This == &gGenetComponentName2)
+                               );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL                    *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  if (ChildHandle != NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return LookupUnicodeString2 (Language,
+                               This->SupportedLanguages,
+                               mGenetDeviceNameTable,
+                               ControllerName,
+                               (BOOLEAN)(This == &gGenetComponentName2)
+                               );
+}
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_COMPONENT_NAME_PROTOCOL gGenetComponentName = {
+  (EFI_COMPONENT_NAME_GET_DRIVER_NAME) GenetComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) GenetComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_COMPONENT_NAME2_PROTOCOL gGenetComponentName2 = {
+  GenetComponentNameGetDriverName,
+  GenetComponentNameGetControllerName,
+  "en"
+};
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/DriverBinding.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/DriverBinding.c
new file mode 100644
index 000000000000..f37da3b65511
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/DriverBinding.c
@@ -0,0 +1,408 @@
+/** @file
+  Device driver for the Broadcom GENET controller
+
+  Copyright (c) 2020 Jared McNeill. All rights reserved.
+  Copyright (c) 2020, ARM Limited. All rights reserved.
+  Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/NetLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Protocol/BcmGenetPlatformDevice.h>
+
+#include "BcmGenetDxe.h"
+
+/**
+  Tests to see if this driver supports a given controller.
+
+  @param  This[in]                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                   instance.
+  @param  ControllerHandle[in]     The handle of the controller to test.
+  @param  RemainingDevicePath[in]  The remaining device path.
+                                   (Ignored - this is not a bus driver.)
+
+  @retval EFI_SUCCESS              The driver supports this controller.
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle is
+                                   already being managed by the driver specified
+                                   by This.
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle is
+                                   not supported by the driver specified by This.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  BCM_GENET_PLATFORM_DEVICE_PROTOCOL *Dev;
+  EFI_STATUS                         Status;
+
+  //
+  //  Connect to the non-discoverable device
+  //
+  Status = gBS->OpenProtocol (ControllerHandle,
+                              &gBcmGenetPlatformDeviceProtocolGuid,
+                              (VOID **)&Dev,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Clean up.
+  //
+  gBS->CloseProtocol (ControllerHandle,
+                      &gBcmGenetPlatformDeviceProtocolGuid,
+                      This->DriverBindingHandle,
+                      ControllerHandle);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Callback function to shut down the network device at ExitBootServices
+
+  @param  Event                   Pointer to this event
+  @param  Context                 Event handler private data
+
+**/
+STATIC
+VOID
+EFIAPI
+GenetNotifyExitBootServices (
+  EFI_EVENT     Event,
+  VOID          *Context
+  )
+{
+  GenetDisableTxRx ((GENET_PRIVATE_DATA *)Context);
+}
+
+/**
+  Starts a device controller or a bus controller.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                   instance.
+  @param[in]  ControllerHandle     The handle of the device to start. This
+                                   handle must support a protocol interface that
+                                   supplies an I/O abstraction to the driver.
+  @param[in]  RemainingDevicePath  The remaining portion of the device path.
+                                   (Ignored - this is not a bus driver.)
+
+  @retval EFI_SUCCESS              The device was started.
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a
+                                   device error.
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a
+                                   lack of resources.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA      *Genet;
+  EFI_STATUS              Status;
+
+  // Allocate Resources
+  Genet = AllocateZeroPool (sizeof (GENET_PRIVATE_DATA));
+  if (Genet == NULL) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Couldn't allocate private data\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = gBS->OpenProtocol (ControllerHandle,
+                              &gBcmGenetPlatformDeviceProtocolGuid,
+                              (VOID **)&Genet->Dev,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Couldn't open protocol: %r\n", __FUNCTION__, Status));
+    goto FreeDevice;
+  }
+
+  Status = GenetDmaAlloc (Genet);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Couldn't allocate DMA buffers: %r\n", __FUNCTION__, Status));
+    goto FreeDevice;
+  }
+
+  Genet->Signature                     = GENET_DRIVER_SIGNATURE;
+  Genet->RegBase                       = Genet->Dev->BaseAddress;
+  Genet->Phy.PrivateData               = Genet;
+  Genet->Phy.Read                      = GenetPhyRead;
+  Genet->Phy.Write                     = GenetPhyWrite;
+  Genet->Phy.Configure                 = GenetPhyConfigure;
+  Genet->Phy.ResetAction               = GenetPhyResetAction;
+  Genet->PhyMode                       = GENET_PHY_MODE_RGMII_RXID;
+
+  EfiInitializeLock (&Genet->Lock, TPL_CALLBACK);
+  CopyMem (&Genet->Snp, &gGenetSimpleNetworkTemplate, sizeof Genet->Snp);
+
+  Genet->Snp.Mode                       = &Genet->SnpMode;
+  Genet->SnpMode.State                  = EfiSimpleNetworkStopped;
+  Genet->SnpMode.HwAddressSize          = NET_ETHER_ADDR_LEN;
+  Genet->SnpMode.MediaHeaderSize        = sizeof (ETHER_HEAD);
+  Genet->SnpMode.MaxPacketSize          = MAX_ETHERNET_PKT_SIZE;
+  Genet->SnpMode.NvRamSize              = 0;
+  Genet->SnpMode.NvRamAccessSize        = 0;
+  Genet->SnpMode.ReceiveFilterMask      = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+                                          EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
+                                          EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+  Genet->SnpMode.ReceiveFilterSetting   = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+                                          EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+  Genet->SnpMode.MaxMCastFilterCount    = 0;
+  Genet->SnpMode.MCastFilterCount       = 0;
+  Genet->SnpMode.IfType                 = NET_IFTYPE_ETHERNET;
+  Genet->SnpMode.MacAddressChangeable   = TRUE;
+  Genet->SnpMode.MultipleTxSupported    = FALSE;
+  Genet->SnpMode.MediaPresentSupported  = TRUE;
+  Genet->SnpMode.MediaPresent           = FALSE;
+
+  SetMem (&Genet->SnpMode.BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xff);
+
+  CopyMem (&Genet->SnpMode.PermanentAddress, &Genet->Dev->MacAddress,
+    sizeof(EFI_MAC_ADDRESS));
+  CopyMem (&Genet->SnpMode.CurrentAddress, &Genet->Dev->MacAddress,
+    sizeof(EFI_MAC_ADDRESS));
+
+  Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+                  GenetNotifyExitBootServices, Genet,
+                  &gEfiEventExitBootServicesGuid,
+                  &Genet->ExitBootServicesEvent);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN,
+      "GenetDriverBindingStart: failed to register for ExitBootServices event - %r\n",
+      Status));
+    goto FreeDevice;
+  }
+
+  Status = gBS->InstallMultipleProtocolInterfaces (&ControllerHandle,
+                  &gEfiSimpleNetworkProtocolGuid,   &Genet->Snp,
+                  NULL);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Couldn't install protocol interfaces: %r\n", __FUNCTION__, Status));
+    gBS->CloseProtocol (ControllerHandle,
+                        &gBcmGenetPlatformDeviceProtocolGuid,
+                        This->DriverBindingHandle,
+                        ControllerHandle);
+    goto FreeEvent;
+  }
+
+  Genet->ControllerHandle = ControllerHandle;
+  return EFI_SUCCESS;
+
+FreeEvent:
+  gBS->CloseEvent (Genet->ExitBootServicesEvent);
+FreeDevice:
+  DEBUG ((DEBUG_WARN, "%a: Returning %r\n", __FUNCTION__, Status));
+  FreePool (Genet);
+  return Status;
+}
+
+
+/**
+  Stops a device controller or a bus controller.
+
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                instance.
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle
+                                must support a bus specific I/O protocol for the
+                                driver to use to stop the device.
+  @param[in]  NumberOfChildren  The number of child device handles in
+                                ChildHandleBuffer.
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be
+                                NULL if NumberOfChildren is 0.
+
+  @retval EFI_SUCCESS           The device was stopped.
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device
+                                error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetDriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer   OPTIONAL
+  )
+{
+  EFI_SIMPLE_NETWORK_PROTOCOL     *SnpProtocol;
+  GENET_PRIVATE_DATA              *Genet;
+  EFI_STATUS                      Status;
+
+  Status = gBS->HandleProtocol (ControllerHandle,
+                                &gEfiSimpleNetworkProtocolGuid,
+                                (VOID **)&SnpProtocol
+                                );
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (SnpProtocol);
+
+  ASSERT (Genet->ControllerHandle == ControllerHandle);
+
+  Status = gBS->UninstallProtocolInterface (ControllerHandle,
+                                            &gEfiSimpleNetworkProtocolGuid,
+                                            &Genet->Snp
+                                            );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->CloseEvent (Genet->ExitBootServicesEvent);
+  ASSERT_EFI_ERROR (Status);
+
+  GenetDmaFree (Genet);
+
+  Status = gBS->CloseProtocol (ControllerHandle,
+                               &gBcmGenetPlatformDeviceProtocolGuid,
+                               This->DriverBindingHandle,
+                               ControllerHandle);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  FreePool (Genet);
+
+  return EFI_SUCCESS;
+}
+
+STATIC EFI_DRIVER_BINDING_PROTOCOL mGenetDriverBinding = {
+  GenetDriverBindingSupported,
+  GenetDriverBindingStart,
+  GenetDriverBindingStop,
+  GENET_VERSION,
+  NULL,
+  NULL
+};
+
+/**
+  The entry point of GENET UEFI Driver.
+
+  @param  ImageHandle                The image handle of the UEFI Driver.
+  @param  SystemTable                A pointer to the EFI System Table.
+
+  @retval  EFI_SUCCESS               The Driver or UEFI Driver exited normally.
+  @retval  EFI_INCOMPATIBLE_VERSION  _gUefiDriverRevision is greater than
+                                     SystemTable->Hdr.Revision.
+
+**/
+EFI_STATUS
+EFIAPI
+GenetEntryPoint (
+  IN  EFI_HANDLE          ImageHandle,
+  IN  EFI_SYSTEM_TABLE    *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &mGenetDriverBinding,
+             ImageHandle,
+             &gGenetComponentName,
+             &gGenetComponentName2
+             );
+
+  ASSERT_EFI_ERROR (Status);
+
+  DEBUG ((DEBUG_INIT | DEBUG_INFO, "Installed GENET UEFI driver!\n"));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Unload function of GENET UEFI Driver.
+
+  @param  ImageHandle            The allocated handle for the EFI image
+
+  @retval EFI_SUCCESS            The driver was unloaded successfully
+  @retval EFI_INVALID_PARAMETER  ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+GenetUnload (
+  IN EFI_HANDLE  ImageHandle
+  )
+{
+  EFI_STATUS  Status;
+  EFI_HANDLE  *HandleBuffer;
+  UINTN       HandleCount;
+  UINTN       Index;
+
+  //
+  // Retrieve all BcmGenetPlatformDevice handles in the handle database
+  //
+  Status = gBS->LocateHandleBuffer (ByProtocol,
+                                    &gBcmGenetPlatformDeviceProtocolGuid,
+                                    NULL,
+                                    &HandleCount,
+                                    &HandleBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Disconnect the driver from the handles in the handle database
+  //
+  for (Index = 0; Index < HandleCount && !EFI_ERROR (Status); Index++) {
+    Status = gBS->DisconnectController (HandleBuffer[Index],
+                                        gImageHandle,
+                                        NULL);
+  }
+
+  //
+  // Free the handle array
+  //
+  gBS->FreePool (HandleBuffer);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: failed to disconnect all controllers - %r\n",
+      __FUNCTION__, Status));
+    return Status;
+  }
+
+  //
+  // Uninstall protocols installed by the driver in its entrypoint
+  //
+  Status = EfiLibUninstallDriverBindingComponentName2 (
+             &mGenetDriverBinding,
+             &gGenetComponentName,
+             &gGenetComponentName2
+             );
+
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c
new file mode 100644
index 000000000000..9e5d30fafd38
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c
@@ -0,0 +1,404 @@
+/** @file
+
+  Copyright (c) 2020 Jared McNeill. All rights reserved.
+  Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "GenericPhy.h"
+
+#define PHY_RESET_TIMEOUT       500
+
+/**
+  Perform a PHY register read.
+
+  @param  Phy[in]      Pointer to GENERIC_PHY_PRIVATE_DATA.
+  @param  PhyAddr[in]  PHY address.
+  @param  Reg[in]      PHY register.
+  @param  Data[out]    Pointer to register data read.
+
+  @retval EFI_SUCCESS       Data read successfully.
+  @retval EFI_DEVICE_ERROR  Failed to read data.
+
+**/
+STATIC
+EFI_STATUS
+GenericPhyRead (
+  IN  GENERIC_PHY_PRIVATE_DATA    *Phy,
+  IN  UINT8                       PhyAddr,
+  IN  UINT8                       Reg,
+  OUT UINT16                      *Data
+  )
+{
+  return Phy->Read (Phy->PrivateData, PhyAddr, Reg, Data);
+}
+
+/**
+  Perform a PHY register write.
+
+  @param  Phy[in]      Pointer to GENERIC_PHY_PRIVATE_DATA.
+  @param  PhyAddr[in]  PHY address.
+  @param  Reg[in]      PHY register.
+  @param  Data[in]     Pointer to register data to write.
+
+  @retval EFI_SUCCESS  Data written successfully.
+  @retval EFI_DEVICE_ERROR  Failed to write data.
+
+**/
+STATIC
+EFI_STATUS
+GenericPhyWrite (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy,
+  IN UINT8                      PhyAddr,
+  IN UINT8                      Reg,
+  IN UINT16                     Data
+  )
+{
+  return Phy->Write (Phy->PrivateData, PhyAddr, Reg, Data);
+}
+
+/**
+  Process a PHY link speed change (e.g. with MAC layer).
+
+  @param  Phy[in]     Pointer to GENERIC_PHY_PRIVATE_DATA.
+  @param  Speed[in]   Speed setting.
+  @param  Duplex[in]  Duplex setting.
+
+**/
+STATIC
+VOID
+GenericPhyConfigure (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy,
+  IN GENERIC_PHY_SPEED          Speed,
+  IN GENERIC_PHY_DUPLEX         Duplex
+  )
+{
+  Phy->Configure (Phy->PrivateData, Speed, Duplex);
+}
+
+/**
+  Detect address for the first PHY seen, probing all possible addresses.
+
+  @param  Phy[in]  Pointer to GENERIC_PHY_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS       Found a PHY and programmed Phy->PhyAddr
+  @retval EFI_DEVICE_ERROR  Error reading/writing a PHY register.
+  @retval EFI_NOT_FOUND     No PHY detected.
+
+**/
+STATIC
+EFI_STATUS
+GenericPhyDetect (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       PhyAddr;
+  UINT16      Id1, Id2;
+
+  for (PhyAddr = 0; PhyAddr < 32; PhyAddr++) {
+    Status = GenericPhyRead (Phy, PhyAddr, GENERIC_PHY_PHYIDR1, &Id1);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+    Status = GenericPhyRead (Phy, PhyAddr, GENERIC_PHY_PHYIDR2, &Id2);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+    if (Id1 != 0xFFFF && Id2 != 0xFFFF) {
+      Phy->PhyAddr = PhyAddr;
+      DEBUG ((DEBUG_INFO,
+        "%a: PHY detected at address 0x%02X (PHYIDR1=0x%04X, PHYIDR2=0x%04X)\n",
+        __FUNCTION__, PhyAddr, Id1, Id2));
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Start link auto-negotiation on a PHY.
+
+  @param  Phy[in]  Pointer to GENERIC_PHY_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS       Auto-netogiation started.
+  @retval EFI_DEVICE_ERROR  PHY register read/write error.
+
+**/
+STATIC
+EFI_STATUS
+GenericPhyAutoNegotiate (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy
+  )
+{
+  EFI_STATUS    Status;
+  UINT16        Anar, Gbcr, Bmcr;
+
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_ANAR, &Anar);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Anar |= GENERIC_PHY_ANAR_100BASETX_FDX |
+          GENERIC_PHY_ANAR_100BASETX |
+          GENERIC_PHY_ANAR_10BASET_FDX |
+          GENERIC_PHY_ANAR_10BASET;
+  Status = GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_ANAR, Anar);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_GBCR, &Gbcr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Gbcr |= GENERIC_PHY_GBCR_1000BASET_FDX |
+          GENERIC_PHY_GBCR_1000BASET;
+  Status = GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_GBCR, Gbcr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR, &Bmcr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Bmcr |= GENERIC_PHY_BMCR_ANE |
+          GENERIC_PHY_BMCR_RESTART_AN;
+  return GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR, Bmcr);
+}
+
+/**
+  Initialize the first PHY detected, performing a reset and enabling
+  auto-negotiation.
+
+  @param  Phy[in]  Pointer to GENERIC_PHY_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS       Auto-negotiation started.
+  @retval EFI_DEVICE_ERROR  PHY register read/write error.
+  @retval EFI_TIMEOUT       PHY reset time-out.
+  @retval EFI_NOT_FOUND     No PHY detected.
+
+**/
+EFI_STATUS
+EFIAPI
+GenericPhyInit (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy
+  )
+{
+  EFI_STATUS    Status;
+
+  ASSERT (Phy->Read != NULL);
+  ASSERT (Phy->Write != NULL);
+  ASSERT (Phy->Configure != NULL);
+
+  Status = GenericPhyDetect (Phy);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GenericPhyReset (Phy);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return GenericPhyAutoNegotiate (Phy);
+}
+
+/**
+  Perform a PHY reset.
+
+  @param  Phy[in]  Pointer to GENERIC_PHY_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS       Auto-negotiation started.
+  @retval EFI_DEVICE_ERROR  PHY register read/write error.
+  @retval EFI_TIMEOUT       PHY reset time-out.
+
+**/
+EFI_STATUS
+EFIAPI
+GenericPhyReset (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy
+  )
+{
+  EFI_STATUS    Status;
+  UINTN         Retry;
+  UINT16        Data;
+
+  // Start reset sequence
+  Status = GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR,
+             GENERIC_PHY_BMCR_RESET);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Wait up to 500ms for it to complete
+  for (Retry = PHY_RESET_TIMEOUT; Retry > 0; Retry--) {
+    Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR, &Data);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    if ((Data & GENERIC_PHY_BMCR_RESET) == 0) {
+      break;
+    }
+    gBS->Stall (1000);
+  }
+  if (Retry == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  if (Phy->ResetAction != NULL) {
+    Phy->ResetAction (Phy->PrivateData);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Probe link status.
+
+  @param  Phy[in]  Pointer to GENERIC_PHY_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS       Link is up and auto-negotiation is complete.
+  @retval EFI_DEVICE_ERROR  PHY register read/write error,
+
+**/
+STATIC
+EFI_STATUS
+GenericPhyGetLinkStatus (
+  IN GENERIC_PHY_PRIVATE_DATA   *Phy
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Bmsr;
+
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_BMSR, &Bmsr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if ((Bmsr & GENERIC_PHY_BMSR_LINK_STATUS) == 0) {
+   return EFI_TIMEOUT;
+  }
+
+  if ((Bmsr & GENERIC_PHY_BMSR_ANEG_COMPLETE) == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Return PHY link configuration.
+
+  @param  Phy[in]      Pointer to GENERIC_PHY_PRIVATE_DATA.
+  @param  Speed[out]   Pointer to store link speed.
+  @param  Duplex[out]  Pointer to store link duplex setting.
+
+  @retval EFI_SUCCESS       Link configuration settings read.
+  @retval EFI_DEVICE_ERROR  PHY register read/write error,
+
+**/
+STATIC
+EFI_STATUS
+GenericPhyGetConfig (
+  IN  GENERIC_PHY_PRIVATE_DATA  *Phy,
+  OUT GENERIC_PHY_SPEED         *Speed,
+  OUT GENERIC_PHY_DUPLEX        *Duplex
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Gbcr, Gbsr, Anlpar, Anar;
+  UINT16      Gb, An;
+
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_GBCR, &Gbcr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_GBSR, &Gbsr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_ANLPAR, &Anlpar);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_ANAR, &Anar);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Gb = (Gbsr >> 2) & Gbcr;
+  An = Anlpar & Anar;
+
+  if ((Gb & (GENERIC_PHY_GBCR_1000BASET_FDX |
+             GENERIC_PHY_GBCR_1000BASET)) != 0) {
+    *Speed = PHY_SPEED_1000;
+    *Duplex = (Gb & GENERIC_PHY_GBCR_1000BASET_FDX) ? PHY_DUPLEX_FULL
+                                                    : PHY_DUPLEX_HALF;
+  } else if ((An & (GENERIC_PHY_ANAR_100BASETX_FDX |
+                    GENERIC_PHY_ANAR_100BASETX)) != 0) {
+    *Speed = PHY_SPEED_100;
+    *Duplex = (An & GENERIC_PHY_ANAR_100BASETX_FDX) ? PHY_DUPLEX_FULL
+                                                    : PHY_DUPLEX_HALF;
+  } else {
+    *Speed = PHY_SPEED_10;
+    *Duplex = (An & GENERIC_PHY_ANAR_10BASET_FDX) ? PHY_DUPLEX_FULL
+                                                  : PHY_DUPLEX_HALF;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: Link speed %d Mbps, %a-duplex\n",
+    __FUNCTION__, *Speed, *Duplex == PHY_DUPLEX_FULL ? "full" : "half"));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Update link status, propagating PHY link state into the MAC layer.
+
+  @param  Phy[in]  Pointer to GENERIC_PHY_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS       Link is up.
+  @retval EFI_DEVICE_ERROR  PHY register read/write error.
+  @retval EFI_NOT_READY     Link is down.
+
+**/
+EFI_STATUS
+EFIAPI
+GenericPhyUpdateConfig (
+  IN GENERIC_PHY_PRIVATE_DATA *Phy
+  )
+{
+  EFI_STATUS          Status;
+  GENERIC_PHY_SPEED   Speed;
+  GENERIC_PHY_DUPLEX  Duplex;
+  BOOLEAN             LinkUp;
+
+  Status = GenericPhyGetLinkStatus (Phy);
+  LinkUp = EFI_ERROR (Status) ? FALSE : TRUE;
+
+  if (Phy->LinkUp != LinkUp) {
+    if (LinkUp) {
+      DEBUG ((DEBUG_VERBOSE, "%a: Link is up\n", __FUNCTION__));
+
+      Status = GenericPhyGetConfig (Phy, &Speed, &Duplex);
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+
+      GenericPhyConfigure (Phy, Speed, Duplex);
+    } else {
+      DEBUG ((DEBUG_VERBOSE, "%a: Link is down\n", __FUNCTION__));
+    }
+  }
+
+  Phy->LinkUp = LinkUp;
+
+  return LinkUp ? EFI_SUCCESS : EFI_NOT_READY;
+}
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/Genet.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/Genet.c
deleted file mode 100644
index d40ce8b07a9d..000000000000
--- a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/Genet.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/** @file
-
-  Copyright (c) 2020, Jeremy Linton All rights reserved.<BR>
-
-  SPDX-License-Identifier: BSD-2-Clause-Patent
-
-  This driver acts like a stub to set the Broadcom
-  Genet MAC address, until the actual network driver
-  is in place.
-
-**/
-
-#include <Library/ArmLib.h>
-#include <Library/DebugLib.h>
-#include <Library/IoLib.h>
-#include <Library/PcdLib.h>
-#include <Library/UefiBootServicesTableLib.h>
-#include <Library/UefiLib.h>
-
-#include <Net/Genet.h>
-#include <PiDxe.h>
-
-STATIC
-VOID
-RMWRegister (
-  UINT32                Offset,
-  UINT32                Mask,
-  UINT32                In
-  )
-{
-  EFI_PHYSICAL_ADDRESS  Addr;
-  UINT32                Data;
-  UINT32                Shift;
-
-  Addr = GENET_BASE_ADDRESS + Offset;
-  Data = 0;
-  Shift = 1;
-  if (In) {
-    while (!(Mask & Shift))
-      Shift <<= 1;
-    Data = (MmioRead32 (Addr) & ~Mask) | ((In * Shift) & Mask);
-  } else {
-    Data = MmioRead32 (Addr) & ~Mask;
-  }
-
-  MmioWrite32 (Addr, Data);
-
-  ArmDataMemoryBarrier ();
-}
-
-STATIC
-VOID
-WdRegister (
-  UINT32                Offset,
-  UINT32                In
-  )
-{
-  EFI_PHYSICAL_ADDRESS  Base = GENET_BASE_ADDRESS;
-
-  MmioWrite32 (Base + Offset, In);
-
-  ArmDataMemoryBarrier ();
-}
-
-STATIC
-VOID
-SetMacAddress (
-  UINT8*                MacAddr
-)
-{
-  // Bring the UMAC out of reset
-  RMWRegister (GENET_SYS_RBUF_FLUSH_CTRL, 0x2, 1);
-  gBS->Stall (10);
-  RMWRegister (GENET_SYS_RBUF_FLUSH_CTRL, 0x2, 0);
-
-  // Update the MAC
-  DEBUG ((DEBUG_INFO, "Using MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
-    MacAddr[0], MacAddr[1], MacAddr[2], MacAddr[3], MacAddr[4], MacAddr[5]));
-
-  WdRegister (GENET_UMAC_MAC0, (MacAddr[0] << 24) | (MacAddr[1] << 16) |
-    (MacAddr[2] << 8) | MacAddr[3]);
-  WdRegister (GENET_UMAC_MAC1, (MacAddr[4] << 8) | MacAddr[5]);
-
-}
-
-/**
-  The entry point of Genet UEFI Driver.
-
-  @param  ImageHandle                The image handle of the UEFI Driver.
-  @param  SystemTable                A pointer to the EFI System Table.
-
-  @retval  EFI_SUCCESS               The Driver or UEFI Driver exited normally.
-  @retval  EFI_INCOMPATIBLE_VERSION  _gUefiDriverRevision is greater than
-                                     SystemTable->Hdr.Revision.
-
-**/
-EFI_STATUS
-EFIAPI
-GenetEntryPoint (
-  IN  EFI_HANDLE          ImageHandle,
-  IN  EFI_SYSTEM_TABLE    *SystemTable
-  )
-{
-  UINT64 MacAddr;
-
-  // Read the MAC address
-  MacAddr = PcdGet64 (PcdBcmGenetMacAddress);
-
-  if (MacAddr != 0) {
-    SetMacAddress ((UINT8*)&MacAddr);
-  }
-
-  return EFI_SUCCESS;
-}
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c
new file mode 100644
index 000000000000..4b0995e30ddf
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c
@@ -0,0 +1,814 @@
+/** @file
+
+  Copyright (c) 2020 Jared McNeill. All rights reserved.
+  Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/DmaLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "BcmGenetDxe.h"
+
+#define GENET_PHY_RETRY     1000
+
+STATIC CONST
+EFI_PHYSICAL_ADDRESS   mDmaAddressLimit = FixedPcdGet64 (PcdDmaDeviceLimit) -
+                                          FixedPcdGet64 (PcdDmaDeviceOffset);
+
+/**
+  Read a memory-mapped device CSR.
+
+  @param  Genet[in]   Pointer to GENERIC_PHY_PRIVATE_DATA instance.
+  @param  Offset[in]  Register offset.
+
+  @retval Value
+
+**/
+STATIC
+UINT32
+GenetMmioRead (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN UINT32             Offset
+  )
+{
+  ASSERT ((Offset & 3) == 0);
+
+  return MmioRead32 (Genet->RegBase + Offset);
+}
+
+/**
+  Write a memory-mapped device CSR.
+
+  @param  Genet[in]   Pointer to GENERIC_PHY_PRIVATE_DATA instance.
+  @param  Offset[in]  Register offset.
+  @param  Data[in]    Data to write.
+
+  @retval Value
+
+**/
+STATIC
+VOID
+GenetMmioWrite (
+  IN GENET_PRIVATE_DATA *Genet,
+  IN UINT32             Offset,
+  IN UINT32             Data
+  )
+{
+  ASSERT ((Offset & 3) == 0);
+
+  MemoryFence ();
+  MmioWrite32 (Genet->RegBase + Offset, Data);
+}
+
+/**
+  Perform a GENET PHY register read.
+
+  @param  Priv[in]     Pointer to GENET_PRIVATE_DATA.
+  @param  PhyAddr[in]  PHY address.
+  @param  Reg[in]      PHY register.
+  @param  Data[out]    Pointer to register data read.
+
+  @retval EFI_SUCCESS       Data read successfully.
+  @retval EFI_DEVICE_ERROR  Failed to read data.
+
+**/
+EFI_STATUS
+EFIAPI
+GenetPhyRead (
+  IN  VOID   *Priv,
+  IN  UINT8  PhyAddr,
+  IN  UINT8  Reg,
+  OUT UINT16 *Data
+  )
+{
+  GENET_PRIVATE_DATA   *Genet;
+  UINTN                Retry;
+  UINT32               Value;
+
+  Genet = Priv;
+  Value = GENET_MDIO_READ |
+          GENET_MDIO_START_BUSY |
+          SHIFTIN (PhyAddr, GENET_MDIO_PMD) |
+          SHIFTIN (Reg, GENET_MDIO_REG);
+  GenetMmioWrite (Genet, GENET_MDIO_CMD, Value);
+
+  for (Retry = GENET_PHY_RETRY; Retry > 0; Retry--) {
+    Value = GenetMmioRead (Genet, GENET_MDIO_CMD);
+    if ((Value & GENET_MDIO_START_BUSY) == 0) {
+      *Data = Value & 0xffff;
+      break;
+    }
+    gBS->Stall (10);
+  }
+
+  if (Retry == 0) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Timeout reading PhyAddr %d, Reg %d\n", __FUNCTION__, PhyAddr, Reg));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Perform a GENET PHY register write.
+
+  @param  Priv[in]     Pointer to GENET_PRIVATE_DATA.
+  @param  PhyAddr[in]  PHY address.
+  @param  Reg[in]      PHY register.
+  @param  Data[in]     Pointer to register data to write.
+
+  @retval EFI_SUCCESS       Data written successfully.
+  @retval EFI_DEVICE_ERROR  Failed to write data.
+
+**/
+EFI_STATUS
+EFIAPI
+GenetPhyWrite (
+  IN VOID   *Priv,
+  IN UINT8  PhyAddr,
+  IN UINT8  Reg,
+  IN UINT16 Data
+  )
+{
+  GENET_PRIVATE_DATA    *Genet;
+  UINTN                 Retry;
+  UINT32                Value;
+
+  Genet = Priv;
+  Value = GENET_MDIO_WRITE |
+          GENET_MDIO_START_BUSY |
+          SHIFTIN (PhyAddr, GENET_MDIO_PMD) |
+          SHIFTIN (Reg, GENET_MDIO_REG);
+  GenetMmioWrite (Genet, GENET_MDIO_CMD, Value | Data);
+
+  for (Retry = GENET_PHY_RETRY; Retry > 0; Retry--) {
+    Value = GenetMmioRead (Genet, GENET_MDIO_CMD);
+    if ((Value & GENET_MDIO_START_BUSY) == 0) {
+      break;
+    }
+    gBS->Stall (10);
+  }
+
+  if (Retry == 0) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Timeout writing PhyAddr %d, Reg %d\n", __FUNCTION__, PhyAddr, Reg));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Process a PHY link speed change (e.g. with MAC layer).
+
+  @param  Priv[in]    Pointer to GENET_PRIVATE_DATA.
+  @param  Speed[in]   Speed setting.
+  @param  Duplex[in]  Duplex setting.
+
+**/
+VOID
+EFIAPI
+GenetPhyConfigure (
+  IN VOID               *Priv,
+  IN GENERIC_PHY_SPEED  Speed,
+  IN GENERIC_PHY_DUPLEX Duplex
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  UINT32              Value;
+
+  Genet = Priv;
+  Value = GenetMmioRead (Genet, GENET_EXT_RGMII_OOB_CTRL);
+  Value &= ~GENET_EXT_RGMII_OOB_OOB_DISABLE;
+  Value |= GENET_EXT_RGMII_OOB_RGMII_LINK;
+  Value |= GENET_EXT_RGMII_OOB_RGMII_MODE_EN;
+  if (Genet->PhyMode == GENET_PHY_MODE_RGMII) {
+    Value |= GENET_EXT_RGMII_OOB_ID_MODE_DISABLE;
+  } else {
+    Value &= ~GENET_EXT_RGMII_OOB_ID_MODE_DISABLE;
+  }
+  GenetMmioWrite (Genet, GENET_EXT_RGMII_OOB_CTRL, Value);
+
+  Value = GenetMmioRead (Genet, GENET_UMAC_CMD);
+  Value &= ~GENET_UMAC_CMD_SPEED;
+  switch (Speed) {
+    case PHY_SPEED_1000:
+      Value |= SHIFTIN (GENET_UMAC_CMD_SPEED_1000, GENET_UMAC_CMD_SPEED);
+      break;
+    case PHY_SPEED_100:
+      Value |= SHIFTIN (GENET_UMAC_CMD_SPEED_100, GENET_UMAC_CMD_SPEED);
+      break;
+    default:
+      Value |= SHIFTIN (GENET_UMAC_CMD_SPEED_10, GENET_UMAC_CMD_SPEED);
+      break;
+  }
+  if (Duplex == PHY_DUPLEX_FULL) {
+    Value &= ~GENET_UMAC_CMD_HD_EN;
+  } else {
+    Value |= GENET_UMAC_CMD_HD_EN;
+  }
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, Value);
+}
+
+/**
+  Extra action to run after a PHY reset. This adds the appropriate clock
+  delay based on the PHY mode.
+
+  @param  Priv[in]  Pointer to GENET_PRIVATE_DATA.
+
+**/
+EFI_STATUS
+EFIAPI
+GenetPhyResetAction (
+  IN VOID             *Priv
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  UINT16              Value;
+  EFI_STATUS          Status;
+
+  Genet = Priv;
+  Status = GenetPhyWrite (Priv, Genet->Phy.PhyAddr, BRGPHY_MII_AUXCTL,
+             BRGPHY_AUXCTL_SHADOW_MISC |
+             (BRGPHY_AUXCTL_SHADOW_MISC << BRGPHY_AUXCTL_MISC_READ_SHIFT));
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GenetPhyRead (Priv, Genet->Phy.PhyAddr, BRGPHY_MII_AUXCTL, &Value);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Value &= BRGPHY_AUXCTL_MISC_DATA_MASK;
+
+  if (Genet->PhyMode == GENET_PHY_MODE_RGMII_RXID ||
+      Genet->PhyMode == GENET_PHY_MODE_RGMII_ID) {
+    Value |= BRGPHY_AUXCTL_MISC_RGMII_SKEW_EN;
+  } else {
+    Value &= ~BRGPHY_AUXCTL_MISC_RGMII_SKEW_EN;
+  }
+
+  Status = GenetPhyWrite (Priv, Genet->Phy.PhyAddr, BRGPHY_MII_AUXCTL,
+             BRGPHY_AUXCTL_MISC_WRITE_EN | BRGPHY_AUXCTL_SHADOW_MISC | Value);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GenetPhyWrite (Priv, Genet->Phy.PhyAddr, BRGPHY_MII_SHADOW_1C,
+                          BRGPHY_SHADOW_1C_CLK_CTRL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GenetPhyRead (Priv, Genet->Phy.PhyAddr, BRGPHY_MII_SHADOW_1C, &Value);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Value &= BRGPHY_SHADOW_1C_DATA_MASK;
+
+  if (Genet->PhyMode == GENET_PHY_MODE_RGMII_TXID ||
+      Genet->PhyMode == GENET_PHY_MODE_RGMII_ID) {
+    Value |= BRGPHY_SHADOW_1C_GTXCLK_EN;
+  } else {
+    Value &= ~BRGPHY_SHADOW_1C_GTXCLK_EN;
+  }
+
+  Status = GenetPhyWrite (Priv, Genet->Phy.PhyAddr, BRGPHY_MII_SHADOW_1C,
+             BRGPHY_SHADOW_1C_WRITE_EN | BRGPHY_SHADOW_1C_CLK_CTRL | Value);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Reset GENET.
+
+  @param  Genet[in]  Pointer to GENET_PRIVATE_DATA.
+
+**/
+VOID
+GenetReset (
+  IN GENET_PRIVATE_DATA   *Genet
+  )
+{
+  UINT32  Value;
+
+  Value = GenetMmioRead (Genet, GENET_SYS_RBUF_FLUSH_CTRL);
+  Value |= GENET_SYS_RBUF_FLUSH_RESET;
+  GenetMmioWrite (Genet, GENET_SYS_RBUF_FLUSH_CTRL, Value);
+  gBS->Stall (10);
+
+  Value &= ~GENET_SYS_RBUF_FLUSH_RESET;
+  GenetMmioWrite (Genet, GENET_SYS_RBUF_FLUSH_CTRL, Value);
+  gBS->Stall (10);
+
+  GenetMmioWrite (Genet, GENET_SYS_RBUF_FLUSH_CTRL, 0);
+  gBS->Stall (10);
+
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, 0);
+  GenetMmioWrite (Genet, GENET_UMAC_CMD,
+    GENET_UMAC_CMD_LCL_LOOP_EN | GENET_UMAC_CMD_SW_RESET);
+  gBS->Stall (10);
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, 0);
+
+  GenetMmioWrite (Genet, GENET_UMAC_MIB_CTRL,
+    GENET_UMAC_MIB_RESET_RUNT | GENET_UMAC_MIB_RESET_RX | GENET_UMAC_MIB_RESET_TX);
+  GenetMmioWrite (Genet, GENET_UMAC_MIB_CTRL, 0);
+
+  GenetMmioWrite (Genet, GENET_UMAC_MAX_FRAME_LEN, GENET_MAX_PACKET_SIZE);
+
+  Value = GenetMmioRead (Genet, GENET_RBUF_CTRL);
+  Value |= GENET_RBUF_ALIGN_2B;
+  GenetMmioWrite (Genet, GENET_RBUF_CTRL, Value);
+
+  GenetMmioWrite (Genet, GENET_RBUF_TBUF_SIZE_CTRL, 1);
+}
+
+/**
+  Set the station address.
+
+  @param  Genet[in]    Pointer to GENET_PRIVATE_DATA.
+  @param  MacAddr[in]  MAC address to set.
+
+**/
+VOID
+EFIAPI
+GenetSetMacAddress (
+  IN GENET_PRIVATE_DATA   *Genet,
+  IN EFI_MAC_ADDRESS      *MacAddr
+  )
+{
+  UINT32  Value;
+
+  Value = MacAddr->Addr[3] |
+          MacAddr->Addr[2] << 8 |
+          MacAddr->Addr[1] << 16 |
+          MacAddr->Addr[0] << 24;
+  GenetMmioWrite (Genet, GENET_UMAC_MAC0, Value);
+  Value = MacAddr->Addr[5] |
+          MacAddr->Addr[4] << 8;
+  GenetMmioWrite (Genet, GENET_UMAC_MAC1, Value);
+}
+
+/**
+  Set a PHY mode.
+
+  @param  Genet[in]    Pointer to GENET_PRIVATE_DATA.
+  @param  PhyMode[in]  Mode to set.
+
+**/
+VOID
+GenetSetPhyMode (
+  IN GENET_PRIVATE_DATA   *Genet,
+  IN GENET_PHY_MODE       PhyMode
+  )
+{
+  UINT32  Value;
+
+  switch (PhyMode) {
+    case GENET_PHY_MODE_RGMII:
+    case GENET_PHY_MODE_RGMII_RXID:
+    case GENET_PHY_MODE_RGMII_TXID:
+    case GENET_PHY_MODE_RGMII_ID:
+      Value = GENET_SYS_PORT_MODE_EXT_GPHY;
+      break;
+    default:
+      Value = 0;
+      break;
+  }
+  GenetMmioWrite (Genet, GENET_SYS_PORT_CTRL, Value);
+}
+
+/**
+  Enable TX/RX.
+
+  @param  Genet[in]  Pointer to GENET_PRIVATE_DATA.
+
+**/
+VOID
+GenetEnableTxRx (
+  IN GENET_PRIVATE_DATA   *Genet
+  )
+{
+  UINT32 Value;
+
+  // Start TX DMA on default queue
+  Value = GenetMmioRead (Genet, GENET_TX_DMA_CTRL);
+  Value |= GENET_TX_DMA_CTRL_EN |
+           GENET_TX_DMA_CTRL_RBUF_EN (GENET_DMA_DEFAULT_QUEUE);
+  GenetMmioWrite (Genet, GENET_TX_DMA_CTRL, Value);
+
+  // Start RX DMA on default queue
+  Value = GenetMmioRead (Genet, GENET_RX_DMA_CTRL);
+  Value |= GENET_RX_DMA_CTRL_EN |
+           GENET_RX_DMA_CTRL_RBUF_EN (GENET_DMA_DEFAULT_QUEUE);
+  GenetMmioWrite (Genet, GENET_RX_DMA_CTRL, Value);
+
+  // Enable transmitter and receiver
+  Value = GenetMmioRead (Genet, GENET_UMAC_CMD);
+  Value |= GENET_UMAC_CMD_TXEN | GENET_UMAC_CMD_RXEN;
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, Value);
+
+  // Enable interrupts
+  GenetMmioWrite (Genet, GENET_INTRL2_CPU_CLEAR_MASK,
+    GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE);
+}
+
+/**
+  Disable TX/RX.
+
+  @param  Genet[in]  Pointer to GENET_PRIVATE_DATA.
+
+**/
+VOID
+GenetDisableTxRx (
+  IN GENET_PRIVATE_DATA   *Genet
+  )
+{
+  UINT32  Value;
+
+  // Disable interrupts
+  GenetMmioWrite (Genet, GENET_INTRL2_CPU_SET_MASK, 0xFFFFFFFF);
+  GenetMmioWrite (Genet, GENET_INTRL2_CPU_CLEAR, 0xFFFFFFFF);
+
+  // Disable receiver
+  Value = GenetMmioRead (Genet, GENET_UMAC_CMD);
+  Value &= ~GENET_UMAC_CMD_RXEN;
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, Value);
+
+  // Stop RX DMA
+  Value = GenetMmioRead (Genet, GENET_RX_DMA_CTRL);
+  Value &= ~GENET_RX_DMA_CTRL_EN;
+  Value &= ~GENET_RX_DMA_CTRL_RBUF_EN (GENET_DMA_DEFAULT_QUEUE);
+  GenetMmioWrite (Genet, GENET_RX_DMA_CTRL, Value);
+
+  // Stop TX DMA
+  Value = GenetMmioRead (Genet, GENET_TX_DMA_CTRL);
+  Value &= ~GENET_TX_DMA_CTRL_EN;
+  Value &= ~GENET_TX_DMA_CTRL_RBUF_EN (GENET_DMA_DEFAULT_QUEUE);
+  GenetMmioWrite (Genet, GENET_TX_DMA_CTRL, Value);
+
+  // Flush data in the TX FIFO
+  GenetMmioWrite (Genet, GENET_UMAC_TX_FLUSH, 1);
+  gBS->Stall (10);
+  GenetMmioWrite (Genet, GENET_UMAC_TX_FLUSH, 0);
+
+  // Disable transmitter
+  Value = GenetMmioRead (Genet, GENET_UMAC_CMD);
+  Value &= ~GENET_UMAC_CMD_TXEN;
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, Value);
+}
+
+/**
+  Change promiscuous mode state.
+
+  @param  Genet[in]   Pointer to GENET_PRIVATE_DATA.
+  @param  Enable[in]  Promiscuous mode state.
+
+**/
+VOID
+GenetSetPromisc (
+  IN GENET_PRIVATE_DATA   *Genet,
+  IN BOOLEAN              Enable
+  )
+{
+  UINT32 Value;
+
+  Value = GenetMmioRead (Genet, GENET_UMAC_CMD);
+  if (Enable) {
+    Value |= GENET_UMAC_CMD_PROMISC;
+  } else {
+    Value &= ~GENET_UMAC_CMD_PROMISC;
+  }
+  GenetMmioWrite (Genet, GENET_UMAC_CMD, Value);
+}
+
+/**
+  Enable the MAC filter for the Ethernet broadcast address
+
+  @param  Genet[in]   Pointer to GENET_PRIVATE_DATA.
+  @param  Enable[in]  Promiscuous mode state.
+
+**/
+VOID
+GenetEnableBroadcastFilter (
+  IN GENET_PRIVATE_DATA   *Genet,
+  IN BOOLEAN              Enable
+  )
+{
+  CONST EFI_MAC_ADDRESS   *MacAddr;
+  UINT32                  Value;
+
+  if (Enable) {
+    MacAddr = &Genet->SnpMode.CurrentAddress;
+
+    GenetMmioWrite (Genet, GENET_UMAC_MDF_ADDR0 (0),
+      MacAddr->Addr[1] | MacAddr->Addr[0] << 8);
+    GenetMmioWrite (Genet, GENET_UMAC_MDF_ADDR1 (0),
+      MacAddr->Addr[5] | MacAddr->Addr[4] << 8 |
+      MacAddr->Addr[3] << 16 | MacAddr->Addr[2] << 24);
+
+    GenetMmioWrite (Genet, GENET_UMAC_MDF_ADDR0 (1), 0xffff);
+    GenetMmioWrite (Genet, GENET_UMAC_MDF_ADDR1 (1), 0xffffffff);
+
+    Value = BIT16 | BIT15; // enable filters 0 and 1
+  } else {
+    Value = 0;
+  }
+  GenetMmioWrite (Genet, GENET_UMAC_MDF_CTRL, Value);
+}
+
+/**
+  Configure DMA TX and RX queues, enabling them.
+
+  @param  Genet[in]  Pointer to GENET_PRIVATE_DATA.
+
+**/
+VOID
+GenetDmaInitRings (
+  IN GENET_PRIVATE_DATA *Genet
+  )
+{
+  UINT8 Qid;
+
+  Qid = GENET_DMA_DEFAULT_QUEUE;
+
+  Genet->TxQueued = 0;
+  Genet->TxNext = 0;
+  Genet->TxConsIndex = 0;
+  Genet->TxProdIndex = 0;
+
+  Genet->RxConsIndex = 0;
+  Genet->RxProdIndex = 0;
+
+  // Configure TX queue
+  GenetMmioWrite (Genet, GENET_TX_SCB_BURST_SIZE, 0x08);
+  GenetMmioWrite (Genet, GENET_TX_DMA_READ_PTR_LO (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_READ_PTR_HI (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_CONS_INDEX (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_PROD_INDEX (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_RING_BUF_SIZE (Qid),
+    SHIFTIN (GENET_DMA_DESC_COUNT, GENET_TX_DMA_RING_BUF_SIZE_DESC_COUNT) |
+    SHIFTIN (GENET_MAX_PACKET_SIZE, GENET_TX_DMA_RING_BUF_SIZE_BUF_LENGTH));
+  GenetMmioWrite (Genet, GENET_TX_DMA_START_ADDR_LO (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_START_ADDR_HI (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_END_ADDR_LO (Qid),
+    GENET_DMA_DESC_COUNT * GENET_DMA_DESC_SIZE / 4 - 1);
+  GenetMmioWrite (Genet, GENET_TX_DMA_END_ADDR_HI (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_MBUF_DONE_THRES (Qid), 1);
+  GenetMmioWrite (Genet, GENET_TX_DMA_FLOW_PERIOD (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_WRITE_PTR_LO (Qid), 0);
+  GenetMmioWrite (Genet, GENET_TX_DMA_WRITE_PTR_HI (Qid), 0);
+
+  // Enable TX queue
+  GenetMmioWrite (Genet, GENET_TX_DMA_RING_CFG, (1U << Qid));
+
+  // Configure RX queue
+  GenetMmioWrite (Genet, GENET_RX_SCB_BURST_SIZE, 0x08);
+  GenetMmioWrite (Genet, GENET_RX_DMA_WRITE_PTR_LO (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_WRITE_PTR_HI (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_PROD_INDEX (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_CONS_INDEX (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_RING_BUF_SIZE (Qid),
+    SHIFTIN (GENET_DMA_DESC_COUNT, GENET_RX_DMA_RING_BUF_SIZE_DESC_COUNT) |
+    SHIFTIN (GENET_MAX_PACKET_SIZE, GENET_RX_DMA_RING_BUF_SIZE_BUF_LENGTH));
+  GenetMmioWrite (Genet, GENET_RX_DMA_START_ADDR_LO (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_START_ADDR_HI (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_END_ADDR_LO (Qid),
+    GENET_DMA_DESC_COUNT * GENET_DMA_DESC_SIZE / 4 - 1);
+  GenetMmioWrite (Genet, GENET_RX_DMA_END_ADDR_HI (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_XON_XOFF_THRES (Qid),
+    SHIFTIN (5, GENET_RX_DMA_XON_XOFF_THRES_LO) |
+    SHIFTIN (GENET_DMA_DESC_COUNT >> 4, GENET_RX_DMA_XON_XOFF_THRES_HI));
+  GenetMmioWrite (Genet, GENET_RX_DMA_READ_PTR_LO (Qid), 0);
+  GenetMmioWrite (Genet, GENET_RX_DMA_READ_PTR_HI (Qid), 0);
+
+  // Enable RX queue
+  GenetMmioWrite (Genet, GENET_RX_DMA_RING_CFG, (1U << Qid));
+}
+
+/**
+  Allocate DMA buffers for RX.
+
+  @param  Genet[in]  Pointer to GENET_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS           DMA buffers allocated.
+  @retval EFI_OUT_OF_RESOURCES  DMA buffers could not be allocated.
+**/
+EFI_STATUS
+GenetDmaAlloc (
+  IN GENET_PRIVATE_DATA   *Genet
+  )
+{
+  EFI_STATUS              Status;
+
+  Genet->RxBuffer = mDmaAddressLimit;
+  Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData,
+                  EFI_SIZE_TO_PAGES (GENET_MAX_PACKET_SIZE * GENET_DMA_DESC_COUNT),
+                  &Genet->RxBuffer);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Failed to allocate RX buffer: %r\n", __FUNCTION__, Status));
+  }
+  return Status;
+}
+
+/**
+  Given an RX buffer descriptor index, program the IO address of the buffer into the hardware.
+
+  @param  Genet[in]      Pointer to GENET_PRIVATE_DATA.
+  @param  DescIndex[in]  Index of RX buffer descriptor.
+
+  @retval EFI_SUCCESS  DMA buffers allocated.
+  @retval Others       Programmatic errors, as buffers come from DmaAllocateBuffer, and thus
+                       cannot fail DmaMap (for the expected NonCoherentDmaLib).
+**/
+EFI_STATUS
+GenetDmaMapRxDescriptor (
+  IN GENET_PRIVATE_DATA * Genet,
+  IN UINT8                DescIndex
+  )
+{
+  EFI_STATUS    Status;
+  UINTN         DmaNumberOfBytes;
+
+  ASSERT (Genet->RxBufferMap[DescIndex].Mapping == NULL);
+  ASSERT (Genet->RxBuffer != 0);
+
+  DmaNumberOfBytes = GENET_MAX_PACKET_SIZE;
+  Status = DmaMap (MapOperationBusMasterWrite,
+             GENET_RX_BUFFER (Genet, DescIndex),
+             &DmaNumberOfBytes,
+             &Genet->RxBufferMap[DescIndex].PhysAddress,
+             &Genet->RxBufferMap[DescIndex].Mapping);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Failed to map RX buffer: %r\n",
+      __FUNCTION__, Status));
+    return Status;
+  }
+
+  GenetMmioWrite (Genet, GENET_RX_DESC_ADDRESS_LO (DescIndex),
+    Genet->RxBufferMap[DescIndex].PhysAddress & 0xFFFFFFFF);
+  GenetMmioWrite (Genet, GENET_RX_DESC_ADDRESS_HI (DescIndex),
+    (Genet->RxBufferMap[DescIndex].PhysAddress >> 32) & 0xFFFFFFFF);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Given an RX buffer descriptor index, undo the DmaMap operation on the buffer.
+
+  @param  Genet[in]      Pointer to GENET_PRIVATE_DATA.
+  @param  DescIndex[in]  Index of RX buffer descriptor.
+
+**/
+VOID
+GenetDmaUnmapRxDescriptor (
+  IN GENET_PRIVATE_DATA * Genet,
+  IN UINT8                DescIndex
+  )
+{
+  if (Genet->RxBufferMap[DescIndex].Mapping != NULL) {
+    DmaUnmap (Genet->RxBufferMap[DescIndex].Mapping);
+    Genet->RxBufferMap[DescIndex].Mapping = NULL;
+  }
+}
+
+/**
+  Free DMA buffers for RX, undoing GenetDmaAlloc.
+
+  @param  Genet[in]      Pointer to GENET_PRIVATE_DATA.
+  @param  DescIndex[in]  Index of RX buffer descriptor.
+
+**/
+VOID
+GenetDmaFree (
+  IN GENET_PRIVATE_DATA *Genet
+  )
+{
+  UINTN Idx;
+
+  for (Idx = 0; Idx < GENET_DMA_DESC_COUNT; Idx++) {
+    GenetDmaUnmapRxDescriptor (Genet, Idx);
+  }
+  gBS->FreePages (Genet->RxBuffer,
+         EFI_SIZE_TO_PAGES (GENET_MAX_PACKET_SIZE * GENET_DMA_DESC_COUNT));
+}
+
+/**
+  Queue TX transmission, given a buffer to transmit and a TX descriptor index.
+
+  @param  Genet[in]          Pointer to GENET_PRIVATE_DATA.
+  @param  DescIndex[in]      TX descriptor index.
+  @param  PhysAddr[in]       Buffer to transmit.
+  @param  NumberOfBytes[in]  Buffer length.
+
+**/
+VOID
+GenetDmaTriggerTx (
+  IN GENET_PRIVATE_DATA * Genet,
+  IN UINT8                DescIndex,
+  IN EFI_PHYSICAL_ADDRESS PhysAddr,
+  IN UINTN                NumberOfBytes
+  )
+{
+  UINT32    DescStatus;
+
+  DescStatus = GENET_TX_DESC_STATUS_SOP |
+               GENET_TX_DESC_STATUS_EOP |
+               GENET_TX_DESC_STATUS_CRC |
+               GENET_TX_DESC_STATUS_QTAG |
+               SHIFTIN (NumberOfBytes, GENET_TX_DESC_STATUS_BUFLEN);
+
+  GenetMmioWrite (Genet, GENET_TX_DESC_ADDRESS_LO (DescIndex),
+    PhysAddr & 0xFFFFFFFF);
+  GenetMmioWrite (Genet, GENET_TX_DESC_ADDRESS_HI (DescIndex),
+    (PhysAddr >> 32) & 0xFFFFFFFF);
+  GenetMmioWrite (Genet, GENET_TX_DESC_STATUS (DescIndex), DescStatus);
+
+  GenetMmioWrite (Genet, GENET_TX_DMA_PROD_INDEX (GENET_DMA_DEFAULT_QUEUE),
+    (DescIndex + 1) & 0xFFFF);
+}
+
+/**
+  Simulate a "TX interrupt", return the next (completed) TX buffer to recycle.
+
+  @param  Genet[in]   Pointer to GENET_PRIVATE_DATA.
+  @param  TxBuf[out]  Location to store pointer to next TX buffer to recycle.
+
+**/
+VOID
+GenetTxIntr (
+  IN  GENET_PRIVATE_DATA *Genet,
+  OUT VOID               **TxBuf
+  )
+{
+  UINT32  ConsIndex, Total;
+
+  ConsIndex = GenetMmioRead (Genet,
+                GENET_TX_DMA_CONS_INDEX (GENET_DMA_DEFAULT_QUEUE)) & 0xFFFF;
+
+  Total = (ConsIndex - Genet->TxConsIndex) & 0xFFFF;
+  if (Genet->TxQueued > 0 && Total > 0) {
+    DmaUnmap (Genet->TxBufferMap[Genet->TxNext]);
+    *TxBuf = Genet->TxBuffer[Genet->TxNext];
+    Genet->TxQueued--;
+    Genet->TxNext = (Genet->TxNext + 1) % GENET_DMA_DESC_COUNT;
+    Genet->TxConsIndex++;
+  } else {
+    *TxBuf = NULL;
+  }
+}
+
+/**
+  Simulate an "RX interrupt", returning the index of a completed RX buffer and
+  corresponding frame length.
+
+  @param  Genet[in]         Pointer to GENET_PRIVATE_DATA.
+  @param  DescIndex[out]    Location to store completed RX buffer index.
+  @param  FrameLength[out]  Location to store frame length.
+
+  @retval EFI_SUCCESS    Data received.
+  @retval EFI_NOT_READY  No RX buffers ready as no data received.
+
+**/
+EFI_STATUS
+GenetRxIntr (
+  IN  GENET_PRIVATE_DATA *Genet,
+  OUT UINT8              *DescIndex,
+  OUT UINTN              *FrameLength
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        ProdIndex, Total;
+  UINT32        DescStatus;
+
+  ProdIndex = GenetMmioRead (Genet,
+                GENET_RX_DMA_PROD_INDEX (GENET_DMA_DEFAULT_QUEUE)) & 0xFFFF;
+
+  Total = (ProdIndex - Genet->RxConsIndex) & 0xFFFF;
+  if (Total > 0) {
+    *DescIndex = Genet->RxConsIndex % GENET_DMA_DESC_COUNT;
+    DescStatus = GenetMmioRead (Genet, GENET_RX_DESC_STATUS (*DescIndex));
+    *FrameLength = SHIFTOUT (DescStatus, GENET_RX_DESC_STATUS_BUFLEN);
+
+    Genet->RxConsIndex = (Genet->RxConsIndex + 1) & 0xFFFF;
+    GenetMmioWrite (Genet, GENET_RX_DMA_CONS_INDEX (GENET_DMA_DEFAULT_QUEUE),
+      Genet->RxConsIndex);
+    Status = EFI_SUCCESS;
+  } else {
+    Status = EFI_NOT_READY;
+  }
+
+  return Status;
+}
diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c
new file mode 100644
index 000000000000..74677927bef7
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c
@@ -0,0 +1,838 @@
+/** @file
+  Provides the Simple Network functions.
+
+  Copyright (c) 2020 Jared McNeill. All rights reserved.
+  Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DmaLib.h>
+#include <Library/NetLib.h>
+#include <Protocol/SimpleNetwork.h>
+
+#include "BcmGenetDxe.h"
+
+/**
+  Changes the state of a network interface from "stopped" to "started".
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS           The network interface was started.
+  @retval EFI_ALREADY_STARTED   The network interface is already in the started state.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network interface is not in the right (stopped) state.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkStart (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStarted) {
+    return EFI_ALREADY_STARTED;
+  } else if (Genet->SnpMode.State != EfiSimpleNetworkStopped) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Genet->SnpMode.State = EfiSimpleNetworkStarted;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Changes the state of a network interface from "started" to "stopped".
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS           The network interface was stopped.
+  @retval EFI_NOT_STARTED       The network interface is already in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network interface is not in the right (started) state.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkStop (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  } else if (Genet->SnpMode.State != EfiSimpleNetworkStarted) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  GenetDisableTxRx (Genet);
+
+  Genet->SnpMode.State = EfiSimpleNetworkStopped;
+  return EFI_SUCCESS;
+}
+
+/**
+  Resets a network adapter and allocates the transmit and receive buffers
+  required by the network interface; optionally, also requests allocation
+  of additional transmit and receive buffers.
+
+  @param  This              The protocol instance pointer.
+  @param  ExtraRxBufferSize The size, in bytes, of the extra receive buffer space
+                            that the driver should allocate for the network interface.
+                            Some network interfaces will not be able to use the extra
+                            buffer, and the caller will not know if it is actually
+                            being used.
+  @param  ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space
+                            that the driver should allocate for the network interface.
+                            Some network interfaces will not be able to use the extra
+                            buffer, and the caller will not know if it is actually
+                            being used.
+
+  @retval EFI_SUCCESS           The network interface was initialized.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_OUT_OF_RESOURCES  There was not enough memory for the transmit and
+                                receive buffers.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (started) state.
+  @retval EFI_DEVICE_ERROR      PHY register read/write error.
+  @retval EFI_TIMEOUT           PHY reset time-out.
+  @retval EFI_NOT_FOUND         No PHY detected.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkInitialize (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN UINTN                       ExtraRxBufferSize, OPTIONAL
+  IN UINTN                       ExtraTxBufferSize  OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  EFI_STATUS          Status;
+  UINTN               Idx;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  } else if (Genet->SnpMode.State != EfiSimpleNetworkStarted) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  GenetReset (Genet);
+  GenetSetPhyMode (Genet, Genet->PhyMode);
+
+  Status = GenericPhyInit (&Genet->Phy);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  GenetSetMacAddress (Genet, &Genet->SnpMode.CurrentAddress);
+
+  GenetDmaInitRings (Genet);
+
+  // Map RX buffers
+  for (Idx = 0; Idx < GENET_DMA_DESC_COUNT; Idx++) {
+    Status = GenetDmaMapRxDescriptor (Genet, Idx);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  GenetEnableTxRx (Genet);
+
+  Genet->SnpMode.State = EfiSimpleNetworkInitialized;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Resets a network adapter and re-initializes it with the parameters that were
+  provided in the previous call to Initialize().
+
+  @param  This                 The protocol instance pointer.
+  @param  ExtendedVerification Indicates that the driver may perform a more
+                               exhaustive verification operation of the device
+                               during reset.
+
+  @retval EFI_SUCCESS           The network interface was reset.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkReset (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN BOOLEAN                     ExtendedVerification
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  EFI_STATUS          Status;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = GenericPhyReset (&Genet->Phy);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Resets a network adapter and leaves it in a state that is safe for
+  another driver to initialize.
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS           The network interface was shutdown.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkShutdown (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  UINTN               Idx;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  GenetDisableTxRx (Genet);
+
+  for (Idx = 0; Idx < GENET_DMA_DESC_COUNT; Idx++) {
+    GenetDmaUnmapRxDescriptor (Genet, Idx);
+  }
+
+  Genet->SnpMode.State = EfiSimpleNetworkStarted;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Manages the receive filters of a network interface.
+
+  @param  This             The protocol instance pointer.
+  @param  Enable           A bit mask of receive filters to enable on the network interface.
+  @param  Disable          A bit mask of receive filters to disable on the network interface.
+  @param  ResetMCastFilter Set to TRUE to reset the contents of the multicast receive
+                           filters on the network interface to their default values.
+  @param  McastFilterCnt   Number of multicast HW MAC addresses in the new
+                           MCastFilter list. This value must be less than or equal to
+                           the MCastFilterCnt field of EFI_SIMPLE_NETWORK_MODE. This
+                           field is optional if ResetMCastFilter is TRUE.
+  @param  MCastFilter      A pointer to a list of new multicast receive filter HW MAC
+                           addresses. This list will replace any existing multicast
+                           HW MAC address list. This field is optional if
+                           ResetMCastFilter is TRUE.
+
+  @retval EFI_SUCCESS           The multicast receive filter list was updated.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkReceiveFilters (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN UINT32                      Enable,
+  IN UINT32                      Disable,
+  IN BOOLEAN                     ResetMCastFilter,
+  IN UINTN                       MCastFilterCnt, OPTIONAL
+  IN EFI_MAC_ADDRESS             *MCastFilter    OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (((Enable | Disable) & ~Genet->SnpMode.ReceiveFilterMask) != 0 ||
+      (!ResetMCastFilter && MCastFilterCnt > Genet->SnpMode.MaxMCastFilterCount)) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  GenetEnableBroadcastFilter (Genet,
+    (Enable & ~Disable & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0);
+
+  GenetSetPromisc (Genet,
+    (Enable & ~Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Modifies or resets the current station address, if supported.
+
+  @param  This  The protocol instance pointer.
+  @param  Reset Flag used to reset the station address to the network interfaces
+                permanent address.
+  @param  New   The new station address to be used for the network interface.
+
+  @retval EFI_SUCCESS           The network interfaces station address was updated.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkStationAddress (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN BOOLEAN                     Reset,
+  IN EFI_MAC_ADDRESS             *New    OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+
+  if (This == NULL || This->Mode == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (Reset == TRUE && New == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (Reset) {
+    // Use permanent address
+    CopyMem (&This->Mode->CurrentAddress, &This->Mode->PermanentAddress,
+      sizeof (This->Mode->CurrentAddress));
+  } else {
+    // Use specified address
+    CopyMem (&This->Mode->CurrentAddress, New,
+      sizeof (This->Mode->CurrentAddress));
+  }
+
+  GenetSetMacAddress (Genet, &Genet->SnpMode.CurrentAddress);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Resets or collects the statistics on a network interface.
+
+  @param  This            Protocol instance pointer.
+  @param  Reset           Set to TRUE to reset the statistics for the network interface.
+  @param  StatisticsSize  On input the size, in bytes, of StatisticsTable. On
+                          output the size, in bytes, of the resulting table of
+                          statistics.
+  @param  StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+                          contains the statistics.
+
+  @retval EFI_SUCCESS           The statistics were collected from the network interface.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_BUFFER_TOO_SMALL  The Statistics buffer was too small. The current buffer
+                                size needed to hold the statistics is returned in
+                                StatisticsSize.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkStatistics (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN BOOLEAN                     Reset,
+  IN OUT UINTN                   *StatisticsSize, OPTIONAL
+  OUT EFI_NETWORK_STATISTICS     *StatisticsTable OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Performs read and write operations on the NVRAM device attached to a
+  network interface.
+
+  @param  This       The protocol instance pointer.
+  @param  ReadWrite  TRUE for read operations, FALSE for write operations.
+  @param  Offset     Byte offset in the NVRAM device at which to start the read or
+                     write operation. This must be a multiple of NvRamAccessSize and
+                     less than NvRamSize.
+  @param  BufferSize The number of bytes to read or write from the NVRAM device.
+                     This must also be a multiple of NvramAccessSize.
+  @param  Buffer     A pointer to the data buffer.
+
+  @retval EFI_SUCCESS           The NVRAM access was performed.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkNvData (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN BOOLEAN                     ReadWrite,
+  IN UINTN                       Offset,
+  IN UINTN                       BufferSize,
+  IN OUT VOID                    *Buffer
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Reads the current interrupt status and recycled transmit buffer status from
+  a network interface.
+
+  @param  This            The protocol instance pointer.
+  @param  InterruptStatus A pointer to the bit mask of the currently active interrupts
+                          If this is NULL, the interrupt status will not be read from
+                          the device. If this is not NULL, the interrupt status will
+                          be read from the device. When the  interrupt status is read,
+                          it will also be cleared. Clearing the transmit  interrupt
+                          does not empty the recycled transmit buffer array.
+  @param  TxBuf           Recycled transmit buffer address. The network interface will
+                          not transmit if its internal recycled transmit buffer array
+                          is full. Reading the transmit buffer does not clear the
+                          transmit interrupt. If this is NULL, then the transmit buffer
+                          status will not be read. If there are no transmit buffers to
+                          recycle and TxBuf is not NULL, * TxBuf will be set to NULL.
+
+  @retval EFI_SUCCESS           The status of the network interface was retrieved.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkGetStatus (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  OUT UINT32                     *InterruptStatus, OPTIONAL
+  OUT VOID                       **TxBuf           OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  EFI_STATUS          Status;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = GenericPhyUpdateConfig (&Genet->Phy);
+  if (EFI_ERROR (Status)) {
+    Genet->SnpMode.MediaPresent = FALSE;
+  } else {
+    Genet->SnpMode.MediaPresent = TRUE;
+
+    if (TxBuf != NULL) {
+      GenetTxIntr (Genet, TxBuf);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Places a packet in the transmit queue of a network interface.
+
+  @param  This       The protocol instance pointer.
+  @param  HeaderSize The size, in bytes, of the media header to be filled in by
+                     the Transmit() function. If HeaderSize is non-zero, then it
+                     must be equal to This->Mode->MediaHeaderSize and the DestAddr
+                     and Protocol parameters must not be NULL.
+  @param  BufferSize The size, in bytes, of the entire packet (media header and
+                     data) to be transmitted through the network interface.
+  @param  Buffer     A pointer to the packet (media header followed by data) to be
+                     transmitted. This parameter cannot be NULL. If HeaderSize is zero,
+                     then the media header in Buffer must already be filled in by the
+                     caller. If HeaderSize is non-zero, then the media header will be
+                     filled in by the Transmit() function.
+  @param  SrcAddr    The source HW MAC address. If HeaderSize is zero, then this parameter
+                     is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then
+                     This->Mode->CurrentAddress is used for the source HW MAC address.
+  @param  DestAddr   The destination HW MAC address. If HeaderSize is zero, then this
+                     parameter is ignored.
+  @param  Protocol   The type of header to build. If HeaderSize is zero, then this
+                     parameter is ignored. See RFC 1700, section "Ether Types", for
+                     examples.
+
+  @retval EFI_SUCCESS           The packet was placed on the transmit queue.
+  @retval EFI_NOT_STARTED       The network interface has not been started.
+  @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
+  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkTransmit (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  IN UINTN                       HeaderSize,
+  IN UINTN                       BufferSize,
+  IN VOID                        *Buffer,
+  IN EFI_MAC_ADDRESS             *SrcAddr,  OPTIONAL
+  IN EFI_MAC_ADDRESS             *DestAddr, OPTIONAL
+  IN UINT16                      *Protocol  OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  EFI_STATUS          Status;
+  UINT8               *Frame = Buffer;
+  UINT8               Desc;
+  PHYSICAL_ADDRESS    DmaDeviceAddress;
+  UINTN               DmaNumberOfBytes;
+
+  if (This == NULL || Buffer == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid parameter (missing handle or buffer)\n",
+      __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (!Genet->SnpMode.MediaPresent) {
+    //
+    // Don't bother transmitting if there's no link.
+    //
+    return EFI_NOT_READY;
+  }
+
+  if (HeaderSize != 0) {
+    if (HeaderSize != Genet->SnpMode.MediaHeaderSize) {
+      DEBUG ((DEBUG_ERROR,
+        "%a: Invalid parameter (header size mismatch; HeaderSize 0x%X, SnpMode.MediaHeaderSize 0x%X))\n",
+        __FUNCTION__, HeaderSize, Genet->SnpMode.MediaHeaderSize));
+      return EFI_INVALID_PARAMETER;
+    }
+    if (DestAddr == NULL || Protocol == NULL) {
+      DEBUG ((DEBUG_ERROR,
+        "%a: Invalid parameter (dest addr or protocol missing)\n",
+        __FUNCTION__));
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  if (BufferSize < Genet->SnpMode.MediaHeaderSize) {
+    DEBUG ((DEBUG_ERROR, "%a: Buffer too small\n", __FUNCTION__));
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  Status = EfiAcquireLockOrFail (&Genet->Lock);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Couldn't get lock: %r\n", __FUNCTION__, Status));
+    return EFI_ACCESS_DENIED;
+  }
+
+  if (Genet->TxQueued == GENET_DMA_DESC_COUNT - 1) {
+    EfiReleaseLock (&Genet->Lock);
+
+    DEBUG ((DEBUG_ERROR, "%a: Queue full\n", __FUNCTION__));
+    return EFI_NOT_READY;
+  }
+
+  if (HeaderSize != 0) {
+    CopyMem (&Frame[0], &DestAddr->Addr[0], NET_ETHER_ADDR_LEN);
+    CopyMem (&Frame[6], &SrcAddr->Addr[0], NET_ETHER_ADDR_LEN);
+    Frame[12] = (*Protocol & 0xFF00) >> 8;
+    Frame[13] = *Protocol & 0xFF;
+  }
+
+  Desc = Genet->TxProdIndex % GENET_DMA_DESC_COUNT;
+
+  Genet->TxBuffer[Desc] = Frame;
+
+  DmaNumberOfBytes = BufferSize;
+  Status = DmaMap (MapOperationBusMasterRead,
+                   (VOID *)(UINTN)Frame,
+                   &DmaNumberOfBytes,
+                   &DmaDeviceAddress,
+                   &Genet->TxBufferMap[Desc]);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: DmaMap failed: %r\n", __FUNCTION__, Status));
+    EfiReleaseLock (&Genet->Lock);
+    return Status;
+  }
+
+  GenetDmaTriggerTx (Genet, Desc, DmaDeviceAddress, DmaNumberOfBytes);
+
+  Genet->TxProdIndex = (Genet->TxProdIndex + 1) % 0xFFFF;
+  Genet->TxQueued++;
+
+  EfiReleaseLock (&Genet->Lock);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Receives a packet from a network interface.
+
+  @param  This       The protocol instance pointer.
+  @param  HeaderSize The size, in bytes, of the media header received on the network
+                     interface. If this parameter is NULL, then the media header size
+                     will not be returned.
+  @param  BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
+                     bytes, of the packet that was received on the network interface.
+  @param  Buffer     A pointer to the data buffer to receive both the media header and
+                     the data.
+  @param  SrcAddr    The source HW MAC address. If this parameter is NULL, the
+                     HW MAC source address will not be extracted from the media
+                     header.
+  @param  DestAddr   The destination HW MAC address. If this parameter is NULL,
+                     the HW MAC destination address will not be extracted from the
+                     media header.
+  @param  Protocol   The media header type. If this parameter is NULL, then the
+                     protocol will not be extracted from the media header. See
+                     RFC 1700 section "Ether Types" for examples.
+
+  @retval  EFI_SUCCESS           The received data was stored in Buffer, and BufferSize has
+                                 been updated to the number of bytes received.
+  @retval  EFI_NOT_STARTED       The network interface has not been started.
+  @retval  EFI_NOT_READY         No packets received.
+  @retval  EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
+  @retval  EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
+  @retval  EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval  EFI_UNSUPPORTED       This function is not supported by the network interface.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkReceive (
+  IN     EFI_SIMPLE_NETWORK_PROTOCOL *This,
+  OUT    UINTN                       *HeaderSize, OPTIONAL
+  IN OUT UINTN                       *BufferSize,
+  OUT    VOID                        *Buffer,
+  OUT    EFI_MAC_ADDRESS             *SrcAddr,    OPTIONAL
+  OUT    EFI_MAC_ADDRESS             *DestAddr,   OPTIONAL
+  OUT    UINT16                      *Protocol    OPTIONAL
+  )
+{
+  GENET_PRIVATE_DATA  *Genet;
+  EFI_STATUS          Status;
+  UINT8               DescIndex;
+  UINT8               *Frame;
+  UINTN               FrameLength;
+
+  if (This == NULL || Buffer == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid parameter (missing handle or buffer)\n",
+      __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (This);
+  if (Genet->SnpMode.State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+  if (Genet->SnpMode.State != EfiSimpleNetworkInitialized) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = EfiAcquireLockOrFail (&Genet->Lock);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Couldn't get lock: %r\n", __FUNCTION__, Status));
+    return EFI_ACCESS_DENIED;
+  }
+
+  Status = GenetRxIntr (Genet, &DescIndex, &FrameLength);
+  if (EFI_ERROR (Status)) {
+    EfiReleaseLock (&Genet->Lock);
+    return Status;
+  }
+
+  ASSERT (Genet->RxBufferMap[DescIndex].Mapping != NULL);
+
+  GenetDmaUnmapRxDescriptor (Genet, DescIndex);
+
+  Frame = GENET_RX_BUFFER (Genet, DescIndex);
+
+  if (FrameLength > 2 + Genet->SnpMode.MediaHeaderSize) {
+    // Received frame has 2 bytes of padding at the start
+    Frame += 2;
+    FrameLength -= 2;
+
+    if (*BufferSize < FrameLength) {
+      DEBUG ((DEBUG_ERROR,
+        "%a: Buffer size (0x%X) is too small for frame (0x%X)\n",
+        __FUNCTION__, *BufferSize, FrameLength));
+      Status = GenetDmaMapRxDescriptor (Genet, DescIndex);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "%a: Failed to remap RX descriptor!\n",
+          __FUNCTION__));
+      }
+      EfiReleaseLock (&Genet->Lock);
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    if (DestAddr != NULL) {
+      CopyMem (&DestAddr->Addr[0], &Frame[0], NET_ETHER_ADDR_LEN);
+    }
+    if (SrcAddr != NULL) {
+      CopyMem (&SrcAddr->Addr[0], &Frame[6], NET_ETHER_ADDR_LEN);
+    }
+    if (Protocol != NULL) {
+      *Protocol = (UINT16) ((Frame[12] << 8) | Frame[13]);
+    }
+    if (HeaderSize != NULL) {
+      *HeaderSize = Genet->SnpMode.MediaHeaderSize;
+    }
+
+    CopyMem (Buffer, Frame, FrameLength);
+    *BufferSize = FrameLength;
+
+    Status = EFI_SUCCESS;
+  } else {
+    DEBUG ((DEBUG_ERROR, "%a: Short packet (FrameLength 0x%X)",
+      __FUNCTION__, FrameLength));
+    Status = EFI_NOT_READY;
+  }
+
+  Status = GenetDmaMapRxDescriptor (Genet, DescIndex);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Failed to remap RX descriptor!\n", __FUNCTION__));
+  }
+
+  EfiReleaseLock (&Genet->Lock);
+  return Status;
+}
+
+/**
+  This function converts a multicast IP address to a multicast HW MAC address
+  for all packet transactions.
+
+  @param [in] SimpleNetwork     Protocol instance pointer
+  @param [in] IPv6              Set to TRUE if the multicast IP address is IPv6 [RFC2460].
+                                Set to FALSE if the multicast IP address is IPv4 [RFC 791].
+  @param [in] IP                The multicast IP address that is to be converted to a
+                                multicast HW MAC address.
+  @param [in] MAC               The multicast HW MAC address that is to be generated from IP.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The network inteface is not in the right (initialized) state.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenetSimpleNetworkMCastIPtoMAC (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork,
+  IN  BOOLEAN                     IPv6,
+  IN  EFI_IP_ADDRESS              *IP,
+  OUT EFI_MAC_ADDRESS             *MAC
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+///
+/// Simple Network Protocol instance
+///
+CONST EFI_SIMPLE_NETWORK_PROTOCOL gGenetSimpleNetworkTemplate = {
+  EFI_SIMPLE_NETWORK_PROTOCOL_REVISION,       // Revision
+  GenetSimpleNetworkStart,                    // Start
+  GenetSimpleNetworkStop,                     // Stop
+  GenetSimpleNetworkInitialize,               // Initialize
+  GenetSimpleNetworkReset,                    // Reset
+  GenetSimpleNetworkShutdown,                 // Shutdown
+  GenetSimpleNetworkReceiveFilters,           // ReceiveFilters
+  GenetSimpleNetworkStationAddress,           // StationAddress
+  GenetSimpleNetworkStatistics,               // Statistics
+  GenetSimpleNetworkMCastIPtoMAC,             // MCastIpToMac
+  GenetSimpleNetworkNvData,                   // NvData
+  GenetSimpleNetworkGetStatus,                // GetStatus
+  GenetSimpleNetworkTransmit,                 // Transmit
+  GenetSimpleNetworkReceive,                  // Receive
+  NULL,                                       // WaitForPacket
+  NULL                                        // Mode
+};
-- 
2.17.1


  parent reply	other threads:[~2020-05-12 16:41 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-12 16:41 [PATCH edk2-platforms v3 0/8] Add Broadcom GENET driver for RPi4 Ard Biesheuvel
2020-05-12 16:41 ` [PATCH edk2-platforms v3 1/8] Platform/RaspberryPi4: set DMA translation for BCM Genet driver Ard Biesheuvel
2020-05-12 16:41 ` [PATCH edk2-platforms v3 2/8] Silicon/Broadcom: Add BcmGenetPlatformDevice protocol Ard Biesheuvel
2020-05-12 16:41 ` Ard Biesheuvel [this message]
2020-05-12 16:41 ` [PATCH edk2-platforms v3 4/8] Platform/RaspberryPi4: Clean up PCDs out of the GENET driver Ard Biesheuvel
2020-05-12 16:41 ` [PATCH edk2-platforms v3 5/8] Platform/RaspberryPi/ConfigDxe: preliminary cleanup Ard Biesheuvel
2020-05-12 16:41 ` [PATCH edk2-platforms v3 6/8] Platform/RaspberryPi4: Register GENET platform device protocol Ard Biesheuvel
2020-05-12 16:41 ` [PATCH edk2-platforms v3 7/8] Platform/RaspberryPi4: Remove PlatformPcdLib Ard Biesheuvel
2020-05-12 16:41 ` [PATCH edk2-platforms v3 8/8] Platform/RaspberryPi4: remove ASIX 88772b driver Ard Biesheuvel
2020-05-12 17:15 ` [edk2-devel] [PATCH edk2-platforms v3 0/8] Add Broadcom GENET driver for RPi4 Leif Lindholm
2020-05-12 17:21   ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200512164125.28139-4-ard.biesheuvel@arm.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox