diff mbox

[edk2,v4] ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe: Set Marvell Yukon MAC address

Message ID 1483996851-37828-1-git-send-email-daniil.egranov@arm.com
State New
Headers show

Commit Message

Daniil Egranov Jan. 9, 2017, 9:20 p.m. UTC
From: Daniil Egranov <daniil.egranov@linaro.org>


The patch reads a valid MAC address form the Juno IOFPGA registers 
and pushes it into onboard Marvell Yukon NIC.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Daniil Egranov <daniil.egranov@linaro.org>

---

Hi Leif,

The Marvell control registers should be accesable through the memory
specified in BAR 0.

The new helper function should cover all 4 checks:
- We can read BAR 0 attributes. 
 We always read BAR 0
- BAR 0 resources contain at least one QWORD Address Space Descriptor.
- The first of these refers to something memory mapped.
- That it is not (ARM terminology) Normal memory.
 The BAR 0 for Marvell Yukon should always be a memory type. I am not 
 sure if it's possible to have more than one descriptor for a BAR. The 
 current implementation of PciIoGetBarAttributes() will return only one
 descriptor based on BAR index following the End Tag one. This update 
 will step through Address Space Descriptors anyway until the End Tag.  

Thanks,
Daniil

Changelog:

v4
Provided more comments on the code and corrected functions name.
Created helper function for reading base memory address from BAR.
Added stepping through ACPI address space descriptor until End Tag.

v3
Changed code structure per Leif Lindholm's request

v2
Corrected style issues and code structure.

v1
The patch reads a valid MAC address form the Juno IOFPGA registers 
and pushes it into onboard Marvell Yukon NIC.

 .../ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c     | 289 +++++++++++++++++++++
 .../Drivers/ArmJunoDxe/ArmJunoDxeInternal.h        |  13 +
 2 files changed, 302 insertions(+)

-- 
2.7.4

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Comments

Leif Lindholm Jan. 10, 2017, 10:36 p.m. UTC | #1
On Mon, Jan 09, 2017 at 03:20:51PM -0600, Daniil Egranov wrote:
> From: Daniil Egranov <daniil.egranov@linaro.org>

> 

> The patch reads a valid MAC address form the Juno IOFPGA registers 

> and pushes it into onboard Marvell Yukon NIC.

> 

> Contributed-under: TianoCore Contribution Agreement 1.0

> Signed-off-by: Daniil Egranov <daniil.egranov@linaro.org>


Thanks for sticking with it.

I fixed a typo in commit message an shortened the subject line
slightly, but:
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>

Pushed as a8675a1

Regards,

Leif

> ---

> 

> Hi Leif,

> 

> The Marvell control registers should be accesable through the memory

> specified in BAR 0.

> 

> The new helper function should cover all 4 checks:

> - We can read BAR 0 attributes. 

>  We always read BAR 0

> - BAR 0 resources contain at least one QWORD Address Space Descriptor.

> - The first of these refers to something memory mapped.

> - That it is not (ARM terminology) Normal memory.

>  The BAR 0 for Marvell Yukon should always be a memory type. I am not 

>  sure if it's possible to have more than one descriptor for a BAR. The 

>  current implementation of PciIoGetBarAttributes() will return only one

>  descriptor based on BAR index following the End Tag one. This update 

>  will step through Address Space Descriptors anyway until the End Tag.  

> 

> Thanks,

> Daniil

> 

> Changelog:

> 

> v4

> Provided more comments on the code and corrected functions name.

> Created helper function for reading base memory address from BAR.

> Added stepping through ACPI address space descriptor until End Tag.

> 

> v3

> Changed code structure per Leif Lindholm's request

> 

> v2

> Corrected style issues and code structure.

> 

> v1

> The patch reads a valid MAC address form the Juno IOFPGA registers 

> and pushes it into onboard Marvell Yukon NIC.

> 

>  .../ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c     | 289 +++++++++++++++++++++

>  .../Drivers/ArmJunoDxe/ArmJunoDxeInternal.h        |  13 +

>  2 files changed, 302 insertions(+)

> 

> diff --git a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c

> index b97f044..5847d0a 100644

> --- a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c

> +++ b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c

> @@ -15,7 +15,9 @@

>  #include "ArmJunoDxeInternal.h"

>  #include <ArmPlatform.h>

>  

> +#include <IndustryStandard/Pci.h>

>  #include <Protocol/DevicePathFromText.h>

> +#include <Protocol/PciIo.h>

>  #include <Protocol/PciRootBridgeIo.h>

>  

>  #include <Guid/EventGroup.h>

> @@ -69,6 +71,290 @@ STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mPciRootComplexDevicePath = {

>  EFI_EVENT mAcpiRegistration = NULL;

>  

>  /**

> +  This function reads PCI ID of the controller.

> +

> +  @param[in]  PciIo   PCI IO protocol handle

> +  @param[in]  PciId   Looking for specified PCI ID Vendor/Device

> +**/

> +STATIC

> +EFI_STATUS

> +ReadMarvellYoukonPciId (

> +  IN EFI_PCI_IO_PROTOCOL  *PciIo,

> +  IN UINT32               PciId

> +  )

> +{

> +  UINT32      DevicePciId;

> +  EFI_STATUS  Status;

> +

> +  Status = PciIo->Pci.Read (

> +                        PciIo,

> +                        EfiPciIoWidthUint32,

> +                        PCI_VENDOR_ID_OFFSET,

> +                        1,

> +                        &DevicePciId);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  if (DevicePciId != PciId) {

> +    return EFI_NOT_FOUND;

> +  }

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  This function searches for Marvell Yukon NIC on the Juno

> +  platform and returns PCI IO protocol handle for the controller.

> +

> +  @param[out]  PciIo   PCI IO protocol handle

> +**/

> +STATIC

> +EFI_STATUS

> +GetMarvellYukonPciIoProtocol (

> +  OUT EFI_PCI_IO_PROTOCOL  **PciIo

> +  )

> +{

> +  UINTN       HandleCount;

> +  EFI_HANDLE  *HandleBuffer;

> +  UINTN       HIndex;

> +  EFI_STATUS  Status;

> +

> +  Status = gBS->LocateHandleBuffer (

> +                  ByProtocol,

> +                  &gEfiPciIoProtocolGuid,

> +                  NULL,

> +                  &HandleCount,

> +                  &HandleBuffer);

> +  if (EFI_ERROR (Status)) {

> +    return (Status);

> +  }

> +

> +  for (HIndex = 0; HIndex < HandleCount; ++HIndex) {

> +    // If PciIo opened with EFI_OPEN_PROTOCOL_GET_PROTOCOL, the CloseProtocol() is not required

> +    Status = gBS->OpenProtocol (

> +                    HandleBuffer[HIndex],

> +                    &gEfiPciIoProtocolGuid,

> +                    (VOID **) PciIo,

> +                    NULL,

> +                    NULL,

> +                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);

> +    if (EFI_ERROR (Status)) {

> +      continue;

> +    }

> +

> +    Status = ReadMarvellYoukonPciId (*PciIo, JUNO_MARVELL_YUKON_ID);

> +    if (EFI_ERROR (Status)) {

> +      continue;

> +    } else {

> +      break;

> +    }

> +  }

> +

> +  gBS->FreePool (HandleBuffer);

> +

> +  return Status;

> +}

> +

> +/**

> + This function restore the original controller attributes

> +

> +   @param[in]   PciIo               PCI IO protocol handle

> +   @param[in]   PciAttr             PCI controller attributes.

> +   @param[in]   AcpiResDescriptor   ACPI 2.0 resource descriptors for the BAR

> +**/

> +STATIC

> +VOID

> +RestorePciDev (

> +  IN EFI_PCI_IO_PROTOCOL                *PciIo,

> +  IN UINT64                             PciAttr

> +  )

> +{

> +  PciIo->Attributes (

> +           PciIo,

> +           EfiPciIoAttributeOperationSet,

> +           PciAttr,

> +           NULL

> +           );

> +}

> +

> +/**

> + This function returns PCI MMIO base address for a controller

> +

> +   @param[in]   PciIo               PCI IO protocol handle

> +   @param[out]  PciRegBase          PCI base MMIO address

> +**/

> +STATIC

> +EFI_STATUS

> +BarIsDeviceMemory (

> +  IN   EFI_PCI_IO_PROTOCOL *PciIo,

> +  OUT  UINT32              *PciRegBase

> +  )

> +{

> +  EFI_STATUS                         Status;

> +  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR  *AcpiResDescriptor;

> +  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR  *AcpiCurrentDescriptor;

> +

> +  // Marvell Yukon's Bar0 provides base memory address for control registers

> +  Status = PciIo->GetBarAttributes (PciIo, PCI_BAR_IDX0, NULL, (VOID**)&AcpiResDescriptor);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  AcpiCurrentDescriptor = AcpiResDescriptor;

> +

> +  // Search for a memory type descriptor

> +  while (AcpiCurrentDescriptor->Desc != ACPI_END_TAG_DESCRIPTOR) {

> +

> +    // Check if Bar is memory type one and fetch a base address

> +    if (AcpiCurrentDescriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR &&

> +        AcpiCurrentDescriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM &&

> +        !(AcpiCurrentDescriptor->SpecificFlag & ACPI_SPECFLAG_PREFETCHABLE)) {

> +      *PciRegBase = AcpiCurrentDescriptor->AddrRangeMin;

> +      break;

> +    } else {

> +      Status = EFI_UNSUPPORTED;

> +    }

> +

> +    AcpiCurrentDescriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (AcpiCurrentDescriptor + 1);

> +  }

> +

> +  gBS->FreePool (AcpiResDescriptor);

> +

> +  return Status;

> +}

> +

> +/**

> + This function provides PCI MMIO base address, old PCI controller attributes.

> +

> +   @param[in]   PciIo               PCI IO protocol handle

> +   @param[out]  PciRegBase          PCI base MMIO address

> +   @param[out]  OldPciAttr          Old PCI controller attributes.

> +**/

> +STATIC

> +EFI_STATUS

> +InitPciDev (

> +  IN EFI_PCI_IO_PROTOCOL                 *PciIo,

> +  OUT UINT32                             *PciRegBase,

> +  OUT UINT64                             *OldPciAttr

> +  )

> +{

> +  UINT64      AttrSupports;

> +  EFI_STATUS  Status;

> +

> +  // Get controller's current attributes

> +  Status = PciIo->Attributes (

> +                    PciIo,

> +                    EfiPciIoAttributeOperationGet,

> +                    0,

> +                    OldPciAttr);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  // Fetch supported attributes

> +  Status = PciIo->Attributes (

> +                    PciIo,

> +                    EfiPciIoAttributeOperationSupported,

> +                    0,

> +                    &AttrSupports);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  // Enable EFI_PCI_IO_ATTRIBUTE_IO, EFI_PCI_IO_ATTRIBUTE_MEMORY and

> +  // EFI_PCI_IO_ATTRIBUTE_BUS_MASTER bits in the PCI Config Header

> +  AttrSupports &= EFI_PCI_DEVICE_ENABLE;

> +  Status = PciIo->Attributes (

> +                    PciIo,

> +                    EfiPciIoAttributeOperationEnable,

> +                    AttrSupports,

> +                    NULL);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Status = BarIsDeviceMemory (PciIo, PciRegBase);

> +  if (EFI_ERROR (Status)) {

> +    RestorePciDev (PciIo, *OldPciAttr);

> +  }

> +

> +  return Status;

> +}

> +

> +/**

> + This function reads MAC address from IOFPGA and writes it to Marvell Yukon NIC

> +

> +   @param[in]   PciRegBase   PCI base MMIO address

> +**/

> +STATIC

> +EFI_STATUS

> +WriteMacAddress (

> +  IN UINT32  PciRegBase

> +  )

> +{

> +  UINT32  MacHigh;

> +  UINT32  MacLow;

> +

> +  // Read MAC address from IOFPGA

> +  MacHigh= MmioRead32 (ARM_JUNO_SYS_PCIGBE_H);

> +  MacLow = MmioRead32 (ARM_JUNO_SYS_PCIGBE_L);

> +

> +  // Set software reset control register to protect from deactivation

> +  // the config write state

> +  MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);

> +

> +  // Convert to Marvell MAC Address register format

> +  MacHigh = SwapBytes32 ((MacHigh & 0xFFFF) << 16 |

> +                         (MacLow & 0xFFFF0000) >> 16);

> +  MacLow = SwapBytes32 (MacLow) >> 16;

> +

> +  // Set MAC Address

> +  MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_ENABLE);

> +  MmioWrite32 (PciRegBase + R_MAC, MacHigh);

> +  MmioWrite32 (PciRegBase + R_MAC_MAINT, MacHigh);

> +  MmioWrite32 (PciRegBase + R_MAC + R_MAC_LOW, MacLow);

> +  MmioWrite32 (PciRegBase + R_MAC_MAINT + R_MAC_LOW, MacLow);

> +  MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_DISABLE);

> +

> +  // Initiate device reset

> +  MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_SET);

> +  MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

> +  The function reads MAC address from Juno IOFPGA registers and writes it

> +  into Marvell Yukon NIC.

> +**/

> +STATIC

> +EFI_STATUS

> +ArmJunoSetNicMacAddress ()

> +{

> +  UINT64                              OldPciAttr;

> +  EFI_PCI_IO_PROTOCOL*                PciIo;

> +  UINT32                              PciRegBase;

> +  EFI_STATUS                          Status;

> +

> +  Status = GetMarvellYukonPciIoProtocol (&PciIo);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Status = InitPciDev (PciIo, &PciRegBase, &OldPciAttr);

> +  if (EFI_ERROR (Status)) {

> +    return Status;

> +  }

> +

> +  Status = WriteMacAddress (PciRegBase);

> +

> +  RestorePciDev (PciIo, OldPciAttr);

> +

> +  return EFI_SUCCESS;

> +}

> +

> +/**

>    Notification function of the event defined as belonging to the

>    EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in

>    the entry point of the driver.

> @@ -106,6 +392,9 @@ OnEndOfDxe (

>  

>    Status = gBS->ConnectController (Handle, NULL, PciRootComplexDevicePath, FALSE);

>    ASSERT_EFI_ERROR (Status);

> +

> +  Status = ArmJunoSetNicMacAddress ();

> +  ASSERT_EFI_ERROR (Status);

>  }

>  

>  STATIC

> diff --git a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h

> index 662c413..df02770 100644

> --- a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h

> +++ b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h

> @@ -29,6 +29,19 @@

>  

>  #include <IndustryStandard/Acpi.h>

>  

> +#define ACPI_SPECFLAG_PREFETCHABLE    0x06

> +#define JUNO_MARVELL_YUKON_ID         0x438011AB /* Juno Marvell PCI Dev ID */

> +#define TST_CFG_WRITE_ENABLE          0x02       /* Enable Config Write */

> +#define TST_CFG_WRITE_DISABLE         0x00       /* Disable Config Write */

> +#define CS_RESET_CLR                  0x02       /* SW Reset Clear */

> +#define CS_RESET_SET                  0x00       /* SW Reset Set */

> +#define R_CONTROL_STATUS              0x0004     /* Control/Status Register */

> +#define R_MAC                         0x0100     /* MAC Address */

> +#define R_MAC_MAINT                   0x0110     /* MAC Address Maintenance */

> +#define R_MAC_LOW                     0x04       /* MAC Address Low Register Offset */

> +#define R_TST_CTRL_1                  0x0158     /* Test Control Register 1 */

> +

> +

>  EFI_STATUS

>  PciEmulationEntryPoint (

>    VOID

> -- 

> 2.7.4

> 

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Ryan Harkin Jan. 11, 2017, 11:45 a.m. UTC | #2
On 10 January 2017 at 22:36, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> On Mon, Jan 09, 2017 at 03:20:51PM -0600, Daniil Egranov wrote:

>> From: Daniil Egranov <daniil.egranov@linaro.org>

>>

>> The patch reads a valid MAC address form the Juno IOFPGA registers

>> and pushes it into onboard Marvell Yukon NIC.

>>

>> Contributed-under: TianoCore Contribution Agreement 1.0

>> Signed-off-by: Daniil Egranov <daniil.egranov@linaro.org>

>

> Thanks for sticking with it.

>

> I fixed a typo in commit message an shortened the subject line

> slightly, but:

> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>

> Pushed as a8675a1

>


Excellent, another patch in my fork that can go.

Daniil,

When you originally posted this, you posted it along with
"EmbeddedPkg: Added device configuration protocol". As Leif is happy
with this patch as-is, I assume it isn't needed any more. You haven't
chased it, you didn't respond to my ping about it and it's currently
in my tree.

Unless you get back to me about it, I'll drop it next time I rebase.

Cheers,
Ryan.
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Ryan Harkin Jan. 11, 2017, 12:24 p.m. UTC | #3
On 11 January 2017 at 11:45, Ryan Harkin <ryan.harkin@linaro.org> wrote:
> On 10 January 2017 at 22:36, Leif Lindholm <leif.lindholm@linaro.org> wrote:

>> On Mon, Jan 09, 2017 at 03:20:51PM -0600, Daniil Egranov wrote:

>>> From: Daniil Egranov <daniil.egranov@linaro.org>

>>>

>>> The patch reads a valid MAC address form the Juno IOFPGA registers

>>> and pushes it into onboard Marvell Yukon NIC.

>>>

>>> Contributed-under: TianoCore Contribution Agreement 1.0

>>> Signed-off-by: Daniil Egranov <daniil.egranov@linaro.org>

>>

>> Thanks for sticking with it.

>>

>> I fixed a typo in commit message an shortened the subject line

>> slightly, but:

>> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>

>> Pushed as a8675a1

>>

>

> Excellent, another patch in my fork that can go.

>


Unfortunately, while this works on Juno R1 and R2, it hangs R0 every time:

ASSERT [ArmJunoDxe]
/linaro/platforms/uefi/edk2/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c(397):
!EFI_ERROR (Status)

I guess that before the refactor, it would return EFI_SUCCESS on R0
when the MAC configuration failed...

> Daniil,

>

> When you originally posted this, you posted it along with

> "EmbeddedPkg: Added device configuration protocol". As Leif is happy

> with this patch as-is, I assume it isn't needed any more. You haven't

> chased it, you didn't respond to my ping about it and it's currently

> in my tree.

>

> Unless you get back to me about it, I'll drop it next time I rebase.

>


And I tested with this patch removed and everything still seemed to
work, so I'm assuming it's not needed and leaving it out unless you
explain otherwise.

> Cheers,

> Ryan.

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox

Patch

diff --git a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c
index b97f044..5847d0a 100644
--- a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c
+++ b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c
@@ -15,7 +15,9 @@ 
 #include "ArmJunoDxeInternal.h"
 #include <ArmPlatform.h>
 
+#include <IndustryStandard/Pci.h>
 #include <Protocol/DevicePathFromText.h>
+#include <Protocol/PciIo.h>
 #include <Protocol/PciRootBridgeIo.h>
 
 #include <Guid/EventGroup.h>
@@ -69,6 +71,290 @@  STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mPciRootComplexDevicePath = {
 EFI_EVENT mAcpiRegistration = NULL;
 
 /**
+  This function reads PCI ID of the controller.
+
+  @param[in]  PciIo   PCI IO protocol handle
+  @param[in]  PciId   Looking for specified PCI ID Vendor/Device
+**/
+STATIC
+EFI_STATUS
+ReadMarvellYoukonPciId (
+  IN EFI_PCI_IO_PROTOCOL  *PciIo,
+  IN UINT32               PciId
+  )
+{
+  UINT32      DevicePciId;
+  EFI_STATUS  Status;
+
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        PCI_VENDOR_ID_OFFSET,
+                        1,
+                        &DevicePciId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (DevicePciId != PciId) {
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function searches for Marvell Yukon NIC on the Juno
+  platform and returns PCI IO protocol handle for the controller.
+
+  @param[out]  PciIo   PCI IO protocol handle
+**/
+STATIC
+EFI_STATUS
+GetMarvellYukonPciIoProtocol (
+  OUT EFI_PCI_IO_PROTOCOL  **PciIo
+  )
+{
+  UINTN       HandleCount;
+  EFI_HANDLE  *HandleBuffer;
+  UINTN       HIndex;
+  EFI_STATUS  Status;
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer);
+  if (EFI_ERROR (Status)) {
+    return (Status);
+  }
+
+  for (HIndex = 0; HIndex < HandleCount; ++HIndex) {
+    // If PciIo opened with EFI_OPEN_PROTOCOL_GET_PROTOCOL, the CloseProtocol() is not required
+    Status = gBS->OpenProtocol (
+                    HandleBuffer[HIndex],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) PciIo,
+                    NULL,
+                    NULL,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Status = ReadMarvellYoukonPciId (*PciIo, JUNO_MARVELL_YUKON_ID);
+    if (EFI_ERROR (Status)) {
+      continue;
+    } else {
+      break;
+    }
+  }
+
+  gBS->FreePool (HandleBuffer);
+
+  return Status;
+}
+
+/**
+ This function restore the original controller attributes
+
+   @param[in]   PciIo               PCI IO protocol handle
+   @param[in]   PciAttr             PCI controller attributes.
+   @param[in]   AcpiResDescriptor   ACPI 2.0 resource descriptors for the BAR
+**/
+STATIC
+VOID
+RestorePciDev (
+  IN EFI_PCI_IO_PROTOCOL                *PciIo,
+  IN UINT64                             PciAttr
+  )
+{
+  PciIo->Attributes (
+           PciIo,
+           EfiPciIoAttributeOperationSet,
+           PciAttr,
+           NULL
+           );
+}
+
+/**
+ This function returns PCI MMIO base address for a controller
+
+   @param[in]   PciIo               PCI IO protocol handle
+   @param[out]  PciRegBase          PCI base MMIO address
+**/
+STATIC
+EFI_STATUS
+BarIsDeviceMemory (
+  IN   EFI_PCI_IO_PROTOCOL *PciIo,
+  OUT  UINT32              *PciRegBase
+  )
+{
+  EFI_STATUS                         Status;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR  *AcpiResDescriptor;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR  *AcpiCurrentDescriptor;
+
+  // Marvell Yukon's Bar0 provides base memory address for control registers
+  Status = PciIo->GetBarAttributes (PciIo, PCI_BAR_IDX0, NULL, (VOID**)&AcpiResDescriptor);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  AcpiCurrentDescriptor = AcpiResDescriptor;
+
+  // Search for a memory type descriptor
+  while (AcpiCurrentDescriptor->Desc != ACPI_END_TAG_DESCRIPTOR) {
+
+    // Check if Bar is memory type one and fetch a base address
+    if (AcpiCurrentDescriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR &&
+        AcpiCurrentDescriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM &&
+        !(AcpiCurrentDescriptor->SpecificFlag & ACPI_SPECFLAG_PREFETCHABLE)) {
+      *PciRegBase = AcpiCurrentDescriptor->AddrRangeMin;
+      break;
+    } else {
+      Status = EFI_UNSUPPORTED;
+    }
+
+    AcpiCurrentDescriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (AcpiCurrentDescriptor + 1);
+  }
+
+  gBS->FreePool (AcpiResDescriptor);
+
+  return Status;
+}
+
+/**
+ This function provides PCI MMIO base address, old PCI controller attributes.
+
+   @param[in]   PciIo               PCI IO protocol handle
+   @param[out]  PciRegBase          PCI base MMIO address
+   @param[out]  OldPciAttr          Old PCI controller attributes.
+**/
+STATIC
+EFI_STATUS
+InitPciDev (
+  IN EFI_PCI_IO_PROTOCOL                 *PciIo,
+  OUT UINT32                             *PciRegBase,
+  OUT UINT64                             *OldPciAttr
+  )
+{
+  UINT64      AttrSupports;
+  EFI_STATUS  Status;
+
+  // Get controller's current attributes
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationGet,
+                    0,
+                    OldPciAttr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Fetch supported attributes
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationSupported,
+                    0,
+                    &AttrSupports);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Enable EFI_PCI_IO_ATTRIBUTE_IO, EFI_PCI_IO_ATTRIBUTE_MEMORY and
+  // EFI_PCI_IO_ATTRIBUTE_BUS_MASTER bits in the PCI Config Header
+  AttrSupports &= EFI_PCI_DEVICE_ENABLE;
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationEnable,
+                    AttrSupports,
+                    NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = BarIsDeviceMemory (PciIo, PciRegBase);
+  if (EFI_ERROR (Status)) {
+    RestorePciDev (PciIo, *OldPciAttr);
+  }
+
+  return Status;
+}
+
+/**
+ This function reads MAC address from IOFPGA and writes it to Marvell Yukon NIC
+
+   @param[in]   PciRegBase   PCI base MMIO address
+**/
+STATIC
+EFI_STATUS
+WriteMacAddress (
+  IN UINT32  PciRegBase
+  )
+{
+  UINT32  MacHigh;
+  UINT32  MacLow;
+
+  // Read MAC address from IOFPGA
+  MacHigh= MmioRead32 (ARM_JUNO_SYS_PCIGBE_H);
+  MacLow = MmioRead32 (ARM_JUNO_SYS_PCIGBE_L);
+
+  // Set software reset control register to protect from deactivation
+  // the config write state
+  MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);
+
+  // Convert to Marvell MAC Address register format
+  MacHigh = SwapBytes32 ((MacHigh & 0xFFFF) << 16 |
+                         (MacLow & 0xFFFF0000) >> 16);
+  MacLow = SwapBytes32 (MacLow) >> 16;
+
+  // Set MAC Address
+  MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_ENABLE);
+  MmioWrite32 (PciRegBase + R_MAC, MacHigh);
+  MmioWrite32 (PciRegBase + R_MAC_MAINT, MacHigh);
+  MmioWrite32 (PciRegBase + R_MAC + R_MAC_LOW, MacLow);
+  MmioWrite32 (PciRegBase + R_MAC_MAINT + R_MAC_LOW, MacLow);
+  MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_DISABLE);
+
+  // Initiate device reset
+  MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_SET);
+  MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  The function reads MAC address from Juno IOFPGA registers and writes it
+  into Marvell Yukon NIC.
+**/
+STATIC
+EFI_STATUS
+ArmJunoSetNicMacAddress ()
+{
+  UINT64                              OldPciAttr;
+  EFI_PCI_IO_PROTOCOL*                PciIo;
+  UINT32                              PciRegBase;
+  EFI_STATUS                          Status;
+
+  Status = GetMarvellYukonPciIoProtocol (&PciIo);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = InitPciDev (PciIo, &PciRegBase, &OldPciAttr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = WriteMacAddress (PciRegBase);
+
+  RestorePciDev (PciIo, OldPciAttr);
+
+  return EFI_SUCCESS;
+}
+
+/**
   Notification function of the event defined as belonging to the
   EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in
   the entry point of the driver.
@@ -106,6 +392,9 @@  OnEndOfDxe (
 
   Status = gBS->ConnectController (Handle, NULL, PciRootComplexDevicePath, FALSE);
   ASSERT_EFI_ERROR (Status);
+
+  Status = ArmJunoSetNicMacAddress ();
+  ASSERT_EFI_ERROR (Status);
 }
 
 STATIC
diff --git a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h
index 662c413..df02770 100644
--- a/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h
+++ b/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxeInternal.h
@@ -29,6 +29,19 @@ 
 
 #include <IndustryStandard/Acpi.h>
 
+#define ACPI_SPECFLAG_PREFETCHABLE    0x06
+#define JUNO_MARVELL_YUKON_ID         0x438011AB /* Juno Marvell PCI Dev ID */
+#define TST_CFG_WRITE_ENABLE          0x02       /* Enable Config Write */
+#define TST_CFG_WRITE_DISABLE         0x00       /* Disable Config Write */
+#define CS_RESET_CLR                  0x02       /* SW Reset Clear */
+#define CS_RESET_SET                  0x00       /* SW Reset Set */
+#define R_CONTROL_STATUS              0x0004     /* Control/Status Register */
+#define R_MAC                         0x0100     /* MAC Address */
+#define R_MAC_MAINT                   0x0110     /* MAC Address Maintenance */
+#define R_MAC_LOW                     0x04       /* MAC Address Low Register Offset */
+#define R_TST_CTRL_1                  0x0158     /* Test Control Register 1 */
+
+
 EFI_STATUS
 PciEmulationEntryPoint (
   VOID