3 Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
4 Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
5 Portions copyright (c) 2011-2014, ARM Ltd. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21 Driver implementing the GIC interrupt controller protocol
27 #include <Library/ArmLib.h>
28 #include <Library/BaseLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/BaseMemoryLib.h>
31 #include <Library/MemoryAllocationLib.h>
32 #include <Library/UefiBootServicesTableLib.h>
33 #include <Library/UefiLib.h>
34 #include <Library/PcdLib.h>
35 #include <Library/IoLib.h>
36 #include <Library/ArmGicLib.h>
38 #include <Protocol/Cpu.h>
39 #include <Protocol/HardwareInterrupt.h>
41 #define ARM_GIC_DEFAULT_PRIORITY 0x80
43 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
;
48 EFI_EVENT EfiExitBootServicesEvent
= (EFI_EVENT
)NULL
;
50 // Maximum Number of Interrupts
51 UINTN mGicNumInterrupts
= 0;
53 HARDWARE_INTERRUPT_HANDLER
*gRegisteredInterruptHandlers
= NULL
;
56 Register Handler for the specified interrupt source.
58 @param This Instance pointer for this protocol
59 @param Source Hardware source of the interrupt
60 @param Handler Callback for interrupt. NULL to unregister
62 @retval EFI_SUCCESS Source was updated to support Handler.
63 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
68 RegisterInterruptSource (
69 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
70 IN HARDWARE_INTERRUPT_SOURCE Source
,
71 IN HARDWARE_INTERRUPT_HANDLER Handler
74 if (Source
> mGicNumInterrupts
) {
76 return EFI_UNSUPPORTED
;
79 if ((Handler
== NULL
) && (gRegisteredInterruptHandlers
[Source
] == NULL
)) {
80 return EFI_INVALID_PARAMETER
;
83 if ((Handler
!= NULL
) && (gRegisteredInterruptHandlers
[Source
] != NULL
)) {
84 return EFI_ALREADY_STARTED
;
87 gRegisteredInterruptHandlers
[Source
] = Handler
;
89 // If the interrupt handler is unregistered then disable the interrupt
91 return This
->DisableInterruptSource (This
, Source
);
93 return This
->EnableInterruptSource (This
, Source
);
98 Enable interrupt source Source.
100 @param This Instance pointer for this protocol
101 @param Source Hardware source of the interrupt
103 @retval EFI_SUCCESS Source interrupt enabled.
104 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
109 EnableInterruptSource (
110 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
111 IN HARDWARE_INTERRUPT_SOURCE Source
114 if (Source
> mGicNumInterrupts
) {
116 return EFI_UNSUPPORTED
;
119 ArmGicEnableInterrupt (FixedPcdGet32 (PcdGicDistributorBase
), Source
);
125 Disable interrupt source Source.
127 @param This Instance pointer for this protocol
128 @param Source Hardware source of the interrupt
130 @retval EFI_SUCCESS Source interrupt disabled.
131 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
136 DisableInterruptSource (
137 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
138 IN HARDWARE_INTERRUPT_SOURCE Source
141 if (Source
> mGicNumInterrupts
) {
143 return EFI_UNSUPPORTED
;
146 ArmGicDisableInterrupt (PcdGet32(PcdGicDistributorBase
), Source
);
152 Return current state of interrupt source Source.
154 @param This Instance pointer for this protocol
155 @param Source Hardware source of the interrupt
156 @param InterruptState TRUE: source enabled, FALSE: source disabled.
158 @retval EFI_SUCCESS InterruptState is valid
159 @retval EFI_DEVICE_ERROR InterruptState is not valid
164 GetInterruptSourceState (
165 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
166 IN HARDWARE_INTERRUPT_SOURCE Source
,
167 IN BOOLEAN
*InterruptState
170 if (Source
> mGicNumInterrupts
) {
172 return EFI_UNSUPPORTED
;
175 *InterruptState
= ArmGicIsInterruptEnabled (PcdGet32(PcdGicDistributorBase
), Source
);
181 Signal to the hardware that the End Of Intrrupt state
184 @param This Instance pointer for this protocol
185 @param Source Hardware source of the interrupt
187 @retval EFI_SUCCESS Source interrupt EOI'ed.
188 @retval EFI_DEVICE_ERROR Hardware could not be programmed.
194 IN EFI_HARDWARE_INTERRUPT_PROTOCOL
*This
,
195 IN HARDWARE_INTERRUPT_SOURCE Source
198 if (Source
> mGicNumInterrupts
) {
200 return EFI_UNSUPPORTED
;
203 ArmGicEndOfInterrupt (PcdGet32(PcdGicInterruptInterfaceBase
), Source
);
208 EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
210 @param InterruptType Defines the type of interrupt or exception that
211 occurred on the processor.This parameter is processor architecture specific.
212 @param SystemContext A pointer to the processor context when
213 the interrupt occurred on the processor.
220 IrqInterruptHandler (
221 IN EFI_EXCEPTION_TYPE InterruptType
,
222 IN EFI_SYSTEM_CONTEXT SystemContext
226 HARDWARE_INTERRUPT_HANDLER InterruptHandler
;
228 GicInterrupt
= ArmGicAcknowledgeInterrupt (PcdGet32(PcdGicInterruptInterfaceBase
));
230 // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the number of interrupt (ie: Spurious interrupt).
231 if ((GicInterrupt
& ARM_GIC_ICCIAR_ACKINTID
) >= mGicNumInterrupts
) {
232 // The special interrupt do not need to be acknowledge
236 InterruptHandler
= gRegisteredInterruptHandlers
[GicInterrupt
];
237 if (InterruptHandler
!= NULL
) {
238 // Call the registered interrupt handler.
239 InterruptHandler (GicInterrupt
, SystemContext
);
241 DEBUG ((EFI_D_ERROR
, "Spurious GIC interrupt: 0x%x\n", GicInterrupt
));
244 EndOfInterrupt (&gHardwareInterruptProtocol
, GicInterrupt
);
248 // Making this global saves a few bytes in image size
250 EFI_HANDLE gHardwareInterruptHandle
= NULL
;
253 // The protocol instance produced by this driver
255 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol
= {
256 RegisterInterruptSource
,
257 EnableInterruptSource
,
258 DisableInterruptSource
,
259 GetInterruptSourceState
,
264 Shutdown our hardware
266 DXE Core will disable interrupts and turn off the timer and disable interrupts
267 after all the event handlers have run.
269 @param[in] Event The Event that is being processed
270 @param[in] Context Event Context
274 ExitBootServicesEvent (
281 // Acknowledge all pending interrupts
282 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
283 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
286 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
287 EndOfInterrupt (&gHardwareInterruptProtocol
, Index
);
290 // Disable Gic Interface
291 ArmGicDisableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase
));
293 // Disable Gic Distributor
294 ArmGicDisableDistributor (PcdGet32(PcdGicDistributorBase
));
298 Initialize the state information for the CPU Architectural Protocol
300 @param ImageHandle of the loaded driver
301 @param SystemTable Pointer to the System Table
303 @retval EFI_SUCCESS Protocol registered
304 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
305 @retval EFI_DEVICE_ERROR Hardware problems
309 InterruptDxeInitialize (
310 IN EFI_HANDLE ImageHandle
,
311 IN EFI_SYSTEM_TABLE
*SystemTable
318 EFI_CPU_ARCH_PROTOCOL
*Cpu
;
321 // Make sure the Interrupt Controller Protocol is not already installed in the system.
322 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gHardwareInterruptProtocolGuid
);
324 mGicNumInterrupts
= ArmGicGetMaxNumInterrupts (PcdGet32(PcdGicDistributorBase
));
326 for (Index
= 0; Index
< mGicNumInterrupts
; Index
++) {
327 DisableInterruptSource (&gHardwareInterruptProtocol
, Index
);
330 RegOffset
= Index
/ 4;
331 RegShift
= (Index
% 4) * 8;
333 PcdGet32(PcdGicDistributorBase
) + ARM_GIC_ICDIPR
+ (4*RegOffset
),
335 ARM_GIC_DEFAULT_PRIORITY
<< RegShift
340 // Targets the interrupts to the Primary Cpu
343 // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
344 // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
345 // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
346 // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
348 // Read the first Interrupt Processor Targets Register (that corresponds to the 4
350 CpuTarget
= MmioRead32 (PcdGet32 (PcdGicDistributorBase
) + ARM_GIC_ICDIPTR
);
352 // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
353 // is 0 when we run on a uniprocessor platform.
354 if (CpuTarget
!= 0) {
355 // The 8 first Interrupt Processor Targets Registers are read-only
356 for (Index
= 8; Index
< (mGicNumInterrupts
/ 4); Index
++) {
357 MmioWrite32 (PcdGet32 (PcdGicDistributorBase
) + ARM_GIC_ICDIPTR
+ (Index
* 4), CpuTarget
);
361 // Set binary point reg to 0x7 (no preemption)
362 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCBPR
, 0x7);
364 // Set priority mask reg to 0xff to allow all priorities through
365 MmioWrite32 (PcdGet32(PcdGicInterruptInterfaceBase
) + ARM_GIC_ICCPMR
, 0xff);
367 // Enable gic cpu interface
368 ArmGicEnableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase
));
370 // Enable gic distributor
371 ArmGicEnableDistributor (PcdGet32(PcdGicDistributorBase
));
373 // Initialize the array for the Interrupt Handlers
374 gRegisteredInterruptHandlers
= (HARDWARE_INTERRUPT_HANDLER
*)AllocateZeroPool (sizeof(HARDWARE_INTERRUPT_HANDLER
) * mGicNumInterrupts
);
376 Status
= gBS
->InstallMultipleProtocolInterfaces (
377 &gHardwareInterruptHandle
,
378 &gHardwareInterruptProtocolGuid
, &gHardwareInterruptProtocol
,
381 ASSERT_EFI_ERROR (Status
);
384 // Get the CPU protocol that this driver requires.
386 Status
= gBS
->LocateProtocol(&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Cpu
);
387 ASSERT_EFI_ERROR(Status
);
390 // Unregister the default exception handler.
392 Status
= Cpu
->RegisterInterruptHandler(Cpu
, ARM_ARCH_EXCEPTION_IRQ
, NULL
);
393 ASSERT_EFI_ERROR(Status
);
396 // Register to receive interrupts
398 Status
= Cpu
->RegisterInterruptHandler(Cpu
, ARM_ARCH_EXCEPTION_IRQ
, IrqInterruptHandler
);
399 ASSERT_EFI_ERROR(Status
);
401 // Register for an ExitBootServicesEvent
402 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_NOTIFY
, ExitBootServicesEvent
, NULL
, &EfiExitBootServicesEvent
);
403 ASSERT_EFI_ERROR (Status
);