public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Daniil Egranov <daniil.egranov@arm.com>
To: edk2-devel@lists.01.org
Cc: leif.lindholm@linaro.org, Daniil Egranov <daniil.egranov@linaro.org>
Subject: [PATCH v4] ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe: Set Marvell Yukon MAC address
Date: Mon,  9 Jan 2017 15:20:51 -0600	[thread overview]
Message-ID: <1483996851-37828-1-git-send-email-daniil.egranov@arm.com> (raw)

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(+)

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



             reply	other threads:[~2017-01-09 21:21 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-09 21:20 Daniil Egranov [this message]
2017-01-10 22:36 ` [PATCH v4] ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe: Set Marvell Yukon MAC address Leif Lindholm
2017-01-11 11:45   ` Ryan Harkin
2017-01-11 12:24     ` Ryan Harkin

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=1483996851-37828-1-git-send-email-daniil.egranov@arm.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

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

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