public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2
@ 2020-04-30 17:16 Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 1/8] Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference Ard Biesheuvel
                   ` (8 more replies)
  0 siblings, 9 replies; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

This is a followup to two patches I sent out separately yesterday, and to
the subsequent discussion regarding annotating obsolete drivers as unfit
for reuse if they don't follow the UEFI driver model.

I have rounded up all the stuff I have in flight for edk2-platforms at
the moment, so this v3 supersedes everything else for edk2-platforms you
may have in your mailbox.

Once this is in, I will follow up with a series for EmbeddedPkg to get
rid of the originals.

Changes since v2:
- move DwEmmcDxe, Lan91x, Lan9118 and ISP 1761 drivers as well
- incorporate a build fixlet that I sent out separately as well
- add some acks

Changes since v1:
- use gEmbeddedMmcHostProtocolGuid not gEdkiiMmcHostProtocolGuid
- add WARNING comment blocks to all files to document the fact that these
  files are only kept for sentimental reasons

Ard Biesheuvel (8):
  Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference
  Platform/ARM/VExpressPkg: incorporate PL180 driver
  Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver
  Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
  Platform/HiKey: switch to relocated version of eMMC driver
  Platform/ARM/VExpressPkg: incorporate Lan91x driver
  Platform/ARM/VExpressPkg: incorporate Lan9118 driver
  Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver

 Platform/ARM/JunoPkg/ArmJuno.dec              |    4 +-
 Platform/ARM/JunoPkg/ArmJuno.dsc              |   10 +-
 Platform/ARM/JunoPkg/ArmJuno.fdf              |    4 +-
 .../Drivers/SataSiI3132Dxe/ComponentName.c    |  179 ++
 .../Drivers/SataSiI3132Dxe/SataSiI3132.c      |  546 ++++
 .../Drivers/SataSiI3132Dxe/SataSiI3132.h      |  286 +++
 .../Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf |   38 +
 .../SataSiI3132Dxe/SiI3132AtaPassThru.c       |  834 ++++++
 Platform/ARM/SgiPkg/SgiPlatform.dsc           |    4 +-
 Platform/ARM/SgiPkg/SgiPlatform.fdf           |    2 +-
 .../ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc  |   14 +-
 .../ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf  |    6 +-
 .../VExpressPkg/ArmVExpress-FVP-AArch64.dsc   |    6 +-
 .../VExpressPkg/ArmVExpress-FVP-AArch64.fdf   |    4 +-
 Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc  |    6 +-
 Platform/ARM/VExpressPkg/ArmVExpressPkg.dec   |   18 +
 .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c     |  636 +++++
 .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h     |  123 +
 .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf   |   39 +
 .../Drivers/Lan9118Dxe/Lan9118Dxe.c           | 1539 ++++++++++++
 .../Drivers/Lan9118Dxe/Lan9118Dxe.h           |  304 +++
 .../Drivers/Lan9118Dxe/Lan9118Dxe.inf         |   58 +
 .../Drivers/Lan9118Dxe/Lan9118DxeHw.h         |  401 +++
 .../Drivers/Lan9118Dxe/Lan9118DxeUtil.c       | 1039 ++++++++
 .../Drivers/Lan9118Dxe/Lan9118DxeUtil.h       |  283 +++
 .../VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c | 2236 +++++++++++++++++
 .../Drivers/Lan91xDxe/Lan91xDxe.inf           |   59 +
 .../Drivers/Lan91xDxe/Lan91xDxeHw.h           |  279 ++
 .../Drivers/PL180MciDxe/PL180Mci.c            |  570 +++++
 .../Drivers/PL180MciDxe/PL180Mci.h            |  169 ++
 .../Drivers/PL180MciDxe/PL180MciDxe.inf       |   53 +
 Platform/Hisilicon/HiKey/HiKey.dsc            |    7 +-
 Platform/Hisilicon/HiKey/HiKey.fdf            |    2 +-
 Silicon/Synopsys/DesignWare/DesignWare.dec    |    9 +
 Silicon/Synopsys/DesignWare/DesignWare.dsc    |    2 +
 .../DesignWare/Drivers/DwEmmcDxe/DwEmmc.h     |  132 +
 .../DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c  |  693 +++++
 .../Drivers/DwEmmcDxe/DwEmmcDxe.inf           |   56 +
 .../LcdGraphicsOutputDxe.inf                  |    1 +
 39 files changed, 10618 insertions(+), 33 deletions(-)
 create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c
 create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c
 create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h
 create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
 create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.c
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.h
 create mode 100644 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
 create mode 100644 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
 create mode 100644 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
 create mode 100644 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf

-- 
2.17.1


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 1/8] Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 2/8] Platform/ARM/VExpressPkg: incorporate PL180 driver Ard Biesheuvel
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

ArmPkg's ArmLib recently dropped an unnecessary reference to
gEfiCpuArchProtocolGuid, causing the build to fail for BeagleBoard,
due to the fact that LcdGraphicsOutputDxe does need it, but lacks the
reference, which no longer gets satisfied transitively through ArmLib.

So add the reference to LcdGraphicsOutputDxe directly.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Silicon/TexasInstruments/Omap35xxPkg/LcdGraphicsOutputDxe/LcdGraphicsOutputDxe.inf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Silicon/TexasInstruments/Omap35xxPkg/LcdGraphicsOutputDxe/LcdGraphicsOutputDxe.inf b/Silicon/TexasInstruments/Omap35xxPkg/LcdGraphicsOutputDxe/LcdGraphicsOutputDxe.inf
index 65624ce7e189..d07be1c74cf5 100644
--- a/Silicon/TexasInstruments/Omap35xxPkg/LcdGraphicsOutputDxe/LcdGraphicsOutputDxe.inf
+++ b/Silicon/TexasInstruments/Omap35xxPkg/LcdGraphicsOutputDxe/LcdGraphicsOutputDxe.inf
@@ -37,6 +37,7 @@ [LibraryClasses]
   BaseMemoryLib
 
 [Protocols]
+  gEfiCpuArchProtocolGuid
   gEfiDevicePathProtocolGuid
   gEfiGraphicsOutputProtocolGuid
   gEfiDevicePathToTextProtocolGuid
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 2/8] Platform/ARM/VExpressPkg: incorporate PL180 driver
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 1/8] Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver Ard Biesheuvel
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

The PL180 SD host controller driver is only used on emulated ARM
platforms, uses an obsolete version of the MMC host protocol and
does not adhere to the UEFI driver model.

Given the above, let's just move it into VExpressPkg where it
belongs.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>
---
 Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc            |   6 +-
 Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf            |   2 +-
 Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc         |   6 +-
 Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf         |   2 +-
 Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                  |   4 +
 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.c      | 570 ++++++++++++++++++++
 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.h      | 169 ++++++
 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf |  53 ++
 8 files changed, 804 insertions(+), 8 deletions(-)

diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
index 2f8021d3eabc..6dad31026aa5 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
@@ -146,8 +146,8 @@ [PcdsFixedAtBuild.common]
   #
   # PL180 MMC/SD card controller
   #
-  gArmPlatformTokenSpaceGuid.PcdPL180SysMciRegAddress|0x1C010048
-  gArmPlatformTokenSpaceGuid.PcdPL180MciBaseAddress|0x1C050000
+  gArmVExpressTokenSpaceGuid.PcdPL180SysMciRegAddress|0x1C010048
+  gArmVExpressTokenSpaceGuid.PcdPL180MciBaseAddress|0x1C050000
 
 
   #
@@ -249,7 +249,7 @@ [Components.common]
   # Multimedia Card Interface
   #
   EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
-  ArmPlatformPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
+  Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
 
   # SMSC LAN 9118
   EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
index 082f80d9996d..64da1102ff07 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
@@ -103,7 +103,7 @@ [FV.FvMain]
   # Multimedia Card Interface
   #
   INF EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
-  INF ArmPlatformPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
 
   #
   # Filesystems
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc b/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc
index 63d79a488500..a6f536a33228 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc
@@ -151,8 +151,8 @@ [PcdsFixedAtBuild.common]
 !endif
 
   ## PL180 MMC/SD card controller
-  gArmPlatformTokenSpaceGuid.PcdPL180SysMciRegAddress|0x1C010048
-  gArmPlatformTokenSpaceGuid.PcdPL180MciBaseAddress|0x1C050000
+  gArmVExpressTokenSpaceGuid.PcdPL180SysMciRegAddress|0x1C010048
+  gArmVExpressTokenSpaceGuid.PcdPL180MciBaseAddress|0x1C050000
 
   #
   # ARM Generic Interrupt Controller
@@ -290,7 +290,7 @@ [Components.common]
   # Multimedia Card Interface
   #
   EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
-  ArmPlatformPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
+  Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
 
   #
   # Platform Driver
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf
index 8f49ed3dba3c..f18ead75eaec 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf
@@ -139,7 +139,7 @@ [FV.FvMain]
   # Multimedia Card Interface
   #
   INF EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
-  INF ArmPlatformPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
 
   #
   # SMBIOS Support
diff --git a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
index a659cda2e44a..a4e9bfd73eb6 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+++ b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
@@ -52,3 +52,7 @@ [PcdsFixedAtBuild.common]
   #
   gArmVExpressTokenSpaceGuid.PcdAndroidFastbootNvmDevicePath|""|VOID*|0x00000006
   gArmVExpressTokenSpaceGuid.PcdAndroidFastbootProductName|""|VOID*|0x00000007
+
+  ## PL180 MCI
+  gArmVExpressTokenSpaceGuid.PcdPL180SysMciRegAddress|0x00000000|UINT32|0x00000009
+  gArmVExpressTokenSpaceGuid.PcdPL180MciBaseAddress|0x00000000|UINT32|0x0000000A
diff --git a/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.c b/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.c
new file mode 100644
index 000000000000..8572513f559a
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.c
@@ -0,0 +1,570 @@
+/** @file
+  This file implement the MMC Host Protocol for the ARM PrimeCell PL180.
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PL180Mci.h"
+
+#include <Library/DevicePathLib.h>
+#include <Library/BaseMemoryLib.h>
+
+EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
+
+// Untested ...
+//#define USE_STREAM
+
+#define MMCI0_BLOCKLEN 512
+#define MMCI0_POW2_BLOCKLEN     9
+#define MMCI0_TIMEOUT           1000
+
+#define SYS_MCI_CARDIN          BIT0
+#define SYS_MCI_WPROT           BIT1
+
+BOOLEAN
+MciIsPowerOn (
+  VOID
+  )
+{
+  return ((MmioRead32 (MCI_POWER_CONTROL_REG) & MCI_POWER_ON) == MCI_POWER_ON);
+}
+
+EFI_STATUS
+MciInitialize (
+  VOID
+  )
+{
+  MCI_TRACE ("MciInitialize()");
+  return EFI_SUCCESS;
+}
+
+BOOLEAN
+MciIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return (MmioRead32 (FixedPcdGet32 (PcdPL180SysMciRegAddress)) & SYS_MCI_CARDIN);
+}
+
+BOOLEAN
+MciIsReadOnly (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return (MmioRead32 (FixedPcdGet32 (PcdPL180SysMciRegAddress)) & SYS_MCI_WPROT);
+}
+
+// Convert block size to 2^n
+STATIC
+UINT32
+GetPow2BlockLen (
+  IN UINT32 BlockLen
+  )
+{
+  UINTN Loop;
+  UINTN Pow2BlockLen;
+
+  Loop    = 0x8000;
+  Pow2BlockLen = 15;
+  do {
+    Loop = (Loop >> 1) & 0xFFFF;
+    Pow2BlockLen--;
+  } while (Pow2BlockLen && (!(Loop & BlockLen)));
+
+  return Pow2BlockLen;
+}
+
+VOID
+MciPrepareDataPath (
+  IN UINTN TransferDirection
+  )
+{
+  // Set Data Length & Data Timer
+  MmioWrite32 (MCI_DATA_TIMER_REG, 0xFFFFFFF);
+  MmioWrite32 (MCI_DATA_LENGTH_REG, MMCI0_BLOCKLEN);
+
+#ifndef USE_STREAM
+  //Note: we are using a hardcoded BlockLen (==512). If we decide to use a variable size, we could
+  // compute the pow2 of BlockLen with the above function GetPow2BlockLen ()
+  MmioWrite32 (MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_DMA_ENABLE | TransferDirection | (MMCI0_POW2_BLOCKLEN << 4));
+#else
+  MmioWrite32 (MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_DMA_ENABLE | TransferDirection | MCI_DATACTL_STREAM_TRANS);
+#endif
+}
+
+EFI_STATUS
+MciSendCommand (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_CMD                    MmcCmd,
+  IN UINT32                     Argument
+  )
+{
+  UINT32  Status;
+  UINT32  Cmd;
+  UINTN   RetVal;
+  UINTN   CmdCtrlReg;
+  UINT32  DoneMask;
+
+  RetVal = EFI_SUCCESS;
+
+  if ((MmcCmd == MMC_CMD17) || (MmcCmd == MMC_CMD11)) {
+    MciPrepareDataPath (MCI_DATACTL_CARD_TO_CONT);
+  } else if ((MmcCmd == MMC_CMD24) || (MmcCmd == MMC_CMD20)) {
+    MciPrepareDataPath (MCI_DATACTL_CONT_TO_CARD);
+  } else if (MmcCmd == MMC_CMD6) {
+    MmioWrite32 (MCI_DATA_TIMER_REG, 0xFFFFFFF);
+    MmioWrite32 (MCI_DATA_LENGTH_REG, 64);
+#ifndef USE_STREAM
+    MmioWrite32 (MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_CARD_TO_CONT | GetPow2BlockLen (64));
+#else
+    MmioWrite32 (MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_CARD_TO_CONT | MCI_DATACTL_STREAM_TRANS);
+#endif
+  } else if (MmcCmd == MMC_ACMD51) {
+    MmioWrite32 (MCI_DATA_TIMER_REG, 0xFFFFFFF);
+    /* SCR register is 8 bytes long. */
+    MmioWrite32 (MCI_DATA_LENGTH_REG, 8);
+#ifndef USE_STREAM
+    MmioWrite32 (MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_CARD_TO_CONT | GetPow2BlockLen (8));
+#else
+    MmioWrite32 (MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_CARD_TO_CONT | MCI_DATACTL_STREAM_TRANS);
+#endif
+  }
+
+  // Create Command for PL180
+  Cmd = (MMC_GET_INDX (MmcCmd) & INDX_MASK)  | MCI_CPSM_ENABLE;
+  if (MmcCmd & MMC_CMD_WAIT_RESPONSE) {
+    Cmd |= MCI_CPSM_WAIT_RESPONSE;
+  }
+
+  if (MmcCmd & MMC_CMD_LONG_RESPONSE) {
+    Cmd |= MCI_CPSM_LONG_RESPONSE;
+  }
+
+  // Clear Status register static flags
+  MmioWrite32 (MCI_CLEAR_STATUS_REG, MCI_CLR_ALL_STATUS);
+
+  // Write to command argument register
+  MmioWrite32 (MCI_ARGUMENT_REG, Argument);
+
+  // Write to command register
+  MmioWrite32 (MCI_COMMAND_REG, Cmd);
+
+  DoneMask  = (Cmd & MCI_CPSM_WAIT_RESPONSE)
+                ? (MCI_STATUS_CMD_RESPEND | MCI_STATUS_CMD_ERROR)
+                : (MCI_STATUS_CMD_SENT    | MCI_STATUS_CMD_ERROR);
+  do {
+    Status = MmioRead32 (MCI_STATUS_REG);
+  } while (! (Status & DoneMask));
+
+  if ((Status & MCI_STATUS_CMD_ERROR)) {
+    // Clear Status register error flags
+    MmioWrite32 (MCI_CLEAR_STATUS_REG, MCI_STATUS_CMD_ERROR);
+
+    if ((Status & MCI_STATUS_CMD_START_BIT_ERROR)) {
+      DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) Start bit Error! Response:0x%X Status:0x%x\n", (Cmd & 0x3F), MmioRead32 (MCI_RESPONSE0_REG), Status));
+      RetVal = EFI_NO_RESPONSE;
+    } else if ((Status & MCI_STATUS_CMD_CMDTIMEOUT)) {
+      //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT! Response:0x%X Status:0x%x\n", (Cmd & 0x3F), MmioRead32 (MCI_RESPONSE0_REG), Status));
+      RetVal = EFI_TIMEOUT;
+    } else if ((!(MmcCmd & MMC_CMD_NO_CRC_RESPONSE)) && (Status & MCI_STATUS_CMD_CMDCRCFAIL)) {
+      // The CMD1 and response type R3 do not contain CRC. We should ignore the CRC failed Status.
+      RetVal = EFI_CRC_ERROR;
+    }
+  }
+
+  // Disable Command Path
+  CmdCtrlReg = MmioRead32 (MCI_COMMAND_REG);
+  MmioWrite32 (MCI_COMMAND_REG, (CmdCtrlReg & ~MCI_CPSM_ENABLE));
+  return RetVal;
+}
+
+EFI_STATUS
+MciReceiveResponse (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_RESPONSE_TYPE          Type,
+  IN UINT32*                    Buffer
+  )
+{
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (   (Type == MMC_RESPONSE_TYPE_R1)
+      || (Type == MMC_RESPONSE_TYPE_R1b)
+      || (Type == MMC_RESPONSE_TYPE_R3)
+      || (Type == MMC_RESPONSE_TYPE_R6)
+      || (Type == MMC_RESPONSE_TYPE_R7))
+  {
+    Buffer[0] = MmioRead32 (MCI_RESPONSE3_REG);
+  } else if (Type == MMC_RESPONSE_TYPE_R2) {
+    Buffer[0] = MmioRead32 (MCI_RESPONSE0_REG);
+    Buffer[1] = MmioRead32 (MCI_RESPONSE1_REG);
+    Buffer[2] = MmioRead32 (MCI_RESPONSE2_REG);
+    Buffer[3] = MmioRead32 (MCI_RESPONSE3_REG);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MciReadBlockData (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN EFI_LBA                    Lba,
+  IN UINTN                      Length,
+  IN UINT32*                    Buffer
+  )
+{
+  UINTN Loop;
+  UINTN Finish;
+  UINTN Status;
+  EFI_STATUS RetVal;
+  UINTN  DataCtrlReg;
+  EFI_TPL Tpl;
+
+  RetVal = EFI_SUCCESS;
+
+  // Read data from the RX FIFO
+  Loop   = 0;
+  if (Length < MMCI0_BLOCKLEN) {
+    Finish = Length / 4;
+  } else {
+    Finish = MMCI0_BLOCKLEN / 4;
+  }
+
+  // Raise the TPL at the highest level to disable Interrupts.
+  Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+  do {
+    // Read the Status flags
+    Status = MmioRead32 (MCI_STATUS_REG);
+
+    // Do eight reads if possible else a single read
+    if (Status & MCI_STATUS_CMD_RXFIFOHALFFULL) {
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+    } else if (Status & MCI_STATUS_CMD_RXDATAAVAILBL) {
+      Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
+      Loop++;
+    } else {
+      //Check for error conditions and timeouts
+      if (Status & MCI_STATUS_CMD_DATATIMEOUT) {
+        DEBUG ((EFI_D_ERROR, "MciReadBlockData(): TIMEOUT! Response:0x%X Status:0x%x\n", MmioRead32 (MCI_RESPONSE0_REG), Status));
+        RetVal = EFI_TIMEOUT;
+        break;
+      } else if (Status & MCI_STATUS_CMD_DATACRCFAIL) {
+        DEBUG ((EFI_D_ERROR, "MciReadBlockData(): CRC Error! Response:0x%X Status:0x%x\n", MmioRead32 (MCI_RESPONSE0_REG), Status));
+        RetVal = EFI_CRC_ERROR;
+        break;
+      } else if (Status & MCI_STATUS_CMD_START_BIT_ERROR) {
+        DEBUG ((EFI_D_ERROR, "MciReadBlockData(): Start-bit Error! Response:0x%X Status:0x%x\n", MmioRead32 (MCI_RESPONSE0_REG), Status));
+        RetVal = EFI_NO_RESPONSE;
+        break;
+      }
+    }
+    //clear RX over run flag
+    if(Status & MCI_STATUS_CMD_RXOVERRUN) {
+      MmioWrite32(MCI_CLEAR_STATUS_REG, MCI_STATUS_CMD_RXOVERRUN);
+    }
+  } while ((Loop < Finish));
+
+  // Restore Tpl
+  gBS->RestoreTPL (Tpl);
+
+  // Clear Status flags
+  MmioWrite32 (MCI_CLEAR_STATUS_REG, MCI_CLR_ALL_STATUS);
+
+  //Disable Data path
+  DataCtrlReg = MmioRead32 (MCI_DATA_CTL_REG);
+  MmioWrite32 (MCI_DATA_CTL_REG, (DataCtrlReg & MCI_DATACTL_DISABLE_MASK));
+
+  return RetVal;
+}
+
+EFI_STATUS
+MciWriteBlockData (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN EFI_LBA                   Lba,
+  IN UINTN                     Length,
+  IN UINT32*                   Buffer
+  )
+{
+  UINTN Loop;
+  UINTN Finish;
+  UINTN Timer;
+  UINTN Status;
+  EFI_STATUS RetVal;
+  UINTN  DataCtrlReg;
+  EFI_TPL Tpl;
+
+  RetVal = EFI_SUCCESS;
+
+  // Write the data to the TX FIFO
+  Loop   = 0;
+  Finish = MMCI0_BLOCKLEN / 4;
+  Timer  = MMCI0_TIMEOUT * 100;
+
+  // Raise the TPL at the highest level to disable Interrupts.
+  Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+  do {
+    // Read the Status flags
+    Status = MmioRead32 (MCI_STATUS_REG);
+
+    // Do eight writes if possible else a single write
+    if (Status & MCI_STATUS_CMD_TXFIFOHALFEMPTY) {
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+      MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+      Loop++;
+    } else if (!(Status & MCI_STATUS_CMD_TXFIFOFULL)) {
+        MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
+        Loop++;
+    } else {
+      // Check for error conditions and timeouts
+      if (Status & MCI_STATUS_CMD_DATATIMEOUT) {
+        DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): TIMEOUT! Response:0x%X Status:0x%x\n", MmioRead32 (MCI_RESPONSE0_REG), Status));
+        RetVal = EFI_TIMEOUT;
+        goto Exit;
+      } else if (Status & MCI_STATUS_CMD_DATACRCFAIL) {
+        DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): CRC Error! Response:0x%X Status:0x%x\n", MmioRead32 (MCI_RESPONSE0_REG), Status));
+        RetVal = EFI_CRC_ERROR;
+        goto Exit;
+      } else if (Status & MCI_STATUS_CMD_TX_UNDERRUN) {
+        DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): TX buffer Underrun! Response:0x%X Status:0x%x, Number of bytes written 0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status, Loop));
+        RetVal = EFI_BUFFER_TOO_SMALL;
+        ASSERT(0);
+        goto Exit;
+      }
+    }
+  } while (Loop < Finish);
+
+  // Restore Tpl
+  gBS->RestoreTPL (Tpl);
+
+  // Wait for FIFO to drain
+  Timer  = MMCI0_TIMEOUT * 60;
+  Status = MmioRead32 (MCI_STATUS_REG);
+#ifndef USE_STREAM
+  // Single block
+  while (((Status & MCI_STATUS_TXDONE) != MCI_STATUS_TXDONE) && Timer) {
+#else
+  // Stream
+  while (((Status & MCI_STATUS_CMD_DATAEND) != MCI_STATUS_CMD_DATAEND) && Timer) {
+#endif
+    NanoSecondDelay(10);
+    Status = MmioRead32 (MCI_STATUS_REG);
+    Timer--;
+  }
+
+  // Clear Status flags
+  MmioWrite32 (MCI_CLEAR_STATUS_REG, MCI_CLR_ALL_STATUS);
+
+  if (Timer == 0) {
+    DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): Data End timeout Number of words written 0x%x\n", Loop));
+    RetVal = EFI_TIMEOUT;
+  }
+
+Exit:
+  // Disable Data path
+  DataCtrlReg = MmioRead32 (MCI_DATA_CTL_REG);
+  MmioWrite32 (MCI_DATA_CTL_REG, (DataCtrlReg & MCI_DATACTL_DISABLE_MASK));
+  return RetVal;
+}
+
+EFI_STATUS
+MciNotifyState (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_STATE                  State
+  )
+{
+  UINT32      Data32;
+
+  switch (State) {
+  case MmcInvalidState:
+    ASSERT (0);
+    break;
+  case MmcHwInitializationState:
+    // If device already turn on then restart it
+    Data32 = MmioRead32 (MCI_POWER_CONTROL_REG);
+    if ((Data32 & 0x2) == MCI_POWER_UP) {
+      MCI_TRACE ("MciNotifyState(MmcHwInitializationState): TurnOff MCI");
+
+      // Turn off
+      MmioWrite32 (MCI_CLOCK_CONTROL_REG, 0);
+      MmioWrite32 (MCI_POWER_CONTROL_REG, 0);
+      MicroSecondDelay (100);
+    }
+
+    MCI_TRACE ("MciNotifyState(MmcHwInitializationState): TurnOn MCI");
+    // Setup clock
+    //  - 0x1D = 29 => should be the clock divider to be less than 400kHz at MCLK = 24Mhz
+    MmioWrite32 (MCI_CLOCK_CONTROL_REG, 0x1D | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);
+
+    // Set the voltage
+    MmioWrite32 (MCI_POWER_CONTROL_REG, MCI_POWER_OPENDRAIN | (15<<2));
+    MmioWrite32 (MCI_POWER_CONTROL_REG, MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_UP);
+    MicroSecondDelay (10);
+    MmioWrite32 (MCI_POWER_CONTROL_REG, MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_ON);
+    MicroSecondDelay (100);
+
+    // Set Data Length & Data Timer
+    MmioWrite32 (MCI_DATA_TIMER_REG, 0xFFFFF);
+    MmioWrite32 (MCI_DATA_LENGTH_REG, 8);
+
+    ASSERT ((MmioRead32 (MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);
+    break;
+  case MmcIdleState:
+    MCI_TRACE ("MciNotifyState(MmcIdleState)");
+    break;
+  case MmcReadyState:
+    MCI_TRACE ("MciNotifyState(MmcReadyState)");
+    break;
+  case MmcIdentificationState:
+    MCI_TRACE ("MciNotifyState (MmcIdentificationState)");
+    break;
+  case MmcStandByState:{
+    volatile UINT32 PwrCtrlReg;
+    MCI_TRACE ("MciNotifyState (MmcStandByState)");
+
+    // Enable MCICMD push-pull drive
+    PwrCtrlReg = MmioRead32 (MCI_POWER_CONTROL_REG);
+    //Disable Open Drain output
+    PwrCtrlReg &= ~ (MCI_POWER_OPENDRAIN);
+    MmioWrite32 (MCI_POWER_CONTROL_REG, PwrCtrlReg);
+
+    // Set MMCI0 clock to 4MHz (24MHz may be possible with cache enabled)
+    //
+    // Note: Increasing clock speed causes TX FIFO under-run errors.
+    //       So careful when optimising this driver for higher performance.
+    //
+    MmioWrite32(MCI_CLOCK_CONTROL_REG,0x02 | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);
+    // Set MMCI0 clock to 24MHz (by bypassing the divider)
+    //MmioWrite32(MCI_CLOCK_CONTROL_REG,MCI_CLOCK_BYPASS | MCI_CLOCK_ENABLE);
+    break;
+  }
+  case MmcTransferState:
+    //MCI_TRACE ("MciNotifyState(MmcTransferState)");
+    break;
+  case MmcSendingDataState:
+    MCI_TRACE ("MciNotifyState(MmcSendingDataState)");
+    break;
+  case MmcReceiveDataState:
+    MCI_TRACE ("MciNotifyState(MmcReceiveDataState)");
+    break;
+  case MmcProgrammingState:
+    MCI_TRACE ("MciNotifyState(MmcProgrammingState)");
+    break;
+  case MmcDisconnectState:
+    MCI_TRACE ("MciNotifyState(MmcDisconnectState)");
+    break;
+  default:
+    ASSERT (0);
+  }
+  return EFI_SUCCESS;
+}
+
+EFI_GUID mPL180MciDevicePathGuid = EFI_CALLER_ID_GUID;
+
+EFI_STATUS
+MciBuildDevicePath (
+  IN EFI_MMC_HOST_PROTOCOL      *This,
+  IN EFI_DEVICE_PATH_PROTOCOL   **DevicePath
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL    *NewDevicePathNode;
+
+  NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH));
+  CopyGuid (& ((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &mPL180MciDevicePathGuid);
+
+  *DevicePath = NewDevicePathNode;
+  return EFI_SUCCESS;
+}
+
+EFI_MMC_HOST_PROTOCOL gMciHost = {
+  MMC_HOST_PROTOCOL_REVISION,
+  MciIsCardPresent,
+  MciIsReadOnly,
+  MciBuildDevicePath,
+  MciNotifyState,
+  MciSendCommand,
+  MciReceiveResponse,
+  MciReadBlockData,
+  MciWriteBlockData
+};
+
+EFI_STATUS
+PL180MciDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+  EFI_HANDLE    Handle;
+
+  DEBUG ((EFI_D_WARN, "Probing ID registers at 0x%lx for a PL180\n",
+    MCI_PERIPH_ID_REG0));
+
+  // Check if this is a PL180
+  if (MmioRead8 (MCI_PERIPH_ID_REG0) != MCI_PERIPH_ID0 ||
+      MmioRead8 (MCI_PERIPH_ID_REG1) != MCI_PERIPH_ID1 ||
+      MmioRead8 (MCI_PERIPH_ID_REG2) != MCI_PERIPH_ID2 ||
+      MmioRead8 (MCI_PCELL_ID_REG0)  != MCI_PCELL_ID0  ||
+      MmioRead8 (MCI_PCELL_ID_REG1)  != MCI_PCELL_ID1  ||
+      MmioRead8 (MCI_PCELL_ID_REG2)  != MCI_PCELL_ID2  ||
+      MmioRead8 (MCI_PCELL_ID_REG3)  != MCI_PCELL_ID3) {
+
+    DEBUG ((EFI_D_WARN, "Probing ID registers at 0x%lx for a PL180"
+      " failed\n", MCI_PERIPH_ID_REG0));
+    return EFI_NOT_FOUND;
+  }
+
+  Handle = NULL;
+
+  MCI_TRACE ("PL180MciDxeInitialize()");
+
+  //Publish Component Name, BlockIO protocol interfaces
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEmbeddedMmcHostProtocolGuid,       &gMciHost,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.h b/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.h
new file mode 100644
index 000000000000..4c5246044e75
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.h
@@ -0,0 +1,169 @@
+/** @file
+  Header for the MMC Host Protocol implementation for the ARM PrimeCell PL180.
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __PL180_MCI_H
+#define __PL180_MCI_H
+
+#include <Uefi.h>
+
+#include <Protocol/MmcHost.h>
+
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+#include <Library/PcdLib.h>
+
+#define PL180_MCI_DXE_VERSION           0x10
+
+#define MCI_SYSCTL  FixedPcdGet32 (PcdPL180MciBaseAddress)
+
+#define MCI_POWER_CONTROL_REG           (MCI_SYSCTL + 0x000)
+#define MCI_CLOCK_CONTROL_REG           (MCI_SYSCTL + 0x004)
+#define MCI_ARGUMENT_REG                (MCI_SYSCTL + 0x008)
+#define MCI_COMMAND_REG                 (MCI_SYSCTL + 0x00C)
+#define MCI_RESPCMD_REG                 (MCI_SYSCTL + 0x010)
+#define MCI_RESPONSE3_REG               (MCI_SYSCTL + 0x014)
+#define MCI_RESPONSE2_REG               (MCI_SYSCTL + 0x018)
+#define MCI_RESPONSE1_REG               (MCI_SYSCTL + 0x01C)
+#define MCI_RESPONSE0_REG               (MCI_SYSCTL + 0x020)
+#define MCI_DATA_TIMER_REG              (MCI_SYSCTL + 0x024)
+#define MCI_DATA_LENGTH_REG             (MCI_SYSCTL + 0x028)
+#define MCI_DATA_CTL_REG                (MCI_SYSCTL + 0x02C)
+#define MCI_DATA_COUNTER                (MCI_SYSCTL + 0x030)
+#define MCI_STATUS_REG                  (MCI_SYSCTL + 0x034)
+#define MCI_CLEAR_STATUS_REG            (MCI_SYSCTL + 0x038)
+#define MCI_INT0_MASK_REG               (MCI_SYSCTL + 0x03C)
+#define MCI_INT1_MASK_REG               (MCI_SYSCTL + 0x040)
+#define MCI_SELECT_REG                  (MCI_SYSCTL + 0x044)
+#define MCI_FIFOCOUNT_REG               (MCI_SYSCTL + 0x048)
+#define MCI_FIFO_REG                    (MCI_SYSCTL + 0x080)
+#define MCI_PERIPH_ID_REG0              (MCI_SYSCTL + 0xFE0)
+#define MCI_PERIPH_ID_REG1              (MCI_SYSCTL + 0xFE4)
+#define MCI_PERIPH_ID_REG2              (MCI_SYSCTL + 0xFE8)
+#define MCI_PERIPH_ID_REG3              (MCI_SYSCTL + 0xFEC)
+#define MCI_PCELL_ID_REG0               (MCI_SYSCTL + 0xFF0)
+#define MCI_PCELL_ID_REG1               (MCI_SYSCTL + 0xFF4)
+#define MCI_PCELL_ID_REG2               (MCI_SYSCTL + 0xFF8)
+#define MCI_PCELL_ID_REG3               (MCI_SYSCTL + 0xFFC)
+
+#define MCI_PERIPH_ID0                  0x80
+#define MCI_PERIPH_ID1                  0x11
+#define MCI_PERIPH_ID2                  0x04
+#define MCI_PERIPH_ID3                  0x00
+#define MCI_PCELL_ID0                   0x0D
+#define MCI_PCELL_ID1                   0xF0
+#define MCI_PCELL_ID2                   0x05
+#define MCI_PCELL_ID3                   0xB1
+
+#define MCI_POWER_OFF                   0
+#define MCI_POWER_UP                    BIT1
+#define MCI_POWER_ON                    (BIT1 | BIT0)
+#define MCI_POWER_OPENDRAIN             BIT6
+#define MCI_POWER_ROD                   BIT7
+
+#define MCI_CLOCK_ENABLE                BIT8
+#define MCI_CLOCK_POWERSAVE             BIT9
+#define MCI_CLOCK_BYPASS                BIT10
+#define MCI_CLOCK_WIDEBUS               BIT11
+
+#define MCI_STATUS_CMD_CMDCRCFAIL       BIT0
+#define MCI_STATUS_CMD_DATACRCFAIL      BIT1
+#define MCI_STATUS_CMD_CMDTIMEOUT       BIT2
+#define MCI_STATUS_CMD_DATATIMEOUT      BIT3
+#define MCI_STATUS_CMD_TX_UNDERRUN      BIT4
+#define MCI_STATUS_CMD_RXOVERRUN        BIT5
+#define MCI_STATUS_CMD_RESPEND          BIT6
+#define MCI_STATUS_CMD_SENT             BIT7
+#define MCI_STATUS_CMD_DATAEND          BIT8
+#define MCI_STATUS_CMD_START_BIT_ERROR  BIT9
+#define MCI_STATUS_CMD_DATABLOCKEND     BIT10
+#define MCI_STATUS_CMD_ACTIVE           BIT11
+#define MCI_STATUS_CMD_TXACTIVE         BIT12
+#define MCI_STATUS_CMD_RXACTIVE         BIT13
+#define MCI_STATUS_CMD_TXFIFOHALFEMPTY  BIT14
+#define MCI_STATUS_CMD_RXFIFOHALFFULL   BIT15
+#define MCI_STATUS_CMD_TXFIFOFULL       BIT16
+#define MCI_STATUS_CMD_RXFIFOFULL       BIT17
+#define MCI_STATUS_CMD_TXFIFOEMPTY      BIT18
+#define MCI_STATUS_CMD_RXFIFOEMPTY      BIT19
+#define MCI_STATUS_CMD_TXDATAAVAILBL    BIT20
+#define MCI_STATUS_CMD_RXDATAAVAILBL    BIT21
+
+#define MCI_STATUS_TXDONE               (MCI_STATUS_CMD_DATAEND | MCI_STATUS_CMD_DATABLOCKEND)
+#define MCI_STATUS_RXDONE               (MCI_STATUS_CMD_DATAEND | MCI_STATUS_CMD_DATABLOCKEND)
+#define MCI_STATUS_READ_ERROR           (  MCI_STATUS_CMD_DATACRCFAIL     \
+                                         | MCI_STATUS_CMD_DATATIMEOUT     \
+                                         | MCI_STATUS_CMD_RXOVERRUN       \
+                                         | MCI_STATUS_CMD_START_BIT_ERROR )
+#define MCI_STATUS_WRITE_ERROR          (  MCI_STATUS_CMD_DATACRCFAIL \
+                                         | MCI_STATUS_CMD_DATATIMEOUT \
+                                         | MCI_STATUS_CMD_TX_UNDERRUN )
+#define MCI_STATUS_CMD_ERROR            (  MCI_STATUS_CMD_CMDCRCFAIL      \
+                                         | MCI_STATUS_CMD_CMDTIMEOUT      \
+                                         | MCI_STATUS_CMD_START_BIT_ERROR )
+
+#define MCI_CLR_CMD_STATUS              (  MCI_STATUS_CMD_RESPEND \
+                                         | MCI_STATUS_CMD_SENT    \
+                                         | MCI_STATUS_CMD_ERROR )
+
+#define MCI_CLR_READ_STATUS             (  MCI_STATUS_RXDONE     \
+                                         | MCI_STATUS_READ_ERROR )
+
+#define MCI_CLR_WRITE_STATUS            (  MCI_STATUS_TXDONE      \
+                                         | MCI_STATUS_WRITE_ERROR )
+
+#define MCI_CLR_ALL_STATUS              (BIT11 - 1)
+
+#define MCI_DATACTL_DISABLE_MASK        0xFE
+#define MCI_DATACTL_ENABLE              BIT0
+#define MCI_DATACTL_CONT_TO_CARD        0
+#define MCI_DATACTL_CARD_TO_CONT        BIT1
+#define MCI_DATACTL_BLOCK_TRANS         0
+#define MCI_DATACTL_STREAM_TRANS        BIT2
+#define MCI_DATACTL_DMA_DISABLED        0
+#define MCI_DATACTL_DMA_ENABLE          BIT3
+
+#define INDX_MASK                       0x3F
+
+#define MCI_CPSM_WAIT_RESPONSE          BIT6
+#define MCI_CPSM_LONG_RESPONSE          BIT7
+#define MCI_CPSM_LONG_INTERRUPT         BIT8
+#define MCI_CPSM_LONG_PENDING           BIT9
+#define MCI_CPSM_ENABLE                 BIT10
+
+#define MCI_TRACE(txt)                  DEBUG ((EFI_D_BLKIO, "ARM_MCI: " txt "\n"))
+
+EFI_STATUS
+EFIAPI
+MciGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+MciGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+#endif
diff --git a/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf b/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
new file mode 100644
index 000000000000..34690a135e16
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
@@ -0,0 +1,53 @@
+#/** @file
+#  INF file for the MMC Host Protocol implementation for the ARM PrimeCell PL180.
+#
+#  WARNING:
+#  This driver fails to follow the UEFI driver model without a good
+#  reason, and only remains in the tree because it is still used by
+#  a small number of platforms. It will be removed when no longer used.
+#  New platforms should not use it, and no one should use this as
+#  reference code for developing new drivers.
+#
+#  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PL180MciDxe
+  FILE_GUID                      = 09831032-6fa3-4484-af4f-0a000a8d3a82
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = PL180MciDxeInitialize
+
+[Sources.common]
+  PL180Mci.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  ArmLib
+  IoLib
+  TimerLib
+
+[Protocols]
+  gEfiCpuArchProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEmbeddedMmcHostProtocolGuid
+
+[Pcd]
+  gArmVExpressTokenSpaceGuid.PcdPL180SysMciRegAddress
+  gArmVExpressTokenSpaceGuid.PcdPL180MciBaseAddress
+
+[Depex]
+  gEfiCpuArchProtocolGuid
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 1/8] Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 2/8] Platform/ARM/VExpressPkg: incorporate PL180 driver Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-05-04 10:57   ` Leif Lindholm
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg Ard Biesheuvel
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

Juno is the only user of the SiI3132 SATA controller driver, which
is not quite fit for reuse in its current state. So incorporate it
into JunoPkg so we will be able to drop it from the core EDK2
repository.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
Reviewed-by: Leif Lindholm <leif@nuviainc.com>
---
 Platform/ARM/JunoPkg/ArmJuno.dec                                 |   4 +-
 Platform/ARM/JunoPkg/ArmJuno.dsc                                 |   2 +-
 Platform/ARM/JunoPkg/ArmJuno.fdf                                 |   2 +-
 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c      | 179 +++++
 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c        | 546 +++++++++++++
 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h        | 286 +++++++
 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf   |  38 +
 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c | 834 ++++++++++++++++++++
 8 files changed, 1888 insertions(+), 3 deletions(-)

diff --git a/Platform/ARM/JunoPkg/ArmJuno.dec b/Platform/ARM/JunoPkg/ArmJuno.dec
index 27fe75790721..37ea6857366f 100644
--- a/Platform/ARM/JunoPkg/ArmJuno.dec
+++ b/Platform/ARM/JunoPkg/ArmJuno.dec
@@ -28,6 +28,9 @@ [Guids.common]
 [PcdsFeatureFlag.common]
   gArmJunoTokenSpaceGuid.PcdPciMaxPayloadFixup|FALSE|BOOLEAN|0x00000013
 
+  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeaturePMPSupport|FALSE|BOOLEAN|0x00000018
+  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeatureDirectCommandIssuing|FALSE|BOOLEAN|0x00000019
+
 [PcdsFixedAtBuild.common]
   gArmJunoTokenSpaceGuid.PcdPcieControlBaseAddress|0x7FF20000|UINT64|0x0000000B
   gArmJunoTokenSpaceGuid.PcdPcieRootPortBaseAddress|0x7FF30000|UINT64|0x0000000C
@@ -54,4 +57,3 @@ [PcdsFixedAtBuild.common]
   #
   # For a list of mode numbers look in HdLcdArmJuno.c
   gArmJunoTokenSpaceGuid.PcdArmHdLcdMaxMode|0|UINT32|0x00000017
-
diff --git a/Platform/ARM/JunoPkg/ArmJuno.dsc b/Platform/ARM/JunoPkg/ArmJuno.dsc
index 954faca1bbfa..1c39da4897ed 100644
--- a/Platform/ARM/JunoPkg/ArmJuno.dsc
+++ b/Platform/ARM/JunoPkg/ArmJuno.dsc
@@ -314,7 +314,7 @@ [Components.common]
   # SATA Controller
   #
   MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
-  EmbeddedPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
+  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
 
   #
   # NVMe boot devices
diff --git a/Platform/ARM/JunoPkg/ArmJuno.fdf b/Platform/ARM/JunoPkg/ArmJuno.fdf
index 7c128b2c5bff..d771cbf35790 100644
--- a/Platform/ARM/JunoPkg/ArmJuno.fdf
+++ b/Platform/ARM/JunoPkg/ArmJuno.fdf
@@ -184,7 +184,7 @@ [FV.FvMain]
   # SATA Controller
   #
   INF MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
-  INF EmbeddedPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
+  INF Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
 
   #
   # NVMe boot devices
diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c
new file mode 100644
index 000000000000..944e827f7170
--- /dev/null
+++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c
@@ -0,0 +1,179 @@
+/** @file
+  UEFI Component Name(2) protocol implementation for Silicon Image I3132 SATA controller
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "SataSiI3132.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gSataSiI3132ComponentName = {
+  SataSiI3132ComponentNameGetDriverName,
+  SataSiI3132ComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gSataSiI3132ComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SataSiI3132ComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SataSiI3132ComponentNameGetControllerName,
+  "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSataSiI3132DriverNameTable[] = {
+  { "eng;en", L"Pci SATA Silicon Image 3132 Driver" },
+  { 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.
+
+**/
+EFI_STATUS
+EFIAPI
+SataSiI3132ComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mSataSiI3132DriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gSataSiI3132ComponentName)
+           );
+}
+
+/**
+  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 not a valid EFI_HANDLE.
+
+  @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.
+
+**/
+EFI_STATUS
+EFIAPI
+SataSiI3132ComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c
new file mode 100644
index 000000000000..ad7cc1cd75a1
--- /dev/null
+++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c
@@ -0,0 +1,546 @@
+/** @file
+  PCIe Sata support for the Silicon Image I3132
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "SataSiI3132.h"
+
+#include <IndustryStandard/Acpi10.h>
+
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/BaseLib.h>
+
+#define ACPI_SPECFLAG_PREFETCHABLE      0x06
+
+EFI_DRIVER_BINDING_PROTOCOL
+gSataSiI3132DriverBinding = {
+  SataSiI3132DriverBindingSupported,
+  SataSiI3132DriverBindingStart,
+  SataSiI3132DriverBindingStop,
+  0x30,
+  NULL,
+  NULL
+};
+
+EFI_STATUS
+SataSiI3132PortConstructor (
+  IN  SATA_SI3132_INSTANCE *SataSiI3132Instance,
+  IN  UINTN                Index
+  )
+{
+  EFI_STATUS            Status;
+  SATA_SI3132_PORT      *Port;
+  VOID                  *HostPRB;
+  EFI_PHYSICAL_ADDRESS  PhysAddrHostPRB;
+  VOID                  *PciAllocMappingPRB;
+  UINTN                 NumberOfBytes;
+
+  Port = &(SataSiI3132Instance->Ports[Index]);
+
+  Port->Index    = Index;
+  Port->RegBase  = Index * 0x2000;
+  Port->Instance = SataSiI3132Instance;
+  InitializeListHead (&(Port->Devices));
+
+  NumberOfBytes = sizeof (SATA_SI3132_PRB);
+  Status = SataSiI3132Instance->PciIo->AllocateBuffer (
+             SataSiI3132Instance->PciIo, AllocateAnyPages, EfiBootServicesData,
+             EFI_SIZE_TO_PAGES (NumberOfBytes), &HostPRB, 0
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Check the alignment of the PCI Buffer
+  ASSERT (((UINTN)HostPRB & (0x1000 - 1)) == 0);
+  Status = SataSiI3132Instance->PciIo->Map (
+             SataSiI3132Instance->PciIo, EfiPciIoOperationBusMasterCommonBuffer, HostPRB,
+             &NumberOfBytes, &PhysAddrHostPRB, &PciAllocMappingPRB
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Port->HostPRB            = HostPRB;
+  Port->PhysAddrHostPRB    = PhysAddrHostPRB;
+  Port->PciAllocMappingPRB = PciAllocMappingPRB;
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SataSiI3132Constructor (
+  IN  EFI_PCI_IO_PROTOCOL     *PciIo,
+  OUT SATA_SI3132_INSTANCE**  SataSiI3132Instance
+  )
+{
+  SATA_SI3132_INSTANCE    *Instance;
+  EFI_ATA_PASS_THRU_MODE  *AtaPassThruMode;
+
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Instance = (SATA_SI3132_INSTANCE*)AllocateZeroPool (sizeof (SATA_SI3132_INSTANCE));
+  if (Instance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Instance->Signature           = SATA_SII3132_SIGNATURE;
+  Instance->PciIo               = PciIo;
+
+  AtaPassThruMode = (EFI_ATA_PASS_THRU_MODE*)AllocatePool (sizeof (EFI_ATA_PASS_THRU_MODE));
+  AtaPassThruMode->Attributes = EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL;
+  AtaPassThruMode->IoAlign = 0x1000;
+
+  // Initialize SiI3132 ports
+  SataSiI3132PortConstructor (Instance, 0);
+  SataSiI3132PortConstructor (Instance, 1);
+
+  // Set ATA Pass Thru Protocol
+  Instance->AtaPassThruProtocol.Mode            = AtaPassThruMode;
+  Instance->AtaPassThruProtocol.PassThru        = SiI3132AtaPassThru;
+  Instance->AtaPassThruProtocol.GetNextPort     = SiI3132GetNextPort;
+  Instance->AtaPassThruProtocol.GetNextDevice   = SiI3132GetNextDevice;
+  Instance->AtaPassThruProtocol.BuildDevicePath = SiI3132BuildDevicePath;
+  Instance->AtaPassThruProtocol.GetDevice       = SiI3132GetDevice;
+  Instance->AtaPassThruProtocol.ResetPort       = SiI3132ResetPort;
+  Instance->AtaPassThruProtocol.ResetDevice     = SiI3132ResetDevice;
+
+  *SataSiI3132Instance = Instance;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SiI3132SoftResetCommand (
+  IN   SATA_SI3132_PORT *Port,
+  OUT  UINT32* Signature
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_ATA_PASS_THRU_COMMAND_PACKET  Packet;
+  EFI_ATA_STATUS_BLOCK              Asb;
+  EFI_ATA_COMMAND_BLOCK             Acb;
+  CONST UINT16                      PortMultiplierPort = 0;
+
+  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
+
+  Acb.Reserved1[1] = 0;
+
+  Packet.Asb      = &Asb;
+  Packet.Acb      = &Acb;
+  Packet.Timeout  = 100000;
+  Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET;
+
+  Status = SiI3132AtaPassThruCommand (Port->Instance, Port, PortMultiplierPort, &Packet, 0);
+
+  if (Status == EFI_SUCCESS) {
+    *Signature = (Asb.AtaCylinderHigh << 24) | (Asb.AtaCylinderLow << 16) |
+                 (Asb.AtaSectorNumber << 8 ) | (Asb.AtaSectorCount);
+  }
+  return Status;
+}
+
+EFI_STATUS
+SataSiI3132PortInitialization (
+  IN SATA_SI3132_PORT *Port
+  )
+{
+  UINT32                  Value32;
+  SATA_SI3132_DEVICE*     Device;
+  UINT32                  Signature;
+  EFI_STATUS              Status;
+  EFI_PCI_IO_PROTOCOL*    PciIo;
+
+  Status = SiI3132HwResetPort (Port);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  PciIo = Port->Instance->PciIo;
+
+  // Is a device is present ?
+  Status = SATA_PORT_READ32 (Port->RegBase + SII3132_PORT_SSTATUS_REG, &Value32);
+  if (!EFI_ERROR (Status) && (Value32 & 0x3)) {
+    // Do a soft reset to see if it is a port multiplier
+    SATA_TRACE ("SataSiI3132PortInitialization: soft reset - it is a port multiplier\n");
+    Status = SiI3132SoftResetCommand (Port, &Signature);
+    if (!EFI_ERROR (Status)) {
+      if (Signature == SII3132_PORT_SIGNATURE_PMP) {
+        SATA_TRACE ("SataSiI3132PortInitialization(): a Port Multiplier is present");
+        if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
+          ASSERT (0); // Not supported yet
+        } else {
+          return EFI_UNSUPPORTED;
+        }
+      } else if (Signature == SII3132_PORT_SIGNATURE_ATAPI) {
+        ASSERT (0); // Not supported yet
+        SATA_TRACE ("SataSiI3132PortInitialization(): an ATAPI device is present");
+        return EFI_UNSUPPORTED;
+      } else if (Signature == SII3132_PORT_SIGNATURE_ATA) {
+        SATA_TRACE ("SataSiI3132PortInitialization(): an ATA device is present");
+      } else {
+        SATA_TRACE ("SataSiI3132PortInitialization(): Present device unknown!");
+        ASSERT (0); // Not supported
+        return EFI_UNSUPPORTED;
+      }
+
+      // Create Device
+      Device            = (SATA_SI3132_DEVICE*)AllocatePool (sizeof (SATA_SI3132_DEVICE));
+      Device->Index     = Port->Index; //TODO: Could need to be fixed when SATA Port Multiplier support
+      Device->Port      = Port;
+      Device->BlockSize = 0;
+
+      // Attached the device to the Sata Port
+      InsertTailList (&Port->Devices, &Device->Link);
+
+      SATA_TRACE ("SataSiI3132PortInitialization(): Port Ready");
+    }
+  }
+  return Status;
+}
+
+EFI_STATUS
+SataSiI3132Initialization (
+  IN SATA_SI3132_INSTANCE* SataSiI3132Instance
+  )
+{
+  UINTN                 Index;
+  EFI_PCI_IO_PROTOCOL*  PciIo;
+
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PciIo = SataSiI3132Instance->PciIo;
+
+  // Turn Off GPIO
+  SATA_GLOBAL_WRITE32 (SII3132_GLOBAL_FLASHADDR_REG, 0x0);
+
+  // Clear Global Control Register
+  SATA_GLOBAL_WRITE32 (SII3132_GLOBAL_CONTROL_REG, 0x0);
+
+  for (Index = 0; Index < SATA_SII3132_MAXPORT; Index++) {
+    SataSiI3132PortInitialization (&(SataSiI3132Instance->Ports[Index]));
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Test to see if this driver supports ControllerHandle.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to test.
+  @param  RemainingDevicePath  Not used.
+
+  @return EFI_SUCCESS          This driver supports this device.
+  @return EFI_UNSUPPORTED      This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+SataSiI3132DriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  )
+{
+  EFI_STATUS              Status;
+  EFI_PCI_IO_PROTOCOL     *PciIo;
+  UINT32                   PciID;
+
+  //
+  // Test whether there is PCI IO Protocol attached on the controller handle.
+  //
+  Status = gBS->OpenProtocol (
+                Controller,
+                &gEfiPciIoProtocolGuid,
+                (VOID **) &PciIo,
+                This->DriverBindingHandle,
+                Controller,
+                EFI_OPEN_PROTOCOL_BY_DRIVER
+                );
+  if (EFI_ERROR (Status)) {
+      return Status;
+  }
+
+  Status = PciIo->Pci.Read (
+                      PciIo,
+                      EfiPciIoWidthUint32,
+                      PCI_VENDOR_ID_OFFSET,
+                      1,
+                      &PciID
+                      );
+  if (EFI_ERROR (Status)) {
+      Status = EFI_UNSUPPORTED;
+      goto ON_EXIT;
+  }
+
+  //
+  // Test whether the controller belongs to SATA Mass Storage type
+  //
+  if (PciID != ((SATA_SII3132_DEVICE_ID << 16) | SATA_SII3132_VENDOR_ID)) {
+      Status = EFI_UNSUPPORTED;
+  }
+
+ON_EXIT:
+  gBS->CloseProtocol (
+       Controller,
+       &gEfiPciIoProtocolGuid,
+       This->DriverBindingHandle,
+       Controller
+       );
+
+  return Status;
+}
+
+BOOLEAN mbStarted = FALSE;
+
+/**
+  Starting the Pci SATA Driver.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to test.
+  @param  RemainingDevicePath  Not used.
+
+  @return EFI_SUCCESS          supports this device.
+  @return EFI_UNSUPPORTED      do not support this device.
+  @return EFI_DEVICE_ERROR     cannot be started due to device Error.
+  @return EFI_OUT_OF_RESOURCES cannot allocate resources.
+
+**/
+EFI_STATUS
+EFIAPI
+SataSiI3132DriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  )
+{
+  EFI_STATUS              Status;
+  EFI_PCI_IO_PROTOCOL     *PciIo;
+  UINT64                  Supports;
+  UINT64                  OriginalPciAttributes;
+  BOOLEAN                 PciAttributesSaved;
+  UINT32                  PciID;
+  SATA_SI3132_INSTANCE    *SataSiI3132Instance = NULL;
+
+  SATA_TRACE ("SataSiI3132DriverBindingStart()");
+
+  //TODO: Find a nicer way to do it !
+  if (mbStarted) {
+    return EFI_SUCCESS; // Don't restart me !
+  }
+
+  //
+  // Open the PciIo Protocol
+  //
+  Status = gBS->OpenProtocol (
+                Controller,
+                &gEfiPciIoProtocolGuid,
+                (VOID **) &PciIo,
+                This->DriverBindingHandle,
+                Controller,
+                EFI_OPEN_PROTOCOL_BY_DRIVER
+                );
+  if (EFI_ERROR (Status)) {
+      return Status;
+  }
+
+  PciAttributesSaved = FALSE;
+  //
+  // Save original PCI attributes
+  //
+  Status = PciIo->Attributes (
+                  PciIo,
+                  EfiPciIoAttributeOperationGet,
+                  0,
+                  &OriginalPciAttributes
+                  );
+  if (EFI_ERROR (Status)) {
+      goto CLOSE_PCIIO;
+  }
+  PciAttributesSaved = TRUE;
+
+  Status = PciIo->Attributes (
+                  PciIo,
+                  EfiPciIoAttributeOperationSupported,
+                  0,
+                  &Supports
+                  );
+  if (!EFI_ERROR (Status)) {
+      Supports &= EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE;
+      Status = PciIo->Attributes (
+                        PciIo,
+                        EfiPciIoAttributeOperationEnable,
+                        Supports,
+                        NULL
+                        );
+  }
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "SataSiI3132DriverBindingStart: failed to enable controller\n"));
+    goto CLOSE_PCIIO;
+  }
+
+  //
+  // Get the Pci device class code.
+  //
+  Status = PciIo->Pci.Read (
+                      PciIo,
+                      EfiPciIoWidthUint32,
+                      PCI_VENDOR_ID_OFFSET,
+                      1,
+                      &PciID
+                      );
+  if (EFI_ERROR (Status)) {
+    Status = EFI_UNSUPPORTED;
+    goto CLOSE_PCIIO;
+  }
+
+  //
+  // Test whether the controller belongs to SATA Mass Storage type
+  //
+  if (PciID != ((SATA_SII3132_DEVICE_ID << 16) | SATA_SII3132_VENDOR_ID)) {
+    Status = EFI_UNSUPPORTED;
+    goto CLOSE_PCIIO;
+  }
+
+  // Create SiI3132 Sata Instance
+  Status = SataSiI3132Constructor (PciIo, &SataSiI3132Instance);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Initialize SiI3132 Sata Controller
+  Status = SataSiI3132Initialization (SataSiI3132Instance);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Install Ata Pass Thru Protocol
+  Status = gBS->InstallProtocolInterface (
+              &Controller,
+              &gEfiAtaPassThruProtocolGuid,
+              EFI_NATIVE_INTERFACE,
+              &(SataSiI3132Instance->AtaPassThruProtocol)
+              );
+  if (EFI_ERROR (Status)) {
+    goto FREE_POOL;
+  }
+
+/*  //
+  // Create event to stop the HC when exit boot service.
+  //
+  Status = gBS->CreateEventEx (
+                EVT_NOTIFY_SIGNAL,
+                TPL_NOTIFY,
+                EhcExitBootService,
+                Ehc,
+                &gEfiEventExitBootServicesGuid,
+                &Ehc->ExitBootServiceEvent
+                );
+  if (EFI_ERROR (Status)) {
+      goto UNINSTALL_USBHC;
+  }*/
+
+  mbStarted = TRUE;
+
+  SATA_TRACE ("SataSiI3132DriverBindingStart() Success!");
+  return EFI_SUCCESS;
+
+FREE_POOL:
+  //TODO: Free SATA Instance
+
+CLOSE_PCIIO:
+  if (PciAttributesSaved) {
+      //
+      // Restore original PCI attributes
+      //
+      PciIo->Attributes (
+                      PciIo,
+                      EfiPciIoAttributeOperationSet,
+                      OriginalPciAttributes,
+                      NULL
+                      );
+  }
+
+  gBS->CloseProtocol (
+       Controller,
+       &gEfiPciIoProtocolGuid,
+       This->DriverBindingHandle,
+       Controller
+       );
+
+  return Status;
+}
+
+/**
+  Stop this driver on ControllerHandle. Support stopping any child handles
+  created by this driver.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to stop driver on.
+  @param  NumberOfChildren     Number of Children in the ChildHandleBuffer.
+  @param  ChildHandleBuffer    List of handles for the children we need to stop.
+
+  @return EFI_SUCCESS          Success.
+  @return EFI_DEVICE_ERROR     Fail.
+
+**/
+EFI_STATUS
+EFIAPI
+SataSiI3132DriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  )
+{
+  SATA_TRACE ("SataSiI3132DriverBindingStop()");
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Entry point of this driver
+
+  @param ImageHandle     Handle of driver image
+  @param SystemTable     Point to EFI_SYSTEM_TABLE
+
+  @retval EFI_OUT_OF_RESOURCES  Can not allocate memory resource
+  @retval EFI_DEVICE_ERROR      Can not install the protocol instance
+  @retval EFI_SUCCESS           Success to initialize the Pci host bridge.
+**/
+EFI_STATUS
+EFIAPI
+InitializeSataSiI3132 (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  SATA_TRACE ("InitializeSataSiI3132 ()");
+
+  return EfiLibInstallDriverBindingComponentName2 (
+         ImageHandle,
+         SystemTable,
+         &gSataSiI3132DriverBinding,
+         ImageHandle,
+         &gSataSiI3132ComponentName,
+         &gSataSiI3132ComponentName2
+         );
+}
diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h
new file mode 100644
index 000000000000..f799fe84a047
--- /dev/null
+++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h
@@ -0,0 +1,286 @@
+/** @file
+  Header containing the structure specific to the Silicon Image I3132 Sata PCI card
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __SATASII3132_H
+#define __SATASII3132_H
+
+#include <PiDxe.h>
+
+#include <Protocol/AtaPassThru.h>
+#include <Protocol/PciIo.h>
+
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <IndustryStandard/Pci.h>
+
+#define SATA_SII3132_DEVICE_ID      0x3132
+#define SATA_SII3132_VENDOR_ID      0x1095
+
+#define SII3132_PORT_SIGNATURE_PMP      0x96690101
+#define SII3132_PORT_SIGNATURE_ATAPI    0xEB140101
+#define SII3132_PORT_SIGNATURE_ATA      0x00000101
+
+/*
+ * Silicon Image SiI3132 Registers
+ */
+#define SII3132_GLOBAL_CONTROL_REG              0x40
+#define SII3132_GLOBAL_FLASHADDR_REG            0x70
+
+#define SII3132_PORT_STATUS_REG                 0x1000
+#define SII3132_PORT_CONTROLSET_REG             0x1000
+#define SII3132_PORT_CONTROLCLEAR_REG           0x1004
+#define SII3132_PORT_INTSTATUS_REG              0x1008
+#define SII3132_PORT_ENABLEINT_REG              0x1010
+#define SII3132_PORT_INTCLEAR_REG               0x1014
+#define SII3132_PORT_32BITACTIVADDR_REG         0x101C
+#define SII3132_PORT_CMDEXECFIFO_REG            0x1020
+#define SII3132_PORT_CMDERROR_REG               0x1024
+#define SII3132_PORT_ERRCOUNTDECODE             0x1040
+#define SII3132_PORT_ERRCOUNTCRC                0x1044
+#define SII3132_PORT_ERRCOUNTHANDSHAKE          0x1048
+#define SII3132_PORT_SLOTSTATUS_REG             0x1800
+#define SII3132_PORT_CMDACTIV_REG               0x1C00
+#define SII3132_PORT_SSTATUS_REG                0x1F04
+
+#define SII3132_PORT_CONTROL_RESET              (1 << 0)
+#define SII3132_PORT_DEVICE_RESET               (1 << 1)
+#define SII3132_PORT_CONTROL_INT                (1 << 2)
+#define SII3132_PORT_CONTROL_32BITACTIVATION    (1 << 10)
+
+#define SII3132_PORT_STATUS_PORTREADY           0x80000000
+
+#define SII3132_PORT_INT_CMDCOMPL               (1 << 0)
+#define SII3132_PORT_INT_CMDERR                 (1 << 1)
+#define SII3132_PORT_INT_PORTRDY                (1 << 2)
+
+#define SATA_SII3132_MAXPORT    2
+
+#define PRB_CTRL_ATA            0x0
+#define PRB_CTRL_PROT_OVERRIDE  0x1
+#define PRB_CTRL_RESTRANSMIT    0x2
+#define PRB_CTRL_EXT_CMD        0x4
+#define PRB_CTRL_RCV            0x8
+#define PRB_CTRL_PKT_READ       0x10
+#define PRB_CTRL_PKT_WRITE      0x20
+#define PRB_CTRL_INT_MASK       0x40
+#define PRB_CTRL_SRST           0x80
+
+#define PRB_PROT_PACKET         0x01
+#define PRB_PROT_LEGACY_QUEUE   0x02
+#define PRB_PROT_NATIVE_QUEUE   0x04
+#define PRB_PROT_READ           0x08
+#define PRB_PROT_WRITE          0x10
+#define PRB_PROT_TRANSPARENT    0x20
+
+#define SGE_XCF     (1 << 28)
+#define SGE_DRD     (1 << 29)
+#define SGE_LNK     (1 << 30)
+#define SGE_TRM     0x80000000
+
+typedef struct _SATA_SI3132_SGE {
+    UINT32      DataAddressLow;
+    UINT32      DataAddressHigh;
+    UINT32      DataCount;
+    UINT32      Attributes;
+} SATA_SI3132_SGE;
+
+typedef struct _SATA_SI3132_FIS {
+    UINT8               FisType;
+    UINT8               Control;
+    UINT8               Command;
+    UINT8               Features;
+    UINT8               Fis[5 * 4];
+} SATA_SI3132_FIS;
+
+typedef struct _SATA_SI3132_PRB {
+    UINT16              Control;
+    UINT16              ProtocolOverride;
+    UINT32              RecTransCount;
+    SATA_SI3132_FIS     Fis;
+    SATA_SI3132_SGE     Sge[2];
+} SATA_SI3132_PRB;
+
+typedef struct _SATA_SI3132_DEVICE {
+    LIST_ENTRY                  Link; // This attribute must be the first entry of this structure (to avoid pointer computation)
+    UINTN                       Index;
+    struct _SATA_SI3132_PORT    *Port;  //Parent Port
+    UINT32                      BlockSize;
+} SATA_SI3132_DEVICE;
+
+typedef struct _SATA_SI3132_PORT {
+    UINTN                           Index;
+    UINTN                           RegBase;
+    struct _SATA_SI3132_INSTANCE    *Instance;
+
+    //TODO: Support Port multiplier
+    LIST_ENTRY                      Devices;
+
+    SATA_SI3132_PRB*                HostPRB;
+    EFI_PHYSICAL_ADDRESS            PhysAddrHostPRB;
+    VOID*                           PciAllocMappingPRB;
+} SATA_SI3132_PORT;
+
+typedef struct _SATA_SI3132_INSTANCE {
+    UINTN                       Signature;
+
+    SATA_SI3132_PORT            Ports[SATA_SII3132_MAXPORT];
+
+    EFI_ATA_PASS_THRU_PROTOCOL  AtaPassThruProtocol;
+
+    EFI_PCI_IO_PROTOCOL         *PciIo;
+} SATA_SI3132_INSTANCE;
+
+#define SATA_SII3132_SIGNATURE              SIGNATURE_32('s', 'i', '3', '2')
+#define INSTANCE_FROM_ATAPASSTHRU_THIS(a)   CR(a, SATA_SI3132_INSTANCE, AtaPassThruProtocol, SATA_SII3132_SIGNATURE)
+
+#define SATA_GLOBAL_READ32(Offset, Value)  PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 0, Offset, 1, Value)
+#define SATA_GLOBAL_WRITE32(Offset, Value) { UINT32 Value32 = Value; PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 0, Offset, 1, &Value32); }
+
+#define SATA_PORT_READ32(Offset, Value)  PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 1, Offset, 1, Value)
+#define SATA_PORT_WRITE32(Offset, Value) { UINT32 Value32 = Value; PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 1, Offset, 1, &Value32); }
+
+#define SATA_TRACE(txt)  DEBUG((EFI_D_VERBOSE, "ARM_SATA: " txt "\n"))
+
+extern EFI_COMPONENT_NAME_PROTOCOL  gSataSiI3132ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gSataSiI3132ComponentName2;
+
+/*
+ * Component Name Protocol Functions
+ */
+EFI_STATUS
+EFIAPI
+SataSiI3132ComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+SataSiI3132ComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+EFI_STATUS SiI3132HwResetPort (SATA_SI3132_PORT *Port);
+
+/*
+ * Driver Binding Protocol Functions
+ */
+EFI_STATUS
+EFIAPI
+SataSiI3132DriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+SataSiI3132DriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+SataSiI3132DriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132AtaPassThruCommand (
+  IN     SATA_SI3132_INSTANCE             *pSataSiI3132Instance,
+  IN     SATA_SI3132_PORT                 *pSataPort,
+  IN     UINT16                           PortMultiplierPort,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
+  IN     EFI_EVENT                        Event OPTIONAL
+  );
+
+/**
+ * EFI ATA Pass Thru Protocol
+ */
+EFI_STATUS
+EFIAPI
+SiI3132AtaPassThru (
+  IN     EFI_ATA_PASS_THRU_PROTOCOL       *This,
+  IN     UINT16                           Port,
+  IN     UINT16                           PortMultiplierPort,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
+  IN     EFI_EVENT                        Event OPTIONAL
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132GetNextPort (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT16                 *Port
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132GetNextDevice (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN UINT16                     Port,
+  IN OUT UINT16                 *PortMultiplierPort
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132BuildDevicePath (
+  IN     EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN     UINT16                     Port,
+  IN     UINT16                     PortMultiplierPort,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL   **DevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132GetDevice (
+  IN  EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL   *DevicePath,
+  OUT UINT16                     *Port,
+  OUT UINT16                     *PortMultiplierPort
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132ResetPort (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN UINT16                     Port
+  );
+
+EFI_STATUS
+EFIAPI
+SiI3132ResetDevice (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN UINT16                     Port,
+  IN UINT16                     PortMultiplierPort
+  );
+
+#endif
diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
new file mode 100644
index 000000000000..a73b3bc168a8
--- /dev/null
+++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
@@ -0,0 +1,38 @@
+#/** @file
+#  INF file for the Silicon Image I3132 SATA controller
+#
+#  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 1.27
+  BASE_NAME                      = SataSiI3132Dxe
+  FILE_GUID                      = 1df18da0-a18b-11df-8c3a-0002a5d5c51b
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = InitializeSataSiI3132
+
+[Packages]
+  MdePkg/MdePkg.dec
+  Platform/ARM/JunoPkg/ArmJuno.dec
+
+[LibraryClasses]
+  MemoryAllocationLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Sources]
+  ComponentName.c
+  SataSiI3132.c
+  SiI3132AtaPassThru.c
+
+[Protocols]
+  gEfiPciIoProtocolGuid                         # CONSUMED
+  gEfiAtaPassThruProtocolGuid                   # PRODUCED
+
+[Pcd]
+  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeaturePMPSupport
+  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeatureDirectCommandIssuing
diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
new file mode 100644
index 000000000000..f15b59788310
--- /dev/null
+++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
@@ -0,0 +1,834 @@
+/** @file
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "SataSiI3132.h"
+
+#include <IndustryStandard/Atapi.h>
+#include <Library/DevicePathLib.h>
+
+SATA_SI3132_DEVICE*
+GetSataDevice (
+  IN  SATA_SI3132_INSTANCE* SataInstance,
+  IN  UINT16 Port,
+  IN  UINT16 PortMultiplierPort
+) {
+  LIST_ENTRY              *List;
+  SATA_SI3132_PORT        *SataPort;
+  SATA_SI3132_DEVICE      *SataDevice;
+
+  if (Port >= SATA_SII3132_MAXPORT) {
+    return NULL;
+  }
+
+  SataPort = &(SataInstance->Ports[Port]);
+  List = SataPort->Devices.ForwardLink;
+
+  while (List != &SataPort->Devices) {
+    SataDevice = (SATA_SI3132_DEVICE*)List;
+    if (SataDevice->Index == PortMultiplierPort) {
+      return SataDevice;
+    }
+    List = List->ForwardLink;
+  }
+  return NULL;
+}
+
+EFI_STATUS
+EFIAPI
+SiI3132AtaPassThruCommand (
+  IN     SATA_SI3132_INSTANCE             *SataSiI3132Instance,
+  IN     SATA_SI3132_PORT                 *SataPort,
+  IN     UINT16                           PortMultiplierPort,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
+  IN     EFI_EVENT                        Event OPTIONAL
+  )
+{
+  SATA_SI3132_DEVICE      *SataDevice;
+  EFI_PHYSICAL_ADDRESS    PhysInDataBuffer;
+  UINTN                   InDataBufferLength = 0;
+  EFI_PHYSICAL_ADDRESS    PhysOutDataBuffer;
+  UINTN                   OutDataBufferLength;
+  CONST UINTN             EmptySlot = 0;
+  UINTN                   Control = PRB_CTRL_ATA;
+  UINTN                   Protocol = 0;
+  UINT32                  Value32, Error, Timeout = 0;
+  CONST UINT32            IrqMask = (SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR) << 16;
+  EFI_STATUS              Status;
+  VOID*                   PciAllocMapping = NULL;
+  EFI_PCI_IO_PROTOCOL     *PciIo;
+
+  PciIo = SataSiI3132Instance->PciIo;
+  ZeroMem (SataPort->HostPRB, sizeof (SATA_SI3132_PRB));
+
+  // Construct Si3132 PRB
+  switch (Packet->Protocol) {
+  case EFI_ATA_PASS_THRU_PROTOCOL_ATA_HARDWARE_RESET:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET:
+    SATA_TRACE ("SiI3132AtaPassThru() EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET");
+    Control = PRB_CTRL_SRST;
+
+    if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
+        SataPort->HostPRB->Fis.Control = 0x0F;
+    }
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:
+    ASSERT (0); //TODO: Implement me!
+    break;
+
+  // There is no difference for SiI3132 between PIO and DMA invokation
+  case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN:
+  case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:
+    // Fixup the size for block transfer. Following UEFI Specification, 'InTransferLength' should
+    // be in number of bytes. But for most data transfer commands, the value is in number of blocks
+    if (Packet->Acb->AtaCommand == ATA_CMD_IDENTIFY_DRIVE) {
+      InDataBufferLength = Packet->InTransferLength;
+    } else {
+      SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);
+      if (!SataDevice || (SataDevice->BlockSize == 0)) {
+        return EFI_INVALID_PARAMETER;
+      }
+
+      InDataBufferLength = Packet->InTransferLength * SataDevice->BlockSize;
+    }
+
+    Status = PciIo->Map (
+               PciIo, EfiPciIoOperationBusMasterWrite,
+               Packet->InDataBuffer, &InDataBufferLength, &PhysInDataBuffer, &PciAllocMapping
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    // Construct SGEs (32-bit system)
+    SataPort->HostPRB->Sge[0].DataAddressLow = (UINT32)PhysInDataBuffer;
+    SataPort->HostPRB->Sge[0].DataAddressHigh = (UINT32)(PhysInDataBuffer >> 32);
+    SataPort->HostPRB->Sge[0].Attributes = SGE_TRM; // Only one SGE
+    SataPort->HostPRB->Sge[0].DataCount = InDataBufferLength;
+
+    // Copy the Ata Command Block
+    CopyMem (&SataPort->HostPRB->Fis, Packet->Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
+
+    // Fixup the FIS
+    SataPort->HostPRB->Fis.FisType = 0x27; // Register - Host to Device FIS
+    SataPort->HostPRB->Fis.Control = 1 << 7; // Is a command
+    if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
+      SataPort->HostPRB->Fis.Control |= PortMultiplierPort & 0xFF;
+    }
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT:
+  case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:
+    SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);
+    if (!SataDevice || (SataDevice->BlockSize == 0)) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    // Fixup the size for block transfer. Following UEFI Specification, 'InTransferLength' should
+    // be in number of bytes. But for most data transfer commands, the value is in number of blocks
+    OutDataBufferLength = Packet->OutTransferLength * SataDevice->BlockSize;
+
+    Status = PciIo->Map (
+               PciIo, EfiPciIoOperationBusMasterRead,
+               Packet->OutDataBuffer, &OutDataBufferLength, &PhysOutDataBuffer, &PciAllocMapping
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    // Construct SGEs (32-bit system)
+    SataPort->HostPRB->Sge[0].DataAddressLow  = (UINT32)PhysOutDataBuffer;
+    SataPort->HostPRB->Sge[0].DataAddressHigh = (UINT32)(PhysOutDataBuffer >> 32);
+    SataPort->HostPRB->Sge[0].Attributes      = SGE_TRM; // Only one SGE
+    SataPort->HostPRB->Sge[0].DataCount       = OutDataBufferLength;
+
+    // Copy the Ata Command Block
+    CopyMem (&SataPort->HostPRB->Fis, Packet->Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
+
+    // Fixup the FIS
+    SataPort->HostPRB->Fis.FisType = 0x27; // Register - Host to Device FIS
+    SataPort->HostPRB->Fis.Control = 1 << 7; // Is a command
+    if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
+      SataPort->HostPRB->Fis.Control |= PortMultiplierPort & 0xFF;
+    }
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_DMA:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_DMA_QUEUED:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_DIAGNOSTIC:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_RESET:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_FPDMA:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  case EFI_ATA_PASS_THRU_PROTOCOL_RETURN_RESPONSE:
+    ASSERT (0); //TODO: Implement me!
+    break;
+  default:
+    ASSERT (0);
+    break;
+  }
+
+  SataPort->HostPRB->Control = Control;
+  SataPort->HostPRB->ProtocolOverride = Protocol;
+
+  // Clear IRQ
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, IrqMask);
+
+  if (!FeaturePcdGet (PcdSataSiI3132FeatureDirectCommandIssuing)) {
+    // Indirect Command Issuance
+
+    //TODO: Find which slot is free (maybe use the Cmd FIFO)
+    //SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_CMDEXECFIFO_REG, &EmptySlot);
+
+    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDACTIV_REG + (EmptySlot * 8),
+                     (UINT32)(SataPort->PhysAddrHostPRB & 0xFFFFFFFF));
+    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDACTIV_REG + (EmptySlot * 8) + 4,
+                     (UINT32)((SataPort->PhysAddrHostPRB >> 32) & 0xFFFFFFFF));
+  } else {
+    // Direct Command Issuance
+    Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 1, // Bar 1
+        SataPort->RegBase + (EmptySlot * 0x80),
+        sizeof (SATA_SI3132_PRB) / 4,
+        SataPort->HostPRB);
+    ASSERT_EFI_ERROR (Status);
+
+    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDEXECFIFO_REG, EmptySlot);
+  }
+
+#if 0
+  // Could need to be implemented if we run multiple command in parallel to know which slot has been completed
+  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_SLOTSTATUS_REG, &Value32);
+  Timeout = Packet->Timeout;
+  while (!Timeout && !Value32) {
+    gBS->Stall (1);
+    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_SLOTSTATUS_REG, &Value32);
+    Timeout--;
+  }
+#else
+  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
+  if (!Packet->Timeout) {
+    while (!(Value32 & IrqMask)) {
+      gBS->Stall (1);
+      SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
+    }
+  } else {
+    Timeout = Packet->Timeout;
+    while (Timeout && !(Value32 & IrqMask)) {
+      gBS->Stall (1);
+      SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
+      Timeout--;
+    }
+  }
+#endif
+  // Fill Packet Ata Status Block
+  Status = PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 1, // Bar 1
+      SataPort->RegBase + 0x08,
+      sizeof (EFI_ATA_STATUS_BLOCK) / 4,
+      Packet->Asb);
+  ASSERT_EFI_ERROR (Status);
+
+
+  if ((Packet->Timeout != 0) && (Timeout == 0)) {
+    DEBUG ((EFI_D_ERROR, "SiI3132AtaPassThru() Err:Timeout\n"));
+    //ASSERT (0);
+    return EFI_TIMEOUT;
+  } else if (Value32 & (SII3132_PORT_INT_CMDERR << 16)) {
+    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_CMDERROR_REG, &Error);
+    DEBUG ((EFI_D_ERROR, "SiI3132AtaPassThru() CmdErr:0x%X (SiI3132 Err:0x%X)\n", Value32, Error));
+    ASSERT (0);
+    return EFI_DEVICE_ERROR;
+  } else if (Value32 & (SII3132_PORT_INT_CMDCOMPL << 16)) {
+    // Clear Command Complete
+    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, SII3132_PORT_INT_CMDCOMPL << 16);
+
+    if (PciAllocMapping) {
+      Status = PciIo->Unmap (PciIo, PciAllocMapping);
+      ASSERT (!EFI_ERROR (Status));
+    }
+
+    // If the command was ATA_CMD_IDENTIFY_DRIVE then we need to update the BlockSize
+    if (Packet->Acb->AtaCommand == ATA_CMD_IDENTIFY_DRIVE) {
+      ATA_IDENTIFY_DATA *IdentifyData = (ATA_IDENTIFY_DATA*)Packet->InDataBuffer;
+
+      // Get the corresponding Block Device
+      SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);
+
+      // Check logical block size
+      if ((IdentifyData->phy_logic_sector_support & BIT12) != 0) {
+        ASSERT (SataDevice != NULL);
+        SataDevice->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |
+                                            IdentifyData->logic_sector_size_lo) * sizeof (UINT16));
+      } else {
+        SataDevice->BlockSize = 0x200;
+      }
+    }
+    return EFI_SUCCESS;
+  } else {
+    ASSERT (0);
+    return EFI_DEVICE_ERROR;
+  }
+}
+
+/**
+  Sends an ATA command to an ATA device that is attached to the ATA controller. This function
+  supports both blocking I/O and non-blocking I/O. The blocking I/O functionality is required,
+  and the non-blocking I/O functionality is optional.
+
+  @param[in]     This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in]     Port                The port number of the ATA device to send the command.
+  @param[in]     PortMultiplierPort  The port multiplier port number of the ATA device to send the command.
+                                     If there is no port multiplier, then specify 0.
+  @param[in,out] Packet              A pointer to the ATA command to send to the ATA device specified by Port
+                                     and PortMultiplierPort.
+  @param[in]     Event               If non-blocking I/O is not supported then Event is ignored, and blocking
+                                     I/O is performed. If Event is NULL, then blocking I/O is performed. If
+                                     Event is not NULL and non blocking I/O is supported, then non-blocking
+                                     I/O is performed, and Event will be signaled when the ATA command completes.
+
+  @retval EFI_SUCCESS                The ATA command was sent by the host. For bi-directional commands,
+                                     InTransferLength bytes were transferred from InDataBuffer. For write and
+                                     bi-directional commands, OutTransferLength bytes were transferred by OutDataBuffer.
+  @retval EFI_BAD_BUFFER_SIZE        The ATA command was not executed. The number of bytes that could be transferred
+                                     is returned in InTransferLength. For write and bi-directional commands,
+                                     OutTransferLength bytes were transferred by OutDataBuffer.
+  @retval EFI_NOT_READY              The ATA command could not be sent because there are too many ATA commands
+                                     already queued. The caller may retry again later.
+  @retval EFI_DEVICE_ERROR           A device error occurred while attempting to send the ATA command.
+  @retval EFI_INVALID_PARAMETER      Port, PortMultiplierPort, or the contents of Acb are invalid. The ATA
+                                     command was not sent, so no additional status information is available.
+
+**/
+EFI_STATUS
+EFIAPI
+SiI3132AtaPassThru (
+  IN     EFI_ATA_PASS_THRU_PROTOCOL       *This,
+  IN     UINT16                           Port,
+  IN     UINT16                           PortMultiplierPort,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
+  IN     EFI_EVENT                        Event OPTIONAL
+  )
+{
+  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
+  SATA_SI3132_DEVICE      *SataDevice;
+  SATA_SI3132_PORT        *SataPort;
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);
+  if (!SataDevice) {
+    return EFI_INVALID_PARAMETER;
+  }
+  SataPort = SataDevice->Port;
+
+  DEBUG ((EFI_D_INFO, "SiI3132AtaPassThru(%d,%d) : AtaCmd:0x%X Prot:%d\n", Port, PortMultiplierPort,
+         Packet->Acb->AtaCommand, Packet->Protocol));
+
+  return SiI3132AtaPassThruCommand (SataSiI3132Instance, SataPort, PortMultiplierPort, Packet, Event);
+}
+
+/**
+  Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.
+  These can either be the list of ports where ATA devices are actually present or the
+  list of legal port numbers for the ATA controller. Regardless, the caller of this
+  function must probe the port number returned to see if an ATA device is actually
+  present at that location on the ATA controller.
+
+  The GetNextPort() function retrieves the port number on an ATA controller. If on input
+  Port is 0xFFFF, then the port number of the first port on the ATA controller is returned
+  in Port and EFI_SUCCESS is returned.
+
+  If Port is a port number that was returned on a previous call to GetNextPort(), then the
+  port number of the next port on the ATA controller is returned in Port, and EFI_SUCCESS
+  is returned. If Port is not 0xFFFF and Port was not returned on a previous call to
+  GetNextPort(), then EFI_INVALID_PARAMETER is returned.
+
+  If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND is
+  returned.
+
+  @param[in]     This           A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in,out] Port           On input, a pointer to the port number on the ATA controller.
+                                On output, a pointer to the next port number on the ATA
+                                controller. An input value of 0xFFFF retrieves the first port
+                                number on the ATA controller.
+
+  @retval EFI_SUCCESS           The next port number on the ATA controller was returned in Port.
+  @retval EFI_NOT_FOUND         There are no more ports on this ATA controller.
+  @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned on a previous call
+                                to GetNextPort().
+
+**/
+EFI_STATUS
+EFIAPI
+SiI3132GetNextPort (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT16                 *Port
+  )
+{
+  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
+  UINTN                   PrevPort;
+  EFI_STATUS              Status = EFI_SUCCESS;
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PrevPort = *Port;
+
+  if (PrevPort == 0xFFFF) {
+    *Port = 0;
+  } else {
+    if (PrevPort < SATA_SII3132_MAXPORT) {
+        *Port = PrevPort + 1;
+    } else {
+        Status = EFI_NOT_FOUND;
+    }
+  }
+  return Status;
+}
+
+/**
+  Used to retrieve the list of legal port multiplier port numbers for ATA devices on a port of an ATA
+  controller. These can either be the list of port multiplier ports where ATA devices are actually
+  present on port or the list of legal port multiplier ports on that port. Regardless, the caller of this
+  function must probe the port number and port multiplier port number returned to see if an ATA
+  device is actually present.
+
+  The GetNextDevice() function retrieves the port multiplier port number of an ATA device
+  present on a port of an ATA controller.
+
+  If PortMultiplierPort points to a port multiplier port number value that was returned on a
+  previous call to GetNextDevice(), then the port multiplier port number of the next ATA device
+  on the port of the ATA controller is returned in PortMultiplierPort, and EFI_SUCCESS is
+  returned.
+
+  If PortMultiplierPort points to 0xFFFF, then the port multiplier port number of the first
+  ATA device on port of the ATA controller is returned in PortMultiplierPort and
+  EFI_SUCCESS is returned.
+
+  If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort
+  was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER
+  is returned.
+
+  If PortMultiplierPort is the port multiplier port number of the last ATA device on the port of
+  the ATA controller, then EFI_NOT_FOUND is returned.
+
+  @param[in]     This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in]     Port                The port number present on the ATA controller.
+  @param[in,out] PortMultiplierPort  On input, a pointer to the port multiplier port number of an
+                                     ATA device present on the ATA controller.
+                                     If on input a PortMultiplierPort of 0xFFFF is specified,
+                                     then the port multiplier port number of the first ATA device
+                                     is returned. On output, a pointer to the port multiplier port
+                                     number of the next ATA device present on an ATA controller.
+
+  @retval EFI_SUCCESS                The port multiplier port number of the next ATA device on the port
+                                     of the ATA controller was returned in PortMultiplierPort.
+  @retval EFI_NOT_FOUND              There are no more ATA devices on this port of the ATA controller.
+  @retval EFI_INVALID_PARAMETER      PortMultiplierPort is not 0xFFFF, and PortMultiplierPort was not
+                                     returned on a previous call to GetNextDevice().
+
+**/
+EFI_STATUS
+EFIAPI
+SiI3132GetNextDevice (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN UINT16                     Port,
+  IN OUT UINT16                 *PortMultiplierPort
+  )
+{
+  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
+  SATA_SI3132_PORT        *SataPort;
+  SATA_SI3132_DEVICE      *SataDevice;
+  LIST_ENTRY              *List;
+  EFI_STATUS              Status = EFI_SUCCESS;
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Port >= SATA_SII3132_MAXPORT) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SataPort = &(SataSiI3132Instance->Ports[Port]);
+
+  if (*PortMultiplierPort == 0xFFFF) {
+    List = SataPort->Devices.ForwardLink;
+    if (List != &SataPort->Devices) {
+      // The list is not empty, return the first device
+      *PortMultiplierPort = ((SATA_SI3132_DEVICE*)List)->Index;
+    } else {
+      Status = EFI_NOT_FOUND;
+    }
+  } else {
+    SataDevice = GetSataDevice (SataSiI3132Instance, Port, *PortMultiplierPort);
+    if (SataDevice != NULL) {
+      // We have found the previous port multiplier, return the next one
+      List = SataDevice->Link.ForwardLink;
+      if (List != &SataPort->Devices) {
+        *PortMultiplierPort = ((SATA_SI3132_DEVICE*)List)->Index;
+      } else {
+        Status = EFI_NOT_FOUND;
+      }
+    } else {
+      Status = EFI_NOT_FOUND;
+    }
+  }
+  return Status;
+}
+
+/**
+  Used to allocate and build a device path node for an ATA device on an ATA controller.
+
+  The BuildDevicePath() function allocates and builds a single device node for the ATA
+  device specified by Port and PortMultiplierPort. If the ATA device specified by Port and
+  PortMultiplierPort is not present on the ATA controller, then EFI_NOT_FOUND is returned.
+  If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. If there are not enough
+  resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned.
+
+  Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of
+  DevicePath are initialized to describe the ATA device specified by Port and PortMultiplierPort,
+  and EFI_SUCCESS is returned.
+
+  @param[in]     This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in]     Port                Port specifies the port number of the ATA device for which a
+                                     device path node is to be allocated and built.
+  @param[in]     PortMultiplierPort  The port multiplier port number of the ATA device for which a
+                                     device path node is to be allocated and built. If there is no
+                                     port multiplier, then specify 0.
+  @param[in,out] DevicePath          A pointer to a single device path node that describes the ATA
+                                     device specified by Port and PortMultiplierPort. This function
+                                     is responsible for allocating the buffer DevicePath with the
+                                     boot service AllocatePool(). It is the caller's responsibility
+                                     to free DevicePath when the caller is finished with DevicePath.
+  @retval EFI_SUCCESS                The device path node that describes the ATA device specified by
+                                     Port and PortMultiplierPort was allocated and returned in DevicePath.
+  @retval EFI_NOT_FOUND              The ATA device specified by Port and PortMultiplierPort does not
+                                     exist on the ATA controller.
+  @retval EFI_INVALID_PARAMETER      DevicePath is NULL.
+  @retval EFI_OUT_OF_RESOURCES       There are not enough resources to allocate DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+SiI3132BuildDevicePath (
+  IN     EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN     UINT16                     Port,
+  IN     UINT16                     PortMultiplierPort,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL   **DevicePath
+  )
+{
+  SATA_SI3132_INSTANCE        *SataSiI3132Instance;
+  SATA_SI3132_DEVICE          *SataDevice;
+  EFI_DEVICE_PATH_PROTOCOL    *SiI3132DevicePath;
+
+  SATA_TRACE ("SiI3132BuildDevicePath()");
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);
+  if (SataDevice == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  SiI3132DevicePath = CreateDeviceNode (MESSAGING_DEVICE_PATH, MSG_SATA_DP, sizeof (SATA_DEVICE_PATH));
+  if (SiI3132DevicePath == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ((SATA_DEVICE_PATH*)SiI3132DevicePath)->HBAPortNumber = Port;
+  if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
+    ((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = PortMultiplierPort;
+  } else {
+    //Temp:((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = SATA_HBA_DIRECT_CONNECT_FLAG;
+    ((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = 0;
+  }
+  ((SATA_DEVICE_PATH*)SiI3132DevicePath)->Lun = Port; //TODO: Search information how to define properly LUN (Logical Unit Number)
+
+  *DevicePath = SiI3132DevicePath;
+  return EFI_SUCCESS;
+}
+
+/**
+  Used to translate a device path node to a port number and port multiplier port number.
+
+  The GetDevice() function determines the port and port multiplier port number associated with
+  the ATA device described by DevicePath. If DevicePath is a device path node type that the
+  ATA Pass Thru driver supports, then the ATA Pass Thru driver will attempt to translate the contents
+  DevicePath into a port number and port multiplier port number.
+
+  If this translation is successful, then that port number and port multiplier port number are returned
+  in Port and PortMultiplierPort, and EFI_SUCCESS is returned.
+
+  If DevicePath, Port, or PortMultiplierPort are NULL, then EFI_INVALID_PARAMETER is returned.
+
+  If DevicePath is not a device path node type that the ATA Pass Thru driver supports, then
+  EFI_UNSUPPORTED is returned.
+
+  If DevicePath is a device path node type that the ATA Pass Thru driver supports, but there is not
+  a valid translation from DevicePath to a port number and port multiplier port number, then
+  EFI_NOT_FOUND is returned.
+
+  @param[in]  This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in]  DevicePath          A pointer to the device path node that describes an ATA device on the
+                                  ATA controller.
+  @param[out] Port                On return, points to the port number of an ATA device on the ATA controller.
+  @param[out] PortMultiplierPort  On return, points to the port multiplier port number of an ATA device
+                                  on the ATA controller.
+
+  @retval EFI_SUCCESS             DevicePath was successfully translated to a port number and port multiplier
+                                  port number, and they were returned in Port and PortMultiplierPort.
+  @retval EFI_INVALID_PARAMETER   DevicePath is NULL.
+  @retval EFI_INVALID_PARAMETER   Port is NULL.
+  @retval EFI_INVALID_PARAMETER   PortMultiplierPort is NULL.
+  @retval EFI_UNSUPPORTED         This driver does not support the device path node type in DevicePath.
+  @retval EFI_NOT_FOUND           A valid translation from DevicePath to a port number and port multiplier
+                                  port number does not exist.
+**/
+EFI_STATUS
+EFIAPI
+SiI3132GetDevice (
+  IN  EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL   *DevicePath,
+  OUT UINT16                     *Port,
+  OUT UINT16                     *PortMultiplierPort
+  )
+{
+  SATA_SI3132_INSTANCE        *SataSiI3132Instance;
+
+  SATA_TRACE ("SiI3132GetDevice()");
+
+  if (!DevicePath || !Port || !PortMultiplierPort) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_SATA_DP)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((SATA_DEVICE_PATH*)DevicePath)->Lun >= SATA_SII3132_MAXPORT) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
+    ASSERT (0); //TODO: Implement me!
+    return EFI_UNSUPPORTED;
+  } else {
+    *Port = ((SATA_DEVICE_PATH*)DevicePath)->Lun;
+    // Return the first Sata Device as there should be only one directly connected
+    *PortMultiplierPort = ((SATA_SI3132_DEVICE*)SataSiI3132Instance->Ports[*Port].Devices.ForwardLink)->Index;
+    return EFI_SUCCESS;
+  }
+}
+
+EFI_STATUS
+SiI3132HwResetPort (
+  IN SATA_SI3132_PORT *SataPort
+  )
+{
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  UINT32              Value32;
+  UINTN               Timeout;
+
+  SATA_TRACE ("SiI3132HwResetPort()");
+
+  PciIo = SataPort->Instance->PciIo;
+
+  // Clear Port Reset
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CONTROLCLEAR_REG, SII3132_PORT_CONTROL_RESET);
+
+  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);
+  ASSERT (!(Value32 & SII3132_PORT_CONTROL_RESET));
+
+  // Initialize error counters
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTDECODE, 0);
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTCRC, 0);
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTHANDSHAKE, 0);
+
+  // Enable interrupts for command completion and command errors
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ENABLEINT_REG, SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR);
+
+  // Clear IRQ
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ENABLEINT_REG, SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR | SII3132_PORT_INT_PORTRDY | (1 << 3));
+
+  // Wait until Port Ready
+  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
+  Timeout = 1000;
+  while ((Timeout > 0) && ((Value32 & SII3132_PORT_INT_PORTRDY) == 0)) {
+    gBS->Stall (1);
+    Timeout--;
+    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
+  }
+  // Clear IRQ
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, SII3132_PORT_INT_PORTRDY);
+
+  if (Timeout == 0) {
+    SATA_TRACE ("SiI3132HwResetPort(): Timeout");
+    return EFI_TIMEOUT;
+  } else if ((Value32 & SII3132_PORT_INT_PORTRDY) == 0) {
+    SATA_TRACE ("SiI3132HwResetPort(): Port Not Ready");
+    return EFI_DEVICE_ERROR;
+  } else {
+    return EFI_SUCCESS;
+  }
+}
+
+/**
+  Resets a specific port on the ATA controller. This operation also resets all the ATA devices
+  connected to the port.
+
+  The ResetChannel() function resets an a specific port on an ATA controller. This operation
+  resets all the ATA devices connected to that port. If this ATA controller does not support
+  a reset port operation, then EFI_UNSUPPORTED is returned.
+
+  If a device error occurs while executing that port reset operation, then EFI_DEVICE_ERROR is
+  returned.
+
+  If a timeout occurs during the execution of the port reset operation, then EFI_TIMEOUT is returned.
+
+  If the port reset operation is completed, then EFI_SUCCESS is returned.
+
+  @param[in]  This          A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in]  Port          The port number on the ATA controller.
+
+  @retval EFI_SUCCESS       The ATA controller port was reset.
+  @retval EFI_UNSUPPORTED   The ATA controller does not support a port reset operation.
+  @retval EFI_DEVICE_ERROR  A device error occurred while attempting to reset the ATA port.
+  @retval EFI_TIMEOUT       A timeout occurred while attempting to reset the ATA port.
+
+**/
+EFI_STATUS
+EFIAPI
+SiI3132ResetPort (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN UINT16                     Port
+  )
+{
+  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
+  SATA_SI3132_PORT        *SataPort;
+
+  SATA_TRACE ("SiI3132ResetPort()");
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Port >= SATA_SII3132_MAXPORT) {
+    return EFI_UNSUPPORTED;
+  }
+
+  SataPort = &(SataSiI3132Instance->Ports[Port]);
+  return SiI3132HwResetPort (SataPort);
+}
+
+/**
+  Resets an ATA device that is connected to an ATA controller.
+
+  The ResetDevice() function resets the ATA device specified by Port and PortMultiplierPort.
+  If this ATA controller does not support a device reset operation, then EFI_UNSUPPORTED is
+  returned.
+
+  If Port or PortMultiplierPort are not in a valid range for this ATA controller, then
+  EFI_INVALID_PARAMETER is returned.
+
+  If a device error occurs while executing that device reset operation, then EFI_DEVICE_ERROR
+  is returned.
+
+  If a timeout occurs during the execution of the device reset operation, then EFI_TIMEOUT is
+  returned.
+
+  If the device reset operation is completed, then EFI_SUCCESS is returned.
+
+  @param[in] This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
+  @param[in] Port                Port represents the port number of the ATA device to be reset.
+  @param[in] PortMultiplierPort  The port multiplier port number of the ATA device to reset.
+                                 If there is no port multiplier, then specify 0.
+  @retval EFI_SUCCESS            The ATA device specified by Port and PortMultiplierPort was reset.
+  @retval EFI_UNSUPPORTED        The ATA controller does not support a device reset operation.
+  @retval EFI_INVALID_PARAMETER  Port or PortMultiplierPort are invalid.
+  @retval EFI_DEVICE_ERROR       A device error occurred while attempting to reset the ATA device
+                                 specified by Port and PortMultiplierPort.
+  @retval EFI_TIMEOUT            A timeout occurred while attempting to reset the ATA device
+                                 specified by Port and PortMultiplierPort.
+
+**/
+EFI_STATUS
+EFIAPI
+SiI3132ResetDevice (
+  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
+  IN UINT16                     Port,
+  IN UINT16                     PortMultiplierPort
+  )
+{
+  EFI_PCI_IO_PROTOCOL     *PciIo;
+  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
+  SATA_SI3132_PORT        *SataPort;
+  SATA_SI3132_DEVICE      *SataDevice;
+  UINTN                   Timeout;
+  UINT32                  Value32;
+
+  SATA_TRACE ("SiI3132ResetDevice()");
+
+  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
+  if (!SataSiI3132Instance) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PciIo = SataSiI3132Instance->PciIo;
+
+  SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);
+  if (!SataDevice) {
+    return EFI_INVALID_PARAMETER;
+  }
+  SataPort = SataDevice->Port;
+
+  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CONTROLSET_REG, SII3132_PORT_DEVICE_RESET);
+
+  Timeout = 100;
+  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);
+  while ((Timeout > 0) && ((Value32 & SII3132_PORT_DEVICE_RESET) != 0)) {
+    gBS->Stall (1);
+    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);
+    Timeout--;
+  }
+
+  if (Timeout == 0) {
+    SATA_TRACE ("SiI3132ResetDevice(): Timeout");
+    return EFI_TIMEOUT;
+  } else {
+    return EFI_SUCCESS;
+  }
+}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
                   ` (2 preceding siblings ...)
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-05-04 10:58   ` Leif Lindholm
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 5/8] Platform/HiKey: switch to relocated version of eMMC driver Ard Biesheuvel
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

Incorporate the driver for the DesignWare eMMC host controller that is
based on the obsolete MMC host controller protocol that is defined in
EmbeddedPkg.

This driver does not follow the UEFI driver model, and is only kept
around for its only users, which is the HiKey platform, which is
rapidly reaching obsolescence itself, at which point this driver may
be removed again.

To prevent inadvertent use in new platforms, add a PCD that needs to
be changed from its default value in order for the driver to be
functional.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Silicon/Synopsys/DesignWare/DesignWare.dec                  |   9 +
 Silicon/Synopsys/DesignWare/DesignWare.dsc                  |   2 +
 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h      | 132 ++++
 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c   | 693 ++++++++++++++++++++
 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf |  56 ++
 5 files changed, 892 insertions(+)

diff --git a/Silicon/Synopsys/DesignWare/DesignWare.dec b/Silicon/Synopsys/DesignWare/DesignWare.dec
index 71ddd24b7404..f7ec7927543c 100755
--- a/Silicon/Synopsys/DesignWare/DesignWare.dec
+++ b/Silicon/Synopsys/DesignWare/DesignWare.dec
@@ -21,4 +21,13 @@ [Guids.common]
   gDesignWareTokenSpaceGuid = { 0x89cb1241, 0xd283, 0x4543, { 0x88, 0x9c, 0x6b, 0x62, 0x36, 0x1a, 0x95, 0x7a } }
   gDwEmacNetNonDiscoverableDeviceGuid = { 0x401950CD, 0xF9CD, 0x4A65, { 0xAD, 0x8E, 0x84, 0x9F, 0x3B, 0xAF, 0x23, 0x04 } }
 
+[PcdsFixedAtBuild.common]
+  #
+  # Permit the use of obsolete drivers in this package
+  #
+  gDesignWareTokenSpaceGuid.PcdDwPermitObsoleteDrivers|FALSE|BOOLEAN|0x00000001
 
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000002
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000003
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeMaxClockFreqInHz|0x0|UINT32|0x00000004
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeFifoDepth|0x0|UINT32|0x00000005
diff --git a/Silicon/Synopsys/DesignWare/DesignWare.dsc b/Silicon/Synopsys/DesignWare/DesignWare.dsc
index ad6a5ede4ae0..098bba3f7d68 100755
--- a/Silicon/Synopsys/DesignWare/DesignWare.dsc
+++ b/Silicon/Synopsys/DesignWare/DesignWare.dsc
@@ -20,6 +20,7 @@ [LibraryClasses]
   ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
   BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
   BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
   DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
   DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
   DmaLib|EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
@@ -39,3 +40,4 @@ [LibraryClasses]
 
 [Components]
   Silicon/Synopsys/DesignWare/Drivers/DwEmacSnpDxe/DwEmacSnpDxe.inf
+  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
new file mode 100644
index 000000000000..09ad9b8428c4
--- /dev/null
+++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
@@ -0,0 +1,132 @@
+/** @file
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*
+*  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+
+#ifndef __DWEMMC_H__
+#define __DWEMMC_H__
+
+#include <Protocol/EmbeddedGpio.h>
+
+// DW MMC Registers
+#define DWEMMC_CTRL             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
+#define DWEMMC_PWREN            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
+#define DWEMMC_CLKDIV           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
+#define DWEMMC_CLKSRC           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
+#define DWEMMC_CLKENA           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
+#define DWEMMC_TMOUT            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
+#define DWEMMC_CTYPE            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
+#define DWEMMC_BLKSIZ           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
+#define DWEMMC_BYTCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
+#define DWEMMC_INTMASK          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
+#define DWEMMC_CMDARG           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
+#define DWEMMC_CMD              ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
+#define DWEMMC_RESP0            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
+#define DWEMMC_RESP1            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
+#define DWEMMC_RESP2            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
+#define DWEMMC_RESP3            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
+#define DWEMMC_RINTSTS          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
+#define DWEMMC_STATUS           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
+#define DWEMMC_FIFOTH           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
+#define DWEMMC_TCBCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x05c)
+#define DWEMMC_TBBCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x060)
+#define DWEMMC_DEBNCE           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
+#define DWEMMC_HCON             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x070)
+#define DWEMMC_UHSREG           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
+#define DWEMMC_BMOD             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
+#define DWEMMC_DBADDR           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
+#define DWEMMC_IDSTS            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
+#define DWEMMC_IDINTEN          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
+#define DWEMMC_DSCADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
+#define DWEMMC_BUFADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
+#define DWEMMC_CARDTHRCTL       ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
+#define DWEMMC_DATA             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X200)
+
+#define CMD_UPDATE_CLK                          0x80202000
+#define CMD_START_BIT                           (1 << 31)
+
+#define MMC_8BIT_MODE                           (1 << 16)
+
+#define BIT_CMD_RESPONSE_EXPECT                 (1 << 6)
+#define BIT_CMD_LONG_RESPONSE                   (1 << 7)
+#define BIT_CMD_CHECK_RESPONSE_CRC              (1 << 8)
+#define BIT_CMD_DATA_EXPECTED                   (1 << 9)
+#define BIT_CMD_READ                            (0 << 10)
+#define BIT_CMD_WRITE                           (1 << 10)
+#define BIT_CMD_BLOCK_TRANSFER                  (0 << 11)
+#define BIT_CMD_STREAM_TRANSFER                 (1 << 11)
+#define BIT_CMD_SEND_AUTO_STOP                  (1 << 12)
+#define BIT_CMD_WAIT_PRVDATA_COMPLETE           (1 << 13)
+#define BIT_CMD_STOP_ABORT_CMD                  (1 << 14)
+#define BIT_CMD_SEND_INIT                       (1 << 15)
+#define BIT_CMD_UPDATE_CLOCK_ONLY               (1 << 21)
+#define BIT_CMD_READ_CEATA_DEVICE               (1 << 22)
+#define BIT_CMD_CCS_EXPECTED                    (1 << 23)
+#define BIT_CMD_ENABLE_BOOT                     (1 << 24)
+#define BIT_CMD_EXPECT_BOOT_ACK                 (1 << 25)
+#define BIT_CMD_DISABLE_BOOT                    (1 << 26)
+#define BIT_CMD_MANDATORY_BOOT                  (0 << 27)
+#define BIT_CMD_ALTERNATE_BOOT                  (1 << 27)
+#define BIT_CMD_VOLT_SWITCH                     (1 << 28)
+#define BIT_CMD_USE_HOLD_REG                    (1 << 29)
+#define BIT_CMD_START                           (1 << 31)
+
+#define DWEMMC_INT_EBE                          (1 << 15)       /* End-bit Err */
+#define DWEMMC_INT_SBE                          (1 << 13)       /* Start-bit  Err */
+#define DWEMMC_INT_HLE                          (1 << 12)       /* Hardware-lock Err */
+#define DWEMMC_INT_FRUN                         (1 << 11)       /* FIFO UN/OV RUN */
+#define DWEMMC_INT_DRT                          (1 << 9)        /* Data timeout */
+#define DWEMMC_INT_RTO                          (1 << 8)        /* Response timeout */
+#define DWEMMC_INT_DCRC                         (1 << 7)        /* Data CRC err */
+#define DWEMMC_INT_RCRC                         (1 << 6)        /* Response CRC err */
+#define DWEMMC_INT_RXDR                         (1 << 5)
+#define DWEMMC_INT_TXDR                         (1 << 4)
+#define DWEMMC_INT_DTO                          (1 << 3)        /* Data trans over */
+#define DWEMMC_INT_CMD_DONE                     (1 << 2)
+#define DWEMMC_INT_RE                           (1 << 1)
+
+#define DWEMMC_IDMAC_DES0_DIC                   (1 << 1)
+#define DWEMMC_IDMAC_DES0_LD                    (1 << 2)
+#define DWEMMC_IDMAC_DES0_FS                    (1 << 3)
+#define DWEMMC_IDMAC_DES0_CH                    (1 << 4)
+#define DWEMMC_IDMAC_DES0_ER                    (1 << 5)
+#define DWEMMC_IDMAC_DES0_CES                   (1 << 30)
+#define DWEMMC_IDMAC_DES0_OWN                   (1 << 31)
+#define DWEMMC_IDMAC_DES1_BS1(x)                ((x) & 0x1fff)
+#define DWEMMC_IDMAC_DES2_BS2(x)                (((x) & 0x1fff) << 13)
+#define DWEMMC_IDMAC_SWRESET                    (1 << 0)
+#define DWEMMC_IDMAC_FB                         (1 << 1)
+#define DWEMMC_IDMAC_ENABLE                     (1 << 7)
+
+#define EMMC_FIX_RCA                            6
+
+/* bits in MMC0_CTRL */
+#define DWEMMC_CTRL_RESET                       (1 << 0)
+#define DWEMMC_CTRL_FIFO_RESET                  (1 << 1)
+#define DWEMMC_CTRL_DMA_RESET                   (1 << 2)
+#define DWEMMC_CTRL_INT_EN                      (1 << 4)
+#define DWEMMC_CTRL_DMA_EN                      (1 << 5)
+#define DWEMMC_CTRL_IDMAC_EN                    (1 << 25)
+#define DWEMMC_CTRL_RESET_ALL                   (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
+
+#define DWEMMC_STS_DATA_BUSY                    (1 << 9)
+
+#define DWEMMC_FIFO_TWMARK(x)                   (x & 0xfff)
+#define DWEMMC_FIFO_RWMARK(x)                   ((x & 0x1ff) << 16)
+#define DWEMMC_DMA_BURST_SIZE(x)                ((x & 0x7) << 28)
+
+#define DWEMMC_CARD_RD_THR(x)                   ((x & 0xfff) << 16)
+#define DWEMMC_CARD_RD_THR_EN                   (1 << 0)
+
+#define DWEMMC_GET_HDATA_WIDTH(x)               (((x) >> 7) & 0x7)
+
+#endif  // __DWEMMC_H__
diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
new file mode 100644
index 000000000000..eed5fc57fc22
--- /dev/null
+++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
@@ -0,0 +1,693 @@
+/** @file
+  This file implement the MMC Host Protocol for the DesignWare eMMC.
+
+  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/MmcHost.h>
+
+#include "DwEmmc.h"
+
+#define DWEMMC_DESC_PAGE                1
+#define DWEMMC_BLOCK_SIZE               512
+#define DWEMMC_DMA_BUF_SIZE             (512 * 8)
+#define DWEMMC_MAX_DESC_PAGES           512
+
+typedef struct {
+  UINT32                        Des0;
+  UINT32                        Des1;
+  UINT32                        Des2;
+  UINT32                        Des3;
+} DWEMMC_IDMAC_DESCRIPTOR;
+
+EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
+DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
+EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
+STATIC UINT32 mDwEmmcCommand;
+STATIC UINT32 mDwEmmcArgument;
+
+EFI_STATUS
+DwEmmcReadBlockData (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN EFI_LBA                    Lba,
+  IN UINTN                      Length,
+  IN UINT32*                    Buffer
+  );
+
+BOOLEAN
+DwEmmcIsPowerOn (
+  VOID
+  )
+{
+    return TRUE;
+}
+
+EFI_STATUS
+DwEmmcInitialize (
+  VOID
+  )
+{
+    DEBUG ((DEBUG_BLKIO, "DwEmmcInitialize()"));
+    return EFI_SUCCESS;
+}
+
+BOOLEAN
+DwEmmcIsCardPresent (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return TRUE;
+}
+
+BOOLEAN
+DwEmmcIsReadOnly (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return FALSE;
+}
+
+BOOLEAN
+DwEmmcIsDmaSupported (
+  IN EFI_MMC_HOST_PROTOCOL     *This
+  )
+{
+  return TRUE;
+}
+
+EFI_STATUS
+DwEmmcBuildDevicePath (
+  IN EFI_MMC_HOST_PROTOCOL      *This,
+  IN EFI_DEVICE_PATH_PROTOCOL   **DevicePath
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
+
+  NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH));
+  CopyGuid (& ((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &mDwEmmcDevicePathGuid);
+
+  *DevicePath = NewDevicePathNode;
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcUpdateClock (
+  VOID
+  )
+{
+  UINT32 Data;
+
+  /* CMD_UPDATE_CLK */
+  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
+         BIT_CMD_START;
+  MmioWrite32 (DWEMMC_CMD, Data);
+  while (1) {
+    Data = MmioRead32 (DWEMMC_CMD);
+    if (!(Data & CMD_START_BIT)) {
+      break;
+    }
+    Data = MmioRead32 (DWEMMC_RINTSTS);
+    if (Data & DWEMMC_INT_HLE) {
+      Print (L"failed to update mmc clock frequency\n");
+      return EFI_DEVICE_ERROR;
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcSetClock (
+  IN UINTN                     ClockFreq
+  )
+{
+  UINT32 Divider, Rate, Data;
+  EFI_STATUS Status;
+  BOOLEAN Found = FALSE;
+
+  for (Divider = 1; Divider < 256; Divider++) {
+    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
+    if ((Rate / (2 * Divider)) <= ClockFreq) {
+      Found = TRUE;
+      break;
+    }
+  }
+  if (Found == FALSE) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Wait until MMC is idle
+  do {
+    Data = MmioRead32 (DWEMMC_STATUS);
+  } while (Data & DWEMMC_STS_DATA_BUSY);
+
+  // Disable MMC clock first
+  MmioWrite32 (DWEMMC_CLKENA, 0);
+  Status = DwEmmcUpdateClock ();
+  ASSERT (!EFI_ERROR (Status));
+
+  MmioWrite32 (DWEMMC_CLKDIV, Divider);
+  Status = DwEmmcUpdateClock ();
+  ASSERT (!EFI_ERROR (Status));
+
+  // Enable MMC clock
+  MmioWrite32 (DWEMMC_CLKENA, 1);
+  MmioWrite32 (DWEMMC_CLKSRC, 0);
+  Status = DwEmmcUpdateClock ();
+  ASSERT (!EFI_ERROR (Status));
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcNotifyState (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_STATE                 State
+  )
+{
+  UINT32      Data;
+  EFI_STATUS  Status;
+
+  switch (State) {
+  case MmcInvalidState:
+    return EFI_INVALID_PARAMETER;
+  case MmcHwInitializationState:
+    MmioWrite32 (DWEMMC_PWREN, 1);
+
+    // If device already turn on then restart it
+    Data = DWEMMC_CTRL_RESET_ALL;
+    MmioWrite32 (DWEMMC_CTRL, Data);
+    do {
+      // Wait until reset operation finished
+      Data = MmioRead32 (DWEMMC_CTRL);
+    } while (Data & DWEMMC_CTRL_RESET_ALL);
+
+    // Setup clock that could not be higher than 400KHz.
+    Status = DwEmmcSetClock (400000);
+    ASSERT (!EFI_ERROR (Status));
+    // Wait clock stable
+    MicroSecondDelay (100);
+
+    MmioWrite32 (DWEMMC_RINTSTS, ~0);
+    MmioWrite32 (DWEMMC_INTMASK, 0);
+    MmioWrite32 (DWEMMC_TMOUT, ~0);
+    MmioWrite32 (DWEMMC_IDINTEN, 0);
+    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
+
+    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
+    do {
+      Data = MmioRead32 (DWEMMC_BMOD);
+    } while (Data & DWEMMC_IDMAC_SWRESET);
+    break;
+  case MmcIdleState:
+    break;
+  case MmcReadyState:
+    break;
+  case MmcIdentificationState:
+    break;
+  case MmcStandByState:
+    break;
+  case MmcTransferState:
+    break;
+  case MmcSendingDataState:
+    break;
+  case MmcReceiveDataState:
+    break;
+  case MmcProgrammingState:
+    break;
+  case MmcDisconnectState:
+    break;
+  default:
+    return EFI_INVALID_PARAMETER;
+  }
+  return EFI_SUCCESS;
+}
+
+// Need to prepare DMA buffer first before sending commands to MMC card
+BOOLEAN
+IsPendingReadCommand (
+  IN MMC_CMD                    MmcCmd
+  )
+{
+  UINTN  Mask;
+
+  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
+  if ((MmcCmd & Mask) == Mask) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+BOOLEAN
+IsPendingWriteCommand (
+  IN MMC_CMD                    MmcCmd
+  )
+{
+  UINTN  Mask;
+
+  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
+  if ((MmcCmd & Mask) == Mask) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+EFI_STATUS
+SendCommand (
+  IN MMC_CMD                    MmcCmd,
+  IN UINT32                     Argument
+  )
+{
+  UINT32      Data, ErrMask;
+
+  // Wait until MMC is idle
+  do {
+    Data = MmioRead32 (DWEMMC_STATUS);
+  } while (Data & DWEMMC_STS_DATA_BUSY);
+
+  MmioWrite32 (DWEMMC_RINTSTS, ~0);
+  MmioWrite32 (DWEMMC_CMDARG, Argument);
+  MmioWrite32 (DWEMMC_CMD, MmcCmd);
+
+  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
+            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
+  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
+  do {
+    MicroSecondDelay(500);
+    Data = MmioRead32 (DWEMMC_RINTSTS);
+
+    if (Data & ErrMask) {
+      return EFI_DEVICE_ERROR;
+    }
+    if (Data & DWEMMC_INT_DTO) {     // Transfer Done
+      break;
+    }
+  } while (!(Data & DWEMMC_INT_CMD_DONE));
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+DwEmmcSendCommand (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_CMD                    MmcCmd,
+  IN UINT32                     Argument
+  )
+{
+  UINT32       Cmd = 0;
+  EFI_STATUS   Status = EFI_SUCCESS;
+
+  switch (MMC_GET_INDX(MmcCmd)) {
+  case MMC_INDX(0):
+    Cmd = BIT_CMD_SEND_INIT;
+    break;
+  case MMC_INDX(1):
+    Cmd = BIT_CMD_RESPONSE_EXPECT;
+    break;
+  case MMC_INDX(2):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE |
+           BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT;
+    break;
+  case MMC_INDX(3):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_SEND_INIT;
+    break;
+  case MMC_INDX(7):
+    if (Argument)
+        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
+    else
+        Cmd = 0;
+    break;
+  case MMC_INDX(8):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    break;
+  case MMC_INDX(9):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_LONG_RESPONSE;
+    break;
+  case MMC_INDX(12):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_STOP_ABORT_CMD;
+    break;
+  case MMC_INDX(13):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    break;
+  case MMC_INDX(16):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    break;
+  case MMC_INDX(17):
+  case MMC_INDX(18):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    break;
+  case MMC_INDX(24):
+  case MMC_INDX(25):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE |
+           BIT_CMD_WAIT_PRVDATA_COMPLETE;
+    break;
+  case MMC_INDX(30):
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
+           BIT_CMD_DATA_EXPECTED;
+    break;
+  default:
+    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
+    break;
+  }
+
+  Cmd |= MMC_GET_INDX(MmcCmd) | BIT_CMD_USE_HOLD_REG | BIT_CMD_START;
+  if (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
+    mDwEmmcCommand = Cmd;
+    mDwEmmcArgument = Argument;
+  } else {
+    Status = SendCommand (Cmd, Argument);
+  }
+  return Status;
+}
+
+EFI_STATUS
+DwEmmcReceiveResponse (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN MMC_RESPONSE_TYPE          Type,
+  IN UINT32*                    Buffer
+  )
+{
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (   (Type == MMC_RESPONSE_TYPE_R1)
+      || (Type == MMC_RESPONSE_TYPE_R1b)
+      || (Type == MMC_RESPONSE_TYPE_R3)
+      || (Type == MMC_RESPONSE_TYPE_R6)
+      || (Type == MMC_RESPONSE_TYPE_R7))
+  {
+    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
+  } else if (Type == MMC_RESPONSE_TYPE_R2) {
+    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
+    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
+    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
+    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
+  }
+  return EFI_SUCCESS;
+}
+
+VOID
+DwEmmcAdjustFifoThreshold (
+  VOID
+  )
+{
+  /* DMA multiple transaction size map to reg value as array index */
+  CONST UINT32 BurstSize[] = {1, 4, 8, 16, 32, 64, 128, 256};
+  UINT32 BlkDepthInFifo, FifoThreshold, FifoWidth, FifoDepth;
+  UINT32 BlkSize = DWEMMC_BLOCK_SIZE, Idx = 0, RxWatermark = 1, TxWatermark, TxWatermarkInvers;
+
+  /* Skip FIFO adjustment if we do not have platform FIFO depth info */
+  FifoDepth = PcdGet32 (PcdDwEmmcDxeFifoDepth);
+  if (!FifoDepth) {
+    return;
+  }
+
+  TxWatermark = FifoDepth / 2;
+  TxWatermarkInvers = FifoDepth - TxWatermark;
+
+  FifoWidth = DWEMMC_GET_HDATA_WIDTH (MmioRead32 (DWEMMC_HCON));
+  if (!FifoWidth) {
+    FifoWidth = 2;
+  } else if (FifoWidth == 2) {
+    FifoWidth = 8;
+  } else {
+    FifoWidth = 4;
+  }
+
+  BlkDepthInFifo = BlkSize / FifoWidth;
+
+  Idx = ARRAY_SIZE (BurstSize) - 1;
+  while (Idx && ((BlkDepthInFifo % BurstSize[Idx]) || (TxWatermarkInvers % BurstSize[Idx]))) {
+    Idx--;
+  }
+
+  RxWatermark = BurstSize[Idx] - 1;
+  FifoThreshold = DWEMMC_DMA_BURST_SIZE (Idx) | DWEMMC_FIFO_TWMARK (TxWatermark)
+           | DWEMMC_FIFO_RWMARK (RxWatermark);
+  MmioWrite32 (DWEMMC_FIFOTH, FifoThreshold);
+}
+
+EFI_STATUS
+PrepareDmaData (
+  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
+  IN UINTN                      Length,
+  IN UINT32*                    Buffer
+  )
+{
+  UINTN  Cnt, Blks, Idx, LastIdx;
+
+  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
+  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
+  Length = DWEMMC_BLOCK_SIZE * Blks;
+
+  for (Idx = 0; Idx < Cnt; Idx++) {
+    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
+                              DWEMMC_IDMAC_DES0_DIC;
+    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
+    /* Buffer Address */
+    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
+    /* Next Descriptor Address */
+    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
+                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
+  }
+  /* First Descriptor */
+  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
+  /* Last Descriptor */
+  LastIdx = Cnt - 1;
+  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
+  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
+  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
+                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
+  /* Set the Next field of Last Descriptor */
+  (IdmacDesc + LastIdx)->Des3 = 0;
+  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
+
+  return EFI_SUCCESS;
+}
+
+VOID
+StartDma (
+  UINTN    Length
+  )
+{
+  UINT32 Data;
+
+  Data = MmioRead32 (DWEMMC_CTRL);
+  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
+  MmioWrite32 (DWEMMC_CTRL, Data);
+  Data = MmioRead32 (DWEMMC_BMOD);
+  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
+  MmioWrite32 (DWEMMC_BMOD, Data);
+
+  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
+  MmioWrite32 (DWEMMC_BYTCNT, Length);
+}
+
+EFI_STATUS
+DwEmmcReadBlockData (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN EFI_LBA                    Lba,
+  IN UINTN                      Length,
+  IN UINT32*                   Buffer
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      DescPages, CountPerPage, Count;
+  EFI_TPL     Tpl;
+
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  CountPerPage = EFI_PAGE_SIZE / 16;
+  Count = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
+  DescPages = (Count + CountPerPage - 1) / CountPerPage;
+
+  InvalidateDataCacheRange (Buffer, Length);
+
+  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
+  if (EFI_ERROR (Status)) {
+    goto out;
+  }
+
+  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
+  StartDma (Length);
+
+  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
+    goto out;
+  }
+out:
+  // Restore Tpl
+  gBS->RestoreTPL (Tpl);
+  return Status;
+}
+
+EFI_STATUS
+DwEmmcWriteBlockData (
+  IN EFI_MMC_HOST_PROTOCOL     *This,
+  IN EFI_LBA                    Lba,
+  IN UINTN                      Length,
+  IN UINT32*                    Buffer
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      DescPages, CountPerPage, Count;
+  EFI_TPL     Tpl;
+
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  CountPerPage = EFI_PAGE_SIZE / 16;
+  Count = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
+  DescPages = (Count + CountPerPage - 1) / CountPerPage;
+
+  WriteBackDataCacheRange (Buffer, Length);
+
+  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
+  if (EFI_ERROR (Status)) {
+    goto out;
+  }
+
+  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
+  StartDma (Length);
+
+  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
+    goto out;
+  }
+out:
+  // Restore Tpl
+  gBS->RestoreTPL (Tpl);
+  return Status;
+}
+
+EFI_STATUS
+DwEmmcSetIos (
+  IN EFI_MMC_HOST_PROTOCOL      *This,
+  IN  UINT32                    BusClockFreq,
+  IN  UINT32                    BusWidth,
+  IN  UINT32                    TimingMode
+  )
+{
+  EFI_STATUS Status = EFI_SUCCESS;
+  UINT32    Data;
+
+  if ((PcdGet32 (PcdDwEmmcDxeMaxClockFreqInHz) != 0) &&
+      (BusClockFreq > PcdGet32 (PcdDwEmmcDxeMaxClockFreqInHz))) {
+    return EFI_UNSUPPORTED;
+  }
+  if (TimingMode != EMMCBACKWARD) {
+    Data = MmioRead32 (DWEMMC_UHSREG);
+    switch (TimingMode) {
+    case EMMCHS52DDR1V2:
+    case EMMCHS52DDR1V8:
+      Data |= 1 << 16;
+      break;
+    case EMMCHS52:
+    case EMMCHS26:
+      Data &= ~(1 << 16);
+      break;
+    default:
+      return EFI_UNSUPPORTED;
+    }
+    MmioWrite32 (DWEMMC_UHSREG, Data);
+  }
+
+  switch (BusWidth) {
+  case 1:
+    MmioWrite32 (DWEMMC_CTYPE, 0);
+    break;
+  case 4:
+    MmioWrite32 (DWEMMC_CTYPE, 1);
+    break;
+  case 8:
+    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
+    break;
+  default:
+    return EFI_UNSUPPORTED;
+  }
+  if (BusClockFreq) {
+    Status = DwEmmcSetClock (BusClockFreq);
+  }
+  return Status;
+}
+
+BOOLEAN
+DwEmmcIsMultiBlock (
+  IN EFI_MMC_HOST_PROTOCOL      *This
+  )
+{
+  return TRUE;
+}
+
+EFI_MMC_HOST_PROTOCOL gMciHost = {
+  MMC_HOST_PROTOCOL_REVISION,
+  DwEmmcIsCardPresent,
+  DwEmmcIsReadOnly,
+  DwEmmcBuildDevicePath,
+  DwEmmcNotifyState,
+  DwEmmcSendCommand,
+  DwEmmcReceiveResponse,
+  DwEmmcReadBlockData,
+  DwEmmcWriteBlockData,
+  DwEmmcSetIos,
+  DwEmmcIsMultiBlock
+};
+
+EFI_STATUS
+DwEmmcDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+  EFI_HANDLE    Handle;
+
+  if (!FixedPcdGetBool (PcdDwPermitObsoleteDrivers)) {
+    ASSERT (FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  Handle = NULL;
+
+  DwEmmcAdjustFifoThreshold ();
+  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
+  if (gpIdmacDesc == NULL) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  DEBUG ((DEBUG_BLKIO, "DwEmmcDxeInitialize()\n"));
+
+  //Publish Component Name, BlockIO protocol interfaces
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEmbeddedMmcHostProtocolGuid,    &gMciHost,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
new file mode 100644
index 000000000000..7f70fe1e2a38
--- /dev/null
+++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
@@ -0,0 +1,56 @@
+#/** @file
+#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
+#
+#  WARNING:
+#  This driver fails to follow the UEFI driver model without a good
+#  reason, and only remains in the tree because it is still used by
+#  a small number of platforms. It will be removed when no longer used.
+#
+#  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010019
+  BASE_NAME                      = DwEmmcDxe
+  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = DwEmmcDxeInitialize
+
+[Sources.common]
+  DwEmmcDxe.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/Synopsys/DesignWare/DesignWare.dec
+
+[LibraryClasses]
+  ArmLib
+  BaseLib
+  BaseMemoryLib
+  CacheMaintenanceLib
+  IoLib
+  MemoryAllocationLib
+  TimerLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiCpuArchProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEmbeddedMmcHostProtocolGuid
+
+[Pcd]
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeMaxClockFreqInHz
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeFifoDepth
+  gDesignWareTokenSpaceGuid.PcdDwPermitObsoleteDrivers
+
+[Depex]
+  TRUE
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 5/8] Platform/HiKey: switch to relocated version of eMMC driver
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
                   ` (3 preceding siblings ...)
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver Ard Biesheuvel
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

Switch to the version of the DesignWare eMMC host controller driver
that was moved into edk2-platforms, as part of a spring cleaning
exercise of EmbeddedPkg.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Platform/Hisilicon/HiKey/HiKey.dsc | 7 ++++---
 Platform/Hisilicon/HiKey/HiKey.fdf | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Platform/Hisilicon/HiKey/HiKey.dsc b/Platform/Hisilicon/HiKey/HiKey.dsc
index bcbe4fafce1a..600f19a30762 100644
--- a/Platform/Hisilicon/HiKey/HiKey.dsc
+++ b/Platform/Hisilicon/HiKey/HiKey.dsc
@@ -131,8 +131,9 @@ [PcdsFixedAtBuild.common]
   #
   # DW MMC/SD card controller
   #
-  gEmbeddedTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0xF723D000
-  gEmbeddedTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|100000000
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0xF723D000
+  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|100000000
+  gDesignWareTokenSpaceGuid.PcdDwPermitObsoleteDrivers|TRUE
 
   #
   #
@@ -216,7 +217,7 @@ [Components.common]
   # MMC/SD
   #
   EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
-  EmbeddedPkg/Drivers/DwEmmcDxe/DwEmmcDxe.inf
+  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
 
   #
   # USB Host Support
diff --git a/Platform/Hisilicon/HiKey/HiKey.fdf b/Platform/Hisilicon/HiKey/HiKey.fdf
index 33fa0da208fb..657e6c8ef2ec 100644
--- a/Platform/Hisilicon/HiKey/HiKey.fdf
+++ b/Platform/Hisilicon/HiKey/HiKey.fdf
@@ -128,7 +128,7 @@ [FV.FvMain]
   # Multimedia Card Interface
   #
   INF EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
-  INF EmbeddedPkg/Drivers/DwEmmcDxe/DwEmmcDxe.inf
+  INF Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
 
   #
   # USB Host Support
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
                   ` (4 preceding siblings ...)
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 5/8] Platform/HiKey: switch to relocated version of eMMC driver Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-05-04 11:41   ` [edk2-devel] " Philippe Mathieu-Daudé
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver Ard Biesheuvel
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

Incorporate the Lan91x driver from EmbeddedPkg, which is only used
on emulated ARM development platforms and does not follow the UEFI
driver model. This will allow us to drop if from the core EDK2
repository.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Platform/ARM/SgiPkg/SgiPlatform.dsc                      |    4 +-
 Platform/ARM/SgiPkg/SgiPlatform.fdf                      |    2 +-
 Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf     |    2 +-
 Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc             |    4 +-
 Platform/ARM/VExpressPkg/ArmVExpressPkg.dec              |    3 +
 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c   | 2236 ++++++++++++++++++++
 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf |   59 +
 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h |  279 +++
 8 files changed, 2583 insertions(+), 6 deletions(-)

diff --git a/Platform/ARM/SgiPkg/SgiPlatform.dsc b/Platform/ARM/SgiPkg/SgiPlatform.dsc
index 5226c5751e98..fddc1fef65d4 100644
--- a/Platform/ARM/SgiPkg/SgiPlatform.dsc
+++ b/Platform/ARM/SgiPkg/SgiPlatform.dsc
@@ -188,7 +188,7 @@ [PcdsFixedAtBuild.common]
 
   # Ethernet / Virtio Network
 !ifdef EDK2_ENABLE_SMSC_91X
-  gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x18000000
+  gArmVExpressTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x18000000
 !endif
   gArmSgiTokenSpaceGuid.PcdVirtioNetBaseAddress|0x1c150000
   gArmSgiTokenSpaceGuid.PcdVirtioNetSize|0x10000
@@ -313,7 +313,7 @@ [Components.common]
 
   # SMSC LAN 91C111 / Virtio Network
 !ifdef EDK2_ENABLE_SMSC_91X
-  EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
+  Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
 !endif
   OvmfPkg/VirtioNetDxe/VirtioNet.inf
 
diff --git a/Platform/ARM/SgiPkg/SgiPlatform.fdf b/Platform/ARM/SgiPkg/SgiPlatform.fdf
index 3d13998015b9..2bf00f0022f3 100644
--- a/Platform/ARM/SgiPkg/SgiPlatform.fdf
+++ b/Platform/ARM/SgiPkg/SgiPlatform.fdf
@@ -187,7 +187,7 @@ [FV.FvMain]
 !include NetworkPkg/Network.fdf.inc
 
 !ifdef EDK2_ENABLE_SMSC_91X
-  INF EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
 !endif
   INF OvmfPkg/VirtioNetDxe/VirtioNet.inf
 
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf
index f18ead75eaec..513054dbcbc5 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf
@@ -154,7 +154,7 @@ [FV.FvMain]
 
 !ifdef EDK2_ENABLE_SMSC_91X
 !include Platform/ARM/VExpressPkg/ArmVExpress-networking.fdf.inc
-  INF EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
 !endif
 
   #
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
index 0d543d2a61b1..912ad5e5a1ec 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
+++ b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
@@ -368,7 +368,7 @@ [PcdsFixedAtBuild.common]
 
 !ifdef EDK2_ENABLE_SMSC_91X
   # Ethernet (SMSC 91C111)
-  gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x1A000000
+  gArmVExpressTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x1A000000
 !endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
@@ -417,7 +417,7 @@ [Components.common]
 !include NetworkPkg/Network.dsc.inc
 !ifdef EDK2_ENABLE_SMSC_91X
   # SMSC LAN 91C111
-  EmbeddedPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
+  Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
 !endif
 
   #
diff --git a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
index a4e9bfd73eb6..ea000a0e5387 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+++ b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
@@ -56,3 +56,6 @@ [PcdsFixedAtBuild.common]
   ## PL180 MCI
   gArmVExpressTokenSpaceGuid.PcdPL180SysMciRegAddress|0x00000000|UINT32|0x00000009
   gArmVExpressTokenSpaceGuid.PcdPL180MciBaseAddress|0x00000000|UINT32|0x0000000A
+
+  # LAN91x Ethernet Driver PCDs
+  gArmVExpressTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x0|UINT32|0x0000000B
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c b/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c
new file mode 100644
index 000000000000..fae318ab96d5
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c
@@ -0,0 +1,2236 @@
+/** @file
+*  SMSC LAN91x series Network Controller Driver.
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2013-2017 Linaro.org
+*
+*  Derived from the LAN9118 driver. Original sources
+*  Copyright (c) 2012-2013, ARM Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include <Uefi.h>
+#include <Uefi/UefiSpec.h>
+#include <Base.h>
+
+// Protocols used by this driver
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/DevicePath.h>
+
+// Libraries used by this driver
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/NetLib.h>
+#include <Library/DevicePathLib.h>
+
+// Hardware register definitions
+#include "Lan91xDxeHw.h"
+
+// Debugging output options
+//#define LAN91X_PRINT_REGISTERS 1
+//#define LAN91X_PRINT_PACKET_HEADERS 1
+//#define LAN91X_PRINT_RECEIVE_FILTERS 1
+
+// Chip power-down option -- UNTESTED
+//#define LAN91X_POWER_DOWN 1
+
+/*---------------------------------------------------------------------------------------------------------------------
+
+  LAN91x Information Structure
+
+---------------------------------------------------------------------------------------------------------------------*/
+typedef struct _LAN91X_DRIVER {
+  // Driver signature
+  UINT32            Signature;
+  EFI_HANDLE        ControllerHandle;
+
+  // EFI SNP protocol instances
+  EFI_SIMPLE_NETWORK_PROTOCOL Snp;
+  EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+  // EFI Snp statistics instance
+  EFI_NETWORK_STATISTICS Stats;
+
+  // Transmit Buffer recycle queue
+
+  LIST_ENTRY TransmitQueueHead;
+
+  // Register access variables
+  UINTN             IoBase;             // I/O Base Address
+  UINT8             Revision;           // Chip Revision Number
+  INT8              PhyAd;              // Phy Address
+  UINT8             BankSel;            // Currently selected register bank
+
+} LAN91X_DRIVER;
+
+#define LAN91X_NO_PHY (-1)              // PhyAd value if PHY not detected
+
+#define LAN91X_SIGNATURE                        SIGNATURE_32('S', 'M', '9', '1')
+#define INSTANCE_FROM_SNP_THIS(a)               CR(a, LAN91X_DRIVER, Snp, LAN91X_SIGNATURE)
+
+#define LAN91X_STALL              2
+#define LAN91X_MEMORY_ALLOC_POLLS 100   // Max times to poll for memory allocation
+#define LAN91X_PKT_OVERHEAD       6     // Overhead bytes in packet buffer
+
+// Synchronization TPLs
+#define LAN91X_TPL  TPL_CALLBACK
+
+// Most common CRC32 Polynomial for little endian machines
+#define CRC_POLYNOMIAL               0xEDB88320
+
+
+typedef struct {
+  MAC_ADDR_DEVICE_PATH      Lan91x;
+  EFI_DEVICE_PATH_PROTOCOL  End;
+} LAN91X_DEVICE_PATH;
+
+LAN91X_DEVICE_PATH Lan91xPathTemplate =  {
+  {
+    {
+      MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP,
+      { (UINT8) (sizeof(MAC_ADDR_DEVICE_PATH)), (UINT8) ((sizeof(MAC_ADDR_DEVICE_PATH)) >> 8) }
+    },
+    { { 0 } },
+    0
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    { sizeof(EFI_DEVICE_PATH_PROTOCOL), 0 }
+  }
+};
+
+// Chip ID numbers and name strings
+#define CHIP_9192       3
+#define CHIP_9194       4
+#define CHIP_9195       5
+#define CHIP_9196       6
+#define CHIP_91100      7
+#define CHIP_91100FD    8
+#define CHIP_91111FD    9
+
+STATIC CHAR16 CONST * CONST ChipIds[ 16 ] =  {
+  NULL, NULL, NULL,
+  /* 3 */ L"SMC91C90/91C92",
+  /* 4 */ L"SMC91C94",
+  /* 5 */ L"SMC91C95",
+  /* 6 */ L"SMC91C96",
+  /* 7 */ L"SMC91C100",
+  /* 8 */ L"SMC91C100FD",
+  /* 9 */ L"SMC91C11xFD",
+  NULL, NULL, NULL,
+  NULL, NULL, NULL
+};
+
+/* ------------------ TxBuffer Queue structures ------------------- */
+
+typedef struct {
+  VOID            *Buf;
+  UINTN           Length;
+} MSK_SYSTEM_BUF;
+
+typedef struct {
+  UINTN           Signature;
+  LIST_ENTRY      Link;
+  MSK_SYSTEM_BUF  SystemBuf;
+} MSK_LINKED_SYSTEM_BUF;
+
+#define TX_MBUF_SIGNATURE  SIGNATURE_32 ('t','x','m','b')
+
+/* ------------------ MAC Address Hash Calculations ------------------- */
+
+/*
+**  Generate a hash value from a multicast address
+**
+**  This uses the Ethernet standard CRC32 algorithm
+**
+**  INFO USED:
+**    1: http://en.wikipedia.org/wiki/Cyclic_redundancy_check
+**
+**    2: http://www.erg.abdn.ac.uk/~gorry/eg3567/dl-pages/crc.html
+**
+**    3: http://en.wikipedia.org/wiki/Computation_of_CRC
+*/
+STATIC
+UINT32
+MulticastHash (
+  IN    EFI_MAC_ADDRESS *Mac,
+  IN    UINT32 AddrLen
+  )
+{
+  UINT32 Iter;
+  UINT32 Remainder;
+  UINT32 Crc32;
+  UINT8 *Addr;
+
+  // 0xFFFFFFFF is standard seed for Ethernet
+  Remainder = 0xFFFFFFFF;
+
+  // Generate the remainder byte-by-byte (LSB first)
+  Addr = &Mac->Addr[0];
+  while (AddrLen-- > 0) {
+    Remainder ^= *Addr++;
+    for (Iter = 0; Iter < 8; ++Iter) {
+      // Check if exponent is set
+      if ((Remainder & 1) != 0) {
+        Remainder = (Remainder >> 1) ^ CRC_POLYNOMIAL;
+      } else {
+        Remainder = (Remainder >> 1) ^ 0;
+      }
+    }
+  }
+
+  // Reverse the bits of the remainder
+  Crc32 = 0;
+  for (Iter = 0; Iter < 32; ++Iter) {
+    Crc32 <<= 1;
+    Crc32 |= Remainder & 1;
+    Remainder >>= 1;
+  }
+  return Crc32;
+}
+
+
+/* ---------------- Banked Register Operations ------------------ */
+
+// Select the proper I/O bank
+STATIC
+VOID
+SelectIoBank (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Register
+  )
+{
+  UINT8   Bank;
+
+  Bank = RegisterToBank (Register);
+
+  // Select the proper I/O bank
+  if (LanDriver->BankSel != Bank) {
+    MmioWrite16 (LanDriver->IoBase + LAN91X_BANK_OFFSET, Bank);
+    LanDriver->BankSel = Bank;
+  }
+}
+
+// Read a 16-bit I/O-space register
+STATIC
+UINT16
+ReadIoReg16 (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Register
+  )
+{
+  UINT8   Offset;
+
+  // Select the proper I/O bank
+  SelectIoBank (LanDriver, Register);
+
+  // Read the requested register
+  Offset = RegisterToOffset (Register);
+  return MmioRead16 (LanDriver->IoBase + Offset);
+}
+
+// Write a 16-bit I/O-space register
+STATIC
+UINT16
+WriteIoReg16 (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Register,
+  UINT16           Value
+  )
+{
+  UINT8   Offset;
+
+  // Select the proper I/O bank
+  SelectIoBank (LanDriver, Register);
+
+  // Write the requested register
+  Offset = RegisterToOffset (Register);
+  return MmioWrite16 (LanDriver->IoBase + Offset, Value);
+}
+
+// Read an 8-bit I/O-space register
+STATIC
+UINT8
+ReadIoReg8 (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Register
+  )
+{
+  UINT8   Offset;
+
+  // Select the proper I/O bank
+  SelectIoBank (LanDriver, Register);
+
+  // Read the requested register
+  Offset = RegisterToOffset (Register);
+  return MmioRead8 (LanDriver->IoBase + Offset);
+}
+
+// Write an 8-bit I/O-space register
+STATIC
+UINT8
+WriteIoReg8 (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Register,
+  UINT8            Value
+  )
+{
+  UINT8   Offset;
+
+  // Select the proper I/O bank
+  SelectIoBank (LanDriver, Register);
+
+  // Write the requested register
+  Offset = RegisterToOffset (Register);
+  return MmioWrite8 (LanDriver->IoBase + Offset, Value);
+}
+
+
+/* ---------------- MII/PHY Access Operations ------------------ */
+
+#define LAN91X_MDIO_STALL   1
+
+STATIC
+VOID
+MdioOutput (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Bits,
+  UINT32           Value
+  )
+{
+  UINT16          MgmtReg;
+  UINT32          Mask;
+
+  MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT);
+  MgmtReg &= ~MGMT_MCLK;
+  MgmtReg |= MGMT_MDOE;
+
+  for (Mask = (1 << (Bits - 1)); Mask != 0; Mask >>= 1) {
+    if ((Value & Mask) != 0) {
+      MgmtReg |= MGMT_MDO;
+    } else {
+      MgmtReg &= ~MGMT_MDO;
+    }
+
+    WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);
+    gBS->Stall (LAN91X_MDIO_STALL);
+    WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg | MGMT_MCLK);
+    gBS->Stall (LAN91X_MDIO_STALL);
+  }
+}
+#define PHY_OUTPUT_TIME (2 * LAN91X_MDIO_STALL)
+
+STATIC
+UINT32
+MdioInput (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            Bits
+  )
+{
+  UINT16          MgmtReg;
+  UINT32          Mask;
+  UINT32          Value;
+
+  MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT);
+  MgmtReg &= ~(MGMT_MDOE | MGMT_MCLK | MGMT_MDO);
+  WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);
+
+  Value = 0;
+  for (Mask = (1 << (Bits - 1)); Mask != 0; Mask >>= 1) {
+    if ((ReadIoReg16 (LanDriver, LAN91X_MGMT) & MGMT_MDI) != 0) {
+       Value |= Mask;
+    }
+
+    WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);
+    gBS->Stall (LAN91X_MDIO_STALL);
+    WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg | MGMT_MCLK);
+    gBS->Stall (LAN91X_MDIO_STALL);
+  }
+
+  return Value;
+}
+#define PHY_INPUT_TIME (2 * LAN91X_MDIO_STALL)
+
+STATIC
+VOID
+MdioIdle (
+  LAN91X_DRIVER   *LanDriver
+  )
+{
+  UINT16          MgmtReg;
+
+  MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT);
+  MgmtReg &= ~(MGMT_MDOE | MGMT_MCLK | MGMT_MDO);
+  WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);
+}
+
+// Write to a PHY register
+STATIC
+VOID
+WritePhyReg16 (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            RegAd,
+  UINT16           Value
+  )
+{
+  // Bit-bang the MII Serial Frame write operation
+  MdioOutput (LanDriver, 32, 0xffffffff);       // Send 32 Ones as a preamble
+  MdioOutput (LanDriver,  2, 0x01);             // Send Start (01)
+  MdioOutput (LanDriver,  2, 0x01);             // Send Write (01)
+  MdioOutput (LanDriver,  5, LanDriver->PhyAd); // Send PHYAD[4:0]
+  MdioOutput (LanDriver,  5, RegAd);            // Send REGAD[4:0]
+  MdioOutput (LanDriver,  2, 0x02);             // Send TurnAround (10)
+  MdioOutput (LanDriver, 16, Value);            // Write 16 data bits
+
+  // Idle the MDIO bus
+  MdioIdle (LanDriver);
+}
+// Calculate approximate time to write a PHY register in microseconds
+#define PHY_WRITE_TIME  ((32 + 2 + 2 + 5 + 5 + 2 + 16) * PHY_OUTPUT_TIME)
+
+// Read from a PHY register
+STATIC
+UINT16
+ReadPhyReg16 (
+  LAN91X_DRIVER   *LanDriver,
+  UINTN            RegAd
+  )
+{
+  UINT32 Value;
+
+  // Bit-bang the MII Serial Frame read operation
+  MdioOutput (LanDriver, 32, 0xffffffff);       // Send 32 Ones as a preamble
+  MdioOutput (LanDriver,  2, 0x01);             // Send Start (01)
+  MdioOutput (LanDriver,  2, 0x02);             // Send Read (10)
+  MdioOutput (LanDriver,  5, LanDriver->PhyAd); // Send PHYAD[4:0]
+  MdioOutput (LanDriver,  5, RegAd);            // Send REGAD[4:0]
+
+  (VOID)  MdioInput (LanDriver, 2);             // Discard TurnAround bits
+  Value = MdioInput (LanDriver, 16);            // Read 16 data bits
+
+  // Idle the MDIO bus
+  MdioIdle (LanDriver);
+
+  return (Value & 0xffff);
+}
+// Calculate approximate time to read a PHY register in microseconds
+#define PHY_READ_TIME  (((32 + 2 + 2 + 5 + 5) * PHY_OUTPUT_TIME) + \
+                        ((2 + 16) * PHY_INPUT_TIME))
+
+
+/* ---------------- Debug Functions ------------------ */
+
+#ifdef LAN91X_PRINT_REGISTERS
+STATIC
+VOID
+PrintIoRegisters (
+  IN  LAN91X_DRIVER   *LanDriver
+  )
+{
+  UINTN   Bank;
+  UINTN   Offset;
+  UINT16  Value;
+
+  DEBUG ((DEBUG_ERROR, "\nLAN91x I/O Register Dump:\n"));
+
+  // Print current bank select register
+  Value = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET);
+  DEBUG ((DEBUG_ERROR, "  BankSel: %d  Bank Register %04x (%d)\n",
+      LanDriver->BankSel, Value, Value & 0x0007));
+
+  // Print all I/O registers
+  for (Offset = 0; Offset < 0x0e; Offset += 2) {
+    DEBUG ((DEBUG_ERROR, "  %02x:", Offset));
+    for (Bank = 0; Bank <= 3; ++Bank) {
+      DEBUG ((DEBUG_ERROR, "  %04x", ReadIoReg16 (LanDriver, MakeRegister (Bank, Offset))));
+    }
+    DEBUG ((DEBUG_ERROR, "\n"));
+  }
+}
+
+STATIC
+VOID
+PrintPhyRegisters (
+  IN  LAN91X_DRIVER   *LanDriver
+  )
+{
+  UINTN   RegNum;
+
+  DEBUG ((DEBUG_ERROR, "\nLAN91x Phy %d Register Dump:\n", LanDriver->PhyAd));
+
+  // Print all Phy registers
+  for (RegNum = 0; RegNum <= 5; ++RegNum) {
+    DEBUG ((DEBUG_ERROR, "  %2d:  %04x\n",
+           RegNum,
+           ReadPhyReg16 (LanDriver, RegNum)
+    ));
+  }
+  for (RegNum = 16; RegNum <= 20; ++RegNum) {
+    DEBUG ((DEBUG_ERROR, "  %2d:  %04x\n",
+           RegNum,
+           ReadPhyReg16 (LanDriver, RegNum)
+    ));
+  }
+}
+#endif
+
+#if LAN91X_PRINT_PACKET_HEADERS
+STATIC
+VOID
+PrintIpDgram (
+  IN  CONST VOID  *DstMac,
+  IN  CONST VOID  *SrcMac,
+  IN  CONST VOID  *Proto,
+  IN  CONST VOID  *IpDgram
+  )
+{
+  CONST UINT8   *Ptr;
+  UINT16         SrcPort;
+  UINT16         DstPort;
+
+  Ptr = DstMac;
+  DEBUG ((DEBUG_ERROR, "  Dst: %02x-%02x-%02x",
+         Ptr[0], Ptr[1], Ptr[2]));
+  DEBUG ((DEBUG_ERROR, "-%02x-%02x-%02x",
+         Ptr[3], Ptr[4], Ptr[5]));
+
+  Ptr = SrcMac;
+  DEBUG ((DEBUG_ERROR, "  Src: %02x-%02x-%02x",
+         Ptr[0], Ptr[1], Ptr[2]));
+  DEBUG ((DEBUG_ERROR, "-%02x-%02x-%02x",
+         Ptr[3], Ptr[4], Ptr[5]));
+
+  Ptr = Proto;
+  DEBUG ((DEBUG_ERROR, "  Proto: %02x%02x\n",
+         Ptr[0], Ptr[1]));
+
+  Ptr = IpDgram;
+  switch (Ptr[9]) {
+  case EFI_IP_PROTO_ICMP:
+    DEBUG ((DEBUG_ERROR, "  ICMP"));
+    break;
+  case EFI_IP_PROTO_TCP:
+    DEBUG ((DEBUG_ERROR, "  TCP"));
+    break;
+  case EFI_IP_PROTO_UDP:
+    DEBUG ((DEBUG_ERROR, "  UDP"));
+    break;
+  default:
+    DEBUG ((DEBUG_ERROR, "  IpProto %d\n", Ptr[9]));
+    return;
+  }
+
+  DEBUG ((DEBUG_ERROR, "  SrcIp: %d.%d.%d.%d",
+         Ptr[12], Ptr[13], Ptr[14], Ptr[15]));
+  DEBUG ((DEBUG_ERROR, "  DstIp: %d.%d.%d.%d",
+         Ptr[16], Ptr[17], Ptr[18], Ptr[19]));
+
+  SrcPort = (Ptr[20] << 8) | Ptr[21];
+  DstPort = (Ptr[22] << 8) | Ptr[23];
+  DEBUG ((DEBUG_ERROR, "  SrcPort: %d  DstPort: %d\n", SrcPort, DstPort));
+}
+#endif
+
+
+/* ---------------- PHY Management Operations ----------------- */
+
+STATIC
+EFI_STATUS
+PhyDetect (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+  UINT16  PhyId1;
+  UINT16  PhyId2;
+
+  for (LanDriver->PhyAd = 0x1f; LanDriver->PhyAd >= 0 ; --LanDriver->PhyAd) {
+    PhyId1 = ReadPhyReg16 (LanDriver, PHY_INDEX_ID1);
+    PhyId2 = ReadPhyReg16 (LanDriver, PHY_INDEX_ID2);
+
+    if ((PhyId1 != 0x0000) && (PhyId1 != 0xffff) &&
+        (PhyId2 != 0x0000) && (PhyId2 != 0xffff)) {
+      if ((PhyId1 == 0x0016) && ((PhyId2 & 0xfff0) == 0xf840)) {
+        DEBUG ((DEBUG_ERROR, "LAN91x: PHY type LAN83C183 (LAN91C111 Internal)\n"));
+      } else if ((PhyId1 == 0x0282) && ((PhyId2 & 0xfff0) == 0x1c50)) {
+        DEBUG ((DEBUG_ERROR, "LAN91x: PHY type LAN83C180\n"));
+      } else {
+        DEBUG ((DEBUG_ERROR, "LAN91x: PHY id %04x:%04x\n", PhyId1, PhyId2));
+      }
+      return EFI_SUCCESS;
+    }
+  }
+
+  DEBUG ((DEBUG_ERROR, "LAN91x: PHY detection failed\n"));
+  return EFI_NO_MEDIA;
+}
+
+
+// Check the Link Status and take appropriate action
+STATIC
+BOOLEAN
+CheckLinkStatus (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+  UINT16  PhyStatus;
+
+  // Get the PHY Status
+  PhyStatus = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS);
+
+  return (PhyStatus & PHYSTS_LINK_STS) != 0;
+}
+
+
+// Do auto-negotiation
+STATIC
+EFI_STATUS
+PhyAutoNegotiate (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+  UINTN  Retries;
+  UINT16 PhyControl;
+  UINT16 PhyStatus;
+  UINT16 PhyAdvert;
+
+  // If there isn't a PHY, don't try to reset it
+  if (LanDriver->PhyAd == LAN91X_NO_PHY) {
+    return EFI_SUCCESS;
+  }
+
+  // Next check that auto-negotiation is supported
+  PhyStatus = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS);
+  if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) {
+    return EFI_SUCCESS;
+  }
+
+  // Translate capabilities to advertise
+  PhyAdvert = PHYANA_CSMA;
+
+  if ((PhyStatus & PHYSTS_10BASET_HDPLX) != 0) {
+    PhyAdvert |= PHYANA_10BASET;
+  }
+  if ((PhyStatus & PHYSTS_10BASET_FDPLX) != 0) {
+    PhyAdvert |= PHYANA_10BASETFD;
+  }
+  if ((PhyStatus & PHYSTS_100BASETX_HDPLX) != 0) {
+    PhyAdvert |= PHYANA_100BASETX;
+  }
+  if ((PhyStatus & PHYSTS_100BASETX_FDPLX) != 0) {
+    PhyAdvert |= PHYANA_100BASETXFD;
+  }
+  if ((PhyStatus & PHYSTS_100BASE_T4) != 0) {
+    PhyAdvert |= PHYANA_100BASET4;
+  }
+
+  // Set the capabilities to advertise
+  WritePhyReg16 (LanDriver, PHY_INDEX_AUTO_NEG_ADVERT, PhyAdvert);
+  (VOID) ReadPhyReg16 (LanDriver, PHY_INDEX_AUTO_NEG_ADVERT);
+
+  // Restart Auto-Negotiation
+  PhyControl = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL);
+  PhyControl &= ~(PHYCR_SPEED_SEL | PHYCR_DUPLEX_MODE);
+  PhyControl |= PHYCR_AUTO_EN | PHYCR_RST_AUTO;
+  WritePhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL, PhyControl);
+
+  // Wait up to 2 seconds for the process to complete
+  Retries = 2000000 / (PHY_READ_TIME + 100);
+  while ((ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0) {
+    if (--Retries == 0) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: PHY auto-negotiation timed-out\n"));
+      return EFI_TIMEOUT;
+    }
+    gBS->Stall (100);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+// Perform PHY software reset
+STATIC
+EFI_STATUS
+PhySoftReset (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+  UINTN     Retries;
+
+  // If there isn't a PHY, don't try to reset it
+  if (LanDriver->PhyAd == LAN91X_NO_PHY) {
+    return EFI_SUCCESS;
+  }
+
+  // Request a PHY reset
+  WritePhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL, PHYCR_RESET);
+
+  // The internal PHY will reset within 50ms. Allow 100ms.
+  Retries = 100000 / (PHY_READ_TIME + 100);
+  while (ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) {
+    if (--Retries == 0) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: PHY reset timed-out\n"));
+      return EFI_TIMEOUT;
+    }
+    gBS->Stall (100);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/* ---------------- General Operations ----------------- */
+
+STATIC
+EFI_MAC_ADDRESS
+GetCurrentMacAddress (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+  UINTN            RegNum;
+  UINT8           *Addr;
+  EFI_MAC_ADDRESS  MacAddress;
+
+  SetMem (&MacAddress, sizeof(MacAddress), 0);
+
+  Addr = &MacAddress.Addr[0];
+  for (RegNum = LAN91X_IAR0; RegNum <= LAN91X_IAR5; ++RegNum) {
+    *Addr = ReadIoReg8 (LanDriver, RegNum);
+    ++Addr;
+  }
+
+  return MacAddress;
+}
+
+STATIC
+EFI_STATUS
+SetCurrentMacAddress (
+  IN  LAN91X_DRIVER   *LanDriver,
+  IN  EFI_MAC_ADDRESS *MacAddress
+  )
+{
+  UINTN            RegNum;
+  UINT8           *Addr;
+
+  Addr = &MacAddress->Addr[0];
+  for (RegNum = LAN91X_IAR0; RegNum <= LAN91X_IAR5; ++RegNum) {
+    WriteIoReg8 (LanDriver, RegNum, *Addr);
+    ++Addr;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MmuOperation (
+  IN  LAN91X_DRIVER *LanDriver,
+  IN  UINTN          MmuOp
+  )
+{
+  UINTN   Polls;
+
+  WriteIoReg16 (LanDriver, LAN91X_MMUCR, MmuOp);
+  Polls = 100;
+  while ((ReadIoReg16 (LanDriver, LAN91X_MMUCR) & MMUCR_BUSY) != 0) {
+    if (--Polls == 0) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: MMU operation %04x timed-out\n", MmuOp));
+      return EFI_TIMEOUT;
+    }
+    gBS->Stall (LAN91X_STALL);
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Read bytes from the DATA register
+STATIC
+EFI_STATUS
+ReadIoData (
+  IN  LAN91X_DRIVER *LanDriver,
+  IN  VOID          *Buffer,
+  IN  UINTN          BufLen
+  )
+{
+  UINT8     *Ptr;
+
+  Ptr = Buffer;
+  for (; BufLen > 0; --BufLen) {
+    *Ptr = ReadIoReg8 (LanDriver, LAN91X_DATA0);
+    ++Ptr;
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Write bytes to the DATA register
+STATIC
+EFI_STATUS
+WriteIoData (
+  IN  LAN91X_DRIVER *LanDriver,
+  IN  VOID          *Buffer,
+  IN  UINTN          BufLen
+  )
+{
+  UINT8     *Ptr;
+
+  Ptr = Buffer;
+  for (; BufLen > 0; --BufLen) {
+    WriteIoReg8 (LanDriver, LAN91X_DATA0, *Ptr);
+    ++Ptr;
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Disable the interface
+STATIC
+EFI_STATUS
+ChipDisable (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+#ifdef LAN91X_POWER_DOWN
+  UINT16  Val16;
+#endif
+
+  // Stop Rx and Tx operations
+  WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR);
+  WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_CLEAR);
+
+#ifdef LAN91X_POWER_DOWN
+  // Power-down the chip
+  Val16 = ReadIoReg16 (LanDriver, LAN91X_CR);
+  Val16 &= ~CR_EPH_POWER_EN;
+  WriteIoReg16 (LanDriver, LAN91X_CR, Val16);
+#endif
+
+  return EFI_SUCCESS;
+}
+
+// Enable the interface
+STATIC
+EFI_STATUS
+ChipEnable (
+  IN  LAN91X_DRIVER *LanDriver
+  )
+{
+#ifdef LAN91X_POWER_DOWN
+  UINT16  Val16;
+
+  // Power-up the chip
+  Val16 = ReadIoReg16 (LanDriver, LAN91X_CR);
+  Val16 |= CR_EPH_POWER_EN;
+  WriteIoReg16 (LanDriver, LAN91X_CR, Val16);
+  gBS->Stall (LAN91X_STALL);
+#endif
+
+  // Start Rx and Tx operations
+  WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_DEFAULT);
+  WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_DEFAULT);
+
+  return EFI_SUCCESS;
+}
+
+
+// Perform software reset on the LAN91x
+STATIC
+EFI_STATUS
+SoftReset (
+  IN  LAN91X_DRIVER   *LanDriver
+  )
+{
+  UINT16  Val16;
+
+  // Issue the reset
+  WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_SOFT_RST);
+  gBS->Stall (LAN91X_STALL);
+  WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR);
+
+  // Set the configuration register
+  WriteIoReg16 (LanDriver, LAN91X_CR, CR_DEFAULT);
+  gBS->Stall (LAN91X_STALL);
+
+  // Stop Rx and Tx
+  WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR);
+  WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_CLEAR);
+
+  // Initialize the Control Register
+  Val16 = ReadIoReg16 (LanDriver, LAN91X_CTR);
+  Val16 |= CTR_AUTO_REL;
+  WriteIoReg16 (LanDriver, LAN91X_CTR, Val16);
+
+  // Reset the MMU
+  MmuOperation (LanDriver, MMUCR_OP_RESET_MMU);
+
+  return EFI_SUCCESS;
+}
+
+/*
+**  Probe()
+**
+**  Validate that there is a LAN91x device.
+**
+*/
+STATIC
+EFI_STATUS
+Probe (
+  IN  LAN91X_DRIVER   *LanDriver
+  )
+{
+  UINT16        Bank;
+  UINT16        Val16;
+  CHAR16 CONST *ChipId;
+  UINTN         ResetTime;
+
+  // First check that the Bank Select register is valid
+  Bank = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET);
+  if ((Bank & 0xff00) != 0x3300) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: signature error: expecting 33xx, read %04x\n", Bank));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Try reading the revision register next
+  LanDriver->BankSel = 0xff;
+  Val16 = ReadIoReg16 (LanDriver, LAN91X_REV);
+
+  Bank = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET);
+  if ((Bank & 0xff03) != 0x3303) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: signature error: expecting 33x3, read %04x\n", Bank));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Validate the revision register
+  if ((Val16 & 0xff00) != 0x3300) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: revision error: expecting 33xx, read %04x\n", Val16));
+    return EFI_DEVICE_ERROR;
+  }
+
+  ChipId = ChipIds[(Val16 >> 4) & 0x0f];
+  if (ChipId == NULL) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: unrecognized revision: %04x\n", Val16));
+    return EFI_DEVICE_ERROR;
+  }
+  DEBUG ((DEBUG_ERROR, "LAN91x: detected chip %s rev %d\n", ChipId, Val16 & 0xf));
+  LanDriver->Revision = Val16 & 0xff;
+
+  // Reload from EEPROM to get the hardware MAC address
+  WriteIoReg16 (LanDriver, LAN91X_CTR, CTR_RESERVED | CTR_RELOAD);
+  ResetTime = 1000;
+  while ((ReadIoReg16 (LanDriver, LAN91X_CTR) & CTR_RELOAD) != 0) {
+    if (--ResetTime == 0) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: reload from EEPROM timed-out\n"));
+      WriteIoReg16 (LanDriver, LAN91X_CTR, CTR_RESERVED);
+      return EFI_DEVICE_ERROR;
+    }
+    gBS->Stall (LAN91X_STALL);
+  }
+
+  // Read and save the Permanent MAC Address
+  LanDriver->SnpMode.PermanentAddress = GetCurrentMacAddress (LanDriver);
+  LanDriver->SnpMode.CurrentAddress = LanDriver->SnpMode.PermanentAddress;
+  DEBUG ((DEBUG_ERROR, //DEBUG_NET | DEBUG_INFO,
+         "LAN91x: HW MAC Address: %02x-%02x-%02x-%02x-%02x-%02x\n",
+         LanDriver->SnpMode.PermanentAddress.Addr[0],
+         LanDriver->SnpMode.PermanentAddress.Addr[1],
+         LanDriver->SnpMode.PermanentAddress.Addr[2],
+         LanDriver->SnpMode.PermanentAddress.Addr[3],
+         LanDriver->SnpMode.PermanentAddress.Addr[4],
+         LanDriver->SnpMode.PermanentAddress.Addr[5]
+         ));
+
+  // Reset the device
+  SoftReset (LanDriver);
+
+  // Try to detect a PHY
+  if (LanDriver->Revision > (CHIP_91100 << 4)) {
+    PhyDetect (LanDriver);
+  } else {
+    LanDriver->PhyAd = LAN91X_NO_PHY;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+
+
+/*------------------ Simple Network Driver entry point functions ------------------*/
+
+// Refer to the Simple Network Protocol section (21.1)
+// in the UEFI 2.3.1 Specification for documentation.
+
+#define ReturnUnlock(s) do { Status = (s); goto exit_unlock; } while(0)
+
+
+/*
+**  UEFI Start() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpStart (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+ )
+{
+  EFI_SIMPLE_NETWORK_MODE *Mode;
+  EFI_TPL                  SavedTpl;
+  EFI_STATUS               Status;
+
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+  Mode = Snp->Mode;
+
+  // Check state of the driver
+  switch (Mode->State) {
+  case EfiSimpleNetworkStopped:
+    break;
+  case EfiSimpleNetworkStarted:
+  case EfiSimpleNetworkInitialized:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver already started\n"));
+    ReturnUnlock (EFI_ALREADY_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+
+  // Change state
+  Mode->State = EfiSimpleNetworkStarted;
+  Status = EFI_SUCCESS;
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI Stop() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpStop (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  )
+{
+  LAN91X_DRIVER *LanDriver;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check state of the driver
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkStarted:
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Stop the Tx and Rx
+  ChipDisable (LanDriver);
+
+  // Change the state
+  Snp->Mode->State = EfiSimpleNetworkStopped;
+  Status = EFI_SUCCESS;
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI Initialize() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpInitialize (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN  UINTN                        RxBufferSize    OPTIONAL,
+  IN  UINTN                        TxBufferSize    OPTIONAL
+  )
+{
+  LAN91X_DRIVER *LanDriver;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started but not initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkStarted:
+    break;
+  case EfiSimpleNetworkInitialized:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver already initialized\n"));
+    ReturnUnlock (EFI_SUCCESS);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Initiate a software reset
+  Status = SoftReset (LanDriver);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Soft reset failed\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Initiate a PHY reset
+  if (PhySoftReset (LanDriver) < 0) {
+    Snp->Mode->State = EfiSimpleNetworkStopped;
+    DEBUG ((DEBUG_WARN, "LAN91x: PHY soft reset timeout\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  }
+
+  // Do auto-negotiation
+  Status = PhyAutoNegotiate (LanDriver);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((DEBUG_WARN, "LAN91x: PHY auto-negotiation failed\n"));
+  }
+
+  // Enable the receiver and transmitter
+  ChipEnable (LanDriver);
+
+  // Now acknowledge all interrupts
+  WriteIoReg8 (LanDriver, LAN91X_IST, 0xFF);
+
+  // Declare the driver as initialized
+  Snp->Mode->State = EfiSimpleNetworkInitialized;
+  Status = EFI_SUCCESS;
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI Reset () function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpReset (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN Verification
+  )
+{
+  LAN91X_DRIVER *LanDriver;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started and initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Initiate a software reset
+  if (EFI_ERROR (SoftReset (LanDriver))) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Soft reset failed\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Initiate a PHY reset
+  if (EFI_ERROR (PhySoftReset (LanDriver))) {
+    DEBUG ((DEBUG_WARN, "LAN91x: PHY soft reset failed\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Enable the receiver and transmitter
+  Status = ChipEnable (LanDriver);
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI Shutdown () function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpShutdown (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  )
+{
+  LAN91X_DRIVER *LanDriver;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // First check that driver has already been initialized
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver in stopped state\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Disable the interface
+  Status = ChipDisable (LanDriver);
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+
+/*
+**  UEFI ReceiveFilters() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpReceiveFilters (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        UINT32 Enable,
+  IN        UINT32 Disable,
+  IN        BOOLEAN Reset,
+  IN        UINTN NumMfilter          OPTIONAL,
+  IN        EFI_MAC_ADDRESS *Mfilter  OPTIONAL
+  )
+{
+#define MCAST_HASH_BYTES  8
+
+  LAN91X_DRIVER           *LanDriver;
+  EFI_SIMPLE_NETWORK_MODE *SnpMode;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+  UINTN          i;
+  UINT32         Crc;
+  UINT16         RcvCtrl;
+  UINT8          McastHash[MCAST_HASH_BYTES];
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // First check that driver has already been initialized
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+  SnpMode = Snp->Mode;
+
+#ifdef LAN91X_PRINT_RECEIVE_FILTERS
+  DEBUG ((DEBUG_ERROR, "LAN91x:SnpReceiveFilters()\n"));
+  DEBUG ((DEBUG_ERROR, "  Enable     = %08x\n", Enable));
+  DEBUG ((DEBUG_ERROR, "  Disable    = %08x\n", Disable));
+  DEBUG ((DEBUG_ERROR, "  Reset      = %d\n",  Reset));
+  DEBUG ((DEBUG_ERROR, "  NumMfilter = %d\n",  NumMfilter));
+  for (i = 0; i < NumMfilter; ++i) {
+    DEBUG ((DEBUG_ERROR,
+           "    [%2d] = %02x-%02x-%02x-%02x-%02x-%02x\n",
+           i,
+           Mfilter[i].Addr[0],
+           Mfilter[i].Addr[1],
+           Mfilter[i].Addr[2],
+           Mfilter[i].Addr[3],
+           Mfilter[i].Addr[4],
+           Mfilter[i].Addr[5]));
+  }
+#endif
+
+  // Update the Multicast Hash registers
+  if (Reset) {
+    // Clear the hash table
+    SetMem (McastHash, MCAST_HASH_BYTES, 0);
+    SnpMode->MCastFilterCount = 0;
+  } else {
+    // Read the current hash table
+    for (i = 0; i < MCAST_HASH_BYTES; ++i) {
+      McastHash[i] = ReadIoReg8 (LanDriver, LAN91X_MT0 + i);
+    }
+    // Set the new additions
+    for (i = 0; i < NumMfilter; ++i) {
+      Crc = MulticastHash (&Mfilter[i], NET_ETHER_ADDR_LEN);
+      McastHash[(Crc >> 29) & 0x3] |= 1 << ((Crc >> 26) & 0x3);
+    }
+    SnpMode->MCastFilterCount = NumMfilter;
+  }
+  // If the hash registers need updating, write them
+  if (Reset || NumMfilter > 0) {
+    for (i = 0; i < MCAST_HASH_BYTES; ++i) {
+      WriteIoReg8 (LanDriver, LAN91X_MT0 + i, McastHash[i]);
+    }
+  }
+
+  RcvCtrl = ReadIoReg16 (LanDriver, LAN91X_RCR);
+  if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
+    RcvCtrl |= RCR_PRMS;
+    SnpMode->ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+  }
+  if ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
+    RcvCtrl &= ~RCR_PRMS;
+    SnpMode->ReceiveFilterSetting &= ~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+  }
+
+  if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+    RcvCtrl |= RCR_ALMUL;
+    SnpMode->ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+  }
+  if ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+    RcvCtrl &= ~RCR_ALMUL;
+    SnpMode->ReceiveFilterSetting &= ~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+  }
+  WriteIoReg16 (LanDriver, LAN91X_RCR, RcvCtrl);
+
+  Status = SetCurrentMacAddress (LanDriver, &SnpMode->CurrentAddress);
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI StationAddress() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpStationAddress (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
+  IN        BOOLEAN Reset,
+  IN        EFI_MAC_ADDRESS *NewMac
+)
+{
+  LAN91X_DRIVER *LanDriver;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started and initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  if (Reset) {
+    Snp->Mode->CurrentAddress = Snp->Mode->PermanentAddress;
+  } else {
+    if (NewMac == NULL) {
+      ReturnUnlock (EFI_INVALID_PARAMETER);
+    }
+    Snp->Mode->CurrentAddress = *NewMac;
+  }
+
+  Status = SetCurrentMacAddress (LanDriver, &Snp->Mode->CurrentAddress);
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI Statistics() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpStatistics (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN Reset,
+  IN  OUT   UINTN *StatSize,
+      OUT   EFI_NETWORK_STATISTICS *Statistics
+  )
+{
+  LAN91X_DRIVER *LanDriver;
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check pointless condition
+  if ((!Reset) && (StatSize == NULL) && (Statistics == NULL)) {
+    return EFI_SUCCESS;
+  }
+
+  // Check the parameters
+  if ((StatSize == NULL) && (Statistics != NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started and initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Do a reset if required
+  if (Reset) {
+    ZeroMem (&LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS));
+  }
+
+  // Check buffer size
+  if (*StatSize < sizeof(EFI_NETWORK_STATISTICS)) {
+    *StatSize = sizeof(EFI_NETWORK_STATISTICS);
+    ReturnUnlock (EFI_BUFFER_TOO_SMALL);
+    goto exit_unlock;
+  }
+
+  // Fill in the statistics
+  CopyMem(&Statistics, &LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS));
+  Status = EFI_SUCCESS;
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+/*
+**  UEFI MCastIPtoMAC() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpMcastIptoMac (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN IsIpv6,
+  IN        EFI_IP_ADDRESS *Ip,
+      OUT   EFI_MAC_ADDRESS *McastMac
+  )
+{
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check parameters
+  if ((McastMac == NULL) || (Ip == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Make sure MAC address is empty
+  ZeroMem (McastMac, sizeof(EFI_MAC_ADDRESS));
+
+  // If we need ipv4 address
+  if (!IsIpv6) {
+    // Most significant 25 bits of a multicast HW address are set
+    McastMac->Addr[0] = 0x01;
+    McastMac->Addr[1] = 0x00;
+    McastMac->Addr[2] = 0x5E;
+
+    // Lower 23 bits from ipv4 address
+    McastMac->Addr[3] = (Ip->v4.Addr[1] & 0x7F); // Clear the ms bit (25th bit of MAC must be 0)
+    McastMac->Addr[4] = Ip->v4.Addr[2];
+    McastMac->Addr[5] = Ip->v4.Addr[3];
+  } else {
+    // Most significant 16 bits of multicast v6 HW address are set
+    McastMac->Addr[0] = 0x33;
+    McastMac->Addr[1] = 0x33;
+
+    // lower four octets are taken from ipv6 address
+    McastMac->Addr[2] = Ip->v6.Addr[8];
+    McastMac->Addr[3] = Ip->v6.Addr[9];
+    McastMac->Addr[4] = Ip->v6.Addr[10];
+    McastMac->Addr[5] = Ip->v6.Addr[11];
+  }
+
+  return EFI_SUCCESS;
+}
+
+/*
+**  UEFI NvData() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpNvData (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* pobj,
+  IN        BOOLEAN read_write,
+  IN        UINTN offset,
+  IN        UINTN buff_size,
+  IN  OUT   VOID *data
+  )
+{
+  DEBUG ((DEBUG_ERROR, "LAN91x: Non-volatile storage not supported\n"));
+
+  return EFI_UNSUPPORTED;
+}
+
+
+/*
+**  UEFI GetStatus () function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpGetStatus (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
+      OUT   UINT32   *IrqStat   OPTIONAL,
+      OUT   VOID    **TxBuff    OPTIONAL
+  )
+{
+  LAN91X_DRIVER         *LanDriver;
+  EFI_TPL               SavedTpl;
+  EFI_STATUS            Status;
+  BOOLEAN               MediaPresent;
+  UINT8                 IstReg;
+  MSK_LINKED_SYSTEM_BUF *LinkedTXRecycleBuff;
+
+  // Check preliminaries
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started and initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Arbitrarily set the interrupt status to 0
+  if (IrqStat != NULL) {
+    *IrqStat = 0;
+    IstReg = ReadIoReg8 (LanDriver, LAN91X_IST);
+    if ((IstReg & IST_RCV) != 0) {
+      *IrqStat |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+    }
+    if ((IstReg & IST_TX) != 0) {
+      *IrqStat |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+    }
+  }
+
+  // Pass back the completed buffer address
+  // The transmit buffer status is not read when TxBuf is NULL
+  if (TxBuff != NULL) {
+    *((UINT8 **) TxBuff) = (UINT8 *) 0;
+    if( !IsListEmpty (&LanDriver->TransmitQueueHead))
+    {
+      LinkedTXRecycleBuff = CR (GetFirstNode (&LanDriver->TransmitQueueHead), MSK_LINKED_SYSTEM_BUF, Link, TX_MBUF_SIGNATURE);
+      if(LinkedTXRecycleBuff != NULL) {
+        *TxBuff = LinkedTXRecycleBuff->SystemBuf.Buf;
+        RemoveEntryList (&LinkedTXRecycleBuff->Link);
+        FreePool (LinkedTXRecycleBuff);
+      }
+    }
+  }
+
+  // Update the media status
+  MediaPresent = CheckLinkStatus (LanDriver);
+  if (MediaPresent != Snp->Mode->MediaPresent) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Link %s\n", MediaPresent ? L"up" : L"down"));
+  }
+  Snp->Mode->MediaPresent = MediaPresent;
+  Status = EFI_SUCCESS;
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+
+/*
+**  UEFI Transmit() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpTransmit (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
+  IN        UINTN            HdrSize,
+  IN        UINTN            BufSize,
+  IN        VOID            *BufAddr,
+  IN        EFI_MAC_ADDRESS *SrcAddr    OPTIONAL,
+  IN        EFI_MAC_ADDRESS *DstAddr    OPTIONAL,
+  IN        UINT16          *Protocol   OPTIONAL
+  )
+{
+  LAN91X_DRIVER   *LanDriver;
+  EFI_TPL          SavedTpl;
+  EFI_STATUS       Status;
+  UINT8           *Ptr;
+  UINTN            Len;
+  UINTN            MmuPages;
+  UINTN            Retries;
+  UINT16           Proto;
+  UINT8            PktNum;
+  MSK_LINKED_SYSTEM_BUF   *LinkedTXRecycleBuff;
+
+
+  // Check preliminaries
+  if ((Snp == NULL) || (BufAddr == NULL)) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: SnpTransmit(): NULL Snp (%p) or BufAddr (%p)\n",
+        Snp, BufAddr));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started and initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Ensure header is correct size if non-zero
+  if (HdrSize != 0) {
+    if (HdrSize != Snp->Mode->MediaHeaderSize) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: SnpTransmit(): Invalid HdrSize %d\n", HdrSize));
+      ReturnUnlock (EFI_INVALID_PARAMETER);
+    }
+
+    if ((DstAddr == NULL) || (Protocol == NULL)) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: SnpTransmit(): NULL DstAddr %p or Protocol %p\n",
+          DstAddr, Protocol));
+      ReturnUnlock (EFI_INVALID_PARAMETER);
+    }
+  }
+
+  // Before transmitting check the link status
+  if (!Snp->Mode->MediaPresent) {
+    DEBUG ((DEBUG_WARN, "LAN91x: SnpTransmit(): Link not ready\n"));
+    ReturnUnlock (EFI_NOT_READY);
+  }
+
+  // Calculate the request size in 256-byte "pages" minus 1
+  // The 91C111 ignores this, but some older devices need it.
+  MmuPages = ((BufSize & ~1) + LAN91X_PKT_OVERHEAD - 1) >> 8;
+  if (MmuPages > 7) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Tx buffer too large (%d bytes)\n", BufSize));
+    LanDriver->Stats.TxOversizeFrames += 1;
+    LanDriver->Stats.TxDroppedFrames += 1;
+    ReturnUnlock (EFI_BAD_BUFFER_SIZE);
+  }
+
+  // Request allocation of a transmit buffer
+  Status = MmuOperation (LanDriver, MMUCR_OP_TX_ALLOC | MmuPages);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer request failure: %d\n", Status));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Wait for allocation request completion
+  Retries = LAN91X_MEMORY_ALLOC_POLLS;
+  while ((ReadIoReg8 (LanDriver, LAN91X_IST) & IST_ALLOC) == 0) {
+    if (--Retries == 0) {
+      DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer allocation timeout\n"));
+      ReturnUnlock (EFI_TIMEOUT);
+    }
+  }
+
+  // Check for successful allocation
+  PktNum = ReadIoReg8 (LanDriver, LAN91X_ARR);
+  if ((PktNum & ARR_FAILED) != 0) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer allocation failure: %02x\n", PktNum));
+    ReturnUnlock (EFI_NOT_READY);
+  }
+  PktNum &= ARR_PACKET;
+
+  // Check for the nature of the frame
+  // If no destination address, it's ARP broadcast
+  if(DstAddr != NULL)
+  {
+    if (DstAddr->Addr[0] == 0xFF) {
+      LanDriver->Stats.TxBroadcastFrames += 1;
+    } else if ((DstAddr->Addr[0] & 0x1) == 1) {
+      LanDriver->Stats.TxMulticastFrames += 1;
+    } else {
+      LanDriver->Stats.TxUnicastFrames += 1;
+    }
+  } else {
+    LanDriver->Stats.TxBroadcastFrames += 1;
+  }
+
+  // Set the Packet Number and Pointer registers
+  WriteIoReg8 (LanDriver, LAN91X_PNR, PktNum);
+  WriteIoReg16 (LanDriver, LAN91X_PTR, PTR_AUTO_INCR);
+
+  // Set up mutable buffer information variables
+  Ptr = BufAddr;
+  Len = BufSize;
+
+  // Write Status and Byte Count first
+  WriteIoReg16 (LanDriver, LAN91X_DATA0, 0);
+  WriteIoReg16 (LanDriver, LAN91X_DATA0, (Len + LAN91X_PKT_OVERHEAD) & BCW_COUNT);
+
+  // This packet may come with a preconfigured Ethernet header.
+  // If not, we need to construct one from optional parameters.
+  if (HdrSize) {
+
+    // Write the destination address
+    WriteIoData (LanDriver, DstAddr, NET_ETHER_ADDR_LEN);
+
+    // Write the Source Address
+    if (SrcAddr != NULL) {
+      WriteIoData (LanDriver, SrcAddr, NET_ETHER_ADDR_LEN);
+    } else {
+      WriteIoData (LanDriver, &LanDriver->SnpMode.CurrentAddress, NET_ETHER_ADDR_LEN);
+    }
+
+    // Write the Protocol word
+    Proto = HTONS (*Protocol);
+    WriteIoReg16 (LanDriver, LAN91X_DATA0, Proto);
+
+    // Adjust the data start and length
+    Ptr += sizeof(ETHER_HEAD);
+    Len -= sizeof(ETHER_HEAD);
+  }
+
+  // Copy the remainder data buffer, except the odd byte
+  WriteIoData (LanDriver, Ptr, Len & ~1);
+  Ptr += Len & ~1;
+  Len &= 1;
+
+  // Write the Packet Control Word and odd byte
+  WriteIoReg16 (LanDriver, LAN91X_DATA0,
+      (Len != 0) ? (PCW_ODD | PCW_CRC | *Ptr) : PCW_CRC);
+
+  // Release the packet for transmission
+  Status = MmuOperation (LanDriver, MMUCR_OP_TX_PUSH);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer release failure: %d\n", Status));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Update the Tx statistics
+  LanDriver->Stats.TxTotalBytes += BufSize;
+  LanDriver->Stats.TxGoodFrames += 1;
+
+  // Update the Tx Buffer cache
+  LinkedTXRecycleBuff = AllocateZeroPool (sizeof (MSK_LINKED_SYSTEM_BUF));
+  if (LinkedTXRecycleBuff == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  LinkedTXRecycleBuff->Signature = TX_MBUF_SIGNATURE;
+  //
+  // Add the passed Buffer to the transmit queue. Don't copy.
+  //
+  LinkedTXRecycleBuff->SystemBuf.Buf = BufAddr;
+  LinkedTXRecycleBuff->SystemBuf.Length = BufSize;
+  InsertTailList (&LanDriver->TransmitQueueHead, &LinkedTXRecycleBuff->Link);
+
+  Status = EFI_SUCCESS;
+
+  // Dump the packet header
+#if LAN91X_PRINT_PACKET_HEADERS
+  Ptr = BufAddr;
+  DEBUG ((DEBUG_ERROR, "LAN91X:SnpTransmit()\n"));
+  DEBUG ((DEBUG_ERROR, "  HdrSize: %d, SrcAddr: %p, Length: %d, Last byte: %02x\n",
+         HdrSize, SrcAddr, BufSize, Ptr[BufSize - 1]));
+  PrintIpDgram (
+      (HdrSize == 0) ? (EFI_MAC_ADDRESS *)&Ptr[0] : DstAddr,
+      (HdrSize == 0) ? (EFI_MAC_ADDRESS *)&Ptr[6] : (SrcAddr != NULL) ? SrcAddr : &LanDriver->SnpMode.CurrentAddress,
+      (HdrSize == 0) ? (UINT16 *)&Ptr[12] : &Proto,
+      &Ptr[14]
+      );
+#endif
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+
+/*
+**  UEFI Receive() function
+**
+*/
+EFI_STATUS
+EFIAPI
+SnpReceive (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL *Snp,
+      OUT   UINTN           *HdrSize      OPTIONAL,
+  IN  OUT   UINTN           *BuffSize,
+      OUT   VOID            *Data,
+      OUT   EFI_MAC_ADDRESS *SrcAddr      OPTIONAL,
+      OUT   EFI_MAC_ADDRESS *DstAddr      OPTIONAL,
+      OUT   UINT16 *Protocol              OPTIONAL
+  )
+{
+  EFI_TPL        SavedTpl;
+  EFI_STATUS     Status;
+  LAN91X_DRIVER *LanDriver;
+  UINT8         *DataPtr;
+  UINT16         PktStatus;
+  UINT16         PktLength;
+  UINT16         PktControl;
+  UINT8          IstReg;
+
+  // Check preliminaries
+  if ((Snp == NULL) || (Data == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Serialize access to data and registers
+  SavedTpl = gBS->RaiseTPL (LAN91X_TPL);
+
+  // Check that driver was started and initialised
+  switch (Snp->Mode->State) {
+  case EfiSimpleNetworkInitialized:
+    break;
+  case EfiSimpleNetworkStarted:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  case EfiSimpleNetworkStopped:
+    DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));
+    ReturnUnlock (EFI_NOT_STARTED);
+  default:
+    DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",
+          (UINTN)Snp->Mode->State));
+    ReturnUnlock (EFI_DEVICE_ERROR);
+  }
+
+  // Find the LanDriver structure
+  LanDriver = INSTANCE_FROM_SNP_THIS(Snp);
+
+  // Check for Rx Overrun
+  IstReg = ReadIoReg8 (LanDriver, LAN91X_IST);
+  if ((IstReg & IST_RX_OVRN) != 0) {
+    LanDriver->Stats.RxTotalFrames += 1;
+    LanDriver->Stats.RxDroppedFrames += 1;
+    WriteIoReg8 (LanDriver, LAN91X_IST, IST_RX_OVRN);
+    DEBUG ((DEBUG_WARN, "LAN91x: Receiver overrun\n"));
+  }
+
+  // Check for Rx data available
+  if ((IstReg & IST_RCV) == 0) {
+    ReturnUnlock (EFI_NOT_READY);
+  }
+
+  // Configure the PTR register for reading
+  WriteIoReg16 (LanDriver, LAN91X_PTR, PTR_RCV | PTR_AUTO_INCR | PTR_READ);
+
+  // Read the Packet Status and Packet Length words
+  PktStatus = ReadIoReg16 (LanDriver, LAN91X_DATA0);
+  PktLength = ReadIoReg16 (LanDriver, LAN91X_DATA0) & BCW_COUNT;
+
+  // Check for valid received packet
+  if ((PktStatus == 0) && (PktLength == 0)) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Received zero-length packet. IST=%04x\n", IstReg));
+    ReturnUnlock (EFI_NOT_READY);
+  }
+  LanDriver->Stats.RxTotalFrames += 1;
+
+  // Check if we got a CRC error
+  if ((PktStatus & RX_BAD_CRC) != 0) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Received frame CRC error\n"));
+    LanDriver->Stats.RxCrcErrorFrames += 1;
+    LanDriver->Stats.RxDroppedFrames += 1;
+    Status = EFI_DEVICE_ERROR;
+    goto exit_release;
+  }
+
+  // Check if we got a too-short frame
+  if ((PktStatus & RX_TOO_SHORT) != 0) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Received frame too short (%d bytes)\n", PktLength));
+    LanDriver->Stats.RxUndersizeFrames += 1;
+    LanDriver->Stats.RxDroppedFrames += 1;
+    Status = EFI_DEVICE_ERROR;
+    goto exit_release;
+  }
+
+   // Check if we got a too-long frame
+  if ((PktStatus & RX_TOO_LONG) != 0) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Received frame too long (%d bytes)\n", PktLength));
+    LanDriver->Stats.RxOversizeFrames += 1;
+    LanDriver->Stats.RxDroppedFrames += 1;
+    Status = EFI_DEVICE_ERROR;
+    goto exit_release;
+  }
+
+   // Check if we got an alignment error
+  if ((PktStatus & RX_ALGN_ERR) != 0) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Received frame alignment error\n"));
+    // Don't seem to keep track of these specifically
+    LanDriver->Stats.RxDroppedFrames += 1;
+    Status = EFI_DEVICE_ERROR;
+    goto exit_release;
+  }
+
+  // Classify the received fram
+  if ((PktStatus & RX_MULTICAST) != 0) {
+    LanDriver->Stats.RxMulticastFrames += 1;
+  } else if ((PktStatus & RX_BROADCAST) != 0) {
+    LanDriver->Stats.RxBroadcastFrames += 1;
+  } else {
+    LanDriver->Stats.RxUnicastFrames += 1;
+  }
+
+  // Calculate the received packet data length
+  PktLength -= LAN91X_PKT_OVERHEAD;
+  if ((PktStatus & RX_ODD_FRAME) != 0) {
+    PktLength += 1;
+  }
+
+  // Check buffer size
+  if (*BuffSize < PktLength) {
+    DEBUG ((DEBUG_WARN, "LAN91x: Receive buffer too small for packet (%d < %d)\n",
+        *BuffSize, PktLength));
+    *BuffSize = PktLength;
+    Status = EFI_BUFFER_TOO_SMALL;
+    goto exit_release;
+  }
+
+  // Transfer the data bytes
+  DataPtr = Data;
+  ReadIoData (LanDriver, DataPtr, PktLength & ~0x0001);
+
+  // Read the PktControl and Odd Byte from the FIFO
+  PktControl = ReadIoReg16 (LanDriver, LAN91X_DATA0);
+  if ((PktControl & PCW_ODD) != 0) {
+    DataPtr[PktLength - 1] = PktControl & PCW_ODD_BYTE;
+  }
+
+  // Update buffer size
+  *BuffSize = PktLength;
+
+  if (HdrSize != NULL) {
+    *HdrSize = LanDriver->SnpMode.MediaHeaderSize;
+  }
+
+  // Extract the destination address
+  if (DstAddr != NULL) {
+    CopyMem (DstAddr, &DataPtr[0], NET_ETHER_ADDR_LEN);
+  }
+
+  // Get the source address
+  if (SrcAddr != NULL) {
+    CopyMem (SrcAddr, &DataPtr[6], NET_ETHER_ADDR_LEN);
+  }
+
+  // Get the protocol
+  if (Protocol != NULL) {
+    *Protocol = NTOHS (*(UINT16*)(&DataPtr[12]));
+  }
+
+  // Update the Rx statistics
+  LanDriver->Stats.RxTotalBytes += PktLength;
+  LanDriver->Stats.RxGoodFrames += 1;
+  Status = EFI_SUCCESS;
+
+#if LAN91X_PRINT_PACKET_HEADERS
+  // Dump the packet header
+  DEBUG ((DEBUG_ERROR, "LAN91X:SnpReceive()\n"));
+  DEBUG ((DEBUG_ERROR, "  HdrSize: %p, SrcAddr: %p, DstAddr: %p, Protocol: %p\n",
+         HdrSize, SrcAddr, DstAddr, Protocol));
+  DEBUG ((DEBUG_ERROR, "  Length: %d, Last byte: %02x\n", PktLength, DataPtr[PktLength - 1]));
+  PrintIpDgram (&DataPtr[0], &DataPtr[6], &DataPtr[12], &DataPtr[14]);
+#endif
+
+  // Release the FIFO buffer
+exit_release:
+  MmuOperation (LanDriver, MMUCR_OP_RX_POP_REL);
+
+  // Restore TPL and return
+exit_unlock:
+  gBS->RestoreTPL (SavedTpl);
+  return Status;
+}
+
+
+/*------------------ Driver Execution Environment main entry point ------------------*/
+
+/*
+**  Entry point for the LAN91x driver
+**
+*/
+EFI_STATUS
+Lan91xDxeEntry (
+  IN EFI_HANDLE Handle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS Status;
+  LAN91X_DRIVER *LanDriver;
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+  EFI_SIMPLE_NETWORK_MODE *SnpMode;
+  LAN91X_DEVICE_PATH *Lan91xPath;
+
+  // The PcdLan91xDxeBaseAddress PCD must be defined
+  ASSERT(PcdGet32 (PcdLan91xDxeBaseAddress) != 0);
+
+  // Allocate Resources
+  LanDriver = AllocateZeroPool (sizeof(LAN91X_DRIVER));
+  Lan91xPath = AllocateCopyPool (sizeof(LAN91X_DEVICE_PATH), &Lan91xPathTemplate);
+
+  // Initialize I/O Space access info
+  LanDriver->IoBase = PcdGet32 (PcdLan91xDxeBaseAddress);
+  LanDriver->PhyAd = LAN91X_NO_PHY;
+  LanDriver->BankSel = 0xff;
+
+  // Initialize pointers
+  Snp = &(LanDriver->Snp);
+  SnpMode = &(LanDriver->SnpMode);
+  Snp->Mode = SnpMode;
+
+  // Set the signature of the LAN Driver structure
+  LanDriver->Signature = LAN91X_SIGNATURE;
+
+  // Probe the device
+  Status = Probe (LanDriver);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((DEBUG_ERROR, "LAN91x:Lan91xDxeEntry(): Probe failed with status %d\n", Status));
+    return Status;
+  }
+
+#ifdef LAN91X_PRINT_REGISTERS
+  PrintIoRegisters (LanDriver);
+  PrintPhyRegisters (LanDriver);
+#endif
+
+  // Initialize transmit queue
+  InitializeListHead (&LanDriver->TransmitQueueHead);
+
+  // Assign fields and func pointers
+  Snp->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+  Snp->WaitForPacket = NULL;
+  Snp->Initialize = SnpInitialize;
+  Snp->Start = SnpStart;
+  Snp->Stop = SnpStop;
+  Snp->Reset = SnpReset;
+  Snp->Shutdown = SnpShutdown;
+  Snp->ReceiveFilters = SnpReceiveFilters;
+  Snp->StationAddress = SnpStationAddress;
+  Snp->Statistics = SnpStatistics;
+  Snp->MCastIpToMac = SnpMcastIptoMac;
+  Snp->NvData = SnpNvData;
+  Snp->GetStatus = SnpGetStatus;
+  Snp->Transmit = SnpTransmit;
+  Snp->Receive = SnpReceive;
+
+  // Fill in simple network mode structure
+  SnpMode->State = EfiSimpleNetworkStopped;
+  SnpMode->HwAddressSize = NET_ETHER_ADDR_LEN;    // HW address is 6 bytes
+  SnpMode->MediaHeaderSize = sizeof(ETHER_HEAD);  // Size of an Ethernet header
+  SnpMode->MaxPacketSize = EFI_PAGE_SIZE;         // Ethernet Frame (with VLAN tag +4 bytes)
+
+  // Supported receive filters
+  SnpMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+                               EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
+                               EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
+                               EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS |
+                               EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+
+  // Initially-enabled receive filters
+  SnpMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+                                  EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
+                                  EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+
+  // LAN91x has 64bit hash table. We can filter an infinite MACs, but
+  // higher-level software must filter out any hash collisions.
+  SnpMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
+  SnpMode->MCastFilterCount = 0;
+  ZeroMem (&SnpMode->MCastFilter, MAX_MCAST_FILTER_CNT * sizeof(EFI_MAC_ADDRESS));
+
+  // Set the interface type (1: Ethernet or 6: IEEE 802 Networks)
+  SnpMode->IfType = NET_IFTYPE_ETHERNET;
+
+  // Mac address is changeable
+  SnpMode->MacAddressChangeable = TRUE;
+
+  // We can only transmit one packet at a time
+  SnpMode->MultipleTxSupported = FALSE;
+
+  // MediaPresent checks for cable connection and partner link
+  SnpMode->MediaPresentSupported = TRUE;
+  SnpMode->MediaPresent = FALSE;
+
+  //  Set broadcast address
+  SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);
+
+  // Assign fields for device path
+  Lan91xPath->Lan91x.MacAddress = SnpMode->PermanentAddress;
+  Lan91xPath->Lan91x.IfType = SnpMode->IfType;
+
+  // Initialise the protocol
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &LanDriver->ControllerHandle,
+                  &gEfiSimpleNetworkProtocolGuid, Snp,
+                  &gEfiDevicePathProtocolGuid, Lan91xPath,
+                  NULL
+                  );
+
+  // Say what the status of loading the protocol structure is
+  if (EFI_ERROR(Status)) {
+    FreePool (LanDriver);
+  }
+
+  return Status;
+}
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf b/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
new file mode 100644
index 000000000000..11a9f74c50f4
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
@@ -0,0 +1,59 @@
+#/** @file
+#  INF file for the SMSC LAN91x series Network Controller Driver.
+#
+#  WARNING:
+#  This driver fails to follow the UEFI driver model without a good
+#  reason, and only remains in the tree because it is still used by
+#  a small number of platforms. It will be removed when no longer used.
+#  New platforms should not use it, and no one should use this as
+#  reference code for developing new drivers.
+#
+#  Copyright (c) 2013-2017 Linaro.org
+#
+#  Derived from the LAN9118 driver. Original sources
+#  Copyright (c) 2012-2013, ARM Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010019
+  BASE_NAME                      = Lan91xDxe
+  FILE_GUID                      = 5c12ea2f-9897-48af-8138-25f4ce6ff8d6
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 0.1
+  ENTRY_POINT                    = Lan91xDxeEntry
+
+[Sources.common]
+  Lan91xDxe.c
+  Lan91xDxeHw.h
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  NetworkPkg/NetworkPkg.dec
+  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+
+[LibraryClasses]
+  ArmLib
+  BaseLib
+  BaseMemoryLib
+  DevicePathLib
+  IoLib
+  NetLib
+  TimerLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiSimpleNetworkProtocolGuid
+  gEfiMetronomeArchProtocolGuid
+  gEfiPxeBaseCodeProtocolGuid
+  gEfiDevicePathProtocolGuid
+
+[FixedPcd]
+  gArmVExpressTokenSpaceGuid.PcdLan91xDxeBaseAddress
+
+[Depex]
+  TRUE
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h b/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h
new file mode 100644
index 000000000000..23d9713e4d2b
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h
@@ -0,0 +1,279 @@
+/** @file
+*  SMSC LAN91x series Network Controller Driver.
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2013-2017 Linaro.org
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#ifndef __LAN91XDXEHW_H__
+#define __LAN91XDXEHW_H__
+
+#include <Base.h>
+
+#define MakeRegister(Bank, Offset)  (((Bank) << 8) | (Offset))
+#define RegisterToBank(Register)    (((Register) >> 8) & 0x07)
+#define RegisterToOffset(Register)  ((Register) & 0x0f)
+
+/*---------------------------------------------------------------------------------------------------------------------
+
+        SMSC LAN91x Registers
+
+---------------------------------------------------------------------------------------------------------------------*/
+#define LAN91X_BANK_OFFSET      0xe                     // Bank Select Register (all banks)
+
+#define LAN91X_TCR      MakeRegister (0, 0x0)           // Transmit Control Register
+#define LAN91X_EPHSR    MakeRegister (0, 0x2)           // EPH Status Register
+#define LAN91X_RCR      MakeRegister (0, 0x4)           // Receive Control Register
+#define LAN91X_ECR      MakeRegister (0, 0x6)           // Counter Register
+#define LAN91X_MIR      MakeRegister (0, 0x8)           // Memory Information Register
+#define LAN91X_RPCR     MakeRegister (0, 0xa)           // Receive/Phy Control Register
+
+#define LAN91X_CR       MakeRegister (1, 0x0)           // Configuration Register
+#define LAN91X_BAR      MakeRegister (1, 0x2)           // Base Address Register
+#define LAN91X_IAR0     MakeRegister (1, 0x4)           // Individual Address Register 0
+#define LAN91X_IAR1     MakeRegister (1, 0x5)           // Individual Address Register 1
+#define LAN91X_IAR2     MakeRegister (1, 0x6)           // Individual Address Register 2
+#define LAN91X_IAR3     MakeRegister (1, 0x7)           // Individual Address Register 3
+#define LAN91X_IAR4     MakeRegister (1, 0x8)           // Individual Address Register 4
+#define LAN91X_IAR5     MakeRegister (1, 0x9)           // Individual Address Register 5
+#define LAN91X_GPR      MakeRegister (1, 0xa)           // General Purpose Register
+#define LAN91X_CTR      MakeRegister (1, 0xc)           // Control Register
+
+#define LAN91X_MMUCR    MakeRegister (2, 0x0)           // MMU Command Register
+#define LAN91X_PNR      MakeRegister (2, 0x2)           // Packet Number Register
+#define LAN91X_ARR      MakeRegister (2, 0x3)           // Allocation Result Register
+#define LAN91X_FIFO     MakeRegister (2, 0x4)           // FIFO Ports Register
+#define LAN91X_PTR      MakeRegister (2, 0x6)           // Pointer Register
+#define LAN91X_DATA0    MakeRegister (2, 0x8)           // Data Register 0
+#define LAN91X_DATA1    MakeRegister (2, 0x9)           // Data Register 1
+#define LAN91X_DATA2    MakeRegister (2, 0xa)           // Data Register 2
+#define LAN91X_DATA3    MakeRegister (2, 0xb)           // Data Register 3
+#define LAN91X_IST      MakeRegister (2, 0xc)           // Interrupt Status Register
+#define LAN91X_MSK      MakeRegister (2, 0xd)           // Interrupt Mask Register
+
+#define LAN91X_MT0      MakeRegister (3, 0x0)           // Multicast Table Register 0
+#define LAN91X_MT1      MakeRegister (3, 0x1)           // Multicast Table Register 1
+#define LAN91X_MT2      MakeRegister (3, 0x2)           // Multicast Table Register 2
+#define LAN91X_MT3      MakeRegister (3, 0x3)           // Multicast Table Register 3
+#define LAN91X_MT4      MakeRegister (3, 0x4)           // Multicast Table Register 4
+#define LAN91X_MT5      MakeRegister (3, 0x5)           // Multicast Table Register 5
+#define LAN91X_MT6      MakeRegister (3, 0x6)           // Multicast Table Register 6
+#define LAN91X_MT7      MakeRegister (3, 0x7)           // Multicast Table Register 7
+#define LAN91X_MGMT     MakeRegister (3, 0x8)           // Management Interface Register
+#define LAN91X_REV      MakeRegister (3, 0xa)           // Revision Register
+#define LAN91X_RCV      MakeRegister (3, 0xc)           // RCV Register
+
+// Transmit Control Register Bits
+#define TCR_TXENA       BIT0
+#define TCR_LOOP        BIT1
+#define TCR_FORCOL      BIT2
+#define TCR_PAD_EN      BIT7
+#define TCR_NOCRC       BIT8
+#define TCR_MON_CSN     BIT10
+#define TCR_FDUPLX      BIT11
+#define TCR_STP_SQET    BIT12
+#define TCR_EPH_LOOP    BIT13
+#define TCR_SWFDUP      BIT15
+
+#define TCR_DEFAULT     (TCR_TXENA | TCR_PAD_EN)
+#define TCR_CLEAR       0x0
+
+// EPH Status Register Bits
+#define EPHSR_TX_SUC    BIT0
+#define EPHSR_SNGLCOL   BIT1
+#define EPHSR_MULCOL    BIT2
+#define EPHSR_LTX_MULT  BIT3
+#define EPHSR_16COL     BIT4
+#define EPHSR_SQET      BIT5
+#define EPHSR_LTX_BRD   BIT6
+#define EPHSR_TX_DEFR   BIT7
+#define EPHSR_LATCOL    BIT9
+#define EPHSR_LOST_CARR BIT10
+#define EPHSR_EXC_DEF   BIT11
+#define EPHSR_CTR_ROL   BIT12
+#define EPHSR_LINK_OK   BIT14
+
+// Receive Control Register Bits
+#define RCR_RX_ABORT    BIT0
+#define RCR_PRMS        BIT1
+#define RCR_ALMUL       BIT2
+#define RCR_RXEN        BIT8
+#define RCR_STRIP_CRC   BIT9
+#define RCR_ABORT_ENB   BIT13
+#define RCR_FILT_CAR    BIT14
+#define RCR_SOFT_RST    BIT15
+
+#define RCR_DEFAULT     (RCR_STRIP_CRC | RCR_RXEN)
+#define RCR_CLEAR       0x0
+
+// Receive/Phy Control Register Bits
+#define RPCR_LS0B       BIT2
+#define RPCR_LS1B       BIT3
+#define RPCR_LS2B       BIT4
+#define RPCR_LS0A       BIT5
+#define RPCR_LS1A       BIT6
+#define RPCR_LS2A       BIT7
+#define RPCR_ANEG       BIT11
+#define RPCR_DPLX       BIT12
+#define RPCR_SPEED      BIT13
+
+// Configuration Register Bits
+#define CR_EXT_PHY      BIT9
+#define CR_GPCNTRL      BIT10
+#define CR_NO_WAIT      BIT12
+#define CR_EPH_POWER_EN BIT15
+
+#define CR_DEFAULT      (CR_EPH_POWER_EN | CR_NO_WAIT)
+
+// Control Register Bits
+#define CTR_STORE       BIT0
+#define CTR_RELOAD      BIT1
+#define CTR_EEPROM_SEL  BIT2
+#define CTR_TE_ENABLE   BIT5
+#define CTR_CR_ENABLE   BIT6
+#define CTR_LE_ENABLE   BIT7
+#define CTR_AUTO_REL    BIT11
+#define CTR_RCV_BAD     BIT14
+
+#define CTR_RESERVED    (BIT12 | BIT9 | BIT4)
+#define CTR_DEFAULT     (CTR_RESERVED | CTR_AUTO_REL)
+
+// MMU Command Register Bits
+#define MMUCR_BUSY      BIT0
+
+// MMU Command Register Operaction Codes
+#define MMUCR_OP_NOOP           (0 << 5)        // No operation
+#define MMUCR_OP_TX_ALLOC       (1 << 5)        // Allocate memory for TX
+#define MMUCR_OP_RESET_MMU      (2 << 5)        // Reset MMU to initial state
+#define MMUCR_OP_RX_POP         (3 << 5)        // Remove frame from top of RX FIFO
+#define MMUCR_OP_RX_POP_REL     (4 << 5)        // Remove and release frame from top of RX FIFO
+#define MMUCR_OP_RX_REL         (5 << 5)        // Release specific RX frame
+#define MMUCR_OP_TX_PUSH        (6 << 5)        // Enqueue packet number into TX FIFO
+#define MMUCR_OP_TX_RESET       (7 << 5)        // Reset TX FIFOs
+
+// Packet Number Register Bits
+#define PNR_PACKET      (0x3f)
+
+// Allocation Result Register Bits
+#define ARR_PACKET      (0x3f)
+#define ARR_FAILED      BIT7
+
+// FIFO Ports Register Bits
+#define FIFO_TX_PACKET  (0x003f)
+#define FIFO_TEMPTY     BIT7
+#define FIFO_RX_PACKET  (0x3f00)
+#define FIFO_REMPTY     BIT15
+
+// Pointer Register Bits
+#define PTR_POINTER     (0x07ff)
+#define PTR_NOT_EMPTY   BIT11
+#define PTR_READ        BIT13
+#define PTR_AUTO_INCR   BIT14
+#define PTR_RCV         BIT15
+
+// Interrupt Status and Mask Register Bits
+#define IST_RCV         BIT0
+#define IST_TX          BIT1
+#define IST_TX_EMPTY    BIT2
+#define IST_ALLOC       BIT3
+#define IST_RX_OVRN     BIT4
+#define IST_EPH         BIT5
+#define IST_MD          BIT7
+
+// Management Interface
+#define MGMT_MDO        BIT0
+#define MGMT_MDI        BIT1
+#define MGMT_MCLK       BIT2
+#define MGMT_MDOE       BIT3
+#define MGMT_MSK_CRS100 BIT14
+
+// RCV Register
+#define RCV_MBO         (0x1f)
+#define RCV_RCV_DISCRD  BIT7
+
+// Packet RX Status word bits
+#define RX_MULTICAST    BIT0
+#define RX_HASH         (0x7e)
+#define RX_TOO_SHORT    BIT10
+#define RX_TOO_LONG     BIT11
+#define RX_ODD_FRAME    BIT12
+#define RX_BAD_CRC      BIT13
+#define RX_BROADCAST    BIT14
+#define RX_ALGN_ERR     BIT15
+
+// Packet Byte Count word bits
+#define BCW_COUNT       (0x7fe)
+
+// Packet Control Word bits
+#define PCW_ODD_BYTE    (0x00ff)
+#define PCW_CRC         BIT12
+#define PCW_ODD         BIT13
+
+/*---------------------------------------------------------------------------------------------------------------------
+
+        SMSC PHY Registers
+
+        Most of these should be common, as there is
+        documented STANDARD for PHY registers!
+
+---------------------------------------------------------------------------------------------------------------------*/
+//
+// PHY Register Numbers
+//
+#define PHY_INDEX_BASIC_CTRL              0
+#define PHY_INDEX_BASIC_STATUS            1
+#define PHY_INDEX_ID1                     2
+#define PHY_INDEX_ID2                     3
+#define PHY_INDEX_AUTO_NEG_ADVERT         4
+#define PHY_INDEX_AUTO_NEG_LINK_ABILITY   5
+
+#define PHY_INDEX_CONFIG1                 16
+#define PHY_INDEX_CONFIG2                 17
+#define PHY_INDEX_STATUS_OUTPUT           18
+#define PHY_INDEX_MASK                    19
+
+
+// PHY control register bits
+#define PHYCR_COLL_TEST                       BIT7                  // Collision test enable
+#define PHYCR_DUPLEX_MODE                     BIT8                  // Set Duplex Mode
+#define PHYCR_RST_AUTO                        BIT9                  // Restart Auto-Negotiation of Link abilities
+#define PHYCR_PD                              BIT11                 // Power-Down switch
+#define PHYCR_AUTO_EN                         BIT12                 // Auto-Negotiation Enable
+#define PHYCR_SPEED_SEL                       BIT13                 // Link Speed Selection
+#define PHYCR_LOOPBK                          BIT14                 // Set loopback mode
+#define PHYCR_RESET                           BIT15                 // Do a PHY reset
+
+// PHY status register bits
+#define PHYSTS_EXT_CAP                        BIT0                  // Extended Capabilities Register capability
+#define PHYSTS_JABBER                         BIT1                  // Jabber condition detected
+#define PHYSTS_LINK_STS                       BIT2                  // Link Status
+#define PHYSTS_AUTO_CAP                       BIT3                  // Auto-Negotiation Capability
+#define PHYSTS_REMOTE_FAULT                   BIT4                  // Remote fault detected
+#define PHYSTS_AUTO_COMP                      BIT5                  // Auto-Negotiation Completed
+#define PHYSTS_10BASET_HDPLX                  BIT11                 // 10Mbps Half-Duplex ability
+#define PHYSTS_10BASET_FDPLX                  BIT12                 // 10Mbps Full-Duplex ability
+#define PHYSTS_100BASETX_HDPLX                BIT13                 // 100Mbps Half-Duplex ability
+#define PHYSTS_100BASETX_FDPLX                BIT14                 // 100Mbps Full-Duplex ability
+#define PHYSTS_100BASE_T4                     BIT15                 // Base T4 ability
+
+// PHY Auto-Negotiation advertisement
+#define PHYANA_SEL_MASK                       ((UINT32)0x1F)        // Link type selector
+#define PHYANA_CSMA                           BIT0                  // Advertise CSMA capability
+#define PHYANA_10BASET                        BIT5                  // Advertise 10BASET capability
+#define PHYANA_10BASETFD                      BIT6                  // Advertise 10BASET Full duplex capability
+#define PHYANA_100BASETX                      BIT7                  // Advertise 100BASETX capability
+#define PHYANA_100BASETXFD                    BIT8                  // Advertise 100 BASETX Full duplex capability
+#define PHYANA_100BASET4                      BIT9                  // Advertise 100 BASETX Full duplex capability
+#define PHYANA_PAUSE_OP_MASK                  (3 << 10)             // Advertise PAUSE frame capability
+#define PHYANA_REMOTE_FAULT                   BIT13                 // Remote fault detected
+
+#endif /* __LAN91XDXEHW_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
                   ` (5 preceding siblings ...)
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-05-04 11:42   ` [edk2-devel] " Philippe Mathieu-Daudé
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver Ard Biesheuvel
  2020-05-04 11:00 ` [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Leif Lindholm
  8 siblings, 1 reply; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

Incorporate the Lan91x driver from EmbeddedPkg, which is only used
on obsolete ARM development platforms and does not follow the UEFI
driver model. This will allow us to drop if from the core EDK2
repository.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Platform/ARM/JunoPkg/ArmJuno.dsc                             |    8 +-
 Platform/ARM/JunoPkg/ArmJuno.fdf                             |    2 +-
 Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc            |    6 +-
 Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf            |    2 +-
 Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                  |    8 +
 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c     | 1539 ++++++++++++++++++++
 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h     |  304 ++++
 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf   |   58 +
 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h   |  401 +++++
 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c | 1039 +++++++++++++
 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h |  283 ++++
 11 files changed, 3641 insertions(+), 9 deletions(-)

diff --git a/Platform/ARM/JunoPkg/ArmJuno.dsc b/Platform/ARM/JunoPkg/ArmJuno.dsc
index 1c39da4897ed..a6ea957b3832 100644
--- a/Platform/ARM/JunoPkg/ArmJuno.dsc
+++ b/Platform/ARM/JunoPkg/ArmJuno.dsc
@@ -150,9 +150,9 @@ [PcdsFixedAtBuild.common]
 !endif
 
   # LAN9118 Ethernet Driver
-  gEmbeddedTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x18000000
-  gEmbeddedTokenSpaceGuid.PcdLan9118DefaultMacAddress|0x1215161822242628
-  gEmbeddedTokenSpaceGuid.PcdLan9118DefaultNegotiationTimeout|40000
+  gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x18000000
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultMacAddress|0x1215161822242628
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultNegotiationTimeout|40000
 
   #
   # ARM Generic Interrupt Controller
@@ -324,7 +324,7 @@ [Components.common]
   #
   # Networking stack
   #
-  EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
+  Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
 !if 0
   OptionRomPkg/MarvellYukonDxe/MarvellYukonDxe.inf
 !endif
diff --git a/Platform/ARM/JunoPkg/ArmJuno.fdf b/Platform/ARM/JunoPkg/ArmJuno.fdf
index d771cbf35790..5d791a4e1607 100644
--- a/Platform/ARM/JunoPkg/ArmJuno.fdf
+++ b/Platform/ARM/JunoPkg/ArmJuno.fdf
@@ -196,7 +196,7 @@ [FV.FvMain]
   #
 !include Platform/ARM/VExpressPkg/ArmVExpress-networking.fdf.inc
 
-  INF EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
 !if 0
   INF OptionRomPkg/MarvellYukonDxe/MarvellYukonDxe.inf
 !endif
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
index 6dad31026aa5..bee7913feb52 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
@@ -160,8 +160,8 @@ [PcdsFixedAtBuild.common]
   gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
 
   # Ethernet (SMSC LAN9118)
-  gEmbeddedTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x1A000000
-  gEmbeddedTokenSpaceGuid.PcdLan9118DefaultNegotiationTimeout|400000
+  gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x1A000000
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultNegotiationTimeout|400000
 
   #
   # Define the device path to the FDT for the platform
@@ -252,7 +252,7 @@ [Components.common]
   Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
 
   # SMSC LAN 9118
-  EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
+  Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
 
   #
   # FAT filesystem + GPT/MBR partitioning
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
index 64da1102ff07..b133375e1a11 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
@@ -143,7 +143,7 @@ [FV.FvMain]
   #
 !include Platform/ARM/VExpressPkg/ArmVExpress-networking.fdf.inc
 
-  INF EmbeddedPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
 
   #
   # UEFI application
diff --git a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
index ea000a0e5387..e42905aabf2b 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+++ b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
@@ -59,3 +59,11 @@ [PcdsFixedAtBuild.common]
 
   # LAN91x Ethernet Driver PCDs
   gArmVExpressTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x0|UINT32|0x0000000B
+
+  # LAN9118 Ethernet Driver PCDs
+  gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x0|UINT32|0x00000025
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultMacAddress|0x0|UINT64|0x00000026
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultNegotiationTimeout|4000|UINT32|0x00000027
+  # The default feature mask below disables full duplex negotiation, since full
+  # duplex operation is suspected to be broken in the driver.
+  gArmVExpressTokenSpaceGuid.PcdLan9118NegotiationFeatureMask|0xFFFFFEBF|UINT32|0x00000028
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c
new file mode 100644
index 000000000000..b90c32346fab
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c
@@ -0,0 +1,1539 @@
+/** @file
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include "Lan9118Dxe.h"
+
+typedef struct {
+  MAC_ADDR_DEVICE_PATH      Lan9118;
+  EFI_DEVICE_PATH_PROTOCOL  End;
+} LAN9118_DEVICE_PATH;
+
+LAN9118_DEVICE_PATH Lan9118PathTemplate =  {
+  {
+    {
+      MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP,
+      { (UINT8) (sizeof(MAC_ADDR_DEVICE_PATH)), (UINT8) ((sizeof(MAC_ADDR_DEVICE_PATH)) >> 8) }
+    },
+    { { 0 } },
+    0
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    { sizeof(EFI_DEVICE_PATH_PROTOCOL), 0 }
+  }
+};
+
+/*
+**  Entry point for the LAN9118 driver
+**
+*/
+EFI_STATUS
+EFIAPI
+Lan9118DxeEntry (
+  IN EFI_HANDLE Handle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS                   Status;
+  LAN9118_DRIVER              *LanDriver;
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+  EFI_SIMPLE_NETWORK_MODE     *SnpMode;
+  LAN9118_DEVICE_PATH         *Lan9118Path;
+  EFI_HANDLE                   ControllerHandle;
+
+  // The PcdLan9118DxeBaseAddress PCD must be defined
+  ASSERT (PcdGet32 (PcdLan9118DxeBaseAddress) != 0);
+
+  // Allocate Resources
+  LanDriver = AllocateZeroPool (sizeof (LAN9118_DRIVER));
+  if (LanDriver == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  Lan9118Path = (LAN9118_DEVICE_PATH*)AllocateCopyPool (sizeof (LAN9118_DEVICE_PATH), &Lan9118PathTemplate);
+  if (Lan9118Path == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  // Initialize pointers
+  Snp = &(LanDriver->Snp);
+  SnpMode = &(LanDriver->SnpMode);
+  Snp->Mode = SnpMode;
+
+  // Set the signature of the LAN Driver structure
+  LanDriver->Signature = LAN9118_SIGNATURE;
+
+  // Assign fields and func pointers
+  Snp->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+  Snp->WaitForPacket = NULL;
+  Snp->Initialize = SnpInitialize;
+  Snp->Start = SnpStart;
+  Snp->Stop = SnpStop;
+  Snp->Reset = SnpReset;
+  Snp->Shutdown = SnpShutdown;
+  Snp->ReceiveFilters = SnpReceiveFilters;
+  Snp->StationAddress = SnpStationAddress;
+  Snp->Statistics = SnpStatistics;
+  Snp->MCastIpToMac = SnpMcastIptoMac;
+  Snp->NvData = SnpNvData;
+  Snp->GetStatus = SnpGetStatus;
+  Snp->Transmit = SnpTransmit;
+  Snp->Receive = SnpReceive;
+
+  // Start completing simple network mode structure
+  SnpMode->State = EfiSimpleNetworkStopped;
+  SnpMode->HwAddressSize = NET_ETHER_ADDR_LEN; // HW address is 6 bytes
+  SnpMode->MediaHeaderSize = sizeof(ETHER_HEAD); // Not sure of this
+  SnpMode->MaxPacketSize = EFI_PAGE_SIZE; // Preamble + SOF + Ether Frame (with VLAN tag +4bytes)
+  SnpMode->NvRamSize = 0;           // No NVRAM with this device
+  SnpMode->NvRamAccessSize = 0; // No NVRAM with this device
+
+  //
+  // Claim that all receive filter settings are supported, though the MULTICAST mode
+  // is not completely supported. The LAN9118 Ethernet controller is only able to
+  // do a "hash filtering" and not a perfect filtering on multicast addresses. The
+  // controller does not filter the multicast addresses directly but a hash value
+  // of them. The hash value of a multicast address is derived from its CRC and
+  // ranges from 0 to 63 included.
+  // We claim that the perfect MULTICAST filtering mode is supported because
+  // we do not want the user to switch directly to the PROMISCOUS_MULTICAST mode
+  // and thus not being able to take advantage of the hash filtering.
+  //
+  SnpMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST              |
+                               EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST            |
+                               EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST            |
+                               EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS          |
+                               EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+
+  // We do not intend to receive anything for the time being.
+  SnpMode->ReceiveFilterSetting = 0;
+
+  // LAN9118 has 64bit hash table, can filter 64 MCast MAC Addresses
+  SnpMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
+  SnpMode->MCastFilterCount = 0;
+  ZeroMem (&SnpMode->MCastFilter, MAX_MCAST_FILTER_CNT * sizeof(EFI_MAC_ADDRESS));
+
+  // Set the interface type (1: Ethernet or 6: IEEE 802 Networks)
+  SnpMode->IfType = NET_IFTYPE_ETHERNET;
+
+  // Mac address is changeable as it is loaded from erasable memory
+  SnpMode->MacAddressChangeable = TRUE;
+
+  // Can only transmit one packet at a time
+  SnpMode->MultipleTxSupported = FALSE;
+
+  // MediaPresent checks for cable connection and partner link
+  SnpMode->MediaPresentSupported = TRUE;
+  SnpMode->MediaPresent = FALSE;
+
+  // Set broadcast address
+  SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);
+
+  // Power up the device so we can find the MAC address
+  Status = Lan9118Initialize (Snp);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "LAN9118: Error initialising hardware\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Assign fields for device path
+  CopyMem (&Lan9118Path->Lan9118.MacAddress, &Snp->Mode->CurrentAddress, NET_ETHER_ADDR_LEN);
+  Lan9118Path->Lan9118.IfType = Snp->Mode->IfType;
+
+  // Initialise the protocol
+  ControllerHandle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &ControllerHandle,
+                  &gEfiSimpleNetworkProtocolGuid, Snp,
+                  &gEfiDevicePathProtocolGuid, Lan9118Path,
+                  NULL
+                  );
+  // Say what the status of loading the protocol structure is
+  if (EFI_ERROR(Status)) {
+    FreePool (LanDriver);
+  } else {
+    LanDriver->ControllerHandle = ControllerHandle;
+  }
+
+  return Status;
+}
+
+/*
+ *  UEFI Start() function
+ *
+ *  Parameters:
+ *
+ *  @param Snp:  A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ *
+ *  Description:
+ *
+ *    This function starts a network interface. If the network interface successfully starts, then
+ *    EFI_SUCCESS will be returned.
+ */
+EFI_STATUS
+EFIAPI
+SnpStart (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp
+ )
+{
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check state
+  if ((Snp->Mode->State == EfiSimpleNetworkStarted)    ||
+      (Snp->Mode->State == EfiSimpleNetworkInitialized)  ) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  // Change state
+  Snp->Mode->State = EfiSimpleNetworkStarted;
+  return EFI_SUCCESS;
+}
+
+/*
+ *  UEFI Stop() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpStop (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  )
+{
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check state of the driver
+  if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    return EFI_NOT_STARTED;
+  }
+
+  // Stop the Tx and Rx
+  StopTx (STOP_TX_CFG | STOP_TX_MAC, Snp);
+  StopRx (0, Snp);
+
+  // Change the state
+  switch (Snp->Mode->State) {
+    case EfiSimpleNetworkStarted:
+    case EfiSimpleNetworkInitialized:
+      Snp->Mode->State = EfiSimpleNetworkStopped;
+      break;
+    default:
+      return EFI_DEVICE_ERROR;
+  }
+
+  // Put the device into a power saving mode ?
+  return EFI_SUCCESS;
+}
+
+
+// Allocated receive and transmit buffers
+STATIC UINT32 gTxBuffer = 0;
+
+/*
+ *  UEFI Initialize() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpInitialize (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        UINTN                        RxBufferSize    OPTIONAL,
+  IN        UINTN                        TxBufferSize    OPTIONAL
+  )
+{
+  EFI_STATUS Status;
+  UINT32     PmConf;
+  INT32      AllocResult;
+  UINT32     RxStatusSize;
+  UINT32     TxStatusSize;
+
+  // Initialize variables
+  // Global variables to hold tx and rx FIFO allocation
+  gTxBuffer = 0;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // First check that driver has not already been initialized
+  if (Snp->Mode->State == EfiSimpleNetworkInitialized) {
+    DEBUG ((EFI_D_WARN, "LAN9118 Driver already initialized\n"));
+    return EFI_SUCCESS;
+  } else
+  if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "LAN9118 Driver not started\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Initiate a PHY reset
+  Status = PhySoftReset (PHY_RESET_PMT, Snp);
+  if (EFI_ERROR (Status)) {
+    Snp->Mode->State = EfiSimpleNetworkStopped;
+    DEBUG ((EFI_D_WARN, "Warning: Link not ready after TimeOut. Check ethernet cable\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Initiate a software reset
+  Status = SoftReset (0, Snp);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((EFI_D_WARN, "Soft Reset Failed: Hardware Error\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Read the PM register
+  PmConf = Lan9118MmioRead32 (LAN9118_PMT_CTRL);
+
+  // MPTCTRL_WOL_EN: Allow Wake-On-Lan to detect wake up frames or magic packets
+  // MPTCTRL_ED_EN:  Allow energy detection to allow lowest power consumption mode
+  // MPTCTRL_PME_EN: Allow Power Management Events
+  PmConf = 0;
+  PmConf |= (MPTCTRL_WOL_EN | MPTCTRL_ED_EN | MPTCTRL_PME_EN);
+
+  // Write the current configuration to the register
+  Lan9118MmioWrite32 (LAN9118_PMT_CTRL, PmConf);
+
+  // Configure GPIO and HW
+  Status = ConfigureHardware (HW_CONF_USE_LEDS, Snp);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  // Assign the transmitter buffer size (default values)
+  TxStatusSize = LAN9118_TX_STATUS_SIZE;
+  RxStatusSize = LAN9118_RX_STATUS_SIZE;
+
+  // Check that a buff size was specified
+  if (TxBufferSize > 0) {
+    if (RxBufferSize == 0) {
+      RxBufferSize = LAN9118_RX_DATA_SIZE;
+    }
+
+    AllocResult = ChangeFifoAllocation (
+                          ALLOC_USE_FIFOS,
+                          &TxBufferSize,
+                          &RxBufferSize,
+                          &TxStatusSize,
+                          &RxStatusSize,
+                          Snp
+                          );
+
+    if (AllocResult < 0) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  // Do auto-negotiation if supported
+  Status = AutoNegotiate (AUTO_NEGOTIATE_ADVERTISE_ALL, Snp);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((EFI_D_WARN, "LAN9118: Auto Negotiation failed.\n"));
+  }
+
+  // Configure flow control depending on speed capabilities
+  Status = ConfigureFlow (0, 0, 0, 0, Snp);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  // Enable the transmitter
+  Status = StartTx (START_TX_MAC | START_TX_CFG, Snp);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  // Now acknowledge all interrupts
+  Lan9118MmioWrite32 (LAN9118_INT_STS, ~0);
+
+  // Declare the driver as initialized
+  Snp->Mode->State = EfiSimpleNetworkInitialized;
+
+  return Status;
+}
+
+/*
+ *  UEFI Reset () function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpReset (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN Verification
+  )
+{
+  UINT32     PmConf;
+  UINT32     HwConf;
+  UINT32     ResetFlags;
+  EFI_STATUS Status;
+
+  PmConf = 0;
+  HwConf = 0;
+  ResetFlags = 0;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // First check that driver has not already been initialized
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not yet initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not started\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Initiate a PHY reset
+  Status = PhySoftReset (PHY_RESET_PMT, Snp);
+  if (EFI_ERROR (Status)) {
+    Snp->Mode->State = EfiSimpleNetworkStopped;
+    return EFI_NOT_STARTED;
+  }
+
+  // Initiate a software reset
+  ResetFlags |= SOFT_RESET_CHECK_MAC_ADDR_LOAD | SOFT_RESET_CLEAR_INT;
+
+  if (Verification) {
+    ResetFlags |= SOFT_RESET_SELF_TEST;
+  }
+
+  Status = SoftReset (ResetFlags, Snp);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_WARN, "Warning: Soft Reset Failed: Hardware Error\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Read the PM register
+  PmConf = Lan9118MmioRead32 (LAN9118_PMT_CTRL);
+
+  // MPTCTRL_WOL_EN: Allow Wake-On-Lan to detect wake up frames or magic packets
+  // MPTCTRL_ED_EN:  Allow energy detection to allow lowest power consumption mode
+  // MPTCTRL_PME_EN: Allow Power Management Events
+  PmConf |= (MPTCTRL_WOL_EN | MPTCTRL_ED_EN | MPTCTRL_PME_EN);
+
+  // Write the current configuration to the register
+  Lan9118MmioWrite32 (LAN9118_PMT_CTRL, PmConf);
+
+  // Reactivate the LEDs
+  Status = ConfigureHardware (HW_CONF_USE_LEDS, Snp);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Check that a buffer size was specified in SnpInitialize
+  if (gTxBuffer != 0) {
+    HwConf = Lan9118MmioRead32 (LAN9118_HW_CFG);        // Read the HW register
+    HwConf &= ~HW_CFG_TX_FIFO_SIZE_MASK;         // Clear buffer bits first
+    HwConf |= HW_CFG_TX_FIFO_SIZE(gTxBuffer);    // assign size chosen in SnpInitialize
+
+    Lan9118MmioWrite32 (LAN9118_HW_CFG, HwConf);        // Write the conf
+  }
+
+  // Enable the receiver and transmitter and clear their contents
+  StartRx (START_RX_CLEAR, Snp);
+  StartTx (START_TX_MAC | START_TX_CFG | START_TX_CLEAR, Snp);
+
+  // Now acknowledge all interrupts
+  Lan9118MmioWrite32 (LAN9118_INT_STS, ~0);
+
+  return EFI_SUCCESS;
+}
+
+/*
+ *  UEFI Shutdown () function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpShutdown (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  )
+{
+  EFI_STATUS Status;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // First check that driver has not already been initialized
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not yet initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not started\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Initiate a PHY reset
+  Status = PhySoftReset (PHY_RESET_PMT, Snp);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Initiate a software reset
+  Status = SoftReset (0, Snp);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_WARN, "Warning: Soft Reset Failed: Hardware Error\n"));
+    return Status;
+  }
+
+  // Back to the started and thus not initialized state
+  Snp->Mode->State = EfiSimpleNetworkStarted;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Enable and/or disable the receive filters of the LAN9118
+
+  Please refer to the UEFI specification for the precedence rules among the
+  Enable, Disable and ResetMCastFilter parameters.
+
+  @param[in]  Snp               A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL
+                                instance.
+  @param[in]  Enable            A bit mask of receive filters to enable.
+  @param[in]  Disable           A bit mask of receive filters to disable.
+  @param[in]  ResetMCastFilter  Set to TRUE to reset the contents of the multicast
+                                receive filters on the network interface to
+                                their default values.
+  @param[in]  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[in]  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 receive filters of the LAN9118 were updated.
+  @retval  EFI_NOT_STARTED        The LAN9118 has not been started.
+  @retval  EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE :
+                                  . This is NULL
+                                  . Multicast is being enabled (the
+                                    EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit is set in
+                                    Enable, it is not set in Disable, and ResetMCastFilter
+                                    is FALSE) and MCastFilterCount is zero.
+                                  . Multicast is being enabled and MCastFilterCount is
+                                    greater than Snp->Mode->MaxMCastFilterCount.
+                                  . Multicast is being enabled and MCastFilter is NULL
+                                  . Multicast is being enabled and one or more of the
+                                    addresses in the MCastFilter list are not valid
+                                    multicast MAC addresses.
+  @retval  EFI_DEVICE_ERROR       The LAN9118 has been started but not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpReceiveFilters (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp,
+  IN  UINT32                       Enable,
+  IN  UINT32                       Disable,
+  IN  BOOLEAN                      ResetMCastFilter,
+  IN  UINTN                        MCastFilterCnt  OPTIONAL,
+  IN  EFI_MAC_ADDRESS              *MCastFilter  OPTIONAL
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE  *Mode;
+  UINT32                   MultHashTableHigh;
+  UINT32                   MultHashTableLow;
+  UINT32                   Count;
+  UINT32                   Crc;
+  UINT8                    HashValue;
+  UINT32                   MacCSRValue;
+  UINT32                   ReceiveFilterSetting;
+  EFI_MAC_ADDRESS          *Mac;
+  EFI_MAC_ADDRESS          ZeroMac;
+
+  // Check Snp Instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  Mode = Snp->Mode;
+
+  // Check that driver was started and initialised
+  if (Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  if ((Enable  & (~Mode->ReceiveFilterMask)) ||
+      (Disable & (~Mode->ReceiveFilterMask))    ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check the validity of the multicast setting and compute the
+  // hash values of the multicast mac addresses to listen to.
+  //
+
+  MultHashTableHigh = 0;
+  MultHashTableLow  = 0;
+  if ((!ResetMCastFilter)                                     &&
+      ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) == 0) &&
+      ((Enable  & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0)    ) {
+    if ((MCastFilterCnt == 0)                        ||
+        (MCastFilterCnt > Mode->MaxMCastFilterCount) ||
+        (MCastFilter == NULL)                           ) {
+      return EFI_INVALID_PARAMETER;
+    }
+    //
+    // Check the validity of all multicast addresses before to change
+    // anything.
+    //
+    for (Count = 0; Count < MCastFilterCnt; Count++) {
+      if ((MCastFilter[Count].Addr[0] & 1) == 0) {
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+
+    //
+    // Go through each filter address and set appropriate bits on hash table
+    //
+    for (Count = 0; Count < MCastFilterCnt; Count++) {
+      Mac = &(MCastFilter[Count]);
+      CopyMem (&Mode->MCastFilter[Count], Mac, sizeof(EFI_MAC_ADDRESS));
+
+      Crc = GenEtherCrc32 (Mac, NET_ETHER_ADDR_LEN);
+      //gBS->CalculateCrc32 ((VOID*)&Mfilter[Count],6,&Crc); <-- doesn't work as desired
+
+      //
+      // The most significant 6 bits of the MAC address CRC constitute the hash
+      // value of the MAC address.
+      //
+      HashValue = (Crc >> 26) & 0x3F;
+
+      // Select hashlow register if MSB is not set
+      if ((HashValue & 0x20) == 0) {
+        MultHashTableLow |= (1 << HashValue);
+      } else {
+        MultHashTableHigh |= (1 << (HashValue & 0x1F));
+      }
+    }
+    Mode->MCastFilterCount = MCastFilterCnt;
+  } else if (ResetMCastFilter) {
+    Mode->MCastFilterCount = 0;
+  } else {
+    MultHashTableLow  = IndirectMACRead32 (INDIRECT_MAC_INDEX_HASHL);
+    MultHashTableHigh = IndirectMACRead32 (INDIRECT_MAC_INDEX_HASHH);
+  }
+
+  //
+  // Before to change anything, stop and reset the reception of
+  // packets.
+  //
+  StopRx (STOP_RX_CLEAR, Snp);
+
+  //
+  // Write the mask of the selected hash values for the multicast filtering.
+  // The two masks are set to zero if the multicast filtering is not enabled.
+  //
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_HASHL, MultHashTableLow);
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_HASHH, MultHashTableHigh);
+
+  ReceiveFilterSetting = (Mode->ReceiveFilterSetting | Enable) & (~Disable);
+
+  //
+  // Read MAC controller
+  //
+  MacCSRValue  = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
+  MacCSRValue &= ~(MACCR_HPFILT | MACCR_BCAST | MACCR_PRMS | MACCR_MCPAS);
+
+  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) {
+    Lan9118SetMacAddress (&Mode->CurrentAddress, Snp);
+    DEBUG ((DEBUG_NET, "Allowing Unicast Frame Reception\n"));
+  } else {
+    //
+    // The Unicast packets do not have to be listen to, set the MAC
+    // address of the LAN9118 to be the "not configured" all zeroes
+    // ethernet MAC address.
+    //
+    ZeroMem (&ZeroMac, NET_ETHER_ADDR_LEN);
+    Lan9118SetMacAddress (&ZeroMac, Snp);
+  }
+
+  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) {
+    MacCSRValue |= MACCR_HPFILT;
+    DEBUG ((DEBUG_NET, "Allowing Multicast Frame Reception\n"));
+  }
+
+  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) {
+    MacCSRValue |= MACCR_MCPAS;
+    DEBUG ((DEBUG_NET, "Enabling Promiscuous Multicast Mode\n"));
+  }
+
+  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) == 0) {
+    MacCSRValue |= MACCR_BCAST;
+  } else {
+    DEBUG ((DEBUG_NET, "Allowing Broadcast Frame Reception\n"));
+  }
+
+  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) {
+    MacCSRValue |= MACCR_PRMS;
+    DEBUG ((DEBUG_NET, "Enabling Promiscuous Mode\n"));
+  }
+
+  //
+  // Write the options to the MAC_CSR
+  //
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCSRValue);
+
+  //
+  // If we have to retrieve something, start packet reception.
+  //
+  Mode->ReceiveFilterSetting = ReceiveFilterSetting;
+  if (ReceiveFilterSetting != 0) {
+    StartRx (0, Snp);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Modify of reset the current station address
+
+  @param[in]  Snp               A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL
+                                instance.
+  @param[in]  Reset             Flag used to reset the station address to the
+                                LAN9118's permanent address.
+  @param[in]  New               New station address to be used for the network interface.
+
+  @retval  EFI_SUCCESS            The LAN9118's station address was updated.
+  @retval  EFI_NOT_STARTED        The LAN9118 has not been started.
+  @retval  EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE :
+                                  . The "New" station address is invalid.
+                                  . "Reset" is FALSE and "New" is NULL.
+  @retval  EFI_DEVICE_ERROR       The LAN9118 has been started but not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpStationAddress (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp,
+  IN  BOOLEAN                      Reset,
+  IN  EFI_MAC_ADDRESS              *New
+)
+{
+  UINT32 Count;
+  UINT8  PermAddr[NET_ETHER_ADDR_LEN];
+
+  DEBUG ((DEBUG_NET, "SnpStationAddress()\n"));
+
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that driver was started and initialised
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Get the Permanent MAC address if need reset
+  if (Reset) {
+    // Try using EEPROM first. Read the first byte of data from EEPROM at the address 0x0
+    if ((IndirectEEPROMRead32 (0) & 0xFF) == EEPROM_EXTERNAL_SERIAL_EEPROM) {
+      for (Count = 0; Count < NET_ETHER_ADDR_LEN; Count++) {
+        PermAddr[Count] = IndirectEEPROMRead32 (Count + 1);
+      }
+      New = (EFI_MAC_ADDRESS *) PermAddr;
+      Lan9118SetMacAddress ((EFI_MAC_ADDRESS *) PermAddr, Snp);
+    } else {
+      DEBUG ((EFI_D_ERROR, "LAN9118: Warning: No valid MAC address in EEPROM, using fallback\n"));
+      New = (EFI_MAC_ADDRESS*) (FixedPcdGet64 (PcdLan9118DefaultMacAddress));
+    }
+  } else {
+    // Otherwise use the specified new MAC address
+    if (New == NULL) {
+      return EFI_INVALID_PARAMETER;
+    }
+    //
+    // If it is a multicast address, it is not valid.
+    //
+    if (New->Addr[0] & 0x01) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  CopyMem (&Snp->Mode->CurrentAddress, New, NET_ETHER_ADDR_LEN);
+
+  //
+  // If packet reception is currently activated, stop and reset it,
+  // set the new ethernet address and restart the packet reception.
+  // Otherwise, nothing to do, the MAC address will be updated in
+  // SnpReceiveFilters() when the UNICAST packet reception will be
+  // activated.
+  //
+  if (Snp->Mode->ReceiveFilterSetting  != 0) {
+    StopRx (STOP_RX_CLEAR, Snp);
+    Lan9118SetMacAddress (New, Snp);
+    StartRx (0, Snp);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/*
+ *  UEFI Statistics() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpStatistics (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN Reset,
+  IN  OUT   UINTN *StatSize,
+      OUT   EFI_NETWORK_STATISTICS *Statistics
+  )
+{
+  LAN9118_DRIVER  *LanDriver;
+  EFI_STATUS      Status;
+
+  LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
+
+  DEBUG ((DEBUG_NET, "SnpStatistics()\n"));
+
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that driver was started and initialised
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  //
+  // Do a reset if required. It is not clearly stated in the UEFI specification
+  // whether the reset has to be done before to copy the statistics in "Statictics"
+  // or after. It is a bit strange to do it before but that is what is expected by
+  // the SCT test on Statistics() with reset : "0x3de76704,0x4bf5,0x42cd,0x8c,0x89,
+  // 0x54,0x7e,0x4f,0xad,0x4f,0x24".
+  //
+  if (Reset) {
+    ZeroMem (&LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS));
+  }
+
+  Status = EFI_SUCCESS;
+  if (StatSize == NULL) {
+    if (Statistics != NULL) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else {
+    if (Statistics == NULL) {
+      Status = EFI_BUFFER_TOO_SMALL;
+    } else {
+      // Fill in the statistics
+      CopyMem (
+        Statistics, &LanDriver->Stats,
+        MIN (*StatSize, sizeof (EFI_NETWORK_STATISTICS))
+        );
+      if (*StatSize < sizeof (EFI_NETWORK_STATISTICS)) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+    }
+    *StatSize = sizeof (EFI_NETWORK_STATISTICS);
+  }
+
+  return Status;
+}
+
+/*
+ *  UEFI MCastIPtoMAC() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpMcastIptoMac (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN IsIpv6,
+  IN        EFI_IP_ADDRESS *Ip,
+      OUT   EFI_MAC_ADDRESS *McastMac
+  )
+{
+  DEBUG ((DEBUG_NET, "SnpMcastIptoMac()\n"));
+
+  // Check Snp instance
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that driver was started and initialised
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Check parameters
+  if ((McastMac == NULL) || (Ip == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Make sure MAC address is empty
+  ZeroMem (McastMac, sizeof(EFI_MAC_ADDRESS));
+
+  // If we need ipv4 address
+  if (!IsIpv6) {
+    // Most significant 25 bits of a multicast HW address are set.
+    // 01-00-5E is the IPv4 Ethernet Multicast Address (see RFC 1112)
+    McastMac->Addr[0] = 0x01;
+    McastMac->Addr[1] = 0x00;
+    McastMac->Addr[2] = 0x5E;
+
+    // Lower 23 bits from ipv4 address
+    McastMac->Addr[3] = (Ip->v4.Addr[1] & 0x7F); // Clear the most significant bit (25th bit of MAC must be 0)
+    McastMac->Addr[4] = Ip->v4.Addr[2];
+    McastMac->Addr[5] = Ip->v4.Addr[3];
+  } else {
+    // Most significant 16 bits of multicast v6 HW address are set
+    // 33-33 is the IPv6 Ethernet Multicast Address (see RFC 2464)
+    McastMac->Addr[0] = 0x33;
+    McastMac->Addr[1] = 0x33;
+
+    // lower four octets are taken from ipv6 address
+    McastMac->Addr[2] = Ip->v6.Addr[8];
+    McastMac->Addr[3] = Ip->v6.Addr[9];
+    McastMac->Addr[4] = Ip->v6.Addr[10];
+    McastMac->Addr[5] = Ip->v6.Addr[11];
+  }
+
+  return EFI_SUCCESS;
+}
+
+/*
+ *  UEFI NvData() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpNvData (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* pobj,
+  IN        BOOLEAN read_write,
+  IN        UINTN offset,
+  IN        UINTN buff_size,
+  IN  OUT   VOID *data
+  )
+{
+  DEBUG ((DEBUG_NET, "SnpNvData()\n"));
+
+  return EFI_UNSUPPORTED;
+}
+
+
+/*
+ *  UEFI GetStatus () function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpGetStatus (
+  IN   EFI_SIMPLE_NETWORK_PROTOCOL  *Snp,
+  OUT  UINT32                       *IrqStat  OPTIONAL,
+  OUT  VOID                         **TxBuff  OPTIONAL
+  )
+{
+  UINT32          FifoInt;
+  EFI_STATUS      Status;
+  UINTN           NumTxStatusEntries;
+  UINT32          TxStatus;
+  UINT16          PacketTag;
+  UINT32          Interrupts;
+  LAN9118_DRIVER *LanDriver;
+
+  LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
+
+  // Check preliminaries
+  if (Snp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that driver was started and initialised
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Check and acknowledge TX Status interrupt (this will happen if the
+  // consumer of SNP does not call GetStatus.)
+  // TODO will we lose TxStatuses if this happens? Maybe in SnpTransmit we
+  // should check for it and dump the TX Status FIFO.
+  FifoInt = Lan9118MmioRead32 (LAN9118_FIFO_INT);
+
+  // Clear the TX Status FIFO Overflow
+  if ((FifoInt & INSTS_TXSO) == 0) {
+    FifoInt |= INSTS_TXSO;
+    Lan9118MmioWrite32 (LAN9118_FIFO_INT, FifoInt);
+  }
+
+  // Read interrupt status if IrqStat is not NULL
+  if (IrqStat != NULL) {
+    *IrqStat = 0;
+
+    // Check for receive interrupt
+    if (Lan9118MmioRead32 (LAN9118_INT_STS) & INSTS_RSFL) { // Data moved from rx FIFO
+      *IrqStat |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+      Lan9118MmioWrite32 (LAN9118_INT_STS,INSTS_RSFL);
+    }
+
+    // Check for transmit interrupt
+    if (Lan9118MmioRead32 (LAN9118_INT_STS) & INSTS_TSFL) {
+      *IrqStat |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+      Lan9118MmioWrite32 (LAN9118_INT_STS,INSTS_TSFL);
+    }
+
+    // Check for software interrupt
+    if (Lan9118MmioRead32 (LAN9118_INT_STS) & INSTS_SW_INT) {
+      *IrqStat |= EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT;
+      Lan9118MmioWrite32 (LAN9118_INT_STS,INSTS_SW_INT);
+    }
+  }
+
+  // Check Status of transmitted packets
+  // (We ignore TXSTATUS_NO_CA has it might happen in Full Duplex)
+
+  NumTxStatusEntries = Lan9118MmioRead32(LAN9118_TX_FIFO_INF) & TXFIFOINF_TXSUSED_MASK;
+  if (NumTxStatusEntries > 0) {
+    TxStatus = Lan9118MmioRead32 (LAN9118_TX_STATUS);
+    PacketTag = TxStatus >> 16;
+    TxStatus = TxStatus & 0xFFFF;
+    if ((TxStatus & TXSTATUS_ES) && (TxStatus != (TXSTATUS_ES | TXSTATUS_NO_CA))) {
+      DEBUG ((EFI_D_ERROR, "LAN9118: There was an error transmitting. TxStatus=0x%08x:", TxStatus));
+      if (TxStatus & TXSTATUS_NO_CA) {
+        DEBUG ((EFI_D_ERROR, "- No carrier\n"));
+      }
+      if (TxStatus & TXSTATUS_DEF) {
+        DEBUG ((EFI_D_ERROR, "- Packet tx was deferred\n"));
+      }
+      if (TxStatus & TXSTATUS_EDEF) {
+        DEBUG ((EFI_D_ERROR, "- Tx ended because of excessive deferral\n"));
+      }
+      if (TxStatus & TXSTATUS_ECOLL) {
+        DEBUG ((EFI_D_ERROR, "- Tx ended because of Excessive Collisions\n"));
+      }
+      if (TxStatus & TXSTATUS_LCOLL) {
+        DEBUG ((EFI_D_ERROR, "- Packet Tx aborted after coll window of 64 bytes\n"));
+      }
+      if (TxStatus & TXSTATUS_LOST_CA) {
+        DEBUG ((EFI_D_ERROR, "- Lost carrier during Tx\n"));
+      }
+      return EFI_DEVICE_ERROR;
+    } else if (TxBuff != NULL) {
+      LanDriver->Stats.TxTotalFrames += 1;
+      *TxBuff = LanDriver->TxRing[PacketTag % LAN9118_TX_RING_NUM_ENTRIES];
+    }
+  } else if (TxBuff != NULL) {
+    *TxBuff = NULL;
+  }
+
+  // Check for a TX Error interrupt
+  Interrupts = Lan9118MmioRead32 (LAN9118_INT_STS);
+  if (Interrupts & INSTS_TXE) {
+    DEBUG ((EFI_D_ERROR, "LAN9118: Transmitter error. Restarting..."));
+
+    // Software reset, the TXE interrupt is cleared by the reset.
+    Status = SoftReset (0, Snp);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "\n\tSoft Reset Failed: Hardware Error\n"));
+      return EFI_DEVICE_ERROR;
+    }
+
+    // Reactivate the LEDs
+    Status = ConfigureHardware (HW_CONF_USE_LEDS, Snp);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // Restart the transmitter and if necessary the receiver.
+    // Do not ask for FIFO reset as it has already been done
+    // by SoftReset().
+    //
+    StartTx (START_TX_MAC | START_TX_CFG, Snp);
+    if (Snp->Mode->ReceiveFilterSetting != 0) {
+      StartRx (0, Snp);
+    }
+  }
+
+  // Update the media status
+  Status = CheckLinkStatus (0, Snp);
+  if (EFI_ERROR(Status)) {
+    Snp->Mode->MediaPresent = FALSE;
+  } else {
+    Snp->Mode->MediaPresent = TRUE;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/*
+ *  UEFI Transmit() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpTransmit (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp,
+  IN  UINTN                        HdrSize,
+  IN  UINTN                        BuffSize,
+  IN  VOID*                        Data,
+  IN  EFI_MAC_ADDRESS              *SrcAddr  OPTIONAL,
+  IN  EFI_MAC_ADDRESS              *DstAddr  OPTIONAL,
+  IN  UINT16                       *Protocol OPTIONAL
+  )
+{
+  LAN9118_DRIVER *LanDriver;
+  UINT32 TxFreeSpace;
+  UINT32 TxStatusSpace;
+  INT32 Count;
+  UINT32 CommandA;
+  UINT32 CommandB;
+  UINT16 LocalProtocol;
+  UINT32 *LocalData;
+  UINT16 PacketTag;
+
+#if defined(EVAL_PERFORMANCE)
+  UINT64 Perf;
+  UINT64 StartClock;
+  UINT64 EndClock;
+
+  Perf = GetPerformanceCounterProperties (NULL, NULL);
+  StartClock = GetPerformanceCounter ();
+#endif
+
+  LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
+
+  // Check preliminaries
+  if ((Snp == NULL) || (Data == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that driver was started and initialised
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  // Ensure header is correct size if non-zero
+  if (HdrSize) {
+    if (HdrSize != Snp->Mode->MediaHeaderSize) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if ((DstAddr == NULL) || (Protocol == NULL)) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Check validity of BufferSize
+  //
+  if (BuffSize < Snp->Mode->MediaHeaderSize) {
+      return EFI_BUFFER_TOO_SMALL;
+  }
+
+  // Before transmitting check the link status
+  /*if (CheckLinkStatus (0, Snp) < 0) {
+    return EFI_NOT_READY;
+  }*/
+
+  // Get DATA FIFO free space in bytes
+  TxFreeSpace = TxDataFreeSpace (0, Snp);
+  if (TxFreeSpace < BuffSize) {
+    return EFI_NOT_READY;
+  }
+
+  // Get STATUS FIFO used space in bytes
+  TxStatusSpace = TxStatusUsedSpace (0, Snp);
+  if (TxStatusSpace > 500) {
+    return EFI_NOT_READY;
+  }
+
+  // If DstAddr is not provided, get it from Buffer (we trust that the caller
+  // has provided a well-formed frame).
+  if (DstAddr == NULL) {
+    DstAddr = (EFI_MAC_ADDRESS *) Data;
+  }
+
+  // Check for the nature of the frame
+  if ((DstAddr->Addr[0] & 0x1) == 1) {
+    LanDriver->Stats.TxMulticastFrames += 1;
+  } else {
+    LanDriver->Stats.TxUnicastFrames += 1;
+  }
+
+  // Check if broadcast
+  if (DstAddr->Addr[0] == 0xFF) {
+    LanDriver->Stats.TxBroadcastFrames += 1;
+  }
+
+  PacketTag = LanDriver->NextPacketTag;
+  LanDriver->NextPacketTag++;
+
+  if (HdrSize) {
+
+    // Format pointer
+    LocalData = (UINT32*) Data;
+    LocalProtocol = *Protocol;
+
+    // Create first buffer to pass to controller (for the header)
+    CommandA = TX_CMD_A_FIRST_SEGMENT | TX_CMD_A_BUFF_SIZE (HdrSize);
+    CommandB = TX_CMD_B_PACKET_TAG (PacketTag) | TX_CMD_B_PACKET_LENGTH (BuffSize);
+
+    // Write the commands first
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, CommandA);
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, CommandB);
+
+    // Write the destination address
+    Lan9118MmioWrite32 (LAN9118_TX_DATA,
+               (DstAddr->Addr[0]) |
+               (DstAddr->Addr[1] << 8) |
+               (DstAddr->Addr[2] << 16) |
+               (DstAddr->Addr[3] << 24)
+               );
+
+    Lan9118MmioWrite32 (LAN9118_TX_DATA,
+               (DstAddr->Addr[4]) |
+               (DstAddr->Addr[5] << 8) |
+               (SrcAddr->Addr[0] << 16) | // Write the Source Address
+               (SrcAddr->Addr[1] << 24)
+               );
+
+    Lan9118MmioWrite32 (LAN9118_TX_DATA,
+               (SrcAddr->Addr[2]) |
+               (SrcAddr->Addr[3] << 8) |
+               (SrcAddr->Addr[4] << 16) |
+               (SrcAddr->Addr[5] << 24)
+               );
+
+    // Write the Protocol
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, (UINT32)(HTONS (LocalProtocol)));
+
+    // Next buffer is the payload
+    CommandA = TX_CMD_A_LAST_SEGMENT | TX_CMD_A_BUFF_SIZE (BuffSize - HdrSize) | TX_CMD_A_COMPLETION_INT | TX_CMD_A_DATA_START_OFFSET (2); // 2 bytes beginning offset
+
+    // Write the commands
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, CommandA);
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, CommandB);
+
+    // Write the payload
+    for (Count = 0; Count < ((BuffSize + 3) >> 2) - 3; Count++) {
+      Lan9118MmioWrite32 (LAN9118_TX_DATA, LocalData[Count + 3]);
+    }
+  } else {
+    // Format pointer
+    LocalData = (UINT32*) Data;
+
+    // Create a buffer to pass to controller
+    CommandA = TX_CMD_A_FIRST_SEGMENT | TX_CMD_A_LAST_SEGMENT | TX_CMD_A_BUFF_SIZE (BuffSize) | TX_CMD_A_COMPLETION_INT;
+    CommandB = TX_CMD_B_PACKET_TAG (PacketTag) | TX_CMD_B_PACKET_LENGTH (BuffSize);
+
+    // Write the commands first
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, CommandA);
+    Lan9118MmioWrite32 (LAN9118_TX_DATA, CommandB);
+
+    // Write all the data
+    for (Count = 0; Count < ((BuffSize + 3) >> 2); Count++) {
+      Lan9118MmioWrite32 (LAN9118_TX_DATA, LocalData[Count]);
+    }
+  }
+
+  // Save the address of the submitted packet so we can notify the consumer that
+  // it has been sent in GetStatus. When the packet tag appears in the Tx Status
+  // Fifo, we will return Buffer in the TxBuff parameter of GetStatus.
+  LanDriver->TxRing[PacketTag % LAN9118_TX_RING_NUM_ENTRIES] = Data;
+
+#if defined(EVAL_PERFORMANCE)
+  EndClock = GetPerformanceCounter ();
+  DEBUG ((EFI_D_ERROR, "Time processing: %d counts @ %d Hz\n", StartClock - EndClock,Perf));
+#endif
+
+  LanDriver->Stats.TxGoodFrames += 1;
+
+  return EFI_SUCCESS;
+}
+
+
+/*
+ *  UEFI Receive() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpReceive (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+      OUT   UINTN *HdrSize                OPTIONAL,
+  IN  OUT   UINTN *BuffSize,
+      OUT   VOID *Data,
+      OUT   EFI_MAC_ADDRESS *SrcAddr      OPTIONAL,
+      OUT   EFI_MAC_ADDRESS *DstAddr      OPTIONAL,
+      OUT   UINT16 *Protocol              OPTIONAL
+  )
+{
+  LAN9118_DRIVER  *LanDriver;
+  UINT32          IntSts;
+  UINT32          RxFifoStatus;
+  UINT32          NumPackets;
+  UINT32          RxCfgValue;
+  UINT32          PLength; // Packet length
+  UINT32          ReadLimit;
+  UINT32          Count;
+  UINT32          Padding;
+  UINT32          *RawData;
+  EFI_MAC_ADDRESS Dst;
+  EFI_MAC_ADDRESS Src;
+  UINTN           DroppedFrames;
+  EFI_STATUS      Status;
+
+  LanDriver = INSTANCE_FROM_SNP_THIS (Snp);
+
+#if defined(EVAL_PERFORMANCE)
+  UINT64 Perf = GetPerformanceCounterProperties (NULL, NULL);
+  UINT64 StartClock = GetPerformanceCounter ();
+#endif
+
+  // Check preliminaries
+  if ((Snp == NULL) || (Data == NULL) || (BuffSize == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that driver was started and initialised
+  if (Snp->Mode->State == EfiSimpleNetworkStarted) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver not initialized\n"));
+    return EFI_DEVICE_ERROR;
+  } else if (Snp->Mode->State == EfiSimpleNetworkStopped) {
+    DEBUG ((EFI_D_WARN, "Warning: LAN9118 Driver in stopped state\n"));
+    return EFI_NOT_STARTED;
+  }
+
+  //
+  // If the receiver raised the RXE error bit, check if the receiver status
+  // FIFO is full and if not just acknowledge the error. The two other
+  // conditions to get a RXE error are :
+  // . the RX data FIFO is read whereas being empty.
+  // . the RX status FIFO is read whereas being empty.
+  // The RX data and status FIFO are read by this driver only in the following
+  // code of this function. After the readings, the RXE error bit is checked
+  // and if raised, the controller is reset. Thus, at this point, we consider
+  // that the only valid reason to get an RXE error is the receiver status
+  // FIFO being full. And if this is not the case, we consider that this is
+  // a spurious error and we just get rid of it. We experienced such 'spurious'
+  // errors when running the driver on an A57 on Juno. No valid reason to
+  // explain those errors has been found so far and everything seems to
+  // work perfectly when they are just ignored.
+  //
+  IntSts = Lan9118MmioRead32 (LAN9118_INT_STS);
+  if ((IntSts & INSTS_RXE) && (!(IntSts & INSTS_RSFF))) {
+    Lan9118MmioWrite32 (LAN9118_INT_STS, INSTS_RXE);
+  }
+
+  // Count dropped frames
+  DroppedFrames = Lan9118MmioRead32 (LAN9118_RX_DROP);
+  LanDriver->Stats.RxDroppedFrames += DroppedFrames;
+
+  NumPackets = RxStatusUsedSpace (0, Snp) / 4;
+  if (!NumPackets) {
+    return EFI_NOT_READY;
+  }
+
+  // Read Rx Status (only if not empty)
+  RxFifoStatus = Lan9118MmioRead32 (LAN9118_RX_STATUS);
+  LanDriver->Stats.RxTotalFrames += 1;
+
+  // First check for errors
+  if ((RxFifoStatus & RXSTATUS_MII_ERROR) ||
+      (RxFifoStatus & RXSTATUS_RXW_TO) ||
+      (RxFifoStatus & RXSTATUS_FTL) ||
+      (RxFifoStatus & RXSTATUS_LCOLL) ||
+      (RxFifoStatus & RXSTATUS_LE) ||
+      (RxFifoStatus & RXSTATUS_DB))
+  {
+    DEBUG ((EFI_D_WARN, "Warning: There was an error on frame reception.\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Check if we got a CRC error
+  if (RxFifoStatus & RXSTATUS_CRC_ERROR) {
+    DEBUG ((EFI_D_WARN, "Warning: Crc Error\n"));
+    LanDriver->Stats.RxCrcErrorFrames += 1;
+    LanDriver->Stats.RxDroppedFrames += 1;
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Check if we got a runt frame
+  if (RxFifoStatus & RXSTATUS_RUNT) {
+    DEBUG ((EFI_D_WARN, "Warning: Runt Frame\n"));
+    LanDriver->Stats.RxUndersizeFrames += 1;
+    LanDriver->Stats.RxDroppedFrames += 1;
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Check filtering status for this packet
+  if (RxFifoStatus & RXSTATUS_FILT_FAIL) {
+    DEBUG ((EFI_D_WARN, "Warning: Frame Failed Filtering\n"));
+    // fast forward?
+  }
+
+  // Check if we got a broadcast frame
+  if (RxFifoStatus & RXSTATUS_BCF) {
+    LanDriver->Stats.RxBroadcastFrames += 1;
+  }
+
+  // Check if we got a multicast frame
+  if (RxFifoStatus & RXSTATUS_MCF) {
+    LanDriver->Stats.RxMulticastFrames += 1;
+  }
+
+  // Check if we got a unicast frame
+  if ((RxFifoStatus & RXSTATUS_BCF) && ((RxFifoStatus & RXSTATUS_MCF) == 0)) {
+    LanDriver->Stats.RxUnicastFrames += 1;
+  }
+
+  // Get the received packet length
+  PLength = GET_RXSTATUS_PACKET_LENGTH(RxFifoStatus);
+  LanDriver->Stats.RxTotalBytes += (PLength - 4);
+
+  // If padding is applied, read more DWORDs
+  if (PLength % 4) {
+    Padding = 4 - (PLength % 4);
+    ReadLimit = (PLength + Padding)/4;
+  } else {
+    ReadLimit = PLength/4;
+    Padding = 0;
+  }
+
+  // Check buffer size
+  if (*BuffSize < (PLength + Padding)) {
+    *BuffSize = PLength + Padding;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  // Set the amount of data to be transferred out of FIFO for THIS packet
+  // This can be used to trigger an interrupt, and status can be checked
+  RxCfgValue = Lan9118MmioRead32 (LAN9118_RX_CFG);
+  RxCfgValue &= ~(RXCFG_RX_DMA_CNT_MASK);
+  RxCfgValue |= RXCFG_RX_DMA_CNT (ReadLimit);
+
+  // Set end alignment to 4-bytes
+  RxCfgValue &= ~(RXCFG_RX_END_ALIGN_MASK);
+  Lan9118MmioWrite32 (LAN9118_RX_CFG, RxCfgValue);
+
+  // Update buffer size
+  *BuffSize = PLength; // -4 bytes may be needed: Received in buffer as
+                       // 4 bytes longer than packet actually is, unless
+                       // packet is < 64 bytes
+
+  if (HdrSize != NULL)
+    *HdrSize = Snp->Mode->MediaHeaderSize;
+
+  // Format the pointer
+  RawData = (UINT32*)Data;
+
+  // Read Rx Packet
+  for (Count = 0; Count < ReadLimit; Count++) {
+    RawData[Count] = Lan9118MmioRead32 (LAN9118_RX_DATA);
+  }
+
+  // Get the destination address
+  if (DstAddr != NULL) {
+    Dst.Addr[0] = (RawData[0] & 0xFF);
+    Dst.Addr[1] = (RawData[0] & 0xFF00) >> 8;
+    Dst.Addr[2] = (RawData[0] & 0xFF0000) >> 16;
+    Dst.Addr[3] = (RawData[0] & 0xFF000000) >> 24;
+    Dst.Addr[4] = (RawData[1] & 0xFF);
+    Dst.Addr[5] = (RawData[1] & 0xFF00) >> 8;
+    CopyMem (DstAddr, &Dst, NET_ETHER_ADDR_LEN);
+  }
+
+  // Get the source address
+  if (SrcAddr != NULL) {
+    Src.Addr[0] = (RawData[1] & 0xFF0000) >> 16;
+    Src.Addr[1] = (RawData[1] & 0xFF000000) >> 24;
+    Src.Addr[2] = (RawData[2] & 0xFF);
+    Src.Addr[3] = (RawData[2] & 0xFF00) >> 8;
+    Src.Addr[4] = (RawData[2] & 0xFF0000) >> 16;
+    Src.Addr[5] = (RawData[2] & 0xFF000000) >> 24;
+    CopyMem (SrcAddr, &Src, NET_ETHER_ADDR_LEN);
+  }
+
+  // Get the protocol
+  if (Protocol != NULL) {
+    *Protocol = NTOHS (RawData[3] & 0xFFFF);
+  }
+
+  // Check for Rx errors (worst possible error)
+  if (Lan9118MmioRead32 (LAN9118_INT_STS) & INSTS_RXE) {
+    DEBUG ((EFI_D_WARN, "Warning: Receiver Error. Restarting...\n"));
+
+    // Software reset, the RXE interrupt is cleared by the reset.
+    Status = SoftReset (0, Snp);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "Error: Soft Reset Failed: Hardware Error.\n"));
+      return EFI_DEVICE_ERROR;
+    }
+
+    // Reactivate the LEDs
+    Status = ConfigureHardware (HW_CONF_USE_LEDS, Snp);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // Restart the receiver and the transmitter without resetting the FIFOs
+    // as it has been done by SoftReset().
+    //
+    StartRx (0, Snp);
+    StartTx (START_TX_MAC | START_TX_CFG, Snp);
+
+    // Say that command could not be sent
+    return EFI_DEVICE_ERROR;
+  }
+
+#if defined(EVAL_PERFORMANCE)
+  UINT64 EndClock = GetPerformanceCounter ();
+  DEBUG ((EFI_D_ERROR, "Receive Time processing: %d counts @ %d Hz\n", StartClock - EndClock,Perf));
+#endif
+
+  LanDriver->Stats.RxGoodFrames += 1;
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h
new file mode 100644
index 000000000000..edc106a1df1b
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h
@@ -0,0 +1,304 @@
+/** @file
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#ifndef __LAN9118_DXE_H__
+#define __LAN9118_DXE_H__
+
+#include <Uefi.h>
+#include <Uefi/UefiSpec.h>
+#include <Base.h>
+
+// Protocols used by this driver
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/DevicePath.h>
+
+// Libraries used by this driver
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/NetLib.h>
+#include <Library/DevicePathLib.h>
+
+#include "Lan9118DxeUtil.h"
+#include "Lan9118DxeHw.h"
+
+#define LAN9118_STALL     2
+
+#define LAN9118_DEFAULT_MAC_ADDRL     0x00F70200
+#define LAN9118_DEFAULT_MAC_ADDRH     0x00009040
+
+#define LAN9118_TX_DATA_SIZE          4608
+#define LAN9118_TX_STATUS_SIZE        512
+#define LAN9118_RX_DATA_SIZE          10560
+#define LAN9118_RX_STATUS_SIZE        704
+
+#define LAN9118_TX_RING_NUM_ENTRIES 32
+
+/*------------------------------------------------------------------------------
+  LAN9118 Information Structure
+------------------------------------------------------------------------------*/
+
+typedef struct {
+  // Driver signature
+  UINT32            Signature;
+  EFI_HANDLE        ControllerHandle;
+
+  // EFI SNP protocol instances
+  EFI_SIMPLE_NETWORK_PROTOCOL Snp;
+  EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+  // EFI Snp statistics instance
+  EFI_NETWORK_STATISTICS Stats;
+
+  // Saved transmitted buffers so we can notify consumers when packets have been sent.
+  UINT16  NextPacketTag;
+  VOID    *TxRing[LAN9118_TX_RING_NUM_ENTRIES];
+} LAN9118_DRIVER;
+
+#define LAN9118_SIGNATURE                       SIGNATURE_32('l', 'a', 'n', '9')
+#define INSTANCE_FROM_SNP_THIS(a)               CR(a, LAN9118_DRIVER, Snp, LAN9118_SIGNATURE)
+
+
+/*---------------------------------------------------------------------------------------------------------------------
+
+  UEFI-Compliant functions for EFI_SIMPLE_NETWORK_PROTOCOL
+
+  Refer to the Simple Network Protocol section (21.1) in the UEFI 2.3.1 Specification for related definitions
+
+---------------------------------------------------------------------------------------------------------------------*/
+
+
+/*
+ *  UEFI Start() function
+ *
+ *  Parameters:
+ *
+ *  @param pobj:  A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ *
+ *  Description:
+ *
+ *  This function starts a network interface. If the network interface successfully starts, then
+ *  EFI_SUCCESS will be returned.
+ */
+EFI_STATUS
+EFIAPI
+SnpStart (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  );
+
+/*
+ *  UEFI Stop() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpStop (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  );
+
+/*
+ *  UEFI Initialize() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpInitialize (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        UINTN rx_buff_size,
+  IN        UINTN tx_buff_size
+  );
+
+/*
+ *  UEFI Reset() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpReset (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN ext_ver
+  );
+
+/*
+ *  UEFI Shutdown() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpShutdown (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp
+  );
+
+/*
+ *  UEFI ReceiveFilters() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpReceiveFilters (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        UINT32 enable,
+  IN        UINT32 disable,
+  IN        BOOLEAN reset_mfilter,
+  IN        UINTN num_mfilter,
+  IN        EFI_MAC_ADDRESS *mfilter
+  );
+
+/*
+ *  UEFI StationAddress() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpStationAddress (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN reset,
+  IN        EFI_MAC_ADDRESS *new_maddr
+  );
+
+/*
+ *  UEFI Statistics() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpStatistics (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN reset,
+  IN  OUT   UINTN *stat_size,
+      OUT   EFI_NETWORK_STATISTICS *stat_table
+  );
+
+/*
+ *  UEFI MCastIPtoMAC() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpMcastIptoMac (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN use_ipv6,
+  IN        EFI_IP_ADDRESS *ip_addr,
+      OUT   EFI_MAC_ADDRESS *mac_addr
+  );
+
+/*
+ *  UEFI NvData() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpNvData (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        BOOLEAN read_write,
+  IN        UINTN offset,
+  IN        UINTN buff_size,
+  IN  OUT   VOID *data
+  );
+
+/*
+ *  UEFI GetStatus() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpGetStatus (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+      OUT   UINT32 *irq_stat  OPTIONAL,
+      OUT   VOID **tx_buff    OPTIONAL
+  );
+
+/*
+ *  UEFI Transmit() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpTransmit (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+  IN        UINTN hdr_size,
+  IN        UINTN buff_size,
+  IN        VOID *data,
+  IN        EFI_MAC_ADDRESS *src_addr   OPTIONAL,
+  IN        EFI_MAC_ADDRESS *dest_addr  OPTIONAL,
+  IN        UINT16 *protocol            OPTIONAL
+  );
+
+/*
+ *  UEFI Receive() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpReceive (
+  IN        EFI_SIMPLE_NETWORK_PROTOCOL* Snp,
+      OUT   UINTN *hdr_size               OPTIONAL,
+  IN  OUT   UINTN *buff_size,
+      OUT   VOID *data,
+      OUT   EFI_MAC_ADDRESS *src_addr     OPTIONAL,
+      OUT   EFI_MAC_ADDRESS *dest_addr    OPTIONAL,
+      OUT   UINT16 *protocol              OPTIONAL
+  );
+
+
+/*---------------------------------------------------------------------------------------------------------------------
+
+  UEFI-Compliant functions for EFI_COMPONENT_NAME2_PROTOCOL
+
+  Refer to the Component Name Protocol section (10.5) in the UEFI 2.3.1 Specification for related definitions
+
+---------------------------------------------------------------------------------------------------------------------*/
+
+/*
+ *  UEFI GetDriverName() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpGetDriverName (
+  IN        EFI_COMPONENT_NAME2_PROTOCOL *Snp,
+  IN        CHAR8 *Lang,
+      OUT   CHAR16 **DriverName
+  );
+
+/*
+ *  UEFI GetControllerName() function
+ *
+ */
+EFI_STATUS
+EFIAPI
+SnpGetControllerName (
+  IN        EFI_COMPONENT_NAME2_PROTOCOL *Cnp,
+  IN        EFI_HANDLE ControllerHandle,
+  IN        EFI_HANDLE ChildHandle            OPTIONAL,
+  IN        CHAR8 *Lang,
+      OUT   CHAR16 **ControllerName
+  );
+
+/*------------------------------------------------------------------------------
+  Utility functions
+------------------------------------------------------------------------------*/
+
+EFI_MAC_ADDRESS
+GetCurrentMacAddress (
+  VOID
+  );
+
+#endif // __LAN9118_DXE_H__
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
new file mode 100644
index 000000000000..b289f80c35a5
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
@@ -0,0 +1,58 @@
+#/** @file
+#  INF file for the LAN9118 Network Controller Driver.
+#
+#  WARNING:
+#  This driver fails to follow the UEFI driver model without a good
+#  reason, and only remains in the tree because it is still used by
+#  a small number of platforms. It will be removed when no longer used.
+#  New platforms should not use it, and no one should use this as
+#  reference code for developing new drivers.
+#
+#  Copyright (c) 2012-2015, ARM Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010006
+  BASE_NAME                      = Lan9118Dxe
+  FILE_GUID                      = 4356b162-d0b2-11e1-8952-4437e6a60ea5
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 0.1
+  ENTRY_POINT                    = Lan9118DxeEntry
+
+[Sources.common]
+  Lan9118Dxe.c
+  Lan9118DxeUtil.c
+  Lan9118Dxe.h
+
+[Packages]
+  NetworkPkg/NetworkPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  NetLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  IoLib
+  DevicePathLib
+
+[Protocols]
+  gEfiSimpleNetworkProtocolGuid
+  gEfiMetronomeArchProtocolGuid
+  gEfiPxeBaseCodeProtocolGuid
+  gEfiDevicePathProtocolGuid
+
+[FixedPcd]
+  gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultMacAddress
+  gArmVExpressTokenSpaceGuid.PcdLan9118DefaultNegotiationTimeout
+  gArmVExpressTokenSpaceGuid.PcdLan9118NegotiationFeatureMask
+
+[Depex]
+  TRUE
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h
new file mode 100644
index 000000000000..b5553be1905a
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h
@@ -0,0 +1,401 @@
+/** @file
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#ifndef __LAN9118_DXE_HW_H__
+#define __LAN9118_DXE_HW_H__
+
+/*------------------------------------------------------------------------------
+  LAN9118 SMCS Registers
+------------------------------------------------------------------------------*/
+
+// Base address as on the VE board
+#define LAN9118_BA                            ((UINT32) PcdGet32(PcdLan9118DxeBaseAddress))
+
+/* ------------- Tx and Rx Data and Status Memory Locations ------------------*/
+#define LAN9118_RX_DATA                       (0x00000000 + LAN9118_BA)
+#define LAN9118_RX_STATUS                     (0x00000040 + LAN9118_BA)
+#define LAN9118_RX_STATUS_PEEK                (0x00000044 + LAN9118_BA)
+#define LAN9118_TX_DATA                       (0x00000020 + LAN9118_BA)
+#define LAN9118_TX_STATUS                     (0x00000048 + LAN9118_BA)
+#define LAN9118_TX_STATUS_PEEK                (0x0000004C + LAN9118_BA)
+
+/* ------------- System Control and Status Registers -------------------------*/
+#define LAN9118_ID_REV                        (0x00000050 + LAN9118_BA)    // Chip ID and Revision
+#define LAN9118_IRQ_CFG                       (0x00000054 + LAN9118_BA)    // Interrupt Configuration
+#define LAN9118_INT_STS                       (0x00000058 + LAN9118_BA)    // Interrupt Status
+#define LAN9118_INT_EN                        (0x0000005C + LAN9118_BA)    // Interrupt Enable
+//#define LAN9118_RESERVED                    (0x00000060)
+#define LAN9118_BYTE_TEST                     (0x00000064 + LAN9118_BA)    // Byte Order Test
+#define LAN9118_FIFO_INT                      (0x00000068 + LAN9118_BA)    // FIFO Level Interrupts
+#define LAN9118_RX_CFG                        (0x0000006C + LAN9118_BA)    // Receive Configuration
+#define LAN9118_TX_CFG                        (0x00000070 + LAN9118_BA)    // Transmit Configuration
+#define LAN9118_HW_CFG                        (0x00000074 + LAN9118_BA)    // Hardware Configuration
+#define LAN9118_RX_DP_CTL                     (0x00000078 + LAN9118_BA)    // Receive Data-Path Configuration
+#define LAN9118_RX_FIFO_INF                   (0x0000007C + LAN9118_BA)    // Receive FIFO Information
+#define LAN9118_TX_FIFO_INF                   (0x00000080 + LAN9118_BA)    // Transmit FIFO Information
+#define LAN9118_PMT_CTRL                      (0x00000084 + LAN9118_BA)    // Power Management Control
+#define LAN9118_GPIO_CFG                      (0x00000088 + LAN9118_BA)    // General Purpose IO Configuration
+#define LAN9118_GPT_CFG                       (0x0000008C + LAN9118_BA)    // General Purpose Timer Configuration
+#define LAN9118_GPT_CNT                       (0x00000090 + LAN9118_BA)    // General Purpose Timer Current Count
+#define LAN9118_WORD_SWAP                     (0x00000098 + LAN9118_BA)    // Word Swap Control
+#define LAN9118_FREE_RUN                      (0x0000009C + LAN9118_BA)    // Free-Run 25MHz Counter
+#define LAN9118_RX_DROP                       (0x000000A0 + LAN9118_BA)    // Receiver Dropped Frames Counter
+#define LAN9118_MAC_CSR_CMD                   (0x000000A4 + LAN9118_BA)    // MAC CSR Synchronizer Command
+#define LAN9118_MAC_CSR_DATA                  (0x000000A8 + LAN9118_BA)    // MAC CSR Synchronizer Data
+#define LAN9118_AFC_CFG                       (0x000000AC + LAN9118_BA)    // Automatic Flow Control Configuration
+#define LAN9118_E2P_CMD                       (0x000000B0 + LAN9118_BA)    // EEPROM Command
+#define LAN9118_E2P_DATA                      (0x000000B4 + LAN9118_BA)    // EEPROM Data
+
+/*
+ * Required delays following write cycles (number of BYTE_TEST reads)
+ * Taken from Table 6.1 in Revision 1.5 (07-11-08) of the LAN9118 datasheet.
+ * Where no delay listed, 0 has been assumed.
+ */
+#define LAN9118_RX_DATA_WR_DELAY              0
+#define LAN9118_RX_STATUS_WR_DELAY            0
+#define LAN9118_RX_STATUS_PEEK_WR_DELAY       0
+#define LAN9118_TX_DATA_WR_DELAY              0
+#define LAN9118_TX_STATUS_WR_DELAY            0
+#define LAN9118_TX_STATUS_PEEK_WR_DELAY       0
+#define LAN9118_ID_REV_WR_DELAY               0
+#define LAN9118_IRQ_CFG_WR_DELAY              3
+#define LAN9118_INT_STS_WR_DELAY              2
+#define LAN9118_INT_EN_WR_DELAY               1
+#define LAN9118_BYTE_TEST_WR_DELAY            0
+#define LAN9118_FIFO_INT_WR_DELAY             1
+#define LAN9118_RX_CFG_WR_DELAY               1
+#define LAN9118_TX_CFG_WR_DELAY               1
+#define LAN9118_HW_CFG_WR_DELAY               1
+#define LAN9118_RX_DP_CTL_WR_DELAY            1
+#define LAN9118_RX_FIFO_INF_WR_DELAY          0
+#define LAN9118_TX_FIFO_INF_WR_DELAY          3
+#define LAN9118_PMT_CTRL_WR_DELAY             7
+#define LAN9118_GPIO_CFG_WR_DELAY             1
+#define LAN9118_GPT_CFG_WR_DELAY              1
+#define LAN9118_GPT_CNT_WR_DELAY              3
+#define LAN9118_WORD_SWAP_WR_DELAY            1
+#define LAN9118_FREE_RUN_WR_DELAY             4
+#define LAN9118_RX_DROP_WR_DELAY              0
+#define LAN9118_MAC_CSR_CMD_WR_DELAY          1
+#define LAN9118_MAC_CSR_DATA_WR_DELAY         1
+#define LAN9118_AFC_CFG_WR_DELAY              1
+#define LAN9118_E2P_CMD_WR_DELAY              1
+#define LAN9118_E2P_DATA_WR_DELAY             1
+
+/*
+ * Required delays following read cycles (number of BYTE_TEST reads)
+ * Taken from Table 6.2 in Revision 1.5 (07-11-08) of the LAN9118 datasheet.
+ * Where no delay listed, 0 has been assumed.
+ */
+#define LAN9118_RX_DATA_RD_DELAY              3
+#define LAN9118_RX_STATUS_RD_DELAY            3
+#define LAN9118_RX_STATUS_PEEK_RD_DELAY       0
+#define LAN9118_TX_DATA_RD_DELAY              0
+#define LAN9118_TX_STATUS_RD_DELAY            3
+#define LAN9118_TX_STATUS_PEEK_RD_DELAY       0
+#define LAN9118_ID_REV_RD_DELAY               0
+#define LAN9118_IRQ_CFG_RD_DELAY              0
+#define LAN9118_INT_STS_RD_DELAY              0
+#define LAN9118_INT_EN_RD_DELAY               0
+#define LAN9118_BYTE_TEST_RD_DELAY            0
+#define LAN9118_FIFO_INT_RD_DELAY             0
+#define LAN9118_RX_CFG_RD_DELAY               0
+#define LAN9118_TX_CFG_RD_DELAY               0
+#define LAN9118_HW_CFG_RD_DELAY               0
+#define LAN9118_RX_DP_CTL_RD_DELAY            0
+#define LAN9118_RX_FIFO_INF_RD_DELAY          0
+#define LAN9118_TX_FIFO_INF_RD_DELAY          0
+#define LAN9118_PMT_CTRL_RD_DELAY             0
+#define LAN9118_GPIO_CFG_RD_DELAY             0
+#define LAN9118_GPT_CFG_RD_DELAY              0
+#define LAN9118_GPT_CNT_RD_DELAY              0
+#define LAN9118_WORD_SWAP_RD_DELAY            0
+#define LAN9118_FREE_RUN_RD_DELAY             0
+#define LAN9118_RX_DROP_RD_DELAY              4
+#define LAN9118_MAC_CSR_CMD_RD_DELAY          0
+#define LAN9118_MAC_CSR_DATA_RD_DELAY         0
+#define LAN9118_AFC_CFG_RD_DELAY              0
+#define LAN9118_E2P_CMD_RD_DELAY              0
+#define LAN9118_E2P_DATA_RD_DELAY             0
+
+// Receiver Status bits
+#define RXSTATUS_CRC_ERROR                    BIT1                      // Cyclic Redundancy Check Error
+#define RXSTATUS_DB                           BIT2                      // Dribbling bit: Frame had non-integer multiple of 8bits
+#define RXSTATUS_MII_ERROR                    BIT3                      // Receive error during interception
+#define RXSTATUS_RXW_TO                       BIT4                      // Incoming frame larger than 2kb
+#define RXSTATUS_FT                           BIT5                      // 1: Ether type / 0: 802.3 type frame
+#define RXSTATUS_LCOLL                        BIT6                      // Late collision detected
+#define RXSTATUS_FTL                          BIT7                      // Frame longer than Ether type
+#define RXSTATUS_MCF                          BIT10                     // Frame has Multicast Address
+#define RXSTATUS_RUNT                         BIT11                     // Bad frame
+#define RXSTATUS_LE                           BIT12                     // Actual length of frame different than it claims
+#define RXSTATUS_BCF                          BIT13                     // Frame has Broadcast Address
+#define RXSTATUS_ES                           BIT15                     // Reports any error from bits 1,6,7 and 11
+#define RXSTATUS_PL_MASK                      (0x3FFF0000)              // Packet length bit mask
+#define GET_RXSTATUS_PACKET_LENGTH(RxStatus)  (((RxStatus) >> 16) & 0x3FFF) // Packet length bit mask
+#define RXSTATUS_FILT_FAIL                    BIT30                     // The frame failed filtering test
+
+// Transmitter Status bits
+#define TXSTATUS_DEF                          BIT0                      // Packet tx was deferred
+#define TXSTATUS_EDEF                         BIT2                      // Tx ended because of excessive deferral (> 24288 bit times)
+#define TXSTATUS_CC_MASK                      (0x00000078)              // Collision Count (before Tx) bit mask
+#define TXSTATUS_ECOLL                        BIT8                      // Tx ended because of Excessive Collisions (makes CC_MASK invalid after 16 collisions)
+#define TXSTATUS_LCOLL                        BIT9                      // Packet Tx aborted after coll window of 64 bytes
+#define TXSTATUS_NO_CA                        BIT10                     // Carrier signal not present during Tx (bad?)
+#define TXSTATUS_LOST_CA                      BIT11                     // Lost carrier during Tx
+#define TXSTATUS_ES                           BIT15                     // Reports any errors from bits 1,2,8,9,10 and 11
+#define TXSTATUS_PTAG_MASK                    (0xFFFF0000)              // Mask for Unique ID of packets (So we know who the packets are for)
+
+// ID_REV register bits
+#define IDREV_ID                              ((Lan9118MmioRead32(LAN9118_ID_REV) & 0xFFFF0000) >> 16)
+#define IDREV_REV                             (Lan9118MmioRead32(LAN9118_ID_REV) & 0x0000FFFF)
+
+// Interrupt Config Register bits
+#define IRQCFG_IRQ_TYPE                       BIT0                    // IRQ Buffer type
+#define IRQCFG_IRQ_POL                        BIT4                    // IRQ Polarity
+#define IRQCFG_IRQ_EN                         BIT8                    // Enable external interrupt
+#define IRQCFG_IRQ_INT                        BIT12                   // State of internal interrupts line
+#define IRQCFG_INT_DEAS_STS                   BIT13                   // State of deassertion interval
+#define IRQCFG_INT_DEAS_CLR                   BIT14                   // Clear the deassertion counter
+#define IRQCFG_INT_DEAS_MASK                  (0xFF000000)            // Interrupt deassertion interval value mask
+
+// Interrupt Status Register bits
+#define INSTS_GPIO_MASK                       (0x7)                   // GPIO interrupts mask
+#define INSTS_RSFL                            (0x8)                   // Rx Status FIFO Level reached
+#define INSTS_RSFF                            BIT4                    // Rx Status FIFO full
+#define INSTS_RXDF_INT                        BIT6                    // Rx Frame dropped
+#define INSTS_TSFL                            BIT7                    // Tx Status FIFO Level reached
+#define INSTS_TSFF                            BIT8                    // Tx Status FIFO full
+#define INSTS_TDFA                            BIT9                    // Tx Data FIFO Level exceeded
+#define INSTS_TDFO                            BIT10                   // Tx Data FIFO full
+#define INSTS_TXE                             BIT13                   // Transmitter Error
+#define INSTS_RXE                             BIT14                   // Receiver Error
+#define INSTS_RWT                             BIT15                   // Packet > 2048 bytes received
+#define INSTS_TXSO                            BIT16                   // Tx Status FIFO Overflow
+#define INSTS_PME_INT                         BIT17                   // PME Signal detected
+#define INSTS_PHY_INT                         BIT18                   // Indicates PHY Interrupt
+#define INSTS_GPT_INT                         BIT19                   // GP Timer wrapped past 0xFFFF
+#define INSTS_RXD_INT                         BIT20                   // Indicates that amount of data written to RX_CFG was cleared
+#define INSTS_TX_IOC                          BIT21                   // Finished loading IOC flagged buffer to Tx FIFO
+#define INSTS_RXDFH_INT                       BIT23                   // Rx Dropped frames went past 0x7FFFFFFF
+#define INSTS_RXSTOP_INT                      BIT24                   // Rx was stopped
+#define INSTS_TXSTOP_INT                      BIT25                   // Tx was stopped
+#define INSTS_SW_INT                          BIT31                   // Software Interrupt occurred
+
+// Interrupt Enable Register bits
+
+
+// Hardware Config Register bits
+#define HWCFG_SRST                            BIT0                       // Software Reset bit         (SC)
+#define HWCFG_SRST_TO                         BIT1                       // Software Reset Timeout bit (RO)
+#define HWCFG_BMODE                           BIT2                       // 32/16 bit Mode bit         (RO)
+#define HWCFG_TX_FIFO_SIZE_MASK               (~ (UINT32)0xF0000)        // Mask to Clear FIFO Size
+#define HWCFG_MBO                             BIT20                      // Must Be One bit
+
+// Power Management Control Register
+#define MPTCTRL_READY                         BIT0                // Device ready indicator
+#define MPTCTRL_PME_EN                        BIT1                // Enable external PME signals
+#define MPTCTRL_PME_POL                       BIT2                // Set polarity of PME signals
+#define MPTCTRL_PME_IND                       BIT3                // Signal type of PME (refer to Spec)
+#define MPTCTRL_WUPS_MASK                     (0x18)              // Wake up status indicator mask
+#define MPTCTRL_PME_TYPE                      BIT6                // PME Buffer type (Open Drain or Push-Pull)
+#define MPTCTRL_ED_EN                         BIT8                // Energy-detect enable
+#define MPTCTRL_WOL_EN                        BIT9                // Enable wake-on-lan
+#define MPTCTRL_PHY_RST                       BIT10               // Reset the PHY
+#define MPTCTRL_PM_MODE_MASK                  (BIT12 | BIT13)     // Set the power mode
+
+// PHY control register bits
+#define PHYCR_COLL_TEST                       BIT7                  // Collision test enable
+#define PHYCR_DUPLEX_MODE                     BIT8                  // Set Duplex Mode
+#define PHYCR_RST_AUTO                        BIT9                  // Restart Auto-Negotiation of Link abilities
+#define PHYCR_PD                              BIT11                 // Power-Down switch
+#define PHYCR_AUTO_EN                         BIT12                 // Auto-Negotiation Enable
+#define PHYCR_SPEED_SEL                       BIT13                 // Link Speed Selection
+#define PHYCR_LOOPBK                          BIT14                 // Set loopback mode
+#define PHYCR_RESET                           BIT15                 // Do a PHY reset
+
+// PHY status register bits
+#define PHYSTS_EXT_CAP                        BIT0                  // Extended Capabilities Register capability
+#define PHYSTS_JABBER                         BIT1                  // Jabber condition detected
+#define PHYSTS_LINK_STS                       BIT2                  // Link Status
+#define PHYSTS_AUTO_CAP                       BIT3                  // Auto-Negotiation Capability
+#define PHYSTS_REMOTE_FAULT                   BIT4                  // Remote fault detected
+#define PHYSTS_AUTO_COMP                      BIT5                  // Auto-Negotiation Completed
+#define PHYSTS_10BASET_HDPLX                  BIT11                 // 10Mbps Half-Duplex ability
+#define PHYSTS_10BASET_FDPLX                  BIT12                 // 10Mbps Full-Duplex ability
+#define PHYSTS_100BASETX_HDPLX                BIT13                 // 100Mbps Half-Duplex ability
+#define PHYSTS_100BASETX_FDPLX                BIT14                 // 100Mbps Full-Duplex ability
+#define PHYSTS_100BASE_T4                     BIT15                 // Base T4 ability
+
+// PHY Auto-Negotiation advertisement
+#define PHYANA_SEL_MASK                       ((UINT32)0x1F)        // Link type selector
+#define PHYANA_10BASET                        BIT5                  // Advertise 10BASET capability
+#define PHYANA_10BASETFD                      BIT6                  // Advertise 10BASET Full duplex capability
+#define PHYANA_100BASETX                      BIT7                  // Advertise 100BASETX capability
+#define PHYANA_100BASETXFD                    BIT8                  // Advertise 100 BASETX Full duplex capability
+#define PHYANA_PAUSE_OP_MASK                  (3 << 10)             // Advertise PAUSE frame capability
+#define PHYANA_REMOTE_FAULT                   BIT13                 // Remote fault detected
+
+
+// PHY Auto-Negotiation Link Partner Ability
+
+// PHY Auto-Negotiation Expansion
+
+// PHY Mode control/status
+
+// PHY Special Modes
+
+// PHY Special control/status
+
+// PHY Interrupt Source Flags
+
+// PHY Interrupt Mask
+
+// PHY Super Special control/status
+#define PHYSSCS_HCDSPEED_MASK                 (7 << 2)              // Speed indication
+#define PHYSSCS_AUTODONE                      BIT12                 // Auto-Negotiation Done
+
+
+// MAC control register bits
+#define MACCR_RX_EN                       BIT2                     // Enable Receiver bit
+#define MACCR_TX_EN                       BIT3                     // Enable Transmitter bit
+#define MACCR_DFCHK                       BIT5                     // Deferral Check bit
+#define MACCR_PADSTR                      BIT8                     // Automatic Pad Stripping bit
+#define MACCR_BOLMT_MASK                  (0xC0)                   // Back-Off limit mask
+#define MACCR_DISRTY                      BIT10                    // Disable Transmit Retry bit
+#define MACCR_BCAST                       BIT11                    // Disable Broadcast Frames bit
+#define MACCR_LCOLL                       BIT12                    // Late Collision Control bit
+#define MACCR_HPFILT                      BIT13                    // Hash/Perfect Filtering Mode bit
+#define MACCR_HO                          BIT15                    // Hash Only Filtering Mode
+#define MACCR_PASSBAD                     BIT16                    // Receive all frames that passed filter bit
+#define MACCR_INVFILT                     BIT17                    // Enable Inverse Filtering bit
+#define MACCR_PRMS                        BIT18                    // Promiscuous Mode bit
+#define MACCR_MCPAS                       BIT19                    // Pass all Multicast packets bit
+#define MACCR_FDPX                        BIT20                    // Full Duplex Mode bit
+#define MACCR_LOOPBK                      BIT21                    // Loopback operation mode bit
+#define MACCR_RCVOWN                      BIT23                    // Disable Receive Own frames bit
+#define MACCR_RX_ALL                      BIT31                    // Receive all Packets and route to Filter
+
+// Wake-Up Control and Status Register
+#define WUCSR_MPEN                        BIT1                     // Magic Packet enable (allow wake from Magic P)
+#define WUCSR_WUEN                        BIT2                     // Allow remote wake up using Wake-Up Frames
+#define WUCSR_MPR_MASK                    (0x10)                   // Received Magic Packet
+#define WUCSR_WUFR_MASK                   (0x20)                   // Received Wake-Up Frame
+#define WUCSR_GUE                         BIT9                     // Enable wake on global unicast frames
+
+// RX Configuration Register bits
+#define RXCFG_RXDOFF_MASK                 (0x1F00)                 // Rx Data Offset in Bytes
+#define RXCFG_RX_DUMP                     BIT15                    // Clear Rx data and status FIFOs
+#define RXCFG_RX_DMA_CNT_MASK             (0x0FFF0000)             // Amount of data to be read from Rx FIFO
+#define RXCFG_RX_DMA_CNT(cnt)             (((cnt) & 0xFFF) << 16)  // Amount of data to be read from Rx FIFO
+#define RXCFG_RX_END_ALIGN_MASK           (0xC0000000)             // Alignment to preserve
+
+// TX Configuration Register bits
+#define TXCFG_STOP_TX                     BIT0                     // Stop the transmitter
+#define TXCFG_TX_ON                       BIT1                     // Start the transmitter
+#define TXCFG_TXSAO                       BIT2                     // Tx Status FIFO full
+#define TXCFG_TXD_DUMP                    BIT14                    // Clear Tx Data FIFO
+#define TXCFG_TXS_DUMP                    BIT15                    // Clear Tx Status FIFO
+
+// Rx FIFO Information Register bits
+#define RXFIFOINF_RXDUSED_MASK            (0xFFFF)                 // Rx Data FIFO Used Space
+#define RXFIFOINF_RXSUSED_MASK            (0xFF0000)               // Rx Status FIFO Used Space
+
+// Tx FIFO Information Register bits
+#define TXFIFOINF_TDFREE_MASK             (0xFFFF)                 // Tx Data FIFO Free Space
+#define TXFIFOINF_TXSUSED_MASK            (0xFF0000)               // Tx Status FIFO Used Space
+
+// E2P Register
+#define E2P_EPC_BUSY                BIT31
+#define E2P_EPC_CMD_READ            (0)
+#define E2P_EPC_TIMEOUT             BIT9
+#define E2P_EPC_MAC_ADDRESS_LOADED  BIT8
+#define E2P_EPC_ADDRESS(address)    ((address) & 0xFFFF)
+
+// GPIO Configuration register
+#define GPIO_GPIO0_PUSH_PULL        BIT16
+#define GPIO_GPIO1_PUSH_PULL        BIT17
+#define GPIO_GPIO2_PUSH_PULL        BIT18
+#define GPIO_LED1_ENABLE            BIT28
+#define GPIO_LED2_ENABLE            BIT29
+#define GPIO_LED3_ENABLE            BIT30
+
+// MII_ACC bits
+#define MII_ACC_MII_BUSY        BIT0
+#define MII_ACC_MII_WRITE       BIT1
+#define MII_ACC_MII_READ        0
+
+#define MII_ACC_PHY_VALUE             BIT11
+#define MII_ACC_MII_REG_INDEX(index)  (((index) & 0x1F) << 6)
+
+//
+// PHY Control Indexes
+//
+#define PHY_INDEX_BASIC_CTRL              0
+#define PHY_INDEX_BASIC_STATUS            1
+#define PHY_INDEX_ID1                     2
+#define PHY_INDEX_ID2                     3
+#define PHY_INDEX_AUTO_NEG_ADVERT         4
+#define PHY_INDEX_AUTO_NEG_LINK_ABILITY   5
+#define PHY_INDEX_AUTO_NEG_EXP            6
+#define PHY_INDEX_MODE                    17
+#define PHY_INDEX_SPECIAL_MODES           18
+#define PHY_INDEX_SPECIAL_CTLR            27
+#define PHY_INDEX_INT_SRC                 29
+#define PHY_INDEX_INT_MASK                30
+#define PHY_INDEX_SPECIAL_PHY_CTLR        31
+
+// Indirect MAC Indexes
+#define INDIRECT_MAC_INDEX_CR         1
+#define INDIRECT_MAC_INDEX_ADDRH      2
+#define INDIRECT_MAC_INDEX_ADDRL      3
+#define INDIRECT_MAC_INDEX_HASHH      4
+#define INDIRECT_MAC_INDEX_HASHL      5
+#define INDIRECT_MAC_INDEX_MII_ACC    6
+#define INDIRECT_MAC_INDEX_MII_DATA   7
+
+//
+// MAC CSR Synchronizer Command register
+//
+#define MAC_CSR_BUSY            BIT31
+#define MAC_CSR_READ            BIT30
+#define MAC_CSR_WRITE           0
+#define MAC_CSR_ADDR(Addr)      ((Addr) & 0xFF)
+
+//
+// TX Packet Format
+//
+#define TX_CMD_A_COMPLETION_INT             BIT31
+#define TX_CMD_A_FIRST_SEGMENT              BIT13
+#define TX_CMD_A_LAST_SEGMENT               BIT12
+#define TX_CMD_A_BUFF_SIZE(size)            ((size) & 0x000003FF)
+#define TX_CMD_A_DATA_START_OFFSET(offset)  (((offset) & 0x1F) << 16)
+#define TX_CMD_B_PACKET_LENGTH(size)        ((size) & 0x000003FF)
+#define TX_CMD_B_PACKET_TAG(tag)            (((tag) & 0x3FF) << 16)
+
+// Hardware Configuration Register
+#define HW_CFG_TX_FIFO_SIZE_MASK        (0xF << 16)
+#define HW_CFG_TX_FIFO_SIZE(size)       (((size) & 0xF) << 16)
+
+// EEPROM Definition
+#define EEPROM_EXTERNAL_SERIAL_EEPROM   0xA5
+
+//
+// Conditional compilation flags
+//
+//#define EVAL_PERFORMANCE
+
+
+#endif /* __LAN9118_DXE_HDR_H__ */
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c
new file mode 100644
index 000000000000..4ccbcad6b9e1
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c
@@ -0,0 +1,1039 @@
+/** @file
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include "Lan9118Dxe.h"
+
+STATIC EFI_MAC_ADDRESS mZeroMac = { { 0 } };
+
+/**
+  This internal function reverses bits for 32bit data.
+
+  @param  Value                 The data to be reversed.
+
+  @return                       Data reversed.
+
+**/
+UINT32
+ReverseBits (
+  UINT32  Value
+  )
+{
+  UINTN   Index;
+  UINT32  NewValue;
+
+  NewValue = 0;
+  for (Index = 0; Index < 32; Index++) {
+    if ((Value & (1 << Index)) != 0) {
+      NewValue = NewValue | (1 << (31 - Index));
+    }
+  }
+
+  return NewValue;
+}
+
+/*
+**  Create Ethernet CRC
+**
+**  INFO USED:
+**    1: http://en.wikipedia.org/wiki/Cyclic_redundancy_check
+**
+**    2: http://www.erg.abdn.ac.uk/~gorry/eg3567/dl-pages/crc.html
+**
+**    3: http://en.wikipedia.org/wiki/Computation_of_CRC
+*/
+UINT32
+GenEtherCrc32 (
+  IN    EFI_MAC_ADDRESS *Mac,
+  IN    UINT32 AddrLen
+  )
+{
+  INT32 Iter;
+  UINT32 Remainder;
+  UINT8 *Ptr;
+
+  Iter = 0;
+  Remainder = 0xFFFFFFFF;    // 0xFFFFFFFF is standard seed for Ethernet
+
+  // Convert Mac Address to array of bytes
+  Ptr = (UINT8*)Mac;
+
+  // Generate the Crc bit-by-bit (LSB first)
+  while (AddrLen--) {
+    Remainder ^= *Ptr++;
+    for (Iter = 0;Iter < 8;Iter++) {
+      // Check if exponent is set
+      if (Remainder & 1) {
+        Remainder = (Remainder >> 1) ^ CRC_POLYNOMIAL;
+      } else {
+        Remainder = (Remainder >> 1) ^ 0;
+      }
+    }
+  }
+
+  // Reverse the bits before returning (to Big Endian)
+  //TODO: Need to be reviewed. Do we want to do a bit reverse or a byte reverse (in this case use SwapBytes32())
+  return ReverseBits (Remainder);
+}
+
+// Function to read from MAC indirect registers
+UINT32
+IndirectMACRead32 (
+  UINT32 Index
+  )
+{
+  UINT32 MacCSR;
+
+  // Check index is in the range
+  ASSERT(Index <= 12);
+
+  // Wait until CSR busy bit is cleared
+  while ((Lan9118MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
+
+  // Set CSR busy bit to ensure read will occur
+  // Set the R/W bit to indicate we are reading
+  // Set the index of CSR Address to access desired register
+  MacCSR = MAC_CSR_BUSY | MAC_CSR_READ | MAC_CSR_ADDR(Index);
+
+  // Write to the register
+  Lan9118MmioWrite32 (LAN9118_MAC_CSR_CMD, MacCSR);
+
+  // Wait until CSR busy bit is cleared
+  while ((Lan9118MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
+
+  // Now read from data register to get read value
+  return Lan9118MmioRead32 (LAN9118_MAC_CSR_DATA);
+}
+
+/*
+ * LAN9118 chips have special restrictions on some back-to-back Write/Read or
+ * Read/Read pairs of accesses. After a read or write that changes the state of
+ * the device, there is a period in which stale values may be returned in
+ * response to a read. This period is dependent on the registers accessed.
+ *
+ * We must delay prior reads by this period. This can either be achieved by
+ * timer-based delays, or by performing dummy reads of the BYTE_TEST register,
+ * for which the recommended number of reads is described in the LAN9118 data
+ * sheet. This is required in addition to any memory barriers.
+ *
+ * This function performs a number of dummy reads of the BYTE_TEST register, as
+ * a building block for the above.
+ */
+VOID
+WaitDummyReads (
+  UINTN Count
+  )
+{
+  while (Count--)
+    MmioRead32(LAN9118_BYTE_TEST);
+}
+
+UINT32
+Lan9118RawMmioRead32(
+  UINTN Address,
+  UINTN Delay
+  )
+{
+  UINT32 Value;
+
+  Value = MmioRead32(Address);
+  WaitDummyReads(Delay);
+  return Value;
+}
+
+UINT32
+Lan9118RawMmioWrite32(
+  UINTN Address,
+  UINT32 Value,
+  UINTN Delay
+  )
+{
+  MmioWrite32(Address, Value);
+  WaitDummyReads(Delay);
+  return Value;
+}
+
+// Function to write to MAC indirect registers
+UINT32
+IndirectMACWrite32 (
+  UINT32 Index,
+  UINT32 Value
+  )
+{
+  UINT32 ValueWritten;
+  UINT32 MacCSR;
+
+  // Check index is in the range
+  ASSERT(Index <= 12);
+
+  // Wait until CSR busy bit is cleared
+  while ((Lan9118MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
+
+  // Set CSR busy bit to ensure read will occur
+  // Set the R/W bit to indicate we are writing
+  // Set the index of CSR Address to access desired register
+  MacCSR = MAC_CSR_BUSY | MAC_CSR_WRITE | MAC_CSR_ADDR(Index);
+
+  // Now write the value to the register before issuing the write command
+  ValueWritten = Lan9118MmioWrite32 (LAN9118_MAC_CSR_DATA, Value);
+
+  // Write the config to the register
+  Lan9118MmioWrite32 (LAN9118_MAC_CSR_CMD, MacCSR);
+
+  // Wait until CSR busy bit is cleared
+  while ((Lan9118MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY);
+
+  return ValueWritten;
+}
+
+// Function to read from MII register (PHY Access)
+UINT32
+IndirectPHYRead32 (
+  UINT32 Index
+  )
+{
+  UINT32 ValueRead;
+  UINT32 MiiAcc;
+
+  // Check it is a valid index
+  ASSERT(Index < 31);
+
+  // Wait for busy bit to clear
+  while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
+
+  // Clear the R/W bit to indicate we are reading
+  // Set the index of the MII register
+  // Set the PHY Address
+  // Set the MII busy bit to allow read
+  MiiAcc = MII_ACC_MII_READ | MII_ACC_MII_REG_INDEX(Index) | MII_ACC_PHY_VALUE | MII_ACC_MII_BUSY;
+
+  // Now write this config to register
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_ACC, MiiAcc & 0xFFFF);
+
+  // Wait for busy bit to clear
+  while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
+
+  // Now read the value of the register
+  ValueRead = (IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_DATA) & 0xFFFF); // only lower 16 bits are valid for any PHY register
+
+  return ValueRead;
+}
+
+
+// Function to write to the MII register (PHY Access)
+UINT32
+IndirectPHYWrite32 (
+  UINT32 Index,
+  UINT32 Value
+  )
+{
+  UINT32 MiiAcc;
+  UINT32 ValueWritten;
+
+  // Check it is a valid index
+  ASSERT(Index < 31);
+
+  // Wait for busy bit to clear
+  while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
+
+  // Clear the R/W bit to indicate we are reading
+  // Set the index of the MII register
+  // Set the PHY Address
+  // Set the MII busy bit to allow read
+  MiiAcc = MII_ACC_MII_WRITE | MII_ACC_MII_REG_INDEX(Index) | MII_ACC_PHY_VALUE | MII_ACC_MII_BUSY;
+
+  // Write the desired value to the register first
+  ValueWritten = IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_DATA, (Value & 0xFFFF));
+
+  // Now write the config to register
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_ACC, MiiAcc & 0xFFFF);
+
+  // Wait for operation to terminate
+  while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY);
+
+  return ValueWritten;
+}
+
+
+/* ---------------- EEPROM Operations ------------------ */
+
+
+// Function to read from EEPROM memory
+UINT32
+IndirectEEPROMRead32 (
+  UINT32 Index
+  )
+{
+  UINT32 EepromCmd;
+
+  // Set the busy bit to ensure read will occur
+  EepromCmd = E2P_EPC_BUSY | E2P_EPC_CMD_READ;
+
+  // Set the index to access desired EEPROM memory location
+  EepromCmd |= E2P_EPC_ADDRESS(Index);
+
+  // Write to Eeprom command register
+  Lan9118MmioWrite32 (LAN9118_E2P_CMD, EepromCmd);
+
+  // Wait until operation has completed
+  while (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
+
+  // Check that operation didn't time out
+  if (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_TIMEOUT) {
+    DEBUG ((EFI_D_ERROR, "EEPROM Operation Timed out: Read command on index %x\n",Index));
+    return 0;
+  }
+
+  // Wait until operation has completed
+  while (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
+
+  // Finally read the value
+  return Lan9118MmioRead32 (LAN9118_E2P_DATA);
+}
+
+// Function to write to EEPROM memory
+UINT32
+IndirectEEPROMWrite32 (
+  UINT32 Index,
+  UINT32 Value
+  )
+{
+  UINT32 ValueWritten;
+  UINT32 EepromCmd;
+
+  ValueWritten = 0;
+
+  // Read the EEPROM Command register
+  EepromCmd = Lan9118MmioRead32 (LAN9118_E2P_CMD);
+
+  // Set the busy bit to ensure read will occur
+  EepromCmd |= ((UINT32)1 << 31);
+
+  // Set the EEPROM command to write(0b011)
+  EepromCmd &= ~(7 << 28);    // Clear the command first
+  EepromCmd |= (3 << 28);     // Write 011
+
+  // Set the index to access desired EEPROM memory location
+  EepromCmd |= (Index & 0xF);
+
+  // Write the value to the data register first
+  ValueWritten = Lan9118MmioWrite32 (LAN9118_E2P_DATA, Value);
+
+  // Write to Eeprom command register
+  Lan9118MmioWrite32 (LAN9118_E2P_CMD, EepromCmd);
+
+  // Wait until operation has completed
+  while (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
+
+  // Check that operation didn't time out
+  if (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_TIMEOUT) {
+    DEBUG ((EFI_D_ERROR, "EEPROM Operation Timed out: Write command at memloc 0x%x, with value 0x%x\n",Index, Value));
+    return 0;
+  }
+
+  // Wait until operation has completed
+  while (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
+
+  return ValueWritten;
+}
+
+/* ---------------- General Operations ----------------- */
+
+VOID
+Lan9118SetMacAddress (
+  EFI_MAC_ADDRESS             *Mac,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL,
+                      (Mac->Addr[0] & 0xFF) |
+                      ((Mac->Addr[1] & 0xFF) << 8) |
+                      ((Mac->Addr[2] & 0xFF) << 16) |
+                      ((Mac->Addr[3] & 0xFF) << 24)
+                    );
+
+  IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH,
+                      (UINT32)(Mac->Addr[4] & 0xFF) |
+                      ((Mac->Addr[5] & 0xFF) << 8)
+                    );
+}
+
+VOID
+Lan9118ReadMacAddress (
+  OUT EFI_MAC_ADDRESS *MacAddress
+  )
+{
+  UINT32          MacAddrHighValue;
+  UINT32          MacAddrLowValue;
+
+  // Read the Mac Addr high register
+  MacAddrHighValue = (IndirectMACRead32 (INDIRECT_MAC_INDEX_ADDRH) & 0xFFFF);
+  // Read the Mac Addr low register
+  MacAddrLowValue = IndirectMACRead32 (INDIRECT_MAC_INDEX_ADDRL);
+
+  SetMem (MacAddress, sizeof(*MacAddress), 0);
+  MacAddress->Addr[0] = (MacAddrLowValue & 0xFF);
+  MacAddress->Addr[1] = (MacAddrLowValue & 0xFF00) >> 8;
+  MacAddress->Addr[2] = (MacAddrLowValue & 0xFF0000) >> 16;
+  MacAddress->Addr[3] = (MacAddrLowValue & 0xFF000000) >> 24;
+  MacAddress->Addr[4] = (MacAddrHighValue & 0xFF);
+  MacAddress->Addr[5] = (MacAddrHighValue & 0xFF00) >> 8;
+}
+
+/*
+ *  Power up the 9118 and find its MAC address.
+ *
+ *  This operation can be carried out when the LAN9118 is in any power state
+ *
+ */
+EFI_STATUS
+Lan9118Initialize (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINTN  Retries;
+  UINT64 DefaultMacAddress;
+
+  // Attempt to wake-up the device if it is in a lower power state
+  if (((Lan9118MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_PM_MODE_MASK) >> 12) != 0) {
+    DEBUG ((DEBUG_NET, "Waking from reduced power state.\n"));
+    Lan9118MmioWrite32 (LAN9118_BYTE_TEST, 0xFFFFFFFF);
+  }
+
+  // Check that device is active
+  Retries = 20;
+  while ((Lan9118MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_READY) == 0 && --Retries) {
+    gBS->Stall (LAN9118_STALL);
+  }
+  if (!Retries) {
+    return EFI_TIMEOUT;
+  }
+
+  // Check that EEPROM isn't active
+  Retries = 20;
+  while ((Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY) && --Retries){
+    gBS->Stall (LAN9118_STALL);
+  }
+  if (!Retries) {
+    return EFI_TIMEOUT;
+  }
+
+  // Check if a MAC address was loaded from EEPROM, and if it was, set it as the
+  // current address.
+  if ((Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_MAC_ADDRESS_LOADED) == 0) {
+    DEBUG ((EFI_D_ERROR, "Warning: There was an error detecting EEPROM or loading the MAC Address.\n"));
+
+    // If we had an address before (set by StationAddress), continue to use it
+    if (CompareMem (&Snp->Mode->CurrentAddress, &mZeroMac, NET_ETHER_ADDR_LEN)) {
+      Lan9118SetMacAddress (&Snp->Mode->CurrentAddress, Snp);
+    } else {
+      // If there are no cached addresses, then fall back to a default
+      DEBUG ((EFI_D_WARN, "Warning: using driver-default MAC address\n"));
+      DefaultMacAddress = FixedPcdGet64 (PcdLan9118DefaultMacAddress);
+      Lan9118SetMacAddress((EFI_MAC_ADDRESS *) &DefaultMacAddress, Snp);
+      CopyMem (&Snp->Mode->CurrentAddress, &DefaultMacAddress, NET_ETHER_ADDR_LEN);
+    }
+  } else {
+    // Store the MAC address that was loaded from EEPROM
+    Lan9118ReadMacAddress (&Snp->Mode->CurrentAddress);
+    CopyMem (&Snp->Mode->PermanentAddress, &Snp->Mode->CurrentAddress, NET_ETHER_ADDR_LEN);
+  }
+
+  // Clear and acknowledge interrupts
+  Lan9118MmioWrite32 (LAN9118_INT_EN, 0);
+  Lan9118MmioWrite32 (LAN9118_IRQ_CFG, 0);
+  Lan9118MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF);
+
+  // Do self tests here?
+
+  return EFI_SUCCESS;
+}
+
+
+// Perform software reset on the LAN9118
+// Return 0 on success, -1 on error
+EFI_STATUS
+SoftReset (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 HwConf;
+  UINT32 ResetTime;
+
+  // Initialize variable
+  ResetTime = 0;
+
+  // Stop Rx and Tx
+  StopTx (STOP_TX_MAC | STOP_TX_CFG | STOP_TX_CLEAR, Snp);
+  StopRx (STOP_RX_CLEAR, Snp); // Clear receiver FIFO
+
+  // Issue the reset
+  HwConf = Lan9118MmioRead32 (LAN9118_HW_CFG);
+  HwConf |= 1;
+
+  // Set the Must Be One (MBO) bit
+  if (((HwConf & HWCFG_MBO) >> 20) == 0) {
+    HwConf |= HWCFG_MBO;
+  }
+
+  // Check that EEPROM isn't active
+  while (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
+
+  // Write the configuration
+  Lan9118MmioWrite32 (LAN9118_HW_CFG, HwConf);
+
+  // Wait for reset to complete
+  while (Lan9118MmioRead32 (LAN9118_HW_CFG) & HWCFG_SRST) {
+
+    gBS->Stall (LAN9118_STALL);
+    ResetTime += 1;
+
+    // If time taken exceeds 100us, then there was an error condition
+    if (ResetTime > 1000) {
+      Snp->Mode->State = EfiSimpleNetworkStopped;
+      return EFI_TIMEOUT;
+    }
+  }
+
+  // Check that EEPROM isn't active
+  while (Lan9118MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY);
+
+  // TODO we probably need to re-set the mac address here.
+
+  // Clear and acknowledge all interrupts
+  if (Flags & SOFT_RESET_CLEAR_INT) {
+    Lan9118MmioWrite32 (LAN9118_INT_EN, 0);
+    Lan9118MmioWrite32 (LAN9118_IRQ_CFG, 0);
+    Lan9118MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF);
+  }
+
+  // Do self tests here?
+  if (Flags & SOFT_RESET_SELF_TEST) {
+
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+// Perform PHY software reset
+EFI_STATUS
+PhySoftReset (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 PmtCtrl = 0;
+
+  // PMT PHY reset takes precedence over BCR
+  if (Flags & PHY_RESET_PMT) {
+    PmtCtrl = Lan9118MmioRead32 (LAN9118_PMT_CTRL);
+    PmtCtrl |= MPTCTRL_PHY_RST;
+    Lan9118MmioWrite32 (LAN9118_PMT_CTRL,PmtCtrl);
+
+    // Wait for completion
+    while (Lan9118MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_PHY_RST) {
+      gBS->Stall (LAN9118_STALL);
+    }
+  // PHY Basic Control Register reset
+  } else if (Flags & PHY_RESET_BCR) {
+    IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PHYCR_RESET);
+
+    // Wait for completion
+    while (IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) {
+      gBS->Stall (LAN9118_STALL);
+    }
+  }
+
+  // Clear and acknowledge all interrupts
+  if (Flags & PHY_SOFT_RESET_CLEAR_INT) {
+    Lan9118MmioWrite32 (LAN9118_INT_EN, 0);
+    Lan9118MmioWrite32 (LAN9118_IRQ_CFG, 0);
+    Lan9118MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+// Configure hardware for LAN9118
+EFI_STATUS
+ConfigureHardware (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 GpioConf;
+
+  // Check if we want to use LEDs on GPIO
+  if (Flags & HW_CONF_USE_LEDS) {
+    GpioConf = Lan9118MmioRead32 (LAN9118_GPIO_CFG);
+
+    // Enable GPIO as LEDs and Config as Push-Pull driver
+    GpioConf |= GPIO_GPIO0_PUSH_PULL | GPIO_GPIO1_PUSH_PULL | GPIO_GPIO2_PUSH_PULL |
+                GPIO_LED1_ENABLE | GPIO_LED2_ENABLE | GPIO_LED3_ENABLE;
+
+    // Write the configuration
+    Lan9118MmioWrite32 (LAN9118_GPIO_CFG, GpioConf);
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Configure flow control
+EFI_STATUS
+ConfigureFlow (
+  UINT32 Flags,
+  UINT32 HighTrig,
+  UINT32 LowTrig,
+  UINT32 BPDuration,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  return EFI_SUCCESS;
+}
+
+// Do auto-negotiation
+EFI_STATUS
+AutoNegotiate (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 PhyControl;
+  UINT32 PhyStatus;
+  UINT32 Features;
+  UINT32 Retries;
+
+  // First check that auto-negotiation is supported
+  PhyStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS);
+  if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) {
+    DEBUG ((EFI_D_ERROR, "Auto-negotiation not supported.\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Check that link is up first
+  if ((PhyStatus & PHYSTS_LINK_STS) == 0) {
+    // Wait until it is up or until Time Out
+    Retries = FixedPcdGet32 (PcdLan9118DefaultNegotiationTimeout) / LAN9118_STALL;
+    while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_LINK_STS) == 0) {
+      gBS->Stall (LAN9118_STALL);
+      Retries--;
+      if (!Retries) {
+        DEBUG ((EFI_D_ERROR, "Link timeout in auto-negotiation.\n"));
+        return EFI_TIMEOUT;
+      }
+    }
+  }
+
+  // Configure features to advertise
+  Features = IndirectPHYRead32 (PHY_INDEX_AUTO_NEG_ADVERT);
+
+  if ((Flags & AUTO_NEGOTIATE_ADVERTISE_ALL) > 0) {
+    // Link speed capabilities
+    Features |= (PHYANA_10BASET | PHYANA_10BASETFD | PHYANA_100BASETX | PHYANA_100BASETXFD);
+
+    // Pause frame capabilities
+    Features &= ~(PHYANA_PAUSE_OP_MASK);
+    Features |= 3 << 10;
+  }
+  Features &= FixedPcdGet32 (PcdLan9118NegotiationFeatureMask);
+
+  // Write the features
+  IndirectPHYWrite32 (PHY_INDEX_AUTO_NEG_ADVERT, Features);
+
+  // Read control register
+  PhyControl = IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL);
+
+  // Enable Auto-Negotiation
+  if ((PhyControl & PHYCR_AUTO_EN) == 0) {
+    PhyControl |= PHYCR_AUTO_EN;
+  }
+
+  // Restart auto-negotiation
+  PhyControl |= PHYCR_RST_AUTO;
+
+  // Enable collision test if required to do so
+  if (Flags & AUTO_NEGOTIATE_COLLISION_TEST) {
+    PhyControl |= PHYCR_COLL_TEST;
+  } else {
+    PhyControl &= ~ PHYCR_COLL_TEST;
+  }
+
+  // Write this configuration
+  IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PhyControl);
+
+  // Wait until process has completed
+  while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0);
+
+  return EFI_SUCCESS;
+}
+
+// Check the Link Status and take appropriate action
+EFI_STATUS
+CheckLinkStatus (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  // Get the PHY Status
+  UINT32 PhyBStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS);
+
+  if (PhyBStatus & PHYSTS_LINK_STS) {
+    return EFI_SUCCESS;
+  } else {
+    return EFI_DEVICE_ERROR;
+  }
+}
+
+// Stop the transmitter
+EFI_STATUS
+StopTx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 MacCsr;
+  UINT32 TxCfg;
+
+  MacCsr = 0;
+  TxCfg = 0;
+
+  // Check if we want to clear tx
+  if (Flags & STOP_TX_CLEAR) {
+    TxCfg = Lan9118MmioRead32 (LAN9118_TX_CFG);
+    TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP;
+    Lan9118MmioWrite32 (LAN9118_TX_CFG, TxCfg);
+  }
+
+  // Check if already stopped
+  if (Flags & STOP_TX_MAC) {
+    MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
+
+    if (MacCsr & MACCR_TX_EN) {
+      MacCsr &= ~MACCR_TX_EN;
+      IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
+    }
+  }
+
+  if (Flags & STOP_TX_CFG) {
+    TxCfg = Lan9118MmioRead32 (LAN9118_TX_CFG);
+
+    if (TxCfg & TXCFG_TX_ON) {
+      TxCfg |= TXCFG_STOP_TX;
+      Lan9118MmioWrite32 (LAN9118_TX_CFG, TxCfg);
+
+      // Wait for Tx to finish transmitting
+      while (Lan9118MmioRead32 (LAN9118_TX_CFG) & TXCFG_STOP_TX);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Stop the receiver
+EFI_STATUS
+StopRx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 MacCsr;
+  UINT32 RxCfg;
+
+  RxCfg = 0;
+
+  // Check if already stopped
+  MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
+
+  if (MacCsr & MACCR_RX_EN) {
+    MacCsr &= ~ MACCR_RX_EN;
+    IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
+  }
+
+  // Check if we want to clear receiver FIFOs
+  if (Flags & STOP_RX_CLEAR) {
+    RxCfg = Lan9118MmioRead32 (LAN9118_RX_CFG);
+    RxCfg |= RXCFG_RX_DUMP;
+    Lan9118MmioWrite32 (LAN9118_RX_CFG, RxCfg);
+
+    while (Lan9118MmioRead32 (LAN9118_RX_CFG) & RXCFG_RX_DUMP);
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Start the transmitter
+EFI_STATUS
+StartTx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 MacCsr;
+  UINT32 TxCfg;
+
+  MacCsr = 0;
+  TxCfg = 0;
+
+  // Check if we want to clear tx
+  if (Flags & START_TX_CLEAR) {
+    TxCfg = Lan9118MmioRead32 (LAN9118_TX_CFG);
+    TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP;
+    Lan9118MmioWrite32 (LAN9118_TX_CFG, TxCfg);
+  }
+
+  // Check if tx was started from MAC and enable if not
+  if (Flags & START_TX_MAC) {
+    MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
+    if ((MacCsr & MACCR_TX_EN) == 0) {
+      MacCsr |= MACCR_TX_EN;
+      IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
+    }
+  }
+
+  // Check if tx was started from TX_CFG and enable if not
+  if (Flags & START_TX_CFG) {
+    TxCfg = Lan9118MmioRead32 (LAN9118_TX_CFG);
+    if ((TxCfg & TXCFG_TX_ON) == 0) {
+      TxCfg |= TXCFG_TX_ON;
+      Lan9118MmioWrite32 (LAN9118_TX_CFG, TxCfg);
+    }
+  }
+
+  // Set the tx data trigger level
+
+  return EFI_SUCCESS;
+}
+
+// Start the receiver
+EFI_STATUS
+StartRx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 MacCsr;
+  UINT32 RxCfg;
+
+  RxCfg = 0;
+
+  // Check if already started
+  MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR);
+
+  if ((MacCsr & MACCR_RX_EN) == 0) {
+    // Check if we want to clear receiver FIFOs before starting
+    if (Flags & START_RX_CLEAR) {
+      RxCfg = Lan9118MmioRead32 (LAN9118_RX_CFG);
+      RxCfg |= RXCFG_RX_DUMP;
+      Lan9118MmioWrite32 (LAN9118_RX_CFG, RxCfg);
+
+      while (Lan9118MmioRead32 (LAN9118_RX_CFG) & RXCFG_RX_DUMP);
+    }
+
+    MacCsr |= MACCR_RX_EN;
+    IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr);
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Check Tx Data available space
+UINT32
+TxDataFreeSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 TxInf;
+  UINT32 FreeSpace;
+
+  // Get the amount of free space from information register
+  TxInf = Lan9118MmioRead32 (LAN9118_TX_FIFO_INF);
+  FreeSpace = (TxInf & TXFIFOINF_TDFREE_MASK);
+
+  return FreeSpace; // Value in bytes
+}
+
+// Check Tx Status used space
+UINT32
+TxStatusUsedSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 TxInf;
+  UINT32 UsedSpace;
+
+  // Get the amount of used space from information register
+  TxInf = Lan9118MmioRead32 (LAN9118_TX_FIFO_INF);
+  UsedSpace = (TxInf & TXFIFOINF_TXSUSED_MASK) >> 16;
+
+  return UsedSpace << 2; // Value in bytes
+}
+
+// Check Rx Data used space
+UINT32
+RxDataUsedSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 RxInf;
+  UINT32 UsedSpace;
+
+  // Get the amount of used space from information register
+  RxInf = Lan9118MmioRead32 (LAN9118_RX_FIFO_INF);
+  UsedSpace = (RxInf & RXFIFOINF_RXDUSED_MASK);
+
+  return UsedSpace; // Value in bytes (rounded up to nearest DWORD)
+}
+
+// Check Rx Status used space
+UINT32
+RxStatusUsedSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 RxInf;
+  UINT32 UsedSpace;
+
+  // Get the amount of used space from information register
+  RxInf = Lan9118MmioRead32 (LAN9118_RX_FIFO_INF);
+  UsedSpace = (RxInf & RXFIFOINF_RXSUSED_MASK) >> 16;
+
+  return UsedSpace << 2; // Value in bytes
+}
+
+
+// Change the allocation of FIFOs
+EFI_STATUS
+ChangeFifoAllocation (
+  IN      UINT32 Flags,
+  IN  OUT UINTN  *TxDataSize    OPTIONAL,
+  IN  OUT UINTN  *RxDataSize    OPTIONAL,
+  IN  OUT UINT32 *TxStatusSize  OPTIONAL,
+  IN  OUT UINT32 *RxStatusSize  OPTIONAL,
+  IN  OUT EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  )
+{
+  UINT32 HwConf;
+  UINT32 TxFifoOption;
+
+  // Check that desired sizes don't exceed limits
+  if (*TxDataSize > TX_FIFO_MAX_SIZE)
+    return EFI_INVALID_PARAMETER;
+
+#if defined(RX_FIFO_MIN_SIZE) && defined(RX_FIFO_MAX_SIZE)
+  if (*RxDataSize > RX_FIFO_MAX_SIZE) {
+    return EFI_INVALID_PARAMETER;
+  }
+#endif
+
+  if (Flags & ALLOC_USE_DEFAULT) {
+    return EFI_SUCCESS;
+  }
+
+  // If we use the FIFOs (always use this first)
+  if (Flags & ALLOC_USE_FIFOS) {
+    // Read the current value of allocation
+    HwConf = Lan9118MmioRead32 (LAN9118_HW_CFG);
+    TxFifoOption = (HwConf >> 16) & 0xF;
+
+    // Choose the correct size (always use larger than requested if possible)
+    if (*TxDataSize < TX_FIFO_MIN_SIZE) {
+      *TxDataSize = TX_FIFO_MIN_SIZE;
+      *RxDataSize = 13440;
+      *RxStatusSize = 896;
+      TxFifoOption = 2;
+    } else if ((*TxDataSize > TX_FIFO_MIN_SIZE) && (*TxDataSize <= 2560)) {
+      *TxDataSize = 2560;
+      *RxDataSize = 12480;
+      *RxStatusSize = 832;
+      TxFifoOption = 3;
+    } else if ((*TxDataSize > 2560) && (*TxDataSize <= 3584)) {
+      *TxDataSize = 3584;
+      *RxDataSize = 11520;
+      *RxStatusSize = 768;
+      TxFifoOption = 4;
+    } else if ((*TxDataSize > 3584) && (*TxDataSize <= 4608)) { // default option
+      *TxDataSize = 4608;
+      *RxDataSize = 10560;
+      *RxStatusSize = 704;
+      TxFifoOption = 5;
+    } else if ((*TxDataSize > 4608) && (*TxDataSize <= 5632)) {
+      *TxDataSize = 5632;
+      *RxDataSize = 9600;
+      *RxStatusSize = 640;
+      TxFifoOption = 6;
+    } else if ((*TxDataSize > 5632) && (*TxDataSize <= 6656)) {
+      *TxDataSize = 6656;
+      *RxDataSize = 8640;
+      *RxStatusSize = 576;
+      TxFifoOption = 7;
+    } else if ((*TxDataSize > 6656) && (*TxDataSize <= 7680)) {
+      *TxDataSize = 7680;
+      *RxDataSize = 7680;
+      *RxStatusSize = 512;
+      TxFifoOption = 8;
+    } else if ((*TxDataSize > 7680) && (*TxDataSize <= 8704)) {
+      *TxDataSize = 8704;
+      *RxDataSize = 6720;
+      *RxStatusSize = 448;
+      TxFifoOption = 9;
+    } else if ((*TxDataSize > 8704) && (*TxDataSize <= 9728)) {
+      *TxDataSize = 9728;
+      *RxDataSize = 5760;
+      *RxStatusSize = 384;
+      TxFifoOption = 10;
+    } else if ((*TxDataSize > 9728) && (*TxDataSize <= 10752)) {
+      *TxDataSize = 10752;
+      *RxDataSize = 4800;
+      *RxStatusSize = 320;
+      TxFifoOption = 11;
+    } else if ((*TxDataSize > 10752) && (*TxDataSize <= 11776)) {
+      *TxDataSize = 11776;
+      *RxDataSize = 3840;
+      *RxStatusSize = 256;
+      TxFifoOption = 12;
+    } else if ((*TxDataSize > 11776) && (*TxDataSize <= 12800)) {
+      *TxDataSize = 12800;
+      *RxDataSize = 2880;
+      *RxStatusSize = 192;
+      TxFifoOption = 13;
+    } else if ((*TxDataSize > 12800) && (*TxDataSize <= 13824)) {
+      *TxDataSize = 13824;
+      *RxDataSize = 1920;
+      *RxStatusSize = 128;
+      TxFifoOption = 14;
+    }
+  } else {
+    ASSERT(0); // Untested code path
+    HwConf = 0;
+    TxFifoOption = 0;
+  }
+
+  // Do we need DMA?
+  if (Flags & ALLOC_USE_DMA) {
+    return EFI_UNSUPPORTED; // Unsupported as of now
+  }
+  // Clear and assign the new size option
+  HwConf &= ~(0xF0000);
+  HwConf |= ((TxFifoOption & 0xF) << 16);
+  Lan9118MmioWrite32 (LAN9118_HW_CFG, HwConf);
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h
new file mode 100644
index 000000000000..601714c94387
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h
@@ -0,0 +1,283 @@
+/** @file
+*
+*  WARNING:
+*  This driver fails to follow the UEFI driver model without a good
+*  reason, and only remains in the tree because it is still used by
+*  a small number of platforms. It will be removed when no longer used.
+*  New platforms should not use it, and no one should use this as
+*  reference code for developing new drivers.
+*
+*  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#ifndef __LAN9118_DXE_UTIL_H__
+#define __LAN9118_DXE_UTIL_H__
+
+// Most common CRC32 Polynomial for little endian machines
+#define CRC_POLYNOMIAL               0xEDB88320
+
+/**
+  This internal function reverses bits for 32bit data.
+
+  @param  Value                 The data to be reversed.
+
+  @return                       Data reversed.
+
+**/
+UINT32
+ReverseBits (
+  UINT32  Value
+  );
+
+// Create an Ethernet CRC
+UINT32
+GenEtherCrc32 (
+  IN    EFI_MAC_ADDRESS *Mac,
+  IN    UINT32 AddrLen
+  );
+
+UINT32
+Lan9118RawMmioRead32(
+  UINTN Address,
+  UINTN Delay
+  );
+#define Lan9118MmioRead32(a) \
+	Lan9118RawMmioRead32(a, a ## _RD_DELAY)
+
+UINT32
+Lan9118RawMmioWrite32(
+  UINTN Address,
+  UINT32 Value,
+  UINTN Delay
+  );
+#define Lan9118MmioWrite32(a, v) \
+	Lan9118RawMmioWrite32(a, v, a ## _WR_DELAY)
+
+/* ------------------ MAC CSR Access ------------------- */
+
+// Read from MAC indirect registers
+UINT32
+IndirectMACRead32 (
+  UINT32 Index
+  );
+
+
+// Write to indirect registers
+UINT32
+IndirectMACWrite32 (
+  UINT32 Index,
+  UINT32 Value
+  );
+
+
+/* --------------- PHY Registers Access ---------------- */
+
+// Read from MII register (PHY Access)
+UINT32
+IndirectPHYRead32(
+  UINT32 Index
+  );
+
+
+// Write to the MII register (PHY Access)
+UINT32
+IndirectPHYWrite32(
+  UINT32 Index,
+  UINT32 Value
+  );
+
+/* ---------------- EEPROM Operations ------------------ */
+
+// Read from EEPROM memory
+UINT32
+IndirectEEPROMRead32 (
+  UINT32 Index
+  );
+
+// Write to EEPROM memory
+UINT32
+IndirectEEPROMWrite32 (
+  UINT32 Index,
+  UINT32 Value
+  );
+
+/* ---------------- General Operations ----------------- */
+
+VOID
+Lan9118SetMacAddress (
+  EFI_MAC_ADDRESS             *Mac,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Initialise the LAN9118
+EFI_STATUS
+Lan9118Initialize (
+  IN  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Flags for software reset
+#define SOFT_RESET_CHECK_MAC_ADDR_LOAD                  BIT0
+#define SOFT_RESET_CLEAR_INT                            BIT1
+#define SOFT_RESET_SELF_TEST                            BIT2
+
+// Perform software reset on the LAN9118
+EFI_STATUS
+SoftReset (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Flags for PHY reset
+#define PHY_RESET_PMT                                   BIT0
+#define PHY_RESET_BCR                                   BIT1
+#define PHY_SOFT_RESET_CLEAR_INT                        BIT2
+
+// Perform PHY software reset
+EFI_STATUS
+PhySoftReset (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Flags for Hardware configuration
+#define HW_CONF_USE_LEDS                                BIT0
+
+// Configure hardware for LAN9118
+EFI_STATUS
+ConfigureHardware (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Configure flow control
+EFI_STATUS
+ConfigureFlow (
+  UINT32 Flags,
+  UINT32 HighTrig,
+  UINT32 LowTrig,
+  UINT32 BPDuration,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Flags for auto negotiation
+#define AUTO_NEGOTIATE_COLLISION_TEST         BIT0
+#define AUTO_NEGOTIATE_ADVERTISE_ALL          BIT1
+
+// Do auto-negotiation
+EFI_STATUS
+AutoNegotiate (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Check the Link Status and take appropriate action
+EFI_STATUS
+CheckLinkStatus (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Stop transmitter flags
+#define STOP_TX_MAC                       BIT0
+#define STOP_TX_CFG                       BIT1
+#define STOP_TX_CLEAR                     BIT2
+
+// Stop the transmitter
+EFI_STATUS
+StopTx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Stop receiver flags
+#define STOP_RX_CLEAR                     BIT0
+
+// Stop the receiver
+EFI_STATUS
+StopRx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Start transmitter flags
+#define START_TX_MAC                      BIT0
+#define START_TX_CFG                      BIT1
+#define START_TX_CLEAR                    BIT2
+
+// Start the transmitter
+EFI_STATUS
+StartTx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Stop receiver flags
+#define START_RX_CLEAR                     BIT0
+
+// Start the receiver
+EFI_STATUS
+StartRx (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Check Tx Data available space
+UINT32
+TxDataFreeSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Check Tx Status used space
+UINT32
+TxStatusUsedSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Check Rx Data used space
+UINT32
+RxDataUsedSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+// Check Rx Status used space
+UINT32
+RxStatusUsedSpace (
+  UINT32 Flags,
+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+
+// Flags for FIFO allocation
+#define ALLOC_USE_DEFAULT                 BIT0
+#define ALLOC_USE_FIFOS                   BIT1
+#define ALLOC_USE_DMA                     BIT2
+
+// FIFO min and max sizes
+#define TX_FIFO_MIN_SIZE            0x00000600
+#define TX_FIFO_MAX_SIZE            0x00003600
+//#define RX_FIFO_MIN_SIZE
+//#define RX_FIFO_MAX_SIZE
+
+// Change the allocation of FIFOs
+EFI_STATUS
+ChangeFifoAllocation (
+  IN      UINT32 Flags,
+  IN  OUT UINTN  *TxDataSize    OPTIONAL,
+  IN  OUT UINTN  *RxDataSize    OPTIONAL,
+  IN  OUT UINT32 *TxStatusSize  OPTIONAL,
+  IN  OUT UINT32 *RxStatusSize  OPTIONAL,
+  IN  OUT EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+  );
+
+VOID
+Lan9118ReadMacAddress (
+  OUT EFI_MAC_ADDRESS *Mac
+  );
+
+#endif // __LAN9118_DXE_UTIL_H__
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
                   ` (6 preceding siblings ...)
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver Ard Biesheuvel
@ 2020-04-30 17:16 ` Ard Biesheuvel
  2020-05-04 11:00   ` Leif Lindholm
  2020-05-04 11:00 ` [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Leif Lindholm
  8 siblings, 1 reply; 20+ messages in thread
From: Ard Biesheuvel @ 2020-04-30 17:16 UTC (permalink / raw)
  To: devel; +Cc: leif, Ard Biesheuvel

Incorporate the ISP 1761 USB host driver from EmbeddedPkg, which is
only used on obsolete ARM development platforms and does not follow
the UEFI driver model. This will allow us to drop it from the core EDK2
repository.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
---
 Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc                |   2 +-
 Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf                |   2 +-
 Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc                     |   2 +-
 Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                      |   3 +
 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c   | 636 ++++++++++++++++++++
 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h   | 123 ++++
 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf |  39 ++
 7 files changed, 804 insertions(+), 3 deletions(-)

diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
index bee7913feb52..144dd4f8b8e9 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
@@ -157,7 +157,7 @@ [PcdsFixedAtBuild.common]
   gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase|0x2C002000
 
   # ISP1761 USB OTG Controller
-  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
+  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
 
   # Ethernet (SMSC LAN9118)
   gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x1A000000
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
index b133375e1a11..f98de162e634 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
+++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
@@ -126,7 +126,7 @@ [FV.FvMain]
   #
   # USB support
   #
-  INF EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
+  INF Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
 
   #
   # Android Fastboot
diff --git a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
index 912ad5e5a1ec..bde3437b56d7 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
+++ b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
@@ -455,4 +455,4 @@ [Components.common]
 [Components.ARM]
 
   # ISP1761 USB OTG Controller
-  EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
+  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
diff --git a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
index e42905aabf2b..f78c5ce7c764 100644
--- a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+++ b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
@@ -67,3 +67,6 @@ [PcdsFixedAtBuild.common]
   # The default feature mask below disables full duplex negotiation, since full
   # duplex operation is suspected to be broken in the driver.
   gArmVExpressTokenSpaceGuid.PcdLan9118NegotiationFeatureMask|0xFFFFFEBF|UINT32|0x00000028
+
+  # ISP1761 USB OTG Controller
+  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT32|0x00000029
diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
new file mode 100644
index 000000000000..c23c0ecf737d
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
@@ -0,0 +1,636 @@
+/** @file
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <IndustryStandard/Usb.h>
+
+#include <Protocol/UsbDevice.h>
+
+#include "Isp1761UsbDxe.h"
+
+/*
+  Driver for using the NXP ISP1761 as a USB Peripheral controller.
+  Doesn't use USB OTG - just sets it in Pure Peripheral mode.
+
+  The ISP1582 datasheet has a little more info on the Peripheral controller
+  registers than the ISP1761 datasheet
+
+  We don't do string descriptors. They're optional.
+  We currently assume the device has one configuration, one interface, one IN
+  endpoint, and one OUT endpoint (plus the default control endpoint).
+
+  In fact, this driver is the minimum required to implement fastboot.
+*/
+
+// TODO Make sure the controller isn't sending empty packets when it shouldn't
+// (check behaviour in cases when Buffer Length isn't explicitly set)
+
+// ISP1582 Datasheet:
+// "Data transfers preceding the status stage must first be fully
+// completed before the STATUS bit can be set."
+// This variable stores whether some control data has been pended in the EP0TX
+// Tx buffer, so that when an EP0TX interrupt is received we can set the STATUS
+// bit to go to the Status stage of the control transfer.
+STATIC BOOLEAN mControlTxPending = FALSE;
+
+STATIC USB_DEVICE_DESCRIPTOR    *mDeviceDescriptor;
+
+// The config descriptor, interface descriptor, and endpoint descriptors in a
+// buffer (in that order)
+STATIC VOID                     *mDescriptors;
+// Convenience pointers to those descriptors inside the buffer:
+STATIC USB_INTERFACE_DESCRIPTOR *mInterfaceDescriptor;
+STATIC USB_CONFIG_DESCRIPTOR    *mConfigDescriptor;
+STATIC USB_ENDPOINT_DESCRIPTOR  *mEndpointDescriptors;
+
+STATIC USB_DEVICE_RX_CALLBACK   mDataReceivedCallback;
+STATIC USB_DEVICE_TX_CALLBACK   mDataSentCallback;
+
+// The time between interrupt polls, in units of 100 nanoseconds
+// 10 Microseconds
+#define ISP1761_INTERRUPT_POLL_PERIOD 10000
+
+STATIC
+VOID
+SelectEndpoint (
+  IN UINT8 Endpoint
+  )
+{
+  // The DMA Endpoint Index must not point to the same as the
+  // Endpoint Index Register.
+  WRITE_REG32 (ISP1761_DMA_ENDPOINT_INDEX, ((Endpoint + 2) % ISP1761_NUM_ENDPOINTS));
+  WRITE_REG32 (ISP1761_ENDPOINT_INDEX, Endpoint);
+}
+
+// Enable going to the Data stage of a control transfer
+STATIC
+VOID
+DataStageEnable (
+  IN UINT8 Endpoint
+  )
+{
+  SelectEndpoint (Endpoint);
+  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_DSEN);
+}
+
+// Go to the Status stage of a successful control transfer
+STATIC
+VOID
+StatusAcknowledge (
+  IN UINT8 Endpoint
+)
+{
+  SelectEndpoint (Endpoint);
+  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_STATUS);
+}
+
+// Read the FIFO for the endpoint indexed by Endpoint, into the buffer pointed
+// at by Buffer, whose size is *Size bytes.
+//
+// If *Size is less than the number of bytes in the FIFO, return EFI_BUFFER_TOO_SMALL
+//
+// Update *Size with the number of bytes of data in the FIFO.
+STATIC
+EFI_STATUS
+ReadEndpointBuffer (
+  IN      UINT8   Endpoint,
+  IN OUT  UINTN  *Size,
+  IN OUT  VOID   *Buffer
+  )
+{
+  UINT16  NumBytesAvailable;
+  UINT32  Val32;
+  UINTN   Index;
+  UINTN   NumBytesRead;
+
+  SelectEndpoint (Endpoint);
+
+  NumBytesAvailable = READ_REG16 (ISP1761_BUFFER_LENGTH);
+
+  if (NumBytesAvailable > *Size) {
+    *Size = NumBytesAvailable;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+  *Size = NumBytesAvailable;
+
+  /* -- NB! --
+    The datasheet says the Data Port is 16 bits but it actually appears to
+    be 32 bits.
+   */
+
+  // Read 32-bit chunks
+  for (Index = 0; Index < NumBytesAvailable / 4; Index++) {
+    ((UINT32 *) Buffer)[Index] = READ_REG32 (ISP1761_DATA_PORT);
+  }
+
+  // Read remaining bytes
+
+  // Round NumBytesAvailable down to nearest power of 4
+  NumBytesRead = NumBytesAvailable & (~0x3);
+  if (NumBytesRead != NumBytesAvailable) {
+    Val32 = READ_REG32 (ISP1761_DATA_PORT);
+    // Copy each required byte of 32-bit word into buffer
+    for (Index = 0; Index < NumBytesAvailable % 4; Index++) {
+      ((UINT8 *) Buffer)[NumBytesRead + Index] = Val32 >> (Index * 8);
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/*
+  Write an endpoint buffer. Parameters:
+  Endpoint        Endpoint index (see Endpoint Index Register in datasheet)
+  MaxPacketSize   The MaxPacketSize this endpoint is configured for
+  Size            The size of the Buffer
+  Buffer          The data
+
+  Assumes MaxPacketSize is a multiple of 4.
+  (It seems that all valid values for MaxPacketSize _are_ multiples of 4)
+*/
+STATIC
+EFI_STATUS
+WriteEndpointBuffer (
+  IN       UINT8   Endpoint,
+  IN       UINTN   MaxPacketSize,
+  IN       UINTN   Size,
+  IN CONST VOID   *Buffer
+  )
+{
+  UINTN    Index;
+  UINT32  *DwordBuffer;
+
+  DwordBuffer = (UINT32 *) Buffer;
+  SelectEndpoint (Endpoint);
+
+  /* -- NB! --
+    The datasheet says the Data Port is 16 bits but it actually appears to
+    be 32 bits.
+   */
+
+  // Write packets of size MaxPacketSize
+  while (Size > MaxPacketSize) {
+    for (Index = 0; Index < MaxPacketSize / 4; Index++) {
+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[Index]);
+    }
+    Size -= MaxPacketSize;
+    DwordBuffer += (MaxPacketSize / sizeof (UINT32));
+  }
+
+  // Write remaining data
+
+  if (Size > 0) {
+    WRITE_REG32 (ISP1761_BUFFER_LENGTH, Size);
+
+    while (Size > 4) {
+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);
+      Size -= 4;
+      DwordBuffer++;
+    }
+
+    if (Size > 0) {
+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+HandleGetDescriptor (
+  IN USB_DEVICE_REQUEST  *Request
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       DescriptorType;
+  UINTN       ResponseSize;
+  VOID       *ResponseData;
+
+  ResponseSize = 0;
+  ResponseData = NULL;
+  Status = EFI_SUCCESS;
+
+  // Pretty confused if bmRequestType is anything but this:
+  ASSERT (Request->RequestType == USB_DEV_GET_DESCRIPTOR_REQ_TYPE);
+
+  // Choose the response
+  DescriptorType = Request->Value >> 8;
+  switch (DescriptorType) {
+  case USB_DESC_TYPE_DEVICE:
+    DEBUG ((EFI_D_INFO, "USB: Got a request for device descriptor\n"));
+    ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR);
+    ResponseData = mDeviceDescriptor;
+    break;
+  case USB_DESC_TYPE_CONFIG:
+    DEBUG ((EFI_D_INFO, "USB: Got a request for config descriptor\n"));
+    ResponseSize = mConfigDescriptor->TotalLength;
+    ResponseData = mDescriptors;
+    break;
+  case USB_DESC_TYPE_STRING:
+    DEBUG ((EFI_D_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF));
+    break;
+  default:
+    DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));
+    Status = EFI_NOT_FOUND;
+    break;
+  }
+
+  // Send the response
+  if (ResponseData) {
+    ASSERT (ResponseSize != 0);
+
+    if (Request->Length < ResponseSize) {
+      // Truncate response
+      ResponseSize = Request->Length;
+    } else if (Request->Length > ResponseSize) {
+      DEBUG ((EFI_D_INFO, "USB: Info: ResponseSize < wLength\n"));
+    }
+
+    DataStageEnable (ISP1761_EP0TX);
+    Status = WriteEndpointBuffer (
+              ISP1761_EP0TX,
+              MAX_PACKET_SIZE_CONTROL,
+              ResponseSize,
+              ResponseData
+              );
+    if (!EFI_ERROR (Status)) {
+      // Setting this value should cause us to go to the Status stage on the
+      // next EP0TX interrupt
+      mControlTxPending = TRUE;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+HandleSetAddress (
+  IN USB_DEVICE_REQUEST  *Request
+  )
+{
+  // Pretty confused if bmRequestType is anything but this:
+  ASSERT (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE);
+  // USB Spec: "The USB device does not change its device address until after
+  // the Status stage of this request is completed successfully."
+  // ISP1582 datasheet: "The new device address is activated when the
+  // device receives an acknowledgment from the host for the empty packet
+  // token". (StatusAcknowledge causes an empty packet to be sent).
+  // So, we write the Address register _before_ acking the SET_ADDRESS.
+  DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));
+  WRITE_REG32 (ISP1761_ADDRESS, Request->Value | ISP1761_ADDRESS_DEVEN);
+  StatusAcknowledge (ISP1761_EP0TX);
+
+  return EFI_SUCCESS;
+}
+
+// Move the device to the Configured state.
+// (This code only supports one configuration for a device, so the configuration
+//  index is ignored)
+STATIC
+EFI_STATUS
+HandleSetConfiguration (
+  IN USB_DEVICE_REQUEST  *Request
+  )
+{
+  USB_ENDPOINT_DESCRIPTOR  *EPDesc;
+  UINTN                     Index;
+  UINT8                     EndpointIndex;
+
+  ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);
+  DEBUG ((EFI_D_INFO, "USB: Setting configuration.\n"));
+
+  // Configure endpoints
+  for (Index = 0; Index < mInterfaceDescriptor->NumEndpoints; Index++) {
+    EPDesc = &mEndpointDescriptors[Index];
+
+    // To simplify for now, assume endpoints aren't "sparse", and are in order.
+    ASSERT ((EPDesc->EndpointAddress & 0xF) == ((Index / 2) + 1));
+
+    // Convert from USB endpoint index to ISP1761 endpoint Index
+    // USB:     Endpoint number is bits [3:0], IN/OUT is bit [7]
+    // ISP1761: Endpoint number is bits [4:1], IN/OUT is bit [0]
+    EndpointIndex = ((EPDesc->EndpointAddress & 0xF) << 1) |
+                    ((EPDesc->EndpointAddress & BIT7) >> 7);
+    SelectEndpoint (EndpointIndex);
+    // Set endpoint type (Bulk/Isochronous/Interrupt)
+    WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, EPDesc->MaxPacketSize);
+    // Hardware foible (bug?): Although the datasheet seems to suggest it should
+    // automatically be set to MaxPacketSize, the Buffer Length register appears
+    // to be reset to 0, which causes an empty packet to be sent in response to
+    // the first IN token of the session. The NOEMPKT field of the Endpoint Type
+    // register sounds like it might fix this problem, but it doesn't
+    // (it's "applicable only in the DMA mode").
+    WRITE_REG32 (ISP1761_BUFFER_LENGTH, EPDesc->MaxPacketSize);
+    WRITE_REG32 (ISP1761_ENDPOINT_TYPE, (EPDesc->Attributes & 0x3) |
+                                        ISP1761_ENDPOINT_TYPE_ENABLE);
+  }
+
+  StatusAcknowledge (ISP1761_EP0TX);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+HandleDeviceRequest (
+  IN USB_DEVICE_REQUEST  *Request
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = EFI_SUCCESS;
+
+  switch (Request->Request) {
+  case USB_DEV_GET_DESCRIPTOR:
+    Status = HandleGetDescriptor (Request);
+    break;
+  case USB_DEV_SET_ADDRESS:
+    Status = HandleSetAddress (Request);
+    break;
+  case USB_DEV_SET_CONFIGURATION:
+    Status = HandleSetConfiguration (Request);
+    break;
+  default:
+    DEBUG ((EFI_D_ERROR,
+      "Didn't understand RequestType 0x%x Request 0x%x\n",
+      Request->RequestType, Request->Request));
+      Status = EFI_INVALID_PARAMETER;
+    break;
+  }
+
+  return Status;
+}
+
+// Instead of actually registering interrupt handlers, we poll the controller's
+//  interrupt source register in this function.
+STATIC
+VOID
+CheckInterrupts (
+  IN EFI_EVENT  Event,
+  IN VOID      *Context
+  )
+{
+  UINT32      DcInterrupts;
+  UINTN       NumBytes;
+  UINTN       MoreBytes;
+  UINT8       Packet[512];
+  VOID       *DataPacket;
+  UINT32      HandledInterrupts;
+  UINT32      UnhandledInterrupts;
+  EFI_STATUS  Status;
+
+  // Set bits in HandledInterrupts to mark the interrupt source handled.
+  HandledInterrupts = 0;
+
+  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);
+
+  DcInterrupts = READ_REG32 (ISP1761_DC_INTERRUPT);
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_SUSP) {
+    DEBUG ((EFI_D_INFO, "USB: Suspend\n"));
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_SUSP;
+  }
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_RESUME) {
+    DEBUG ((EFI_D_INFO, "USB: Resume\n"));
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_RESUME;
+  }
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0SETUP) {
+    NumBytes = 512;
+    ReadEndpointBuffer (0x20, &NumBytes, &Packet);
+    ASSERT (NumBytes == 8);
+    HandleDeviceRequest ((USB_DEVICE_REQUEST *) Packet);
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0SETUP;
+  }
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0RX) {
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0RX;
+  }
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0TX) {
+    if (mControlTxPending) {
+      // We previously put some data in the Control Endpoint's IN (Tx) FIFO.
+      // We assume that that data has now been sent in response to the IN token
+      // that triggered this interrupt. We can therefore go to the Status stage
+      // of the control transfer.
+      StatusAcknowledge (ISP1761_EP0TX);
+      mControlTxPending = FALSE;
+    }
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0TX;
+  }
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1RX) {
+    NumBytes = 512;
+    DataPacket = AllocatePool (NumBytes);
+    Status = ReadEndpointBuffer (ISP1761_EP1RX, &NumBytes, DataPacket);
+    if (EFI_ERROR (Status) || NumBytes == 0) {
+      if (EFI_ERROR (Status)) {
+        DEBUG ((EFI_D_ERROR, "Couldn't read EP1RX data: %r\n", Status));
+      }
+      FreePool (DataPacket);
+    } else {
+      // Signal this event again so we poll again ASAP
+      gBS->SignalEvent (Event);
+      mDataReceivedCallback (NumBytes, DataPacket);
+    }
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1RX;
+  }
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1TX) {
+    mDataSentCallback (1);
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1TX;
+  }
+  if (DcInterrupts & (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF)) {
+    // Don't care about SOFs or pseudo-SOFs
+    HandledInterrupts |= (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF);
+  }
+  if (ISP1761_DC_INTERRUPT_BRESET) {
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_BRESET;
+  }
+  if (ISP1761_DC_INTERRUPT_HS_STAT) {
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_HS_STAT;
+  }
+  if (ISP1761_DC_INTERRUPT_VBUS) {
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_VBUS;
+  }
+
+  UnhandledInterrupts = DcInterrupts & (~HandledInterrupts) & ISP1761_DC_INTERRUPT_MASK;
+  if (UnhandledInterrupts) {
+    DEBUG ((EFI_D_ERROR, "USB: Unhandled DC Interrupts: 0x%08x\n",
+      UnhandledInterrupts));
+  }
+
+  // Check if we received any more data while we were handling the interrupt.
+  SelectEndpoint (ISP1761_EP1RX);
+  MoreBytes = READ_REG16 (ISP1761_BUFFER_LENGTH);
+  if (MoreBytes) {
+    HandledInterrupts &= ~ISP1761_DC_INTERRUPT_EP1RX;
+  }
+
+  WRITE_REG32 (ISP1761_DC_INTERRUPT, HandledInterrupts);
+}
+
+EFI_STATUS
+Isp1761PeriphSend (
+  IN        UINT8  EndpointIndex,
+  IN        UINTN  Size,
+  IN  CONST VOID  *Buffer
+  )
+{
+  return WriteEndpointBuffer (
+          (EndpointIndex << 1) | 0x1, //Convert to ISP1761 endpoint index, Tx
+          MAX_PACKET_SIZE_BULK,
+          Size,
+          Buffer
+          );
+}
+
+EFI_STATUS
+EFIAPI
+Isp1761PeriphStart (
+  IN USB_DEVICE_DESCRIPTOR   *DeviceDescriptor,
+  IN VOID                   **Descriptors,
+  IN USB_DEVICE_RX_CALLBACK   RxCallback,
+  IN USB_DEVICE_TX_CALLBACK   TxCallback
+  )
+{
+  UINT16                    OtgStatus;
+  UINT8                    *Ptr;
+  EFI_STATUS                Status;
+  EFI_EVENT                 TimerEvent;
+
+  ASSERT (DeviceDescriptor != NULL);
+  ASSERT (Descriptors[0] != NULL);
+  ASSERT (RxCallback != NULL);
+  ASSERT (TxCallback != NULL);
+
+  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);
+
+  WRITE_REG32 (ISP1761_SW_RESET_REG, ISP1761_SW_RESET_ALL);
+  while (READ_REG32 (ISP1761_SW_RESET_REG) & ISP1761_SW_RESET_ALL) {
+    //busy wait
+  }
+  WRITE_REG32 (ISP1761_MODE, ISP1761_MODE_SFRESET);
+  while (READ_REG32 (ISP1761_MODE) & ISP1761_MODE_SFRESET) {
+    //busy wait
+  }
+  DEBUG ((EFI_D_INFO, "USB: Software reset done\n"));
+
+  WRITE_REG32 (ISP1761_DC_INTERRUPT_ENABLE, 0x03FFFFFF);
+  WRITE_REG32 (ISP1761_OTG_INTERRUPT_ENABLE_RISE, 0x07FF);
+
+  WRITE_REG8 (ISP1761_ADDRESS, ISP1761_ADDRESS_DEVEN);
+  WRITE_REG8 (ISP1761_MODE, ISP1761_MODE_WKUPCS | ISP1761_MODE_CLKAON);
+
+  // Use port 1 as peripheral controller (magic - disagrees with datasheet)
+  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0xffff0000);
+  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0x000014d1);
+
+  OtgStatus = READ_REG16 (ISP1761_OTG_STATUS);
+  if ((OtgStatus & ISP1761_OTG_STATUS_B_SESS_END) != 0) {
+    DEBUG ((EFI_D_ERROR, "USB: Vbus not powered.\n"));
+  }
+  if ((OtgStatus & ISP1761_OTG_STATUS_A_B_SESS_VLD) == 0) {
+    DEBUG ((EFI_D_ERROR, "USB: Session not valid.\n"));
+  }
+
+  // Configure Control endpoints
+  SelectEndpoint (0x20);
+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);
+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);
+  SelectEndpoint (0x0);
+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);
+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);
+  SelectEndpoint (0x1);
+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);
+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);
+
+  // Interrupt on all ACK and NAK
+  WRITE_REG32 (ISP1761_INTERRUPT_CONFIG, ISP1761_INTERRUPT_CONFIG_ACK_ONLY);
+
+  mDeviceDescriptor = DeviceDescriptor;
+  mDescriptors = Descriptors[0];
+
+  // Right now we just support one configuration
+  ASSERT (mDeviceDescriptor->NumConfigurations == 1);
+  // ... and one interface
+  mConfigDescriptor = (USB_CONFIG_DESCRIPTOR *)mDescriptors;
+  ASSERT (mConfigDescriptor->NumInterfaces == 1);
+
+  Ptr = ((UINT8 *) mDescriptors) + sizeof (USB_CONFIG_DESCRIPTOR);
+  mInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR *) Ptr;
+  Ptr += sizeof (USB_INTERFACE_DESCRIPTOR);
+
+  mEndpointDescriptors = (USB_ENDPOINT_DESCRIPTOR *) Ptr;
+
+  mDataReceivedCallback = RxCallback;
+  mDataSentCallback = TxCallback;
+
+  // Register a timer event so CheckInterrupts gets called periodically
+  Status = gBS->CreateEvent (
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  CheckInterrupts,
+                  NULL,
+                  &TimerEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->SetTimer (
+                  TimerEvent,
+                  TimerPeriodic,
+                  ISP1761_INTERRUPT_POLL_PERIOD
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+USB_DEVICE_PROTOCOL mUsbDevice = {
+  Isp1761PeriphStart,
+  Isp1761PeriphSend
+};
+
+
+EFI_STATUS
+EFIAPI
+Isp1761PeriphEntryPoint (
+  IN EFI_HANDLE                            ImageHandle,
+  IN EFI_SYSTEM_TABLE                      *SystemTable
+  )
+{
+  UINT32      DeviceId;
+  EFI_HANDLE  Handle;
+
+  DeviceId = READ_REG32 (ISP1761_DEVICE_ID);
+
+  if (DeviceId != ISP1761_DEVICE_ID_VAL) {
+    DEBUG ((EFI_D_ERROR,
+      "ERROR: Read incorrect device ID for ISP1761: 0x%08x, expected 0x%08x\n",
+      DeviceId , ISP1761_DEVICE_ID_VAL
+      ));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Handle = NULL;
+  return gBS->InstallProtocolInterface (
+    &Handle,
+    &gUsbDeviceProtocolGuid,
+    EFI_NATIVE_INTERFACE,
+    &mUsbDevice
+    );
+}
diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
new file mode 100644
index 000000000000..f7155d48d8ad
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
@@ -0,0 +1,123 @@
+/** @file
+
+  WARNING:
+  This driver fails to follow the UEFI driver model without a good
+  reason, and only remains in the tree because it is still used by
+  a small number of platforms. It will be removed when no longer used.
+  New platforms should not use it, and no one should use this as
+  reference code for developing new drivers.
+
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __ISP1761_USB_DXE_H__
+#define __ISP1761_USB_DXE_H__
+
+#define ISP1761_USB_BASE FixedPcdGet32 (PcdIsp1761BaseAddress)
+
+#define READ_REG32(Offset) MmioRead32 (ISP1761_USB_BASE + Offset)
+#define READ_REG16(Offset) (UINT16) READ_REG32 (Offset)
+#define WRITE_REG32(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, Val)
+#define WRITE_REG16(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)
+#define WRITE_REG8(Offset, Val)   MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)
+
+// Max packet size in bytes (For Full Speed USB 64 is the only valid value)
+#define MAX_PACKET_SIZE_CONTROL     64
+
+#define MAX_PACKET_SIZE_BULK        512
+
+// 8 Endpoints, in and out. Don't count the Endpoint 0 setup buffer
+#define ISP1761_NUM_ENDPOINTS               16
+
+// Endpoint Indexes
+#define ISP1761_EP0SETUP                    0x20
+#define ISP1761_EP0RX                       0x00
+#define ISP1761_EP0TX                       0x01
+#define ISP1761_EP1RX                       0x02
+#define ISP1761_EP1TX                       0x03
+
+// DcInterrupt bits
+#define ISP1761_DC_INTERRUPT_BRESET         BIT0
+#define ISP1761_DC_INTERRUPT_SOF            BIT1
+#define ISP1761_DC_INTERRUPT_PSOF           BIT2
+#define ISP1761_DC_INTERRUPT_SUSP           BIT3
+#define ISP1761_DC_INTERRUPT_RESUME         BIT4
+#define ISP1761_DC_INTERRUPT_HS_STAT        BIT5
+#define ISP1761_DC_INTERRUPT_DMA            BIT6
+#define ISP1761_DC_INTERRUPT_VBUS           BIT7
+#define ISP1761_DC_INTERRUPT_EP0SETUP       BIT8
+#define ISP1761_DC_INTERRUPT_EP0RX          BIT10
+#define ISP1761_DC_INTERRUPT_EP0TX          BIT11
+#define ISP1761_DC_INTERRUPT_EP1RX          BIT12
+#define ISP1761_DC_INTERRUPT_EP1TX          BIT13
+// All valid peripheral controller interrupts
+#define ISP1761_DC_INTERRUPT_MASK           0x003FFFDFF
+
+#define ISP1761_ADDRESS                     0x200
+#define ISP1761_ADDRESS_DEVEN               BIT7
+
+#define ISP1761_MODE                        0x20C
+#define ISP1761_MODE_DATA_BUS_WIDTH         BIT8
+#define ISP1761_MODE_CLKAON                 BIT7
+#define ISP1761_MODE_SFRESET                BIT4
+#define ISP1761_MODE_WKUPCS                 BIT2
+
+#define ISP1761_ENDPOINT_MAX_PACKET_SIZE    0x204
+
+#define ISP1761_ENDPOINT_TYPE               0x208
+#define ISP1761_ENDPOINT_TYPE_NOEMPKT       BIT4
+#define ISP1761_ENDPOINT_TYPE_ENABLE        BIT3
+
+#define ISP1761_INTERRUPT_CONFIG            0x210
+// Interrupt config value to only interrupt on ACK of IN and OUT tokens
+#define ISP1761_INTERRUPT_CONFIG_ACK_ONLY   BIT2 | BIT5 | BIT6
+
+#define ISP1761_DC_INTERRUPT                0x218
+#define ISP1761_DC_INTERRUPT_ENABLE         0x214
+
+#define ISP1761_CTRL_FUNCTION               0x228
+#define ISP1761_CTRL_FUNCTION_VENDP         BIT3
+#define ISP1761_CTRL_FUNCTION_DSEN          BIT2
+#define ISP1761_CTRL_FUNCTION_STATUS        BIT1
+
+#define ISP1761_DEVICE_UNLOCK               0x27C
+#define ISP1761_DEVICE_UNLOCK_MAGIC         0xAA37
+
+#define ISP1761_SW_RESET_REG                0x30C
+#define ISP1761_SW_RESET_ALL                BIT0
+
+#define ISP1761_DEVICE_ID                   0x370
+
+#define ISP1761_OTG_CTRL_SET                0x374
+#define ISP1761_OTG_CTRL_CLR                OTG_CTRL_SET + 2
+#define ISP1761_OTG_CTRL_OTG_DISABLE        BIT10
+#define ISP1761_OTG_CTRL_VBUS_CHRG          BIT6
+#define ISP1761_OTG_CTRL_VBUS_DISCHRG       BIT5
+#define ISP1761_OTG_CTRL_DM_PULLDOWN        BIT2
+#define ISP1761_OTG_CTRL_DP_PULLDOWN        BIT1
+#define ISP1761_OTG_CTRL_DP_PULLUP          BIT0
+
+#define ISP1761_OTG_STATUS                  0x378
+#define ISP1761_OTG_STATUS_B_SESS_END       BIT7
+#define ISP1761_OTG_STATUS_A_B_SESS_VLD     BIT1
+
+#define ISP1761_OTG_INTERRUPT_LATCH_SET     0x37C
+#define ISP1761_OTG_INTERRUPT_LATCH_CLR     0x37E
+#define ISP1761_OTG_INTERRUPT_ENABLE_RISE   0x384
+
+#define ISP1761_DMA_ENDPOINT_INDEX          0x258
+
+#define ISP1761_ENDPOINT_INDEX              0x22c
+#define ISP1761_DATA_PORT                   0x220
+#define ISP1761_BUFFER_LENGTH               0x21c
+
+// Device ID Values
+#define PHILLIPS_VENDOR_ID_VAL 0x04cc
+#define ISP1761_PRODUCT_ID_VAL 0x1761
+#define ISP1761_DEVICE_ID_VAL ((ISP1761_PRODUCT_ID_VAL << 16) |\
+                               PHILLIPS_VENDOR_ID_VAL)
+
+#endif //ifndef __ISP1761_USB_DXE_H__
diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
new file mode 100644
index 000000000000..b161547bf73a
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
@@ -0,0 +1,39 @@
+#/** @file
+#
+#  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = Isp1761PeriphDxe
+  FILE_GUID                      = 72d78ea6-4dee-11e3-8100-f3842a48d0a0
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = Isp1761PeriphEntryPoint
+
+[Sources.common]
+  Isp1761UsbDxe.c
+
+[LibraryClasses]
+  DebugLib
+  IoLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiDriverBindingProtocolGuid
+  gUsbDeviceProtocolGuid
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
+
+[Pcd]
+  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver Ard Biesheuvel
@ 2020-05-04 10:57   ` Leif Lindholm
  0 siblings, 0 replies; 20+ messages in thread
From: Leif Lindholm @ 2020-05-04 10:57 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: devel

On Thu, Apr 30, 2020 at 19:16:44 +0200, Ard Biesheuvel wrote:
> Juno is the only user of the SiI3132 SATA controller driver, which
> is not quite fit for reuse in its current state. So incorporate it
> into JunoPkg so we will be able to drop it from the core EDK2
> repository.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Reviewed-by: Leif Lindholm <leif@nuviainc.com>
> ---
>  Platform/ARM/JunoPkg/ArmJuno.dec                                 |   4 +-
>  Platform/ARM/JunoPkg/ArmJuno.dsc                                 |   2 +-
>  Platform/ARM/JunoPkg/ArmJuno.fdf                                 |   2 +-
>  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c      | 179 +++++
>  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c        | 546 +++++++++++++
>  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h        | 286 +++++++
>  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf   |  38 +
>  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c | 834 ++++++++++++++++++++
>  8 files changed, 1888 insertions(+), 3 deletions(-)
> 
> diff --git a/Platform/ARM/JunoPkg/ArmJuno.dec b/Platform/ARM/JunoPkg/ArmJuno.dec
> index 27fe75790721..37ea6857366f 100644
> --- a/Platform/ARM/JunoPkg/ArmJuno.dec
> +++ b/Platform/ARM/JunoPkg/ArmJuno.dec
> @@ -28,6 +28,9 @@ [Guids.common]
>  [PcdsFeatureFlag.common]
>    gArmJunoTokenSpaceGuid.PcdPciMaxPayloadFixup|FALSE|BOOLEAN|0x00000013
>  
> +  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeaturePMPSupport|FALSE|BOOLEAN|0x00000018
> +  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeatureDirectCommandIssuing|FALSE|BOOLEAN|0x00000019
> +
>  [PcdsFixedAtBuild.common]
>    gArmJunoTokenSpaceGuid.PcdPcieControlBaseAddress|0x7FF20000|UINT64|0x0000000B
>    gArmJunoTokenSpaceGuid.PcdPcieRootPortBaseAddress|0x7FF30000|UINT64|0x0000000C
> @@ -54,4 +57,3 @@ [PcdsFixedAtBuild.common]
>    #
>    # For a list of mode numbers look in HdLcdArmJuno.c
>    gArmJunoTokenSpaceGuid.PcdArmHdLcdMaxMode|0|UINT32|0x00000017
> -
> diff --git a/Platform/ARM/JunoPkg/ArmJuno.dsc b/Platform/ARM/JunoPkg/ArmJuno.dsc
> index 954faca1bbfa..1c39da4897ed 100644
> --- a/Platform/ARM/JunoPkg/ArmJuno.dsc
> +++ b/Platform/ARM/JunoPkg/ArmJuno.dsc
> @@ -314,7 +314,7 @@ [Components.common]
>    # SATA Controller
>    #
>    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
> -  EmbeddedPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
> +  Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
>  
>    #
>    # NVMe boot devices
> diff --git a/Platform/ARM/JunoPkg/ArmJuno.fdf b/Platform/ARM/JunoPkg/ArmJuno.fdf
> index 7c128b2c5bff..d771cbf35790 100644
> --- a/Platform/ARM/JunoPkg/ArmJuno.fdf
> +++ b/Platform/ARM/JunoPkg/ArmJuno.fdf
> @@ -184,7 +184,7 @@ [FV.FvMain]
>    # SATA Controller
>    #
>    INF MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
> -  INF EmbeddedPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
> +  INF Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
>  
>    #
>    # NVMe boot devices
> diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c
> new file mode 100644
> index 000000000000..944e827f7170
> --- /dev/null
> +++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c
> @@ -0,0 +1,179 @@
> +/** @file
> +  UEFI Component Name(2) protocol implementation for Silicon Image I3132 SATA controller
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "SataSiI3132.h"
> +
> +//
> +// EFI Component Name Protocol
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gSataSiI3132ComponentName = {
> +  SataSiI3132ComponentNameGetDriverName,
> +  SataSiI3132ComponentNameGetControllerName,
> +  "eng"
> +};
> +
> +//
> +// EFI Component Name 2 Protocol
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gSataSiI3132ComponentName2 = {
> +  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SataSiI3132ComponentNameGetDriverName,
> +  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SataSiI3132ComponentNameGetControllerName,
> +  "en"
> +};
> +
> +
> +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSataSiI3132DriverNameTable[] = {
> +  { "eng;en", L"Pci SATA Silicon Image 3132 Driver" },
> +  { 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.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132ComponentNameGetDriverName (
> +  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
> +  IN  CHAR8                        *Language,
> +  OUT CHAR16                       **DriverName
> +  )
> +{
> +  return LookupUnicodeString2 (
> +           Language,
> +           This->SupportedLanguages,
> +           mSataSiI3132DriverNameTable,
> +           DriverName,
> +           (BOOLEAN)(This == &gSataSiI3132ComponentName)
> +           );
> +}
> +
> +/**
> +  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 not a valid EFI_HANDLE.
> +
> +  @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.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132ComponentNameGetControllerName (
> +  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
> +  IN  EFI_HANDLE                                      ControllerHandle,
> +  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
> +  IN  CHAR8                                           *Language,
> +  OUT CHAR16                                          **ControllerName
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c
> new file mode 100644
> index 000000000000..ad7cc1cd75a1
> --- /dev/null
> +++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c
> @@ -0,0 +1,546 @@
> +/** @file
> +  PCIe Sata support for the Silicon Image I3132
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "SataSiI3132.h"
> +
> +#include <IndustryStandard/Acpi10.h>
> +
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/DxeServicesTableLib.h>
> +#include <Library/BaseLib.h>
> +
> +#define ACPI_SPECFLAG_PREFETCHABLE      0x06
> +
> +EFI_DRIVER_BINDING_PROTOCOL
> +gSataSiI3132DriverBinding = {
> +  SataSiI3132DriverBindingSupported,
> +  SataSiI3132DriverBindingStart,
> +  SataSiI3132DriverBindingStop,
> +  0x30,
> +  NULL,
> +  NULL
> +};
> +
> +EFI_STATUS
> +SataSiI3132PortConstructor (
> +  IN  SATA_SI3132_INSTANCE *SataSiI3132Instance,
> +  IN  UINTN                Index
> +  )
> +{
> +  EFI_STATUS            Status;
> +  SATA_SI3132_PORT      *Port;
> +  VOID                  *HostPRB;
> +  EFI_PHYSICAL_ADDRESS  PhysAddrHostPRB;
> +  VOID                  *PciAllocMappingPRB;
> +  UINTN                 NumberOfBytes;
> +
> +  Port = &(SataSiI3132Instance->Ports[Index]);
> +
> +  Port->Index    = Index;
> +  Port->RegBase  = Index * 0x2000;
> +  Port->Instance = SataSiI3132Instance;
> +  InitializeListHead (&(Port->Devices));
> +
> +  NumberOfBytes = sizeof (SATA_SI3132_PRB);
> +  Status = SataSiI3132Instance->PciIo->AllocateBuffer (
> +             SataSiI3132Instance->PciIo, AllocateAnyPages, EfiBootServicesData,
> +             EFI_SIZE_TO_PAGES (NumberOfBytes), &HostPRB, 0
> +             );
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // Check the alignment of the PCI Buffer
> +  ASSERT (((UINTN)HostPRB & (0x1000 - 1)) == 0);
> +  Status = SataSiI3132Instance->PciIo->Map (
> +             SataSiI3132Instance->PciIo, EfiPciIoOperationBusMasterCommonBuffer, HostPRB,
> +             &NumberOfBytes, &PhysAddrHostPRB, &PciAllocMappingPRB
> +             );
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Port->HostPRB            = HostPRB;
> +  Port->PhysAddrHostPRB    = PhysAddrHostPRB;
> +  Port->PciAllocMappingPRB = PciAllocMappingPRB;
> +
> +  return Status;
> +}
> +
> +STATIC
> +EFI_STATUS
> +SataSiI3132Constructor (
> +  IN  EFI_PCI_IO_PROTOCOL     *PciIo,
> +  OUT SATA_SI3132_INSTANCE**  SataSiI3132Instance
> +  )
> +{
> +  SATA_SI3132_INSTANCE    *Instance;
> +  EFI_ATA_PASS_THRU_MODE  *AtaPassThruMode;
> +
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Instance = (SATA_SI3132_INSTANCE*)AllocateZeroPool (sizeof (SATA_SI3132_INSTANCE));
> +  if (Instance == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Instance->Signature           = SATA_SII3132_SIGNATURE;
> +  Instance->PciIo               = PciIo;
> +
> +  AtaPassThruMode = (EFI_ATA_PASS_THRU_MODE*)AllocatePool (sizeof (EFI_ATA_PASS_THRU_MODE));
> +  AtaPassThruMode->Attributes = EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL;
> +  AtaPassThruMode->IoAlign = 0x1000;
> +
> +  // Initialize SiI3132 ports
> +  SataSiI3132PortConstructor (Instance, 0);
> +  SataSiI3132PortConstructor (Instance, 1);
> +
> +  // Set ATA Pass Thru Protocol
> +  Instance->AtaPassThruProtocol.Mode            = AtaPassThruMode;
> +  Instance->AtaPassThruProtocol.PassThru        = SiI3132AtaPassThru;
> +  Instance->AtaPassThruProtocol.GetNextPort     = SiI3132GetNextPort;
> +  Instance->AtaPassThruProtocol.GetNextDevice   = SiI3132GetNextDevice;
> +  Instance->AtaPassThruProtocol.BuildDevicePath = SiI3132BuildDevicePath;
> +  Instance->AtaPassThruProtocol.GetDevice       = SiI3132GetDevice;
> +  Instance->AtaPassThruProtocol.ResetPort       = SiI3132ResetPort;
> +  Instance->AtaPassThruProtocol.ResetDevice     = SiI3132ResetDevice;
> +
> +  *SataSiI3132Instance = Instance;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +SiI3132SoftResetCommand (
> +  IN   SATA_SI3132_PORT *Port,
> +  OUT  UINT32* Signature
> +  )
> +{
> +  EFI_STATUS                        Status;
> +  EFI_ATA_PASS_THRU_COMMAND_PACKET  Packet;
> +  EFI_ATA_STATUS_BLOCK              Asb;
> +  EFI_ATA_COMMAND_BLOCK             Acb;
> +  CONST UINT16                      PortMultiplierPort = 0;
> +
> +  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
> +
> +  Acb.Reserved1[1] = 0;
> +
> +  Packet.Asb      = &Asb;
> +  Packet.Acb      = &Acb;
> +  Packet.Timeout  = 100000;
> +  Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET;
> +
> +  Status = SiI3132AtaPassThruCommand (Port->Instance, Port, PortMultiplierPort, &Packet, 0);
> +
> +  if (Status == EFI_SUCCESS) {
> +    *Signature = (Asb.AtaCylinderHigh << 24) | (Asb.AtaCylinderLow << 16) |
> +                 (Asb.AtaSectorNumber << 8 ) | (Asb.AtaSectorCount);
> +  }
> +  return Status;
> +}
> +
> +EFI_STATUS
> +SataSiI3132PortInitialization (
> +  IN SATA_SI3132_PORT *Port
> +  )
> +{
> +  UINT32                  Value32;
> +  SATA_SI3132_DEVICE*     Device;
> +  UINT32                  Signature;
> +  EFI_STATUS              Status;
> +  EFI_PCI_IO_PROTOCOL*    PciIo;
> +
> +  Status = SiI3132HwResetPort (Port);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  PciIo = Port->Instance->PciIo;
> +
> +  // Is a device is present ?
> +  Status = SATA_PORT_READ32 (Port->RegBase + SII3132_PORT_SSTATUS_REG, &Value32);
> +  if (!EFI_ERROR (Status) && (Value32 & 0x3)) {
> +    // Do a soft reset to see if it is a port multiplier
> +    SATA_TRACE ("SataSiI3132PortInitialization: soft reset - it is a port multiplier\n");
> +    Status = SiI3132SoftResetCommand (Port, &Signature);
> +    if (!EFI_ERROR (Status)) {
> +      if (Signature == SII3132_PORT_SIGNATURE_PMP) {
> +        SATA_TRACE ("SataSiI3132PortInitialization(): a Port Multiplier is present");
> +        if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
> +          ASSERT (0); // Not supported yet
> +        } else {
> +          return EFI_UNSUPPORTED;
> +        }
> +      } else if (Signature == SII3132_PORT_SIGNATURE_ATAPI) {
> +        ASSERT (0); // Not supported yet
> +        SATA_TRACE ("SataSiI3132PortInitialization(): an ATAPI device is present");
> +        return EFI_UNSUPPORTED;
> +      } else if (Signature == SII3132_PORT_SIGNATURE_ATA) {
> +        SATA_TRACE ("SataSiI3132PortInitialization(): an ATA device is present");
> +      } else {
> +        SATA_TRACE ("SataSiI3132PortInitialization(): Present device unknown!");
> +        ASSERT (0); // Not supported
> +        return EFI_UNSUPPORTED;
> +      }
> +
> +      // Create Device
> +      Device            = (SATA_SI3132_DEVICE*)AllocatePool (sizeof (SATA_SI3132_DEVICE));
> +      Device->Index     = Port->Index; //TODO: Could need to be fixed when SATA Port Multiplier support
> +      Device->Port      = Port;
> +      Device->BlockSize = 0;
> +
> +      // Attached the device to the Sata Port
> +      InsertTailList (&Port->Devices, &Device->Link);
> +
> +      SATA_TRACE ("SataSiI3132PortInitialization(): Port Ready");
> +    }
> +  }
> +  return Status;
> +}
> +
> +EFI_STATUS
> +SataSiI3132Initialization (
> +  IN SATA_SI3132_INSTANCE* SataSiI3132Instance
> +  )
> +{
> +  UINTN                 Index;
> +  EFI_PCI_IO_PROTOCOL*  PciIo;
> +
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  PciIo = SataSiI3132Instance->PciIo;
> +
> +  // Turn Off GPIO
> +  SATA_GLOBAL_WRITE32 (SII3132_GLOBAL_FLASHADDR_REG, 0x0);
> +
> +  // Clear Global Control Register
> +  SATA_GLOBAL_WRITE32 (SII3132_GLOBAL_CONTROL_REG, 0x0);
> +
> +  for (Index = 0; Index < SATA_SII3132_MAXPORT; Index++) {
> +    SataSiI3132PortInitialization (&(SataSiI3132Instance->Ports[Index]));
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Test to see if this driver supports ControllerHandle.
> +
> +  @param  This                 Protocol instance pointer.
> +  @param  Controller           Handle of device to test.
> +  @param  RemainingDevicePath  Not used.
> +
> +  @return EFI_SUCCESS          This driver supports this device.
> +  @return EFI_UNSUPPORTED      This driver does not support this device.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132DriverBindingSupported (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  Controller,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
> +  )
> +{
> +  EFI_STATUS              Status;
> +  EFI_PCI_IO_PROTOCOL     *PciIo;
> +  UINT32                   PciID;
> +
> +  //
> +  // Test whether there is PCI IO Protocol attached on the controller handle.
> +  //
> +  Status = gBS->OpenProtocol (
> +                Controller,
> +                &gEfiPciIoProtocolGuid,
> +                (VOID **) &PciIo,
> +                This->DriverBindingHandle,
> +                Controller,
> +                EFI_OPEN_PROTOCOL_BY_DRIVER
> +                );
> +  if (EFI_ERROR (Status)) {
> +      return Status;
> +  }
> +
> +  Status = PciIo->Pci.Read (
> +                      PciIo,
> +                      EfiPciIoWidthUint32,
> +                      PCI_VENDOR_ID_OFFSET,
> +                      1,
> +                      &PciID
> +                      );
> +  if (EFI_ERROR (Status)) {
> +      Status = EFI_UNSUPPORTED;
> +      goto ON_EXIT;
> +  }
> +
> +  //
> +  // Test whether the controller belongs to SATA Mass Storage type
> +  //
> +  if (PciID != ((SATA_SII3132_DEVICE_ID << 16) | SATA_SII3132_VENDOR_ID)) {
> +      Status = EFI_UNSUPPORTED;
> +  }
> +
> +ON_EXIT:
> +  gBS->CloseProtocol (
> +       Controller,
> +       &gEfiPciIoProtocolGuid,
> +       This->DriverBindingHandle,
> +       Controller
> +       );
> +
> +  return Status;
> +}
> +
> +BOOLEAN mbStarted = FALSE;
> +
> +/**
> +  Starting the Pci SATA Driver.
> +
> +  @param  This                 Protocol instance pointer.
> +  @param  Controller           Handle of device to test.
> +  @param  RemainingDevicePath  Not used.
> +
> +  @return EFI_SUCCESS          supports this device.
> +  @return EFI_UNSUPPORTED      do not support this device.
> +  @return EFI_DEVICE_ERROR     cannot be started due to device Error.
> +  @return EFI_OUT_OF_RESOURCES cannot allocate resources.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132DriverBindingStart (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  Controller,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
> +  )
> +{
> +  EFI_STATUS              Status;
> +  EFI_PCI_IO_PROTOCOL     *PciIo;
> +  UINT64                  Supports;
> +  UINT64                  OriginalPciAttributes;
> +  BOOLEAN                 PciAttributesSaved;
> +  UINT32                  PciID;
> +  SATA_SI3132_INSTANCE    *SataSiI3132Instance = NULL;
> +
> +  SATA_TRACE ("SataSiI3132DriverBindingStart()");
> +
> +  //TODO: Find a nicer way to do it !
> +  if (mbStarted) {
> +    return EFI_SUCCESS; // Don't restart me !
> +  }
> +
> +  //
> +  // Open the PciIo Protocol
> +  //
> +  Status = gBS->OpenProtocol (
> +                Controller,
> +                &gEfiPciIoProtocolGuid,
> +                (VOID **) &PciIo,
> +                This->DriverBindingHandle,
> +                Controller,
> +                EFI_OPEN_PROTOCOL_BY_DRIVER
> +                );
> +  if (EFI_ERROR (Status)) {
> +      return Status;
> +  }
> +
> +  PciAttributesSaved = FALSE;
> +  //
> +  // Save original PCI attributes
> +  //
> +  Status = PciIo->Attributes (
> +                  PciIo,
> +                  EfiPciIoAttributeOperationGet,
> +                  0,
> +                  &OriginalPciAttributes
> +                  );
> +  if (EFI_ERROR (Status)) {
> +      goto CLOSE_PCIIO;
> +  }
> +  PciAttributesSaved = TRUE;
> +
> +  Status = PciIo->Attributes (
> +                  PciIo,
> +                  EfiPciIoAttributeOperationSupported,
> +                  0,
> +                  &Supports
> +                  );
> +  if (!EFI_ERROR (Status)) {
> +      Supports &= EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE;
> +      Status = PciIo->Attributes (
> +                        PciIo,
> +                        EfiPciIoAttributeOperationEnable,
> +                        Supports,
> +                        NULL
> +                        );
> +  }
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((EFI_D_ERROR, "SataSiI3132DriverBindingStart: failed to enable controller\n"));
> +    goto CLOSE_PCIIO;
> +  }
> +
> +  //
> +  // Get the Pci device class code.
> +  //
> +  Status = PciIo->Pci.Read (
> +                      PciIo,
> +                      EfiPciIoWidthUint32,
> +                      PCI_VENDOR_ID_OFFSET,
> +                      1,
> +                      &PciID
> +                      );
> +  if (EFI_ERROR (Status)) {
> +    Status = EFI_UNSUPPORTED;
> +    goto CLOSE_PCIIO;
> +  }
> +
> +  //
> +  // Test whether the controller belongs to SATA Mass Storage type
> +  //
> +  if (PciID != ((SATA_SII3132_DEVICE_ID << 16) | SATA_SII3132_VENDOR_ID)) {
> +    Status = EFI_UNSUPPORTED;
> +    goto CLOSE_PCIIO;
> +  }
> +
> +  // Create SiI3132 Sata Instance
> +  Status = SataSiI3132Constructor (PciIo, &SataSiI3132Instance);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // Initialize SiI3132 Sata Controller
> +  Status = SataSiI3132Initialization (SataSiI3132Instance);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // Install Ata Pass Thru Protocol
> +  Status = gBS->InstallProtocolInterface (
> +              &Controller,
> +              &gEfiAtaPassThruProtocolGuid,
> +              EFI_NATIVE_INTERFACE,
> +              &(SataSiI3132Instance->AtaPassThruProtocol)
> +              );
> +  if (EFI_ERROR (Status)) {
> +    goto FREE_POOL;
> +  }
> +
> +/*  //
> +  // Create event to stop the HC when exit boot service.
> +  //
> +  Status = gBS->CreateEventEx (
> +                EVT_NOTIFY_SIGNAL,
> +                TPL_NOTIFY,
> +                EhcExitBootService,
> +                Ehc,
> +                &gEfiEventExitBootServicesGuid,
> +                &Ehc->ExitBootServiceEvent
> +                );
> +  if (EFI_ERROR (Status)) {
> +      goto UNINSTALL_USBHC;
> +  }*/
> +
> +  mbStarted = TRUE;
> +
> +  SATA_TRACE ("SataSiI3132DriverBindingStart() Success!");
> +  return EFI_SUCCESS;
> +
> +FREE_POOL:
> +  //TODO: Free SATA Instance
> +
> +CLOSE_PCIIO:
> +  if (PciAttributesSaved) {
> +      //
> +      // Restore original PCI attributes
> +      //
> +      PciIo->Attributes (
> +                      PciIo,
> +                      EfiPciIoAttributeOperationSet,
> +                      OriginalPciAttributes,
> +                      NULL
> +                      );
> +  }
> +
> +  gBS->CloseProtocol (
> +       Controller,
> +       &gEfiPciIoProtocolGuid,
> +       This->DriverBindingHandle,
> +       Controller
> +       );
> +
> +  return Status;
> +}
> +
> +/**
> +  Stop this driver on ControllerHandle. Support stopping any child handles
> +  created by this driver.
> +
> +  @param  This                 Protocol instance pointer.
> +  @param  Controller           Handle of device to stop driver on.
> +  @param  NumberOfChildren     Number of Children in the ChildHandleBuffer.
> +  @param  ChildHandleBuffer    List of handles for the children we need to stop.
> +
> +  @return EFI_SUCCESS          Success.
> +  @return EFI_DEVICE_ERROR     Fail.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132DriverBindingStop (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  Controller,
> +  IN UINTN                       NumberOfChildren,
> +  IN EFI_HANDLE                  *ChildHandleBuffer
> +  )
> +{
> +  SATA_TRACE ("SataSiI3132DriverBindingStop()");
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +  Entry point of this driver
> +
> +  @param ImageHandle     Handle of driver image
> +  @param SystemTable     Point to EFI_SYSTEM_TABLE
> +
> +  @retval EFI_OUT_OF_RESOURCES  Can not allocate memory resource
> +  @retval EFI_DEVICE_ERROR      Can not install the protocol instance
> +  @retval EFI_SUCCESS           Success to initialize the Pci host bridge.
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeSataSiI3132 (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  SATA_TRACE ("InitializeSataSiI3132 ()");
> +
> +  return EfiLibInstallDriverBindingComponentName2 (
> +         ImageHandle,
> +         SystemTable,
> +         &gSataSiI3132DriverBinding,
> +         ImageHandle,
> +         &gSataSiI3132ComponentName,
> +         &gSataSiI3132ComponentName2
> +         );
> +}
> diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h
> new file mode 100644
> index 000000000000..f799fe84a047
> --- /dev/null
> +++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h
> @@ -0,0 +1,286 @@
> +/** @file
> +  Header containing the structure specific to the Silicon Image I3132 Sata PCI card
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef __SATASII3132_H
> +#define __SATASII3132_H
> +
> +#include <PiDxe.h>
> +
> +#include <Protocol/AtaPassThru.h>
> +#include <Protocol/PciIo.h>
> +
> +#include <Library/UefiLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +#include <IndustryStandard/Pci.h>
> +
> +#define SATA_SII3132_DEVICE_ID      0x3132
> +#define SATA_SII3132_VENDOR_ID      0x1095
> +
> +#define SII3132_PORT_SIGNATURE_PMP      0x96690101
> +#define SII3132_PORT_SIGNATURE_ATAPI    0xEB140101
> +#define SII3132_PORT_SIGNATURE_ATA      0x00000101
> +
> +/*
> + * Silicon Image SiI3132 Registers
> + */
> +#define SII3132_GLOBAL_CONTROL_REG              0x40
> +#define SII3132_GLOBAL_FLASHADDR_REG            0x70
> +
> +#define SII3132_PORT_STATUS_REG                 0x1000
> +#define SII3132_PORT_CONTROLSET_REG             0x1000
> +#define SII3132_PORT_CONTROLCLEAR_REG           0x1004
> +#define SII3132_PORT_INTSTATUS_REG              0x1008
> +#define SII3132_PORT_ENABLEINT_REG              0x1010
> +#define SII3132_PORT_INTCLEAR_REG               0x1014
> +#define SII3132_PORT_32BITACTIVADDR_REG         0x101C
> +#define SII3132_PORT_CMDEXECFIFO_REG            0x1020
> +#define SII3132_PORT_CMDERROR_REG               0x1024
> +#define SII3132_PORT_ERRCOUNTDECODE             0x1040
> +#define SII3132_PORT_ERRCOUNTCRC                0x1044
> +#define SII3132_PORT_ERRCOUNTHANDSHAKE          0x1048
> +#define SII3132_PORT_SLOTSTATUS_REG             0x1800
> +#define SII3132_PORT_CMDACTIV_REG               0x1C00
> +#define SII3132_PORT_SSTATUS_REG                0x1F04
> +
> +#define SII3132_PORT_CONTROL_RESET              (1 << 0)
> +#define SII3132_PORT_DEVICE_RESET               (1 << 1)
> +#define SII3132_PORT_CONTROL_INT                (1 << 2)
> +#define SII3132_PORT_CONTROL_32BITACTIVATION    (1 << 10)
> +
> +#define SII3132_PORT_STATUS_PORTREADY           0x80000000
> +
> +#define SII3132_PORT_INT_CMDCOMPL               (1 << 0)
> +#define SII3132_PORT_INT_CMDERR                 (1 << 1)
> +#define SII3132_PORT_INT_PORTRDY                (1 << 2)
> +
> +#define SATA_SII3132_MAXPORT    2
> +
> +#define PRB_CTRL_ATA            0x0
> +#define PRB_CTRL_PROT_OVERRIDE  0x1
> +#define PRB_CTRL_RESTRANSMIT    0x2
> +#define PRB_CTRL_EXT_CMD        0x4
> +#define PRB_CTRL_RCV            0x8
> +#define PRB_CTRL_PKT_READ       0x10
> +#define PRB_CTRL_PKT_WRITE      0x20
> +#define PRB_CTRL_INT_MASK       0x40
> +#define PRB_CTRL_SRST           0x80
> +
> +#define PRB_PROT_PACKET         0x01
> +#define PRB_PROT_LEGACY_QUEUE   0x02
> +#define PRB_PROT_NATIVE_QUEUE   0x04
> +#define PRB_PROT_READ           0x08
> +#define PRB_PROT_WRITE          0x10
> +#define PRB_PROT_TRANSPARENT    0x20
> +
> +#define SGE_XCF     (1 << 28)
> +#define SGE_DRD     (1 << 29)
> +#define SGE_LNK     (1 << 30)
> +#define SGE_TRM     0x80000000
> +
> +typedef struct _SATA_SI3132_SGE {
> +    UINT32      DataAddressLow;
> +    UINT32      DataAddressHigh;
> +    UINT32      DataCount;
> +    UINT32      Attributes;
> +} SATA_SI3132_SGE;
> +
> +typedef struct _SATA_SI3132_FIS {
> +    UINT8               FisType;
> +    UINT8               Control;
> +    UINT8               Command;
> +    UINT8               Features;
> +    UINT8               Fis[5 * 4];
> +} SATA_SI3132_FIS;
> +
> +typedef struct _SATA_SI3132_PRB {
> +    UINT16              Control;
> +    UINT16              ProtocolOverride;
> +    UINT32              RecTransCount;
> +    SATA_SI3132_FIS     Fis;
> +    SATA_SI3132_SGE     Sge[2];
> +} SATA_SI3132_PRB;
> +
> +typedef struct _SATA_SI3132_DEVICE {
> +    LIST_ENTRY                  Link; // This attribute must be the first entry of this structure (to avoid pointer computation)
> +    UINTN                       Index;
> +    struct _SATA_SI3132_PORT    *Port;  //Parent Port
> +    UINT32                      BlockSize;
> +} SATA_SI3132_DEVICE;
> +
> +typedef struct _SATA_SI3132_PORT {
> +    UINTN                           Index;
> +    UINTN                           RegBase;
> +    struct _SATA_SI3132_INSTANCE    *Instance;
> +
> +    //TODO: Support Port multiplier
> +    LIST_ENTRY                      Devices;
> +
> +    SATA_SI3132_PRB*                HostPRB;
> +    EFI_PHYSICAL_ADDRESS            PhysAddrHostPRB;
> +    VOID*                           PciAllocMappingPRB;
> +} SATA_SI3132_PORT;
> +
> +typedef struct _SATA_SI3132_INSTANCE {
> +    UINTN                       Signature;
> +
> +    SATA_SI3132_PORT            Ports[SATA_SII3132_MAXPORT];
> +
> +    EFI_ATA_PASS_THRU_PROTOCOL  AtaPassThruProtocol;
> +
> +    EFI_PCI_IO_PROTOCOL         *PciIo;
> +} SATA_SI3132_INSTANCE;
> +
> +#define SATA_SII3132_SIGNATURE              SIGNATURE_32('s', 'i', '3', '2')
> +#define INSTANCE_FROM_ATAPASSTHRU_THIS(a)   CR(a, SATA_SI3132_INSTANCE, AtaPassThruProtocol, SATA_SII3132_SIGNATURE)
> +
> +#define SATA_GLOBAL_READ32(Offset, Value)  PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 0, Offset, 1, Value)
> +#define SATA_GLOBAL_WRITE32(Offset, Value) { UINT32 Value32 = Value; PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 0, Offset, 1, &Value32); }
> +
> +#define SATA_PORT_READ32(Offset, Value)  PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 1, Offset, 1, Value)
> +#define SATA_PORT_WRITE32(Offset, Value) { UINT32 Value32 = Value; PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 1, Offset, 1, &Value32); }
> +
> +#define SATA_TRACE(txt)  DEBUG((EFI_D_VERBOSE, "ARM_SATA: " txt "\n"))
> +
> +extern EFI_COMPONENT_NAME_PROTOCOL  gSataSiI3132ComponentName;
> +extern EFI_COMPONENT_NAME2_PROTOCOL gSataSiI3132ComponentName2;
> +
> +/*
> + * Component Name Protocol Functions
> + */
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132ComponentNameGetDriverName (
> +  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
> +  IN  CHAR8                        *Language,
> +  OUT CHAR16                       **DriverName
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132ComponentNameGetControllerName (
> +  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
> +  IN  EFI_HANDLE                                      ControllerHandle,
> +  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
> +  IN  CHAR8                                           *Language,
> +  OUT CHAR16                                          **ControllerName
> +  );
> +
> +EFI_STATUS SiI3132HwResetPort (SATA_SI3132_PORT *Port);
> +
> +/*
> + * Driver Binding Protocol Functions
> + */
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132DriverBindingSupported (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  Controller,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132DriverBindingStart (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  Controller,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SataSiI3132DriverBindingStop (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  Controller,
> +  IN UINTN                       NumberOfChildren,
> +  IN EFI_HANDLE                  *ChildHandleBuffer
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132AtaPassThruCommand (
> +  IN     SATA_SI3132_INSTANCE             *pSataSiI3132Instance,
> +  IN     SATA_SI3132_PORT                 *pSataPort,
> +  IN     UINT16                           PortMultiplierPort,
> +  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
> +  IN     EFI_EVENT                        Event OPTIONAL
> +  );
> +
> +/**
> + * EFI ATA Pass Thru Protocol
> + */
> +EFI_STATUS
> +EFIAPI
> +SiI3132AtaPassThru (
> +  IN     EFI_ATA_PASS_THRU_PROTOCOL       *This,
> +  IN     UINT16                           Port,
> +  IN     UINT16                           PortMultiplierPort,
> +  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
> +  IN     EFI_EVENT                        Event OPTIONAL
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132GetNextPort (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN OUT UINT16                 *Port
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132GetNextDevice (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN UINT16                     Port,
> +  IN OUT UINT16                 *PortMultiplierPort
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132BuildDevicePath (
> +  IN     EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN     UINT16                     Port,
> +  IN     UINT16                     PortMultiplierPort,
> +  IN OUT EFI_DEVICE_PATH_PROTOCOL   **DevicePath
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132GetDevice (
> +  IN  EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN  EFI_DEVICE_PATH_PROTOCOL   *DevicePath,
> +  OUT UINT16                     *Port,
> +  OUT UINT16                     *PortMultiplierPort
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132ResetPort (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN UINT16                     Port
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132ResetDevice (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN UINT16                     Port,
> +  IN UINT16                     PortMultiplierPort
> +  );
> +
> +#endif
> diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
> new file mode 100644
> index 000000000000..a73b3bc168a8
> --- /dev/null
> +++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
> @@ -0,0 +1,38 @@
> +#/** @file
> +#  INF file for the Silicon Image I3132 SATA controller
> +#
> +#  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent

Comment only:
This file, unlike some other .infs in this series, does not include
the WARNING header. Feel free to fold one in before pushing.

/
    Leif

> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 1.27
> +  BASE_NAME                      = SataSiI3132Dxe
> +  FILE_GUID                      = 1df18da0-a18b-11df-8c3a-0002a5d5c51b
> +  MODULE_TYPE                    = UEFI_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = InitializeSataSiI3132
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  Platform/ARM/JunoPkg/ArmJuno.dec
> +
> +[LibraryClasses]
> +  MemoryAllocationLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Sources]
> +  ComponentName.c
> +  SataSiI3132.c
> +  SiI3132AtaPassThru.c
> +
> +[Protocols]
> +  gEfiPciIoProtocolGuid                         # CONSUMED
> +  gEfiAtaPassThruProtocolGuid                   # PRODUCED
> +
> +[Pcd]
> +  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeaturePMPSupport
> +  gArmJunoTokenSpaceGuid.PcdSataSiI3132FeatureDirectCommandIssuing
> diff --git a/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
> new file mode 100644
> index 000000000000..f15b59788310
> --- /dev/null
> +++ b/Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
> @@ -0,0 +1,834 @@
> +/** @file
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "SataSiI3132.h"
> +
> +#include <IndustryStandard/Atapi.h>
> +#include <Library/DevicePathLib.h>
> +
> +SATA_SI3132_DEVICE*
> +GetSataDevice (
> +  IN  SATA_SI3132_INSTANCE* SataInstance,
> +  IN  UINT16 Port,
> +  IN  UINT16 PortMultiplierPort
> +) {
> +  LIST_ENTRY              *List;
> +  SATA_SI3132_PORT        *SataPort;
> +  SATA_SI3132_DEVICE      *SataDevice;
> +
> +  if (Port >= SATA_SII3132_MAXPORT) {
> +    return NULL;
> +  }
> +
> +  SataPort = &(SataInstance->Ports[Port]);
> +  List = SataPort->Devices.ForwardLink;
> +
> +  while (List != &SataPort->Devices) {
> +    SataDevice = (SATA_SI3132_DEVICE*)List;
> +    if (SataDevice->Index == PortMultiplierPort) {
> +      return SataDevice;
> +    }
> +    List = List->ForwardLink;
> +  }
> +  return NULL;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +SiI3132AtaPassThruCommand (
> +  IN     SATA_SI3132_INSTANCE             *SataSiI3132Instance,
> +  IN     SATA_SI3132_PORT                 *SataPort,
> +  IN     UINT16                           PortMultiplierPort,
> +  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
> +  IN     EFI_EVENT                        Event OPTIONAL
> +  )
> +{
> +  SATA_SI3132_DEVICE      *SataDevice;
> +  EFI_PHYSICAL_ADDRESS    PhysInDataBuffer;
> +  UINTN                   InDataBufferLength = 0;
> +  EFI_PHYSICAL_ADDRESS    PhysOutDataBuffer;
> +  UINTN                   OutDataBufferLength;
> +  CONST UINTN             EmptySlot = 0;
> +  UINTN                   Control = PRB_CTRL_ATA;
> +  UINTN                   Protocol = 0;
> +  UINT32                  Value32, Error, Timeout = 0;
> +  CONST UINT32            IrqMask = (SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR) << 16;
> +  EFI_STATUS              Status;
> +  VOID*                   PciAllocMapping = NULL;
> +  EFI_PCI_IO_PROTOCOL     *PciIo;
> +
> +  PciIo = SataSiI3132Instance->PciIo;
> +  ZeroMem (SataPort->HostPRB, sizeof (SATA_SI3132_PRB));
> +
> +  // Construct Si3132 PRB
> +  switch (Packet->Protocol) {
> +  case EFI_ATA_PASS_THRU_PROTOCOL_ATA_HARDWARE_RESET:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET:
> +    SATA_TRACE ("SiI3132AtaPassThru() EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET");
> +    Control = PRB_CTRL_SRST;
> +
> +    if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
> +        SataPort->HostPRB->Fis.Control = 0x0F;
> +    }
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +
> +  // There is no difference for SiI3132 between PIO and DMA invokation
> +  case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN:
> +  case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:
> +    // Fixup the size for block transfer. Following UEFI Specification, 'InTransferLength' should
> +    // be in number of bytes. But for most data transfer commands, the value is in number of blocks
> +    if (Packet->Acb->AtaCommand == ATA_CMD_IDENTIFY_DRIVE) {
> +      InDataBufferLength = Packet->InTransferLength;
> +    } else {
> +      SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);
> +      if (!SataDevice || (SataDevice->BlockSize == 0)) {
> +        return EFI_INVALID_PARAMETER;
> +      }
> +
> +      InDataBufferLength = Packet->InTransferLength * SataDevice->BlockSize;
> +    }
> +
> +    Status = PciIo->Map (
> +               PciIo, EfiPciIoOperationBusMasterWrite,
> +               Packet->InDataBuffer, &InDataBufferLength, &PhysInDataBuffer, &PciAllocMapping
> +               );
> +    if (EFI_ERROR (Status)) {
> +      return Status;
> +    }
> +
> +    // Construct SGEs (32-bit system)
> +    SataPort->HostPRB->Sge[0].DataAddressLow = (UINT32)PhysInDataBuffer;
> +    SataPort->HostPRB->Sge[0].DataAddressHigh = (UINT32)(PhysInDataBuffer >> 32);
> +    SataPort->HostPRB->Sge[0].Attributes = SGE_TRM; // Only one SGE
> +    SataPort->HostPRB->Sge[0].DataCount = InDataBufferLength;
> +
> +    // Copy the Ata Command Block
> +    CopyMem (&SataPort->HostPRB->Fis, Packet->Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
> +
> +    // Fixup the FIS
> +    SataPort->HostPRB->Fis.FisType = 0x27; // Register - Host to Device FIS
> +    SataPort->HostPRB->Fis.Control = 1 << 7; // Is a command
> +    if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
> +      SataPort->HostPRB->Fis.Control |= PortMultiplierPort & 0xFF;
> +    }
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT:
> +  case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:
> +    SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);
> +    if (!SataDevice || (SataDevice->BlockSize == 0)) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +
> +    // Fixup the size for block transfer. Following UEFI Specification, 'InTransferLength' should
> +    // be in number of bytes. But for most data transfer commands, the value is in number of blocks
> +    OutDataBufferLength = Packet->OutTransferLength * SataDevice->BlockSize;
> +
> +    Status = PciIo->Map (
> +               PciIo, EfiPciIoOperationBusMasterRead,
> +               Packet->OutDataBuffer, &OutDataBufferLength, &PhysOutDataBuffer, &PciAllocMapping
> +               );
> +    if (EFI_ERROR (Status)) {
> +      return Status;
> +    }
> +
> +    // Construct SGEs (32-bit system)
> +    SataPort->HostPRB->Sge[0].DataAddressLow  = (UINT32)PhysOutDataBuffer;
> +    SataPort->HostPRB->Sge[0].DataAddressHigh = (UINT32)(PhysOutDataBuffer >> 32);
> +    SataPort->HostPRB->Sge[0].Attributes      = SGE_TRM; // Only one SGE
> +    SataPort->HostPRB->Sge[0].DataCount       = OutDataBufferLength;
> +
> +    // Copy the Ata Command Block
> +    CopyMem (&SataPort->HostPRB->Fis, Packet->Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
> +
> +    // Fixup the FIS
> +    SataPort->HostPRB->Fis.FisType = 0x27; // Register - Host to Device FIS
> +    SataPort->HostPRB->Fis.Control = 1 << 7; // Is a command
> +    if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
> +      SataPort->HostPRB->Fis.Control |= PortMultiplierPort & 0xFF;
> +    }
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_DMA:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_DMA_QUEUED:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_DIAGNOSTIC:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_RESET:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_FPDMA:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  case EFI_ATA_PASS_THRU_PROTOCOL_RETURN_RESPONSE:
> +    ASSERT (0); //TODO: Implement me!
> +    break;
> +  default:
> +    ASSERT (0);
> +    break;
> +  }
> +
> +  SataPort->HostPRB->Control = Control;
> +  SataPort->HostPRB->ProtocolOverride = Protocol;
> +
> +  // Clear IRQ
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, IrqMask);
> +
> +  if (!FeaturePcdGet (PcdSataSiI3132FeatureDirectCommandIssuing)) {
> +    // Indirect Command Issuance
> +
> +    //TODO: Find which slot is free (maybe use the Cmd FIFO)
> +    //SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_CMDEXECFIFO_REG, &EmptySlot);
> +
> +    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDACTIV_REG + (EmptySlot * 8),
> +                     (UINT32)(SataPort->PhysAddrHostPRB & 0xFFFFFFFF));
> +    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDACTIV_REG + (EmptySlot * 8) + 4,
> +                     (UINT32)((SataPort->PhysAddrHostPRB >> 32) & 0xFFFFFFFF));
> +  } else {
> +    // Direct Command Issuance
> +    Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 1, // Bar 1
> +        SataPort->RegBase + (EmptySlot * 0x80),
> +        sizeof (SATA_SI3132_PRB) / 4,
> +        SataPort->HostPRB);
> +    ASSERT_EFI_ERROR (Status);
> +
> +    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDEXECFIFO_REG, EmptySlot);
> +  }
> +
> +#if 0
> +  // Could need to be implemented if we run multiple command in parallel to know which slot has been completed
> +  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_SLOTSTATUS_REG, &Value32);
> +  Timeout = Packet->Timeout;
> +  while (!Timeout && !Value32) {
> +    gBS->Stall (1);
> +    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_SLOTSTATUS_REG, &Value32);
> +    Timeout--;
> +  }
> +#else
> +  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
> +  if (!Packet->Timeout) {
> +    while (!(Value32 & IrqMask)) {
> +      gBS->Stall (1);
> +      SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
> +    }
> +  } else {
> +    Timeout = Packet->Timeout;
> +    while (Timeout && !(Value32 & IrqMask)) {
> +      gBS->Stall (1);
> +      SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
> +      Timeout--;
> +    }
> +  }
> +#endif
> +  // Fill Packet Ata Status Block
> +  Status = PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 1, // Bar 1
> +      SataPort->RegBase + 0x08,
> +      sizeof (EFI_ATA_STATUS_BLOCK) / 4,
> +      Packet->Asb);
> +  ASSERT_EFI_ERROR (Status);
> +
> +
> +  if ((Packet->Timeout != 0) && (Timeout == 0)) {
> +    DEBUG ((EFI_D_ERROR, "SiI3132AtaPassThru() Err:Timeout\n"));
> +    //ASSERT (0);
> +    return EFI_TIMEOUT;
> +  } else if (Value32 & (SII3132_PORT_INT_CMDERR << 16)) {
> +    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_CMDERROR_REG, &Error);
> +    DEBUG ((EFI_D_ERROR, "SiI3132AtaPassThru() CmdErr:0x%X (SiI3132 Err:0x%X)\n", Value32, Error));
> +    ASSERT (0);
> +    return EFI_DEVICE_ERROR;
> +  } else if (Value32 & (SII3132_PORT_INT_CMDCOMPL << 16)) {
> +    // Clear Command Complete
> +    SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, SII3132_PORT_INT_CMDCOMPL << 16);
> +
> +    if (PciAllocMapping) {
> +      Status = PciIo->Unmap (PciIo, PciAllocMapping);
> +      ASSERT (!EFI_ERROR (Status));
> +    }
> +
> +    // If the command was ATA_CMD_IDENTIFY_DRIVE then we need to update the BlockSize
> +    if (Packet->Acb->AtaCommand == ATA_CMD_IDENTIFY_DRIVE) {
> +      ATA_IDENTIFY_DATA *IdentifyData = (ATA_IDENTIFY_DATA*)Packet->InDataBuffer;
> +
> +      // Get the corresponding Block Device
> +      SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);
> +
> +      // Check logical block size
> +      if ((IdentifyData->phy_logic_sector_support & BIT12) != 0) {
> +        ASSERT (SataDevice != NULL);
> +        SataDevice->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |
> +                                            IdentifyData->logic_sector_size_lo) * sizeof (UINT16));
> +      } else {
> +        SataDevice->BlockSize = 0x200;
> +      }
> +    }
> +    return EFI_SUCCESS;
> +  } else {
> +    ASSERT (0);
> +    return EFI_DEVICE_ERROR;
> +  }
> +}
> +
> +/**
> +  Sends an ATA command to an ATA device that is attached to the ATA controller. This function
> +  supports both blocking I/O and non-blocking I/O. The blocking I/O functionality is required,
> +  and the non-blocking I/O functionality is optional.
> +
> +  @param[in]     This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in]     Port                The port number of the ATA device to send the command.
> +  @param[in]     PortMultiplierPort  The port multiplier port number of the ATA device to send the command.
> +                                     If there is no port multiplier, then specify 0.
> +  @param[in,out] Packet              A pointer to the ATA command to send to the ATA device specified by Port
> +                                     and PortMultiplierPort.
> +  @param[in]     Event               If non-blocking I/O is not supported then Event is ignored, and blocking
> +                                     I/O is performed. If Event is NULL, then blocking I/O is performed. If
> +                                     Event is not NULL and non blocking I/O is supported, then non-blocking
> +                                     I/O is performed, and Event will be signaled when the ATA command completes.
> +
> +  @retval EFI_SUCCESS                The ATA command was sent by the host. For bi-directional commands,
> +                                     InTransferLength bytes were transferred from InDataBuffer. For write and
> +                                     bi-directional commands, OutTransferLength bytes were transferred by OutDataBuffer.
> +  @retval EFI_BAD_BUFFER_SIZE        The ATA command was not executed. The number of bytes that could be transferred
> +                                     is returned in InTransferLength. For write and bi-directional commands,
> +                                     OutTransferLength bytes were transferred by OutDataBuffer.
> +  @retval EFI_NOT_READY              The ATA command could not be sent because there are too many ATA commands
> +                                     already queued. The caller may retry again later.
> +  @retval EFI_DEVICE_ERROR           A device error occurred while attempting to send the ATA command.
> +  @retval EFI_INVALID_PARAMETER      Port, PortMultiplierPort, or the contents of Acb are invalid. The ATA
> +                                     command was not sent, so no additional status information is available.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132AtaPassThru (
> +  IN     EFI_ATA_PASS_THRU_PROTOCOL       *This,
> +  IN     UINT16                           Port,
> +  IN     UINT16                           PortMultiplierPort,
> +  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,
> +  IN     EFI_EVENT                        Event OPTIONAL
> +  )
> +{
> +  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
> +  SATA_SI3132_DEVICE      *SataDevice;
> +  SATA_SI3132_PORT        *SataPort;
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);
> +  if (!SataDevice) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  SataPort = SataDevice->Port;
> +
> +  DEBUG ((EFI_D_INFO, "SiI3132AtaPassThru(%d,%d) : AtaCmd:0x%X Prot:%d\n", Port, PortMultiplierPort,
> +         Packet->Acb->AtaCommand, Packet->Protocol));
> +
> +  return SiI3132AtaPassThruCommand (SataSiI3132Instance, SataPort, PortMultiplierPort, Packet, Event);
> +}
> +
> +/**
> +  Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.
> +  These can either be the list of ports where ATA devices are actually present or the
> +  list of legal port numbers for the ATA controller. Regardless, the caller of this
> +  function must probe the port number returned to see if an ATA device is actually
> +  present at that location on the ATA controller.
> +
> +  The GetNextPort() function retrieves the port number on an ATA controller. If on input
> +  Port is 0xFFFF, then the port number of the first port on the ATA controller is returned
> +  in Port and EFI_SUCCESS is returned.
> +
> +  If Port is a port number that was returned on a previous call to GetNextPort(), then the
> +  port number of the next port on the ATA controller is returned in Port, and EFI_SUCCESS
> +  is returned. If Port is not 0xFFFF and Port was not returned on a previous call to
> +  GetNextPort(), then EFI_INVALID_PARAMETER is returned.
> +
> +  If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND is
> +  returned.
> +
> +  @param[in]     This           A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in,out] Port           On input, a pointer to the port number on the ATA controller.
> +                                On output, a pointer to the next port number on the ATA
> +                                controller. An input value of 0xFFFF retrieves the first port
> +                                number on the ATA controller.
> +
> +  @retval EFI_SUCCESS           The next port number on the ATA controller was returned in Port.
> +  @retval EFI_NOT_FOUND         There are no more ports on this ATA controller.
> +  @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned on a previous call
> +                                to GetNextPort().
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132GetNextPort (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN OUT UINT16                 *Port
> +  )
> +{
> +  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
> +  UINTN                   PrevPort;
> +  EFI_STATUS              Status = EFI_SUCCESS;
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  PrevPort = *Port;
> +
> +  if (PrevPort == 0xFFFF) {
> +    *Port = 0;
> +  } else {
> +    if (PrevPort < SATA_SII3132_MAXPORT) {
> +        *Port = PrevPort + 1;
> +    } else {
> +        Status = EFI_NOT_FOUND;
> +    }
> +  }
> +  return Status;
> +}
> +
> +/**
> +  Used to retrieve the list of legal port multiplier port numbers for ATA devices on a port of an ATA
> +  controller. These can either be the list of port multiplier ports where ATA devices are actually
> +  present on port or the list of legal port multiplier ports on that port. Regardless, the caller of this
> +  function must probe the port number and port multiplier port number returned to see if an ATA
> +  device is actually present.
> +
> +  The GetNextDevice() function retrieves the port multiplier port number of an ATA device
> +  present on a port of an ATA controller.
> +
> +  If PortMultiplierPort points to a port multiplier port number value that was returned on a
> +  previous call to GetNextDevice(), then the port multiplier port number of the next ATA device
> +  on the port of the ATA controller is returned in PortMultiplierPort, and EFI_SUCCESS is
> +  returned.
> +
> +  If PortMultiplierPort points to 0xFFFF, then the port multiplier port number of the first
> +  ATA device on port of the ATA controller is returned in PortMultiplierPort and
> +  EFI_SUCCESS is returned.
> +
> +  If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort
> +  was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER
> +  is returned.
> +
> +  If PortMultiplierPort is the port multiplier port number of the last ATA device on the port of
> +  the ATA controller, then EFI_NOT_FOUND is returned.
> +
> +  @param[in]     This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in]     Port                The port number present on the ATA controller.
> +  @param[in,out] PortMultiplierPort  On input, a pointer to the port multiplier port number of an
> +                                     ATA device present on the ATA controller.
> +                                     If on input a PortMultiplierPort of 0xFFFF is specified,
> +                                     then the port multiplier port number of the first ATA device
> +                                     is returned. On output, a pointer to the port multiplier port
> +                                     number of the next ATA device present on an ATA controller.
> +
> +  @retval EFI_SUCCESS                The port multiplier port number of the next ATA device on the port
> +                                     of the ATA controller was returned in PortMultiplierPort.
> +  @retval EFI_NOT_FOUND              There are no more ATA devices on this port of the ATA controller.
> +  @retval EFI_INVALID_PARAMETER      PortMultiplierPort is not 0xFFFF, and PortMultiplierPort was not
> +                                     returned on a previous call to GetNextDevice().
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132GetNextDevice (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN UINT16                     Port,
> +  IN OUT UINT16                 *PortMultiplierPort
> +  )
> +{
> +  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
> +  SATA_SI3132_PORT        *SataPort;
> +  SATA_SI3132_DEVICE      *SataDevice;
> +  LIST_ENTRY              *List;
> +  EFI_STATUS              Status = EFI_SUCCESS;
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (Port >= SATA_SII3132_MAXPORT) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SataPort = &(SataSiI3132Instance->Ports[Port]);
> +
> +  if (*PortMultiplierPort == 0xFFFF) {
> +    List = SataPort->Devices.ForwardLink;
> +    if (List != &SataPort->Devices) {
> +      // The list is not empty, return the first device
> +      *PortMultiplierPort = ((SATA_SI3132_DEVICE*)List)->Index;
> +    } else {
> +      Status = EFI_NOT_FOUND;
> +    }
> +  } else {
> +    SataDevice = GetSataDevice (SataSiI3132Instance, Port, *PortMultiplierPort);
> +    if (SataDevice != NULL) {
> +      // We have found the previous port multiplier, return the next one
> +      List = SataDevice->Link.ForwardLink;
> +      if (List != &SataPort->Devices) {
> +        *PortMultiplierPort = ((SATA_SI3132_DEVICE*)List)->Index;
> +      } else {
> +        Status = EFI_NOT_FOUND;
> +      }
> +    } else {
> +      Status = EFI_NOT_FOUND;
> +    }
> +  }
> +  return Status;
> +}
> +
> +/**
> +  Used to allocate and build a device path node for an ATA device on an ATA controller.
> +
> +  The BuildDevicePath() function allocates and builds a single device node for the ATA
> +  device specified by Port and PortMultiplierPort. If the ATA device specified by Port and
> +  PortMultiplierPort is not present on the ATA controller, then EFI_NOT_FOUND is returned.
> +  If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. If there are not enough
> +  resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned.
> +
> +  Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of
> +  DevicePath are initialized to describe the ATA device specified by Port and PortMultiplierPort,
> +  and EFI_SUCCESS is returned.
> +
> +  @param[in]     This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in]     Port                Port specifies the port number of the ATA device for which a
> +                                     device path node is to be allocated and built.
> +  @param[in]     PortMultiplierPort  The port multiplier port number of the ATA device for which a
> +                                     device path node is to be allocated and built. If there is no
> +                                     port multiplier, then specify 0.
> +  @param[in,out] DevicePath          A pointer to a single device path node that describes the ATA
> +                                     device specified by Port and PortMultiplierPort. This function
> +                                     is responsible for allocating the buffer DevicePath with the
> +                                     boot service AllocatePool(). It is the caller's responsibility
> +                                     to free DevicePath when the caller is finished with DevicePath.
> +  @retval EFI_SUCCESS                The device path node that describes the ATA device specified by
> +                                     Port and PortMultiplierPort was allocated and returned in DevicePath.
> +  @retval EFI_NOT_FOUND              The ATA device specified by Port and PortMultiplierPort does not
> +                                     exist on the ATA controller.
> +  @retval EFI_INVALID_PARAMETER      DevicePath is NULL.
> +  @retval EFI_OUT_OF_RESOURCES       There are not enough resources to allocate DevicePath.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132BuildDevicePath (
> +  IN     EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN     UINT16                     Port,
> +  IN     UINT16                     PortMultiplierPort,
> +  IN OUT EFI_DEVICE_PATH_PROTOCOL   **DevicePath
> +  )
> +{
> +  SATA_SI3132_INSTANCE        *SataSiI3132Instance;
> +  SATA_SI3132_DEVICE          *SataDevice;
> +  EFI_DEVICE_PATH_PROTOCOL    *SiI3132DevicePath;
> +
> +  SATA_TRACE ("SiI3132BuildDevicePath()");
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);
> +  if (SataDevice == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  SiI3132DevicePath = CreateDeviceNode (MESSAGING_DEVICE_PATH, MSG_SATA_DP, sizeof (SATA_DEVICE_PATH));
> +  if (SiI3132DevicePath == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  ((SATA_DEVICE_PATH*)SiI3132DevicePath)->HBAPortNumber = Port;
> +  if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
> +    ((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = PortMultiplierPort;
> +  } else {
> +    //Temp:((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = SATA_HBA_DIRECT_CONNECT_FLAG;
> +    ((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = 0;
> +  }
> +  ((SATA_DEVICE_PATH*)SiI3132DevicePath)->Lun = Port; //TODO: Search information how to define properly LUN (Logical Unit Number)
> +
> +  *DevicePath = SiI3132DevicePath;
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Used to translate a device path node to a port number and port multiplier port number.
> +
> +  The GetDevice() function determines the port and port multiplier port number associated with
> +  the ATA device described by DevicePath. If DevicePath is a device path node type that the
> +  ATA Pass Thru driver supports, then the ATA Pass Thru driver will attempt to translate the contents
> +  DevicePath into a port number and port multiplier port number.
> +
> +  If this translation is successful, then that port number and port multiplier port number are returned
> +  in Port and PortMultiplierPort, and EFI_SUCCESS is returned.
> +
> +  If DevicePath, Port, or PortMultiplierPort are NULL, then EFI_INVALID_PARAMETER is returned.
> +
> +  If DevicePath is not a device path node type that the ATA Pass Thru driver supports, then
> +  EFI_UNSUPPORTED is returned.
> +
> +  If DevicePath is a device path node type that the ATA Pass Thru driver supports, but there is not
> +  a valid translation from DevicePath to a port number and port multiplier port number, then
> +  EFI_NOT_FOUND is returned.
> +
> +  @param[in]  This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in]  DevicePath          A pointer to the device path node that describes an ATA device on the
> +                                  ATA controller.
> +  @param[out] Port                On return, points to the port number of an ATA device on the ATA controller.
> +  @param[out] PortMultiplierPort  On return, points to the port multiplier port number of an ATA device
> +                                  on the ATA controller.
> +
> +  @retval EFI_SUCCESS             DevicePath was successfully translated to a port number and port multiplier
> +                                  port number, and they were returned in Port and PortMultiplierPort.
> +  @retval EFI_INVALID_PARAMETER   DevicePath is NULL.
> +  @retval EFI_INVALID_PARAMETER   Port is NULL.
> +  @retval EFI_INVALID_PARAMETER   PortMultiplierPort is NULL.
> +  @retval EFI_UNSUPPORTED         This driver does not support the device path node type in DevicePath.
> +  @retval EFI_NOT_FOUND           A valid translation from DevicePath to a port number and port multiplier
> +                                  port number does not exist.
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132GetDevice (
> +  IN  EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN  EFI_DEVICE_PATH_PROTOCOL   *DevicePath,
> +  OUT UINT16                     *Port,
> +  OUT UINT16                     *PortMultiplierPort
> +  )
> +{
> +  SATA_SI3132_INSTANCE        *SataSiI3132Instance;
> +
> +  SATA_TRACE ("SiI3132GetDevice()");
> +
> +  if (!DevicePath || !Port || !PortMultiplierPort) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_SATA_DP)) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (((SATA_DEVICE_PATH*)DevicePath)->Lun >= SATA_SII3132_MAXPORT) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {
> +    ASSERT (0); //TODO: Implement me!
> +    return EFI_UNSUPPORTED;
> +  } else {
> +    *Port = ((SATA_DEVICE_PATH*)DevicePath)->Lun;
> +    // Return the first Sata Device as there should be only one directly connected
> +    *PortMultiplierPort = ((SATA_SI3132_DEVICE*)SataSiI3132Instance->Ports[*Port].Devices.ForwardLink)->Index;
> +    return EFI_SUCCESS;
> +  }
> +}
> +
> +EFI_STATUS
> +SiI3132HwResetPort (
> +  IN SATA_SI3132_PORT *SataPort
> +  )
> +{
> +  EFI_PCI_IO_PROTOCOL *PciIo;
> +  UINT32              Value32;
> +  UINTN               Timeout;
> +
> +  SATA_TRACE ("SiI3132HwResetPort()");
> +
> +  PciIo = SataPort->Instance->PciIo;
> +
> +  // Clear Port Reset
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CONTROLCLEAR_REG, SII3132_PORT_CONTROL_RESET);
> +
> +  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);
> +  ASSERT (!(Value32 & SII3132_PORT_CONTROL_RESET));
> +
> +  // Initialize error counters
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTDECODE, 0);
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTCRC, 0);
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTHANDSHAKE, 0);
> +
> +  // Enable interrupts for command completion and command errors
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ENABLEINT_REG, SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR);
> +
> +  // Clear IRQ
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ENABLEINT_REG, SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR | SII3132_PORT_INT_PORTRDY | (1 << 3));
> +
> +  // Wait until Port Ready
> +  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
> +  Timeout = 1000;
> +  while ((Timeout > 0) && ((Value32 & SII3132_PORT_INT_PORTRDY) == 0)) {
> +    gBS->Stall (1);
> +    Timeout--;
> +    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);
> +  }
> +  // Clear IRQ
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, SII3132_PORT_INT_PORTRDY);
> +
> +  if (Timeout == 0) {
> +    SATA_TRACE ("SiI3132HwResetPort(): Timeout");
> +    return EFI_TIMEOUT;
> +  } else if ((Value32 & SII3132_PORT_INT_PORTRDY) == 0) {
> +    SATA_TRACE ("SiI3132HwResetPort(): Port Not Ready");
> +    return EFI_DEVICE_ERROR;
> +  } else {
> +    return EFI_SUCCESS;
> +  }
> +}
> +
> +/**
> +  Resets a specific port on the ATA controller. This operation also resets all the ATA devices
> +  connected to the port.
> +
> +  The ResetChannel() function resets an a specific port on an ATA controller. This operation
> +  resets all the ATA devices connected to that port. If this ATA controller does not support
> +  a reset port operation, then EFI_UNSUPPORTED is returned.
> +
> +  If a device error occurs while executing that port reset operation, then EFI_DEVICE_ERROR is
> +  returned.
> +
> +  If a timeout occurs during the execution of the port reset operation, then EFI_TIMEOUT is returned.
> +
> +  If the port reset operation is completed, then EFI_SUCCESS is returned.
> +
> +  @param[in]  This          A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in]  Port          The port number on the ATA controller.
> +
> +  @retval EFI_SUCCESS       The ATA controller port was reset.
> +  @retval EFI_UNSUPPORTED   The ATA controller does not support a port reset operation.
> +  @retval EFI_DEVICE_ERROR  A device error occurred while attempting to reset the ATA port.
> +  @retval EFI_TIMEOUT       A timeout occurred while attempting to reset the ATA port.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132ResetPort (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN UINT16                     Port
> +  )
> +{
> +  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
> +  SATA_SI3132_PORT        *SataPort;
> +
> +  SATA_TRACE ("SiI3132ResetPort()");
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (Port >= SATA_SII3132_MAXPORT) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  SataPort = &(SataSiI3132Instance->Ports[Port]);
> +  return SiI3132HwResetPort (SataPort);
> +}
> +
> +/**
> +  Resets an ATA device that is connected to an ATA controller.
> +
> +  The ResetDevice() function resets the ATA device specified by Port and PortMultiplierPort.
> +  If this ATA controller does not support a device reset operation, then EFI_UNSUPPORTED is
> +  returned.
> +
> +  If Port or PortMultiplierPort are not in a valid range for this ATA controller, then
> +  EFI_INVALID_PARAMETER is returned.
> +
> +  If a device error occurs while executing that device reset operation, then EFI_DEVICE_ERROR
> +  is returned.
> +
> +  If a timeout occurs during the execution of the device reset operation, then EFI_TIMEOUT is
> +  returned.
> +
> +  If the device reset operation is completed, then EFI_SUCCESS is returned.
> +
> +  @param[in] This                A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.
> +  @param[in] Port                Port represents the port number of the ATA device to be reset.
> +  @param[in] PortMultiplierPort  The port multiplier port number of the ATA device to reset.
> +                                 If there is no port multiplier, then specify 0.
> +  @retval EFI_SUCCESS            The ATA device specified by Port and PortMultiplierPort was reset.
> +  @retval EFI_UNSUPPORTED        The ATA controller does not support a device reset operation.
> +  @retval EFI_INVALID_PARAMETER  Port or PortMultiplierPort are invalid.
> +  @retval EFI_DEVICE_ERROR       A device error occurred while attempting to reset the ATA device
> +                                 specified by Port and PortMultiplierPort.
> +  @retval EFI_TIMEOUT            A timeout occurred while attempting to reset the ATA device
> +                                 specified by Port and PortMultiplierPort.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SiI3132ResetDevice (
> +  IN EFI_ATA_PASS_THRU_PROTOCOL *This,
> +  IN UINT16                     Port,
> +  IN UINT16                     PortMultiplierPort
> +  )
> +{
> +  EFI_PCI_IO_PROTOCOL     *PciIo;
> +  SATA_SI3132_INSTANCE    *SataSiI3132Instance;
> +  SATA_SI3132_PORT        *SataPort;
> +  SATA_SI3132_DEVICE      *SataDevice;
> +  UINTN                   Timeout;
> +  UINT32                  Value32;
> +
> +  SATA_TRACE ("SiI3132ResetDevice()");
> +
> +  SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);
> +  if (!SataSiI3132Instance) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  PciIo = SataSiI3132Instance->PciIo;
> +
> +  SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);
> +  if (!SataDevice) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  SataPort = SataDevice->Port;
> +
> +  SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CONTROLSET_REG, SII3132_PORT_DEVICE_RESET);
> +
> +  Timeout = 100;
> +  SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);
> +  while ((Timeout > 0) && ((Value32 & SII3132_PORT_DEVICE_RESET) != 0)) {
> +    gBS->Stall (1);
> +    SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);
> +    Timeout--;
> +  }
> +
> +  if (Timeout == 0) {
> +    SATA_TRACE ("SiI3132ResetDevice(): Timeout");
> +    return EFI_TIMEOUT;
> +  } else {
> +    return EFI_SUCCESS;
> +  }
> +}
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg Ard Biesheuvel
@ 2020-05-04 10:58   ` Leif Lindholm
  2020-05-04 11:40     ` [edk2-devel] " Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Leif Lindholm @ 2020-05-04 10:58 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: devel

On Thu, Apr 30, 2020 at 19:16:45 +0200, Ard Biesheuvel wrote:
> Incorporate the driver for the DesignWare eMMC host controller that is
> based on the obsolete MMC host controller protocol that is defined in
> EmbeddedPkg.
> 
> This driver does not follow the UEFI driver model, and is only kept
> around for its only users, which is the HiKey platform, which is
> rapidly reaching obsolescence itself, at which point this driver may
> be removed again.
> 
> To prevent inadvertent use in new platforms, add a PCD that needs to
> be changed from its default value in order for the driver to be
> functional.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> ---
>  Silicon/Synopsys/DesignWare/DesignWare.dec                  |   9 +
>  Silicon/Synopsys/DesignWare/DesignWare.dsc                  |   2 +
>  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h      | 132 ++++
>  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c   | 693 ++++++++++++++++++++
>  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf |  56 ++
>  5 files changed, 892 insertions(+)
> 
> diff --git a/Silicon/Synopsys/DesignWare/DesignWare.dec b/Silicon/Synopsys/DesignWare/DesignWare.dec
> index 71ddd24b7404..f7ec7927543c 100755
> --- a/Silicon/Synopsys/DesignWare/DesignWare.dec
> +++ b/Silicon/Synopsys/DesignWare/DesignWare.dec
> @@ -21,4 +21,13 @@ [Guids.common]
>    gDesignWareTokenSpaceGuid = { 0x89cb1241, 0xd283, 0x4543, { 0x88, 0x9c, 0x6b, 0x62, 0x36, 0x1a, 0x95, 0x7a } }
>    gDwEmacNetNonDiscoverableDeviceGuid = { 0x401950CD, 0xF9CD, 0x4A65, { 0xAD, 0x8E, 0x84, 0x9F, 0x3B, 0xAF, 0x23, 0x04 } }
>  
> +[PcdsFixedAtBuild.common]
> +  #
> +  # Permit the use of obsolete drivers in this package
> +  #
> +  gDesignWareTokenSpaceGuid.PcdDwPermitObsoleteDrivers|FALSE|BOOLEAN|0x00000001
>  
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000002
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000003
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeMaxClockFreqInHz|0x0|UINT32|0x00000004
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeFifoDepth|0x0|UINT32|0x00000005
> diff --git a/Silicon/Synopsys/DesignWare/DesignWare.dsc b/Silicon/Synopsys/DesignWare/DesignWare.dsc
> index ad6a5ede4ae0..098bba3f7d68 100755
> --- a/Silicon/Synopsys/DesignWare/DesignWare.dsc
> +++ b/Silicon/Synopsys/DesignWare/DesignWare.dsc
> @@ -20,6 +20,7 @@ [LibraryClasses]
>    ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
>    BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
>    BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
> +  CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
>    DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
>    DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
>    DmaLib|EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
> @@ -39,3 +40,4 @@ [LibraryClasses]
>  
>  [Components]
>    Silicon/Synopsys/DesignWare/Drivers/DwEmacSnpDxe/DwEmacSnpDxe.inf
> +  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
> diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
> new file mode 100644
> index 000000000000..09ad9b8428c4
> --- /dev/null
> +++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
> @@ -0,0 +1,132 @@
> +/** @file
> +*
> +*  WARNING:
> +*  This driver fails to follow the UEFI driver model without a good
> +*  reason, and only remains in the tree because it is still used by
> +*  a small number of platforms. It will be removed when no longer used.
> +*
> +*  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
> +*
> +*  SPDX-License-Identifier: BSD-2-Clause-Patent
> +*
> +**/
> +
> +
> +#ifndef __DWEMMC_H__
> +#define __DWEMMC_H__
> +
> +#include <Protocol/EmbeddedGpio.h>
> +
> +// DW MMC Registers
> +#define DWEMMC_CTRL             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
> +#define DWEMMC_PWREN            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
> +#define DWEMMC_CLKDIV           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
> +#define DWEMMC_CLKSRC           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
> +#define DWEMMC_CLKENA           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
> +#define DWEMMC_TMOUT            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
> +#define DWEMMC_CTYPE            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
> +#define DWEMMC_BLKSIZ           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
> +#define DWEMMC_BYTCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
> +#define DWEMMC_INTMASK          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
> +#define DWEMMC_CMDARG           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
> +#define DWEMMC_CMD              ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
> +#define DWEMMC_RESP0            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
> +#define DWEMMC_RESP1            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
> +#define DWEMMC_RESP2            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
> +#define DWEMMC_RESP3            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
> +#define DWEMMC_RINTSTS          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
> +#define DWEMMC_STATUS           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
> +#define DWEMMC_FIFOTH           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
> +#define DWEMMC_TCBCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x05c)
> +#define DWEMMC_TBBCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x060)
> +#define DWEMMC_DEBNCE           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
> +#define DWEMMC_HCON             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x070)
> +#define DWEMMC_UHSREG           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
> +#define DWEMMC_BMOD             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
> +#define DWEMMC_DBADDR           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
> +#define DWEMMC_IDSTS            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
> +#define DWEMMC_IDINTEN          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
> +#define DWEMMC_DSCADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
> +#define DWEMMC_BUFADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
> +#define DWEMMC_CARDTHRCTL       ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
> +#define DWEMMC_DATA             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X200)
> +
> +#define CMD_UPDATE_CLK                          0x80202000
> +#define CMD_START_BIT                           (1 << 31)
> +
> +#define MMC_8BIT_MODE                           (1 << 16)
> +
> +#define BIT_CMD_RESPONSE_EXPECT                 (1 << 6)
> +#define BIT_CMD_LONG_RESPONSE                   (1 << 7)
> +#define BIT_CMD_CHECK_RESPONSE_CRC              (1 << 8)
> +#define BIT_CMD_DATA_EXPECTED                   (1 << 9)
> +#define BIT_CMD_READ                            (0 << 10)
> +#define BIT_CMD_WRITE                           (1 << 10)
> +#define BIT_CMD_BLOCK_TRANSFER                  (0 << 11)
> +#define BIT_CMD_STREAM_TRANSFER                 (1 << 11)
> +#define BIT_CMD_SEND_AUTO_STOP                  (1 << 12)
> +#define BIT_CMD_WAIT_PRVDATA_COMPLETE           (1 << 13)
> +#define BIT_CMD_STOP_ABORT_CMD                  (1 << 14)
> +#define BIT_CMD_SEND_INIT                       (1 << 15)
> +#define BIT_CMD_UPDATE_CLOCK_ONLY               (1 << 21)
> +#define BIT_CMD_READ_CEATA_DEVICE               (1 << 22)
> +#define BIT_CMD_CCS_EXPECTED                    (1 << 23)
> +#define BIT_CMD_ENABLE_BOOT                     (1 << 24)
> +#define BIT_CMD_EXPECT_BOOT_ACK                 (1 << 25)
> +#define BIT_CMD_DISABLE_BOOT                    (1 << 26)
> +#define BIT_CMD_MANDATORY_BOOT                  (0 << 27)
> +#define BIT_CMD_ALTERNATE_BOOT                  (1 << 27)
> +#define BIT_CMD_VOLT_SWITCH                     (1 << 28)
> +#define BIT_CMD_USE_HOLD_REG                    (1 << 29)
> +#define BIT_CMD_START                           (1 << 31)
> +
> +#define DWEMMC_INT_EBE                          (1 << 15)       /* End-bit Err */
> +#define DWEMMC_INT_SBE                          (1 << 13)       /* Start-bit  Err */
> +#define DWEMMC_INT_HLE                          (1 << 12)       /* Hardware-lock Err */
> +#define DWEMMC_INT_FRUN                         (1 << 11)       /* FIFO UN/OV RUN */
> +#define DWEMMC_INT_DRT                          (1 << 9)        /* Data timeout */
> +#define DWEMMC_INT_RTO                          (1 << 8)        /* Response timeout */
> +#define DWEMMC_INT_DCRC                         (1 << 7)        /* Data CRC err */
> +#define DWEMMC_INT_RCRC                         (1 << 6)        /* Response CRC err */
> +#define DWEMMC_INT_RXDR                         (1 << 5)
> +#define DWEMMC_INT_TXDR                         (1 << 4)
> +#define DWEMMC_INT_DTO                          (1 << 3)        /* Data trans over */
> +#define DWEMMC_INT_CMD_DONE                     (1 << 2)
> +#define DWEMMC_INT_RE                           (1 << 1)
> +
> +#define DWEMMC_IDMAC_DES0_DIC                   (1 << 1)
> +#define DWEMMC_IDMAC_DES0_LD                    (1 << 2)
> +#define DWEMMC_IDMAC_DES0_FS                    (1 << 3)
> +#define DWEMMC_IDMAC_DES0_CH                    (1 << 4)
> +#define DWEMMC_IDMAC_DES0_ER                    (1 << 5)
> +#define DWEMMC_IDMAC_DES0_CES                   (1 << 30)
> +#define DWEMMC_IDMAC_DES0_OWN                   (1 << 31)
> +#define DWEMMC_IDMAC_DES1_BS1(x)                ((x) & 0x1fff)
> +#define DWEMMC_IDMAC_DES2_BS2(x)                (((x) & 0x1fff) << 13)
> +#define DWEMMC_IDMAC_SWRESET                    (1 << 0)
> +#define DWEMMC_IDMAC_FB                         (1 << 1)
> +#define DWEMMC_IDMAC_ENABLE                     (1 << 7)
> +
> +#define EMMC_FIX_RCA                            6
> +
> +/* bits in MMC0_CTRL */
> +#define DWEMMC_CTRL_RESET                       (1 << 0)
> +#define DWEMMC_CTRL_FIFO_RESET                  (1 << 1)
> +#define DWEMMC_CTRL_DMA_RESET                   (1 << 2)
> +#define DWEMMC_CTRL_INT_EN                      (1 << 4)
> +#define DWEMMC_CTRL_DMA_EN                      (1 << 5)
> +#define DWEMMC_CTRL_IDMAC_EN                    (1 << 25)
> +#define DWEMMC_CTRL_RESET_ALL                   (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
> +
> +#define DWEMMC_STS_DATA_BUSY                    (1 << 9)
> +
> +#define DWEMMC_FIFO_TWMARK(x)                   (x & 0xfff)
> +#define DWEMMC_FIFO_RWMARK(x)                   ((x & 0x1ff) << 16)
> +#define DWEMMC_DMA_BURST_SIZE(x)                ((x & 0x7) << 28)
> +
> +#define DWEMMC_CARD_RD_THR(x)                   ((x & 0xfff) << 16)
> +#define DWEMMC_CARD_RD_THR_EN                   (1 << 0)
> +
> +#define DWEMMC_GET_HDATA_WIDTH(x)               (((x) >> 7) & 0x7)
> +
> +#endif  // __DWEMMC_H__
> diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
> new file mode 100644
> index 000000000000..eed5fc57fc22
> --- /dev/null
> +++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
> @@ -0,0 +1,693 @@
> +/** @file
> +  This file implement the MMC Host Protocol for the DesignWare eMMC.
> +
> +  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent

Comment only:
This file, unlike some other .c files in this series, does not include
the WARNING header. Feel free to fold one in before pushing.

/
    Leif

> +
> +**/
> +
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DevicePathLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/TimerLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +
> +#include <Protocol/MmcHost.h>
> +
> +#include "DwEmmc.h"
> +
> +#define DWEMMC_DESC_PAGE                1
> +#define DWEMMC_BLOCK_SIZE               512
> +#define DWEMMC_DMA_BUF_SIZE             (512 * 8)
> +#define DWEMMC_MAX_DESC_PAGES           512
> +
> +typedef struct {
> +  UINT32                        Des0;
> +  UINT32                        Des1;
> +  UINT32                        Des2;
> +  UINT32                        Des3;
> +} DWEMMC_IDMAC_DESCRIPTOR;
> +
> +EFI_MMC_HOST_PROTOCOL     *gpMmcHost;
> +DWEMMC_IDMAC_DESCRIPTOR   *gpIdmacDesc;
> +EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
> +STATIC UINT32 mDwEmmcCommand;
> +STATIC UINT32 mDwEmmcArgument;
> +
> +EFI_STATUS
> +DwEmmcReadBlockData (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN EFI_LBA                    Lba,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  );
> +
> +BOOLEAN
> +DwEmmcIsPowerOn (
> +  VOID
> +  )
> +{
> +    return TRUE;
> +}
> +
> +EFI_STATUS
> +DwEmmcInitialize (
> +  VOID
> +  )
> +{
> +    DEBUG ((DEBUG_BLKIO, "DwEmmcInitialize()"));
> +    return EFI_SUCCESS;
> +}
> +
> +BOOLEAN
> +DwEmmcIsCardPresent (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +BOOLEAN
> +DwEmmcIsReadOnly (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return FALSE;
> +}
> +
> +BOOLEAN
> +DwEmmcIsDmaSupported (
> +  IN EFI_MMC_HOST_PROTOCOL     *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +EFI_STATUS
> +DwEmmcBuildDevicePath (
> +  IN EFI_MMC_HOST_PROTOCOL      *This,
> +  IN EFI_DEVICE_PATH_PROTOCOL   **DevicePath
> +  )
> +{
> +  EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
> +
> +  NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH));
> +  CopyGuid (& ((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &mDwEmmcDevicePathGuid);
> +
> +  *DevicePath = NewDevicePathNode;
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcUpdateClock (
> +  VOID
> +  )
> +{
> +  UINT32 Data;
> +
> +  /* CMD_UPDATE_CLK */
> +  Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
> +         BIT_CMD_START;
> +  MmioWrite32 (DWEMMC_CMD, Data);
> +  while (1) {
> +    Data = MmioRead32 (DWEMMC_CMD);
> +    if (!(Data & CMD_START_BIT)) {
> +      break;
> +    }
> +    Data = MmioRead32 (DWEMMC_RINTSTS);
> +    if (Data & DWEMMC_INT_HLE) {
> +      Print (L"failed to update mmc clock frequency\n");
> +      return EFI_DEVICE_ERROR;
> +    }
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcSetClock (
> +  IN UINTN                     ClockFreq
> +  )
> +{
> +  UINT32 Divider, Rate, Data;
> +  EFI_STATUS Status;
> +  BOOLEAN Found = FALSE;
> +
> +  for (Divider = 1; Divider < 256; Divider++) {
> +    Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
> +    if ((Rate / (2 * Divider)) <= ClockFreq) {
> +      Found = TRUE;
> +      break;
> +    }
> +  }
> +  if (Found == FALSE) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  // Wait until MMC is idle
> +  do {
> +    Data = MmioRead32 (DWEMMC_STATUS);
> +  } while (Data & DWEMMC_STS_DATA_BUSY);
> +
> +  // Disable MMC clock first
> +  MmioWrite32 (DWEMMC_CLKENA, 0);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +
> +  MmioWrite32 (DWEMMC_CLKDIV, Divider);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +
> +  // Enable MMC clock
> +  MmioWrite32 (DWEMMC_CLKENA, 1);
> +  MmioWrite32 (DWEMMC_CLKSRC, 0);
> +  Status = DwEmmcUpdateClock ();
> +  ASSERT (!EFI_ERROR (Status));
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcNotifyState (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN MMC_STATE                 State
> +  )
> +{
> +  UINT32      Data;
> +  EFI_STATUS  Status;
> +
> +  switch (State) {
> +  case MmcInvalidState:
> +    return EFI_INVALID_PARAMETER;
> +  case MmcHwInitializationState:
> +    MmioWrite32 (DWEMMC_PWREN, 1);
> +
> +    // If device already turn on then restart it
> +    Data = DWEMMC_CTRL_RESET_ALL;
> +    MmioWrite32 (DWEMMC_CTRL, Data);
> +    do {
> +      // Wait until reset operation finished
> +      Data = MmioRead32 (DWEMMC_CTRL);
> +    } while (Data & DWEMMC_CTRL_RESET_ALL);
> +
> +    // Setup clock that could not be higher than 400KHz.
> +    Status = DwEmmcSetClock (400000);
> +    ASSERT (!EFI_ERROR (Status));
> +    // Wait clock stable
> +    MicroSecondDelay (100);
> +
> +    MmioWrite32 (DWEMMC_RINTSTS, ~0);
> +    MmioWrite32 (DWEMMC_INTMASK, 0);
> +    MmioWrite32 (DWEMMC_TMOUT, ~0);
> +    MmioWrite32 (DWEMMC_IDINTEN, 0);
> +    MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
> +
> +    MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
> +    do {
> +      Data = MmioRead32 (DWEMMC_BMOD);
> +    } while (Data & DWEMMC_IDMAC_SWRESET);
> +    break;
> +  case MmcIdleState:
> +    break;
> +  case MmcReadyState:
> +    break;
> +  case MmcIdentificationState:
> +    break;
> +  case MmcStandByState:
> +    break;
> +  case MmcTransferState:
> +    break;
> +  case MmcSendingDataState:
> +    break;
> +  case MmcReceiveDataState:
> +    break;
> +  case MmcProgrammingState:
> +    break;
> +  case MmcDisconnectState:
> +    break;
> +  default:
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +// Need to prepare DMA buffer first before sending commands to MMC card
> +BOOLEAN
> +IsPendingReadCommand (
> +  IN MMC_CMD                    MmcCmd
> +  )
> +{
> +  UINTN  Mask;
> +
> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
> +  if ((MmcCmd & Mask) == Mask) {
> +    return TRUE;
> +  }
> +  return FALSE;
> +}
> +
> +BOOLEAN
> +IsPendingWriteCommand (
> +  IN MMC_CMD                    MmcCmd
> +  )
> +{
> +  UINTN  Mask;
> +
> +  Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
> +  if ((MmcCmd & Mask) == Mask) {
> +    return TRUE;
> +  }
> +  return FALSE;
> +}
> +
> +EFI_STATUS
> +SendCommand (
> +  IN MMC_CMD                    MmcCmd,
> +  IN UINT32                     Argument
> +  )
> +{
> +  UINT32      Data, ErrMask;
> +
> +  // Wait until MMC is idle
> +  do {
> +    Data = MmioRead32 (DWEMMC_STATUS);
> +  } while (Data & DWEMMC_STS_DATA_BUSY);
> +
> +  MmioWrite32 (DWEMMC_RINTSTS, ~0);
> +  MmioWrite32 (DWEMMC_CMDARG, Argument);
> +  MmioWrite32 (DWEMMC_CMD, MmcCmd);
> +
> +  ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
> +            DWEMMC_INT_RCRC | DWEMMC_INT_RE;
> +  ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
> +  do {
> +    MicroSecondDelay(500);
> +    Data = MmioRead32 (DWEMMC_RINTSTS);
> +
> +    if (Data & ErrMask) {
> +      return EFI_DEVICE_ERROR;
> +    }
> +    if (Data & DWEMMC_INT_DTO) {     // Transfer Done
> +      break;
> +    }
> +  } while (!(Data & DWEMMC_INT_CMD_DONE));
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +DwEmmcSendCommand (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN MMC_CMD                    MmcCmd,
> +  IN UINT32                     Argument
> +  )
> +{
> +  UINT32       Cmd = 0;
> +  EFI_STATUS   Status = EFI_SUCCESS;
> +
> +  switch (MMC_GET_INDX(MmcCmd)) {
> +  case MMC_INDX(0):
> +    Cmd = BIT_CMD_SEND_INIT;
> +    break;
> +  case MMC_INDX(1):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT;
> +    break;
> +  case MMC_INDX(2):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE |
> +           BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT;
> +    break;
> +  case MMC_INDX(3):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_SEND_INIT;
> +    break;
> +  case MMC_INDX(7):
> +    if (Argument)
> +        Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
> +    else
> +        Cmd = 0;
> +    break;
> +  case MMC_INDX(8):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(9):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_LONG_RESPONSE;
> +    break;
> +  case MMC_INDX(12):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_STOP_ABORT_CMD;
> +    break;
> +  case MMC_INDX(13):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(16):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(17):
> +  case MMC_INDX(18):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(24):
> +  case MMC_INDX(25):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE |
> +           BIT_CMD_WAIT_PRVDATA_COMPLETE;
> +    break;
> +  case MMC_INDX(30):
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
> +           BIT_CMD_DATA_EXPECTED;
> +    break;
> +  default:
> +    Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
> +    break;
> +  }
> +
> +  Cmd |= MMC_GET_INDX(MmcCmd) | BIT_CMD_USE_HOLD_REG | BIT_CMD_START;
> +  if (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
> +    mDwEmmcCommand = Cmd;
> +    mDwEmmcArgument = Argument;
> +  } else {
> +    Status = SendCommand (Cmd, Argument);
> +  }
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcReceiveResponse (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN MMC_RESPONSE_TYPE          Type,
> +  IN UINT32*                    Buffer
> +  )
> +{
> +  if (Buffer == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (   (Type == MMC_RESPONSE_TYPE_R1)
> +      || (Type == MMC_RESPONSE_TYPE_R1b)
> +      || (Type == MMC_RESPONSE_TYPE_R3)
> +      || (Type == MMC_RESPONSE_TYPE_R6)
> +      || (Type == MMC_RESPONSE_TYPE_R7))
> +  {
> +    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
> +  } else if (Type == MMC_RESPONSE_TYPE_R2) {
> +    Buffer[0] = MmioRead32 (DWEMMC_RESP0);
> +    Buffer[1] = MmioRead32 (DWEMMC_RESP1);
> +    Buffer[2] = MmioRead32 (DWEMMC_RESP2);
> +    Buffer[3] = MmioRead32 (DWEMMC_RESP3);
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +VOID
> +DwEmmcAdjustFifoThreshold (
> +  VOID
> +  )
> +{
> +  /* DMA multiple transaction size map to reg value as array index */
> +  CONST UINT32 BurstSize[] = {1, 4, 8, 16, 32, 64, 128, 256};
> +  UINT32 BlkDepthInFifo, FifoThreshold, FifoWidth, FifoDepth;
> +  UINT32 BlkSize = DWEMMC_BLOCK_SIZE, Idx = 0, RxWatermark = 1, TxWatermark, TxWatermarkInvers;
> +
> +  /* Skip FIFO adjustment if we do not have platform FIFO depth info */
> +  FifoDepth = PcdGet32 (PcdDwEmmcDxeFifoDepth);
> +  if (!FifoDepth) {
> +    return;
> +  }
> +
> +  TxWatermark = FifoDepth / 2;
> +  TxWatermarkInvers = FifoDepth - TxWatermark;
> +
> +  FifoWidth = DWEMMC_GET_HDATA_WIDTH (MmioRead32 (DWEMMC_HCON));
> +  if (!FifoWidth) {
> +    FifoWidth = 2;
> +  } else if (FifoWidth == 2) {
> +    FifoWidth = 8;
> +  } else {
> +    FifoWidth = 4;
> +  }
> +
> +  BlkDepthInFifo = BlkSize / FifoWidth;
> +
> +  Idx = ARRAY_SIZE (BurstSize) - 1;
> +  while (Idx && ((BlkDepthInFifo % BurstSize[Idx]) || (TxWatermarkInvers % BurstSize[Idx]))) {
> +    Idx--;
> +  }
> +
> +  RxWatermark = BurstSize[Idx] - 1;
> +  FifoThreshold = DWEMMC_DMA_BURST_SIZE (Idx) | DWEMMC_FIFO_TWMARK (TxWatermark)
> +           | DWEMMC_FIFO_RWMARK (RxWatermark);
> +  MmioWrite32 (DWEMMC_FIFOTH, FifoThreshold);
> +}
> +
> +EFI_STATUS
> +PrepareDmaData (
> +  IN DWEMMC_IDMAC_DESCRIPTOR*    IdmacDesc,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  )
> +{
> +  UINTN  Cnt, Blks, Idx, LastIdx;
> +
> +  Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
> +  Length = DWEMMC_BLOCK_SIZE * Blks;
> +
> +  for (Idx = 0; Idx < Cnt; Idx++) {
> +    (IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
> +                              DWEMMC_IDMAC_DES0_DIC;
> +    (IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
> +    /* Buffer Address */
> +    (IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
> +    /* Next Descriptor Address */
> +    (IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
> +                                       (sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
> +  }
> +  /* First Descriptor */
> +  IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
> +  /* Last Descriptor */
> +  LastIdx = Cnt - 1;
> +  (IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
> +  (IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
> +  (IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
> +                                                      (LastIdx * DWEMMC_DMA_BUF_SIZE));
> +  /* Set the Next field of Last Descriptor */
> +  (IdmacDesc + LastIdx)->Des3 = 0;
> +  MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
> +
> +  return EFI_SUCCESS;
> +}
> +
> +VOID
> +StartDma (
> +  UINTN    Length
> +  )
> +{
> +  UINT32 Data;
> +
> +  Data = MmioRead32 (DWEMMC_CTRL);
> +  Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
> +  MmioWrite32 (DWEMMC_CTRL, Data);
> +  Data = MmioRead32 (DWEMMC_BMOD);
> +  Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
> +  MmioWrite32 (DWEMMC_BMOD, Data);
> +
> +  MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
> +  MmioWrite32 (DWEMMC_BYTCNT, Length);
> +}
> +
> +EFI_STATUS
> +DwEmmcReadBlockData (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN EFI_LBA                    Lba,
> +  IN UINTN                      Length,
> +  IN UINT32*                   Buffer
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT32      DescPages, CountPerPage, Count;
> +  EFI_TPL     Tpl;
> +
> +  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
> +
> +  CountPerPage = EFI_PAGE_SIZE / 16;
> +  Count = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  DescPages = (Count + CountPerPage - 1) / CountPerPage;
> +
> +  InvalidateDataCacheRange (Buffer, Length);
> +
> +  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
> +  if (EFI_ERROR (Status)) {
> +    goto out;
> +  }
> +
> +  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
> +  StartDma (Length);
> +
> +  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
> +    goto out;
> +  }
> +out:
> +  // Restore Tpl
> +  gBS->RestoreTPL (Tpl);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcWriteBlockData (
> +  IN EFI_MMC_HOST_PROTOCOL     *This,
> +  IN EFI_LBA                    Lba,
> +  IN UINTN                      Length,
> +  IN UINT32*                    Buffer
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT32      DescPages, CountPerPage, Count;
> +  EFI_TPL     Tpl;
> +
> +  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
> +
> +  CountPerPage = EFI_PAGE_SIZE / 16;
> +  Count = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
> +  DescPages = (Count + CountPerPage - 1) / CountPerPage;
> +
> +  WriteBackDataCacheRange (Buffer, Length);
> +
> +  Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
> +  if (EFI_ERROR (Status)) {
> +    goto out;
> +  }
> +
> +  WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
> +  StartDma (Length);
> +
> +  Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
> +    goto out;
> +  }
> +out:
> +  // Restore Tpl
> +  gBS->RestoreTPL (Tpl);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +DwEmmcSetIos (
> +  IN EFI_MMC_HOST_PROTOCOL      *This,
> +  IN  UINT32                    BusClockFreq,
> +  IN  UINT32                    BusWidth,
> +  IN  UINT32                    TimingMode
> +  )
> +{
> +  EFI_STATUS Status = EFI_SUCCESS;
> +  UINT32    Data;
> +
> +  if ((PcdGet32 (PcdDwEmmcDxeMaxClockFreqInHz) != 0) &&
> +      (BusClockFreq > PcdGet32 (PcdDwEmmcDxeMaxClockFreqInHz))) {
> +    return EFI_UNSUPPORTED;
> +  }
> +  if (TimingMode != EMMCBACKWARD) {
> +    Data = MmioRead32 (DWEMMC_UHSREG);
> +    switch (TimingMode) {
> +    case EMMCHS52DDR1V2:
> +    case EMMCHS52DDR1V8:
> +      Data |= 1 << 16;
> +      break;
> +    case EMMCHS52:
> +    case EMMCHS26:
> +      Data &= ~(1 << 16);
> +      break;
> +    default:
> +      return EFI_UNSUPPORTED;
> +    }
> +    MmioWrite32 (DWEMMC_UHSREG, Data);
> +  }
> +
> +  switch (BusWidth) {
> +  case 1:
> +    MmioWrite32 (DWEMMC_CTYPE, 0);
> +    break;
> +  case 4:
> +    MmioWrite32 (DWEMMC_CTYPE, 1);
> +    break;
> +  case 8:
> +    MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
> +    break;
> +  default:
> +    return EFI_UNSUPPORTED;
> +  }
> +  if (BusClockFreq) {
> +    Status = DwEmmcSetClock (BusClockFreq);
> +  }
> +  return Status;
> +}
> +
> +BOOLEAN
> +DwEmmcIsMultiBlock (
> +  IN EFI_MMC_HOST_PROTOCOL      *This
> +  )
> +{
> +  return TRUE;
> +}
> +
> +EFI_MMC_HOST_PROTOCOL gMciHost = {
> +  MMC_HOST_PROTOCOL_REVISION,
> +  DwEmmcIsCardPresent,
> +  DwEmmcIsReadOnly,
> +  DwEmmcBuildDevicePath,
> +  DwEmmcNotifyState,
> +  DwEmmcSendCommand,
> +  DwEmmcReceiveResponse,
> +  DwEmmcReadBlockData,
> +  DwEmmcWriteBlockData,
> +  DwEmmcSetIos,
> +  DwEmmcIsMultiBlock
> +};
> +
> +EFI_STATUS
> +DwEmmcDxeInitialize (
> +  IN EFI_HANDLE         ImageHandle,
> +  IN EFI_SYSTEM_TABLE   *SystemTable
> +  )
> +{
> +  EFI_STATUS    Status;
> +  EFI_HANDLE    Handle;
> +
> +  if (!FixedPcdGetBool (PcdDwPermitObsoleteDrivers)) {
> +    ASSERT (FALSE);
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  Handle = NULL;
> +
> +  DwEmmcAdjustFifoThreshold ();
> +  gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
> +  if (gpIdmacDesc == NULL) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  DEBUG ((DEBUG_BLKIO, "DwEmmcDxeInitialize()\n"));
> +
> +  //Publish Component Name, BlockIO protocol interfaces
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &Handle,
> +                  &gEmbeddedMmcHostProtocolGuid,    &gMciHost,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
> new file mode 100644
> index 000000000000..7f70fe1e2a38
> --- /dev/null
> +++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
> @@ -0,0 +1,56 @@
> +#/** @file
> +#  INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
> +#
> +#  WARNING:
> +#  This driver fails to follow the UEFI driver model without a good
> +#  reason, and only remains in the tree because it is still used by
> +#  a small number of platforms. It will be removed when no longer used.
> +#
> +#  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010019
> +  BASE_NAME                      = DwEmmcDxe
> +  FILE_GUID                      = b549f005-4bd4-4020-a0cb-06f42bda68c3
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +
> +  ENTRY_POINT                    = DwEmmcDxeInitialize
> +
> +[Sources.common]
> +  DwEmmcDxe.c
> +
> +[Packages]
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdePkg/MdePkg.dec
> +  Silicon/Synopsys/DesignWare/DesignWare.dec
> +
> +[LibraryClasses]
> +  ArmLib
> +  BaseLib
> +  BaseMemoryLib
> +  CacheMaintenanceLib
> +  IoLib
> +  MemoryAllocationLib
> +  TimerLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Protocols]
> +  gEfiCpuArchProtocolGuid
> +  gEfiDevicePathProtocolGuid
> +  gEmbeddedMmcHostProtocolGuid
> +
> +[Pcd]
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeMaxClockFreqInHz
> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeFifoDepth
> +  gDesignWareTokenSpaceGuid.PcdDwPermitObsoleteDrivers
> +
> +[Depex]
> +  TRUE
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver Ard Biesheuvel
@ 2020-05-04 11:00   ` Leif Lindholm
  0 siblings, 0 replies; 20+ messages in thread
From: Leif Lindholm @ 2020-05-04 11:00 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: devel

On Thu, Apr 30, 2020 at 19:16:49 +0200, Ard Biesheuvel wrote:
> Incorporate the ISP 1761 USB host driver from EmbeddedPkg, which is
> only used on obsolete ARM development platforms and does not follow
> the UEFI driver model. This will allow us to drop it from the core EDK2
> repository.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> ---
>  Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc                |   2 +-
>  Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf                |   2 +-
>  Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc                     |   2 +-
>  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                      |   3 +
>  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c   | 636 ++++++++++++++++++++
>  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h   | 123 ++++
>  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf |  39 ++
>  7 files changed, 804 insertions(+), 3 deletions(-)
> 
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
> index bee7913feb52..144dd4f8b8e9 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
> +++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
> @@ -157,7 +157,7 @@ [PcdsFixedAtBuild.common]
>    gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase|0x2C002000
>  
>    # ISP1761 USB OTG Controller
> -  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
> +  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
>  
>    # Ethernet (SMSC LAN9118)
>    gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x1A000000
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
> index b133375e1a11..f98de162e634 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
> +++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
> @@ -126,7 +126,7 @@ [FV.FvMain]
>    #
>    # USB support
>    #
> -  INF EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> +  INF Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
>  
>    #
>    # Android Fastboot
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
> index 912ad5e5a1ec..bde3437b56d7 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
> +++ b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
> @@ -455,4 +455,4 @@ [Components.common]
>  [Components.ARM]
>  
>    # ISP1761 USB OTG Controller
> -  EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> +  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> index e42905aabf2b..f78c5ce7c764 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> +++ b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> @@ -67,3 +67,6 @@ [PcdsFixedAtBuild.common]
>    # The default feature mask below disables full duplex negotiation, since full
>    # duplex operation is suspected to be broken in the driver.
>    gArmVExpressTokenSpaceGuid.PcdLan9118NegotiationFeatureMask|0xFFFFFEBF|UINT32|0x00000028
> +
> +  # ISP1761 USB OTG Controller
> +  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT32|0x00000029
> diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
> new file mode 100644
> index 000000000000..c23c0ecf737d
> --- /dev/null
> +++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
> @@ -0,0 +1,636 @@
> +/** @file
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/DebugLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiDriverEntryPoint.h>
> +#include <Library/IoLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +
> +#include <IndustryStandard/Usb.h>
> +
> +#include <Protocol/UsbDevice.h>
> +
> +#include "Isp1761UsbDxe.h"
> +
> +/*
> +  Driver for using the NXP ISP1761 as a USB Peripheral controller.
> +  Doesn't use USB OTG - just sets it in Pure Peripheral mode.
> +
> +  The ISP1582 datasheet has a little more info on the Peripheral controller
> +  registers than the ISP1761 datasheet
> +
> +  We don't do string descriptors. They're optional.
> +  We currently assume the device has one configuration, one interface, one IN
> +  endpoint, and one OUT endpoint (plus the default control endpoint).
> +
> +  In fact, this driver is the minimum required to implement fastboot.
> +*/
> +
> +// TODO Make sure the controller isn't sending empty packets when it shouldn't
> +// (check behaviour in cases when Buffer Length isn't explicitly set)
> +
> +// ISP1582 Datasheet:
> +// "Data transfers preceding the status stage must first be fully
> +// completed before the STATUS bit can be set."
> +// This variable stores whether some control data has been pended in the EP0TX
> +// Tx buffer, so that when an EP0TX interrupt is received we can set the STATUS
> +// bit to go to the Status stage of the control transfer.
> +STATIC BOOLEAN mControlTxPending = FALSE;
> +
> +STATIC USB_DEVICE_DESCRIPTOR    *mDeviceDescriptor;
> +
> +// The config descriptor, interface descriptor, and endpoint descriptors in a
> +// buffer (in that order)
> +STATIC VOID                     *mDescriptors;
> +// Convenience pointers to those descriptors inside the buffer:
> +STATIC USB_INTERFACE_DESCRIPTOR *mInterfaceDescriptor;
> +STATIC USB_CONFIG_DESCRIPTOR    *mConfigDescriptor;
> +STATIC USB_ENDPOINT_DESCRIPTOR  *mEndpointDescriptors;
> +
> +STATIC USB_DEVICE_RX_CALLBACK   mDataReceivedCallback;
> +STATIC USB_DEVICE_TX_CALLBACK   mDataSentCallback;
> +
> +// The time between interrupt polls, in units of 100 nanoseconds
> +// 10 Microseconds
> +#define ISP1761_INTERRUPT_POLL_PERIOD 10000
> +
> +STATIC
> +VOID
> +SelectEndpoint (
> +  IN UINT8 Endpoint
> +  )
> +{
> +  // The DMA Endpoint Index must not point to the same as the
> +  // Endpoint Index Register.
> +  WRITE_REG32 (ISP1761_DMA_ENDPOINT_INDEX, ((Endpoint + 2) % ISP1761_NUM_ENDPOINTS));
> +  WRITE_REG32 (ISP1761_ENDPOINT_INDEX, Endpoint);
> +}
> +
> +// Enable going to the Data stage of a control transfer
> +STATIC
> +VOID
> +DataStageEnable (
> +  IN UINT8 Endpoint
> +  )
> +{
> +  SelectEndpoint (Endpoint);
> +  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_DSEN);
> +}
> +
> +// Go to the Status stage of a successful control transfer
> +STATIC
> +VOID
> +StatusAcknowledge (
> +  IN UINT8 Endpoint
> +)
> +{
> +  SelectEndpoint (Endpoint);
> +  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_STATUS);
> +}
> +
> +// Read the FIFO for the endpoint indexed by Endpoint, into the buffer pointed
> +// at by Buffer, whose size is *Size bytes.
> +//
> +// If *Size is less than the number of bytes in the FIFO, return EFI_BUFFER_TOO_SMALL
> +//
> +// Update *Size with the number of bytes of data in the FIFO.
> +STATIC
> +EFI_STATUS
> +ReadEndpointBuffer (
> +  IN      UINT8   Endpoint,
> +  IN OUT  UINTN  *Size,
> +  IN OUT  VOID   *Buffer
> +  )
> +{
> +  UINT16  NumBytesAvailable;
> +  UINT32  Val32;
> +  UINTN   Index;
> +  UINTN   NumBytesRead;
> +
> +  SelectEndpoint (Endpoint);
> +
> +  NumBytesAvailable = READ_REG16 (ISP1761_BUFFER_LENGTH);
> +
> +  if (NumBytesAvailable > *Size) {
> +    *Size = NumBytesAvailable;
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +  *Size = NumBytesAvailable;
> +
> +  /* -- NB! --
> +    The datasheet says the Data Port is 16 bits but it actually appears to
> +    be 32 bits.
> +   */
> +
> +  // Read 32-bit chunks
> +  for (Index = 0; Index < NumBytesAvailable / 4; Index++) {
> +    ((UINT32 *) Buffer)[Index] = READ_REG32 (ISP1761_DATA_PORT);
> +  }
> +
> +  // Read remaining bytes
> +
> +  // Round NumBytesAvailable down to nearest power of 4
> +  NumBytesRead = NumBytesAvailable & (~0x3);
> +  if (NumBytesRead != NumBytesAvailable) {
> +    Val32 = READ_REG32 (ISP1761_DATA_PORT);
> +    // Copy each required byte of 32-bit word into buffer
> +    for (Index = 0; Index < NumBytesAvailable % 4; Index++) {
> +      ((UINT8 *) Buffer)[NumBytesRead + Index] = Val32 >> (Index * 8);
> +    }
> +  }
> +  return EFI_SUCCESS;
> +}
> +
> +/*
> +  Write an endpoint buffer. Parameters:
> +  Endpoint        Endpoint index (see Endpoint Index Register in datasheet)
> +  MaxPacketSize   The MaxPacketSize this endpoint is configured for
> +  Size            The size of the Buffer
> +  Buffer          The data
> +
> +  Assumes MaxPacketSize is a multiple of 4.
> +  (It seems that all valid values for MaxPacketSize _are_ multiples of 4)
> +*/
> +STATIC
> +EFI_STATUS
> +WriteEndpointBuffer (
> +  IN       UINT8   Endpoint,
> +  IN       UINTN   MaxPacketSize,
> +  IN       UINTN   Size,
> +  IN CONST VOID   *Buffer
> +  )
> +{
> +  UINTN    Index;
> +  UINT32  *DwordBuffer;
> +
> +  DwordBuffer = (UINT32 *) Buffer;
> +  SelectEndpoint (Endpoint);
> +
> +  /* -- NB! --
> +    The datasheet says the Data Port is 16 bits but it actually appears to
> +    be 32 bits.
> +   */
> +
> +  // Write packets of size MaxPacketSize
> +  while (Size > MaxPacketSize) {
> +    for (Index = 0; Index < MaxPacketSize / 4; Index++) {
> +      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[Index]);
> +    }
> +    Size -= MaxPacketSize;
> +    DwordBuffer += (MaxPacketSize / sizeof (UINT32));
> +  }
> +
> +  // Write remaining data
> +
> +  if (Size > 0) {
> +    WRITE_REG32 (ISP1761_BUFFER_LENGTH, Size);
> +
> +    while (Size > 4) {
> +      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);
> +      Size -= 4;
> +      DwordBuffer++;
> +    }
> +
> +    if (Size > 0) {
> +      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +STATIC
> +EFI_STATUS
> +HandleGetDescriptor (
> +  IN USB_DEVICE_REQUEST  *Request
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT8       DescriptorType;
> +  UINTN       ResponseSize;
> +  VOID       *ResponseData;
> +
> +  ResponseSize = 0;
> +  ResponseData = NULL;
> +  Status = EFI_SUCCESS;
> +
> +  // Pretty confused if bmRequestType is anything but this:
> +  ASSERT (Request->RequestType == USB_DEV_GET_DESCRIPTOR_REQ_TYPE);
> +
> +  // Choose the response
> +  DescriptorType = Request->Value >> 8;
> +  switch (DescriptorType) {
> +  case USB_DESC_TYPE_DEVICE:
> +    DEBUG ((EFI_D_INFO, "USB: Got a request for device descriptor\n"));
> +    ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR);
> +    ResponseData = mDeviceDescriptor;
> +    break;
> +  case USB_DESC_TYPE_CONFIG:
> +    DEBUG ((EFI_D_INFO, "USB: Got a request for config descriptor\n"));
> +    ResponseSize = mConfigDescriptor->TotalLength;
> +    ResponseData = mDescriptors;
> +    break;
> +  case USB_DESC_TYPE_STRING:
> +    DEBUG ((EFI_D_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF));
> +    break;
> +  default:
> +    DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));
> +    Status = EFI_NOT_FOUND;
> +    break;
> +  }
> +
> +  // Send the response
> +  if (ResponseData) {
> +    ASSERT (ResponseSize != 0);
> +
> +    if (Request->Length < ResponseSize) {
> +      // Truncate response
> +      ResponseSize = Request->Length;
> +    } else if (Request->Length > ResponseSize) {
> +      DEBUG ((EFI_D_INFO, "USB: Info: ResponseSize < wLength\n"));
> +    }
> +
> +    DataStageEnable (ISP1761_EP0TX);
> +    Status = WriteEndpointBuffer (
> +              ISP1761_EP0TX,
> +              MAX_PACKET_SIZE_CONTROL,
> +              ResponseSize,
> +              ResponseData
> +              );
> +    if (!EFI_ERROR (Status)) {
> +      // Setting this value should cause us to go to the Status stage on the
> +      // next EP0TX interrupt
> +      mControlTxPending = TRUE;
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +STATIC
> +EFI_STATUS
> +HandleSetAddress (
> +  IN USB_DEVICE_REQUEST  *Request
> +  )
> +{
> +  // Pretty confused if bmRequestType is anything but this:
> +  ASSERT (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE);
> +  // USB Spec: "The USB device does not change its device address until after
> +  // the Status stage of this request is completed successfully."
> +  // ISP1582 datasheet: "The new device address is activated when the
> +  // device receives an acknowledgment from the host for the empty packet
> +  // token". (StatusAcknowledge causes an empty packet to be sent).
> +  // So, we write the Address register _before_ acking the SET_ADDRESS.
> +  DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));
> +  WRITE_REG32 (ISP1761_ADDRESS, Request->Value | ISP1761_ADDRESS_DEVEN);
> +  StatusAcknowledge (ISP1761_EP0TX);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +// Move the device to the Configured state.
> +// (This code only supports one configuration for a device, so the configuration
> +//  index is ignored)
> +STATIC
> +EFI_STATUS
> +HandleSetConfiguration (
> +  IN USB_DEVICE_REQUEST  *Request
> +  )
> +{
> +  USB_ENDPOINT_DESCRIPTOR  *EPDesc;
> +  UINTN                     Index;
> +  UINT8                     EndpointIndex;
> +
> +  ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);
> +  DEBUG ((EFI_D_INFO, "USB: Setting configuration.\n"));
> +
> +  // Configure endpoints
> +  for (Index = 0; Index < mInterfaceDescriptor->NumEndpoints; Index++) {
> +    EPDesc = &mEndpointDescriptors[Index];
> +
> +    // To simplify for now, assume endpoints aren't "sparse", and are in order.
> +    ASSERT ((EPDesc->EndpointAddress & 0xF) == ((Index / 2) + 1));
> +
> +    // Convert from USB endpoint index to ISP1761 endpoint Index
> +    // USB:     Endpoint number is bits [3:0], IN/OUT is bit [7]
> +    // ISP1761: Endpoint number is bits [4:1], IN/OUT is bit [0]
> +    EndpointIndex = ((EPDesc->EndpointAddress & 0xF) << 1) |
> +                    ((EPDesc->EndpointAddress & BIT7) >> 7);
> +    SelectEndpoint (EndpointIndex);
> +    // Set endpoint type (Bulk/Isochronous/Interrupt)
> +    WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, EPDesc->MaxPacketSize);
> +    // Hardware foible (bug?): Although the datasheet seems to suggest it should
> +    // automatically be set to MaxPacketSize, the Buffer Length register appears
> +    // to be reset to 0, which causes an empty packet to be sent in response to
> +    // the first IN token of the session. The NOEMPKT field of the Endpoint Type
> +    // register sounds like it might fix this problem, but it doesn't
> +    // (it's "applicable only in the DMA mode").
> +    WRITE_REG32 (ISP1761_BUFFER_LENGTH, EPDesc->MaxPacketSize);
> +    WRITE_REG32 (ISP1761_ENDPOINT_TYPE, (EPDesc->Attributes & 0x3) |
> +                                        ISP1761_ENDPOINT_TYPE_ENABLE);
> +  }
> +
> +  StatusAcknowledge (ISP1761_EP0TX);
> +  return EFI_SUCCESS;
> +}
> +
> +STATIC
> +EFI_STATUS
> +HandleDeviceRequest (
> +  IN USB_DEVICE_REQUEST  *Request
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  Status = EFI_SUCCESS;
> +
> +  switch (Request->Request) {
> +  case USB_DEV_GET_DESCRIPTOR:
> +    Status = HandleGetDescriptor (Request);
> +    break;
> +  case USB_DEV_SET_ADDRESS:
> +    Status = HandleSetAddress (Request);
> +    break;
> +  case USB_DEV_SET_CONFIGURATION:
> +    Status = HandleSetConfiguration (Request);
> +    break;
> +  default:
> +    DEBUG ((EFI_D_ERROR,
> +      "Didn't understand RequestType 0x%x Request 0x%x\n",
> +      Request->RequestType, Request->Request));
> +      Status = EFI_INVALID_PARAMETER;
> +    break;
> +  }
> +
> +  return Status;
> +}
> +
> +// Instead of actually registering interrupt handlers, we poll the controller's
> +//  interrupt source register in this function.
> +STATIC
> +VOID
> +CheckInterrupts (
> +  IN EFI_EVENT  Event,
> +  IN VOID      *Context
> +  )
> +{
> +  UINT32      DcInterrupts;
> +  UINTN       NumBytes;
> +  UINTN       MoreBytes;
> +  UINT8       Packet[512];
> +  VOID       *DataPacket;
> +  UINT32      HandledInterrupts;
> +  UINT32      UnhandledInterrupts;
> +  EFI_STATUS  Status;
> +
> +  // Set bits in HandledInterrupts to mark the interrupt source handled.
> +  HandledInterrupts = 0;
> +
> +  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);
> +
> +  DcInterrupts = READ_REG32 (ISP1761_DC_INTERRUPT);
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_SUSP) {
> +    DEBUG ((EFI_D_INFO, "USB: Suspend\n"));
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_SUSP;
> +  }
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_RESUME) {
> +    DEBUG ((EFI_D_INFO, "USB: Resume\n"));
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_RESUME;
> +  }
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0SETUP) {
> +    NumBytes = 512;
> +    ReadEndpointBuffer (0x20, &NumBytes, &Packet);
> +    ASSERT (NumBytes == 8);
> +    HandleDeviceRequest ((USB_DEVICE_REQUEST *) Packet);
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0SETUP;
> +  }
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0RX) {
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0RX;
> +  }
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0TX) {
> +    if (mControlTxPending) {
> +      // We previously put some data in the Control Endpoint's IN (Tx) FIFO.
> +      // We assume that that data has now been sent in response to the IN token
> +      // that triggered this interrupt. We can therefore go to the Status stage
> +      // of the control transfer.
> +      StatusAcknowledge (ISP1761_EP0TX);
> +      mControlTxPending = FALSE;
> +    }
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0TX;
> +  }
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1RX) {
> +    NumBytes = 512;
> +    DataPacket = AllocatePool (NumBytes);
> +    Status = ReadEndpointBuffer (ISP1761_EP1RX, &NumBytes, DataPacket);
> +    if (EFI_ERROR (Status) || NumBytes == 0) {
> +      if (EFI_ERROR (Status)) {
> +        DEBUG ((EFI_D_ERROR, "Couldn't read EP1RX data: %r\n", Status));
> +      }
> +      FreePool (DataPacket);
> +    } else {
> +      // Signal this event again so we poll again ASAP
> +      gBS->SignalEvent (Event);
> +      mDataReceivedCallback (NumBytes, DataPacket);
> +    }
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1RX;
> +  }
> +  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1TX) {
> +    mDataSentCallback (1);
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1TX;
> +  }
> +  if (DcInterrupts & (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF)) {
> +    // Don't care about SOFs or pseudo-SOFs
> +    HandledInterrupts |= (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF);
> +  }
> +  if (ISP1761_DC_INTERRUPT_BRESET) {
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_BRESET;
> +  }
> +  if (ISP1761_DC_INTERRUPT_HS_STAT) {
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_HS_STAT;
> +  }
> +  if (ISP1761_DC_INTERRUPT_VBUS) {
> +    HandledInterrupts |= ISP1761_DC_INTERRUPT_VBUS;
> +  }
> +
> +  UnhandledInterrupts = DcInterrupts & (~HandledInterrupts) & ISP1761_DC_INTERRUPT_MASK;
> +  if (UnhandledInterrupts) {
> +    DEBUG ((EFI_D_ERROR, "USB: Unhandled DC Interrupts: 0x%08x\n",
> +      UnhandledInterrupts));
> +  }
> +
> +  // Check if we received any more data while we were handling the interrupt.
> +  SelectEndpoint (ISP1761_EP1RX);
> +  MoreBytes = READ_REG16 (ISP1761_BUFFER_LENGTH);
> +  if (MoreBytes) {
> +    HandledInterrupts &= ~ISP1761_DC_INTERRUPT_EP1RX;
> +  }
> +
> +  WRITE_REG32 (ISP1761_DC_INTERRUPT, HandledInterrupts);
> +}
> +
> +EFI_STATUS
> +Isp1761PeriphSend (
> +  IN        UINT8  EndpointIndex,
> +  IN        UINTN  Size,
> +  IN  CONST VOID  *Buffer
> +  )
> +{
> +  return WriteEndpointBuffer (
> +          (EndpointIndex << 1) | 0x1, //Convert to ISP1761 endpoint index, Tx
> +          MAX_PACKET_SIZE_BULK,
> +          Size,
> +          Buffer
> +          );
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +Isp1761PeriphStart (
> +  IN USB_DEVICE_DESCRIPTOR   *DeviceDescriptor,
> +  IN VOID                   **Descriptors,
> +  IN USB_DEVICE_RX_CALLBACK   RxCallback,
> +  IN USB_DEVICE_TX_CALLBACK   TxCallback
> +  )
> +{
> +  UINT16                    OtgStatus;
> +  UINT8                    *Ptr;
> +  EFI_STATUS                Status;
> +  EFI_EVENT                 TimerEvent;
> +
> +  ASSERT (DeviceDescriptor != NULL);
> +  ASSERT (Descriptors[0] != NULL);
> +  ASSERT (RxCallback != NULL);
> +  ASSERT (TxCallback != NULL);
> +
> +  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);
> +
> +  WRITE_REG32 (ISP1761_SW_RESET_REG, ISP1761_SW_RESET_ALL);
> +  while (READ_REG32 (ISP1761_SW_RESET_REG) & ISP1761_SW_RESET_ALL) {
> +    //busy wait
> +  }
> +  WRITE_REG32 (ISP1761_MODE, ISP1761_MODE_SFRESET);
> +  while (READ_REG32 (ISP1761_MODE) & ISP1761_MODE_SFRESET) {
> +    //busy wait
> +  }
> +  DEBUG ((EFI_D_INFO, "USB: Software reset done\n"));
> +
> +  WRITE_REG32 (ISP1761_DC_INTERRUPT_ENABLE, 0x03FFFFFF);
> +  WRITE_REG32 (ISP1761_OTG_INTERRUPT_ENABLE_RISE, 0x07FF);
> +
> +  WRITE_REG8 (ISP1761_ADDRESS, ISP1761_ADDRESS_DEVEN);
> +  WRITE_REG8 (ISP1761_MODE, ISP1761_MODE_WKUPCS | ISP1761_MODE_CLKAON);
> +
> +  // Use port 1 as peripheral controller (magic - disagrees with datasheet)
> +  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0xffff0000);
> +  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0x000014d1);
> +
> +  OtgStatus = READ_REG16 (ISP1761_OTG_STATUS);
> +  if ((OtgStatus & ISP1761_OTG_STATUS_B_SESS_END) != 0) {
> +    DEBUG ((EFI_D_ERROR, "USB: Vbus not powered.\n"));
> +  }
> +  if ((OtgStatus & ISP1761_OTG_STATUS_A_B_SESS_VLD) == 0) {
> +    DEBUG ((EFI_D_ERROR, "USB: Session not valid.\n"));
> +  }
> +
> +  // Configure Control endpoints
> +  SelectEndpoint (0x20);
> +  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);
> +  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);
> +  SelectEndpoint (0x0);
> +  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);
> +  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);
> +  SelectEndpoint (0x1);
> +  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);
> +  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);
> +
> +  // Interrupt on all ACK and NAK
> +  WRITE_REG32 (ISP1761_INTERRUPT_CONFIG, ISP1761_INTERRUPT_CONFIG_ACK_ONLY);
> +
> +  mDeviceDescriptor = DeviceDescriptor;
> +  mDescriptors = Descriptors[0];
> +
> +  // Right now we just support one configuration
> +  ASSERT (mDeviceDescriptor->NumConfigurations == 1);
> +  // ... and one interface
> +  mConfigDescriptor = (USB_CONFIG_DESCRIPTOR *)mDescriptors;
> +  ASSERT (mConfigDescriptor->NumInterfaces == 1);
> +
> +  Ptr = ((UINT8 *) mDescriptors) + sizeof (USB_CONFIG_DESCRIPTOR);
> +  mInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR *) Ptr;
> +  Ptr += sizeof (USB_INTERFACE_DESCRIPTOR);
> +
> +  mEndpointDescriptors = (USB_ENDPOINT_DESCRIPTOR *) Ptr;
> +
> +  mDataReceivedCallback = RxCallback;
> +  mDataSentCallback = TxCallback;
> +
> +  // Register a timer event so CheckInterrupts gets called periodically
> +  Status = gBS->CreateEvent (
> +                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
> +                  TPL_CALLBACK,
> +                  CheckInterrupts,
> +                  NULL,
> +                  &TimerEvent
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = gBS->SetTimer (
> +                  TimerEvent,
> +                  TimerPeriodic,
> +                  ISP1761_INTERRUPT_POLL_PERIOD
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> +
> +USB_DEVICE_PROTOCOL mUsbDevice = {
> +  Isp1761PeriphStart,
> +  Isp1761PeriphSend
> +};
> +
> +
> +EFI_STATUS
> +EFIAPI
> +Isp1761PeriphEntryPoint (
> +  IN EFI_HANDLE                            ImageHandle,
> +  IN EFI_SYSTEM_TABLE                      *SystemTable
> +  )
> +{
> +  UINT32      DeviceId;
> +  EFI_HANDLE  Handle;
> +
> +  DeviceId = READ_REG32 (ISP1761_DEVICE_ID);
> +
> +  if (DeviceId != ISP1761_DEVICE_ID_VAL) {
> +    DEBUG ((EFI_D_ERROR,
> +      "ERROR: Read incorrect device ID for ISP1761: 0x%08x, expected 0x%08x\n",
> +      DeviceId , ISP1761_DEVICE_ID_VAL
> +      ));
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  Handle = NULL;
> +  return gBS->InstallProtocolInterface (
> +    &Handle,
> +    &gUsbDeviceProtocolGuid,
> +    EFI_NATIVE_INTERFACE,
> +    &mUsbDevice
> +    );
> +}
> diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
> new file mode 100644
> index 000000000000..f7155d48d8ad
> --- /dev/null
> +++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
> @@ -0,0 +1,123 @@
> +/** @file
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef __ISP1761_USB_DXE_H__
> +#define __ISP1761_USB_DXE_H__
> +
> +#define ISP1761_USB_BASE FixedPcdGet32 (PcdIsp1761BaseAddress)
> +
> +#define READ_REG32(Offset) MmioRead32 (ISP1761_USB_BASE + Offset)
> +#define READ_REG16(Offset) (UINT16) READ_REG32 (Offset)
> +#define WRITE_REG32(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, Val)
> +#define WRITE_REG16(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)
> +#define WRITE_REG8(Offset, Val)   MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)
> +
> +// Max packet size in bytes (For Full Speed USB 64 is the only valid value)
> +#define MAX_PACKET_SIZE_CONTROL     64
> +
> +#define MAX_PACKET_SIZE_BULK        512
> +
> +// 8 Endpoints, in and out. Don't count the Endpoint 0 setup buffer
> +#define ISP1761_NUM_ENDPOINTS               16
> +
> +// Endpoint Indexes
> +#define ISP1761_EP0SETUP                    0x20
> +#define ISP1761_EP0RX                       0x00
> +#define ISP1761_EP0TX                       0x01
> +#define ISP1761_EP1RX                       0x02
> +#define ISP1761_EP1TX                       0x03
> +
> +// DcInterrupt bits
> +#define ISP1761_DC_INTERRUPT_BRESET         BIT0
> +#define ISP1761_DC_INTERRUPT_SOF            BIT1
> +#define ISP1761_DC_INTERRUPT_PSOF           BIT2
> +#define ISP1761_DC_INTERRUPT_SUSP           BIT3
> +#define ISP1761_DC_INTERRUPT_RESUME         BIT4
> +#define ISP1761_DC_INTERRUPT_HS_STAT        BIT5
> +#define ISP1761_DC_INTERRUPT_DMA            BIT6
> +#define ISP1761_DC_INTERRUPT_VBUS           BIT7
> +#define ISP1761_DC_INTERRUPT_EP0SETUP       BIT8
> +#define ISP1761_DC_INTERRUPT_EP0RX          BIT10
> +#define ISP1761_DC_INTERRUPT_EP0TX          BIT11
> +#define ISP1761_DC_INTERRUPT_EP1RX          BIT12
> +#define ISP1761_DC_INTERRUPT_EP1TX          BIT13
> +// All valid peripheral controller interrupts
> +#define ISP1761_DC_INTERRUPT_MASK           0x003FFFDFF
> +
> +#define ISP1761_ADDRESS                     0x200
> +#define ISP1761_ADDRESS_DEVEN               BIT7
> +
> +#define ISP1761_MODE                        0x20C
> +#define ISP1761_MODE_DATA_BUS_WIDTH         BIT8
> +#define ISP1761_MODE_CLKAON                 BIT7
> +#define ISP1761_MODE_SFRESET                BIT4
> +#define ISP1761_MODE_WKUPCS                 BIT2
> +
> +#define ISP1761_ENDPOINT_MAX_PACKET_SIZE    0x204
> +
> +#define ISP1761_ENDPOINT_TYPE               0x208
> +#define ISP1761_ENDPOINT_TYPE_NOEMPKT       BIT4
> +#define ISP1761_ENDPOINT_TYPE_ENABLE        BIT3
> +
> +#define ISP1761_INTERRUPT_CONFIG            0x210
> +// Interrupt config value to only interrupt on ACK of IN and OUT tokens
> +#define ISP1761_INTERRUPT_CONFIG_ACK_ONLY   BIT2 | BIT5 | BIT6
> +
> +#define ISP1761_DC_INTERRUPT                0x218
> +#define ISP1761_DC_INTERRUPT_ENABLE         0x214
> +
> +#define ISP1761_CTRL_FUNCTION               0x228
> +#define ISP1761_CTRL_FUNCTION_VENDP         BIT3
> +#define ISP1761_CTRL_FUNCTION_DSEN          BIT2
> +#define ISP1761_CTRL_FUNCTION_STATUS        BIT1
> +
> +#define ISP1761_DEVICE_UNLOCK               0x27C
> +#define ISP1761_DEVICE_UNLOCK_MAGIC         0xAA37
> +
> +#define ISP1761_SW_RESET_REG                0x30C
> +#define ISP1761_SW_RESET_ALL                BIT0
> +
> +#define ISP1761_DEVICE_ID                   0x370
> +
> +#define ISP1761_OTG_CTRL_SET                0x374
> +#define ISP1761_OTG_CTRL_CLR                OTG_CTRL_SET + 2
> +#define ISP1761_OTG_CTRL_OTG_DISABLE        BIT10
> +#define ISP1761_OTG_CTRL_VBUS_CHRG          BIT6
> +#define ISP1761_OTG_CTRL_VBUS_DISCHRG       BIT5
> +#define ISP1761_OTG_CTRL_DM_PULLDOWN        BIT2
> +#define ISP1761_OTG_CTRL_DP_PULLDOWN        BIT1
> +#define ISP1761_OTG_CTRL_DP_PULLUP          BIT0
> +
> +#define ISP1761_OTG_STATUS                  0x378
> +#define ISP1761_OTG_STATUS_B_SESS_END       BIT7
> +#define ISP1761_OTG_STATUS_A_B_SESS_VLD     BIT1
> +
> +#define ISP1761_OTG_INTERRUPT_LATCH_SET     0x37C
> +#define ISP1761_OTG_INTERRUPT_LATCH_CLR     0x37E
> +#define ISP1761_OTG_INTERRUPT_ENABLE_RISE   0x384
> +
> +#define ISP1761_DMA_ENDPOINT_INDEX          0x258
> +
> +#define ISP1761_ENDPOINT_INDEX              0x22c
> +#define ISP1761_DATA_PORT                   0x220
> +#define ISP1761_BUFFER_LENGTH               0x21c
> +
> +// Device ID Values
> +#define PHILLIPS_VENDOR_ID_VAL 0x04cc
> +#define ISP1761_PRODUCT_ID_VAL 0x1761
> +#define ISP1761_DEVICE_ID_VAL ((ISP1761_PRODUCT_ID_VAL << 16) |\
> +                               PHILLIPS_VENDOR_ID_VAL)
> +
> +#endif //ifndef __ISP1761_USB_DXE_H__
> diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> new file mode 100644
> index 000000000000..b161547bf73a
> --- /dev/null
> +++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> @@ -0,0 +1,39 @@
> +#/** @file
> +#
> +#  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#

Comment only:
This file, unlike some other .infs in this series, does not include
the WARNING header. Feel free to fold one in before pushing.

> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005
> +  BASE_NAME                      = Isp1761PeriphDxe
> +  FILE_GUID                      = 72d78ea6-4dee-11e3-8100-f3842a48d0a0
> +  MODULE_TYPE                    = UEFI_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = Isp1761PeriphEntryPoint
> +
> +[Sources.common]
> +  Isp1761UsbDxe.c
> +
> +[LibraryClasses]
> +  DebugLib
> +  IoLib
> +  MemoryAllocationLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +
> +[Protocols]
> +  gEfiDriverBindingProtocolGuid
> +  gUsbDeviceProtocolGuid
> +
> +[Packages]
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> +
> +[Pcd]
> +  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2
  2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
                   ` (7 preceding siblings ...)
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver Ard Biesheuvel
@ 2020-05-04 11:00 ` Leif Lindholm
  2020-05-04 13:19   ` Ard Biesheuvel
  8 siblings, 1 reply; 20+ messages in thread
From: Leif Lindholm @ 2020-05-04 11:00 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: devel

On Thu, Apr 30, 2020 at 19:16:41 +0200, Ard Biesheuvel wrote:
> This is a followup to two patches I sent out separately yesterday, and to
> the subsequent discussion regarding annotating obsolete drivers as unfit
> for reuse if they don't follow the UEFI driver model.
> 
> I have rounded up all the stuff I have in flight for edk2-platforms at
> the moment, so this v3 supersedes everything else for edk2-platforms you
> may have in your mailbox.
> 
> Once this is in, I will follow up with a series for EmbeddedPkg to get
> rid of the originals.
> 
> Changes since v2:
> - move DwEmmcDxe, Lan91x, Lan9118 and ISP 1761 drivers as well
> - incorporate a build fixlet that I sent out separately as well
> - add some acks
> 
> Changes since v1:
> - use gEmbeddedMmcHostProtocolGuid not gEdkiiMmcHostProtocolGuid
> - add WARNING comment blocks to all files to document the fact that these
>   files are only kept for sentimental reasons
> 
> Ard Biesheuvel (8):
>   Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference
>   Platform/ARM/VExpressPkg: incorporate PL180 driver
>   Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver
>   Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
>   Platform/HiKey: switch to relocated version of eMMC driver
>   Platform/ARM/VExpressPkg: incorporate Lan91x driver
>   Platform/ARM/VExpressPkg: incorporate Lan9118 driver
>   Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver

Many thanks for this!
For the series:
Reviewed-by: Leif Lindholm <leif@nuviainc.com>

>  Platform/ARM/JunoPkg/ArmJuno.dec              |    4 +-
>  Platform/ARM/JunoPkg/ArmJuno.dsc              |   10 +-
>  Platform/ARM/JunoPkg/ArmJuno.fdf              |    4 +-
>  .../Drivers/SataSiI3132Dxe/ComponentName.c    |  179 ++
>  .../Drivers/SataSiI3132Dxe/SataSiI3132.c      |  546 ++++
>  .../Drivers/SataSiI3132Dxe/SataSiI3132.h      |  286 +++
>  .../Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf |   38 +
>  .../SataSiI3132Dxe/SiI3132AtaPassThru.c       |  834 ++++++
>  Platform/ARM/SgiPkg/SgiPlatform.dsc           |    4 +-
>  Platform/ARM/SgiPkg/SgiPlatform.fdf           |    2 +-
>  .../ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc  |   14 +-
>  .../ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf  |    6 +-
>  .../VExpressPkg/ArmVExpress-FVP-AArch64.dsc   |    6 +-
>  .../VExpressPkg/ArmVExpress-FVP-AArch64.fdf   |    4 +-
>  Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc  |    6 +-
>  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec   |   18 +
>  .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c     |  636 +++++
>  .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h     |  123 +
>  .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf   |   39 +
>  .../Drivers/Lan9118Dxe/Lan9118Dxe.c           | 1539 ++++++++++++
>  .../Drivers/Lan9118Dxe/Lan9118Dxe.h           |  304 +++
>  .../Drivers/Lan9118Dxe/Lan9118Dxe.inf         |   58 +
>  .../Drivers/Lan9118Dxe/Lan9118DxeHw.h         |  401 +++
>  .../Drivers/Lan9118Dxe/Lan9118DxeUtil.c       | 1039 ++++++++
>  .../Drivers/Lan9118Dxe/Lan9118DxeUtil.h       |  283 +++
>  .../VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c | 2236 +++++++++++++++++
>  .../Drivers/Lan91xDxe/Lan91xDxe.inf           |   59 +
>  .../Drivers/Lan91xDxe/Lan91xDxeHw.h           |  279 ++
>  .../Drivers/PL180MciDxe/PL180Mci.c            |  570 +++++
>  .../Drivers/PL180MciDxe/PL180Mci.h            |  169 ++
>  .../Drivers/PL180MciDxe/PL180MciDxe.inf       |   53 +
>  Platform/Hisilicon/HiKey/HiKey.dsc            |    7 +-
>  Platform/Hisilicon/HiKey/HiKey.fdf            |    2 +-
>  Silicon/Synopsys/DesignWare/DesignWare.dec    |    9 +
>  Silicon/Synopsys/DesignWare/DesignWare.dsc    |    2 +
>  .../DesignWare/Drivers/DwEmmcDxe/DwEmmc.h     |  132 +
>  .../DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c  |  693 +++++
>  .../Drivers/DwEmmcDxe/DwEmmcDxe.inf           |   56 +
>  .../LcdGraphicsOutputDxe.inf                  |    1 +
>  39 files changed, 10618 insertions(+), 33 deletions(-)
>  create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/ComponentName.c
>  create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.c
>  create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132.h
>  create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SataSiI3132Dxe.inf
>  create mode 100644 Platform/ARM/JunoPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.c
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180Mci.h
>  create mode 100644 Platform/ARM/VExpressPkg/Drivers/PL180MciDxe/PL180MciDxe.inf
>  create mode 100644 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
>  create mode 100644 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
>  create mode 100644 Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
> 
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
  2020-05-04 10:58   ` Leif Lindholm
@ 2020-05-04 11:40     ` Philippe Mathieu-Daudé
  2020-05-04 11:44       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-04 11:40 UTC (permalink / raw)
  To: devel, leif, Ard Biesheuvel

On 5/4/20 12:58 PM, Leif Lindholm wrote:
> On Thu, Apr 30, 2020 at 19:16:45 +0200, Ard Biesheuvel wrote:
>> Incorporate the driver for the DesignWare eMMC host controller that is
>> based on the obsolete MMC host controller protocol that is defined in
>> EmbeddedPkg.
>>
>> This driver does not follow the UEFI driver model, and is only kept
>> around for its only users, which is the HiKey platform, which is
>> rapidly reaching obsolescence itself, at which point this driver may
>> be removed again.
>>
>> To prevent inadvertent use in new platforms, add a PCD that needs to
>> be changed from its default value in order for the driver to be
>> functional.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> ---
>>   Silicon/Synopsys/DesignWare/DesignWare.dec                  |   9 +
>>   Silicon/Synopsys/DesignWare/DesignWare.dsc                  |   2 +
>>   Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h      | 132 ++++
>>   Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c   | 693 ++++++++++++++++++++
>>   Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf |  56 ++
>>   5 files changed, 892 insertions(+)
>>
>> diff --git a/Silicon/Synopsys/DesignWare/DesignWare.dec b/Silicon/Synopsys/DesignWare/DesignWare.dec
>> index 71ddd24b7404..f7ec7927543c 100755
>> --- a/Silicon/Synopsys/DesignWare/DesignWare.dec
>> +++ b/Silicon/Synopsys/DesignWare/DesignWare.dec
>> @@ -21,4 +21,13 @@ [Guids.common]
>>     gDesignWareTokenSpaceGuid = { 0x89cb1241, 0xd283, 0x4543, { 0x88, 0x9c, 0x6b, 0x62, 0x36, 0x1a, 0x95, 0x7a } }
>>     gDwEmacNetNonDiscoverableDeviceGuid = { 0x401950CD, 0xF9CD, 0x4A65, { 0xAD, 0x8E, 0x84, 0x9F, 0x3B, 0xAF, 0x23, 0x04 } }
>>   
>> +[PcdsFixedAtBuild.common]
>> +  #
>> +  # Permit the use of obsolete drivers in this package
>> +  #
>> +  gDesignWareTokenSpaceGuid.PcdDwPermitObsoleteDrivers|FALSE|BOOLEAN|0x00000001
>>   
>> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000002
>> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000003
>> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeMaxClockFreqInHz|0x0|UINT32|0x00000004
>> +  gDesignWareTokenSpaceGuid.PcdDwEmmcDxeFifoDepth|0x0|UINT32|0x00000005
>> diff --git a/Silicon/Synopsys/DesignWare/DesignWare.dsc b/Silicon/Synopsys/DesignWare/DesignWare.dsc
>> index ad6a5ede4ae0..098bba3f7d68 100755
>> --- a/Silicon/Synopsys/DesignWare/DesignWare.dsc
>> +++ b/Silicon/Synopsys/DesignWare/DesignWare.dsc
>> @@ -20,6 +20,7 @@ [LibraryClasses]
>>     ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
>>     BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
>>     BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
>> +  CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
>>     DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
>>     DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
>>     DmaLib|EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
>> @@ -39,3 +40,4 @@ [LibraryClasses]
>>   
>>   [Components]
>>     Silicon/Synopsys/DesignWare/Drivers/DwEmacSnpDxe/DwEmacSnpDxe.inf
>> +  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
>> diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
>> new file mode 100644
>> index 000000000000..09ad9b8428c4
>> --- /dev/null
>> +++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmc.h
>> @@ -0,0 +1,132 @@
>> +/** @file
>> +*
>> +*  WARNING:
>> +*  This driver fails to follow the UEFI driver model without a good
>> +*  reason, and only remains in the tree because it is still used by
>> +*  a small number of platforms. It will be removed when no longer used.
>> +*
>> +*  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
>> +*
>> +*  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +*
>> +**/
>> +
>> +
>> +#ifndef __DWEMMC_H__
>> +#define __DWEMMC_H__
>> +
>> +#include <Protocol/EmbeddedGpio.h>
>> +
>> +// DW MMC Registers
>> +#define DWEMMC_CTRL             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
>> +#define DWEMMC_PWREN            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
>> +#define DWEMMC_CLKDIV           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
>> +#define DWEMMC_CLKSRC           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
>> +#define DWEMMC_CLKENA           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
>> +#define DWEMMC_TMOUT            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
>> +#define DWEMMC_CTYPE            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
>> +#define DWEMMC_BLKSIZ           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
>> +#define DWEMMC_BYTCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
>> +#define DWEMMC_INTMASK          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
>> +#define DWEMMC_CMDARG           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
>> +#define DWEMMC_CMD              ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
>> +#define DWEMMC_RESP0            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
>> +#define DWEMMC_RESP1            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
>> +#define DWEMMC_RESP2            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
>> +#define DWEMMC_RESP3            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
>> +#define DWEMMC_RINTSTS          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
>> +#define DWEMMC_STATUS           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
>> +#define DWEMMC_FIFOTH           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
>> +#define DWEMMC_TCBCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x05c)
>> +#define DWEMMC_TBBCNT           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x060)
>> +#define DWEMMC_DEBNCE           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
>> +#define DWEMMC_HCON             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x070)
>> +#define DWEMMC_UHSREG           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
>> +#define DWEMMC_BMOD             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
>> +#define DWEMMC_DBADDR           ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
>> +#define DWEMMC_IDSTS            ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
>> +#define DWEMMC_IDINTEN          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
>> +#define DWEMMC_DSCADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
>> +#define DWEMMC_BUFADDR          ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
>> +#define DWEMMC_CARDTHRCTL       ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
>> +#define DWEMMC_DATA             ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X200)
>> +
>> +#define CMD_UPDATE_CLK                          0x80202000
>> +#define CMD_START_BIT                           (1 << 31)
>> +
>> +#define MMC_8BIT_MODE                           (1 << 16)
>> +
>> +#define BIT_CMD_RESPONSE_EXPECT                 (1 << 6)
>> +#define BIT_CMD_LONG_RESPONSE                   (1 << 7)
>> +#define BIT_CMD_CHECK_RESPONSE_CRC              (1 << 8)
>> +#define BIT_CMD_DATA_EXPECTED                   (1 << 9)
>> +#define BIT_CMD_READ                            (0 << 10)
>> +#define BIT_CMD_WRITE                           (1 << 10)
>> +#define BIT_CMD_BLOCK_TRANSFER                  (0 << 11)
>> +#define BIT_CMD_STREAM_TRANSFER                 (1 << 11)
>> +#define BIT_CMD_SEND_AUTO_STOP                  (1 << 12)
>> +#define BIT_CMD_WAIT_PRVDATA_COMPLETE           (1 << 13)
>> +#define BIT_CMD_STOP_ABORT_CMD                  (1 << 14)
>> +#define BIT_CMD_SEND_INIT                       (1 << 15)
>> +#define BIT_CMD_UPDATE_CLOCK_ONLY               (1 << 21)
>> +#define BIT_CMD_READ_CEATA_DEVICE               (1 << 22)
>> +#define BIT_CMD_CCS_EXPECTED                    (1 << 23)
>> +#define BIT_CMD_ENABLE_BOOT                     (1 << 24)
>> +#define BIT_CMD_EXPECT_BOOT_ACK                 (1 << 25)
>> +#define BIT_CMD_DISABLE_BOOT                    (1 << 26)
>> +#define BIT_CMD_MANDATORY_BOOT                  (0 << 27)
>> +#define BIT_CMD_ALTERNATE_BOOT                  (1 << 27)
>> +#define BIT_CMD_VOLT_SWITCH                     (1 << 28)
>> +#define BIT_CMD_USE_HOLD_REG                    (1 << 29)
>> +#define BIT_CMD_START                           (1 << 31)
>> +
>> +#define DWEMMC_INT_EBE                          (1 << 15)       /* End-bit Err */
>> +#define DWEMMC_INT_SBE                          (1 << 13)       /* Start-bit  Err */
>> +#define DWEMMC_INT_HLE                          (1 << 12)       /* Hardware-lock Err */
>> +#define DWEMMC_INT_FRUN                         (1 << 11)       /* FIFO UN/OV RUN */
>> +#define DWEMMC_INT_DRT                          (1 << 9)        /* Data timeout */
>> +#define DWEMMC_INT_RTO                          (1 << 8)        /* Response timeout */
>> +#define DWEMMC_INT_DCRC                         (1 << 7)        /* Data CRC err */
>> +#define DWEMMC_INT_RCRC                         (1 << 6)        /* Response CRC err */
>> +#define DWEMMC_INT_RXDR                         (1 << 5)
>> +#define DWEMMC_INT_TXDR                         (1 << 4)
>> +#define DWEMMC_INT_DTO                          (1 << 3)        /* Data trans over */
>> +#define DWEMMC_INT_CMD_DONE                     (1 << 2)
>> +#define DWEMMC_INT_RE                           (1 << 1)
>> +
>> +#define DWEMMC_IDMAC_DES0_DIC                   (1 << 1)
>> +#define DWEMMC_IDMAC_DES0_LD                    (1 << 2)
>> +#define DWEMMC_IDMAC_DES0_FS                    (1 << 3)
>> +#define DWEMMC_IDMAC_DES0_CH                    (1 << 4)
>> +#define DWEMMC_IDMAC_DES0_ER                    (1 << 5)
>> +#define DWEMMC_IDMAC_DES0_CES                   (1 << 30)
>> +#define DWEMMC_IDMAC_DES0_OWN                   (1 << 31)
>> +#define DWEMMC_IDMAC_DES1_BS1(x)                ((x) & 0x1fff)
>> +#define DWEMMC_IDMAC_DES2_BS2(x)                (((x) & 0x1fff) << 13)
>> +#define DWEMMC_IDMAC_SWRESET                    (1 << 0)
>> +#define DWEMMC_IDMAC_FB                         (1 << 1)
>> +#define DWEMMC_IDMAC_ENABLE                     (1 << 7)
>> +
>> +#define EMMC_FIX_RCA                            6
>> +
>> +/* bits in MMC0_CTRL */
>> +#define DWEMMC_CTRL_RESET                       (1 << 0)
>> +#define DWEMMC_CTRL_FIFO_RESET                  (1 << 1)
>> +#define DWEMMC_CTRL_DMA_RESET                   (1 << 2)
>> +#define DWEMMC_CTRL_INT_EN                      (1 << 4)
>> +#define DWEMMC_CTRL_DMA_EN                      (1 << 5)
>> +#define DWEMMC_CTRL_IDMAC_EN                    (1 << 25)
>> +#define DWEMMC_CTRL_RESET_ALL                   (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
>> +
>> +#define DWEMMC_STS_DATA_BUSY                    (1 << 9)
>> +
>> +#define DWEMMC_FIFO_TWMARK(x)                   (x & 0xfff)
>> +#define DWEMMC_FIFO_RWMARK(x)                   ((x & 0x1ff) << 16)
>> +#define DWEMMC_DMA_BURST_SIZE(x)                ((x & 0x7) << 28)
>> +
>> +#define DWEMMC_CARD_RD_THR(x)                   ((x & 0xfff) << 16)
>> +#define DWEMMC_CARD_RD_THR_EN                   (1 << 0)
>> +
>> +#define DWEMMC_GET_HDATA_WIDTH(x)               (((x) >> 7) & 0x7)
>> +
>> +#endif  // __DWEMMC_H__
>> diff --git a/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
>> new file mode 100644
>> index 000000000000..eed5fc57fc22
>> --- /dev/null
>> +++ b/Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.c
>> @@ -0,0 +1,693 @@
>> +/** @file
>> +  This file implement the MMC Host Protocol for the DesignWare eMMC.
>> +
>> +  Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> 
> Comment only:
> This file, unlike some other .c files in this series, does not include
> the WARNING header. Feel free to fold one in before pushing.

Good catch.

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

> 
> /
>      Leif
> 
[...]


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver Ard Biesheuvel
@ 2020-05-04 11:41   ` Philippe Mathieu-Daudé
  2020-05-04 11:44     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-04 11:41 UTC (permalink / raw)
  To: devel, ard.biesheuvel; +Cc: leif

On 4/30/20 7:16 PM, Ard Biesheuvel wrote:
> Incorporate the Lan91x driver from EmbeddedPkg, which is only used
> on emulated ARM development platforms and does not follow the UEFI
> driver model. This will allow us to drop if from the core EDK2
> repository.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> ---
>   Platform/ARM/SgiPkg/SgiPlatform.dsc                      |    4 +-
>   Platform/ARM/SgiPkg/SgiPlatform.fdf                      |    2 +-
>   Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf     |    2 +-
>   Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc             |    4 +-
>   Platform/ARM/VExpressPkg/ArmVExpressPkg.dec              |    3 +
>   Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c   | 2236 ++++++++++++++++++++
>   Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf |   59 +
>   Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h |  279 +++
>   8 files changed, 2583 insertions(+), 6 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver
  2020-04-30 17:16 ` [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver Ard Biesheuvel
@ 2020-05-04 11:42   ` Philippe Mathieu-Daudé
  2020-05-04 11:44     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-04 11:42 UTC (permalink / raw)
  To: devel, ard.biesheuvel; +Cc: leif

On 4/30/20 7:16 PM, Ard Biesheuvel wrote:
> Incorporate the Lan91x driver from EmbeddedPkg, which is only used
> on obsolete ARM development platforms and does not follow the UEFI
> driver model. This will allow us to drop if from the core EDK2
> repository.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> ---
>   Platform/ARM/JunoPkg/ArmJuno.dsc                             |    8 +-
>   Platform/ARM/JunoPkg/ArmJuno.fdf                             |    2 +-
>   Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc            |    6 +-
>   Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf            |    2 +-
>   Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                  |    8 +
>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c     | 1539 ++++++++++++++++++++
>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h     |  304 ++++
>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf   |   58 +
>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h   |  401 +++++
>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c | 1039 +++++++++++++
>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h |  283 ++++
>   11 files changed, 3641 insertions(+), 9 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
  2020-05-04 11:40     ` [edk2-devel] " Philippe Mathieu-Daudé
@ 2020-05-04 11:44       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-04 11:44 UTC (permalink / raw)
  To: devel, leif, Ard Biesheuvel

On 5/4/20 1:40 PM, Philippe Mathieu-Daudé wrote:
[...]
> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

Oops, without acute on EDK2:
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver
  2020-05-04 11:41   ` [edk2-devel] " Philippe Mathieu-Daudé
@ 2020-05-04 11:44     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-04 11:44 UTC (permalink / raw)
  To: devel, ard.biesheuvel; +Cc: leif

On 5/4/20 1:41 PM, Philippe Mathieu-Daudé wrote:
> On 4/30/20 7:16 PM, Ard Biesheuvel wrote:
>> Incorporate the Lan91x driver from EmbeddedPkg, which is only used
>> on emulated ARM development platforms and does not follow the UEFI
>> driver model. This will allow us to drop if from the core EDK2
>> repository.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> ---
>>   Platform/ARM/SgiPkg/SgiPlatform.dsc                      |    4 +-
>>   Platform/ARM/SgiPkg/SgiPlatform.fdf                      |    2 +-
>>   Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf     |    2 +-
>>   Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc             |    4 +-
>>   Platform/ARM/VExpressPkg/ArmVExpressPkg.dec              |    3 +
>>   Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.c   | 2236 
>> ++++++++++++++++++++
>>   Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxe.inf |   59 +
>>   Platform/ARM/VExpressPkg/Drivers/Lan91xDxe/Lan91xDxeHw.h |  279 +++
>>   8 files changed, 2583 insertions(+), 6 deletions(-)
> 
> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

Without acute on EDK2:
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver
  2020-05-04 11:42   ` [edk2-devel] " Philippe Mathieu-Daudé
@ 2020-05-04 11:44     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-04 11:44 UTC (permalink / raw)
  To: devel, ard.biesheuvel; +Cc: leif

On 5/4/20 1:42 PM, Philippe Mathieu-Daudé wrote:
> On 4/30/20 7:16 PM, Ard Biesheuvel wrote:
>> Incorporate the Lan91x driver from EmbeddedPkg, which is only used
>> on obsolete ARM development platforms and does not follow the UEFI
>> driver model. This will allow us to drop if from the core EDK2
>> repository.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> ---
>>   Platform/ARM/JunoPkg/ArmJuno.dsc                             |    8 +-
>>   Platform/ARM/JunoPkg/ArmJuno.fdf                             |    2 +-
>>   Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc            |    6 +-
>>   Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf            |    2 +-
>>   Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                  |    8 +
>>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.c     | 1539 
>> ++++++++++++++++++++
>>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.h     |  304 
>> ++++
>>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118Dxe.inf   |   58 +
>>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeHw.h   |  401 
>> +++++
>>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.c | 1039 
>> +++++++++++++
>>   Platform/ARM/VExpressPkg/Drivers/Lan9118Dxe/Lan9118DxeUtil.h |  283 
>> ++++
>>   11 files changed, 3641 insertions(+), 9 deletions(-)
> 
> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

Without acute on EDK2:
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2
  2020-05-04 11:00 ` [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Leif Lindholm
@ 2020-05-04 13:19   ` Ard Biesheuvel
  0 siblings, 0 replies; 20+ messages in thread
From: Ard Biesheuvel @ 2020-05-04 13:19 UTC (permalink / raw)
  To: Leif Lindholm; +Cc: devel, Philippe Mathieu-Daudé

On 5/4/20 1:00 PM, Leif Lindholm wrote:
> On Thu, Apr 30, 2020 at 19:16:41 +0200, Ard Biesheuvel wrote:
>> This is a followup to two patches I sent out separately yesterday, and to
>> the subsequent discussion regarding annotating obsolete drivers as unfit
>> for reuse if they don't follow the UEFI driver model.
>>
>> I have rounded up all the stuff I have in flight for edk2-platforms at
>> the moment, so this v3 supersedes everything else for edk2-platforms you
>> may have in your mailbox.
>>
>> Once this is in, I will follow up with a series for EmbeddedPkg to get
>> rid of the originals.
>>
>> Changes since v2:
>> - move DwEmmcDxe, Lan91x, Lan9118 and ISP 1761 drivers as well
>> - incorporate a build fixlet that I sent out separately as well
>> - add some acks
>>
>> Changes since v1:
>> - use gEmbeddedMmcHostProtocolGuid not gEdkiiMmcHostProtocolGuid
>> - add WARNING comment blocks to all files to document the fact that these
>>    files are only kept for sentimental reasons
>>
>> Ard Biesheuvel (8):
>>    Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference
>>    Platform/ARM/VExpressPkg: incorporate PL180 driver
>>    Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver
>>    Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg
>>    Platform/HiKey: switch to relocated version of eMMC driver
>>    Platform/ARM/VExpressPkg: incorporate Lan91x driver
>>    Platform/ARM/VExpressPkg: incorporate Lan9118 driver
>>    Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver
> 
> Many thanks for this!
> For the series:
> Reviewed-by: Leif Lindholm <leif@nuviainc.com>
> 

Thanks all

Pushed as 9cf38493625a..72d6a1e804cb

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2020-05-04 13:19 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 1/8] Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 2/8] Platform/ARM/VExpressPkg: incorporate PL180 driver Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver Ard Biesheuvel
2020-05-04 10:57   ` Leif Lindholm
2020-04-30 17:16 ` [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg Ard Biesheuvel
2020-05-04 10:58   ` Leif Lindholm
2020-05-04 11:40     ` [edk2-devel] " Philippe Mathieu-Daudé
2020-05-04 11:44       ` Philippe Mathieu-Daudé
2020-04-30 17:16 ` [PATCH edk2-platforms v3 5/8] Platform/HiKey: switch to relocated version of eMMC driver Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver Ard Biesheuvel
2020-05-04 11:41   ` [edk2-devel] " Philippe Mathieu-Daudé
2020-05-04 11:44     ` Philippe Mathieu-Daudé
2020-04-30 17:16 ` [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver Ard Biesheuvel
2020-05-04 11:42   ` [edk2-devel] " Philippe Mathieu-Daudé
2020-05-04 11:44     ` Philippe Mathieu-Daudé
2020-04-30 17:16 ` [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver Ard Biesheuvel
2020-05-04 11:00   ` Leif Lindholm
2020-05-04 11:00 ` [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Leif Lindholm
2020-05-04 13:19   ` Ard Biesheuvel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox