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