[edk2-devel] [PATCH edk2-platforms v2 3/7] Silicon/Broadcom/BcmGenetDxe: Add GENET driver

Ard Biesheuvel posted 7 patches 5 years, 9 months ago
There is a newer version of this series
[edk2-devel] [PATCH edk2-platforms v2 3/7] Silicon/Broadcom/BcmGenetDxe: Add GENET driver
Posted by Ard Biesheuvel 5 years, 9 months ago
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/GenericPhy.h    | 106 +++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.h     | 364 +++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c | 198 +++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/DriverBinding.c | 408 ++++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c    | 405 ++++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/Genet.c         | 114 ---
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c     | 816 +++++++++++++++++++
 Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c | 834 ++++++++++++++++++++
 9 files changed, 3164 insertions(+), 127 deletions(-)

diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf
index 9e9301608f24..3e98983c6b07 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
+  ComponentName.c
+  DriverBinding.c
+  GenericPhy.c
+  GenericPhy.h
+  GenetUtil.c
+  GenetUtil.h
+  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/GenericPhy.h b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h
new file mode 100644
index 000000000000..58b52722b4e3
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h
@@ -0,0 +1,106 @@
+/** @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__
+
+#define GENERIC_PHY_BMCR                0x00
+#define  GENERIC_PHY_BMCR_RESET         BIT15
+#define  GENERIC_PHY_BMCR_ANE           BIT12
+#define  GENERIC_PHY_BMCR_RESTART_AN    BIT9
+#define GENERIC_PHY_BMSR                0x01
+#define  GENERIC_PHY_BMSR_ANEG_COMPLETE BIT5
+#define  GENERIC_PHY_BMSR_LINK_STATUS   BIT2
+#define GENERIC_PHY_PHYIDR1             0x02
+#define GENERIC_PHY_PHYIDR2             0x03
+#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
+#define GENERIC_PHY_ANLPAR              0x05
+#define GENERIC_PHY_GBCR                0x09
+#define  GENERIC_PHY_GBCR_1000BASET_FDX BIT9
+#define  GENERIC_PHY_GBCR_1000BASET     BIT8
+#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/GenetUtil.h b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.h
new file mode 100644
index 000000000000..5ae2e0bad273
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.h
@@ -0,0 +1,364 @@
+/** @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 GENET_UTIL_H__
+#define GENET_UTIL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/DriverSupportedEfiVersion.h>
+#include <Protocol/BcmGenetPlatformDevice.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/NetLib.h>
+#include <Library/UefiLib.h>
+
+#include "GenericPhy.h"
+
+/*
+ * 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/ComponentName.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c
new file mode 100644
index 000000000000..b4b8896593f3
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c
@@ -0,0 +1,198 @@
+/** @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 "GenetUtil.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..57f2fb17cb17
--- /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 <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/BcmGenetPlatformDevice.h>
+
+#include "GenetUtil.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..ff67602c92ff
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c
@@ -0,0 +1,405 @@
+/** @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..119691dbeb49
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c
@@ -0,0 +1,816 @@
+/** @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 "GenetUtil.h"
+
+#include <Library/DmaLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.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))
+
+#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..b2cae687b3d4
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c
@@ -0,0 +1,834 @@
+/** @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 "GenetUtil.h"
+
+#include <Library/DmaLib.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


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#59249): https://edk2.groups.io/g/devel/message/59249
Mute This Topic: https://groups.io/mt/74154332/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-

Re: [edk2-devel] [PATCH edk2-platforms v2 3/7] Silicon/Broadcom/BcmGenetDxe: Add GENET driver
Posted by Leif Lindholm 5 years, 9 months ago
On Tue, May 12, 2020 at 09:55:08 +0200, Ard Biesheuvel wrote:
> 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/GenericPhy.h    | 106 +++
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.h     | 364 +++++++++
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c | 198 +++++
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/DriverBinding.c | 408 ++++++++++
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c    | 405 ++++++++++
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/Genet.c         | 114 ---
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c     | 816 +++++++++++++++++++
>  Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c | 834 ++++++++++++++++++++
>  9 files changed, 3164 insertions(+), 127 deletions(-)
> 
> diff --git a/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/BcmGenetDxe.inf
> index 9e9301608f24..3e98983c6b07 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
> +  ComponentName.c
> +  DriverBinding.c
> +  GenericPhy.c
> +  GenericPhy.h
> +  GenetUtil.c
> +  GenetUtil.h
> +  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/GenericPhy.h b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h
> new file mode 100644
> index 000000000000..58b52722b4e3
> --- /dev/null
> +++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.h
> @@ -0,0 +1,106 @@
> +/** @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__
> +
> +#define GENERIC_PHY_BMCR                0x00
> +#define  GENERIC_PHY_BMCR_RESET         BIT15
> +#define  GENERIC_PHY_BMCR_ANE           BIT12
> +#define  GENERIC_PHY_BMCR_RESTART_AN    BIT9
> +#define GENERIC_PHY_BMSR                0x01
> +#define  GENERIC_PHY_BMSR_ANEG_COMPLETE BIT5
> +#define  GENERIC_PHY_BMSR_LINK_STATUS   BIT2
> +#define GENERIC_PHY_PHYIDR1             0x02
> +#define GENERIC_PHY_PHYIDR2             0x03
> +#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
> +#define GENERIC_PHY_ANLPAR              0x05
> +#define GENERIC_PHY_GBCR                0x09
> +#define  GENERIC_PHY_GBCR_1000BASET_FDX BIT9
> +#define  GENERIC_PHY_GBCR_1000BASET     BIT8
> +#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/GenetUtil.h b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.h
> new file mode 100644
> index 000000000000..5ae2e0bad273
> --- /dev/null
> +++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.h
> @@ -0,0 +1,364 @@
> +/** @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 GENET_UTIL_H__
> +#define GENET_UTIL_H__
> +
> +#include <Uefi.h>
> +
> +#include <Protocol/DriverSupportedEfiVersion.h>
> +#include <Protocol/BcmGenetPlatformDevice.h>
> +#include <Protocol/SimpleNetwork.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DevicePathLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/NetLib.h>
> +#include <Library/UefiLib.h>

Could we make sure this file includes only those headers it needs for
itself? (As per https://edk2-docs.gitbook.io/edk-ii-c-coding-standards-specification/5_source_files/53_include_files#5-3-4-include-files-may-include-only-those-headers-that-it-directly-depends-upon)

> +
> +#include "GenericPhy.h"
> +
> +/*
> + * 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')

Swedish person has slight giggle, stays silent, moves on.

> +#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/ComponentName.c b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c
> new file mode 100644
> index 000000000000..b4b8896593f3
> --- /dev/null
> +++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/ComponentName.c
> @@ -0,0 +1,198 @@
> +/** @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 "GenetUtil.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..57f2fb17cb17
> --- /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 <Library/DebugLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +
> +#include <Protocol/BcmGenetPlatformDevice.h>
> +
> +#include "GenetUtil.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..ff67602c92ff
> --- /dev/null
> +++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenericPhy.c
> @@ -0,0 +1,405 @@
> +/** @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;

This file contains a fair amount of definitely-not-camelcase variables
that apppear to refer to register names. Is there any chance of having
those register names added to a proper glossary as part of this file's
comment header?

> +
> +  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..119691dbeb49
> --- /dev/null
> +++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/GenetUtil.c
> @@ -0,0 +1,816 @@
> +/** @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 "GenetUtil.h"
> +
> +#include <Library/DmaLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/UefiBootServicesTableLib.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))

Can we get rid of leading __ in macros?

Ideally, these macros would be in GenetUtil.h.

/
    Leif

> +
> +#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..b2cae687b3d4
> --- /dev/null
> +++ b/Silicon/Broadcom/Drivers/Net/BcmGenetDxe/SimpleNetwork.c
> @@ -0,0 +1,834 @@
> +/** @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 "GenetUtil.h"
> +
> +#include <Library/DmaLib.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
> 
> 
> 
> 

-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#59284): https://edk2.groups.io/g/devel/message/59284
Mute This Topic: https://groups.io/mt/74154332/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-