From a598bf17b06e76def58218dc9f16e6eb9f156c06 Mon Sep 17 00:00:00 2001
From: Olivier Martin <olivier.martin@arm.com>
Date: Tue, 25 Mar 2014 18:02:18 +0000
Subject: [PATCH 14/14] ArmPkg/ArmGic: Added GicV3 support to ArmGicDxe

Change-Id: Iefb6b08d28ab927b9f65e5472f04611d2758e2a5
---
 ArmPkg/Drivers/ArmGic/ArmGicDxe.c         |   2 +
 ArmPkg/Drivers/ArmGic/ArmGicDxe.h         |   9 +
 ArmPkg/Drivers/ArmGic/ArmGicDxe.inf       |   3 +-
 ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c | 292 ++++++++++++++++++++++++++++++
 4 files changed, 305 insertions(+), 1 deletion(-)
 create mode 100755 ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c

diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.c b/ArmPkg/Drivers/ArmGic/ArmGicDxe.c
index 1fd67ca..ab3a9f6 100755
--- a/ArmPkg/Drivers/ArmGic/ArmGicDxe.c
+++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.c
@@ -53,6 +53,8 @@ InterruptDxeInitialize (
 
   if (Revision == ARM_GIC_ARCH_REVISION_2) {
     Status = GicV2DxeInitialize (ImageHandle, SystemTable);
+  } else if (Revision == ARM_GIC_ARCH_REVISION_3) {
+    Status = GicV3DxeInitialize (ImageHandle, SystemTable);
   } else {
     Status = EFI_UNSUPPORTED;
   }
diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.h b/ArmPkg/Drivers/ArmGic/ArmGicDxe.h
index 43bb888..af33aa9 100755
--- a/ArmPkg/Drivers/ArmGic/ArmGicDxe.h
+++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.h
@@ -55,4 +55,13 @@ GicV2DxeInitialize (
   IN EFI_SYSTEM_TABLE   *SystemTable
   );
 
+//
+// GicV3 API
+//
+EFI_STATUS
+GicV3DxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  );
+
 #endif
diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf b/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
index 5f46a91..1838bee 100755
--- a/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
+++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
@@ -25,8 +25,9 @@
 [Sources.common]
   ArmGicDxe.c
   ArmGicCommonDxe.c
-  
+
   GicV2/ArmGicV2Dxe.c
+  GicV3/ArmGicV3Dxe.c
 
 [Packages]
   MdePkg/MdePkg.dec
diff --git a/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c b/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c
new file mode 100755
index 0000000..cd647f9
--- /dev/null
+++ b/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c
@@ -0,0 +1,292 @@
+/** @file
+*
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include "ArmGicDxe.h"
+#include "GicV3/ArmGicV3Lib.h"
+
+#define ARM_GIC_DEFAULT_PRIORITY  0x80
+
+extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;
+
+/**
+  Enable interrupt source Source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+
+  @retval EFI_SUCCESS       Source interrupt enabled.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+EFI_STATUS
+EFIAPI
+GicV3EnableInterruptSource (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source
+  )
+{
+  if (Source > mGicNumInterrupts) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  return ArmGicEnableInterrupt (PcdGet32 (PcdGicDistributorBase), Source);
+}
+
+/**
+  Disable interrupt source Source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+
+  @retval EFI_SUCCESS       Source interrupt disabled.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+EFI_STATUS
+EFIAPI
+GicV3DisableInterruptSource (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source
+  )
+{
+  if (Source > mGicNumInterrupts) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  return ArmGicDisableInterrupt (PcdGet32 (PcdGicDistributorBase), Source);
+}
+
+/**
+  Return current state of interrupt source Source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+  @param InterruptState  TRUE: source enabled, FALSE: source disabled.
+
+  @retval EFI_SUCCESS       InterruptState is valid
+  @retval EFI_DEVICE_ERROR  InterruptState is not valid
+
+**/
+EFI_STATUS
+EFIAPI
+GicV3GetInterruptSourceState (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source,
+  IN BOOLEAN                            *InterruptState
+  )
+{
+  if (Source > mGicNumInterrupts) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  *InterruptState = ArmGicIsInterruptEnabled (PcdGet32 (PcdGicDistributorBase), Source);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Signal to the hardware that the End Of Intrrupt state
+  has been reached.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+
+  @retval EFI_SUCCESS       Source interrupt EOI'ed.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+EFI_STATUS
+EFIAPI
+GicV3EndOfInterrupt (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source
+  )
+{
+  if (Source > mGicNumInterrupts) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  ArmGicV3EndOfInterrupt (Source);
+  return EFI_SUCCESS;
+}
+
+/**
+  EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
+
+  @param  InterruptType    Defines the type of interrupt or exception that
+                           occurred on the processor.This parameter is processor architecture specific.
+  @param  SystemContext    A pointer to the processor context when
+                           the interrupt occurred on the processor.
+
+  @return None
+
+**/
+VOID
+EFIAPI
+GicV3IrqInterruptHandler (
+  IN EFI_EXCEPTION_TYPE           InterruptType,
+  IN EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  UINT32                      GicInterrupt;
+  HARDWARE_INTERRUPT_HANDLER  InterruptHandler;
+
+  GicInterrupt = ArmGicV3AcknowledgeInterrupt ();
+
+  // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
+  if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
+    // The special interrupt do not need to be acknowledge
+    return;
+  }
+
+  InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
+  if (InterruptHandler != NULL) {
+    // Call the registered interrupt handler.
+    InterruptHandler (GicInterrupt, SystemContext);
+  } else {
+    DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
+  }
+
+  GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);
+}
+
+//
+// The protocol instance produced by this driver
+//
+EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
+  RegisterInterruptSource,
+  GicV3EnableInterruptSource,
+  GicV3DisableInterruptSource,
+  GicV3GetInterruptSourceState,
+  GicV3EndOfInterrupt
+};
+
+/**
+  Shutdown our hardware
+
+  DXE Core will disable interrupts and turn off the timer and disable interrupts
+  after all the event handlers have run.
+
+  @param[in]  Event   The Event that is being processed
+  @param[in]  Context Event Context
+**/
+VOID
+EFIAPI
+GicV3ExitBootServicesEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  UINTN    Index;
+
+  // Acknowledge all pending interrupts
+  for (Index = 0; Index < mGicNumInterrupts; Index++) {
+    GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
+  }
+
+  for (Index = 0; Index < mGicNumInterrupts; Index++) {
+    GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, Index);
+  }
+
+  // Disable Gic Interface
+  ArmGicV3DisableInterruptInterface ();
+
+  // Disable Gic Distributor
+  ArmGicDisableDistributor (PcdGet32 (PcdGicDistributorBase));
+}
+
+/**
+  Initialize the state information for the CPU Architectural Protocol
+
+  @param  ImageHandle   of the loaded driver
+  @param  SystemTable   Pointer to the System Table
+
+  @retval EFI_SUCCESS           Protocol registered
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Hardware problems
+
+**/
+EFI_STATUS
+GicV3DxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+  UINTN                   Index;
+  UINT32                  RegOffset;
+  UINTN                   RegShift;
+  UINT32                  CpuTarget;
+
+  // Make sure the Interrupt Controller Protocol is not already installed in the system.
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
+
+  mGicNumInterrupts = ArmGicGetMaxNumInterrupts (PcdGet32 (PcdGicDistributorBase));
+
+  for (Index = 0; Index < mGicNumInterrupts; Index++) {
+    GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
+
+    // Set Priority
+    RegOffset = Index / 4;
+    RegShift = (Index % 4) * 8;
+    MmioAndThenOr32 (
+      PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPR + (4 * RegOffset),
+      ~(0xff << RegShift),
+      ARM_GIC_DEFAULT_PRIORITY << RegShift
+      );
+  }
+
+  //
+  // Targets the interrupts to the Primary Cpu
+  //
+
+  // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
+  // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
+  // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
+  // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
+  //
+  // Read the first Interrupt Processor Targets Register (that corresponds to the 4
+  // first SGIs)
+  CpuTarget = MmioRead32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR);
+
+  // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
+  // is 0 when we run on a uniprocessor platform.
+  if (CpuTarget != 0) {
+    // The 8 first Interrupt Processor Targets Registers are read-only
+    for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
+      MmioWrite32 (PcdGet32 (PcdGicDistributorBase) + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);
+    }
+  }
+
+  // Set binary point reg to 0x7 (no preemption)
+  ArmGicV3SetBinaryPointer (0x7);
+
+  // Set priority mask reg to 0xff to allow all priorities through
+  ArmGicV3SetPriorityMask (0xff);
+
+  // Enable gic cpu interface
+  ArmGicV3EnableInterruptInterface ();
+
+  // Enable gic distributor
+  ArmGicEnableDistributor (PcdGet32 (PcdGicDistributorBase));
+
+  Status = InstallAndRegisterInterruptService (
+          &gHardwareInterruptV3Protocol, GicV3IrqInterruptHandler, GicV3ExitBootServicesEvent);
+
+  return Status;
+}
-- 
1.8.5

