-/*++\r
-\r
-Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>\r
-Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR> \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
-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