+/** @file\r
+ Portions copyright (c) 2010, Apple Inc. All rights reserved.\r
+ \r
+ All rights reserved. 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
+\r
+Copyright (c) 2009, Hewlett-Packard Company \r
+All rights reserved. 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
+Module Name:\r
+\r
+ Gic.c\r
+\r
+Abstract:\r
+\r
+ Driver implementing the GIC interrupt controller protocol\r
+\r
+--*/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/IoLib.h>\r
+\r
+#include <Protocol/Cpu.h>\r
+#include <Protocol/HardwareInterrupt.h>\r
+\r
+\r
+//\r
+// EB board definitions\r
+//\r
+#define EB_GIC1_CPU_INTF_BASE 0x10040000\r
+#define EB_GIC1_DIST_BASE 0x10041000\r
+#define EB_GIC2_CPU_INTF_BASE 0x10050000\r
+#define EB_GIC2_DIST_BASE 0x10051000\r
+#define EB_GIC3_CPU_INTF_BASE 0x10060000\r
+#define EB_GIC3_DIST_BASE 0x10061000\r
+#define EB_GIC4_CPU_INTF_BASE 0x10070000\r
+#define EB_GIC5_DIST_BASE 0x10071000\r
+\r
+// number of interrupts sources supported by each GIC on the EB\r
+#define EB_NUM_GIC_INTERRUPTS 96 \r
+\r
+// number of 32-bit registers needed to represent those interrupts as a bit\r
+// (used for enable set, enable clear, pending set, pending clear, and active regs)\r
+#define EB_NUM_GIC_REG_PER_INT_BITS (EB_NUM_GIC_INTERRUPTS / 32)\r
+\r
+// number of 32-bit registers needed to represent those interrupts as two bits\r
+// (used for configuration reg)\r
+#define EB_NUM_GIC_REG_PER_INT_CFG (EB_NUM_GIC_INTERRUPTS / 16)\r
+\r
+// number of 32-bit registers needed to represent interrupts as 8-bit priority field\r
+// (used for priority regs)\r
+#define EB_NUM_GIC_REG_PER_INT_BYTES (EB_NUM_GIC_INTERRUPTS / 4)\r
+\r
+#define GIC_DEFAULT_PRIORITY 0x80\r
+\r
+//\r
+// GIC definitions\r
+//\r
+\r
+// Distributor\r
+#define GIC_ICDDCR 0x000 // Distributor Control Register\r
+#define GIC_ICDICTR 0x004 // Interrupt Controller Type Register\r
+#define GIC_ICDIIDR 0x008 // Implementer Identification Register\r
+\r
+// each reg base below repeats for EB_NUM_GIC_REG_PER_INT_BITS (see GIC spec)\r
+#define GIC_ICDISR 0x080 // Interrupt Security Registers\r
+#define GIC_ICDISER 0x100 // Interrupt Set-Enable Registers\r
+#define GIC_ICDICER 0x180 // Interrupt Clear-Enable Registers\r
+#define GIC_ICDSPR 0x200 // Interrupt Set-Pending Registers\r
+#define GIC_ICDCPR 0x280 // Interrupt Clear-Pending Registers\r
+#define GIC_ICDABR 0x300 // Active Bit Registers\r
+\r
+// each reg base below repeats for EB_NUM_GIC_REG_PER_INT_BYTES\r
+#define GIC_ICDIPR 0x400 // Interrupt Priority Registers\r
+\r
+// each reg base below repeats for EB_NUM_GIC_INTERRUPTS\r
+#define GIC_ICDIPTR 0x800 // Interrupt Processor Target Registers\r
+#define GIC_ICDICFR 0xC00 // Interrupt Configuration Registers\r
+\r
+// just one of these\r
+#define GIC_ICDSGIR 0xF00 // Software Generated Interrupt Register\r
+\r
+\r
+// Cpu interface\r
+#define GIC_ICCICR 0x00 // CPU Interface Controler Register\r
+#define GIC_ICCPMR 0x04 // Interrupt Priority Mask Register\r
+#define GIC_ICCBPR 0x08 // Binary Point Register\r
+#define GIC_ICCIAR 0x0C // Interrupt Acknowledge Register\r
+#define GIC_ICCEIOR 0x10 // End Of Interrupt Register\r
+#define GIC_ICCRPR 0x14 // Running Priority Register\r
+#define GIC_ICCPIR 0x18 // Highest Pending Interrupt Register\r
+#define GIC_ICCABPR 0x1C // Aliased Binary Point Register\r
+#define GIC_ICCIDR 0xFC // Identification Register\r
+\r
+extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol;\r
+\r
+//\r
+// Notifications\r
+//\r
+VOID *CpuProtocolNotificationToken = NULL;\r
+EFI_EVENT CpuProtocolNotificationEvent = (EFI_EVENT)NULL;\r
+EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;\r
+\r
+\r
+HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers[EB_NUM_GIC_INTERRUPTS];\r
+\r
+/**\r
+ Register Handler for the specified interrupt source.\r
+\r
+ @param This Instance pointer for this protocol\r
+ @param Source Hardware source of the interrupt\r
+ @param Handler Callback for interrupt. NULL to unregister\r
+\r
+ @retval EFI_SUCCESS Source was updated to support Handler.\r
+ @retval EFI_DEVICE_ERROR Hardware could not be programmed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RegisterInterruptSource (\r
+ IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
+ IN HARDWARE_INTERRUPT_SOURCE Source,\r
+ IN HARDWARE_INTERRUPT_HANDLER Handler\r
+ )\r
+{\r
+ if (Source > EB_NUM_GIC_INTERRUPTS) {\r
+ ASSERT(FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ } \r
+ \r
+ if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ gRegisteredInterruptHandlers[Source] = Handler;\r
+ return This->EnableInterruptSource(This, Source);\r
+}\r
+\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
+EnableInterruptSource (\r
+ IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
+ IN HARDWARE_INTERRUPT_SOURCE Source\r
+ )\r
+{\r
+ UINT32 RegOffset;\r
+ UINTN RegShift;\r
+ \r
+ if (Source > EB_NUM_GIC_INTERRUPTS) {\r
+ ASSERT(FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ \r
+ // calculate enable register offset and bit position\r
+ RegOffset = Source / 32;\r
+ RegShift = Source % 32;\r
+\r
+ // write set-enable register\r
+ MmioWrite32 (EB_GIC1_DIST_BASE+GIC_ICDISER+(4*RegOffset), 1 << RegShift);\r
+ \r
+ return EFI_SUCCESS;\r
+}\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
+DisableInterruptSource (\r
+ IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
+ IN HARDWARE_INTERRUPT_SOURCE Source\r
+ )\r
+{\r
+ UINT32 RegOffset;\r
+ UINTN RegShift;\r
+ \r
+ if (Source > EB_NUM_GIC_INTERRUPTS) {\r
+ ASSERT(FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ \r
+ // calculate enable register offset and bit position\r
+ RegOffset = Source / 32;\r
+ RegShift = Source % 32;\r
+\r
+ // write set-enable register\r
+ MmioWrite32 (EB_GIC1_DIST_BASE+GIC_ICDICER+(4*RegOffset), 1 << RegShift);\r
+ \r
+ return EFI_SUCCESS;\r
+}\r
+\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
+GetInterruptSourceState (\r
+ IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
+ IN HARDWARE_INTERRUPT_SOURCE Source,\r
+ IN BOOLEAN *InterruptState\r
+ )\r
+{\r
+ UINT32 RegOffset;\r
+ UINTN RegShift;\r
+ \r
+ if (Source > EB_NUM_GIC_INTERRUPTS) {\r
+ ASSERT(FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ \r
+ // calculate enable register offset and bit position\r
+ RegOffset = Source / 32;\r
+ RegShift = Source % 32;\r
+ \r
+ if ((MmioRead32 (EB_GIC1_DIST_BASE+GIC_ICDISER+(4*RegOffset)) & (1<<RegShift)) == 0) {\r
+ *InterruptState = FALSE;\r
+ } else {\r
+ *InterruptState = TRUE;\r
+ }\r
+ \r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Signal to the hardware that the End Of Intrrupt 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
+EndOfInterrupt (\r
+ IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,\r
+ IN HARDWARE_INTERRUPT_SOURCE Source\r
+ )\r
+{\r
+ if (Source > EB_NUM_GIC_INTERRUPTS) {\r
+ ASSERT(FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ MmioWrite32 (EB_GIC1_CPU_INTF_BASE+GIC_ICCEIOR, Source);\r
+ return EFI_SUCCESS;\r
+}\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
+IrqInterruptHandler (\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 = MmioRead32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCIAR);\r
+ if (GicInterrupt >= EB_NUM_GIC_INTERRUPTS) {\r
+ MmioWrite32 (EB_GIC1_CPU_INTF_BASE+GIC_ICCEIOR, GicInterrupt);\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: %x\n", GicInterrupt));\r
+ }\r
+\r
+ EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt);\r
+}\r
+\r
+\r
+//\r
+// Making this global saves a few bytes in image size\r
+//\r
+EFI_HANDLE gHardwareInterruptHandle = NULL;\r
+\r
+//\r
+// The protocol instance produced by this driver\r
+//\r
+EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {\r
+ RegisterInterruptSource,\r
+ EnableInterruptSource,\r
+ DisableInterruptSource,\r
+ GetInterruptSourceState,\r
+ EndOfInterrupt\r
+};\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
+ExitBootServicesEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UINTN i;\r
+ \r
+ for (i = 0; i < EB_NUM_GIC_INTERRUPTS; i++) {\r
+ DisableInterruptSource (&gHardwareInterruptProtocol, i);\r
+ }\r
+}\r
+\r
+\r
+//\r
+// Notification routines\r
+//\r
+VOID\r
+CpuProtocolInstalledNotification (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_CPU_ARCH_PROTOCOL *Cpu;\r
+ \r
+ //\r
+ // Get the cpu protocol that this driver requires.\r
+ //\r
+ Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ //\r
+ // Unregister the default exception handler.\r
+ //\r
+ Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ //\r
+ // Register to receive interrupts\r
+ //\r
+ Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);\r
+ ASSERT_EFI_ERROR(Status);\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
+InterruptDxeInitialize (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN i;\r
+ UINT32 RegOffset;\r
+ UINTN RegShift;\r
+\r
+ \r
+ // Make sure the Interrupt Controller Protocol is not already installed in the system.\r
+ ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);\r
+\r
+ for (i = 0; i < EB_NUM_GIC_INTERRUPTS; i++) {\r
+ DisableInterruptSource (&gHardwareInterruptProtocol, i);\r
+ \r
+ // Set Priority \r
+ RegOffset = i / 4;\r
+ RegShift = (i % 4) * 8;\r
+ MmioAndThenOr32 (\r
+ EB_GIC1_DIST_BASE+GIC_ICDIPR+(4*RegOffset), \r
+ ~(0xff << RegShift), \r
+ GIC_DEFAULT_PRIORITY << RegShift\r
+ );\r
+ }\r
+\r
+ // configure interrupts for cpu 0\r
+ for (i = 0; i < EB_NUM_GIC_REG_PER_INT_BYTES; i++) {\r
+ MmioWrite32 (EB_GIC1_DIST_BASE + GIC_ICDIPTR + (i*4), 0x01010101);\r
+ }\r
+\r
+ // set binary point reg to 0x7 (no preemption)\r
+ MmioWrite32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCBPR, 0x7);\r
+\r
+ // set priority mask reg to 0xff to allow all priorities through\r
+ MmioWrite32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCPMR, 0xff);\r
+ \r
+ // enable gic cpu interface\r
+ MmioWrite32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCICR, 0x1);\r
+\r
+ // enable gic distributor\r
+ MmioWrite32 (EB_GIC1_DIST_BASE + GIC_ICCICR, 0x1);\r
+\r
+ \r
+ ZeroMem (&gRegisteredInterruptHandlers, sizeof (gRegisteredInterruptHandlers));\r
+ \r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &gHardwareInterruptHandle,\r
+ &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ \r
+ // Set up to be notified when the Cpu protocol is installed.\r
+ Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CpuProtocolInstalledNotification, NULL, &CpuProtocolNotificationEvent); \r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->RegisterProtocolNotify (&gEfiCpuArchProtocolGuid, CpuProtocolNotificationEvent, (VOID *)&CpuProtocolNotificationToken);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ // Register for an ExitBootServicesEvent\r
+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r